Back to Community
Runaway Leverage - Momentum Strategy based on the DOW by (Leo P. Williams)

I would love nothing more than to eliminate the runaway leverage in this algorithm while maintaining the returns. Hence, I am in a sharing mood. If one if you should figure this out, feel free to share it publicly or with me at [email protected]. I like this algorithm because it trades the DOW, the safest of all stocks in my opinion. I'm new to python, three months of experience to be exact, and I literally learned it simply because this platform forced me to. Any improvements we can make to keep leverage 2 or below would be great for swing trading, and any improvement we can make between 4 and below, closing out that same day would be great for a day trading Algo.

Thanks in advance,

Leo

Clone Algorithm
26
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: 591dc7a71cef0d61846fa52d
There was a runtime error.
13 responses
     oo = get_open_orders()  
     for s in context.security_listF:  
        if s in oo: continue  
        if s in context.portfolio.positions: continue  
        if context.vix > context.vl: continue  
        if not context.can_trade == 1: continue  
        if not data.can_trade(s): continue

        # the solution is the next two lines  
        val = context.portfolio.cash / len(context.security_listF)  
        order_target_value(s, val, style=MarketOrder(IBExchange.SMART))

        # understand that stop will only be entered if current shares, so this one never does anything  
        #   currently due to positions condition above. The other one does.  
        context.trail[s] = data.current(s,'price') * .97  
        order_target(s, 0, style=StopOrder(context.trail[s],IBExchange.SMART))  

Thanks. Can you post the working example? How much did this change impact the return?

Hey Blue. I don't see how what you're doing is any different from what I've already done. You're simply using continue statements as opposed to nested if's. Can you post a working example with the change you made? Remember, the objective is to maintain as much of the momentum gain as I originally coded, but limiting the leverage to 2. A bit shocked, I didn't get more responses, given all the rambling that goes on here.

  for s in context.security_list:  
     if context.vix <= context.vl:  
         if pc[s][-2] == max(hi[s]) and pc[s][-2] > ma[s]:  
           if mx[s][-1] > mx[s][-200] and ma[s] > ma2[s]:  
              print s.symbol  
              context.security_listF[s] = s  

     if len(context.security_listF) > 0 and context.portfolio.cash > 0:  
       context.can_trade = 1  
       context.div = (context.portfolio.cash/len(context.security_listF))  

 for s in context.security_listF:  
     #if context.can_trade == 1:  
       if s not in context.portfolio.positions and s not in get_open_orders():  
         if data.can_trade(s):  
           if context.vix <= context.vl:  
             cp = data.current(s,'price')  
             context.trail[s] = cp * .97  
             shares = (context.div/cp)  
             order_target(s,shares,style=MarketOrder(IBExchange.SMART))  
             order_target(s,0,style=StopOrder(context.trail[s],IBExchange.SMART))  

The main change might be dropping the use of .div. You can ignore the continue business, some find that style easier to understand and work with, and since I had changed it for my understanding, I left it in place.

Run this and look at the logs. Then you might want to try my two lines and compare the logging output between the two. From that I'm sure you'll figure out where to go from there. Notice start and stop dates are entered for the tracking, out of initialize to make that easy for you to change. Also I added leverage in the output. It is focused on a time when leverage jumps to 8.

The code also contains https://www.quantopian.com/posts/margin. If you turn that on you'll see that your version carries a cumulative 94 million margin overnight if you total them all up which it does.

In the custom chart, you can see that the leverage high of 32 and spending 121 million results in 7% returns adjusted for the amount risked. Replace with my two lines (or perhaps just drop your use of .div, always operate against current cash instead) and returns will be 70% or so.

Clone Algorithm
18
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: 59373309ce199f6d693fd929
There was a runtime error.

It took me a long time to figure out how to perfectly control leverage, I think everyone solves the issue slightly differently depending on how their algo is set up. People who don't live trade don't run into as many problems with execution

The way I do it is to use order() and specify exactly how many shares I should be ordering based on the cash that I have available. Sell first, wait until all sell orders have filled, then execute your buys. You can use a flag that will only return true once all sell orders are finished and one that signals all stocks are ordered and in the proper allocations and keep repeating the function in handle_data() until the flag returns true.

Currently you have your ordering logic split up into two functions that are both buying and selling in the same minute 1 minute after eachother using a _target() ordering function that doesn't know about the other orders because they haven't executed yet.

Once margin is under control like that, then you can just multiply ordering for any margin leverage target. I did that and found that it ran higher than a target of 2 for a little while. That would surely be due to partial fills with the higher volume and you can investigate with the track_orders code (already present) if you might want to look for a way to further limit margin excess over target when that happens.

It may be that I'm not understanding what Blue is stating, and my apologies on that. It appears he has diagnosed why this is happening, as opposed to crafting the solution, which is of course valuable. However, my goal is very simple, keep leverage under 2, while maintaining symmetric gains, if possible. If I uncomment the line if context.can_trade == 1, the code will not abuse leverage. However, the gains are substantially lower. However, I also notice times where there are funds that are not invested, which is where I felt there was wiggle room to make up for what was lost in decreased usage of leverage. I don't want to over-complicate this. Again, I've been on here all of three months. I'm no expert by any means, but I do firmly understand trading concepts.

This is the ordering I suggested, it has max intraday leverage 1.02, no margin to speak of.

Clone Algorithm
18
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: 5937b5951bc5516d8cfaa399
There was a runtime error.

I was able to answer my own question, fix and recompose the algorithm using somewhat of a different entry/exit strategy. I'm still trading the DOW, now with leverage consistently under 2, but I'm able to get a return of 7000%, roughly 1/4 of the original I posted, but still 70 times the investment. That's basically 5 times your money, per year trading the safest stocks on the exchange. And since I'm going long, I can do this all through Robin Hood. I have to say I'm a bit disappointed that I didn't see many more answers on this one, but I was able to figure out what I needed anyway.

great, can you post the algo?

(on a side note) Sorry dont want to highjack the tread but i just wanted this to be addressed:

It might be problematic to do a back test of the current constituents of the DOW (as companies are added to the index over time), this could introduce a source of hind sight bias (since companies that got added tend to have outperformed those that got removed).

Again, sorry for potentially high jacking the thread.

Irrelevant in the big scheme of things. If you want to code for that, I seriously doubt you would see a huge difference. The stocks in the DOW and S&P500 serve as essentially a market baseline. In addition, most brokers will allow you a 2:1 margin on these stocks, essentially allowing you to double the profit on any algorithm. I personally don't even use the DOW anymore, though I did figure out how to control leverage and yield 85 times the investment over 14 years, using margin. That said, I have created a penny stock algorithm that which yields $70,000,000 on $1000 invested over 14 years, with a max draw down of 55% in 2008, which can be run in Robin Hood, using settled funds. I could care less about the DOW at this point. What I do not like about this platform is the one minute intervals, and I'm looking into QuantConnect at the moment. I simply need more granularity on stops when trading low floats.

L. Williams,

Can you please post the backtest that you achieved the 7000% mark on? Also, are you currently live-trading this on Robinhood?

Thanks,
Kern