Module quantfin.volatility.garch_asym
Asymmetric GARCH
Fits the symmetric GARCH(1,1) model to returns data.
Usage
import quantfin as qf
params = qf.volatility.garch_asym.fit_model(R)
volatilities = qf.volatilty.garch_asym.sigma(R, params)
Source code
"""
# Asymmetric GARCH
Fits the symmetric GARCH(1,1) model to returns data.
## Usage
```python
import quantfin as qf
params = qf.volatility.garch_asym.fit_model(R)
volatilities = qf.volatilty.garch_asym.sigma(R, params)
```
"""
import warnings
import numpy as np
from scipy.optimize import minimize
def sigma(R, params):
"""Calculates volatility using the asymmetric GARCH(1,1) model
The asymmetric GARCH(1,1) model is defined by the recursion relation:
sigma^{2}_{n+1} = omega + alpha * (R_{n} - delta)^2 + beta * sigma^{2}_{n}
where `sigma_n` is the volatilty at time-step `n`, and `R_n` are the returns
of the asset at time-step `n`. We assume that `sigma_0 = omega`.
Args:
R (numpy.array): Financial returns data from which to calculate
volatility.
params (numpy.array): The parameters of the GARCH model to use. Should
be in the form `[alpha, beta, omega, delta]`.
Returns:
A numpy array of volatilties.
"""
alpha, beta, omega, delta = params
N = len(R)
# Recursion relation for calculating sigma^2 using GARCH(1,1)
variance = np.zeros(N)
variance[0] = omega
for n in range(1, N):
variance[n] = omega + alpha * (R[n-1] - delta)**2 + beta * variance[n - 1]
volatility = np.sqrt(variance)
return volatility
def fit_model(R, init=[5, 5, 5], verbose=False):
"""Calculates volatility using the asymmetric GARCH(1,1) model.
For a definition of the model, see `sigma`.
The parameters for the model are cauluated using the maximum likelihood
method. The likelihood function is maximised using scipy.
Args:
R (numpy.array): Financial returns data from which to calculate
volatility.
init (list, optional): Initial values of the parameters to use in
optimisation
verbose (bool, optional): Whether or not to print warnings and
convergence information.
Returns:
An array containing the parameters of the model in the form
`[alpha, beta, omega]`.
Raises:
RuntimeError: If the optimisation algorithm fails to converge.
"""
N = len(R)
# Log of likelihood function
def L(params):
# Calculate variance using model
sigma_sq = sigma(R, params) ** 2
# ln(L) = -0.5 * sum_{n} {ln(sigma^2_n) + (r^2_n / sigma^2_n)}
value = np.log(sigma_sq) + ((R ** 2) / sigma_sq)
value = -0.5 * value.sum()
# Return -L instead of L (minimise -L => maximise L)
return -value
# Constraints for optimisation
# Use def instead of lambdas for speed
def w_con(x): # omega > 0
return np.array([x[2]])
def a_con(x): # alpha > 0
return np.array([x[0]])
def b_con(x): # beta > 0
return np.array([x[1]])
def d_con(x): # delta > 0
return np.array([x[3]])
def ab_con(x): # alpha + beta < 1
return np.array([ 1 - x[0] - x[1] ])
omega_con = { 'type': 'ineq', 'fun': w_con }
alpha_con = { 'type': 'ineq', 'fun': a_con }
beta_con = { 'type': 'ineq', 'fun': b_con }
delta_con = { 'type': 'ineq', 'fun': d_con }
alpha_beta_con = { 'type': 'ineq', 'fun': ab_con }
cons = (alpha_con, beta_con, omega_con, delta_con)
# Don't show warnings unless verbose flag specified
if not verbose:
warnings.filterwarnings('ignore')
# Minimise -L to find values of alpha, beta, and omega
res = minimize(L, [10, 10, 10, 10], constraints=cons,
options={ 'maxiter': 500, 'disp': verbose })
# Turn warnings back on if they were turned off above
if not verbose:
warnings.resetwarnings()
if res.success == False:
raise RuntimeError("Unable to fit GARCH(1,1) model: " + res.message)
# Calculate the volatility using the parameters found by minimize
params = res.x
return params
def expected(params):
"""Calculate the expected volatility predicted by the model.
Args:
params (numpy.array): The parameters from the fitted model of the form
`[alpha, beta, omega, delta]`.
Raises:
ValueError: If the expected volatility is not well defined.
"""
a, b, o, d = params
# Expected volatility is the solution to the quadratic equation
# (a+b-1)sigma^2 - 2*a*d*sigma + o + a*d^2 = 0
if a + b > 1:
raise ValueError("Expected volatilty not defined for alpha + beta > 1")
disc = o*(1 - a - b) + a * d**2 * (1 - b) # Discriminant
# No real solutions if discriminant is negative
if disc < 0:
raise ValueError("Expected volatility not well defined.")
sigma1 = (a * d + np.sqrt(disc)) / (a + b - 1)
sigma2 = (a * d - np.sqrt(disc)) / (a + b - 1)
return max(sigma1, sigma2)
Functions
def expected(params)
-
Calculate the expected volatility predicted by the model.
Args
params
:numpy.array
- The parameters from the fitted model of the form
[alpha, beta, omega, delta]
.
Raises
ValueError
- If the expected volatility is not well defined.
Source code
def expected(params): """Calculate the expected volatility predicted by the model. Args: params (numpy.array): The parameters from the fitted model of the form `[alpha, beta, omega, delta]`. Raises: ValueError: If the expected volatility is not well defined. """ a, b, o, d = params # Expected volatility is the solution to the quadratic equation # (a+b-1)sigma^2 - 2*a*d*sigma + o + a*d^2 = 0 if a + b > 1: raise ValueError("Expected volatilty not defined for alpha + beta > 1") disc = o*(1 - a - b) + a * d**2 * (1 - b) # Discriminant # No real solutions if discriminant is negative if disc < 0: raise ValueError("Expected volatility not well defined.") sigma1 = (a * d + np.sqrt(disc)) / (a + b - 1) sigma2 = (a * d - np.sqrt(disc)) / (a + b - 1) return max(sigma1, sigma2)
def fit_model(R, init=[5, 5, 5], verbose=False)
-
Calculates volatility using the asymmetric GARCH(1,1) model.
For a definition of the model, see
sigma()
. The parameters for the model are cauluated using the maximum likelihood method. The likelihood function is maximised using scipy.Args
R
:numpy.array
- Financial returns data from which to calculate volatility.
init
:list
, optional- Initial values of the parameters to use in optimisation
verbose
:bool
, optional- Whether or not to print warnings and convergence information.
Returns
An array containing the parameters of the model in the form
[alpha, beta, omega]
.Raises
RuntimeError
- If the optimisation algorithm fails to converge.
Source code
def fit_model(R, init=[5, 5, 5], verbose=False): """Calculates volatility using the asymmetric GARCH(1,1) model. For a definition of the model, see `sigma`. The parameters for the model are cauluated using the maximum likelihood method. The likelihood function is maximised using scipy. Args: R (numpy.array): Financial returns data from which to calculate volatility. init (list, optional): Initial values of the parameters to use in optimisation verbose (bool, optional): Whether or not to print warnings and convergence information. Returns: An array containing the parameters of the model in the form `[alpha, beta, omega]`. Raises: RuntimeError: If the optimisation algorithm fails to converge. """ N = len(R) # Log of likelihood function def L(params): # Calculate variance using model sigma_sq = sigma(R, params) ** 2 # ln(L) = -0.5 * sum_{n} {ln(sigma^2_n) + (r^2_n / sigma^2_n)} value = np.log(sigma_sq) + ((R ** 2) / sigma_sq) value = -0.5 * value.sum() # Return -L instead of L (minimise -L => maximise L) return -value # Constraints for optimisation # Use def instead of lambdas for speed def w_con(x): # omega > 0 return np.array([x[2]]) def a_con(x): # alpha > 0 return np.array([x[0]]) def b_con(x): # beta > 0 return np.array([x[1]]) def d_con(x): # delta > 0 return np.array([x[3]]) def ab_con(x): # alpha + beta < 1 return np.array([ 1 - x[0] - x[1] ]) omega_con = { 'type': 'ineq', 'fun': w_con } alpha_con = { 'type': 'ineq', 'fun': a_con } beta_con = { 'type': 'ineq', 'fun': b_con } delta_con = { 'type': 'ineq', 'fun': d_con } alpha_beta_con = { 'type': 'ineq', 'fun': ab_con } cons = (alpha_con, beta_con, omega_con, delta_con) # Don't show warnings unless verbose flag specified if not verbose: warnings.filterwarnings('ignore') # Minimise -L to find values of alpha, beta, and omega res = minimize(L, [10, 10, 10, 10], constraints=cons, options={ 'maxiter': 500, 'disp': verbose }) # Turn warnings back on if they were turned off above if not verbose: warnings.resetwarnings() if res.success == False: raise RuntimeError("Unable to fit GARCH(1,1) model: " + res.message) # Calculate the volatility using the parameters found by minimize params = res.x return params
def sigma(R, params)
-
Calculates volatility using the asymmetric GARCH(1,1) model
The asymmetric GARCH(1,1) model is defined by the recursion relation:
sigma^{2}_{n+1} = omega + alpha * (R_{n} - delta)^2 + beta * sigma^{2}_{n}
where
sigma_n
is the volatilty at time-stepn
, andR_n
are the returns of the asset at time-stepn
. We assume thatsigma_0 = omega
.Args
R
:numpy.array
- Financial returns data from which to calculate volatility.
params
:numpy.array
- The parameters of the GARCH model to use. Should
be in the form
[alpha, beta, omega, delta]
.
Returns
A numpy array of volatilties.
Source code
def sigma(R, params): """Calculates volatility using the asymmetric GARCH(1,1) model The asymmetric GARCH(1,1) model is defined by the recursion relation: sigma^{2}_{n+1} = omega + alpha * (R_{n} - delta)^2 + beta * sigma^{2}_{n} where `sigma_n` is the volatilty at time-step `n`, and `R_n` are the returns of the asset at time-step `n`. We assume that `sigma_0 = omega`. Args: R (numpy.array): Financial returns data from which to calculate volatility. params (numpy.array): The parameters of the GARCH model to use. Should be in the form `[alpha, beta, omega, delta]`. Returns: A numpy array of volatilties. """ alpha, beta, omega, delta = params N = len(R) # Recursion relation for calculating sigma^2 using GARCH(1,1) variance = np.zeros(N) variance[0] = omega for n in range(1, N): variance[n] = omega + alpha * (R[n-1] - delta)**2 + beta * variance[n - 1] volatility = np.sqrt(variance) return volatility