Monday 8 June 2015

Portfolio Risk-Minimizing Weights Solved in Python

In a previous blogpost (Marginal and Component Value-at-Risk: A Python Example http://elenamarinova.blogspot.com/2015/05/marginal-and-component-value-at-risk.html) the Python way for calculating marginal, component and portfolio VaR was introduced. However, it left the portfolio management task a little bit incomplete as the important stage of extracting the risk-minimizing weights of the shares in the portfolio was missing.

Here is again an example of calculating VaR of a simple model portfolio with added a code for extracting weights that minimize the portfolio VaR. A change in the codes introduced in the previous post was made (we worked with annualized numbers in the previous post) to be more precise in the VaR we want, namely 99% confidence interval, 1-day VaR. (Implemented on Python 3.4)


import pandas as pd
import numpy as np
import pandas.io.data as web
import scipy.optimize as scopt
from scipy.stats import norm

Value=1e5 # $100,000
CI=0.99 # set the confidence interval

tickers =['AAPL', 'MSFT', 'YHOO']
numbers=len(tickers)

data=pd.DataFrame()
for share in tickers:
    data[share]=web.DataReader(share, data_source='yahoo', start='2011-01-01', end='2015-06-08')['Adj Close']
data.columns=tickers

ret=data/data.shift(1)-1 # calculate the simple returns
covariances=ret.cov() #gives the covariance of returns
variances=np.diag(covariances) #extracts variances of the individual shares from covariance matrix
volatility=np.sqrt(variances) #gives standard deviation

weights = np.ones(data.columns.size)/data.columns.size
weights
Out: array([ 0.33333333,  0.33333333,  0.33333333])

Pf_variance=np.dot(weights.T,np.dot(ret.cov(),weights)) #Portfolio variance
Pf_volatility=np.sqrt(Pf_variance) #Portfolio standard deviation

USDvariance=np.square(Value)*Pf_variance
USDvolatility=np.sqrt(USDvariance)

covariance_asset_portfolio=np.dot(weights.T,covariances)
covUSD=np.multiply(covariance_asset_portfolio,Value)
beta=np.divide(covariance_asset_portfolio,Pf_variance)

def VaR():
    # this code calculates Portfolio Value-at-risk (Pf_VaR) in USD-terms and Individual Value-at-risk (IndividualVaR) of shares in portfolio. 
    Pf_VaR=norm.ppf(CI)*USDvolatility
    IndividualVaR=np.multiply(volatility,Value*weights)*norm.ppf(CI)
    IndividualVaR = [ '$%.2f' % elem for elem in IndividualVaR ]
    print ('Portfolio VaR: ', '$%0.2f' %Pf_VaR)
    print ('Individual VaR: ',[[tickers[i], IndividualVaR[i]] for i in range (min(len(tickers), len(IndividualVaR)))])

VaR() # call the function
Out: Portfolio VaR:  $2830.87
Individual VaR:  [['AAPL', '$1289.91'], ['MSFT', '$1123.15'], ['YHOO', '$1493.15']]

def marginal_component_VaR():
     # this code calculates Marginal Value-at-risk in USD-terms and Component Value-at-risk of shares in portfolio.
    marginalVaR=np.divide(covUSD,USDvolatility)*norm.ppf(CI)
    componentVaR=np.multiply(weights,beta)*USDvolatility*norm.ppf(CI)
    marginalVaR = [ '%.3f' % elem for elem in marginalVaR ]
    componentVaR=[ '$%.2f' % elem for elem in componentVaR ]
    print ('Marginal VaR:', [[tickers[i], marginalVaR[i]] for i in range (min(len(tickers), len(marginalVaR)))])
    print ('Component VaR: ', [[tickers[i], componentVaR[i]] for i in range (min(len(tickers), len(componentVaR)))])

marginal_component_VaR()# call the function
Out: Marginal VaR: [['AAPL', '0.027'], ['MSFT', '0.023'], ['YHOO', '0.034']]
Component VaR:  [['AAPL', '$908.03'], ['MSFT', '$783.18'], ['YHOO', '$1139.65']]


# Risk-minimizing approach; first, we set the objective function and then we minimize it by changing weights

def VaR(weights):
    return norm.ppf(CI)*np.sqrt(np.square(Value)*(np.dot(weights.T,np.dot(ret.cov(),weights))))

def minVaR(VaR):
    nstocks = data.columns.size
    bounds = [(0.,1.) for i in np.arange(nstocks)] #non-negative weights 
    constraints = ({'type': 'eq',
                        'fun': lambda weights: np.sum(weights) - 1}) #sum of weights to equal 100%
    results = scopt.minimize(VaR, weights,
                                 method='SLSQP',
                                 constraints = constraints,
                                 bounds = bounds)
    result_weights=np.round(results.x, 4)
    result_weights= [ '%.8f' % elem for elem in result_weights ]
    new_VaR=VaR(results['x'])
             
    print ('Optimal VaR: ', new_VaR)
    print ('Optimal Weights: ', [[tickers[i], result_weights[i]] for i in range (len(tickers))])


 minVaR(VaR) # call the function
Out: Optimal VaR:  2756.04924565

Optimal Weights:  [['AAPL', '0.328'], ['MSFT', '0.475'], ['YHOO', '0.197']]

No comments:

Post a Comment