Guides · risk
Expected Shortfall (ES, also called CVaR) is the average loss in the cases where losses exceed VaR. It is never smaller than VaR at the same confidence level, and it is the measure Basel’s FRTB framework standardized on. POST /v1/risk/var returns it twice — historical and parametric — next to VaR, in one call.
A 99% VaR draws a line: on 99 days out of 100, the loss stays below it. It is silent about the hundredth day. Expected Shortfall answers the question VaR dodges: when the line is crossed, how much do we lose on average?
| Measure | Question answered | Blind spot |
|---|---|---|
| VaR | Where does the worst 1% begin? | Says nothing about the size of losses beyond the line. |
| Expected Shortfall | How bad is the worst 1%, on average? | Still an average — individual days can exceed it. |
ES is also mathematically better behaved: it is subadditive (diversifying cannot increase it), which VaR is not. That is why the Basel FRTB market-risk framework moved from 99% VaR to 97.5% ES as its headline measure. To reproduce the FRTB confidence level, send "confidence": 0.975 — the endpoint accepts any level strictly between 0.5 and 1.
The minimal call is a POST with a return series (decimal per period). The body below is truncated for display — the real series has 252 values, and the API rejects fewer than 30:
curl -s https://quantbox.dev/v1/risk/var \
-H "X-API-Key: $QB_KEY" -H "Content-Type: application/json" \
-d '{"returns": [0.004057, -0.01208, 0.009405, 0.011687, -0.023012, …],
"confidence": 0.99, "horizon_days": 10, "portfolio_value": 2500000}'
Runs on the free tier — 500 calls/month, no credit card. Get a key.
The full example is reproducible: the returns are generated with a seeded numpy generator, so running this script yields exactly the numbers shown below. The first three generated values are 0.004057, -0.01208, 0.009405 — if yours differ, the seed differs.
import requests
import numpy as np
KEY = "YOUR_API_KEY" # free key at https://quantbox.dev
rng = np.random.default_rng(42)
returns = np.round(rng.normal(0.0004, 0.012, 252), 6).tolist() # 252 trading days
r = requests.post(
"https://quantbox.dev/v1/risk/var",
headers={"X-API-Key": KEY},
json={"returns": returns, "confidence": 0.99,
"horizon_days": 10, "portfolio_value": 2_500_000},
)
print(r.json())
{
"historical": {
"var": {"fraction_of_portfolio": 0.0746, "amount": 186535.64},
"expected_shortfall": {"fraction_of_portfolio": 0.0788, "amount": 196994.09}
},
"parametric_normal": {
"var": {"fraction_of_portfolio": 0.0833, "amount": 208357.15},
"expected_shortfall": {"fraction_of_portfolio": 0.0954, "amount": 238499.60}
},
"n_observations": 252,
"confidence": 0.99,
"horizon_days": 10,
"assumptions": {
"input": "portfolio return series (decimal per period)",
"horizon_scaling": "sqrt(horizon_days), assumes iid returns",
"parametric": "normal distribution fitted on sample mean/std (ddof=1)",
"historical": "linear-interpolated percentile of empirical losses",
"sign": "positive VaR = potential loss; can be negative if all observations are gains"
}
}
Reading it, on a €2.5M portfolio at 99% over 10 days: historical VaR €186,535.64 against historical ES €196,994.09; parametric VaR €208,357.15 against parametric ES €238,499.60. (Amounts rounded to cents for display; the API returns full precision.) In both columns ES is the larger number — as it must be.
Historical. The historical ES is the mean of the losses at or beyond the empirical VaR percentile — an average of values that are each at least the threshold, so it cannot be smaller. Here it lands about 5.6% above VaR (€196,994.09 vs €186,535.64).
Parametric. Under the fitted normal the API uses the closed form ES = (σ·φ(z)/(1−c) − μ)·√h, where z is the normal quantile at confidence c. At 99% a normal puts ES roughly 15% above VaR mechanically; here €238,499.60 vs €208,357.15, about 14.5% apart (the sample mean nudges the ratio slightly).
The higher the confidence, the more the two measures separate from the body of the distribution — and the more the ES depends on exactly what your tail looks like.
On real market data, the parametric-normal ES is the number to distrust. A normal tail decays like e^(−x²/2); real return series carry excess kurtosis, so genuinely extreme losses get near-zero weight under the fitted normal, and the tail average comes out too small — precisely where ES is supposed to be informative.
Honesty about this example: the series here is drawn from a normal distribution, so the fitted normal is well specified and even lands above the empirical column. With real returns, the ranking frequently flips. Because the endpoint always returns both methods, the spread between the historical and parametric columns is a free mis-specification diagnostic — historical vs parametric VaR walks through how to read it.
Holding per-asset returns instead of a portfolio series? Same endpoint, matrix mode: portfolio VaR from positions and asset returns.
Every response carries an assumptions block so the numbers are never detached from how they were made:
| Field | Verbatim value | What it means |
|---|---|---|
| input | portfolio return series (decimal per period) | One series of decimal returns; frequency defines the period. |
| horizon_scaling | sqrt(horizon_days), assumes iid returns | √10 scaling for the 10-day figures; an approximation if returns are autocorrelated or vol-clustered. |
| parametric | normal distribution fitted on sample mean/std (ddof=1) | Two-parameter fit; blind to skew and kurtosis. |
| historical | linear-interpolated percentile of empirical losses | No distribution assumed; ES averages the losses at or beyond that percentile. |
| sign | positive VaR = potential loss; can be negative if all observations are gains | Sign convention — no clamping. |
One hard constraint: at least 30 observations, or the API returns a 422 — a 99% tail average of a dozen points is noise, not risk management.
Run it on your own series. Key in seconds, then ES and VaR are one POST away.
Get a free API key500 calls/month free · no credit card · full schemas in /docs
Related: Value-at-Risk and Expected Shortfall in one call · Historical vs parametric VaR · Portfolio VaR from positions and asset returns · All guides