Guides · risk
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.
The snippet generates 250 days of synthetic returns with a fixed seed, so you can run it and get byte-identical numbers:
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())
{
"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.
| Method | How | Trusts |
|---|---|---|
| historical | Empirical percentile of your actual losses (linear interpolation). | Your sample, including its fat tails — and its gaps. |
| parametric_normal | Normal 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.
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):
{"returns_matrix": [[0.001, -0.004], [-0.012, 0.002], ...],
"positions": [1500000, 1000000],
"confidence": 0.99, "horizon_days": 1}
√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 key500 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