Notebook

Dynamic Efficient Asset Allocation Strategy¶

Dynamic Universe:¶

  • 30 Equity Options
    • Total US + 9 Style Boxes
    • All 10 Sectors (including REIT)
    • 5 Broad Interntaional Options: developed, developed value, emerging, ex-US small, ex-US REIT
    • 5 Countries: Australia, Hong Kong, Sweden, Brazil, and South Africa
  • 10 Bond Options
    • Emerging Market Bond, Int'l Treasury
    • High Yield Bond, Quality Corporate Bond
    • Total US Bond, Municipal Bonds, TIPS
    • Short Term, Intermediate, and Long Term US Treasuries
  • 2 Alternates
    • Gold or Agricultural Commodities

Pick Trailing 3 Month Efficent Allocation of 8 ETFs¶

  • Limit to "only" 5 bonds: max 62.5%

Rebalance monthly, no fees (Robinhood)¶

In [13]:
bt = get_backtest('598bb4d9b11f81540685ce78')
bt.create_full_tear_sheet()
100% Time: 0:00:01|###########################################################|
Entire data start date: 2008-04-01
Entire data end date: 2017-08-08


Backtest Months: 112
Performance statistics Backtest
annual_return 0.07
cum_returns_final 0.89
annual_volatility 0.07
sharpe_ratio 1.02
calmar_ratio 0.42
stability_of_timeseries 0.94
max_drawdown -0.17
omega_ratio 1.19
sortino_ratio 1.46
skew -0.20
kurtosis 3.21
tail_ratio 0.96
common_sense_ratio 1.03
gross_leverage 1.00
information_ratio -0.01
alpha 0.04
beta 0.24
Worst drawdown periods net drawdown in % peak date valley date recovery date duration
0 16.68 2008-05-20 2008-10-27 2009-09-16 347
1 7.61 2015-01-26 2016-01-20 2016-07-08 380
2 6.28 2010-04-26 2010-07-01 2010-09-22 108
3 5.80 2013-05-15 2013-06-24 2013-09-16 89
4 4.22 2010-01-19 2010-02-08 2010-03-05 34

[-0.008 -0.018]
Stress Events mean min max
Lehmann -0.03% -1.03% 0.74%
US downgrade/European Debt Crisis 0.16% -1.19% 1.28%
Fukushima 0.12% -0.62% 1.02%
EZB IR Event -0.01% -0.65% 0.77%
Mar08 0.28% 0.28% 0.28%
Sept08 -0.06% -1.03% 0.74%
2009Q1 -0.17% -1.11% 0.87%
2009Q2 0.10% -1.33% 1.51%
Flash Crash -0.18% -2.19% 2.93%
Apr14 0.04% -0.39% 0.48%
Oct14 0.09% -0.74% 0.92%
Fall2015 -0.07% -1.53% 1.04%
GFC Crash -0.03% -2.04% 1.87%
Recovery 0.05% -2.26% 2.93%
New Normal 0.03% -1.54% 1.24%
Top 10 long positions of all time max
XLV-19661 16.24%
XLK-19658 16.22%
XLP-19659 15.55%
IJK-21771 15.53%
SCZ-35248 15.48%
EWH-14519 15.44%
XLB-19654 15.39%
IJH-21507 15.39%
IJT-21773 15.07%
XLU-19660 14.87%
Top 10 short positions of all time max
Top 10 positions of all time max
XLV-19661 16.24%
XLK-19658 16.22%
XLP-19659 15.55%
IJK-21771 15.53%
SCZ-35248 15.48%
EWH-14519 15.44%
XLB-19654 15.39%
IJH-21507 15.39%
IJT-21773 15.07%
XLU-19660 14.87%
All positions ever held max
XLV-19661 16.24%
XLK-19658 16.22%
XLP-19659 15.55%
IJK-21771 15.53%
SCZ-35248 15.48%
EWH-14519 15.44%
XLB-19654 15.39%
IJH-21507 15.39%
IJT-21773 15.07%
XLU-19660 14.87%
EWZ-21757 14.69%
IYR-21652 14.57%
XLE-19655 14.55%
TLT-23921 14.45%
IJJ-21770 14.37%
XLY-19662 14.35%
BWX-34793 14.34%
GLD-26807 14.34%
IVE-21512 14.33%
IEF-23870 14.02%
AGG-25485 13.98%
ITOT-25871 13.97%
XLI-19657 13.91%
SHY-23911 13.88%
MUB-34648 13.70%
IVW-21514 13.65%
XLF-19656 13.56%
DBA-33127 13.48%
EMB-35323 13.47%
EFV-27536 13.34%
HYG-33655 13.34%
IJR-21508 13.27%
IVV-21513 13.23%
RWX-33072 12.97%
LQD-23881 12.96%
EWA-14516 12.92%
EFA-22972 12.80%
TIP-25801 12.75%
IJS-21772 12.23%

Load in Backtests of Benchmarks¶

Use simple algorithm, next cell, to find the performance of a simple asset allocation (could be just one asset) to use as benchmark. Then we can load in the performance that includes dividends.

In [ ]:
def initialize(context):
    """
    Called once at the start of the algorithm.
    """   
    set_commission(commission.PerShare(cost=0, min_trade_cost=0))
    # Rebalance every day, 1 hour after market open.
    schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(hours=1))
     
 
def my_rebalance(context,data):
    """
    Execute orders according to our schedule_function() timing. 
    """
    order_target_percent(sid(22972),1.0)
In [4]:
dynamic_algorithm = bt.cumulative_performance.returns
ex_us = get_backtest('598bc5ed59ab1554042ee706').cumulative_performance.returns
ex_us_bond = get_backtest('598bc5d88e33dd5412351555').cumulative_performance.returns
us = get_backtest('598bc5e44a58a153d4ce0262').cumulative_performance.returns
us_bond = get_backtest('598bc5cc59ab1554042ee703').cumulative_performance.returns
100% Time: 0:00:01|###########################################################|
100% Time: 0:00:01|###########################################################|
100% Time: 0:00:01|###########################################################|
100% Time: 0:00:01|###########################################################|

Combine loaded benchmarks into one pandas DataFrame.¶

Then make a copy and add the algorithm result as well.

In [6]:
import pandas as pd
data = pd.DataFrame(us)
data.columns = ['US Stock']
data['US Bond'] = us_bond.values
data['ex-US Bond'] = ex_us_bond.values
data['ex-US Stock'] = ex_us.values
data2 = data.copy()
data2['Dynamic'] = dynamic_algorithm
data2.plot()
Out[6]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fd6c687a850>

Create Static Asset Allocation Portfolios to Compare¶

Define function that computes all possible combinations of an array of arrays that add to 1

In [8]:
import numpy as np
import matplotlib.pyplot as plt
import itertools

def create_portfolios(a):
    """
    Create portfolio combinations
    """
    b = list(itertools.product(*a))
    x = [sum(i) for i in b]
    port = pd.DataFrame(b)
    port['Sum'] = x
    print len(port)
    port = port[port.Sum == 1.0]
    del port['Sum']
    print len(port)
    return port

Allow increments of 12.5% allocation to each asset class, maximum of 62.5% to an asset in line with what Vanguard does with their target date funds

In [9]:
a = [0,.125,.25,.375,.5,.625]
portfolios = create_portfolios([a,a,a,a])
1296
125

Plot Performance of All Asset Allocation Models¶

In [10]:
port_returns = pd.DataFrame(np.dot(data, portfolios.T), index=data.index)   
port_returns.plot(legend=False)
Out[10]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fd6cc50bb90>

Plot our Dynamic asset Allocation Model on Top for Comparison¶

In [12]:
port_returns.plot(legend=False)
dynamic_algorithm.plot(lw=10)
data2.plot(legend=True,lw=5)
Out[12]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fd6c742c750>

Define Overall Universe¶

The following is some of the code in the intialize function of the algorithm. This explicitely lists the ETFs to consider an allocation to at the start of every month.

In [ ]:
    context.us_style = [sid(25871), #ITOT Total US
                        sid(21512), #IVE Large Value
                        sid(21513), #IVV Large Blend
                        sid(21514), #IVW Large Growth
                        sid(21770), #IJJ Mid Value
                        sid(21507), #IJH Mid Blend
                        sid(21771), #IJK Mid Growth
                        sid(21772), #IJS Small Value
                        sid(21508), #IJR Small Blend
                        sid(21773)] #IJT Small Growth
    
    context.us_sector = [sid(19662), #XLY Consumer Cyclical
                         sid(19656), #XLF Financials
                         sid(19659), #XLP Consumer Defensive
                         sid(19661), #XLV Healthcare
                         sid(19657), #XLI Industrials
                         sid(19655), #XLE Energy
                         sid(19654), #XLB Materials
                         sid(19660), #XLU Utilities
                         sid(21652), #IYR ISHARES Real Estate
                         sid(19658)] #XLK Tech
    
    context.ex_us_stock = [sid(22972), #EFA Core EAFE
                           sid(27536), #EFV EAFE Value
                           sid(35248), #SCZ ex-US Small-Cap
                           sid(33072), #RWX ex-US REIT
                           sid(24705)] #EEM Emerging Markets
    
    context.country = [sid(14516), #EWA Australia
                       sid(14519), #EWH Hong Kong
                       sid(12430), #EWD Sweden
                       sid(21757), #EWZ Brazil
                       sid(24611)] #EZA South Africa
    """                sid(14520), #EWJ Japan
                       sid(14529), #EWU UK
                       sid(14527), #EWQ France
                       sid(14518), #EWG Germany
                       sid(14522), #EWL Switzerland
                       sid(25098), #EWI Italy
                       sid(21491), #EWY South Korea
                       sid(21619), #EWT Taiwan
                       sid(14524), #EWN Netherlands
                       sid(14526), #EWP Spain
    """
    
    context.bonds =  [sid(35323), #EMB Emerging Market Bond
                      sid(34793), #BWX Int'l Treasury
                      sid(33655), #HYG High Yield Bond
                      sid(23881), #LQD Quality Corporate Bond
                      sid(34648), #MUB Municipal Bonds
                      sid(25485), #AGG Total Bond
                      sid(23921), #TLT Long Term Government Bond
                      sid(25801), #TIP TIPS
                      sid(23870), #IEF Intermediate Treasury
                      sid(23911)] #SHY Short Term Government Bond
    #                  sid(33154)] #SHV <1 Treasury Bonds
    
    context.other = [sid(26807), #GLD Gold
                     sid(33127)] #DBA Agricultural Commodities