financialkit.time_series#
Returns and their stylized facts (the Hill tail index, moments and the
Jarque-Bera test); tests of the random walk (serial correlation, Fama’s
runs test, Box-Pierce and Ljung-Box, Lo-MacKinlay variance ratios);
Yule-Walker autoregressions (scipy.linalg.solve_toeplitz), partial
autocorrelations, ARMA simulation (scipy.signal.lfilter) and
conditional maximum likelihood (scipy.optimize.least_squares) with
AIC/BIC order selection; the augmented Dickey-Fuller and Engle-Granger
tests with MacKinnon’s critical values; Hurst’s rescaled range and
fractional Gaussian noise; ARCH, GARCH, EGARCH, GJR-GARCH and RiskMetrics
EWMA volatility models fitted by maximum likelihood
(scipy.optimize.minimize), Engle’s ARCH-LM test, Parkinson’s range
estimator and realized variance.
financialkit.time_series: returns, efficiency tests, ARMA, unit roots, long memory and volatility.
Returns and their stylized facts (fat tails by the Hill estimator, the
Jarque-Bera test via scipy.stats.jarque_bera()); tests of the random
walk (serial correlation by scipy.fft, Fama’s runs test, Box-Pierce
and Ljung-Box, Lo-MacKinlay variance ratios); Yule-Walker autoregressions
(scipy.linalg.solve_toeplitz()), partial autocorrelations, ARMA
simulation (scipy.signal.lfilter()) and conditional maximum
likelihood (scipy.optimize.least_squares()) with AIC/BIC order
selection; the augmented Dickey-Fuller and Engle-Granger tests with
MacKinnon’s critical values; Hurst’s rescaled range and fractional Gaussian
noise; and ARCH, GARCH, EGARCH, GJR-GARCH and RiskMetrics EWMA volatility
models fitted by maximum likelihood (scipy.optimize.minimize()),
Engle’s ARCH-LM test, Parkinson’s range estimator and realized variance.
Returns are decimals per period and variances are per period; annualize
with financialkit.constants.TRADING_DAYS_PER_YEAR.
- class financialkit.time_series.ARCH(omega, alpha, mu=0.0)[source]#
Bases:
VolatilityModelEngle’s (1982) ARCH(1): \(\sigma_t^2 = \omega + \alpha\,\varepsilon_{t-1}^2\).
Covariance stationary when \(\alpha < 1\), with kurtosis \(3(1-\alpha^2)/(1-3\alpha^2)\) for \(\alpha^2 < 1/3\): the model fattens the tails of returns even with normal innovations.
Examples
>>> m = ARCH(omega=1e-4, alpha=0.5) >>> round(m.unconditional_variance, 8), round(m.kurtosis, 4) (0.0002, 9.0)
- news_impact(eps, var_prev)[source]#
Next period’s variance given today’s shock eps and variance var_prev; broadcasts.
- class financialkit.time_series.ARMAResult(ar, ma, mean, sigma2, loglik, nobs, n_params, residuals, data)[source]#
Bases:
objectAn ARMA(p, q) model fitted by conditional maximum likelihood.
The model is \(\phi(B)(x_t - \mu) = \theta(B)\varepsilon_t\) with \(\phi(B) = 1 - \phi_1 B - \dots - \phi_p B^p\) and \(\theta(B) = 1 + \theta_1 B + \dots + \theta_q B^q\).
- Parameters:
- forecast(h)[source]#
Minimum mean-square-error forecasts 1..`h` steps ahead and their standard errors.
Box and Jenkins’s recursion: future innovations are replaced by 0 and future values by their forecasts; the h-step error variance is \(\sigma^2 \sum_{j<h} \psi_j^2\).
- class financialkit.time_series.CointegrationResult(statistic, critical_values, intercept, beta, residuals, adf, r_squared)[source]#
Bases:
objectAn Engle-Granger two-step cointegration test.
- Parameters:
- adf: UnitRootResult#
The second-step test on the residuals.
- Type:
- class financialkit.time_series.EGARCH(omega, alpha, gamma, beta, mu=0.0)[source]#
Bases:
VolatilityModelNelson’s (1991) exponential GARCH(1,1).
\[\ln\sigma_t^2 = \omega + \beta\ln\sigma_{t-1}^2 + \alpha\bigl(|z_{t-1}| - E|z|\bigr) + \gamma z_{t-1}, \qquad z_t = \varepsilon_t / \sigma_t\]The log form keeps the variance positive with no sign restrictions, and \(\gamma < 0\) makes negative returns raise volatility more than positive ones. Stationary when \(|\beta| < 1\).
Examples
>>> m = EGARCH(omega=-0.2, alpha=0.15, gamma=-0.08, beta=0.98) >>> bool(m.news_impact(-0.02, 1e-4) > m.news_impact(0.02, 1e-4)) True
- news_impact(eps, var_prev)[source]#
Next period’s variance given today’s shock eps and variance var_prev; broadcasts.
- property unconditional_variance: float#
\(E[\sigma_t^2]\) for normal innovations (Nelson 1991, Theorem 2.1).
\[E[\sigma_t^2] = e^{\omega/(1-\beta)} \prod_{i \ge 0} E\bigl[e^{\beta^i g(z)}\bigr]\]with \(E[e^{a|z| + bz}] = e^{(a+b)^2/2}\Phi(a+b) + e^{(a-b)^2/2}\Phi(a-b)\).
- class financialkit.time_series.EWMA(lam=0.94)[source]#
Bases:
VolatilityModelRiskMetrics (1994) exponentially weighted moving average variance.
\[\sigma_t^2 = \lambda\,\sigma_{t-1}^2 + (1 - \lambda)\,r_{t-1}^2\]an integrated GARCH(1,1) with \(\omega = 0\) and zero mean. RiskMetrics fixed \(\lambda = 0.94\) for daily and \(0.97\) for monthly data across all assets;
fit()estimates it instead. With no mean reversion, the forecast at every horizon is today’s variance.- Parameters:
lam (
float) – Decay factor in (0, 1); the effective memory is about \(1/(1-\lambda)\) periods.
Examples
>>> EWMA(0.94).variance([0.01, -0.02, 0.0], v0=1e-4).round(8).tolist() [0.0001, 0.0001, 0.000118]
- forecast(returns, h)[source]#
Flat variance forecast: \(E_T[\sigma_{T+k}^2] = \sigma_{T+1}^2\) for every k.
- news_impact(eps, var_prev)[source]#
Next period’s variance given today’s shock eps and variance var_prev; broadcasts.
- class financialkit.time_series.GARCH(omega, alpha, beta, mu=0.0)[source]#
Bases:
VolatilityModelBollerslev’s (1986) GARCH(1,1): \(\sigma_t^2 = \omega + \alpha\,\varepsilon_{t-1}^2 + \beta\,\sigma_{t-1}^2\).
Equivalent to an ARCH(\(\infty\)) with geometrically declining weights. Covariance stationary when the persistence \(\alpha + \beta < 1\); shocks to the variance then decay at rate \((\alpha + \beta)^h\), a half-life of \(\ln\frac12 / \ln(\alpha + \beta)\) periods.
Examples
>>> m = GARCH(omega=2e-6, alpha=0.08, beta=0.9) >>> round(m.unconditional_variance, 8), round(m.half_life, 1) (0.0001, 34.3)
- forecast(returns, h)[source]#
Variance forecasts 1..`h` periods after the end of returns.
\[E_T[\sigma_{T+k}^2] = \bar\sigma^2 + (\alpha + \beta)^{k-1}(\sigma_{T+1}^2 - \bar\sigma^2)\]
- news_impact(eps, var_prev)[source]#
Next period’s variance given today’s shock eps and variance var_prev; broadcasts.
- class financialkit.time_series.GJRGARCH(omega, alpha, gamma, beta, mu=0.0)[source]#
Bases:
VolatilityModelGlosten-Jagannathan-Runkle (1993) GARCH with a leverage term.
\[\sigma_t^2 = \omega + (\alpha + \gamma\,\mathbf 1\{\varepsilon_{t-1} < 0\})\,\varepsilon_{t-1}^2 + \beta\,\sigma_{t-1}^2\]With symmetric innovations the persistence is \(\alpha + \gamma/2 + \beta\). \(\gamma > 0\) makes bad news raise volatility more than good news of the same size.
Examples
>>> m = GJRGARCH(omega=1e-6, alpha=0.02, gamma=0.1, beta=0.9) >>> round(m.persistence, 2), float(m.news_impact(-0.01, 1e-4) > m.news_impact(0.01, 1e-4)) (0.97, 1.0)
- forecast(returns, h)[source]#
Variance forecasts 1..`h` periods ahead (persistence \(\alpha + \gamma/2 + \beta\)).
- news_impact(eps, var_prev)[source]#
Next period’s variance given today’s shock eps and variance var_prev; broadcasts.
- class financialkit.time_series.HurstResult(window, rs, expected_rs, hurst, hurst_corrected)[source]#
Bases:
objectRescaled-range (R/S) analysis.
- Parameters:
- class financialkit.time_series.OHLC(open, high, low, close)[source]#
Bases:
objectOpen, high, low and close prices per period.
- class financialkit.time_series.OLSResult(coef, std_errors, t_values, residuals, sigma2, r_squared)[source]#
Bases:
objectOrdinary least squares \(y = X b + e\).
- Parameters:
- class financialkit.time_series.OrderSelectionResult(aic, bic)[source]#
Bases:
objectInformation criteria over a grid of ARMA orders.
- aic: ndarray#
Akaike criterion of ARMA(p, q) at
[p, q](NaN where a fit failed).- Type:
ndarray, shape (max_p + 1, max_q + 1)
- class financialkit.time_series.ReturnStatistics(nobs, mean, std, skewness, excess_kurtosis, jarque_bera, jarque_bera_pvalue)[source]#
Bases:
objectSample moments of a return series and the Jarque-Bera normality test.
- Parameters:
- jarque_bera: float#
Jarque-Bera statistic (
scipy.stats.jarque_bera()).- Type:
- class financialkit.time_series.TailIndexResult(k, alpha, std_error, threshold)[source]#
Bases:
objectHill estimates of a tail index for a range of order statistics.
- class financialkit.time_series.TestResult(name, statistic, pvalue, df=None, nobs=0)[source]#
Bases:
objectA test statistic with its p-value.
- df: int | None = None#
Degrees of freedom of a chi-squared reference distribution.
- Type:
int or None
- class financialkit.time_series.UnitRootResult(statistic, critical_values, lags, nobs, regression, gamma)[source]#
Bases:
objectAn augmented Dickey-Fuller test.
- Parameters:
- class financialkit.time_series.VarianceRatioResult(q, ratio, z, z_robust, nobs)[source]#
Bases:
objectLo-MacKinlay variance ratios for one or more horizons.
- class financialkit.time_series.VolatilityFit(model, loglik, nobs, variance, std_errors=<factory>, converged=True)[source]#
Bases:
objectA volatility model estimated by Gaussian maximum likelihood.
- Parameters:
- model: VolatilityModel#
The fitted model, in the units of the input returns.
- Type:
- class financialkit.time_series.VolatilityModel[source]#
Bases:
ABCA conditional-variance model \(r_t = \mu + \sigma_t z_t\), \(z_t \sim N(0, 1)\).
Subclasses define their parameters (
param_names), the one-step updatenews_impact(), the bounds and constraints of estimation, and how the parameters change when returns are rescaled. Variance filtering, the log-likelihood, simulation and maximum-likelihood fitting are shared. The pre-sample variance is the sample variance of the demeaned returns (“backcasting”).- classmethod fit(returns)[source]#
Maximum-likelihood estimate on returns (Gaussian quasi-likelihood).
The returns are scaled to unit variance before optimizing (so every parameter is of order one) and the estimate is mapped back, which the Gaussian likelihood allows because each model family is closed under rescaling.
scipy.optimize.minimize()(SLSQP) handles the bounds and stationarity constraints.- Return type:
- Returns:
VolatilityFit
- loglikelihood(returns, v0=None)[source]#
Gaussian log-likelihood \(-\tfrac12\sum_t [\ln 2\pi + \ln\sigma_t^2 + \varepsilon_t^2/\sigma_t^2]\).
- abstractmethod news_impact(eps, var_prev)[source]#
Next period’s variance given today’s shock eps and variance var_prev; broadcasts.
- simulate(n, burn=500, rng=None)[source]#
Simulate n returns with Gaussian innovations, after burn discarded periods.
- Return type:
- Returns:
VolatilityPath
- Parameters:
- class financialkit.time_series.VolatilityPath(returns, variance)[source]#
Bases:
objectA simulated return series and its conditional variance.
- financialkit.time_series.adf_test(x, lags=None, regression='c', max_lags=None)[source]#
Augmented Dickey-Fuller test of a unit root.
Regresses
\[\Delta y_t = \text{det}_t + \gamma y_{t-1} + \sum_{i=1}^{k} \delta_i \Delta y_{t-i} + e_t\]and compares the t-ratio of \(\gamma\) with MacKinnon’s critical values; the null \(\gamma = 0\) (a unit root) is rejected for large negative values.
- Parameters:
- Return type:
- Returns:
UnitRootResult
Examples
>>> import numpy as np >>> rng = np.random.default_rng(0) >>> adf_test(np.cumsum(rng.standard_normal(500)), lags=0).rejects() False >>> adf_test(rng.standard_normal(500), lags=0).rejects() True
- financialkit.time_series.anis_lloyd_expected_rs(n)[source]#
Expected \(R/S\) of n independent normals (Anis and Lloyd 1976).
\[E[(R/S)_n] = \frac{\Gamma(\frac{n-1}{2})}{\sqrt\pi\,\Gamma(\frac n2)} \sum_{i=1}^{n-1}\sqrt{\frac{n-i}{i}}\]Examples
>>> import numpy as np >>> float(np.round(anis_lloyd_expected_rs(10), 4)) 3.0233
- Return type:
- financialkit.time_series.ar_cycle_period(ar)[source]#
Pseudo-period of the dominant complex pair of AR roots, in periods (
infif all roots are real).For AR(2) with \(\phi_2 < 0\) and \(\phi_1^2 + 4\phi_2 < 0\), the period is \(2\pi / \arccos\bigl(\phi_1 / (2\sqrt{-\phi_2})\bigr)\).
- Return type:
Examples
>>> round(ar_cycle_period([1.34, -0.65]), 2) 10.65
- financialkit.time_series.arch_lm_test(returns, lags=5, demean=True)[source]#
Engle’s (1982) Lagrange-multiplier test for ARCH effects.
Regresses \(\varepsilon_t^2\) on a constant and \(\varepsilon_{t-1}^2, \dots, \varepsilon_{t-q}^2\); under the null of no ARCH, \(T R^2 \sim \chi^2_q\).
- Parameters:
- Return type:
- Returns:
TestResult
Examples
>>> import numpy as np >>> arch_lm_test(np.random.default_rng(0).standard_normal(2000)).rejects() False >>> arch_lm_test(GARCH(0.05, 0.15, 0.8).simulate(2000, rng=0).returns).rejects() True
- financialkit.time_series.arma_acf(ar=None, ma=None, nlags=20, n_terms=2000)[source]#
Theoretical autocorrelations
k = 0..nlagsof a stationary ARMA process.Uses \(\gamma_k = \sigma^2 \sum_j \psi_j \psi_{j+k}\) with the MA(\(\infty\)) weights truncated at n_terms.
Examples
>>> arma_acf(ar=[0.5], nlags=3).round(6).tolist() [1.0, 0.5, 0.25, 0.125]
- financialkit.time_series.arma_psi_weights(ar, ma, n)[source]#
Weights \(\psi_0 = 1, \psi_1, \dots, \psi_{n-1}\) of \(x_t = \sum_j \psi_j \varepsilon_{t-j}\).
Examples
>>> arma_psi_weights([0.5], [0.4], 4).round(4).tolist() [1.0, 0.9, 0.45, 0.225]
- financialkit.time_series.autocorrelation(x, nlags=20)[source]#
Sample autocorrelations \(\hat\rho_k = \hat\gamma_k / \hat\gamma_0\),
k = 0..nlags.Kendall’s (1953) serial correlations: for price changes of a random walk they are close to zero at every lag, while for price levels they are close to one.
Examples
>>> import numpy as np >>> rho = autocorrelation(np.random.default_rng(0).standard_normal(10_000), 3) >>> float(rho[0]), bool(np.all(np.abs(rho[1:]) < 4 / np.sqrt(10_000))) (1.0, True)
- financialkit.time_series.autocovariance(x, nlags)[source]#
Sample autocovariances \(\hat\gamma_k = \frac1n\sum_{t=k}^{n-1}(x_t-\bar x)(x_{t-k}-\bar x)\),
k = 0..nlags.Computed with
scipy.fft.rfft()(the divisor n keeps the sequence positive semidefinite).
- financialkit.time_series.bartlett_band(nobs, level=0.95)[source]#
Half-width \(z_{1-\alpha/2}/\sqrt n\) of the band that white-noise autocorrelations stay within.
Examples
>>> round(bartlett_band(400), 4) 0.098
- financialkit.time_series.box_pierce(x, lags=10, fitted_params=0)[source]#
Box-Pierce (1970) portmanteau statistic \(Q = n\sum_{k=1}^h \hat\rho_k^2 \sim \chi^2_{h - p - q}\).
- Return type:
- Parameters:
- financialkit.time_series.close_to_close_variance(close)[source]#
Per-period variance proxies \(r_t^2 = (\ln C_t / C_{t-1})^2\) (zero mean assumed).
- Return type:
Examples
>>> import numpy as np >>> close_to_close_variance([100.0, 100.0 * np.exp(0.01)]).round(10).tolist() [0.0001]
- financialkit.time_series.engle_granger(y, x, lags=0, trend='c')[source]#
Engle-Granger two-step test of cointegration between y and the columns of x.
Step 1 regresses \(y_t = a + b' x_t + u_t\) by least squares, which is super-consistent when the series are cointegrated. Step 2 runs a Dickey-Fuller test without deterministic terms on \(\hat u_t\). Because \(b\) was estimated to make \(\hat u_t\) look stationary, the critical values are more negative than Dickey and Fuller’s and depend on the number of variables (MacKinnon 2010).
- Parameters:
- Return type:
- Returns:
CointegrationResult
Examples
>>> from financialkit.time_series.utils.synthetic import simulate_cointegrated_pair >>> y, x = simulate_cointegrated_pair(1000, beta=2.0, phi=0.8, rng=0) >>> res = engle_granger(y, x) >>> round(float(res.beta[0]), 1), res.rejects() (2.0, True)
- financialkit.time_series.fgn_autocovariance(H, nlags)[source]#
Autocovariances
k = 0..nlagsof unit-variance fractional Gaussian noise.Examples
>>> fgn_autocovariance(0.5, 3).tolist() [1.0, 0.0, 0.0, 0.0]
- financialkit.time_series.fit_arma(x, p, q=0, include_mean=True, start=None)[source]#
Fit ARMA(p, q) by conditional maximum likelihood (Box-Jenkins estimation).
Conditioning on the first start observations and on zero pre-sample innovations, the Gaussian log-likelihood is maximized by minimizing the sum of squared innovations, which
scipy.optimize.least_squares()does over a parameterization that keeps the AR part stationary and the MA part invertible (Monahan’s transform of the partial autocorrelations); \(\hat\sigma^2\) is the mean squared innovation. A pure AR model is linear in its parameters and is solved exactly bynumpy.linalg.lstsq().- Parameters:
x (array_like)
p (
int) – Autoregressive and moving-average orders.q (
int) – Autoregressive and moving-average orders.include_mean (
bool) – Estimate the mean \(\mu\) (otherwise \(\mu = 0\)).start (
int|None) – Observations conditioned on (default p). Use a common value to compare models of different orders on the same sample.
- Return type:
- Returns:
ARMAResult
Examples
>>> x = simulate_arma(ar=[0.7], ma=[0.4], n=5000, mean=1.0, rng=2) >>> res = fit_arma(x, 1, 1) >>> res.ar.round(1).tolist(), res.ma.round(1).tolist(), round(res.mean, 1) ([0.7], [0.4], 1.0)
- financialkit.time_series.hill_estimator(x, k=None, side='upper')[source]#
Hill’s estimator of the tail index \(\alpha\) from the k largest observations.
\[\hat\alpha_k = \Bigl[\frac1k \sum_{i=1}^{k} \ln X_{(i)} - \ln X_{(k+1)}\Bigr]^{-1}\]with \(X_{(1)} \ge X_{(2)} \ge \dots\) the descending order statistics. It is the maximum-likelihood estimator for an exact Pareto tail above \(X_{(k+1)}\), with asymptotic standard error \(\alpha/\sqrt k\). Plotting \(\hat\alpha_k\) against k (the Hill plot) shows the trade-off: small k is noisy, large k reaches into the body of the distribution and is biased.
- Parameters:
- Return type:
- Returns:
TailIndexResult
Examples
>>> import numpy as np >>> rng = np.random.default_rng(1) >>> x = rng.pareto(3.0, 100_000) + 1.0 # exact Pareto tail, alpha = 3 >>> res = hill_estimator(x, k=5000) >>> bool(abs(res.alpha[0] - 3.0) < 4 * res.std_error[0]) True
- financialkit.time_series.hurst_exponent(x, min_window=16, n_windows=12)[source]#
Estimate Hurst’s exponent by rescaled-range (R/S) analysis.
For each window length \(n\) (log-spaced from min_window to half the sample), the series is cut into non-overlapping blocks and the block values of \(R/S\) are averaged. \(H\) is the slope of \(\log (R/S)_n\) on \(\log n\). Because \((R/S)_n\) of independent data approaches \(\sqrt{\pi n/2}\) only slowly, the raw slope exceeds 1/2 in small windows;
HurstResult.hurst_correctedregresses the excess over the Anis-Lloyd expectation instead.- Parameters:
- Return type:
- Returns:
HurstResult
Examples
>>> res = hurst_exponent(simulate_fgn(8192, 0.8, rng=0)) >>> bool(0.7 < res.hurst_corrected < 0.9) True
- financialkit.time_series.lag_matrix(x, lags)[source]#
Columns
x[t-1], ..., x[t-lags]fort = lags .. n-1.Examples
>>> lag_matrix([1.0, 2.0, 3.0, 4.0], 2).tolist() [[2.0, 1.0], [3.0, 2.0]]
- financialkit.time_series.ljung_box(x, lags=10, fitted_params=0)[source]#
Ljung-Box (1978) portmanteau statistic.
\[Q = n(n+2)\sum_{k=1}^{h} \frac{\hat\rho_k^2}{n-k} \;\sim\; \chi^2_{h - p - q}\]The \((n+2)/(n-k)\) weights correct the Box-Pierce statistic’s small-sample downward bias. Apply it to ARMA residuals with
fitted_params = p + q, or to squared returns to detect volatility clustering.- Parameters:
- Return type:
- Returns:
TestResult
Examples
>>> import numpy as np >>> from scipy.signal import lfilter >>> e = np.random.default_rng(0).standard_normal(1000) >>> ljung_box(e, 10).rejects(), ljung_box(lfilter([1.0], [1.0, -0.3], e), 10).rejects() (False, True)
- financialkit.time_series.log_returns(prices)[source]#
Continuously compounded returns \(\ln(P_t / P_{t-1})\).
- Return type:
Examples
>>> import numpy as np >>> log_returns([100.0, 100.0 * np.e]).round(12).tolist() [1.0]
- financialkit.time_series.mackinnon_critical_values(nobs, regression='c', n_vars=1)[source]#
MacKinnon (2010) finite-sample critical values of the Dickey-Fuller and Engle-Granger tests.
- Parameters:
- Return type:
- Returns:
dict –
{"1%": ..., "5%": ..., "10%": ...}.
Examples
>>> {k: round(v, 3) for k, v in mackinnon_critical_values(10**9).items()} {'1%': -3.43, '5%': -2.862, '10%': -2.567}
- financialkit.time_series.mincer_zarnowitz(proxy, forecast)[source]#
Mincer-Zarnowitz regression \(\text{proxy}_t = a + b\,\text{forecast}_t + e_t\).
An unbiased forecast has \(a = 0, b = 1\); the \(R^2\) measures how much of the proxy’s variation the forecast explains. Andersen and Bollerslev (1998) showed that with \(r_t^2\) as the proxy even a perfect variance forecast has a small \(R^2\) (at most \(1/\kappa\) of the explainable part, \(\kappa\) the kurtosis of \(z_t\)), and that realized variance restores it.
- Return type:
- Returns:
OLSResult – Coefficients
[a, b]andr_squared.
- financialkit.time_series.numerical_hessian(f, x, rel_step=0.0001)[source]#
Central-difference Hessian of a scalar function at x.
Examples
>>> import numpy as np >>> numerical_hessian(lambda v: v[0] ** 2 + 3 * v[0] * v[1], np.array([1.0, 2.0])).round(6).tolist() [[2.0, 3.0], [3.0, 0.0]]
- financialkit.time_series.ols(y, X)[source]#
Least squares by
numpy.linalg.lstsq(), with classical standard errors.- Parameters:
y (array_like, shape (n,))
X (array_like, shape (n, k))
- Return type:
- Returns:
OLSResult
Examples
>>> import numpy as np >>> x = np.arange(5.0) >>> res = ols(1.0 + 2.0 * x, np.column_stack([np.ones(5), x])) >>> res.coef.round(12).tolist(), res.r_squared ([1.0, 2.0], 1.0)
- financialkit.time_series.parkinson_variance(high, low)[source]#
Parkinson’s per-period variance estimates \((\ln H_t/L_t)^2 / (4\ln 2)\).
Unbiased for driftless Brownian motion observed continuously; average over periods (or smooth) to estimate a variance, and multiply by periods per year to annualize. Discrete sampling of the high and low biases it slightly downward.
- Return type:
Examples
>>> import numpy as np >>> float(parkinson_variance([np.exp(0.02)], [1.0])[0].round(8)) 0.00014427
- financialkit.time_series.partial_autocorrelation(x, nlags=20)[source]#
Sample partial autocorrelations by the Durbin-Levinson recursion,
k = 0..nlags.The lag-k value is the last coefficient of the AR(k) Yule-Walker fit; it cuts off after lag p for an AR(p) process, which is how Box and Jenkins identify the autoregressive order.
Examples
>>> import numpy as np >>> pacf = partial_autocorrelation(simulate_arma(ar=[0.6], n=20_000, rng=1), 3) >>> round(float(pacf[1]), 1), bool(abs(pacf[2]) < 0.03) (0.6, True)
- financialkit.time_series.plot_acf(x, nlags=20, partial=False, ax=None, label='')[source]#
Correlogram of x (lags 1..`nlags`) with the 95% white-noise band.
- financialkit.time_series.plot_conditional_volatility(returns, variance, ax=None, periods_per_year=None)[source]#
Returns with a \(\pm 2\sigma_t\) envelope of the conditional volatility.
- financialkit.time_series.plot_hill(result, true_alpha=None, ax=None, label='')[source]#
Hill plot: \(\hat\alpha_k\) against k with a two-standard-error band.
- Returns:
matplotlib.axes.Axes
- Parameters:
result (TailIndexResult)
true_alpha (float | None)
label (str)
- financialkit.time_series.plot_news_impact(models, var_prev, eps_range=None, ax=None)[source]#
News impact curves: next period’s variance against today’s shock, from the same var_prev.
- financialkit.time_series.plot_rescaled_range(result, ax=None, label='')[source]#
\((R/S)_n\) against \(n\) on log-log axes, with the fitted slope and the Anis-Lloyd benchmark.
- Returns:
matplotlib.axes.Axes
- Parameters:
result (HurstResult)
label (str)
- financialkit.time_series.plot_tail(x, ax=None, label='', side='both')[source]#
Empirical survival function \(P(|X| > u)\) on log-log axes.
- financialkit.time_series.plot_variance_ratio(result, ax=None, label='')[source]#
Variance ratios against horizon q, with 95% bands under the homoskedastic random walk.
- Returns:
matplotlib.axes.Axes
- Parameters:
result (VarianceRatioResult)
label (str)
- financialkit.time_series.realized_variance(intraday_returns, axis=-1)[source]#
Realized variance \(RV_t = \sum_i r_{t,i}^2\), summing intraday returns along axis.
As the sampling interval shrinks, \(RV_t\) converges to the integrated variance \(\int_{t-1}^{t}\sigma_s^2\,ds\) of the day.
Examples
>>> realized_variance([[0.01, -0.01], [0.02, 0.0]]).round(8).tolist() [0.0002, 0.0004]
- financialkit.time_series.rescaled_range(x)[source]#
Hurst’s adjusted rescaled range \(R/S\) of one block.
\(R\) is the range of the cumulative sums of deviations from the block mean and \(S\) the block standard deviation.
- Return type:
Examples
>>> rescaled_range([1.0, -1.0, 1.0, -1.0]) 1.0
- financialkit.time_series.return_statistics(returns)[source]#
Mean, volatility, skewness, excess kurtosis and the Jarque-Bera test.
- Parameters:
returns (array_like)
- Return type:
- Returns:
ReturnStatistics
Examples
>>> import numpy as np >>> rng = np.random.default_rng(0) >>> s = return_statistics(rng.standard_t(3, 20_000)) >>> bool(s.excess_kurtosis > 3), bool(s.jarque_bera_pvalue < 1e-6) (True, True)
- financialkit.time_series.runs_test(x)[source]#
Fama’s (1965) runs test on the signs of price changes.
A run is a maximal sequence of changes with the same sign (+, - or 0). With \(n_i\) changes of sign \(i\) out of \(N\), the number of runs under independence has mean and variance
\[m = \frac{N(N+1) - \sum_i n_i^2}{N}, \qquad \sigma_m^2 = \frac{\sum_i n_i^2\bigl[\sum_i n_i^2 + N(N+1)\bigr] - 2N\sum_i n_i^3 - N^3}{N^2(N-1)}.\]Too few runs means positive dependence (trends), too many means reversals. The statistic is the normal approximation with a half-run continuity correction; with two signs it reduces to the Wald-Wolfowitz test.
- Return type:
- Returns:
TestResult – Two-sided p-value;
statisticis negative when there are fewer runs than expected.
Examples
>>> res = runs_test([1, 1, 1, 1, 1, -1, -1, -1, -1, -1] * 4) >>> bool(res.statistic < -3) True
- financialkit.time_series.select_arma_order(x, max_p=4, max_q=2, include_mean=True)[source]#
AIC and BIC of every ARMA(p, q) with
p <= max_pandq <= max_q.All models are fitted on the same sample (conditioning on the first max_p observations), so their likelihoods are comparable. Akaike’s criterion \(-2\ell + 2k\) estimates the expected Kullback-Leibler divergence and tends to overfit in large samples; Schwarz’s \(-2\ell + k\ln n\) is consistent.
- Return type:
- Returns:
OrderSelectionResult
- Parameters:
Examples
>>> x = simulate_arma(ar=[0.5, 0.3], n=3000, rng=4) >>> select_arma_order(x, max_p=4, max_q=0).best("bic") (2, 0)
- financialkit.time_series.simple_returns(prices)[source]#
Simple returns \(P_t / P_{t-1} - 1\).
- Return type:
Examples
>>> simple_returns([100.0, 110.0, 99.0]).round(12).tolist() [0.1, -0.1]
- financialkit.time_series.simulate_arma(ar=None, ma=None, n=1000, sigma=1.0, mean=0.0, burn=500, rng=None)[source]#
Simulate \(\phi(B)(x_t - \mu) = \theta(B)\varepsilon_t\) with Gaussian innovations.
Filters white noise through
scipy.signal.lfilter()and discards burn start-up values.- Parameters:
- Return type:
- Returns:
ndarray, shape (n,)
- financialkit.time_series.simulate_cointegrated_pair(n, beta=1.0, intercept=0.0, phi=0.9, sigma_x=1.0, sigma_u=1.0, rng=None)[source]#
A random walk x and
y = intercept + beta x + uwith a stationary AR(1) error u.
- financialkit.time_series.simulate_dickey_fuller(n_obs, n_sims, regression='c', rng=None)[source]#
Dickey-Fuller t-statistics of n_sims driftless Gaussian random walks.
Regresses \(\Delta y_t\) on \(y_{t-1}\) (plus a constant for
"c", and a trend for"ct") for all walks at once, which traces out the nonstandard null distribution that Dickey and Fuller tabulated.
- financialkit.time_series.simulate_fgn(n, H, sigma=1.0, rng=None)[source]#
Exact fractional Gaussian noise by circulant embedding (Davies and Harte 1987).
The autocovariance is embedded in a circulant matrix of size \(2n\), whose eigenvalues are its FFT (
scipy.fft.fft()) and are nonnegative for fractional Gaussian noise; colouring complex white noise by their square roots and transforming back gives a sample with exactly the target covariance.np.cumsumof the result is fractional Brownian motion.
- financialkit.time_series.simulate_intraday_returns(daily_variance, m, rng=None)[source]#
Intraday returns whose day-t variance sums to
daily_variance[t].Each of the m intraday returns of day t is \(\sigma_t z_{t,i} / \sqrt m\), so the daily return \(\sum_i r_{t,i}\) has exactly variance \(\sigma_t^2\), as in the continuous-time GARCH diffusion behind Andersen and Bollerslev (1998).
- financialkit.time_series.simulate_ohlc(n_days, sigma, steps_per_day=390, s0=100.0, mu=0.0, rng=None)[source]#
Daily open, high, low and close of geometric Brownian motion.
Each day is sampled on steps_per_day points of an exact GBM path (
financialkit.integrators.gbm_paths()); the next day opens at the previous close. The sampled high and low miss the true extremes between grid points, so range-based estimators are biased slightly downward on coarse grids.
- financialkit.time_series.tail_survival(x, side='both')[source]#
Empirical survival function \(P(|X| > u)\) at the sorted sample points.
On log-log axes a power-law tail \(P(X > u) \sim u^{-\alpha}\) is a straight line of slope \(-\alpha\), Mandelbrot’s diagnostic.
- Parameters:
x (array_like)
side (
str) – Use \(|x|\), the positive values, or the magnitudes of the negative values.
- Return type:
- Returns:
u (ndarray) – Sorted magnitudes (ascending).
prob (ndarray) – Fraction of the sample strictly above each u, i.e.
(n - i) / nshifted so the largest point has probability1/n.
- financialkit.time_series.variance_ratio(log_prices, q=(2, 4, 8, 16))[source]#
Lo-MacKinlay (1988) variance-ratio test of the random walk.
If log prices follow a random walk, the variance of q-period returns is q times that of one-period returns. With overlapping q-period returns and both variances bias-corrected,
\[VR(q) = \frac{\hat\sigma_c^2(q)}{\hat\sigma_a^2}, \qquad z(q) = \frac{VR(q) - 1}{\sqrt{2(2q-1)(q-1)/(3qT)}},\]and the heteroskedasticity-consistent \(z^*(q) = \sqrt T\,(VR(q)-1)/\sqrt{\hat\theta(q)}\) with \(\hat\theta(q) = \sum_{j=1}^{q-1} [2(q-j)/q]^2 \hat\delta(j)\) stays valid under volatility clustering (Lo and MacKinlay 1988, eqs. (12)-(14) and Theorem 3). Since \(VR(q) - 1 \approx 2\sum_{k<q}(1-k/q)\rho_k\), positive autocorrelation pushes the ratio above one.
- Parameters:
- Return type:
- Returns:
VarianceRatioResult
Examples
>>> import numpy as np >>> p = np.cumsum(np.random.default_rng(3).standard_normal(5001)) >>> res = variance_ratio(p, q=[2, 4]) >>> bool(np.all(np.abs(res.z) < 3)) True
- financialkit.time_series.yule_walker(x, order, demean=True)[source]#
Fit an AR(order) model by solving the Yule-Walker equations.
The equations \(\rho_k = \sum_{i=1}^p \phi_i \rho_{k-i}\),
k = 1..p, form a Toeplitz system in the sample autocorrelations, and \(\sigma^2 = \hat\gamma_0 (1 - \sum_i \phi_i \hat\rho_i)\). Yule’s (1927) sunspot fit was \(\phi_1 \approx 1.34\), \(\phi_2 \approx -0.65\).- Parameters:
- Return type:
- Returns:
ARMAResult – With
maempty; the log-likelihood is conditional on the first order values.
Examples
>>> import numpy as np >>> x = simulate_arma(ar=[1.34, -0.65], n=50_000, rng=0) >>> yule_walker(x, 2).ar.round(2).tolist() [1.34, -0.65]