Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
SMA Crossover trading signal

Help on the coding. I tried to use SMA crossover to trade SPY. It is a short only trading rule as
Short: SMA(20min) crosses down SMA(50min) and SMA(2day) is below SMA(3day)
cover: SMA(20min) crosses over SMA(50m).
The following code does not work. please advise. thank you. Jeff

******************************************************************************8

def initialize(context):
context.stock = sid(8554)

def handle_data(context, data):
context.current_price = data[context.stock].price
# short_mavg_25 = data[context.stock].mavg(25)
# short_mavg_60 = data[context.stock].mavg(60)
# long_mavg_750 = data[context.stock].mavg(300)

# long_mavg_500 = data[context.stock].mavg(200)
# mavg_100 = data[context.stock].mavg(100)

historical_data = history(5, '1d', 'price')  
last_2_day_mean = historical_data.tail(2).mean()  
last_3_day_mean = historical_data.tail(5).mean()  
historical_data2 = history(50, '1m', 'price')  
last_20_min_mean = historical_data2.tail(20).mean()  
last_50_min_mean = historical_data2.tail(50).mean()  

if  last_20_min_mean < last_100_min_mean and last_2_day_mean <= last_3_day_mean :  
    order_target_percent(context.stock, -1)  
elif last_100_min_mean < last_20_min_mean :  
    order_target_percent(context.stock, 0.0)

record(   stock_price = context.current_price)  
15 responses

Hey Jeff,

Without knowing the exact reason why its not working I can't advise you much, I can tell you a few things by examination of the code you did post.

  1. Since you are requesting minute-level history, and it trades on the basis of that history you need to make sure you are backtesting in minute mode.
  2. Another thing I can see is that you never define last_100_min_mean so that is another reason why your code will raise an exception.

Make sure to examine the sidebar on the right of the IDE for Build and Runtime Errors as they will often tell you why your code broke. Then you can fix it yourself and get back to coding your algo versus waiting for someone in the forums to help out. We also have a built in debugger that can be super helpful if you are really having hard time finding where the mistake is.

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.

James,
thank you for the inputs, I changed the code to "last_50_min_mean". the error message is

27 Error Runtime exception: ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

and it refers the following code:

if last_20_min_mean < last_50_min_mean and last_2_day_mean <= last_3_day_mean :
I believe it is because it uses both min and day in the code? how to fix it?

So the Error is what is displayed but the message can be a bit confusing. The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all(). This is a type issue that you can run into with Pandas, the data manipulation library. Pandas has 3 main data strucutres: Panel, DataFrame, Series. A Panel you can think about as a 3-D matrix. A DataFrame is 2-D datastrcture, and a Series is, you guessed it, 1-D structure. The history() function returns a DataFrame keyed by Security, so if you want to do operations on a single securities time series from it you have to call it by its key. For example,

security_history = history(10, "1d", "price")[security]  

However, Pandas supports vectorized operations so you don't have to loop through a bunch of securities. So when you take the mean in your code you are applying the mean function to every security in the history DataFrame. Since the mean in a reducing operation you now have a DataFrame that contains a Series of one value (the mean of that Series), for every security.

Finally, to get that scalar value of that one security you need to call on that Security like I showed in the snippet above. So in your example to get the average value of SPY for the past 2 days. You can do this...

historical_data = history(5, '1d', 'price')  
last_2_day_mean = historical_data.tail(2).mean()  
last_2_day_mean_for_SPY = last_2_day_mean[context.stock]  

I was able to run the following code on Daily mode. however, the result executed under the minute model is dramatically different from Daily mode.
Why?

balance = 1
def initialize(context):
context.stock = sid(38054)
context.dayCount = 0

def handle_data(context, data):
context.current_price = data[context.stock].price

historical_data = history(10, '1d', 'price')[context.stock]  
last_2_day_mean = historical_data.tail(2).mean()  
last_3_day_mean = historical_data.tail(3).mean()  
last_5_day_mean = historical_data.tail(5).mean()  
#historical_data2 = history(100, '1m', 'price')[context.stock]  
#last_20_min_mean = historical_data2.tail(20).mean()  
#last_50_min_mean = historical_data2.tail(50).mean()  
#last_100_min_mean = historical_data2.tail(100).mean()  
#    prev_bar = price_history[s][-2]  
#    curr_bar = price_history[s][-1]  
#current_position <=0  
current_position =     context.portfolio.positions[context.stock].amount  
current_position_value = context.portfolio.positions_value  
if context.dayCount%balance == 0:  
    if  last_2_day_mean < last_3_day_mean and last_2_day_mean <= last_5_day_mean :  
        order_target_value(context.stock, -100000)  
            #             order_target_percent(stocks[i], proportion[i])  
    if last_3_day_mean < last_2_day_mean :  
        order_target_value(context.stock, -10000)    #             order_target_percent(stocks[i], proportion[i])  
    if current_position_value < -105000:  
        order_target_value(context.stock, 0.0)  
            #order_target_percent(stocks[i], proportion2[i])  

context.dayCount += 1  

record(  mean2=last_2_day_mean,mean5=last_5_day_mean, stock_price = context.current_price)  
log.info(str(data[context.stock].price))  

My first recommendation is to use "schedule_function" to run your code once a day. Since you just put it in "handle_data", it was run every minute the market was open!

I wasn't quite sure what your logic is, so I guessed and cleaned up a few things. Let us know if you still have questions!

Tristan

thank you,Tristan! More help is needed.
Although running code once a day is a good suggestion, however, the original logic of the algorithm is to Short VXX at 5min time frame.
Since it is not option to choose 5min, all the parameters are already adjusted to be under 1min time frame as:

Short if all criteria are met:
1. SMA_100min < SMA_500min
2. SMA_500min is moving down
3. SMA_25min is crossing down from SMA_60min; that means, in the prior bar, SAM_25min >= SMA_60min
and at the current bar, SMA_25min <SMA_60min

Cover if any of three conditions happen:
1. Price > SMA_500min
2. SMA_25min crossover SMA_500min
3. Stop Loss at -10% loss from the entry

Jeff, I decided to give your algo some more love. (But I couldn't understand what you mean by Cover #2?)

It is an interesting strategy. This is the first time I have used minute pricing data.

the result executed under the minute model is dramatically different from Daily mode.

This is because in daily mode you get data once per day, whereas in minute mode you get fresh price data every minute. Take a look at this thread for a more detailed explanation: https://www.quantopian.com/posts/differences-between-minute-and-daily-backtests

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.

Tristan,
the cover #2 is to exit the short whenever VIX has significantly peaked up. that is why is SMA25 vs SMA500.
I have done the testing under different IDE, I can attach those testing to show its impact in a different note.

Actually, you can see it from your testing over Aug2011 period where VXX goes up more than tripled, the strategy holds well over that period

Just to clarify #2 means that SMA_25min has risen above SMA_500min? Because I think that could never happen before price goes above SMA_500min (stop loss scenario #1). I went ahead and added stop loss #2 into this algo, and ran another backtest. Based on the logs, stop loss #2 only happens the same time that #1 happens, which is what I predicted.

oh, I C. You are right, I checked the original algorithm, the cover #1 should be
1. Price > SMA_500min and Price has gone up for at least 2 consecutive days.

How do you think we can implement your updated #1? If you can describe the logic better, I can try to code it.

first of all, I made few modifications and then backtesting the code
and I realize few things need to be changed
1. the order execution: under the 1min, the short or cover order may not be executed at once. so you add the following code. but
it breaks the rule because it may need to cover the short instead of filling the incomplete order

open_orders = get_open_orders()  
if context.stock in open_orders:  
    pass  
  1. testing on $10,000 gives more reasonable results.
  2. because of that , I wonder how to fix the problem. possible solution is:
    a. use the following few mins to fill the order
    or
    b. discard the order

first of all, I made few modifications and then backtesting the code
and I realize few things need to be changed
1. the order execution: under the 1min, the short or cover order may not be executed at once. so you add the following code. but
it breaks the rule because it may need to cover the short instead of filling the incomplete order

# Don't do anything if there are open orders  
open_orders = get_open_orders()  
if context.stock in open_orders:  
    pass
  1. testing on $10,000 gives more reasonable results.
    because of that , I wonder how to fix the problem. possible solution is:
    a. use the following few mins to fill the order
    or
    b. discard the order

Tristan,
can we force no more trade on the same day if a recover order is in place.

Jeff