Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Mean Reversion Strategy with GLD & USO

Hi Guys,

I use this mean reversion strategy to trade GLD and USO, because some test data find that the two is stationary. But i get the result that final return is more than 60%. I think some of my code is not correct, would someone help find what's wrong in my code?

7 responses

Hi Qingwei,

We took a look at this algo for you. I'm not sure if I'm able to say that your code is 'correct' or not without a little more context on what your intended behavior is. That said, my first suggestion would be to use the record feature to look at your exposures over time. Just add this line in your handle_data method:

record(cash=context.portfolio.cash, gld=context.portfolio.positions[context.gld].amount * data[context.gld].price,
uso=context.portfolio.positions[context.uso].amount * data[context.uso].price)

You'll see that your algo is not maintaining a constant allocation (in dollar terms) across GLD, USO and Cash.

If you can share a bit more about the intended behavior of your algo I'd be happy to help out further with making sure it's working correctly.

Best,
Jess

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 Jessica,

Thanks for your help!

The main idea of this algo is using a linear mean reversion strategy for the portfolio(GLD&USO).
I dynamically calculate the hedge ration between USO and GLD, so the price of this portfolio is "USO-hedgeRatio*GLD".
The hedge ration is based on the price of the past ten days(using leastsq in scipy.optimize).

When trading, the the number of units (shares) of the unit portfolio i should own is set to be the negative Z-Score.
That is if the price is larger that the moving_avg, i should short, otherwise i should long. It's what line 58 mean.
And how much i should long or short GLD or USO is set in line 60 and line 61.

Hope it can a little more clear now :), thanks again!
I'll look into what record() gives tomorrow.

Qingwei,

I don't typically post, but seems like you're fairly interested in learning so I took a look at your code really quick..

A few notes:

(1) "When trading, the the number of units (shares) of the unit portfolio i should own is set to be the negative Z-Score."

Your code:
num_units = -(price - ref[0]) / ref[1]

gld_positions = - num_units * ref[2]
uso_positions = num_units

order(context.gld, gld_positions * 100)
order(context.uso, uso_positions * 100)

Suppose you see the following z-scores: (Day1 : 0, Day2 : 2.0, Day3 : 2.0) and to make things simple let's just say your regression coefficient remains 1.0 for all days and you will get fully filled on your orders. In your current code, your order sizes are (0,0), (-200,200), (-200,200) which means by day 3 your position sizes will be (-400, 400). If I understand what you said correctly you want your position size to be (-200, 200) on Day3.

(2) You neither have slippage or broker commission defined. Now since this is a long/short strategy, you also need to know what your borrow costs on the short position. Transaction costs will often kill the majority of strategies, beware.

(3) I haven't investigated quantopian's system enough to actually figure out how they're handling this for backtests using daily data. However, as a word of caution, your calculations are all done on closing prices then you send an order out - see the problem there? Let's suppose two scenarios of possible ways this issue has been addressed: (a) the price you're doing your calculations are are for the open of 15:59-16:00 eastern and the fill that's being reported is the close (i.e. final print) - is the liquidity there to even execute on at that price? (b) your calculation is on the last print but your fill price is the closing auction price, well the only way you can actually get the closing auction price is via MOC which needs to be sent out at by 15:45. It's entirely possible that what you're catching is excess volatility in the close that's untradable.

Some possible fixes: (a) calculate on 15:00, then execute on VWAP of last hour and (b) calculate on 15:45, send MOC but you need to assume a higher market impact. there are a few other things you could look at but I'll let you figure those out.

Good luck,

-A

Hi al t,

Thanks a lot for pointing out the 3 issues with details!

(1) Yes, it's how i set the position size, i.e. num_units = -(price - moving_avg) / moving_std. Depending on positive or negative, i short or long. (2) I don't have the slippage or broker commission defined, i'll refine it soon. (3) The price issue is exactly what i was confused about when writing this algo. Quantopian' system provides price data on minute bar and day bar and it also gives several kinds of prices.
When i was writing this, i just used closing prices to calculate the ratio for simplicity. It seems not correct and impractical.
I'll go back to do some modification as what you've advised, then i would re-run the back test and see what it gives.

By the way, this algo is from Ernest P. Chan > Chapter 3. I think the idea is good and typical, so i use this system to test its performance. And it gives a different result from what's in the book, so i start to look into what's wrong here.

Thanks again!:)

The book is "Algorithmic Trading_Winning Strategies and Their Rationale".

Qingwei,

I'll refrain from commenting on this specific strategy, but I think a general rule of thumb is that if it's been published and read by a wide audience; the alpha is gone. However, it sounds like you're running this for educational purposes which is great.

Unless you have the author's data and original code, it'll be difficult to replicate his exact results. Another issue to consider is that if you're keeping things in returns space, your actual return and sharpe ratio may differ vastly as well due issues pertaining to leverage.

More on trading costs:

Once you have the other issues worked out, you'll want to generate multiple equity curves for different transaction cost conditions. GLD and USO are relatively liquid, I think they both trade about a penny wide. In any event, you probably want to include different slippage conditions ranging from .05-.015 off mid per trade/share.

Don't forget to adjust for borrow costs on the short positions, I could certainly see USO getting fairly high up there due to term structure dynamics of the futures (it's ~200 bps today).

Even with the minute data, you may want to lag your execution to a minute after you've generated your trading signal just to be conservative.

Suppose you did everything right in your backtests (e.g. no data snooping, overfitting, or lookahead bias), yet you're losing money trading live. I've found often it is the case that you've underestimated trading costs in one way or the other. If you don't have the data available, it's always better to be more conservative with your trading costs and overestimate them.

Best,

-A

Hi al t,

I agree with you. This specific strategy is that practical. But it surely gives me some idea bout what kind of pairs can be used in a portfolio, how to co-integrate them, etc,.

Anyway, you really gives me some idea bout what a real trade look like and what i should really take seriously when building an algo (trading costs). It's really helpful.

I know trading costs is very important in an algo, i just ignored it to verify this trading idea when writing this. I'll take that into account when refining this and building new algo. Not sure quantopian's system can provide a daily margin(spread), guess not, i would set it to 200 bps or more.

Thanks again for your help! :) By the way, 200 bps is really high! I guess with this taking into account, this strategy wouldn't beat the benchmark. :)

Best,
Qingwei