Compare investment strategies, such as cap-weighted vs. equal-weighted, as well as slight modifications such as filtering out assets based on set criteria
Run a backtest using data from 1997 to 2015 comparing the performance of a cap-weighted portfolio vs an equal-weighted portfolio considering 49 industries based on their returns from the period of 1995 to 1996
import numpy as np
import pandas as pd
import edhec_risk_kit as erk
import matplotlib.pyplot as plt
%matplotlib inline
ind_rets = erk.get_ind_returns( weighting='vw', n_inds=49)['1994':'2015']
ind_mcap = erk.get_ind_market_caps(n_inds=49, weights=True)['1994':'2015']
cwr = erk.backtest_ws(
ind_rets,
estimation_window=12,
weighting=erk.weight_cw,
cap_weights=ind_mcap
)
ewr = erk.backtest_ws(
ind_rets,
estimation_window=12,
weighting=erk.weight_ew
)
btr = pd.DataFrame({'EW':ewr, 'CW':cwr})
(1+btr).cumprod()['1995':].plot(
figsize=(12,6),
title='Industry Portfolios - CW vs EW'
)
erk.summary_stats(btr.dropna())
ewr_tethered = erk.backtest_ws(
ind_rets,
estimation_window=12,
weighting=erk.weight_ew,
max_cw_mult=2,
microcap_threshold=0.01,
cap_weights=ind_mcap
)
btr = pd.DataFrame({'EW-T':ewr_tethered, 'CW':cwr})
(1+btr).cumprod()['1995':].plot(
figsize=(12,6),
title='Industry Portfolios - CW & EW-T'
)
erk.summary_stats(btr.dropna())
Tracking error is a measure of risk that shows how closely a portfolio's returns follows an benchmark index's returns. $TE = \sqrt{\text{Var}(r_p - r_b)}$
Calculate the tracking error between the EW portfolio and CW portfolio.
erk.tracking_error(ewr, cwr)
erk.tracking_error(ewr_tethered, cwr)
Run a backtest on the GMV portfolio over the same period. Calculate the weights based on the same 12-month window (returns in 1994)
gmvr = erk.backtest_ws(
ind_rets,
estimation_window=12,
weighting=erk.weight_gmv,
)
btr = pd.DataFrame({'GMV':gmvr, 'EW-T':ewr_tethered, 'CW':cwr})
(1+btr).cumprod()['1995':].plot(
figsize=(12,6),
title='Industry Portfolios - CW, EW-T, & GMV'
)
erk.summary_stats(btr.dropna())
Run the same backtest, but when calculating the GMV weights, estimate the covariance matrix using skrinkage between the constant correlation and sample covariance estimates with a 0.25 delta
def shrinkage_cov(r, delta=0.5, **kwargs):
"""
Covariance estimator that shrinks between the Sample Covariance and the Constant Correlation Estimators
"""
prior = cc_cov(r, **kwargs)
sample = sample_cov(r, **kwargs)
return delta*prior + (1-delta)*sample
def cc_cov(r, **kwargs):
"""
Estimates a covariance matrix by using the Elton/Gruber Constant Correlation model
"""
rhos = r.corr()
n = rhos.shape[0]
# this is a symmetric matrix with diagonals all 1 - so the mean correlation is ...
rho_bar = (rhos.values.sum()-n)/(n*(n-1))
ccor = np.full_like(rhos, rho_bar)
np.fill_diagonal(ccor, 1.)
sd = r.std()
return pd.DataFrame(ccor * np.outer(sd, sd), index=r.columns, columns=r.columns)
gmvr_shrinkage = erk.backtest_ws(
ind_rets,
estimation_window=12,
weighting=erk.weight_gmv,
cov_estimator=erk.shrinkage_cov,
delta=0.25
)
btr = pd.DataFrame({
'GMV_Shrinkage':gmvr_shrinkage,
'GMV':gmvr,
'EW-T':ewr_tethered,
'CW':cwr})
(1+btr).cumprod()['1995':].plot(
figsize=(12,6),
title='Industry Portfolios - CW, EW-T, GMV, GMV-Shrinkage'
)
erk.summary_stats(btr.dropna())