Sample Implementation of Multifactor Risk Model
The multifactor risk model boils down to a simple calcuation, the portofolio variance which can be computed as
{% Variance = \vec{w}^T \Sigma \vec{w} %}
Here {% \Sigma %} is a
covariance matrix. The essence of the model is to calculate this covariance matrix.
Multifactor Covariance Matrix
To calculate portfolio variance, we need to calculate the asset covariance matrix. In the multi index model we have
{% \Sigma = B \Sigma_I B^T + S %}
where {% \Sigma %} is the asset covariance,
{% \Sigma_I %} is the covariance matrix of Index returns,
{% B %} is a matrix of
asset betas, and {% S %} is the diagonal matrix of idiosyncratic
risks. (that is, {% S_{i,i} %} is the variance of the {% i^{th} %} asset's idiosyncratic factor )
{% \Sigma_I %} is a measured covariance matrix, using standard techniques. {% B %} is a matrix of betas, that is {% B = [\textbf{b}_1, \textbf{b}_2, ..., \textbf{b}_k] %} The vector {% \textbf{b}_k %} is each stock beta (exposure) to the kth index, or factor.
Calculating the Index Covariance
index_prices = filter(data, index_tickers)
index_cov = calc_covariance(returns(index_prices))
Calculating the Factor Betas
betas = {}
variances = {}
for ticker in tickers:
dataset = filter(data, [ticker, * index_tickers])
asset_returns = returns(dataset)
regress = rg.ols(asset_returns, index_tickers, ticker, intercept=False)
betas[ticker] = regress['coefficients']
ticker_returns = [
{ticker:x[ticker]}
for x in dataset
]
Calculating the Idiosyncratic Risk
The idiosyncratic risk is the variance of the idiosyncratic risk factor. THis is the variance of the asset after you subract out the risk due to the index factors.
var_returns = []
for item in asset_returns:
total = 0
for iticker in index_tickers:
total += regress['coefficients'][iticker] * item[iticker]
pass
var_returns.append({ticker:item[ticker] - total})
pass
variance = calc_covariance(var_returns)
variances[ticker] = float(variance[ticker][ticker])
Full Script
'''
calc_covariance is a function that calculates the covariance of the columns in a dataset. shoiuld
return its result as a pandas dataframe
'''
def variance(data, tickers, index_tickers, calc_covariance = vr.ewma_covariance):
index_prices = filter(data, index_tickers)
index_cov = calc_covariance(returns(index_prices))
betas = {}
variances = {}
for ticker in tickers:
dataset = filter(data, [ticker, * index_tickers])
asset_returns = returns(dataset)
regress = rg.ols(asset_returns, index_tickers, ticker, intercept=False)
betas[ticker] = regress['coefficients']
ticker_returns = [
{ticker:x[ticker]}
for x in dataset
]
var_returns = []
for item in asset_returns:
total = 0
for iticker in index_tickers:
total += regress['coefficients'][iticker] * item[iticker]
pass
var_returns.append({ticker:item[ticker] - total})
pass
variance = calc_covariance(var_returns)
variances[ticker] = float(variance[ticker][ticker])
pass
index_cov_matrix = np.zeros((len(index_tickers), len(index_tickers)))
for index1, ticker1 in enumerate(index_tickers):
for index2, ticker2 in enumerate(index_tickers):
index_cov_matrix[index1][index2] = index_cov[ticker1][ticker2]
pass
pass
beta_list = []
for ticker in tickers:
row = []
beta_list.append(row)
for index_ticker in index_tickers:
row.append(betas[ticker][index_ticker])
pass
pass
beta_matrix = np.array(beta_list)
covariance = beta_matrix @ index_cov_matrix @ beta_matrix.T
diag_list = [
variances[ticker]
for ticker in tickers
]
covariance = covariance + np.diag(diag_list)
def calc(weights):
nonlocal covariance, tickers
vect_list = [
weights[ticker]
for ticker in tickers
]
weights = np.array(vect_list).reshape(-1, 1)
variance = weights.T @ covariance @ weights
return float(variance[0][0])
return calc