Source code for fynance.models.econometric_models

#!/usr/bin/env python3
# coding: utf-8
# @Author: ArthurBernard
# @Email: arthur.bernard.92@gmail.com
# @Date: 2019-02-20 10:39:56
# @Last modified by: ArthurBernard
# @Last modified time: 2019-05-23 17:31:40

""" Classical econometric time-series models.

Pure-NumPy implementations of moving-average, autoregressive and
GARCH-family processes used to simulate or fit return series. Parameter
estimation is delegated to the Cython routines in
:mod:`fynance.estimator` via :func:`get_parameters`, a single
maximum-likelihood entry point for all four model types.

Main entry points
-----------------
- :func:`MA` — Moving Average process.
- :func:`ARMA` — AutoRegressive Moving Average.
- :func:`ARMA_GARCH` — ARMA with GARCH(p, q) conditional variance.
- :func:`ARMAX_GARCH` — ARMA-GARCH with exogenous regressors.
- :func:`get_parameters` — fit the parameters of any of the above.

"""

# Built-in packages

# External packages
import numpy as np
import pandas as pd

# Local packages
from fynance.models.econometric_models_cy import *

__all__ = [
    'get_parameters', 'MA', 'ARMA', 'ARMA_GARCH', 'ARMAX_GARCH'
]

# =========================================================================== #
#                             PARAMETERS FUNCTION                             #
# =========================================================================== #


[docs] def get_parameters(params, p=0, q=0, Q=0, P=0, cons=True): """ Get parameters for ARMA-GARCH models. Helper that splits the flat ``params`` vector returned by maximum-likelihood estimation into the structured groups expected by the model evaluation routines: AR coefficients ``phi``, MA coefficients ``theta``, GARCH ARCH/GARCH coefficients ``alpha``/``beta``, and constants ``c`` and ``omega``. Pass the same ``p, q, Q, P, cons`` configuration that was used at the estimation step (see :mod:`fynance.estimator`). Parameters ---------- params : np.ndarray[np.float64, ndim=1] Array of model parameters. p, q, Q, P : int, optional Order of model, default is 0. cons : bool, optional True if model contains constant, default is True. Returns ------- phi : np.ndarray[np.float64, ndim=1] AR parameters. theta : np.ndarray[np.float64, ndim=1] MA parameters. alpha : np.ndarray[np.float64, ndim=1] First part GARCH parameters. beta : np.ndarray[np.float64, ndim=1] Last part GARCH parameters. c : float Constant of ARMA part. omega : float Constants of GARCH part. See Also -------- ARMAX_GARCH, ARMA_GARCH, ARMA, MA. """ i = 0 if cons: c = params[i] i += 1 else: c = 0. if p > 0: phi = params[i: p + i] i += p else: phi = np.array([0.], dtype=np.float64) if q > 0: theta = params[i: q + i] i += q else: theta = np.array([0.], dtype=np.float64) if Q > 0 or P > 0: omega = params[i] i += 1 if Q > 0: alpha = params[i: Q + i] i += Q else: alpha = np.array([0.], dtype=np.float64) if P > 0: beta = params[i: P + i] i += P else: beta = np.array([0.], dtype=np.float64) else: omega = 0. alpha = np.array([0.], dtype=np.float64) beta = np.array([0.], dtype=np.float64) return phi, theta, alpha, beta, c, omega
# =========================================================================== # # MODELs # # =========================================================================== #
[docs] def MA(y, theta, c, q): r""" Moving Average model of order `q` s.t: .. math:: y_t = c + \theta_1 * u_{t-1} + ... + \theta_q * u_{t-q} + u_t Parameters ---------- y : np.ndarray[np.float64, ndim=1] Time series. theta : np.ndarray[np.float64, ndim=1] Coefficients of model. c : np.float64 Constant of the model. q : int Order of MA(q) model. Returns ------- u : np.ndarray[ndim=1, dtype=np.float64] Residual of the model. Examples -------- >>> y = np.array([3, 4, 6, 8, 5, 3]) >>> MA(y=y, theta=np.array([0.8]), c=3, q=1) array([ 0. , 1. , 2.2 , 3.24 , -0.592 , 0.4736]) See Also -------- ARMA_GARCH, ARMA, ARMAX_GARCH """ # Set type of variables if isinstance(y, pd.DataFrame) or isinstance(y, pd.Series): y = np.asarray(y) elif isinstance(y, list): y = np.asarray(y) y = y.astype(np.float64).reshape([y.size]) if isinstance(theta, list): theta = np.asarray(theta) theta = theta.astype(np.float64).reshape([theta.size]) # Compute residuals u = MA_cy(y, theta, np.float64(c), int(q)) return u
[docs] def ARMA(y, phi, theta, c, p, q): r""" AutoRegressive Moving Average model of order `q` and `p` s.t: .. math:: y_t = c + \phi_1 * y_{t-1} + ... + \phi_p * y_{t-p} + \theta_1 * u_{t-1} + ... + \theta_q * u_{t-q} + u_t Parameters ---------- y : np.ndarray[np.float64, ndim=1] Time series. phi : np.ndarray[np.float64, ndim=1] Coefficients of AR model. theta : np.ndarray[np.float64, ndim=1] Coefficients of MA model. c : np.float64 Constant of the model. p : int Order of AR(p) model. q : int Order of MA(q) model. Returns ------- u : np.ndarray[np.float64, ndim=1] Residual of the model. See Also -------- ARMA_GARCH, ARMAX_GARCH, MA. """ # Set type variables and parameters y = np.asarray(y, dtype=np.float64).reshape([y.size]) theta = np.asarray(theta, dtype=np.float64) phi = np.asarray(phi, dtype=np.float64) # Compute residuals u = ARMA_cy(y, phi, theta, float(c), int(p), int(q)) return u
[docs] def ARMA_GARCH(y, phi, theta, alpha, beta, c, omega, p, q, Q, P): r""" AutoRegressive Moving Average model of order q and p, such that: .. math:: y_t = c + \phi_1 * y_{t-1} + ... + \phi_p * y_{t-p} + \theta_1 * u_{t-1} + ... + \theta_q * u_{t-q} + u_t With Generalized AutoRegressive Conditional Heteroskedasticity volatility model of order `Q` and `P`, such that: .. math:: u_t = z_t * h_t h_t^2 = \omega + \alpha_1 * u^2_{t-1} + ... + \alpha_Q * u^2_{t-Q} + \beta_1 * h^2_{t-1} + ... + \beta_P * h^2_{t-P} Parameters ---------- y : np.ndarray[np.float64, ndim=1] Time series. phi : np.ndarray[np.float64, ndim=1] Coefficients of AR model. theta : np.ndarray[np.float64, ndim=1] Coefficients of MA model. alpha : np.ndarray[np.float64, ndim=1] Coefficients of MA part of GARCH. beta : np.ndarray[np.float64, ndim=1] Coefficients of AR part of GARCH. c : np.float64 Constant of ARMA model. omega : np.float64 Constant of GARCH model. p : int Order of AR(p) model. q : int Order of MA(q) model. Q : int Order of MA part of GARCH. P : int Order of AR part of GARCH. Returns ------- u : np.ndarray[np.float64, ndim=1] Residual of the model. h : np.ndarray[np.float64, ndim=1] Conditional volatility of the model. See Also -------- ARMAX_GARCH, ARMA, MA. """ y = np.asarray(y, dtype=np.float64).reshape([y.size]) theta = np.asarray(theta, dtype=np.float64) phi = np.asarray(phi, dtype=np.float64) alpha = np.asarray(alpha, dtype=np.float64) beta = np.asarray(beta, dtype=np.float64) u, h = ARMA_GARCH_cy( y, phi, theta, alpha, beta, float(c), float(omega), int(p), int(q), int(Q), int(P) ) return u, h
[docs] def ARMAX_GARCH(y, x, phi, psi, theta, alpha, beta, c, omega, p, q, Q, P): r""" AutoRegressive Moving Average model of order q and p, such that: .. math:: y_t = c + \phi_1 * y_{t-1} + ... + \phi_p * y_{t-p} + \psi_t * x_t + \theta_1 * u_{t-1} + ... + \theta_q * u_{t-q} + u_t With Generalized AutoRegressive Conditional Heteroskedasticity volatility model of order `Q` and `P`, such that: .. math:: u_t = z_t * h_t h_t^2 = \omega + \alpha_1 * u^2_{t-1} + ... + \alpha_Q * u^2_{t-Q} + \beta_1 * h^2_{t-1} + ... + \beta_P * h^2_{t-P} Parameters ---------- y : np.ndarray[np.float64, ndim=1] Time series. x : np.ndarray[np.float64, ndim=2] Time series of external features. phi : np.ndarray[np.float64, ndim=1] Coefficients of AR model. psi : np.ndarray[np.float64, ndim=1] Coefficients of external features. theta : np.ndarray[np.float64, ndim=1] Coefficients of MA model. alpha : np.ndarray[np.float64, ndim=1] Coefficients of MA part of GARCH. beta : np.ndarray[np.float64, ndim=1] Coefficients of AR part of GARCH. c : np.float64 Constant of the model. p : int Order of AR(p) model. q : int Order of MA(q) model. Q : int Order of MA part of GARCH. P : int Order of AR part of GARCH. Returns ------- u : np.ndarray[np.float64, ndim=1] Residual of the model. h : np.ndarray[np.float64, ndim=1] Conditional volatility of the model. See Also -------- ARMA_GARCH, ARMA, MA. """ # Set array variables y = np.asarray(y, dtype=np.float64).reshape([y.size]) x = np.asarray(x, dtype=np.float64) theta = np.asarray(theta, dtype=np.float64) phi = np.asarray(phi, dtype=np.float64) psi = np.asarray(psi, dtype=np.float64) alpha = np.asarray(alpha, dtype=np.float64) beta = np.asarray(beta, dtype=np.float64) # Compute residuals and volatility u, h = ARMAX_GARCH_cy( y, x, phi, theta, psi, alpha, beta, float(c), float(omega), int(p), int(q), int(Q), int(P) ) return u, h