Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
How to create a Break-Out model?

Hi all,

I'm new here. Hope you guys help me build something here in the world of Python.
I want to create a simple model, which is based on 'Break-Out' strategy.
In this post, I want to start with a simple one.


For example, I want to build a '5-day Break-Out' strategy (intraday):

Total Investment: 1000000

1st Long Entry:
If price > highest high over the past 5 days (Not the high price of 5 days ago!!) then
buy 50% of maximum investment.

2nd Long Entry:
If price > yesterday's high then buy the rest of 50% of maximum investment.

Long exit:
If price < moving average (30) then sell all the current shares of long position.

Short strategy is exactly opposite to Long strategy.

Exit on close of day:
Sell(if position is long) or BuytoCover(if short) all current shares at 15:55(EST).


Please help me.
Thank you.

11 responses

Hey Kyu,

Regarding your long strategies, if the price is greater than the highest high over the past 5 days (1st Long Entry), then it must also be greater than yesterday's high (2nd Long Entry). For the 2nd Long Entry, you say 'buy the rest of 50% of maximum investment,' but shouldn't it be 'buy the rest of 50% of maximum investment'' for the 1st Long Entry and 'buy 50% of maximum investment' for 2nd Long Entry. I assume you would want to take a long position in 50% of the max investment if price > yesterday's high and then 50% more (100% total) if the price is also greater than the 5 day high.

Let me know what you think.

Ryan

Hey Kyu,

Here is a first attempt at coding your idea using Quantopian. Let me know your feedback.

Ryan

import numpy as np  
from pytz import timezone  
from datetime import datetime, timedelta  
from zipline.utils.tradingcalendar import get_early_closes

def initialize(context):  
    # Choose asset to include in the algo  
    context.stock = sid(24)  
    # Initialize start and end dates to check for early closes  
    start_date = context.stock.security_start_date  
    end_date = context.stock.security_end_date  
    context.early_closes = get_early_closes(start_date,end_date).date  
    context.minutes_before_close = 5  
    # Setting our maximum position size  
    context.max_notional = 100000.1  
    context.min_notional = -100000.0

def handle_data(context, data):  
    price = data[context.stock].price  
    notional = context.portfolio.positions[context.stock].amount * price  
    # Compute maxmimum high price over the past five days  
    max_5_day_high = max(np.array(history(bar_count=5, frequency='1d', field='high')))  
    # Compute minimum low price over the past five days  
    min_5_day_low = min(np.array(history(bar_count=5, frequency='1d', field='low')))  
    # Compute yesterday's high  
    yesterday_high = np.array(history(bar_count=1, frequency='1d', field='high'))  
    # Compute yesterday's low  
    yesterday_low = np.array(history(bar_count=1, frequency='1d', field='low'))  
    # Compute the 30 day moving average  
    moving_avg_30 = data[context.stock].mavg(30)  
    # Long Strategy  
    if price >=  max_5_day_high and notional < context.max_notional:  
        print('Enter long strategy')  
        order_target_value(context.stock, context.max_notional)  
    elif price <  max_5_day_high and price >= yesterday_high and notional < context.max_notional:  
        print('Enter long strategy')  
        order_target_value(context.stock, 0.5*context.max_notional)  
    # Exit long strategy  
    else:  
        if price < moving_avg_30 and notional > 0:  
            order_target_percent(context.stock, 0)  
    # Short Strategy  
    if price <=  min_5_day_low and notional > context.min_notional:  
        print('Enter short strategy')  
        order_target_value(context.stock, context.min_notional)  
    elif price >  min_5_day_low and price <= yesterday_low and notional > context.min_notional:  
        print('Enter short strategy')  
        order_target_value(context.stock, 0.5*context.min_notional)  
    # Exit short strategy  
    else:  
        if price > moving_avg_30 and notional < 0:  
            order_target_percent(context.stock, 0)  
    # Exit position at end of day  
    if endofday_check(context) == True:  
        order_target_percent(context.stock, 0)  
    else:  
        pass

# Check for the end of the trading day  
def endofday_check(context):  
    # Converts all time-zones into US EST  
    loc_dt = get_datetime().astimezone(timezone('US/Eastern'))  
    date = get_datetime().date()  
    # Checks for an early close on special dates - market closes at 1:00PM  
    if date in context.early_closes:  
        # Returns true if it's 1:00PM - minutes so in this case 12:55PM  
        if loc_dt.hour == 12 and loc_dt.minute == (60-context.minutes_before_close):  
            log.debug(get_datetime())  
            return True  
        else:  
            return False  
    # Returns true if it's 4:00PM EST - minutes so in this case at 3:55PM  
    # Daylight savings time are accounted for, so it will automatically adjust to DST  
    elif loc_dt.hour == 15 and loc_dt.minute == (60-context.minutes_before_close):  
        log.debug(get_datetime())  
        return True  
    else:  
        return False  

Ryan,

Thank you so much. Again, I'm learning a lot.

However, it doesn't seem to work as I expected. I have another backtesting tool coded in different language, the result looks totally different.

Questions:
1). If I set the moving avg to 180, this means that it is based on the calculation of past 180 minutes, right? (because this is a minute base backtest!)
2). Can I add profit target and stop loss based on #of ticks?

Some problems:
1). It does NOT seem to close all the open positions at the end of day.
I added the following to see the log info for it as below.

Exit position at end of day  
    if endofday_check(context) == True:  
        order_target_percent(context.stock, 0)  
        log.info('Exit, end of day' + str(endofday_check(context)))  
    else:  
        pass  

But I can't see any logs showing that closed at the end of day.

2). Also, please see below. This is the logs after I tested with FAS :
2008-11-12handle_data:53INFOSE#1, 5L, [ 109.776], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 109.442], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 109.442], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 109.137], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 108.414], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 108.109], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 107.998], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 107.637], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 107.637], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 106.926], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 106.609], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 105.915], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 105.415], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 105.387], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 104.665], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 104.165], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 103.478], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 103.192], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 102.359], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 102.276], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 102.276], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 101.831], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 100.498], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 99.998], -1000000.0
2008-11-12handle_data:53INFOSE#1, 5L, [ 97.22], -1000000.0

(SE: Short Entry, 5L: 5-day Low.)

As you can see above, on 11/12/2008, total #25 short entry signals were generated based on 5-day low with min_notional. (Do we have unlimited cash??)
This doesn't seem to be right. Isn't it supposed to be just 1 entry with max/min_notional?
Please check this.

To make sure that this strategy goes to the right direction as I tested on other platform, I want to change this strategy simpler:

1). Entries are based on only 5-day high and 5-day low with max/min_notional.

2).I want to add profit target and stop loss based on the #of ticks. Is this possible here? Can you help me?
For example, my profit target is 500 ticks up from my cost basis. And stop loss is 300 ticks down from my cost basis.
So the Sell and BuyToCover orders should be Limit Order.

3). Close at the end of day (15:55 EST): please double check this...

I really appreciate it in advance if you check all of these and correct all the bugs and errors if any.
I'm learning a lot from you and all the memebrs of this forum.

Thank you again.
.

Hey Kyu,

Here are answers to your first two questions.

1) The argument to mavg specifies the number of days for the moving average, not minutes, even if the backtest is running in minute mode. mavg(180) would give the moving average of the past 180 days, not minutes.

2) Yes, you can add profit target and stop loss functions. Here are a few threads where community members have explored these issues.
https://www.quantopian.com/posts/use-a-trailing-stop-loss
https://www.quantopian.com/posts/auto-adjusting-stop-loss
https://www.quantopian.com/posts/stops
https://www.quantopian.com/posts/simple-dual-vwap-with-percentage-based-take-profit-and-stop-loss

I will continue to look at the other issues today.

1). It does NOT seem to close all the open positions at the end of day.

Take a look at the attached backtest. From what I see, we place an order to close the position 5 minutes before close. We then check the outstanding orders one minute before close, and usually by this time all of the orders to exit the position have been filled, so there are no outstanding orders and the notional of the position is zero. Take a look at the logs.

Here are some of the changes to the code that generate the logs.

if endofday_check_v2(context) == True:  
        log.info('End of day order check, time is %s' % get_datetime().astimezone(timezone('US/Eastern')))  
        log.info('Notional is %s'% notional)  
        # retrieve all the open orders and log the total open amount  
        # for each order  
        open_orders = get_open_orders()  
        # open_orders is a dictionary keyed by sid, with values that are lists of orders.  
        if open_orders:  
            # iterate over the dictionary  
            for security, orders in open_orders.iteritems():  
                # iterate over the orders  
                for oo in orders:  
                    message = 'Open order for {amount} shares in {stock}'  
                    message = message.format(amount=oo.amount, stock=security)  
                    log.info(message)  
    else:  
        pass  

2). Also, please see below. This is the logs after I tested with FAS :

I apologize, I don't know what FAS is. Did you backtest the strategy on a different platform. In the logic below, the strategy should only place an order when the current notional value of the portfolio is within the investment limits specified by context.max_notional and context.min_notional. Does this help? While there may be 25 short entry "signals" in the day where the price falls below the minimum 5-day low. Perhaps not all of the orders are being filled by the next bar, so the system does not recognize that the minimum notional has been reached. When I backtest with Quantopian, there are certainly not so many short entry signals.

if price >=  max_5_day_high and notional < context.max_notional:  
        print('Enter long strategy')  
if price <=  min_5_day_low and notional > context.min_notional:  
        print('Enter short strategy')  

Cheers,
Ryan

I modified the code to include flags that indicate whether a long/short strategy has been exited on the day. Now, the algorithm should not enter into multiple long or short strategies in the same day, which should alleviate the issue that you encountered where 25 short entry signals were generated in a day. The algorithm right now does not re-open a position the following day after exiting the position at the end of the day. I assume you would want to re-enter the position the next trading day rather than starting with an empty portfolio.

1). Entries are based on only 5-day high and 5-day low with max/min_notional.

Here is a version that does this and does not include the signal for the 2nd Long/Short entry based on yesterday's price.

Hi Ryan,

Thanks so much again.

1). If mavg is based on only daily basis, how can I create minute basis moving average? Should I create a function for this?

2). I see this model closes open positions at the end of day. However, I don't understand below.
Please see the logs (this is with mavg(10) and 5_day entry model).

2014-01-27handle_data:96INFONotional is 0.0 --> It looks like closed all the open positions on 1/27
2014-01-28PRINTEnter short strategy --> Short entry on 1/28
2014-01-28endofday_check:129DEBUG2014-01-28 20:55:00+00:00
2014-01-28handle_data:73INFOExit, end of day, time is 2014-01-28 15:55:00-05:00
2014-01-28handle_data:74INFONotional is -100047.4002
2014-01-28handle_data:90INFOOpen order for 198 shares in Security(24 [AAPL])
2014-01-28PRINTEnter short strategy --> Enter another short?? on the same day?
2014-01-28endofday_check_v2:151DEBUG2014-01-28 20:59:00+00:00
2014-01-28handle_data:95INFOEnd of day order check, time is 2014-01-28 15:59:00-05:00
2014-01-28handle_data:96INFONotional is -99593.35 --> This looks like there's a still open position
2014-01-29endofday_check:129DEBUG2014-01-29 20:55:00+00:00
2014-01-29handle_data:73INFOExit, end of day, time is 2014-01-29 15:55:00-05:00
2014-01-29handle_data:74INFONotional is -98763.98 --> What is this?
2014-01-29handle_data:90INFOOpen order for 197 shares in Security(24 [AAPL])
2014-01-29PRINTEnter short strategy
2014-01-29endofday_check_v2:151DEBUG2014-01-29 20:59:00+00:00
2014-01-29handle_data:95INFOEnd of day order check, time is 2014-01-29 15:59:00-05:00
2014-01-29handle_data:96INFONotional is -99708.8704
2014-01-30endofday_check:129DEBUG2014-01-30 20:55:00+00:00
2014-01-30handle_data:73INFOExit, end of day, time is 2014-01-30 15:55:00-05:00
2014-01-30handle_data:74INFONotional is -99386.57 --> I'm lost....
2014-01-30handle_data:90INFOOpen order for 199 shares in Security(24 [AAPL])
2014-01-30endofday_check_v2:151DEBUG2014-01-30 20:59:00+00:00
2014-01-30handle_data:95INFOEnd of day order check, time is 2014-01-30 15:59:00-05:00
2014-01-30handle_data:96INFONotional is 0.0

End of logs.

Can you explain the logs above? I don't quite understand them. Please help me.
Also, I just checked with other platform with same entry strategy, it generated short signals everyday from 1/28 to today(1/31). But this model did not generate short signal on 1/30.

See also below Low prices that I got from Yahoo Finance:

Jan 30, 2014, 496.70
Jan 29, 2014, 498.62
Jan 28, 2014, 502.07
Jan 27, 2014, 545.75

3). I wanted to close all the positions at 15:55(EST) and start a new trading day with NO OPEN POSITION at 09:30(EST) daily.

4).

Now, the algorithm should not enter into multiple long or short strategies in the same day, which should alleviate the issue that you encountered where 25 short entry signals were generated in a day.

--> Why does this model NOT enter multiple enter in the same day? If the current shares are 0 after exit during the market session and if there's a right condition, it should generate any entry signals multiple times.

The algorithm right now does not re-open a position the following day after exiting the position at the end of the day.

--> Why?

Sorry to keep asking you. I've already learned a lot from you. But please help me a little more.

Thanks.

Hi Kyu,

To chime in here, I wanted to clarify the differences in prices between Quantopian and Yahoo Finance. There is a difference between the last trade price and EOD price, which will not match. The last trade price is taken from the tick bar at 3:59PM and is used in Quantopian as "close price". Yahoo EOD price is calculated from the closing auctions which are computed after market hours. Quantopian provides intraday minute data and does not cover the open/close auctions. However, the prices should match closely because you will receive the market open price at 9:31AM. For more info, see this thread here: https://www.quantopian.com/posts/load-bars-from-yahoo-values-differ-from-quantopian-values-dot-dot-dot

And welcome to Quantopian! Keep firing questions, we're glad to help out.

Alisa

Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

Hey Kyu,

It looks like the algorithm was entering a short position at the very end of the day (minutes before close) after the context.long_position_exited and context.short_position_exited flags were set back to zero. I made a change so the flags are set to zero at the beginning of the next day so multiple long or short strategies are not entered on the same day. Try the attached algo again and see the logs. Entering multiple long/short strategies daily (as opposed to only one) is a matter of preference (I am mostly trying to make an educational demonstration). You can change the code to allow multiple long positions in a day. I am also happy to help you write code to re-open the position at the beginning of the next day.

As of now, there is no function that computes a moving average at the minute level. This should be easy to code, however!

Ryan