Showing posts with label risk management. Show all posts
Showing posts with label risk management. Show all posts

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.

Evaluation

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',
           'HD','INTC','IBM','JNJ','JPM','MCD','MRK','MSFT','NKE','PFE','PG',
           'TRV','UNH','UTX','VZ','V','WMT','DIS','SPY','DIA','TLT','SHY']
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)'\
      .format(-np.sqrt(sigma2_port_e)))
print('Ex-ante satisfaction index : {:,.2%} (in % terms)'\
      .format(-np.sqrt(sigma2_port_e)/capital))
Ex-ante satisfaction index : -39,747 (in $ terms)
Ex-ante satisfaction index : -3.97% (in % terms)

Attribution

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('')
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

Friday, 10 June 2016

mini-Meucci : Applying The Checklist - Steps 3-5

"In the future, instead of striving to be right at a high cost, it will be more appropriate to be flexible and plural at a lower cost. If you cannot accurately predict the future then you must flexibly be prepared to deal with various possible futures."
Edward de Bono, author and thinker extraordinaire (born 1933)


In this third leg of The Checklist tour, we will take 3 more steps, Projection, Pricing and Aggregation.

Quick Recap

Goal: Apply Meucci's The Checklist to create a toy example low volatility equity portfolio using DJIA stocks, with an investment horizon of 21 days.

Risk drivers: log of stock prices (values): $X_t = \ln V_t$, where $X$ is risk driver, $V$ is stock price (value).

Invariants: modelled using a simple random walk: $X_{t+1} = X_t + \epsilon_{t+1}$, where $\epsilon$ is invariant.

Estimation: Historical (simulation) with Flexible Probabilities approach (HFP)

Projection to the Horizon

The goal of this step is to create various possible scenarios (i.e. de Bono's "various possible futures") of the risk drivers at the investment horizon.

"Ultimately we are interested in the value of our positions at the investment horizon. In order to determine the distribution of our positions, we must first determine the distribution of the risk drivers at the investment horizon. This distribution, in turn, is obtained by projecting to the horizon the invariants distribution, obtained in the Estimation Step 2."
The Prayer (former Checklist)

So 2 sub-steps are involved - first project the invariants, then recover the risk drivers.

In our toy example, we can take a 'short-cut' and set the estimation interval equal to the investment horizon i.e. 21 days (in the previous 2 posts the estimation interval was 1 day). In this case, the estimation step also performs projection of the invariants.

To recover the future risk driver, simply apply the right-hand side of our random walk model i.e. risk driver at horizon = risk driver today plus invariant at horizon.

In general though, if you have an estimation interval shorter than your investment horizon (e.g. 5-day estimation interval and 20-day horizon, you'll need to apply the 2-step recipe of 'project then recover' recursively (e.g. 4 times one after the other).

Read The Prayer (former Checklist) and see slide #33 Projection to the Horizon (general case) and slide #8 (2-stock example).

Like to know more? Go to the ARPM Bootcamp!

Python Code Example

In [1]:
%matplotlib inline
from pandas_datareader import data
import numpy as np
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',
           'HD','INTC','IBM','JNJ','JPM','MCD','MRK','MSFT','NKE','PFE','PG',
           'TRV','UNH','UTX','VZ','V','WMT','DIS','SPY','DIA','TLT','SHY']
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 Horizon

We now have the projected distribution of the risk drivers and the goal of this step is calculate the projected profit and loss (P&L in currency terms not percentage returns) per unit of each asset.

First sub-step is to derive the projected prices from the projected risk drivers. Recall that in our case the risk drivers were log prices, so we can just exponentiate them to get back the prices.

Second sub-step of calculating the $P&L per unit of each asset is simply subtract today's price from the projected price.

Do this for all tickers/scenarios and you get the joint distribution of the ex-ante $P&L's.

Here is the code to do it - a bit different from the formulas in the slides but equivalent...

Python Code Example

In [2]:
# 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

In this step we take the ex-ante $P&L's per unit of each stock and multiply by the quantity of each stock held in the portfolio, and add them up to get the portfolio (aggregated) P&L. Do this for all scenarios and we get the distribution of the projected portfolio P&L at the horizon.

Two examples are shown below for an equally weighted portfolio of the 30 DJIA stocks. The first shows the distribution of the portfolio P&L using equal probabilities for the scenarios, while the second uses time-conditioned flexible probabilities...

Python Code Example

In [8]:
# Aggregation at the Investment Horizon
# Aggregate the individual stock P&Ls into 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)

# Equal probs
equal_probs = np.ones(n_scenarios) / n_scenarios

# 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 probabilities
import rnr_meucci_functions as rnr
mu_port, sigma2_port = rnr.fp_mean_cov(pnl_portfolio.T, equal_probs)  
mu_port_e, sigma2_port_e = rnr.fp_mean_cov(pnl_portfolio.T, exp_probs)  

print('Ex-ante portfolio $P&L mean over horizon (equal probs) : {:,.0f}'\
      .format(mu_port))
print('Ex-ante portfolio $P&L volatility over horizon (equal probs) : {:,.0f}'\
      .format(np.sqrt(sigma2_port)))
print('')
print('Ex-ante portfolio $P&L mean over horizon (flex probs) : {:,.0f}'\
      .format(mu_port_e))
print('Ex-ante portfolio $P&L volatility over horizon (flex probs) : {:,.0f}'\
      .format(np.sqrt(sigma2_port_e)))

fig = plt.figure(figsize=(9, 8))
ax = fig.add_subplot(111)
ax.hist(pnl_portfolio, 50, weights=exp_probs) 
ax.set_title('Ex-ante Distribution of Portfolio P&L \
             (flexbile probabilities with exponential decay)') 
plt.show()
Ex-ante portfolio $P&L mean over horizon (equal probs) : 10,431
Ex-ante portfolio $P&L volatility over horizon (equal probs) : 49,829

Ex-ante portfolio $P&L mean over horizon (flex probs) : 10,395
Ex-ante portfolio $P&L volatility over horizon (flex probs) : 39,750