Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Stock-Bond Balance

The latest year Quantopian staff pushing crowd to create Long-Short portfolios to neutralize beta.
But there are many other ways to reach low beta. One of them Stock-Bond Balance.
I am happy that I am not the only in this community who prefer Balanced portfolios.

Happy New Year to All!!!

24 responses

Awesome! I was literally just looking for this! Are there any other bonds (such as different maturity lengths) available?

Alexander
You may find them here.

Here's the algo run over the past year. It takes a pretty big hit right at the end, resulting in a max drawdown of 10% over the year.

By the way, my little doodle was inspired by Market Tech's post on https://www.quantopian.com/posts/slope-calculation, since we are crediting sources.

@Alexander- Here are some bond ETFs.

  • iShares: AGG TLT TLH SHY SHV IEF IEI HYG LQD
  • SPDR: JNK
  • Vanguard: BND VGLT VGSH VCLT VCSH VMBS EDV

See also

@Andre

Awesome! This helps a lot.

Cheers!

@Alexander- Try this algorithm from half a year ago. Some ETFs produce runtime errors Quantopian doesnt care to explain - probably because they were created after the backtest start date.

Migrated to Q2 version out of sample results:
From 2015-12-30 to 2016-07-01

Total Returns
13.2%
Benchmark Returns
2.2%
Alpha
0.24
Beta
0.05
Sharpe
3.27
Sortino
4.94
Information Ratio
2.88
Volatility
0.07
Max Drawdown
3.1%

Any Idea on how to apply this with results from pipeline? Cant figure out how to implement so far I have...

def before_trading_start(context, data):
context.data = pipeline_output('my_pipeline').dropna()

context.data_sort = context.data.sort(['eps_growth'], ascending=False).iloc[:200]  


context.final_longs_sort = context.data_sort.sort(['dollar_volume'], ascending=False).iloc[:100]  

context.long_final = context.final_longs_sort  

context.security_list = context.long_final.index  

context.hedge = symbol('TLT')  


log.info(context.long_final)  

def rebalance(context, data):

stock = context.security_list  
bond = context.hedge  
period = 70  
target_lev = 1.00  
pw = 0.5

price_hedge = data.history(context.hedge, 'price', period+1, '1d')[0:-1]  
ret_sum1 = price_hedge.pct_change().sum()  

prices = data.history(context.long_final.index, 'price', period+1, '1d')[0:-1]  
ret_sum2 = prices.pct_change().sum()  


r_diff = ret_sum2[stock] - ret_sum1[bond]  
#record(r_diff = r_diff)  
if r_diff>0.0:  
    adpt = (1+r_diff)**pw-1.0  
elif r_diff<0.0:  
    adpt =-((1+abs(r_diff))**pw-1.0)  
else:  
    adpt =0.0  

wt_stock = target_lev*(0.50+adpt)  
wt_bond = target_lev*(0.50-adpt)

order_target_percent(stock, wt_stock)  
order_target_percent(bond, wt_bond)  

record(wt_stock = wt_stock, wt_bond = wt_bond)

record(Leverage = context.account.leverage)  

This is the error I'm getting when I try to implement in my algo. Im new to quantopian and python so not sure what I'm doing wrong here. What should I do?

TypeError: 'float' object has no attribute '__getitem__'
... USER ALGORITHM:122, in rebalance
r_diff = ret_sum2[stock] - ret_sum1[bond]

I think your ret_sum1 is a scalar of type float, and so you can't index it with [bond]. Remove this part.

Also, you probably want the sum of all stock returns, which would be ret_sum2[stock].sum(). Does this work?

Add a line to print r_diff. It should be a float scalar, or you'll get errors in the if statement that follows.

Awesome Thanks André! I will give it a go . Should I remove the whole ret_sum1 or just the [bond] part?

I think that worked André. However, now I get UnsupportedOrderParameters: Passing non-Asset argument to 'order()' is not supported. Use 'sid()' or 'symbol()' methods to look up an Asset.
There was a runtime error on line 135.

This is how its looking now

stock = context.security_list
bond = context.hedge
period = 70
target_lev = 1.00
pw = 0.5

price_hedge = data.history(context.hedge, 'price', period+1, '1d')[0:-1]  
ret_sum1 = price_hedge.pct_change().sum()  

prices = data.history(context.long_final.index, 'price', period+1, '1d')[0:-1]  
ret_sum2 = prices.pct_change().sum()  


r_diff = ret_sum2[stock].sum() - ret_sum1  
if r_diff>0.0:  
    adpt = (1+r_diff)**pw-1.0  
elif r_diff<0.0:  
    adpt =-((1+abs(r_diff))**pw-1.0)  
else:  
    adpt =0.0  

wt_stock = target_lev*(0.50+adpt)  
wt_bond = target_lev*(0.50-adpt)

order_target_percent(stock, wt_stock)  
order_target_percent(bond, wt_bond)  

record(wt_stock = wt_stock, wt_bond = wt_bond)

record(Leverage = context.account.leverage)  

That's because your stock is not a single Asset like your bond, but a list. You have to write a loop over stock, which would order some portion of wt_stock, eg. wt_stock/len(stock), for each individual security.

Hi André check it out. Im posting the backtest with the suggested changes. Theres a couple of things Im having trouble with, for some reason the leverage is really high and then I get a KeyError: 'the label [2016-05-12 00:00:00+00:00] is not in the [index]'
There was a runtime error on line 83.
Before merging my algo with this one, I was not getting any leverage and had no problems. Im wondering if I'm doing something wrong in compute weights and the rebalance. Sorry for the messy code, I'm still trying to figure things out as a newb!

You should check your return sum computations. You want ret_sum2 in line 128 to contain a pandas.Series of sums of daily returns for each stock. But it's possible you're instead creating a Series of sums of returns across stocks for each day. Either way, it shouldn't matter, because you're only using the gross sum across stocks and days. Remove [stock] in line 131.

Also, understand clearly how much of each stock you want to order. If wt_stock is the total percentage of all the stocks in your target portfolio, you wouldn't want this much for each stock, would you? Unless you have only one stock, you'll exceed your target leverage. Reread my previous message.

ahhhh ok. Sorry I got crazy confused there for a sec. This makes sense. Let me give it a go. For the wt_stock issue, would something like this make sense? wt_stock = (target_lev*(0.50+adpt)) / (len(context.long_final))

Yes. Or call it wt_each_stock.

Or insert

    wt_each_stock = wt_stock / len(context.long_final)  

after line 141 and change wt_stock to wt_each_stock in line 148, but not line 156.

Hi André thanks for your help again! Things are working better but still not 100%. Im attaching the latest backtest I ran last night. I think the problem might be that when rebalancing, it is both selling and buying the hedge at the same time. There are times when the stock level goes to 1.14 and the bond level goes to -0.14. Check it out see what you think. Im sure that it is a minor tweak at this point. Again, thanks for all your help!

HI André hope you're doing well. Do you think you can take a look at this to see what the issue could be? I know its something with either the ordering process or weighting but just can't figure it out. I have made several changes but still can't figure it out.

I'm not sure I had seen this post before but it is most refreshing to read Vladimirs first post above.

I made a comment somewhere about sound and fury signifying nothing. It is my bet that if you revisited the vastly complex Quantopian approach in 10, 20 and 50 years hence and compared it to a simple stock bond rebalancing approach you would highly probably have done better with the latter.

@Vladimir - Thank you for sharing! This goes a long way in helping educate noobs like me. ( :

Between the two versions, why the change in the rebalancing calculation? I'm trying to learn the theory behind rebalance threshold used.

adpt = (1+r_diff)**pw-1.0  

For ease:
- r_diff is the total difference in the price changes between stocks and bonds
- pw is 0.5 (sqrt)

@adr,

Over the course of 4.5 years, I did 32 backtests of this algorithm sometimes editing code but not the idea.
Here is the latest version I edited on 02-12-2019.

ret_sum is sum of daily percent changes for period
r_diff is difference between stock ret_sum and bond ret_sum

Hi Anthony,

Looks like I missed your post too.
Thanks for the comment.

It is my bet that if you revisited the vastly complex Quantopian approach in 10, 20 and 50 years
hence and compared it to a simple stock bond rebalancing approach
you would highly probably have done better with the latter.

+1