financialkit.risk#

Value at risk by historical simulation, normal and Student-t closed forms, RiskMetrics delta-normal, the Cornish-Fisher expansion, full-revaluation Monte Carlo through financialkit.integrators.correlated_increments(), Boudoukh-Richardson-Whitelaw age weighting and filtered historical simulation; Roy’s safety-first criterion; Acerbi-Tasche expected shortfall with normal and Student-t closed forms, the coherence axioms and Euler risk allocation; block maxima (scipy.stats.genextreme) and peaks over threshold (scipy.stats.genpareto); Kupiec and Christoffersen backtests and the Basel traffic light; Gaussian and t copulas with tail dependence; drawdowns and the Calmar ratio.

financialkit.risk: value at risk, expected shortfall, extreme values, backtests, copulas and drawdowns.

Value at risk by historical simulation, normal and Student-t closed forms, RiskMetrics delta-normal, the Cornish-Fisher expansion, full revaluation Monte Carlo (through financialkit.integrators), Boudoukh-Richardson-Whitelaw age weighting and filtered historical simulation; Roy’s safety-first criterion; Acerbi-Tasche expected shortfall, the coherence axioms and Euler risk allocation; block maxima and peaks over threshold (scipy.stats.genextreme, scipy.stats.genpareto); Kupiec and Christoffersen backtests and the Basel traffic light; Gaussian and t copulas with tail dependence; drawdowns and the Calmar ratio. Profits and losses go in with losses negative, and every risk figure comes out as a positive loss.

class financialkit.risk.AllocationResult(contributions, total, measure)[source]#

Bases: object

Euler allocation of a portfolio risk figure to its positions.

Parameters:
contributions: ndarray#

Risk contribution of each position; they sum to total.

Type:

ndarray, shape (d,)

measure: str#

"var", "es" or "volatility".

property shares: ndarray#

Contributions as fractions of the total.

Type:

ndarray

total: float#

Portfolio risk figure.

class financialkit.risk.BacktestResult(statistic, p_value, df, n_exceptions, n_obs, expected)[source]#

Bases: object

A likelihood-ratio test of VaR exceptions.

Parameters:
df: int#

Degrees of freedom of the chi-squared reference distribution.

expected: float#

Expected number of exceptions, n_obs * (1 - alpha).

n_exceptions: int#

Number of days the loss exceeded the VaR.

n_obs: int#

Number of days tested.

p_value: float#

Asymptotic chi-squared p-value.

reject(level=0.05)[source]#

Whether the model is rejected at significance level.

Return type:

bool

Parameters:

level (float)

statistic: float#

Likelihood-ratio statistic.

class financialkit.risk.CoherenceResult(monotonicity, subadditivity, positive_homogeneity, translation_invariance, rho_x, rho_y, rho_sum)[source]#

Bases: object

Which of Artzner et al.’s four axioms a risk measure satisfied on a test pair.

Parameters:
property coherent: bool#

True when all four axioms held.

monotonicity: bool#

rho(Z) >= rho(X) for a position Z <= X in every state.

positive_homogeneity: bool#

rho(lam X) = lam rho(X).

rho_sum: float#

rho(X + Y).

rho_x: float#

rho(X).

rho_y: float#

rho(Y).

subadditivity: bool#

rho(X + Y) <= rho(X) + rho(Y).

translation_invariance: bool#

rho(X + c) = rho(X) - c.

class financialkit.risk.DrawdownResult(wealth, peak, drawdown, max_drawdown, peak_index, trough_index, recovery_index)[source]#

Bases: object

The drawdown path of a wealth index and its summary statistics.

Parameters:
drawdown: ndarray#

Fractional fall from the peak, 1 - wealth / peak (non-negative).

Type:

ndarray, shape (n + 1,)

property longest_duration: int#

Longest run of consecutive periods spent below a previous peak.

Type:

int

max_drawdown: float#

Largest drawdown.

peak: ndarray#

Running maximum of the wealth.

Type:

ndarray, shape (n + 1,)

peak_index: int#

Index of the peak preceding the largest drawdown.

recovery_index: int#

First index after the trough at which the peak is regained (-1 if never).

trough_index: int#

Index of the trough of the largest drawdown.

wealth: ndarray#

Wealth index, starting at 1.

Type:

ndarray, shape (n + 1,)

class financialkit.risk.ExpectedShortfall(alpha=0.99)[source]#

Bases: RiskMeasure

Acerbi-Tasche expected shortfall as a RiskMeasure.

Parameters:

alpha (float)

name: str = 'ES'#
class financialkit.risk.GEVResult(xi, mu, sigma, block_size, maxima=<factory>)[source]#

Bases: object

A generalized extreme value fit to block maxima of losses.

Parameters:
block_size: int#

Observations per block.

maxima: ndarray#

The block maxima of the losses.

Type:

ndarray, shape (n_blocks,)

mu: float#

Location.

sigma: float#

Scale.

xi: float#

> 0 Fréchet (fat tails), 0 Gumbel, < 0 Weibull.

Type:

Shape

class financialkit.risk.GPDResult(xi, beta, threshold, n_exceed, n_obs, excesses=<factory>)[source]#

Bases: object

A generalized Pareto fit to losses above a threshold.

Parameters:
beta: float#

Scale of the excesses.

excesses: ndarray#

Losses above u minus u.

Type:

ndarray, shape (n_exceed,)

n_exceed: int#

Number of losses above u.

n_obs: int#

Total number of observations.

threshold: float#

Loss threshold u.

xi: float#

1 / xi is the number of finite moments.

Type:

Tail index

class financialkit.risk.RiskMeasure(alpha=0.99)[source]#

Bases: ABC

A map from a random profit and loss, given by scenarios, to a capital figure.

Parameters:

alpha (float) – Confidence level in (0, 1).

name: str = ''#
class financialkit.risk.TrafficLightResult(zone, n_exceptions, cumulative_probability, multiplier)[source]#

Bases: object

The Basel Committee’s (1996) backtesting zone for an internal model.

Parameters:
  • zone (str)

  • n_exceptions (int)

  • cumulative_probability (float)

  • multiplier (float)

cumulative_probability: float#

Binomial probability of at most n_exceptions exceptions for an accurate model.

multiplier: float#

Capital multiplier, 3 plus the plus factor (nan outside the 250-day, 99% setting).

n_exceptions: int#

Exceptions observed.

zone: str#

"green", "yellow" or "red".

class financialkit.risk.VaRResult(var, es, alpha, method, scenarios, weights, ci=(nan, nan))[source]#

Bases: object

Value at risk and expected shortfall estimated from scenarios.

Parameters:
alpha: float#

Confidence level, e.g. 0.99.

ci: tuple[float, float] = (nan, nan)#

Order-statistic confidence interval for the VaR (Monte Carlo only).

es: float#

Expected shortfall at the same level, a positive loss.

method: str#

"historical", "hybrid", "filtered_historical" or "monte_carlo".

scenarios: ndarray#

Scenario profits and losses the figures were read from.

Type:

ndarray, shape (n,)

var: float#

Value at risk, a positive loss.

weights: ndarray#

Probability of each scenario (equal unless age-weighted).

Type:

ndarray, shape (n,)

class financialkit.risk.ValueAtRisk(alpha=0.99)[source]#

Bases: RiskMeasure

VaR as a RiskMeasure on equally likely scenarios.

Examples

>>> ValueAtRisk(0.95)([-5.0, -1.0, 0.0, 1.0, 2.0] * 4)
5.0
Parameters:

alpha (float)

name: str = 'VaR'#
financialkit.risk.apply_marginals(u, marginals)[source]#

Sklar’s construction: give each copula coordinate its own marginal law.

Parameters:
Return type:

ndarray

Returns:

ndarray, shape (n, d)

financialkit.risk.basel_traffic_light(n_exceptions, n_obs=250, alpha=0.99)[source]#

The Basel Committee’s (1996) green, yellow and red zones.

The zone is set by the binomial probability that an accurate model produces at most n_exceptions exceptions: green below 95%, red from 99.99%, yellow in between. For the regulatory setting (250 days, 99% VaR) that is 0-4 green, 5-9 yellow and 10 or more red, and the capital multiplier is 3 plus the Committee’s plus factor.

Parameters:
  • n_exceptions (int)

  • n_obs (int) – Days in the backtest.

  • alpha (float) – VaR confidence level.

Return type:

TrafficLightResult

Returns:

TrafficLightResult

Examples

>>> res = basel_traffic_light(6)
>>> res.zone, res.multiplier
('yellow', 3.5)
financialkit.risk.block_maxima(pnl, block_size)[source]#

Largest loss in each consecutive block (an incomplete last block is dropped).

Parameters:
  • pnl (ArrayLike) – Profits and losses in time order.

  • block_size (int) – Observations per block (e.g. 21 for months of trading days).

Return type:

ndarray

Returns:

ndarray, shape (n // block_size,)

financialkit.risk.calmar_ratio(returns, periods_per_year=252, log=False)[source]#

Young’s Calmar ratio: compound annual growth rate over maximum drawdown.

Young used the trailing 36 months; pass that window’s returns to reproduce his convention.

Parameters:
  • returns (ArrayLike) – Periodic returns.

  • periods_per_year (float) – 12 for monthly, 252 for daily returns.

  • log (bool) – Treat the returns as log returns.

Return type:

float

Returns:

float – inf if there was no drawdown.

Examples

>>> round(calmar_ratio([0.10, -0.05, 0.08, 0.02], periods_per_year=4), 4)
3.0234
financialkit.risk.check_coherence(measure, X, Y, lam=2.0, c=1.0)[source]#

Test the four coherence axioms of measure on two positions.

Parameters:
  • measure (RiskMeasure)

  • X (ArrayLike) – Profits and losses of two positions in the same n equally likely states of the world.

  • Y (ArrayLike) – Profits and losses of two positions in the same n equally likely states of the world.

  • lam (float) – Positive scale for the homogeneity check.

  • c (float) – Cash amount for the translation check.

Return type:

CoherenceResult

Returns:

CoherenceResult – A failed axiom is a counterexample; passing on one pair proves nothing.

financialkit.risk.christoffersen_independence(hits, alpha=0.99)[source]#

Christoffersen’s test that exceptions do not cluster.

Fits a two-state Markov chain to the hit sequence, with \(\pi_{01} = P(\text{hit} \mid \text{no hit yesterday})\) and \(\pi_{11} = P(\text{hit} \mid \text{hit yesterday})\), and tests \(\pi_{01} = \pi_{11}\) against a single rate (\(\chi^2_1\)).

Parameters:
  • hits (ArrayLike)

  • alpha (float) – VaR confidence level (only used to report the expected count).

Return type:

BacktestResult

Returns:

BacktestResult

financialkit.risk.christoffersen_test(hits, alpha=0.99)[source]#

Christoffersen’s conditional-coverage test, \(LR_{cc} = LR_{uc} + LR_{ind} \sim \chi^2_2\).

Parameters:
Return type:

BacktestResult

Returns:

BacktestResult

financialkit.risk.copula_kendall_tau(rho)[source]#

Kendall’s tau of a Gaussian or t copula, \(\tfrac{2}{\pi}\arcsin\rho\).

Parameters:

rho (ArrayLike)

Return type:

ndarray | float

Returns:

float or ndarray

Examples

>>> round(copula_kendall_tau(0.5), 4)
0.3333
financialkit.risk.cornish_fisher_var(mu, sigma, skew, excess_kurtosis, alpha=0.99)[source]#

VaR from the Cornish-Fisher expansion of the quantile in skewness and kurtosis.

With \(z = \Phi^{-1}(1 - \alpha)\), skewness \(S\) and excess kurtosis \(K\), the adjusted quantile is

\[z_{cf} = z + \frac{(z^2 - 1) S}{6} + \frac{(z^3 - 3z) K}{24} - \frac{(2z^3 - 5z) S^2}{36},\]

and \(\mathrm{VaR} = -(\mu + \sigma z_{cf})\).

Parameters:
  • mu (ArrayLike) – First four moments of the profit and loss.

  • sigma (ArrayLike) – First four moments of the profit and loss.

  • skew (ArrayLike) – First four moments of the profit and loss.

  • excess_kurtosis (ArrayLike) – First four moments of the profit and loss.

  • alpha (ArrayLike) – Confidence level.

Return type:

ndarray | float

Returns:

float or ndarray

Examples

With no skew and no excess kurtosis it is the normal VaR.

>>> round(cornish_fisher_var(0.0, 1.0, 0.0, 0.0, 0.99), 4)
2.3263
>>> round(cornish_fisher_var(0.0, 1.0, -0.5, 3.0, 0.99), 4)
3.3013
financialkit.risk.delta_normal_var(exposures, cov, alpha=0.99, horizon=1.0)[source]#

RiskMetrics delta-normal portfolio VaR, \(q_\alpha \sqrt{h\, w^\top \Sigma w}\).

Parameters:
  • exposures (ArrayLike) – Money exposure to each risk factor (position value, or delta times spot).

  • cov (ArrayLike) – Covariance of the one-period factor returns.

  • alpha (float) – Confidence level.

  • horizon (float) – Number of periods.

Return type:

float

Returns:

float

Examples

Hull, 10th ed., Ch. 22: $10 million in Microsoft (2% daily vol) and $5 million in AT&T (1%), correlation 0.3. The ten-day 99% VaR is about $1.62 million, against $1.83 million if the two VaRs are added.

>>> import numpy as np
>>> cov = np.array([[0.02**2, 0.3 * 0.02 * 0.01], [0.3 * 0.02 * 0.01, 0.01**2]])
>>> round(delta_normal_var([10e6, 5e6], cov, 0.99, horizon=10))
1620114
financialkit.risk.drawdowns(returns, log=False)[source]#

Drawdown path and maximum drawdown of a compounded return series.

Parameters:
  • returns (ArrayLike) – Periodic returns in time order.

  • log (bool) – Treat the returns as log returns rather than simple returns.

Return type:

DrawdownResult

Returns:

DrawdownResult

Examples

Up 50%, down 50%, up 20%: the peak of 1.5 falls to 0.75, a 50% drawdown.

>>> res = drawdowns([0.5, -0.5, 0.2])
>>> res.max_drawdown, res.peak_index, res.trough_index, res.recovery_index
(0.5, 1, 2, -1)
financialkit.risk.empirical_tail_dependence(u, v, q=0.05, tail='lower')[source]#

Finite-level tail dependence \(P(V < q \mid U < q)\) (or the upper analogue).

Parameters:
  • u (ArrayLike) – Copula sample or pseudo-observations.

  • v (ArrayLike) – Copula sample or pseudo-observations.

  • q (ArrayLike) – Tail level(s).

  • tail (str)

Return type:

ndarray | float

Returns:

float or ndarray

financialkit.risk.euler_allocation(weights, cov, alpha=0.99, measure='var')[source]#

Euler contributions to the volatility, normal VaR or normal ES of a portfolio.

Parameters:
  • weights (ArrayLike) – Money exposures.

  • cov (ArrayLike) – Covariance of one-period returns.

  • alpha (float) – Confidence level (ignored for "volatility").

  • measure (str)

Return type:

AllocationResult

Returns:

AllocationResult

Examples

Hull, 10th ed., Ch. 22’s Microsoft/AT&T portfolio: the one-day 99% VaR of about 512,300 splits into component VaRs that sum to it.

>>> import numpy as np
>>> cov = np.array([[0.02**2, 0.3 * 0.02 * 0.01], [0.3 * 0.02 * 0.01, 0.01**2]])
>>> res = euler_allocation([10e6, 5e6], cov, 0.99)
>>> np.round(res.contributions), round(res.total)
(array([454226.,  58099.]), 512325)
financialkit.risk.expected_shortfall(pnl, alpha=0.99)[source]#

Acerbi-Tasche expected shortfall of equally likely scenarios.

Parameters:
  • pnl (ArrayLike) – Scenario profits and losses.

  • alpha (float) – Confidence level.

Return type:

float

Returns:

float – A positive loss, never below the VaR at the same level.

Examples

With 100 outcomes and a 2.5% tail the estimator takes the two worst losses in full and half of the third.

>>> import numpy as np
>>> expected_shortfall(-np.arange(1.0, 101.0), 0.975)
99.2
financialkit.risk.filtered_historical_var(pnl, sigma, sigma_forecast, alpha=0.99)[source]#

Filtered historical simulation (Barone-Adesi, Giannopoulos and Vosper 1999).

Each past outcome is divided by the volatility of its day, giving roughly i.i.d. standardized residuals \(z_t = x_t / \sigma_t\), and rescaled by tomorrow’s forecast: the scenarios are \(\hat\sigma_{n+1} z_t\). The empirical shape of the tail is kept while its scale follows current volatility.

Parameters:
  • pnl (ArrayLike) – Historical profits and losses.

  • sigma (ArrayLike) – Conditional volatility of each day, from any filter (EWMA, GARCH).

  • sigma_forecast (float) – Volatility forecast for the day the VaR is for.

  • alpha (float) – Confidence level.

Return type:

VaRResult

Returns:

VaRResult

financialkit.risk.fit_gev(pnl, block_size)[source]#

Fit the generalized extreme value law to block maxima of the losses.

Parameters:
  • pnl (ArrayLike) – Profits and losses in time order.

  • block_size (int) – Observations per block.

Return type:

GEVResult

Returns:

GEVResult

financialkit.risk.fit_pot(pnl, threshold)[source]#

Fit a generalized Pareto law to the losses above threshold.

Parameters:
  • pnl (ArrayLike) – Profits and losses.

  • threshold (float) – Loss threshold u (a positive loss for a loss tail).

Return type:

GPDResult

Returns:

GPDResult

financialkit.risk.gaussian_copula_sample(corr, n, rng=None)[source]#

Draw from the Gaussian copula with correlation matrix corr.

Parameters:
Return type:

ndarray

Returns:

ndarray, shape (n, d) – Points in the unit cube with uniform marginals.

financialkit.risk.gev_return_level(fit, k)[source]#

Loss level exceeded by the block maximum once every k blocks on average.

Parameters:
Return type:

ndarray | float

Returns:

float or ndarray

Examples

For a Gumbel law (\(\xi = 0\)) the 100-block level is \(\mu - \sigma \ln(-\ln 0.99)\).

>>> round(gev_return_level(GEVResult(xi=0.0, mu=0.0, sigma=1.0, block_size=21), 100), 4)
4.6001
financialkit.risk.historical_var(pnl, alpha=0.99)[source]#

Historical-simulation VaR and ES: past outcomes, equally likely.

Parameters:
  • pnl (ArrayLike) – Historical profits and losses (or returns).

  • alpha (float) – Confidence level.

Return type:

VaRResult

Returns:

VaRResult

Examples

With 100 outcomes the 95% VaR is the fifth-worst loss.

>>> import numpy as np
>>> historical_var(-np.arange(1.0, 101.0), 0.95).var
96.0
financialkit.risk.hybrid_var(pnl, alpha=0.99, lam=0.98)[source]#

Boudoukh-Richardson-Whitelaw age-weighted historical simulation.

The outcome i days old gets probability proportional to \(\lambda^i\), so recent days count more and the VaR reacts to a change in volatility within weeks instead of a whole window; lam = 1 is plain historical simulation.

Parameters:
  • pnl (ArrayLike) – Profits and losses in time order, most recent last.

  • alpha (float) – Confidence level.

  • lam (float) – Decay factor in (0, 1].

Return type:

VaRResult

Returns:

VaRResult

financialkit.risk.kupiec_test(hits, alpha=0.99)[source]#

Kupiec’s proportion-of-failures test of unconditional coverage.

\[LR_{uc} = -2 \ln \frac{(1 - p)^{n - x} p^x}{(1 - \hat p)^{n - x} \hat p^x}, \qquad \hat p = x / n, \quad LR_{uc} \sim \chi^2_1.\]
Parameters:
  • hits (ArrayLike) – Exception indicator per day, e.g. from var_exceptions().

  • alpha (float) – VaR confidence level.

Return type:

BacktestResult

Returns:

BacktestResult

Examples

Twelve exceptions of a 99% VaR in 250 days are far too many.

>>> res = kupiec_test([True] * 12 + [False] * 238)
>>> round(res.statistic, 2), res.reject()
(19.02, True)
financialkit.risk.mean_excess(pnl, thresholds)[source]#

Empirical mean excess function \(e(u) = E[L - u \mid L > u]\) of the losses.

For a generalized Pareto tail it is linear in \(u\) with slope \(\xi / (1 - \xi)\), which is how a threshold is chosen: where the plot turns straight.

Parameters:
Return type:

ndarray

Returns:

ndarray, shape (m,) – nan where no loss exceeds the threshold.

financialkit.risk.monte_carlo_var(pnl_fn, cov, alpha=0.99, mean=None, horizon=1.0, n_paths=10000, ci_level=0.95, rng=None)[source]#

Full-revaluation Monte Carlo VaR (Pritsker 1997).

Draws joint normal risk-factor moves \(\Delta X \sim N(\mu h, \Sigma h)\) through financialkit.integrators.correlated_increments(), revalues the portfolio in every scenario with pnl_fn and reads the VaR and ES off the simulated distribution, with an exact order-statistic confidence interval for the VaR.

Parameters:
  • pnl_fn (Callable[[ndarray], ArrayLike]) – pnl_fn(dX) -> pnl maps factor moves of shape (n_paths, d) to the portfolio’s profit and loss, shape (n_paths,).

  • cov (ArrayLike) – Covariance of the one-period factor moves.

  • alpha (float) – Confidence level.

  • mean (ArrayLike | None) – Mean one-period factor move; zero if omitted.

  • horizon (float) – Number of periods.

  • n_paths (int) – Number of scenarios.

  • ci_level (float) – Coverage of the confidence interval stored in result.ci.

  • rng (Generator | int | None)

Return type:

VaRResult

Returns:

VaRResult

financialkit.risk.parametric_es(mu, sigma, alpha=0.99, horizon=1.0, distribution='normal', df=None)[source]#

Expected shortfall of a normal or (unit-variance-scaled) Student-t profit and loss.

Normal: \(-\mu h + \sigma\sqrt{h}\, \varphi(z_\alpha) / (1 - \alpha)\). Student-t with \(\nu\) degrees of freedom and \(t_\alpha = t_\nu^{-1}(\alpha)\): \(-\mu h + \sigma\sqrt{h}\sqrt{(\nu - 2)/\nu}\; \frac{g_\nu(t_\alpha)}{1 - \alpha} \frac{\nu + t_\alpha^2}{\nu - 1}\) (McNeil, Frey and Embrechts, Example 2.15).

Parameters:
  • mu (ArrayLike) – As in parametric_var().

  • sigma (ArrayLike) – As in parametric_var().

  • alpha (ArrayLike) – As in parametric_var().

  • horizon (ArrayLike) – As in parametric_var().

  • distribution (str) – As in parametric_var().

  • df (float | None) – As in parametric_var().

Return type:

ndarray | float

Returns:

float or ndarray

Examples

The Basel 97.5% ES of a standard normal is close to its 99% VaR.

>>> round(parametric_es(0.0, 1.0, 0.975), 4)
2.3378
financialkit.risk.parametric_var(mu, sigma, alpha=0.99, horizon=1.0, distribution='normal', df=None)[source]#

Parametric VaR of a normal or Student-t profit and loss.

\(\mathrm{VaR} = -\mu h + \sigma \sqrt{h}\, q_\alpha\), with \(q_\alpha\) the standard normal quantile, or the Student-t quantile rescaled to unit variance, \(t_\nu^{-1}(\alpha)\sqrt{(\nu - 2)/\nu}\).

Parameters:
  • mu (ArrayLike) – Mean and standard deviation of the one-period profit and loss.

  • sigma (ArrayLike) – Mean and standard deviation of the one-period profit and loss.

  • alpha (ArrayLike) – Confidence level.

  • horizon (ArrayLike) – Number of periods; the mean scales with h and the volatility with sqrt(h) (independent increments).

  • distribution (str)

  • df (float | None) – Degrees of freedom (> 2) for distribution="t".

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Hull, 10th ed., Ch. 22: a $10 million position with a 2% daily volatility has a one-day 99% VaR of about $465,300 and a ten-day VaR of about $1.47 million.

>>> round(parametric_var(0.0, 200_000.0, 0.99))
465270
>>> round(parametric_var(0.0, 200_000.0, 0.99, horizon=10))
1471312
financialkit.risk.pot_es(fit, alpha=0.99)[source]#

Peaks-over-threshold expected shortfall (finite only for xi < 1).

Parameters:
Return type:

ndarray | float

Returns:

float or ndarray

financialkit.risk.pot_var(fit, alpha=0.99)[source]#

Peaks-over-threshold VaR (valid for 1 - alpha below the exceedance rate).

Parameters:
Return type:

ndarray | float

Returns:

float or ndarray

financialkit.risk.pseudo_observations(x)[source]#

Column-wise ranks scaled into the open unit interval, rank / (n + 1).

The empirical copula of the data: it discards the marginals and keeps only the dependence, as Sklar’s theorem says one can.

Parameters:

x (ArrayLike)

Return type:

ndarray

Returns:

ndarray, shape (n, d)

financialkit.risk.rolling_historical_var(pnl, window=250, alpha=0.99)[source]#

Out-of-sample historical VaR forecasts from a rolling window.

The forecast for day t uses days t - window to t - 1 only, as in the Basel Committee’s (1996) backtesting framework.

Parameters:
  • pnl (ArrayLike) – Daily profits and losses in time order.

  • window (int) – Look-back length in days.

  • alpha (float) – Confidence level.

Return type:

ndarray

Returns:

ndarray, shape (n - window,) – VaR forecasts for days window, ..., n - 1.

financialkit.risk.safety_first_portfolio(mu, cov, threshold=0.0)[source]#

Fully invested weights maximising Roy’s ratio.

Parameters:
  • mu (ArrayLike) – Expected returns.

  • cov (ArrayLike) – Covariance of returns.

  • threshold (float) – Disaster level; it must lie below the global minimum-variance portfolio’s mean, or no risky portfolio beats it.

Return type:

ndarray

Returns:

ndarray, shape (d,) – Weights summing to one.

financialkit.risk.safety_first_ratio(mu, sigma, threshold=0.0)[source]#

Roy’s ratio \((\mu - d) / \sigma\).

Parameters:
  • mu (ArrayLike) – Mean and standard deviation of the return.

  • sigma (ArrayLike) – Mean and standard deviation of the return.

  • threshold (ArrayLike) – Disaster level \(d\).

Return type:

ndarray | float

Returns:

float or ndarray

Examples

>>> safety_first_ratio(0.08, 0.15, 0.02)
0.4
financialkit.risk.scenario_es_allocation(pnl, alpha=0.99)[source]#

Euler contributions to expected shortfall estimated from scenarios.

Position \(i\)’s contribution is minus its mean profit and loss over the \(k = \lceil n(1 - \alpha) \rceil\) scenarios in which the portfolio does worst; the contributions add up to the portfolio’s tail mean.

Parameters:
  • pnl (ArrayLike) – Profit and loss of each position (column) in each scenario (row).

  • alpha (float) – Confidence level.

Return type:

AllocationResult

Returns:

AllocationResult

financialkit.risk.shortfall_probability(mu, sigma, threshold=0.0, bound='chebyshev')[source]#

Probability that the return falls below the disaster level.

Parameters:
  • mu (ArrayLike)

  • sigma (ArrayLike)

  • threshold (ArrayLike)

  • bound (str) – "chebyshev" is Roy’s distribution-free upper bound \(\min(1, 1/s^2)\) (for \(s > 0\)); "normal" is the exact value \(\Phi(-s)\) for normal returns.

Return type:

ndarray | float

Returns:

float or ndarray

Examples

>>> round(shortfall_probability(0.08, 0.15, 0.02, "normal"), 4)
0.3446
>>> shortfall_probability(0.08, 0.03, 0.02)
0.25
financialkit.risk.t_copula_sample(corr, df, n, rng=None)[source]#

Draw from the Student-t copula: correlated normals over a shared \(\sqrt{W / \nu}\).

Parameters:
Return type:

ndarray

Returns:

ndarray, shape (n, d)

financialkit.risk.t_tail_dependence(rho, df)[source]#

Coefficient of (upper = lower) tail dependence of the bivariate t copula.

Parameters:
  • rho (ArrayLike) – Correlation parameter.

  • df (ArrayLike) – Degrees of freedom; inf (the Gaussian copula) gives 0 for rho < 1.

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Demarta and McNeil (2005), Table 1: \(\nu = 4\), \(\rho = 0.5\).

>>> round(t_tail_dependence(0.5, 4), 2)
0.25
>>> t_tail_dependence(0.5, float("inf"))
0.0
financialkit.risk.var_exceptions(pnl, var)[source]#

Days on which the loss exceeded the VaR.

Parameters:
  • pnl (ArrayLike) – Realised profits and losses.

  • var (ArrayLike) – VaR forecast for each day, a positive loss.

Return type:

ndarray

Returns:

ndarray of bool, shape (n,) – The hit sequence: pnl < -var.

financialkit.risk.weighted_tail(pnl, alpha, weights=None)[source]#

VaR and expected shortfall of a discrete profit-and-loss distribution.

Parameters:
  • pnl (ArrayLike) – Scenario profits and losses (losses negative).

  • alpha (float) – Confidence level.

  • weights (ArrayLike | None) – Scenario probabilities (normalised here); equal if omitted.

Return type:

tuple[float, float]

Returns:

var, es (float) – Positive losses. es >= var always.

Examples

Ten equally likely outcomes, a 15% tail: the VaR is the second-worst loss and the ES averages the worst loss with half of the second.

>>> weighted_tail([-10, -6, -4, -1, 0, 1, 2, 3, 4, 5], 0.85)
(6.0, 8.666666666666666)