Guides · risk

Value-at-Risk and Expected Shortfall in one call

Send a return series, get back historical and parametric VaR side by side, each with its Expected Shortfall, at any confidence and horizon — and an assumptions block that says exactly how the numbers were made.

01A reproducible example

The snippet generates 250 days of synthetic returns with a fixed seed, so you can run it and get byte-identical numbers:

python
import requests, numpy as np

rng = np.random.default_rng(42)
returns = rng.normal(0.0004, 0.012, 250).tolist()   # ~1 year of daily returns

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())
response
{
  "historical": {
    "var":                {"fraction_of_portfolio": 0.0747, "amount": 186725.70},
    "expected_shortfall": {"fraction_of_portfolio": 0.0788, "amount": 196994.32}
  },
  "parametric_normal": {
    "var":                {"fraction_of_portfolio": 0.0836, "amount": 209093.42},
    "expected_shortfall": {"fraction_of_portfolio": 0.0957, "amount": 239339.24}
  },
  "n_observations": 250,
  "confidence": 0.99,
  "horizon_days": 10,
  "assumptions": {
    "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, the 99% 10-day VaR is ≈ €187k by the historical method, ≈ €209k under a fitted normal. Both methods on every call — if they disagree hard, that gap is telling you something about your tails.

02Historical vs parametric, honestly

MethodHowTrusts
historicalEmpirical percentile of your actual losses (linear interpolation).Your sample, including its fat tails — and its gaps.
parametric_normalNormal fitted on sample mean and std (ddof=1), analytic quantile.The normality assumption. Smooth, but underestimates tail risk in crises.

Expected Shortfall (the average loss beyond VaR) is included for both — it is what regulators moved to when VaR alone proved too easy to game.

03Portfolio mode: positions × returns matrix

If you have per-asset returns instead of a portfolio series, send a T×N matrix and N market values. The P&L series is computed in currency directly (portfolio_value is ignored — the response says so):

request (delta only)
{"returns_matrix": [[0.001, -0.004], [-0.012, 0.002], ...],
 "positions": [1500000, 1000000],
 "confidence": 0.99, "horizon_days": 1}

04The fine print, stated up front

√t scaling. Multi-day horizons are scaled by sqrt(horizon_days), which assumes iid returns. With autocorrelated or vol-clustered series, that is an approximation — the response labels it instead of hiding it.

Minimum 30 observations. Fewer gets a 422, because a 99% percentile of 12 points is noise dressed as risk management.

Sign convention. Positive VaR = potential loss. A period where everything went up can produce a negative VaR; the API returns it rather than clamping.

Your data stays yours. Return series are anonymous numbers — no tickers, no identifiers — and nothing is stored beyond the request.

Run it on your own series. Key in seconds, then it is one POST from your risk report.

Get a free API key

500 calls/month free · no credit card · full schemas in /docs

Related: Price an option in 5 lines of Python · Bond duration, convexity and BPV over HTTP