Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Trouble Building a Breakout Strategy

Hi everyone,

I am an experienced trader, however, I am not experienced with Quantopian.

Can someone please explain to me why the following code does not work? I have viewed a couple other posts regarding breakouts but found little help in them.

What I am trying to do: when price breaks above an 89 day high, I buy, and when price breaks below an 89 day low, I sell. Obviously there is more to the strategy but right now I am trying to just figure the buying and selling part out.

Any help would be greatly appreciated.

6 responses

Two things,
1) how can the price be higher than the high or lower than the low? You'll want to use an array slice [:-2] to include only up until the previous day. (Alternatively you could set it to check for == instead of >, because as soon as the price exceeds the previous high it sets a new high)
2) Why order only 2%? Why not 100% Also, -2% sells all your long positions and also goes short. Is this what you want? Order_target or order_target_percent of 0 is how you sell.

I set it to rebalance once a day instead of every minute, since handle_data is just sooo much slower and I didn't foresee it having an effect on the results.

So far in my experience these results are about as good as you'll get with simple technical indicators. They just typically don't work better than buy and hold. SMA, RSI, ADX, etc. will typically not beat market, if they turn a profit at all.

Thank you Viridian and Vladimir for the replies,

The problem I was having was not including the '[:-2]'.

Viridian, my apologies, I will explain what I am trying to build in greater detail:

For a buy:
The buy is initiated once price breaks above an 89-day high. The initial stop loss is set 1 ATR(20) below the entry price. However, once the 100SMA's price is greater than the initial stop loss price, the 100SMA is now the trailing stop loss.

The two scenarios are: 1. I buy and get stopped out on the initial stop loss which is 1ATR(20) below the entry price. 2. I buy and get stopped out once price goes below the 100 SMA which has a greater price than the initial stop loss.

For a sell:
The sell is initiated once price breaks below an 89-day low. The initial stop loss is set 1 ATR(20) above the entry price. However, once the 100SMA's price is lower than the initial stop loss price, the 100SMA is now the trailing stop loss.

The two scenarios are: 1. I sell and get stopped out on the initial stop loss which is 1ATR(20) above the entry price. 2. I sell and get stopped out once price goes above the 100 SMA which has a lesser price than the initial stop loss.


I was using only 2% because I want to trade this on multiple different stocks. I was using just SPY for simplicity sake.

So that brings up the next couple questions that I will have to figure out, but would be very grateful for any help with them:

How do I run the algo so it can trade on multiple different stocks at the same time? In the code below, I am trying to trade Tesla, Apple, the S&P, and Amazon. When I try to build the algo I get : "ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
There was a runtime error on line 18."

import talib


def initialize(context):  
    schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open())  
    context.stock = symbols('TSLA', 'AAPL', 'SPY', 'AMZN')  
def my_rebalance(context, data):  
    price = data.current(context.stock, "price")  
    high_89 = data.history(context.stock, "high", 89, "1d")[:-2].max()  
    low_89 = data.history(context.stock, "low", 89, "1d")[:-2].min()  
    hist = data.history(context.stock, "price", 100, "1d")  
    sma_100 = hist.mean()  
    open_orders = get_open_orders()  

    if price > high_89:  
        if context.stock not in open_orders:  
            order_target_percent(context.stock, .05)  
    elif price < sma_100:  
        if context.stock not in open_orders:  
            order_target_percent(context.stock, 0)  

    if price < low_89:  
        if context.stock not in open_orders:  
            order_target_percent(context.stock, -0.05)  
    elif price > sma_100:  
        if context.stock not in open_orders:  
            order_target_percent(context.stock, 0)  
    record(leverage = context.account.leverage)  

The second question is: 1. How do set the initial stop loss to be 1ATR(20) above/below the entry point (depending on if its a buy or sell)? 2. How do I "override", if you will, the initial stoploss with the 100SMA which would act as a trailing stop loss?

I coded it up for you so hopefully you get the gist of how to do these basic things. I recommend going through the Quantopian tutorials (though I've only gotten through the first one so far myself) and also Codecademy's Python tutorials. These things have helped me get started.

Here's what I did: To apply your trading rules to a basket of stocks, you use a for loop to loop through each one. I divided the max desired leverage (1.0) by the amount of stocks in the securities list to get a weight. (Due to the nature of shorts and up-rounding done by the order function it nonetheless sometimes briefly exceeds 1.0 leverage.) Since one of your rules requires keeping track of whether the SMA100 has been breached by the entry price plus or minus ATR, I created a dictionary to store a True/False flag associated with each stock. There's a library called TALIB that has all the classic stock market indicators, so I imported that to get the ATR. I separated the logic into 1. what to do when no positions are currently held for that stock, 2. what to do when we are currently long the stock, and 3. what to do when we are currently short the stock.

Here's my analysis:
- Cool trading rules. Seems to overall work! Market neutral. Appears to beat market in the long run.
- In the short-term though there are periods where you have drawdown periods that last in some cases many years. That could be stressful losing money year after year. This could perhaps be alleviated by a larger basket of less-correlated stocks (ie, from different sectors). Could also help smooth the volatility, which is quite high.
- By entering your own hand-picked stocks you've introduced survivorship bias to the back test. Any sort of bias you introduce into the back test may (and likely will) lead to significantly worse results out-of-sample (ie when you start trading with it). So what you will want to apply the same 89 day logic to a pipeline screen and pull in stocks that way. This will show whether your great results are due to the aforementioned survivorship bias or whether the rules hold up. It will also increase the capacity of the algorithm.
- The shorting aspect (short when price under 89 low) seems to just enter losing trades and costs you quite a bit of gains in this back test. This might be because of the aforementioned survivorship bias (you picked winning stocks that aren't losing money) , and so when using Pipeline to pull in random stocks you might get better results with the shorts.
- As is, the algo is quite often under-leveraged -- sitting on a bunch of cash that isn't being put to work. Quantopian has an optimize portfolio ordering library to help you utilize all of the desired leverage.

For some perspective, here's what the same strategy looks like over a shorter timeframe, using the Dow Jones Industrial Average as the basket of stocks.

That's consistent with my observations of technical indicators -- they work really well with some stocks, some of the time.

Perhaps there's a sweet spot for market cap, or something along those lines to filter appropriate stocks?

I decided it was time for me to learn how to use pipeline, so I gave it a try.
I'm surprised it doesn't perform better.

I decided to try to revisit this. I had some bugs in my code, and there's probably still more... It's rarely returning any short candidates, which doesn't seem right.

Anyways, this version uses volume confirmation for the breakout. So if a price is breaking out above the 89-day high AND volume over the last day and a half is exceeding the average volume by 5x (I pulled these figured out of thin air), then buy. It's also attempting to use the Zack's Broker Ratings dataset to confirm that the security is rated a "Buy," I'd also tried using the SentEx dataset with similar negligible effect on the results... But I'm probably doing it all wrong. -- for that matter there are probably a bunch of mistakes in the code -- or else conventional wisdom on the matter is just wrong (or both).