Tuesday 14 June 2016

mini-Meucci : Applying The Checklist - Steps 6-7

"There are some days when I think I'm going to die from an overdose of satisfaction."
Salvador Dali, Artist (1904-1989)

Today we'll be visiting 2 sites along Via Meucci, Evaluation and Attribution.


We need some way to measure the goodness of the ex-ante portfolio distribution from the scenarios in the Aggregation step, and for this Meucci introduces the concept of a Satisfaction index.

Given the distribution of the ex-ante performance we can compute the satisfaction index (or its opposite, the risk index) associated with the portfolio in a number of ways:

  • expected utility/certainty equivalent: mean-variance, higher moments, prospect theory
  • spectral/distortion: VaR (economic capital), CVaR, Wang
  • non-dimensional ratios: Sharpe, Sortino, Omega and Kappa ratios

Given that our toy example has the goal of creating a low volatility portfolio, we'll choose the (mathematical) opposite of volatility as our measure of satisfaction e.g. if the ex-ante portfolio volatility is \$40,000 then the satisfaction index is -\$40,000.

In this simple example, the investor is only concerned with risk and not return i.e. less volatility is more satisfying than more volatility.

In the Construction step, this satisfaction index will be used to select the optimal portfolio to invest in.

See slide #11 Ex-ante Evaluation (2-stock example) and slide #48 (general case).

For more details, read chapter 5, "Evaluating Allocations", of A. Meucci, 2005, Risk and Asset Allocation, (Springer).

Example Python Code

In [3]:
%matplotlib inline
from pandas_datareader import data
import numpy as np
import pandas as pd
import datetime
import math
import matplotlib.pyplot as plt
import seaborn

# Get Yahoo data on 30 DJIA stocks and a few ETFs
tickers = ['MMM','AXP','AAPL','BA','CAT','CVX','CSCO','KO','DD','XOM','GE','GS',
start = datetime.datetime(2008, 4, 1)
end = datetime.datetime(2016, 5, 31)
rawdata = data.DataReader(tickers, 'yahoo', start, end) 
prices = rawdata.to_frame().unstack(level=1)['Adj Close']

# Quest for Invariance (random walk model) and Estimation (historical approach)
risk_drivers = np.log(prices)

# Set estimation interval = investment horizon (tau)
tau = 21 # investment horizon in days
invariants = risk_drivers.diff(tau).drop(risk_drivers.index[0:tau])

# Projection to the Investment Horizon
# Using the historical simulation approach and setting estimation interval = 
# investment horizon, means that projected invariants = invariants
# Recover the projected scenarios for the risk drivers at the tau-day horizon
risk_drivers_prjn = risk_drivers.loc[end,:] + invariants

# Pricing at the Investment Horizon
# Compute the projected $ P&L per unit of each stock for all scenarios
prices_prjn = np.exp(risk_drivers_prjn)
pnl = prices_prjn - prices.loc[end,:]

# Aggregation at the Investment Horizon
# Aggregate the individual stock P&Ls to projected portfolio P&L for all
# scenarios. Assume equally weighted protfolio at beginning of investment period
capital = 1e6
n_asset = 30
asset_tickers = tickers[0:30]
asset_weights = np.ones(n_asset) / n_asset
# initial holdings ie number of shares
h0 = capital * asset_weights / prices.loc[end, asset_tickers]
pnl_portfolio = np.dot(pnl.loc[:, asset_tickers], h0)

# Apply flexible probabilities to portfolio P&L scenarios
n_scenarios = len(pnl_portfolio)

# Time-conditioned flexible probs with exponential decay
half_life = 252 * 2 # half life of 2 years
es_lambda = math.log(2) / half_life
exp_probs = np.exp(-es_lambda * (np.arange(0, n_scenarios)[::-1]))
exp_probs = exp_probs / sum(exp_probs)
# effective number of scenarios
ens_exp_probs = np.exp(sum(-exp_probs * np.log(exp_probs))) 

# Projected Distribution of Portfolio P&L at Horizon  with flexible probs
import rnr_meucci_functions as rnr
mu_port_e, sigma2_port_e = rnr.fp_mean_cov(pnl_portfolio.T, exp_probs)  

# Ex-ante Evaluation
# Evaluate the ex-ante portfolio by some satisfaction index
# For example, assume the investor evaluates allocations based only on
# volatility, as measured by standard deviation, and does not take into 
# account expected returns. In this case, satisfaction is the opposite of
# the projected volatility of the portfolio
satisfaction = - np.sqrt(sigma2_port_e)
print('Ex-ante satisfaction index : {:,.0f} (in $ terms)'\
print('Ex-ante satisfaction index : {:,.2%} (in % terms)'\
Ex-ante satisfaction index : -39,747 (in $ terms)
Ex-ante satisfaction index : -3.97% (in % terms)


There are 2 goals in this step:

  1. Linearly attribute the portfolio ex-ante P&L to risk factors and a residual
  2. Additively attribute the risk/satisfaction index to the risk factors and a residual

In other words, the objective is to analyse the sources of return and risk that drive the portfolio P&L.

Applications of this analysis include identifying hotspots and unintended bets in the portfolio, monitoring exposure to a given risk factor and hedging a given risk.

Attribution of Portfolio Ex-ante P&L

In practice, this involves a linear regression of the projected portfolio returns against some set of risk factor projected returns . The regression coefficients are called the exposures (loadings / beta) to the risk factors, and this is the output of this sub-step.

Attribution of Risk/Satisfaction Index

If the chosen risk/satisfaction index has the nice property of positive homogeneity, then it can be decomposed and allocated to the risk factors. For example, we can decompose portfolio volatility, Value-at-Risk (VaR), and conditional VaR in this manner (but can't do it for the Sharpe ratio).

See slide #12 Ex-ante Attribution (2-stock example) and slide #49 (general case), and The Prayer (former Checklist).

For a more advanced and innovative discussion on Attribution, check out Meucci's Factors on Demand.

Also do note that it is possible to do the above attributions to the individual assets in the portfolio.

Example Python Code

In [5]:
# Ex-ante Attribution
# Linearly attribute the portfolio ex-ante PnL to the S&P500, long bond ETF
# + a residual
# Additively attribute the volatility of the portfolio's PnL to S&P500, 
# long bond ETF + a residual

# Set factors 
factor_tickers = ['SPY', 'TLT', 'Residual']
n_factor = 2
# Calculate linear returns - historical simulation
asset_rets = np.array(prices.pct_change(tau).ix[tau:, asset_tickers]) 
factor_rets = np.array(prices.pct_change(tau).ix[tau:, ['SPY', 'TLT']]) 

# Calculate portfolio standard deviation (in percentage terms)
port_std = np.sqrt(sigma2_port_e) / capital

# Factor attribution exposures and risk contributions (using flexible probs)
beta, vol_contr_Z = rnr.factor_attribution(asset_rets, factor_rets,
                                           asset_weights, exp_probs, n_factor)

print('Ex-ante factor exposure (beta):')
for i, factor in enumerate(factor_tickers):
    print('\t\t{}:\t{:.2f}'.format(factor, beta[i]))
print('Ex-ante portfolio volatility = {:.2%}'.format(port_std))
print('\tFactor risk contribution:')
for j, factor in enumerate(factor_tickers):
    print('\t\t{}:\t{:.2%}'.format(factor, vol_contr_Z[j]))

# Plot factor risk contribution chart
rnr.plot_waterfall_chart(pd.Series(vol_contr_Z, index=factor_tickers),
                       'Factor Contribution To Portfolio Volatility')
Ex-ante factor exposure (beta):
  SPY: 0.99
  TLT: 0.03
  Residual: 1.00

Ex-ante portfolio volatility = 3.97%
 Factor risk contribution:
  SPY: 3.84%
  TLT: -0.03%
  Residual: 0.17%

Next stop on the tour is Construction...

P.S. If you do sign-up for the Bootcamp, please let the good folks at ARPM know you learnt about it at returnandrisk.com!

References to Attilio Meucci's Work

  1. The Checklist slides
  2. The Prayer (former Checklist)
  3. A. Meucci, 2005, Risk and Asset Allocation, (Springer)
  4. Factors on Demand http://symmys.com/node/164

Download Python Code

Click here for the GitHub repo

No comments:

Post a Comment