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" 4 bonds: max 50%

Rebalance monthly, no fees (Robinhood)¶

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


Backtest Months: 112
Performance statistics Backtest
annual_return 0.08
cum_returns_final 1.00
annual_volatility 0.08
sharpe_ratio 0.95
calmar_ratio 0.39
stability_of_timeseries 0.95
max_drawdown -0.20
omega_ratio 1.18
sortino_ratio 1.36
skew -0.11
kurtosis 4.60
tail_ratio 0.96
common_sense_ratio 1.03
gross_leverage 1.00
information_ratio -0.01
alpha 0.04
beta 0.33
Worst drawdown periods net drawdown in % peak date valley date recovery date duration
0 19.89 2008-05-20 2008-10-27 2009-10-14 367
1 8.36 2015-01-26 2016-01-20 2016-07-05 377
2 8.33 2010-04-23 2010-07-06 2010-09-29 114
3 5.64 2013-05-15 2013-06-24 2013-07-22 49
4 4.93 2010-01-19 2010-02-08 2010-03-15 40

[-0.01  -0.021]
/usr/local/lib/python2.7/dist-packages/numpy/lib/function_base.py:3834: RuntimeWarning: Invalid value encountered in percentile
  RuntimeWarning)
Stress Events mean min max
Lehmann -0.04% -1.18% 0.88%
US downgrade/European Debt Crisis 0.17% -1.64% 1.47%
Fukushima 0.13% -0.60% 1.02%
EZB IR Event -0.02% -0.74% 0.78%
Mar08 0.42% 0.42% 0.42%
Sept08 -0.10% -1.18% 0.88%
2009Q1 -0.18% -1.30% 0.90%
2009Q2 0.13% -1.33% 1.81%
Flash Crash -0.25% -2.62% 3.57%
Apr14 0.04% -0.64% 0.55%
Oct14 0.07% -1.07% 1.06%
Fall2015 -0.09% -2.14% 1.57%
GFC Crash -0.04% -2.50% 3.32%
Recovery 0.05% -2.76% 3.57%
New Normal 0.03% -2.14% 1.57%
/usr/local/lib/python2.7/dist-packages/matplotlib/axes/_base.py:2787: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=733133.0, right=733133.0
  'left=%s, right=%s') % (left, right))
Top 10 long positions of all time max
XLB-19654 16.16%
EWH-14519 15.76%
SCZ-35248 15.54%
XLU-19660 15.51%
IVW-21514 15.48%
XLK-19658 15.31%
XLV-19661 15.24%
XLP-19659 15.23%
IYR-21652 15.22%
XLY-19662 15.20%
Top 10 short positions of all time max
Top 10 positions of all time max
XLB-19654 16.16%
EWH-14519 15.76%
SCZ-35248 15.54%
XLU-19660 15.51%
IVW-21514 15.48%
XLK-19658 15.31%
XLV-19661 15.24%
XLP-19659 15.23%
IYR-21652 15.22%
XLY-19662 15.20%
All positions ever held max
XLB-19654 16.16%
EWH-14519 15.76%
SCZ-35248 15.54%
XLU-19660 15.51%
IVW-21514 15.48%
XLK-19658 15.31%
XLV-19661 15.24%
XLP-19659 15.23%
IYR-21652 15.22%
XLY-19662 15.20%
IJT-21773 14.98%
MUB-34648 14.95%
TLT-23921 14.78%
GLD-26807 14.65%
IJJ-21770 14.54%
XLE-19655 14.46%
IJK-21771 14.44%
RWX-33072 14.42%
SHY-23911 14.30%
XLI-19657 14.13%
XLF-19656 13.92%
IJS-21772 13.89%
AGG-25485 13.86%
IVV-21513 13.76%
BWX-34793 13.72%
IVE-21512 13.68%
EFA-22972 13.63%
EWZ-21757 13.49%
EMB-35323 13.45%
DBA-33127 13.39%
TIP-25801 13.37%
IEF-23870 13.33%
EWA-14516 13.00%
HYG-33655 12.98%
IJH-21507 12.93%
EFV-27536 12.89%
ITOT-25871 12.73%
LQD-23881 12.52%
IJR-21508 12.51%

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 [2]:
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:02|###########################################################|
100% Time: 0:00:01|###########################################################|
100% Time: 0:00:02|###########################################################|
100% Time: 0:00:01|###########################################################|

Combine loaded benchmarks into one pandas DataFrame.¶

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

In [3]:
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[3]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f00ee6f3850>

Create Static Asset Allocation Portfolios to Compare¶

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

In [4]:
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 20% allocation to each asset class, maximum of 60% to an asset in line with what Vanguard does with their target date funds

In [5]:
a = [0,.2,.4,.6]
portfolios = create_portfolios([a,a,a,a])
256
40

Plot Performance of All Asset Allocation Models¶

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

Plot our Dynamic asset Allocation Model on Top for Comparison¶

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

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