Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
What am I doing wrong? (SPY 50/200MA with short component)

I'm a noob. This code doesn't appear to deliver the desired result, what am I doing wrong?

# simple 50/200 MA strategy, all-in long when bullish and all-in short when bearish

def initialize(context):  
    context.etf_long = sid(8554)  
    context.etf_short = sid(32268)  
    context.price_long={}  
    context.price_short={}  
    context.invested_long = False  
    context.invested_short = False  
def handle_data(context, data):  
    price_long = data[context.etf_long].price  
    price_short = data[context.etf_short].price  
    fastMA = data[context.etf_long].mavg(50)  
    slowMA = data[context.etf_long].mavg(200)  
    if (fastMA > slowMA ):  
        if (context.invested_short):  
            log.info("liquidating short")  
            order(context.etf_short,-(context.portfolio.positions[context.etf_short].amount))  
            context.invested_short = False  
        if (not context.invested_long):  
            log.info("going long")  
            order(context.etf_long , round(context.portfolio.cash/price_long))  
            context.invested_long = True  
    else:  
        if (context.invested_long):  
            log.info("liquidating long")  
            order(context.etf_long,-(context.portfolio.positions[context.etf_long].amount))  
            context.invested_long = False  
        if (not context.invested_short):  
            log.info("going short")  
            order(context.etf_short , round(context.portfolio.cash/price_short))  
            context.invested_short = True  
17 responses

Hi, As i have understood it, you only need to use two if statements to make it flip between long/short. Make sure you include portfolio part in your code

Also, do note that doing backtests with built in moving average function mavg[value) and in the Daily mode is not accurate and will produce unrealistic results, both good and bad (i have not yet understood why that is - perhaps an elder here can pitch in and make this clear to the rest of us). Search the forum and help docs for talib stuff and use that in minute mode if you want your stuff to be more realistic.

position = context.portfolio.postions[context.stock].amount

# if number of shares you have is eq. or less that 0, go 100% long  
if current_item_price > your_average and position <= 0:  
order_target_percent(context.stock, 1)  
# if num. of share eq. or more than zero, go 100% short  
if current_item_price < your_average and position >= 0:  
order_target_percent(context.stock, -1)  

Darell, could you clarify what you mean by the mavg in daily mode not displaying accurate values?

In backtesting, moving average will begin to display the 'true' value once the window is filled. For example here is how MAVG(3) is reported:

Day 1: report's the first day price
Day 2: report the average of the 2 day price
Day 3: report the average of the 3 day price. At the end of day 3, the moving average is now "filled" and will act as a true 3 day moving average
Day 4: true moving average of days 1-3
Day 5: true moving average of days 2-4

In live trading, we 'warm-up' the algorithms prior to market open and they have the complete information immediately available. If you were deploy a live algorithm today with a 3 day moving average, it will pull the historical data to correctly display the 3 day mavg.

Note, in backtesting, if you don't want to wait for the window to be filled, you can use the history function with the rolling mean. For example:

#This will return the last 3 days price data plus the last minute's price.  
prices = history(4, '1d', 'price')

#Get only the 3 day price history.  
my_prices = prices[-3:-1]

#Calculate the 3 day mavg  
mavg = my_prices.mean()  
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.

Hi and thanks for getting back. I have run the built in mavg() and talib.SMA side by side, because having a strategy with the built in mavg produces very different result than using talib.SMA for the same timeperiod, i am trying to understand why the mavg() is so different in results to talib.SMA. (I assume talib.SMA is correct, since i checked the signal dates correspondence to the actual SMA in a charting program)

Please explain why the difference between the two methods in the linked code file where both mavg and talib are side by side

Result with mavg (Daily mode backtest):
https://www.dropbox.com/s/7ddcw6vppep5dd0/Sma15Daily.png

Result with talib.SMA (Minute mode backtest):
https://www.dropbox.com/s/vldv9y3vdtml3w4/TalibSma15Minute.png

Thanks

@Darell: Cheers for your feedback, much appreciated. Im afraid I dont get what you mean with the IF statements. Initially there are no positions. Once positions have been taken they must be liquidated and changed at new signals. To me the IF statements make sense. If possible Id very much appreciate if you could integrate your IF statements into the code I pasted to clear things up.

@Fredrik, the mavg() function won't return the true moving average until the window is filled. Since you had a 200-day moving average, this variable will show the correct value on day 201. To fix this, I created a trailing window of 200 days using history and used the pandas mean function to take the average.

I also recorded the fast and slow moving averages, to get a visual sense of the data. In the 8 month backtest window, the lines do not cross, so no orders were triggered. Do you have any specific questions about the code or strategy?

@Darell, could you invite me to those algorithms via the collaborate button in the IDE? My email is [email protected] and I'd be curious to take a closer look at the code to see what's driving the differences.

I plotted the moving averages from the mavg function, talib, and pandas side by side for the 50 day moving averages. You can see that the mavg function gives the wrong value until the algo has traded 50 days, but it does converge onto the other values (but still with a small rounding error sometimes).

The mavg function is not recommended for live trading because of this, it was one of the earlier features built, and has stuck around because it can be convenient for quick tests in daily mode. Using pandas or talib is the way to go.

@Darell, the results from those algos using different methods are different, because one of them uses daily data, and the other uses minute data. The most current bar is always the most recent price in the moving average. In minute mode, the averages could cross at any time through the day, or several times, while in daily, they can only cross at the close prices. The results from different frequencies should be in the same ballpark, but they shouldn't be identical.

David

@David, Completley agree that they should be at least in the same ballpark, but they are not - hence my question, ill try to rephraze it simpler since its probably a very very simple fix:
Q: How to use history ("1d") to use Only Daily data, with the aim on having the algo in Minute mode resemble at least to some degree the result of the Daily backest with mavg().
Meaning: whatever happens intraday is of no interest, all i want is the algo to operate on a Daily open and close price basis (BUT while staying in minute mode, since then i can use history, talib and the other good stuff)

The moving average values with mavg, talib and pandas are close to identical - that is not the issue im highlighting

Daily mode code with mavg:

def initialize(context):  
    context.security = symbol('VXX')

def handle_data(context, data):  


    average_price = data[context.security].mavg(15)  
    current_price = data[context.security].price  
    position = context.portfolio.positions[context.security].amount  
    if current_price < average_price and position >= 0:

        order_target_percent(context.security, -1)  
        log.info("SELL %s" % (context.security.symbol))  


    if current_price > average_price and position <= 0:  
        order_target_percent(context.security, 0)  
        log.info("COVER %s" % (context.security.symbol))  

    record(stock_price=current_price)  
    record(SMA15=average_price)  

Talib SMA, history and Minute mode (since history requires the use of Minute mode):

import talib

def initialize(context):  
    context.security = symbol('VXX')

def handle_data(context, data):  
    price_history = history(15, "1d", "price")  
    timeseries = price_history[context.security]  
    average_price = talib.SMA(timeseries, timeperiod=15)[-1]  
    current_price = data[context.security].price  
    position = context.portfolio.positions[context.security].amount  
    if current_price < average_price and position >= 0:

        order_target_percent(context.security, -1)  
        log.info("SELL %s" % (context.security.symbol))  


    if current_price > average_price and position <= 0:  
        order_target_percent(context.security, 0)  
        log.info("COVER %s" % (context.security.symbol))  

    record(stock_price=current_price)  
    record(SMA15TALIB=average_price)  

This should get you close, it will only look to trade at the close

@David - it works better now, more close to the Daily backtest, two follow up questions if i may:
1. If im only doing backtest on a daily timeframe, but want to use talib functions, does that mean i have to stay in Minute mode ?
2. Please look at the pic below of comparison of the two, the Daily still beats Minute version, may that be because of how orders are filled ?, meaning on the open next day vs on the close next day. I really would like this cleared up so any backtests i work with are realistic
https://www.dropbox.com/s/uxespedf2i9kkfg/Screen%20Shot%202014-08-13%20at%2000.19.32.png

Thanks for your time

Darell, unfortunately talib can only be used in minute mode, you can use batch_transform still, but it is being depreciated so it's not a great habit. As a rule of thumb, trust the minute data results more than the daily, it is a better simulation of live trading. You should also try the algo out paper trading, preferably on an IB paper account because they have more realistic order filling than the zipline paper trading. Paper trading might help you work some of the kinks out as well.

Thanks David - that was what i was trying to get at/understand, that Daily backtests are unrealistic and Minute mode is the one to use.
Regarding the code you gave, once price closes below the sma - does it execute order on the open or close the next day (and how can i set it via code to do either of the two)

@Darell My understanding about talib vs ta in general:
a) talib ... (as in 'import talib') like talib.SMA() is a third party (non-Quantopian) Python wrapper for TA-LIB. In my opinion by the way its documentation is not useful except as crib note reminders once someone knows it since there are no examples the last I looked. talib might be here forever.
b) ta ... is an in-house (Quantopian) built-in wrapper for the talib library above like ta.SMA() that simplified some inputs and didn't (doesn't) require an import statement. It is to be deprecated (is on the chopping block to be going away at some point) because it was found to be producing inaccurate numbers sometimes.
Both can be used in Daily or Minute mode.
talib requires a numpy array which I believe is what history is already providing.
If one is not currently using history then talib can be used in Daily mode (while gathering a list of prices manually), something like:

import numpy as np  
sma_current_value = talib.SMA(np.array(prices_list[-50:]), timeperiod=50)[-1]  

That [-50:] just says the last 50 on the list, use a variable.
By the way, about ta() again, I still use one thing in ta(), ta.STOCH() because I wasn't able to find a way to make talib.STOCH() work right.

@David I really appreciate your SMA examples above because: When I cloned and added my own sma_calc code like sum(in_list[-window_size:]) / window_size I found that my values were slightly off from the talib and pandas values in your example so I've switched to talib.SMA(), thanks.

@Gary. Thanks, that clears up a lot: what is the variable price_list in your sample code? meaning, what do i feed into the array?

Append the current price. I usually do something like the following (this is coming from an algo of 1600 lines, can be simplified, is edited a bit for these purposes and untested tho hopefully can help):

import numpy as np  
import talib

history_list_size = 201  
sma_fst_wndw      = 44  
sma_slw_wndw      = 88

def initialize(context):  
    set_benchmark(symbol('AAPL))  
    context.stocks = symbols('AAPL', 'XOM')  
    context.books  = {}    # Big dictionary for per symbol info.

def handle_data(context, data):  
    for sid in data:  
        sym = str(sid.symbol)

        if warmup(context, sym, data[sid].price):  
            # Next until history is built up  
            continue

        sma_fst, sma_slw = sma_calc(context, sym)

        [major code here]

def sma_calc(context, sym):  
    prices_list = context.books[sym]['prices_list']  
    sma_fst_val = talib.SMA(  
        np.array(prices_list[-sma_fst_wndw:]),  
        timeperiod = sma_fst_wndw  
    )[-1]  
    sma_slw_val = talib.SMA(  
        np.array(prices_list[-sma_slw_wndw:]),  
        timeperiod = sma_slw_wndw  
    )[-1]  
    return(sma_fst_val, sma_slw_val)

def warmup(context, sym, price):  
    context.books[sym]['prices_list'].append(price)  
    if len(context.books[sym]['prices_list']) <= history_list_size:  
        return 1  
    else:  
        context.books[sym]['prices_list'] = \  
          context.books[sym]['prices_list'][-history_list_size:]  
        return 0

Thanks Gary, ill try to get the code in

For everybody who pitched in - i got the difference of Daily vs. Minute backtests figured out. It was because of the, or lack of warmup data to calculate the ma correctly on the Daily tests. Minute tests with history are correct. There are differences in how the orders are filled, in daily backtests the trade timestamps are at 02:00:00 which does not make sense. Minute backtests i got to wait for the close price before doing anything, which i think is correct. I will post the code in this thread later on, in case anyone else has the same question and for validation by the good folks here, thanks.

Next issue is why talib Sma and Ema have the same value, Wma seems to work fine

@Darell Lask it looks like you should use more than 50 days to calculate the exponencial, try:

    price_history = history(100, "1d", "price")