Back to Community
High Return SPY, SPXL, and EDV

I'm very new to Quantopian, and I'm looking for feedback/edits to my algorithm. I initially cloned it from "SPY & SH, minute data", then edited it heavily.

I'm experiencing a couple problems:

• When I paper trade, the algorithm does not take any positions. It's very possible this is just because it hasn't been trading for long enough, but I would like to know definitively if something is wrong.

• It won't let me backtest farther back than the end of 2008. I would like to see how this algorithm does from the start of the economic downturn.

I'm writing this algorithm with the ultimate goal of deploying it to real money trading with Robinhood. If there are any changes I need to make so it is Robinhood-friendly, please let me know.

Clone Algorithm
36
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: 591cf08d90d24b6196f2cf67
There was a runtime error.
8 responses

I also noticed that I have somehow been getting negative cash, which has caused a bit of (accidental) leveraging.

Just for fun, this one still has the occasional accidental leverage (please notice that there is less though!), but I improved the metrics across the board (except drawdown).

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: 591da63b9d9c2161a3dae07d
There was a runtime error.

I edited it a bit and managed to get rid of the leveraging. The return took a significant hit, but I'm not sure if that's a direct result of the leveraging or something else that I screwed up.

Clone Algorithm
36
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: 591da6d506b3116196095082
There was a runtime error.

I can't figure out why the returns are significantly worse when I remove this line:

for stock in context.purchase:  
        if bool(get_open_orders(stock)) or data.current(stock,'last_traded') < get_datetime():  
             return  

If I am correct you are attempting to not trade when you have open orders or when any stock is not trade-able?
If so then the usual way to do this is like this:

if get_open_orders(): return  
     for stock in context.purchase:  
         if not data.can_trade(stock): return  

But, using that code results in the same bad performance as completely removing the code. So what I'm finding in my testing is that data.current(stock,'last_traded') < get_datetime() is giving you false negatives. It's stopping you from trading on some days even though the stock IS tradable. By trading on those days it severely reduces the performance. So now I can't tell if the performance of this algo is just luck of the draw and if it will translate well to live trading.

Hm that's very interesting. As I said, I cloned this from 'SPY & SH, minute data', and that bit was there originally. This was the comment that originally went with that line: "# skip tic if any orders are open or any stocks did not trade ". I don't really understand the 'last traded' bit either. I will look into the discrepancy in return when your change is made.

While I'm at it, I would like to mention that while the original algorithm was presumably supposed to be called every minute of the day (as you can see when the window variable is assigned to 15*390, or 15 days * 390 minutes per day), I recently changed it so it would be run daily with schedule_function. I didn't change window or window_roll because I don't know enough about the calculation of dv_z. It ended up working anyway, so I left it.

Edit: Okay I think I figured out what is causing the severe drop in performance. When you delete that line, it runs handle_data many more times, which means the window for prices and volumes is significantly shortened, and becomes super noisy. This completely screws up the dv_z calculation, which is the entire basis of the logic for buying/selling.

Did some more testing, it's not looking good.

Basically, the problem lies entirely with the way the backtester is processing data.current(stock,'last_traded') < get_datetime() specifically the stocks SPXL and EDV are the ones stopping it. Other stocks stop it at their own different times, SPY rarely stops it, probably because it's so widely traded it has a better data stream.

The actual days it trades are completely random. Sometimes it trades 3 times in a month, sometimes once per 3 months all depending on if if the data stream is caught up to the minute when it hits 11am, usually it is 5-15 minutes behind for some reason. All my attempts to make it trade on a regular schedule (daily, weekly, bi-monthly, monthly) have ended with terrible backtests. In live trading I suspect that the data stream would be more accurate and thus give you the worst returns. Basically, it's impossible to trade this type of algorithm because it's not giving you good returns based on sound logic but pure luck of SPXL and EDV's data timestamps in the backtester.

I've modified this one quite a bit but you can use it to turn off or turn on the effects of the logic that's causing the problem. The list context.text can be set to test whatever stocks you want to test stopping the trading. If you set test to SPXL, EDV, and SPY (comment out line 21) it replicates the original logic and also tells you which days trading was stopped and which days the trades were executed in the log. The chart also records error rates so you can see that visually. By separating test and purchase lists you can set it to purchase a stock like SPY/TLT or VTI/TLT for a much longer backtest. However it's really not worth it, this algo should not be traded!

Clone Algorithm
1
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: 59230f6bdfede94562c932fc
There was a runtime error.

Nice catch, Luke.
One thing I noticed about your backtest is that delta_z is different. I think it might do a lot better if we make the delta_z calculation the same as before (even if we change the frequency with which it makes orders), just so we at least keep the ordering logic consistent. That's probably what is causing the performance hit. The original algorithms were backtested over a period of 8.5 years, so even if there was something funky going on, it kept working for quite a long time. I think it's definitely worth looking into.

Ryan,

When I figured out the non-depreciated way to calculate delta_z I did a side-by-side comparison and it is very close to the original method, probably 99% the same. Also, if you comment out line 21 you will see that the performance with the bug in place is nearly identical to your non leveraged backtest (1000%+ returns).

That's a good point that it has worked for a while, but again that may just be an artifact of the way the backtester gets it's data vs a live stream. the way to test would be to run the algo live and if it executes the trades 2-3 days in a row then you know it doesn't have the same data gap bug and it's performance will not match the backtest. I won't risk it but you could.