RollingCVaR#
Description#
RollingCVaR is the historical Conditional Value-at-Risk, or Expected Shortfall,
of a return series over a trailing window. With the worst
k = max(1, floor(alpha * window)) returns in the window,
CVaR = -mean(the k smallest returns),
a positive number: the average loss you suffer in the worst alpha tail. Unlike
plain Value-at-Risk, CVaR is a coherent tail-risk measure. Value-at-Risk is only
the tail quantile itself, -RollingQuantile(window, alpha); CVaR averages the
losses beyond it, so it captures how bad the tail gets, not just where it starts.
The window is held in an order-statistic tree (O(log W) to update, O(k) to read
the tail). The output is NaN until the window is full; a NaN return leaves the
window untouched (nan_policy: ignore).
Examples#
Usage plot#
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from screamer import RollingCVaR, RollingQuantile
rng = np.random.default_rng(2)
n = 500
r = rng.standard_normal(n) * 0.01
alpha = 0.05
cvar = RollingCVaR(100, alpha=alpha)(r)
var = -RollingQuantile(100, alpha)(r) # VaR is just the negated tail quantile
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, row_heights=[0.5, 0.5],
vertical_spacing=0.08)
fig.add_trace(go.Scatter(y=r, name='returns', line=dict(color='lightslategray'), opacity=0.6),
row=1, col=1)
fig.add_trace(go.Scatter(y=var, name='VaR (5%)', line=dict(color='darkorange')), row=2, col=1)
fig.add_trace(go.Scatter(y=cvar, name='CVaR / expected shortfall (5%)',
line=dict(color='crimson')), row=2, col=1)
fig.update_layout(title='RollingCVaR: the average loss beyond the VaR tail',
yaxis=dict(title='return'), yaxis2=dict(title='loss'),
margin=dict(l=20, r=20, t=60, b=20), legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1))
fig.show()