Guides · risk
Send a T×N matrix of asset returns plus N market values, and POST /v1/risk/var returns portfolio VaR and Expected Shortfall directly in currency. Correlations are captured automatically: each row aggregates into one portfolio P&L observation, so realized co-movement is baked in — no correlation matrix input.
The endpoint has two mutually exclusive input modes: a single returns series, or returns_matrix + positions. In matrix mode, each period’s portfolio P&L is the row-wise sum of position × return — already in currency, since positions are market values. VaR and ES are then computed on that P&L series, historical and parametric, exactly as in the single-series case.
| Rule | Detail |
|---|---|
| Shape | returns_matrix is T×N: rows = periods, columns = assets. All rows must have the same length. |
| Positions | positions = N market values in the same currency; len(positions) must equal the number of columns. |
| Minimum data | At least 30 rows, else HTTP 422. |
| Mode exclusivity | Provide exactly one of returns OR returns_matrix + positions. |
| portfolio_value | Ignored in matrix mode — results are already in currency, so the response returns amount only, no fraction_of_portfolio. |
Three assets, one trading year (252 days), €2M total: €1M in asset A, €600k in B, €400k in C. The minimal call, matrix truncated for display:
curl -s https://quantbox.dev/v1/risk/var \
-H "X-API-Key: $QB_KEY" -H "Content-Type: application/json" \
-d '{"returns_matrix": [[0.000312, 0.024504, 0.005944],
[0.003287, 0.001348, -0.002612],
[-0.002441, -0.020606, -0.005891], …],
"positions": [1000000, 600000, 400000],
"confidence": 0.95, "horizon_days": 1}'
Runs on the free tier — 500 calls/month, no credit card. Get a key.
The complete script generates the matrix with a seeded numpy generator. The order of the three draws is what makes it reproducible — a, then b, then c from the same generator; swap them and every number changes. Your first row should be exactly [0.000312, 0.024504, 0.005944]:
import requests
import numpy as np
KEY = "YOUR_API_KEY" # free key at https://quantbox.dev
# Draw order matters for reproducibility: a, then b, then c.
rng = np.random.default_rng(7)
a = np.round(rng.normal(0.0003, 0.010, 252), 6) # asset A — ~16% annualized vol
b = np.round(rng.normal(0.0005, 0.018, 252), 6) # asset B — ~29% annualized vol
c = np.round(rng.normal(0.0002, 0.007, 252), 6) # asset C — ~11% annualized vol
matrix = np.column_stack([a, b, c]).tolist() # shape (252, 3): rows = days
r = requests.post(
"https://quantbox.dev/v1/risk/var",
headers={"X-API-Key": KEY},
json={"returns_matrix": matrix,
"positions": [1_000_000, 600_000, 400_000],
"confidence": 0.95, "horizon_days": 1},
)
print(r.json())
{
"historical": {
"var": {"amount": 26141.08},
"expected_shortfall": {"amount": 31902.23}
},
"parametric_normal": {
"var": {"amount": 25992.26},
"expected_shortfall": {"amount": 32057.76}
},
"n_observations": 252,
"confidence": 0.95,
"horizon_days": 1,
"assumptions": {
"input": "returns_matrix (T x N asset returns) x positions (N market values); results in currency, portfolio_value ignored",
"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 the €2M portfolio, the 95% 1-day historical VaR is €26,141.08 (about 1.3% of the portfolio) and the historical Expected Shortfall €31,902.23 (about 1.6%); the parametric figures are €25,992.26 and €32,057.76. Amounts rounded to cents for display — the API returns full precision. Note the response carries no fraction_of_portfolio: results are in currency, and portfolio_value would have been ignored if sent.
Nowhere — they are in the data. Because each day’s P&L sums all three assets on the same date, days when assets fall together produce large P&L losses, and that co-movement flows straight into the historical percentile. This is the quiet advantage of historical aggregation over a variance formula you assemble yourself: nothing to estimate, nothing to get wrong about which correlation regime applies.
The parametric figure is fitted on the same aggregated P&L series (sample mean and standard deviation, ddof=1) — which is mathematically equivalent to the classic pᵀΣp portfolio-variance formula with the sample covariance matrix. You get the covariance treatment without ever building Σ.
In this example the two methods land within 0.6% of each other on VaR (€26,141 historical vs €25,992 parametric) — expected, since the three series are drawn from independent normals, so the aggregated P&L really is close to normal. On real data, a wide gap between the columns is your cue that the joint tails are not normal — historical vs parametric VaR covers how to read that gap, and the Expected Shortfall guide covers why the ES column deserves more attention than VaR.
Horizon scaling. This example uses horizon_days: 1, so no scaling is applied. Longer horizons are scaled by sqrt(horizon_days), which assumes iid returns — the assumptions block labels it on every response.
Alignment is on you. The API treats row t as one date across all columns. If your asset series are misaligned (holidays, missing days), the co-movement — and therefore the VaR — is wrong in a way no validator can catch. Align and forward-fill deliberately before calling.
Sign convention. Positive VaR = potential loss; the API returns negative values rather than clamping if the sample only went up.
Your data stays yours. The matrix is anonymous numbers — no tickers, no identifiers — and nothing is stored beyond the request.
Run it on your own book. Key in seconds, then portfolio VaR is one POST from your positions and price history.
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 · Expected Shortfall (CVaR) via API · Historical vs parametric VaR · All guides