quantlab.risk.bias.bias_statistics#

quantlab.risk.bias.bias_statistics(realized, forecast, window=None, min_observations=None)[source]#

Return the bias statistics of volatility forecasts against realized returns.

Parameters:
realizedxr.DataArray

On (timestamp, <portfolio dim>): each portfolio’s return over the bar after timestamp.

forecastxr.DataArray

Same coordinates: the volatility forecast for that return, made at timestamp. A forecast that is missing or not positive gives no outcome.

windowint, optional

Bars per rolling window; without it no rolling statistics are returned. USE4 rolls 12-month windows (252 daily bars).

min_observationsint, optional

Fewest outcomes a rolling window needs for a bias statistic; NaN with fewer. Defaults to half the window, rounded up (our choice: a portfolio may miss a few bars, a symbol on a halt).

Returns:
xr.Dataset

realized, forecast and outcome (their ratio, A1) on the input’s dimensions. Over the whole range, one window (A2, A3), per portfolio: bias, count (outcomes) and band (the half-width of the confidence interval, sqrt(2 / count)). With window, on the input’s dimensions, labelled by each window’s last bar: rolling_bias (A4), rolling_count and rolling_band; and on timestamp, across portfolios, rolling_mean (A5), rolling_p5, rolling_p95 and rolling_mrad (A6).

Raises:
ValueError

If window is below 2, or min_observations is not between 2 and window.

Examples

Twenty portfolios over 1000 bars, their returns drawn with exactly the forecast volatility sigma:

>>> import numpy as np, pandas as pd, xarray as xr
>>> rng = np.random.default_rng(0)
>>> sigma = xr.DataArray(
...     np.full((1000, 20), 0.01), dims=("timestamp", "portfolio"),
...     coords={"timestamp": pd.bdate_range("2020-01-01", periods=1000)},
... )
>>> returns = sigma * rng.standard_normal((1000, 20))
>>> stats = bias_statistics(returns, sigma, window=252)
>>> round(float(stats["bias"].mean()), 2), round(float(stats["band"][0]), 3)
(1.0, 0.045)
>>> round(float(stats["rolling_mean"].isel(timestamp=-1)), 2)
0.99

A forecast of half the volatility underpredicts risk by two:

>>> round(float(bias_statistics(returns, sigma / 2)["bias"].mean()), 2)
1.99