Back to Community
5 10 20 Crossover Strategy with Leveraged ETF

Hello Quantopian community,
I have implemented the classical 5-10-20 EMA crossover trend following strategy with one twist. Instead of buying the SPY, I am shorting SDS (ultra inverse ETF). The strategy buys when 5 day EMA > 10 day EMA and 10 day EMA > 20 day EMA. The strategy has checks in place to ensure that prior orders are executed before placing new and to make sure we do not exceed cash balance.

I am interested to know how this can be improved further including any drawbacks or pitfalls that I might be missing. Any comments about reducing volatility or increasing alpha are certainly welcome.

Thanks,
-Dev

Clone Algorithm
376
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
import talib
import math
import pandas as pd 
import numpy as np
from pytz import timezone

# Put any initialization logic here.  The context object will be passed to
# the other methods in your algorithm.
def initialize(context):
    context.secs =   [ sid(8554),  # SPY
                       sid(32268), # SH
                       sid(32270), # SSO
                       sid(32382)  # SDS
                       ] # 
    
    context.spy  = sid(8554)
    context.sso = sid(32270)  
    context.sds = sid(32382)
    context.sh = sid(32268)
    context.max_notional =  10000.0  
    context.min_notional = -10000.0
    context.invested = False
    context.shorted = False
    context.lastbuydate = '01011990'
    context.longshort = 'none'
    context.shortenabled = False
    set_commission(commission.PerTrade(cost=0.0))
    set_slippage(slippage.FixedSlippage(spread=0.00))

    
    
def lastboughttoday(context):
    loc_dt = get_datetime().astimezone(timezone('US/Eastern')) 
    todaysdate = str(loc_dt.month)+str(loc_dt.day)+str(loc_dt.year)
    
    if(context.lastbuydate == todaysdate ): 
        return True
    else:
        return False
    
def updatelastbought(context):
    loc_dt = get_datetime().astimezone(timezone('US/Eastern')) 
    context.lastbuydate = str(loc_dt.month)+str(loc_dt.day)+str(loc_dt.year)
    
   

def intradingwindow_check(context):  
    # Converts all time-zones into US EST to avoid confusion  
        loc_dt = get_datetime().astimezone(timezone('US/Eastern'))
        if   loc_dt.hour >= 10 and (loc_dt.minute == 45  ):  
            #log.info("First Timestamp: Year {0}, Month {1}, Day {2} ".format(loc_dt.year, loc_dt.month, loc_dt.day))
            return True 
        else:   
            return False

# Will be called on every trade event for the securities you specify. 
def handle_data(context, data):
    loc_dt = get_datetime().astimezone(timezone('US/Eastern'))
    
    if not intradingwindow_check(context): return
    if has_orders(context): return
    if lastboughttoday(context): return
      
    # data[sid(X)] holds the trade event data for that security.    
    
    
    close_price =history(bar_count=25, frequency='1d',field='close_price')
    open_price = history(bar_count=25, frequency='1d',field='open_price')
    high_price = history(bar_count=25, frequency='1d',field='high')
    low_price =  history(bar_count=25, frequency='1d',field='low')    
    concat_price = pd.concat((open_price, close_price,high_price, low_price))
    mean_price =  concat_price.groupby(concat_price.index).mean()
    
    spy_series    = mean_price[context.spy]
    spy_ema5      = talib.EMA(spy_series, timeperiod=5)
    spy_ema10     = talib.EMA(spy_series, timeperiod=10)
    spy_ema20     = talib.EMA(spy_series, timeperiod=20)
    
    # [0:-1] above remove the last entry    
   
    #if loc_dt.hour == 11 and loc_dt.minute == 45:
    #        print('EMA5' + '\n' +  '%s' % spy_ema5 )
            
    
    record(ema5 = spy_ema5[-1], ema10 = spy_ema10[-1], ema20 = spy_ema20[-1] ) 
    #capital = capital_invested(context, data)
    #record(capital_invested = capital)
    #cash = context.portfolio.cash
    #record(cash = cash)    
    #plot positions value
    #positions_value = context.portfolio.positions_value
    #record(positions_value = positions_value)
    #log.info("ALL EMAS, EMA5= {0}, EMA10= {1}, EMA20= {2} EMA5= {3}, EMA10= {4}, EMA20= {5}, Invested = {6}".format( spy_ema5[-1], spy_ema10[-1], spy_ema20[-1], spy_ema5[-2], spy_ema10[-2], spy_ema20[-2],context.invested ))      
    
     
    if spy_ema5[-1] > spy_ema20[-1] and spy_ema10[-1] > spy_ema20[-1] and (spy_ema10[-2]<=spy_ema20[-1] or spy_ema5[-2]<=spy_ema20[-1] ) :             
        if context.longshort == 'short' and context.shortenabled:      
           order_target_percent(context.sh,0)
           context.longshort = 'none'
           return
            
        if context.longshort == 'none':      
           order_target_percent(context.sds,-0.99)
           log.info("Bot SPY  EMA5  = {0}, EMA10 = {1},  EMA20 = {2}".format( spy_ema5[-1], spy_ema10[-1], spy_ema20[-1]))
           context.longshort = 'long'
           updatelastbought(context)
           
                
    elif spy_ema5[-1]<spy_ema20[-1] and spy_ema10[-1]<spy_ema20[-1] and  (spy_ema10[-2]>=spy_ema20[-1] or spy_ema5[-2]>=spy_ema20[-1]  )  :  
        if context.longshort == 'long':      
           order_target_percent(context.sds,0)
           context.longshort = 'none'
           return
         
        if context.longshort == 'none' and context.shortenabled:      
           order_target_percent(context.sh,0.99)
           log.info("Bot SPY  EMA5  = {0}, EMA10 = {1},  EMA20 = {2}".format( spy_ema5[-1], spy_ema10[-1], spy_ema20[-1]))
           context.longshort = 'short'
           updatelastbought(context)
    
    else:
           return
    
    
def capital_invested(context, data):
    # get a sum total of capital spent or borrowed for all current positions
    capital = 0.0  # initialize to zero
    # check every stock in current positions (also works with set_universe)
    for stock in context.portfolio.positions:        
        # get amount of shares in current position for this stock
        amount = context.portfolio.positions[stock].amount        
        # get the cost basis of the shares (how much we spent on average per share)
        cost_basis = context.portfolio.positions[stock].cost_basis
        # check if position is a short trade (negative amount)
        amount = max(amount, -amount) # change amount to a positive number                
        # add dollar amount to the 'spent' total
        capital += amount * cost_basis    
    # return amount of capital tied up in positions
    return capital
           
         
####################################################################################################################################    
def has_orders(context):
    #Return true if there are pending orders.
    #has_orders = False
    #orders = get_open_orders()
    #if orders:
    #   has_orders = True
    
    has_orders = False
    for sec in context.secs:
        orders = get_open_orders(sec)
        if orders:
            log.info("has order")
            for oo in orders:                  
                message = 'Open order for {amount} shares in {stock}'  
                message = message.format(amount=oo.amount, stock=sec)  
                log.info(message)
                
            has_orders = True
    return has_orders   
  

  
    
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.
17 responses

Hello Quantopian community,
Could somebody provide any critique to improve anything with this algorithm?

I would find out from your broker exactly what the rates are to short SDS and incorporate them into your transaction costs.

@Simon

Thanks. That is a good suggestion. Currently, IB lends it for approx 1.5%

Nice, that's not bad at all.

Instead of shorting SDS, why not go long SSO (ultra S&P). You will save the borrow cost.

There appears to be a bug in your "intradingwindow_check" function. As its written now, the function will only return True when the loc_dt hour is >= 10 AND loc_dt minute is == 45 therefore the function will only return True at times 10:45, 11:45, 12:45, 1:45 etc...., What I think you want there (I could be wrong though, perhaps you only want your strategy to execute once per hour) is for the if statement to say "if loc_dt.hour >= 10 or (loc_dt.hour == 9 and loc_dt.minute >= 45):" This version of the if statement should begin executing at 9:45AM which is what I think you want. Also, I am relatively new to the Quantopian community, but I would think that checking the time to make sure its in the market window wouldn't be necessary because the Quantopian backtester only feeds bars that are within the normal trading hours anyways? Or is that not the case? Does Quantopian feed premarket or postmarket data at all?

That's correct, Quantopian only feeds trading bars within the market hours. We don't use pre-market or post-market data

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.

@John, Thanks for your comments. Algorithm execution at every one hour has been designed deliberately. This works together with
if has_orders(context): return ::: This algorithm returns if there are any open orders. ( I don't wish to place another order if the existing order is outstanding)
if lastboughttoday(context): return ::: This block ensures that I do not place more than one trade in one day.

All of these will also ensure that there will be only one position at any time.

@Cody, thanks for your comments about buying SSO vs. shorting SDS. The reason behind shorting SDS is that leveraged ETFs and inverse or -2x in particular almost always lose a lot more money because of the effect of daily compounding. I attached a the same backtest with going long SSO and it returned about 105% vs. 165% for shorting SDS.

This strategy alone is just one idea that is not meant to sole position of any portfolio. I plan to extend the algorithm further to combine multiple strategies to improve risk adjusted returns.

Thanks for your suggestions.

Clone Algorithm
71
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
import talib
import math
import pandas as pd 
import numpy as np
from pytz import timezone

# Put any initialization logic here.  The context object will be passed to
# the other methods in your algorithm.
def initialize(context):
    context.secs =   [ sid(8554),  # SPY
                       sid(32268), # SH
                       sid(32270), # SSO
                       sid(32382)  # SDS
                       ] # 
    
    context.spy  = sid(8554)
    context.sso = sid(32270)  
    context.sds = sid(32382)
    context.sh = sid(32268)
    context.max_notional =  10000.0  
    context.min_notional = -10000.0
    context.invested = False
    context.shorted = False
    context.lastbuydate = '01011990'
    context.longshort = 'none'
    context.shortenabled = False
    set_commission(commission.PerTrade(cost=0.0))
    set_slippage(slippage.FixedSlippage(spread=0.00))

    
    
def lastboughttoday(context):
    loc_dt = get_datetime().astimezone(timezone('US/Eastern')) 
    todaysdate = str(loc_dt.month)+str(loc_dt.day)+str(loc_dt.year)
    
    if(context.lastbuydate == todaysdate ): 
        return True
    else:
        return False
    
def updatelastbought(context):
    loc_dt = get_datetime().astimezone(timezone('US/Eastern')) 
    context.lastbuydate = str(loc_dt.month)+str(loc_dt.day)+str(loc_dt.year)
    
   

def intradingwindow_check(context):  
    # Converts all time-zones into US EST to avoid confusion  
        loc_dt = get_datetime().astimezone(timezone('US/Eastern'))
        if   loc_dt.hour >= 10 and (loc_dt.minute == 45  ):  
            #log.info("First Timestamp: Year {0}, Month {1}, Day {2} ".format(loc_dt.year, loc_dt.month, loc_dt.day))
            return True 
        else:   
            return False

# Will be called on every trade event for the securities you specify. 
def handle_data(context, data):
    loc_dt = get_datetime().astimezone(timezone('US/Eastern'))
    
    if not intradingwindow_check(context): return
    if has_orders(context): return
    if lastboughttoday(context): return
      
    # data[sid(X)] holds the trade event data for that security.    
    
    
    close_price =history(bar_count=25, frequency='1d',field='close_price')
    open_price = history(bar_count=25, frequency='1d',field='open_price')
    high_price = history(bar_count=25, frequency='1d',field='high')
    low_price =  history(bar_count=25, frequency='1d',field='low')    
    concat_price = pd.concat((open_price, close_price,high_price, low_price))
    mean_price =  concat_price.groupby(concat_price.index).mean()
    
    spy_series    = mean_price[context.spy]
    spy_ema5      = talib.EMA(spy_series, timeperiod=5)
    spy_ema10     = talib.EMA(spy_series, timeperiod=10)
    spy_ema20     = talib.EMA(spy_series, timeperiod=20)
    
    # [0:-1] above remove the last entry    
   
    #if loc_dt.hour == 11 and loc_dt.minute == 45:
    #        print('EMA5' + '\n' +  '%s' % spy_ema5 )
            
    
    record(ema5 = spy_ema5[-1], ema10 = spy_ema10[-1], ema20 = spy_ema20[-1] ) 
    #capital = capital_invested(context, data)
    #record(capital_invested = capital)
    #cash = context.portfolio.cash
    #record(cash = cash)    
    #plot positions value
    #positions_value = context.portfolio.positions_value
    #record(positions_value = positions_value)
    #log.info("ALL EMAS, EMA5= {0}, EMA10= {1}, EMA20= {2} EMA5= {3}, EMA10= {4}, EMA20= {5}, Invested = {6}".format( spy_ema5[-1], spy_ema10[-1], spy_ema20[-1], spy_ema5[-2], spy_ema10[-2], spy_ema20[-2],context.invested ))      
    
     
    if spy_ema5[-1] > spy_ema20[-1] and spy_ema10[-1] > spy_ema20[-1] and (spy_ema10[-2]<=spy_ema20[-1] or spy_ema5[-2]<=spy_ema20[-1] ) :             
        if context.longshort == 'short' and context.shortenabled:      
           order_target_percent(context.sh,0)
           context.longshort = 'none'
           return
            
        if context.longshort == 'none':      
           order_target_percent(context.sso,0.99)
           log.info("Bot SPY  EMA5  = {0}, EMA10 = {1},  EMA20 = {2}".format( spy_ema5[-1], spy_ema10[-1], spy_ema20[-1]))
           context.longshort = 'long'
           updatelastbought(context)
           
                
    elif spy_ema5[-1]<spy_ema20[-1] and spy_ema10[-1]<spy_ema20[-1] and  (spy_ema10[-2]>=spy_ema20[-1] or spy_ema5[-2]>=spy_ema20[-1]  )  :  
        if context.longshort == 'long':      
           order_target_percent(context.sso,0)
           context.longshort = 'none'
           return
         
        if context.longshort == 'none' and context.shortenabled:      
           order_target_percent(context.sh,0.99)
           log.info("Bot SPY  EMA5  = {0}, EMA10 = {1},  EMA20 = {2}".format( spy_ema5[-1], spy_ema10[-1], spy_ema20[-1]))
           context.longshort = 'short'
           updatelastbought(context)
    
    else:
           return
    
    
def capital_invested(context, data):
    # get a sum total of capital spent or borrowed for all current positions
    capital = 0.0  # initialize to zero
    # check every stock in current positions (also works with set_universe)
    for stock in context.portfolio.positions:        
        # get amount of shares in current position for this stock
        amount = context.portfolio.positions[stock].amount        
        # get the cost basis of the shares (how much we spent on average per share)
        cost_basis = context.portfolio.positions[stock].cost_basis
        # check if position is a short trade (negative amount)
        amount = max(amount, -amount) # change amount to a positive number                
        # add dollar amount to the 'spent' total
        capital += amount * cost_basis    
    # return amount of capital tied up in positions
    return capital
           
         
####################################################################################################################################    
def has_orders(context):
    #Return true if there are pending orders.
    #has_orders = False
    #orders = get_open_orders()
    #if orders:
    #   has_orders = True
    
    has_orders = False
    for sec in context.secs:
        orders = get_open_orders(sec)
        if orders:
            log.info("has order")
            for oo in orders:                  
                message = 'Open order for {amount} shares in {stock}'  
                message = message.format(amount=oo.amount, stock=sec)  
                log.info(message)
                
            has_orders = True
    return has_orders   
  
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.

@John, To further elaborate on the need to run this algorithm multiple times is because let us say, I stop and redeploy during trading hours, or if the order with the broker gets rejected etc. and more importantly my moving average threshold might not be crossed at lets say 10.45 AM but they may be crossed by 2.45 PM ( I am averaging out open, last, and close prices but still the moving averages can cross each other at any time)
Hope this helps.

@Dev, I understand the need to run it multiple times a day, but why not run it minutely instead of hourly? The checks for has_orders(context): return and lastboughttoday(context): return will ensure that you never place more than one order per day and that you don't place any orders if there are open orders unsettled, but the time frame check seems unnecessary and detrimental to the algorithm to me.

@John, I agree with you John on that. I think another reason I added that was to speed up the backtesting time but they are not needed for a live algo.

this will sting and bit with the margin reqs and borrow fees(http://ibkb.interactivebrokers.com/article/1124). They know these products decay and price the accordingly. You end up having to reserve too much capital (i.e will need 50k to short 25k of a leveraged). I would try something that went long or short based on market conditions.

For example take 2013. use the algo you have to go long SSO - i.e. buying dip all 2013 because it was a bull market. You could use a test by having the 50 above to 200 day = bull market. Then for times its bear you do the exact the sam thing expect you change the security to SDS. The thing is bear market are quicker down than bull markets are up - so you might want to us faster moving averages. Just my two cents - like the algo

Hi, Dev: try to run your code for a short time (i.e., after 2008-09 crash). After cloned the code, got an building error: Execution time out. no line number for the error. any idea what could be wrong in my process.

something is up with the site. I've ran this previously and its worked fine

Thanks, Jonathan, for your quick response. I will just wait till Monday to see if the error clears itself

The backtesting system was temporarily experiencing a technical problem. We've resolved it and you should now be able to successfully build your algorithms. Sorry for the inconvenience!

@Dev Bhosale if SDS is a leverage etf plus the effect of daily compounding then shorting it will give above average returns right? how come I'm getting this results ... ?

Clone Algorithm
2
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
import talib
import math
import pandas as pd 
import numpy as np
from pytz import timezone

# Put any initialization logic here.  The context object will be passed to
# the other methods in your algorithm.
def initialize(context):
    context.secs =   [ sid(8554),  # SPY
                       sid(32268), # SH
                       sid(32270), # SSO
                       sid(32382)  # SDS
                       ] # 
    
    context.spy  = sid(8554)
    context.sso = sid(32270)  
    context.sds = sid(32382)
    context.sh = sid(32268)
    context.max_notional =  10000.0  
    context.min_notional = -10000.0
    context.invested = False
    context.shorted = False
    context.lastbuydate = '01011990'
    context.longshort = 'none'
    context.shortenabled = False
    set_commission(commission.PerTrade(cost=0.0))
    set_slippage(slippage.FixedSlippage(spread=0.00))

    
    
def lastboughttoday(context):
    loc_dt = get_datetime().astimezone(timezone('US/Eastern')) 
    todaysdate = str(loc_dt.month)+str(loc_dt.day)+str(loc_dt.year)
    
    if(context.lastbuydate == todaysdate ): 
        return True
    else:
        return False
    
def updatelastbought(context):
    loc_dt = get_datetime().astimezone(timezone('US/Eastern')) 
    context.lastbuydate = str(loc_dt.month)+str(loc_dt.day)+str(loc_dt.year)
    
   

def intradingwindow_check(context):  
    # Converts all time-zones into US EST to avoid confusion  
        loc_dt = get_datetime().astimezone(timezone('US/Eastern'))
        if   loc_dt.hour >= 10 and (loc_dt.minute == 45  ):  
            #log.info("First Timestamp: Year {0}, Month {1}, Day {2} ".format(loc_dt.year, loc_dt.month, loc_dt.day))
            return True 
        else:   
            return False

# Will be called on every trade event for the securities you specify. 
def handle_data(context, data):
    loc_dt = get_datetime().astimezone(timezone('US/Eastern'))
    
    if not intradingwindow_check(context): return
    if has_orders(context): return
    if lastboughttoday(context): return
      
    # data[sid(X)] holds the trade event data for that security.    
    
    
    close_price =history(bar_count=25, frequency='1d',field='close_price')
    open_price = history(bar_count=25, frequency='1d',field='open_price')
    high_price = history(bar_count=25, frequency='1d',field='high')
    low_price =  history(bar_count=25, frequency='1d',field='low')    
    concat_price = pd.concat((open_price, close_price,high_price, low_price))
    mean_price =  concat_price.groupby(concat_price.index).mean()
    
    spy_series    = mean_price[context.spy]
    spy_ema5      = talib.EMA(spy_series, timeperiod=5)
    spy_ema10     = talib.EMA(spy_series, timeperiod=10)
    spy_ema20     = talib.EMA(spy_series, timeperiod=20)
    
    # [0:-1] above remove the last entry    
   
    #if loc_dt.hour == 11 and loc_dt.minute == 45:
    #        print('EMA5' + '\n' +  '%s' % spy_ema5 )
            
    
    record(ema5 = spy_ema5[-1], ema10 = spy_ema10[-1], ema20 = spy_ema20[-1] ) 
    #capital = capital_invested(context, data)
    #record(capital_invested = capital)
    #cash = context.portfolio.cash
    #record(cash = cash)    
    #plot positions value
    #positions_value = context.portfolio.positions_value
    #record(positions_value = positions_value)
    #log.info("ALL EMAS, EMA5= {0}, EMA10= {1}, EMA20= {2} EMA5= {3}, EMA10= {4}, EMA20= {5}, Invested = {6}".format( spy_ema5[-1], spy_ema10[-1], spy_ema20[-1], spy_ema5[-2], spy_ema10[-2], spy_ema20[-2],context.invested ))      
    
     
    if spy_ema5[-1] > spy_ema20[-1] and spy_ema10[-1] > spy_ema20[-1] and (spy_ema10[-2]<=spy_ema20[-1] or spy_ema5[-2]<=spy_ema20[-1] ) :             
        if context.longshort == 'short' and context.shortenabled:      
           order_target_percent(context.sh,0)
           context.longshort = 'none'
           return
            
        if context.longshort == 'none':      
           order_target_percent(context.sds,-0.99)
           log.info("Bot SPY  EMA5  = {0}, EMA10 = {1},  EMA20 = {2}".format( spy_ema5[-1], spy_ema10[-1], spy_ema20[-1]))
           context.longshort = 'long'
           updatelastbought(context)
           
                
    elif spy_ema5[-1]<spy_ema20[-1] and spy_ema10[-1]<spy_ema20[-1] and  (spy_ema10[-2]>=spy_ema20[-1] or spy_ema5[-2]>=spy_ema20[-1]  )  :  
        if context.longshort == 'long':      
           order_target_percent(context.sds,0)
           context.longshort = 'none'
           return
         
        if context.longshort == 'none' and context.shortenabled:      
           order_target_percent(context.sh,0.99)
           log.info("Bot SPY  EMA5  = {0}, EMA10 = {1},  EMA20 = {2}".format( spy_ema5[-1], spy_ema10[-1], spy_ema20[-1]))
           context.longshort = 'short'
           updatelastbought(context)
    
    else:
           return
    
    
def capital_invested(context, data):
    # get a sum total of capital spent or borrowed for all current positions
    capital = 0.0  # initialize to zero
    # check every stock in current positions (also works with set_universe)
    for stock in context.portfolio.positions:        
        # get amount of shares in current position for this stock
        amount = context.portfolio.positions[stock].amount        
        # get the cost basis of the shares (how much we spent on average per share)
        cost_basis = context.portfolio.positions[stock].cost_basis
        # check if position is a short trade (negative amount)
        amount = max(amount, -amount) # change amount to a positive number                
        # add dollar amount to the 'spent' total
        capital += amount * cost_basis    
    # return amount of capital tied up in positions
    return capital
           
         
####################################################################################################################################    
def has_orders(context):
    #Return true if there are pending orders.
    #has_orders = False
    #orders = get_open_orders()
    #if orders:
    #   has_orders = True
    
    has_orders = False
    for sec in context.secs:
        orders = get_open_orders(sec)
        if orders:
            log.info("has order")
            for oo in orders:                  
                message = 'Open order for {amount} shares in {stock}'  
                message = message.format(amount=oo.amount, stock=sec)  
                log.info(message)
                
            has_orders = True
    return has_orders   
  

  
    
There was a runtime error.