Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Intraday Trading Issue

Hi guys,

As you may have seen in the forum, we have just conduct a backtest on our news and blog data set. The backtest we did represented holding periods from a few days to months. However, there have been excessive demand for an intraday trading strategy on our data set. We would like some help on fixing the code so it will exit all positions 5 minutes before the market close.

Seong Lee gave us a head start on fixing some of the code for us. However, it does not exit all the positions at the end of the trading day (5 mins before market close).

I would really appreciate the help from the community to get this code going so we can start a backtest utilizing intraday strategies.

Instructions: Please make the code exit all positions 5 minutes before market close.

#Code Structure Provided by: Derek Tishler ([email protected] )  
import numpy as np  
import pandas as pd  
from pytz import timezone  
from datetime import datetime, timedelta  
from zipline.utils.tradingcalendar import get_early_closes

def initialize(context):  
    #Use file from dropbox to assign sid's and sentiment values to trade on.  
    set_symbol_lookup_date('2015-04-01')  
    # Universe is set daily by inputs from the cvs fetch. But we will set a benchmark for comparison.  
    context.stocks = symbols('FOXA',  
'ATVI',
'ADBE',
'AKAM',
'ALXN',
'ALTR',
'AMZN',
'AMGN',
'ADI',
'AAPL',
'AMAT',
'ADSK',
'ADP',
'AVGO',
'BIDU',
'BBBY',
'BIIB',
'BRCM',
'CHRW',
'CA',
'CTRX',
'CELG',
'CERN',
'CHTR',
'CHKP',
'CSCO',
'CTXS',
'CTSH',
'COST',
'DTV',
'DISH',
'DLTR',
'EBAY',
'EQIX',
'EXPE',
'EXPD',
'ESRX',
'FFIV',
'FB',
'FAST',
'FISV',
'GRMN',
'GILD',
'GOOG',
'HSIC',
'ILMN',
'INTC',
'INTU',
'ISRG',
'KLAC',
'GMCR',
'KRFT',
'LMCK',
'LMCA',
'LLTC',
'MAR',
'MAT',
'MXIM',
'MU',
'MSFT',
'MDLZ',
'MNST',
'MYL',
'NTAP',
'NFLX',
'NVDA',
'NXPI',
'ORLY',
'PCAR',
'PAYX',
'QCOM',
'REGN',
'ROST',
'SNDK',
'SBAC',
'STX',
'SIAL',
'SIRI',
'SPLS',
'SBUX',
'SRCL',
'SYMC',
'TSLA',
'TXN',
'PCLN',
'TSCO',
'TRIP',
'VRSK',
'VRTX',
'VIAB',
'VIP',
'VOD',
'WDC',
'WFM',
'WYNN',
'XLNX',
'YHOO',
)
    context.minutes_early = 5  
    set_benchmark(symbol('QQQ'))  
    # set a more realistic commission for IB, remove both this and slippage when live trading in IB  
    set_commission(commission.PerShare(cost=0.014, min_trade_cost=1.4))  
    # Default slippage values, but here to mess with for fun.  
    set_slippage(slippage.VolumeShareSlippage(volume_limit=0.25, price_impact=0.1))  
    #Only needed in testing/debugging to ensure orders are closed like in IB  
 #   schedule_function(end_of_day, date_rules.every_day(), time_rules.market_close(minutes=5))  
    schedule_function(exit_all_positions,  
                      date_rules.every_day(),  
                      time_rules.market_close(minutes=5))  
    fetch_csv("https://dl.dropboxusercontent.com/u/70792051/Accern%20Backtest/noUTC10.csv",  
              date_column ='start_date',  
              date_format = '%m-%d-%Y %H:%M')  
    #Article Sentiment  
    context.upper_bound = 0.35  
    context.lower_bound = -0.35  
    #Impact Score  
    context.upper_bound_a = 35  
    context.lower_bound_a = 35  
    #Source Rank  
    context.upper_bound_c = 4  
    context.lower_bound_c = 4  

"""
From quantopian help  
"""
def exit_all_positions(context, data):  
    for stock in context.portfolio.positions:  
        if context.portfolio.positions[stock].amount != 0 and stock in data and type(stock) == type(sid(24)):  
            order_target_percent(stock, 0)

# Will be called on every trade event for the securities you specify.  
def handle_data(context, data):  
    #Get EST Time  
    context.exchange_time = pd.Timestamp(get_datetime()).tz_convert('US/Eastern')  
    #Check that our portfolio does not  contain any invalid/external positions/securities  
    check_invalid_positions(context, data)  
    for stock in data:  
        if ('article_sentiment' and 'event_impact_score_entity_1' and 'overall_source_rank') in data[stock]:  
            record(Accern_Article_Sentiment = data[stock]['article_sentiment'], upperBound = context.upper_bound, lowerBound = context.lower_bound)  
            record(Event_Impact_Entity = data[stock]['event_impact_score_entity_1'], upperBound = context.upper_bound_a, lowerBound = context.lower_bound_a)  
            record(Overall_Source_Rank = data[stock]['overall_source_rank'], upperBound = context.upper_bound_c, lowerBound = context.lower_bound_c)  

            # We will not place orders if a stock is already in the process of handeling an order(fill time)  
            if check_if_no_conflicting_orders(stock):  
                try:  
                    # Go Long(buy), or exit and then buy(since minute mode so this condition will be valid all day  
                    if (data[stock]['article_sentiment'] > context.upper_bound) and (data[stock]['event_impact_score_entity_1'] > context.upper_bound_a) and (data[stock]['overall_source_rank'] > context.upper_bound_c):

                        # If we hav no positions, then we are good to buy  
                        if context.portfolio.positions[stock.sid].amount == 0:  
                            buy_position(context, data, stock)  
                        # We have some positions, if they are short, then exit that position so we can go long.  
                        else:  
                            if context.portfolio.positions[stock.sid].amount < 0:  
                                exit_position(context, data, stock)

                    # Go short(sell), or exit and then short(since minute mode so this condition will be valid all day  
                    elif (data[stock]['article_sentiment'] < context.lower_bound) and (data[stock]['event_impact_score_entity_1'] > context.lower_bound_a) and (data[stock]['overall_source_rank'] > context.lower_bound_c):

                        # If we have no positions, then we are good to buy  
                        if context.portfolio.positions[stock.sid].amount == 0:  
                            short_position(context, data, stock)  
                        # We have some positions, if they are long, then exit that position so we can go short.  
                        else:  
                            if context.portfolio.positions[stock.sid].amount > 0:  
                                exit_position(context, data, stock)  
                except:  
                    pass  

def buy_position(context, data, stock):

    # Place an order, and store the ID to fetch order info  
    orderId    = order_target_percent(stock, 0.05)  
    # How many shares did we just order, since we used target percent of availible cash to place order not share count.  
    shareCount = get_order(orderId).amount

    # We need to calculate our own inter cycle portfolio snapshot as its not updated till next cycle.  
    value_of_open_orders(context, data)  
    availibleCash = context.portfolio.cash-context.cashCommitedToBuy-context.cashCommitedToSell

    log.info("+ BUY {0:,d} of {1:s} at ${2:,.2f} for ${3:,.2f} / ${4:,.2f} @ {5:d}:{6:d}"\  
             .format(shareCount,  
                     stock.symbol,data[stock]['price'],  
                     data[stock]['price']*shareCount,  
                     availibleCash,  
                     context.exchange_time.hour,  
                     context.exchange_time.minute))

def short_position(context, data, stock):  
    #orderId    = order_target_percent(stock, -1.0/len(data))  
    orderId    = order_target_percent(stock, -0.05)  
    # How many shares did we just order, since we used target percent of availible cash to place order not share count.  
    shareCount = get_order(orderId).amount

    # We need to calculate our own inter cycle portfolio snapshot as its not updated till next cycle.  
    value_of_open_orders(context, data)  
    availibleCash = context.portfolio.cash-context.cashCommitedToBuy+context.cashCommitedToSell

    log.info("- SHORT {0:,d} of {1:s} at ${2:,.2f} for ${3:,.2f} / ${4:,.2f} @ {5:d}:{6:d}"\  
             .format(shareCount,  
                     stock.symbol,data[stock]['price'],  
                     data[stock]['price']*shareCount,  
                     availibleCash,  
                     context.exchange_time.hour,  
                     context.exchange_time.minute))

def exit_position(context, data, stock):  
    order_target(stock, -1)  
    value_of_open_orders(context, data)  
    availibleCash = context.portfolio.cash-context.cashCommitedToBuy-context.cashCommitedToSell  
    log.info("- Exit {0:,d} of {1:s} at ${2:,.2f} for ${3:,.2f} / ${4:,.2f} @ {5:d}:{6:d}"\  
                 .format(int(context.portfolio.positions[stock.sid].amount),  
                         stock.symbol,  
                         data[stock]['price'],  
                         data[stock]['price']*context.portfolio.positions[stock.sid].amount,  
                         availibleCash,  
                         context.exchange_time.hour,  
                         context.exchange_time.minute))  
################################################################################

def check_if_no_conflicting_orders(stock):  
    # Check that we are not already trying to move this stock  
    open_orders = get_open_orders()  
    safeToMove  = True  
    if open_orders:  
        for security, orders in open_orders.iteritems():  
            for oo in orders:  
                if oo.sid == stock.sid:  
                    if oo.amount != 0:  
                        safeToMove = False  
    return safeToMove  
    #

def check_invalid_positions(context, securities):  
    # Check that the portfolio does not contain any broken positions  
    # or external securities  
    for sid, position in context.portfolio.positions.iteritems():  
        if sid not in securities and position.amount != 0:  
            errmsg = \  
                "Invalid position found: {sid} amount = {amt} on {date}"\  
                .format(sid=position.sid,  
                        amt=position.amount,  
                        date=get_datetime())  
            raise Exception(errmsg)  
def end_of_day(context, data):  
    # cancle any order at the end of day. Do it ourselves so we can see slow moving stocks.  
    open_orders = get_open_orders()  
    if open_orders:# or context.portfolio.positions_value > 0.:  
        #log.info("")  
        log.info("*** EOD: Stoping Orders & Printing Held ***")

    # Print what positions we are holding overnight  
    for stock in data:  
        if context.portfolio.positions[stock.sid].amount != 0:  
            log.info("{0:s} has remaining {1:,d} Positions worth ${2:,.2f}"\  
                     .format(stock.symbol,  
                             context.portfolio.positions[stock.sid].amount,  
                             context.portfolio.positions[stock.sid].cost_basis\  
                             *context.portfolio.positions[stock.sid].amount))  
    # Cancle any open orders ourselves(In live trading this would be done for us, soon in backtest too)  
    if open_orders:  
        # Cancle any open orders ourselves(In live trading this would be done for us, soon in backtest too)  
        for security, orders in open_orders.iteritems():  
            for oo in orders:  
                log.info("X CANCLED {0:s} with {1:,d} / {2:,d} filled"\  
                                     .format(security.symbol,  
                                             oo.filled,  
                                             oo.amount))  
                cancel_order(oo)  
    #  
    log.info('')  
def value_of_open_orders(context, data):  
    # Current cash commited to open orders, bit of an estimation for logging only  
    context.currentCash = context.portfolio.cash  
    open_orders = get_open_orders()  
    context.cashCommitedToBuy  = 0.0  
    context.cashCommitedToSell = 0.0  
    if open_orders:  
        for security, orders in open_orders.iteritems():  
            for oo in orders:  
                # Estimate value of existing order with current price, best to use order conditons?  
                if(oo.amount>0):  
                    context.cashCommitedToBuy  += oo.amount * data[oo.sid]['price']  
                elif(oo.amount<0):  
                    context.cashCommitedToSell += oo.amount * data[oo.sid]['price']  
    #  
4 responses

Hello Kumesh,

Is it possible that you are not exiting certain positions because there were no historical trades for those securities in the database over the last 5 minutes of the day? For an order to be filled, the backtester needs a real price (not one from forward-filling), so for thinly traded stocks, there's no guarantee of a fill in a specified time. My interpretation of this behavior is that the backtester conservatively assumes that there was effectively no market for a stock if nobody traded it in a given minute.

I'm wondering if a custom slippage model could be written to override this default behavior?

Grant

Hi Grant,

Thank you and I don't think so because large stocks such as Amazon and Apple are not getting exit. I even tried to sell 60 minutes before trading day but no luck. I attached the backtest below and you can clone the code and check it out. Not entirely sure why it's not exiting at the end of the day.

I tried playing around with slippage as well but no luck.

Would really appreciate the help!

Best,
Kumesh

Kumesh,

Thanks for the code that can be cloned. I don't have time now, but it is an interesting problem you're having.

You might consider not using handle_data, but rather a separate function. I'd have to dig in, but I'm wondering if handle_data is emitting orders after you attempt to close everything out for the day?

Grant

Nice Derek,

So you check for any existing orders and exit them afterwards?

Big fan of your work!

Seong

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.