Python for Finance
Risk Measurement
Andras Niedermayer
Outline
1 Risk Measures
2 Parametric VaR, ES
3 Monte Carlo Simulation
4 Historical method
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 2/36
Motivation
• In quantitative finance arguably the most important
task/challenge is the financial risk measurement and assessment.
The majority of the financial engineer’s task is connected to this
field.
• Risk Measurement for a financial institution is very data intense
and computational expensive process.
• Using the adequate, state of the art technologies are key to keep
up with the speed of the financial market innovation, and to be
able to meet the regulatory requirements.
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 3/36
Types of risk measures
There is a wide range of risk measures that spread through the
Market.
• Volatility (σ)
• Value at Risk (VaR)
• Expected Shortfall (ES)
• Conditional Value at Risk (CVaR) - In case of continuous
distributions same as ES
• Average Drawdown, Maximum Drawdown
• etc.
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 4/36
VaR and ES
Formal definitions:
1 Value at Risk:
VaRα (X ) = − inf{x ∈ R : FX (x) > α} = −FX−1 (α) (1)
2 Expected Shortfall:
ESα (X ) =
1
− [E (X ∗ 1{X ≤−VaRα (X )} ) − VaRα (X )(α − P(X ≤ −VaRα (X )))]
α
(2)
1 α
Z
= VaRp (X )dp (3)
α 0
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 5/36
Python Implementation
1 def compute_var (x , alpha ):
2 return - np . percentile (x , alpha *100.0)
3 def compute_es (x , alpha ):
4 x_alpha = - compute_var (x , alpha )
5 prct = sum (x <= x_alpha )/ float ( len ( x ))
6 y = x * ( x <= var_alpha )
7 es = -1.0/ alpha *( y . mean ()+
8 x_alpha *( alpha - prct ))
9 return es
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 6/36
Outline
1 Risk Measures
2 Parametric VaR, ES
3 Monte Carlo Simulation
4 Historical method
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 7/36
Parametric VaR, ES
1 From historical prices we need to calculate the returns:
1 rets = np . log ( S ) - np . log ( S . shift (1))
2 rets = rets [1:]
2 Assuming Normal distribution (r ∼ N(µ, σ 2 )), we need to
estimate the parameters:
1 mu = rets . mean ()
2 sigma = rets . std ()
3 Use the closed form solution to calculate the VaR and ES:
1 var = - scs . norm . ppf ( alpha , loc = mu ,
2 scale = sigma )
3 es = scs . norm . pdf ( scs . norm . ppf ( alpha ))*
4 1/ alpha * sigma - mu
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 8/36
Normality assumption
Many influential theories in finance assume the normality of stock
returns:
1 Portfolio theory and CAPM: Normality of returns implies that
only the mean and variance are relevant for portfolio
optimization.
2 Option pricing theory: The Black and Scholes model, and many
other pricing models assume (log-)returns follow a Brownian
motion, i.e., are normally distributed over any given time
interval.
Is normality a realistic assumption? How to test for it?
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 9/36
Benchmark: Simulated data
Let us simulate 10,000 stock price paths from a Geometric Brownian
motion, and save the log returns.
1 def gen_paths ( S0 ,r , sigma , T ,M , I ):
2 dt = float ( T )/ M
3 paths = np . zeros (( M +1 , I ))
4 paths [0]= S0
5 for t in range (1 , M +1):
6 rand = np . random . randn ( I )
7 paths [ t ]= paths [t -1]*
8 np . exp (( r -0.5* sigma **2)* dt
9 + sigma * np . sqrt ( dt )* rand )
10 return paths
11 paths = gen_paths (100 ,0.05 ,0.2 ,1.0 ,50 ,10000)
12 log_returns = np . log ( paths [1:]/ paths [0: -1])
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 10/36
Informal tests of normality
Graphically, one can assess (non-)normality of data via two types of
plots:
1 Histograms.
1 plt . hist ( log_returns . flatten () , bins =100 ,
2 normed = True )
2 QQ (quantile-quantile) plots.
1 sm . qqplot ( log_returns . flatten ())
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 11/36
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 12/36
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 13/36
Formal tests
• We need to import the [Link] sublibrary.
1 import scipy . stats as scs
• Skewness and skewness test (returns test value and p-value).
1 scs . skew ( log_returns . flatten ())
2 scs . skewtest ( log_returns . flatten ())
• Kurtosis (normalized to 0) and kurtosis test (returns test value
and p-value).
1 scs . kurtosis ( log_returns . flatten ())
2 scs . kurtosistest ( log_returns . flatten ())
• Catch-all normality test (returns test value and p-value), based
on D’Agostino and Pearson (1973).
1 scs . normaltest ( log_returns . flatten ())
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 14/36
Real-world data: the S&P 500 index
We want to see whether the S&P 500 returns are also normally
distributed.
1 plt . hist ( sp_returns , bins =50 , density = True )
2 sm . qqplot ( sp_returns )
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 15/36
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 16/36
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 17/36
Normality tests on S&P 500 data
The S&P 500 data have negative skewness (i.e., the returns are not
symmetric around the mean), and they also have excess kurtosis (fat
tails: excessive extreme returns). Therefore, it fails the normality test.
Measure Value Test statistic p-value
Skewness -0.943 -7.943 1.96e-15
Kurtosis 5.540 8.673 4.19e-18
Normality 138.3 9.18e-31
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 18/36
Outline
1 Risk Measures
2 Parametric VaR, ES
3 Monte Carlo Simulation
4 Historical method
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 19/36
Monte Carlo Simulation
1 Similarly to Parametric calculation the Monte Carlo simulations
are also driven by an assumed distribution. In case of Normality
assumption we can estimate the parameters similarly to the
Parametric case.
2 Generating random numbers based on the given distribution
3 Generating return paths
4 Calculate Risk Measures
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 20/36
Built in random number Generators
1 import numpy . random as npr
2 # Normal distribution
3 r = npr . normal ( loc = mu , scale = sigma , size =( T , N ))
4 R = npr . multivari a t e_ n o rm a l ( mean , cov , size )
5 # Log - Normal distribution :
6 r = npr . lognormal ([ mean , sigma , size ])
7 # Student - t distribution
8 r = npr . standard_t ( dof , N )
Full list: [Link]
reference/[Link]
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 21/36
Path generation with pre-generated returns
1 def make_path (R , S0 =1 , b_return_path = True ):
2 # R . shape = [T , N ]
3 R_with_0 = np . zeros (( R . shape [0]+1 , R . shape [1]))
4 R_with_0 [1: ,:] = R
5 c umulative_r et ur ns = R_with_0 . cumsum ( axis =0)
6 if b_return_path :
7 return np . exp ( c um ul at iv e_ re tu rn s )* S0
8 else :
9 return c um ul at iv e_ re tu rn s
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 22/36
Standardizing variables – mean
Let us generate 100 standard normal variables:
1 X = npr . randn (100)
Especially in small samples, the sample mean and variance will not be
zero and one!
1 X . mean ()= -0.128
2 X . std ()=1.015
Mean adjustment
First, we can normalize the mean by subtracting it from each element:
1 X1 =X - X . mean ()
2 X1 . mean ()=5.16 e -17
3 X1 . std ()=1.015
This solves the mean issue, not the standard deviation!
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 23/36
Standardizing variables – variance
Variance adjustment
Second, we can normalize the variance by dividing each element with
the standard deviation:
1 X2 = X1 / X . std ()
2 X2 . mean ()=5.16 e -17
3 X2 . std ()=1.000
Or, directly:
1 X3 =( X - X . mean ())/ X . std ()
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 24/36
Antithetic variables
Exploiting the feature of a distribution where the transformation of
the variable leads to a similar distribution, we can
• significantly speed up the simulation procedure
• reduce the error of the Monte Carlo Simulation
In case of normal distribution:
X ∼ N(0, σ 2 ), Y := −X ⇒ Y ∼ N(0, σ 2 )
VaR with Antithetic variate
1 rets = npr . randn ( n )
2 [ pct_up , pct_down ] =
3 np . percentile ( rets , [ alpha , 100.0 - alpha ])
4 var = -( sigma *( pct_up - pct_down )/2+ mu )
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 25/36
GARCH model
The Generalised Autoregressive Conditional Heteroskedasticity
model, or GARCH:
1 Allows the volatility of returns to be time-dependent, following a
AR process.
2 Therefore, it is a suitable model to account for the empirical
observation of volatility clustering
3 Clustering: Periods of high volatility are likely to be followed also
by periods of high volatility.
4 An important note is that the volatility is non-stochastic!
5 Estimation of GARCH is done by maximum likelihood.
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 26/36
GARCH specification
Let stock (index) returns be given by:
rt = µ + t , where t ∼ N 0, σt2
(4)
The variance dynamics is:
σt2 = ω + α2t−1 + βσt−1
2
(5)
For the model to remain stationary (variance does not explode) we
require:
α + β < 1. (6)
There are four parameters to estimate: µ, ω, α, β.
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 27/36
GARCH in Python
The arch package contains the tools for GARCH analysis. It can be
downloaded by pip install (available for Python 2 and 3 as well).
1 from arch import arch . model
First we need to define the model that we would like to estimate.
Default is GARCH(1,1).
1 am = arch_model ( returns )
Then we can estimate the model parameters. The default estimation
methodology is Maximum Likelihood.
1 res = am . fit ()
2 res . summary ()
Then using the forecast method we can generate log-returns:
1 fcast = res . forecast ( method = ’ simulation ’ ,
2 simulations =N , horizon = days )
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 28/36
Conditional Volatility of Facebook
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 29/36
Effect of ”GARCH” on Facebook
Comparing Facebook VaR and ES with and without GARCH(1,1)
GARCH Normal
VaR95 0.0387 0.0248
ES95 0.0474 0.0313
VaR99 0.0514 0.0354
ES99 0.0605 0.0407
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 30/36
Outline
1 Risk Measures
2 Parametric VaR, ES
3 Monte Carlo Simulation
4 Historical method
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 31/36
Bootstrap Method
1 There is no assumption on the return distribution, so no
parameter estimation is required. It will use the historical
distribution.
2 Random sampling on the historical returns. NOTE: Keep the
Correlation structure!
3 Generate return path
4 Calculate Risk Measures
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 32/36
Random sampling
The fastest way to generate random sample of historical returns, we
can assign an integer for each observation time, and using a random
sampling algorithm we can generate a the set of simulated returns.
1 df2 = df . reset_index ()
2 nr_of_rets = len ( df2 )
3 return_indexes =
4 npr . choice ( range ( nr_of_rets ) , N )
5 retruns = df2 . loc [ return_indexes ]
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 33/36
Backtesting VaR
The Basel Committee introduced a VaR Backtest Methodology in the
Basel regulation, which is called the BIS Traffic Light System. Their
approach is based on how many breach would happen in the 1 day
99% VaR value today, if the last 250 days return scenario would
repeat.
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 34/36
BIS Traffic Light System
Zone Breach Value Cummulative Probability
Green 0 8.11%
Green 1 28.58%
Green 2 54.32%
Green 3 75.81%
Green 4 89.22%
Yellow 5 95.88%
Yellow 6 98.63%
Yellow 7 99.60%
Yellow 8 99.89%
Yellow 9 99.97%
Red 10+ 99.99%
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 35/36
Thank you for you attention!
Wednesday, April, 2019 Python for Finance - Lecture 9
Andras Niedermayer - Université Paris-Dauphine 36/36