# mini-Meucci : Applying The Checklist - Steps 10+

In this final leg of The Checklist tour we'll be looking at the Dynamic Allocation step and touch briefly on ex-post Performance Analysis.

## Dynamic Allocation

Essentially this involves repeating the previous 9-steps on a periodic basis (e.g. a sequence of monthly allocations) according to a chosen allocation policy.

Examples of dynamic allocations include systematic strategies (based on signals) and portfolio insurance.

### Quantitative/Systematic Strategies

Those interested in active portfolio management and who attend the ARPM bootcamp, will also have access to the ARPM Lab. In it are 2 very interesting chapters covering both the theory and practice (with code) of quant strategies, where you'll learn among other things, how to construct a characteristic portfolio (Grinold and Easton) based on your signals. For an example of such a characteristic portfolio strategy, see the youtube video on slide #56 Dynamic Allocation (video).

### Example Python Code

In our toy example with the goal of constructing a low volatility equity portfolio, our chosen allocation policy will be to weight the 30 DJIA stocks according to the ex-ante minimum variance portfolio, and rebalance the portfolio at the end of each month.

We'll use an expanding historical data window of at least 3 years, apply time-conditioned weights to the observations when estimating the ex-ante distribution, and also use a simple form of shrinkage before optimizing.

To simulate such a sequence of allocations over a 3 year period, we'll use the open source Zipline package.

In [2]:
%matplotlib inline
import rnr_meucci_functions as rnr
import numpy as np
from zipline.api import (set_slippage, slippage, set_commission, commission,
order_target_percent, record, schedule_function,
date_rules, time_rules, get_datetime, symbol)

# Set tickers for data loading i.e. DJIA constituents and DIA ETF for benchmark
tickers = ['MMM','AXP','AAPL','BA','CAT','CVX','CSCO','KO','DD','XOM','GE','GS',
'HD','INTC','IBM','JNJ','JPM','MCD','MRK','MSFT','NKE','PFE','PG',
'TRV','UNH','UTX','VZ','V','WMT','DIS', 'DIA']

# Set investable asset tickers
asset_tickers = ['MMM','AXP','AAPL','BA','CAT','CVX','CSCO','KO','DD','XOM','GE',
'GS','HD','INTC','IBM','JNJ','JPM','MCD','MRK','MSFT','NKE','PFE','PG',
'TRV','UNH','UTX','VZ','V','WMT','DIS']

def initialize(context):
# Turn off the slippage model
# Set the commission model
context.day = -1 # using zero-based counter for days
context.set_benchmark(symbol('DIA'))
context.assets = []
print('Setup investable assets...')
for ticker in asset_tickers:
#print(ticker)
context.assets.append(symbol(ticker))
context.n_asset = len(context.assets)
context.n_portfolio = 40 # num mean-variance efficient portfolios to compute
context.today = None
context.tau = None
context.min_data_window = 756 # min of 3 yrs data for calculations
context.first_rebal_date = None
context.first_rebal_idx = None
context.weights = None
# Schedule dynamic allocation calcs to occur 1 day before month end - note that
# actual trading will occur on the close on the last trading day of the month
schedule_function(rebalance,
date_rule=date_rules.month_end(days_offset=1),
time_rule=time_rules.market_close())
# Record some stuff every day
schedule_function(record_vars,
date_rule=date_rules.every_day(),
time_rule=time_rules.market_close())

def handle_data(context, data):
context.day += 1
#print(context.day)

def rebalance(context, data):
# Wait for 756 trading days (3 yrs) of historical prices before trading
if context.day < context.min_data_window - 1:
return
# Get expanding window of past prices and compute returns
context.today = get_datetime().date()
prices = data.history(context.assets, "price", context.day, "1d")
if context.first_rebal_date is None:
context.first_rebal_date = context.today
context.first_rebal_idx = context.day
print('Starting dynamic allocation simulation...')
# Get investment horizon in days ie number of trading days next month
context.tau = rnr.get_num_days_nxt_month(context.today.month, context.today.year)
# Calculate HFP distribution
asset_rets = np.array(prices.pct_change(context.tau).iloc[context.tau:, :])
num_scenarios = len(asset_rets)
# Set Flexible Probabilities Using Exponential Smoothing
half_life_prjn = 252 * 2 # in days
lambda_prjn = np.log(2) / half_life_prjn
probs_prjn = np.exp(-lambda_prjn * (np.arange(0, num_scenarios)[::-1]))
probs_prjn = probs_prjn / sum(probs_prjn)
mu_pc, sigma2_pc = rnr.fp_mean_cov(asset_rets.T, probs_prjn)
# Perform shrinkage to mitigate estimation risk
mu_shrk, sigma2_shrk = rnr.simple_shrinkage(mu_pc, sigma2_pc)
weights, _, _ = rnr.efficient_frontier_qp_rets(context.n_portfolio,
sigma2_shrk, mu_shrk)
print('Optimal weights calculated 1 day before month end on %s (day=%s)' \
% (context.today, context.day))
#print(weights)
min_var_weights = weights[0,:]
# Rebalance portfolio accordingly
for stock, weight in zip(prices.columns, min_var_weights):
order_target_percent(stock, np.asscalar(weight))
context.weights = min_var_weights

def record_vars(context, data):
record(weights=context.weights, tau=context.tau)

def analyze(perf, bm_value, start_idx):
pd.DataFrame({'portfolio':results.portfolio_value,'benchmark':bm_value})\
.iloc[start_idx:,:].plot(title='Portfolio Performance vs Benchmark',\
figsize=(10, 8))

if __name__ == '__main__':
from datetime import datetime
import pytz
import pandas as pd
import matplotlib.pyplot as plt

# Create and run the algorithm.

start = datetime(2010, 5, 1, 0, 0, 0, 0, pytz.utc)
end = datetime(2016, 5, 31, 0, 0, 0, 0, pytz.utc)
print('Getting Yahoo data for 30 DJIA stocks and DIA ETF as benchmark...')
# Check price data
data.loc[:, :, 'price'].plot(figsize=(8,7), title='Input Price Data')
plt.ylabel('price in \$');
plt.legend(loc='center left', bbox_to_anchor=(1.0, 0.5))
plt.show()

# Run algorithm
results = algo.run(data)

# Fix possible issue with timezone
results.index = results.index.normalize()
if results.index.tzinfo is None:
results.index = results.index.tz_localize('UTC')

# Adjust benchmark returns for delayed trading due to 3 year min data window
bm_rets = algo.perf_tracker.all_benchmark_returns
bm_rets[0:algo.first_rebal_idx + 2] = 0
bm_rets.name = 'DIA'
bm_rets.index.freq = None
bm_value = algo.capital_base * np.cumprod(1+bm_rets)

# Plot portfolio and benchmark values
analyze(results, bm_value, algo.first_rebal_idx + 1)
print('End value portfolio = {:.0f}'.format(results.portfolio_value.ix[-1]))
print('End value benchmark = {:.0f}'.format(bm_value[-1]))

# Plot end weights
pd.DataFrame(results.weights.ix[-1], index=asset_tickers, columns=['w'])\
.sort_values('w', ascending=False).plot(kind='bar', \
title='End Simulation Weights', legend=None, figsize=(10, 8));

Getting Yahoo data for 30 DJIA stocks and DIA ETF as benchmark...

C:\Anaconda3\envs\py34\lib\site-packages\ipykernel\__main__.py:106: DeprecationWarning: load_bars_from_yahoo is deprecated, please register a yahoo_equities data bundle instead

Setup investable assets...
Starting dynamic allocation simulation...
Optimal weights calculated 1 day before month end on 2013-05-30 (day=774)
Optimal weights calculated 1 day before month end on 2013-06-27 (day=794)
Optimal weights calculated 1 day before month end on 2013-07-30 (day=816)
.
.
.
Optimal weights calculated 1 day before month end on 2016-03-30 (day=1487)
Optimal weights calculated 1 day before month end on 2016-04-28 (day=1508)
Optimal weights calculated 1 day before month end on 2016-05-27 (day=1529)
End value portfolio = 130872
End value benchmark = 125771