Back to Community
Dealing with stock splits

How about this for dealing with stock splits (and maybe other extreme jumps).
A portable self-contained single function. Tailor the two variables offset and _days_out as you wish.

The attached backtest contains a test mode that simulates a split and this is the output:

2015-09-22 split:75 INFO TSLA price jump 130.50 to 230.50, possible split, excluding 63 days  
2015-12-21 split:79 INFO Clearing TSLA possible split exclusion after 63 days, now 232.56  
def handle_data(context, data):  
    for stock in data:  
        if split(stock, context, data): continue

        # Your other code, ordering etc ...

def split(s, c, data):  
    ''' Try to detect a stock split or reverse split and quarantine the stock for x days.  
        Especially important in the live/real environment where any indicators involving prices  
          could possibly lead to an erroneous (disastrous) buy or sell,  
          also for the rare backtest splits mistakenly not adjusted for awhile.  
        Input:  c is context, s is the stock object  
        Output: 0 if not excluded, 1 if excluded  
    '''  
    if 'excludes' not in c:  
        c.excludes = {  
            '_days_out': 63,                          # Number of days to keep them out of trading  
            '_date_prv': get_datetime().date()  
        }  
    offset  = .4  # Ratio above or below previous price to consider too much of a jump,  
                  #   and therefore a possible split (or reverse split).  
    exclude = 0  
    sym     = s.symbol  
    price   = data[s].price  
    if sym not in c.excludes:  
        c.excludes[sym] = {  
            'price_prv': price,  
            'days_out' : -1    # Not out, initialization  
        }  
        return 0  
    price_prv = c.excludes[sym]['price_prv']  
    if   price > price_prv + price_prv * offset \  
      or price < price_prv - price_prv * offset:      # If price change beyond offset up or down  
        c.excludes[sym] = {  
            'price_prv': price,  
            'days_out' : 0                            # Begin exclusion  
        }  
        exclude = 1  
        log.info('{} price jump {} to {}, possible split, excluding {} days'.format(  
            sym, '%.2f' % c.excludes[sym]['price_prv'], '%.2f' % price_prv, c.excludes['_days_out']))  
    if c.excludes[sym]['days_out'] >= c.excludes['_days_out']:  
        c.excludes[sym]['days_out'] = -1                   # Reset, no longer excluded  
        log.info('Clearing {} possible split exclusion after {} days, now {}'.format(  
            sym, c.excludes['_days_out'], '%.2f' % price))  
    if get_datetime().date() != c.excludes['_date_prv']:   # New day  
        c.excludes['_date_prv'] = get_datetime().date()    # Save the new date as previous  
        if c.excludes[sym]['days_out'] != -1:  
            c.excludes[sym]['days_out'] += 1               # Is excluded, increment days  
            exclude = 1  
    c.excludes[sym]['price_prv'] = price_prv               # New previous price  
    if exclude:  
        return 1  
    return 0  
Clone Algorithm
4
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 5690dfad68e0761185e07656
There was a runtime error.
1 response

This is nice. Couple suggestions:

  • if 'excludes' not in c: I'd remove this and move the initialization into initialize() so you don't have to check for it over and over again
  • price > price_prv + price_prv * offset is the same as price > price_prv * (1 + offset)
  • I am not familiar with classes in python but it feels like if excludes were modeled as an object so you could hide those details of checking and setting values in it