Market data
Backtest a basket over the last 12 months
Simulate a weighted basket over 12 months, monthly rebalanced, against buy-and-hold SPY.
POST /api/backtest takes a weighted basket and returns its 12-month simulated performance against buy-and-hold SPY. It reads public equity history only — no inference, no user funds.
POST /api/backtest· public, no token required · 20 requests per 60 seconds per IP. Base URL, response envelope, and error shapes: conventions.
The engine starts at 100, renormalizes weights over the covered subset, drifts positions with daily closes, and rebalances every ~21 trading days.
Request body
Section titled “Request body”application/json.
| Field | Type | Required | Description |
|---|---|---|---|
allocations |
array | yes | 1 to 20 legs. |
allocations[].symbol |
string | yes | A tradable ticker, for example AAPL. Unknown symbols return 400. |
allocations[].weightPct |
number | yes | Positive, up to 100. Weights renormalize over the legs that have history. |
Response
Section titled “Response”| Field | Type | Description |
|---|---|---|
backtest |
object | null | null when covered weight is below 50% or SPY history is missing. |
backtest.period |
string | Always "1Y". |
backtest.rebalance |
string | Always "monthly". |
backtest.coveragePct |
number | Share of basket weight that had real history to test (0 to 100). |
backtest.excluded |
string[] | Symbols left out because no honest public series exists. |
backtest.portfolio |
object | Simulated basket: returnPct, volPct, maxDrawdownPct, sharpe, curve (60 points, normalized to 100). |
backtest.benchmark |
object | Same stats for buy-and-hold SPY, plus symbol: "SPY". |
Example
Section titled “Example”curl https://monvera.best/api/backtest \ -H "Content-Type: application/json" \ -d '{ "allocations": [ { "symbol": "AAPL", "weightPct": 35 }, { "symbol": "NVDA", "weightPct": 25 }, { "symbol": "SPY", "weightPct": 25 }, { "symbol": "SGOV", "weightPct": 15 } ] }'The curve arrays hold 60 points each, trimmed here for space.
{ "backtest": { "period": "1Y", "rebalance": "monthly", "coveragePct": 100, "excluded": [], "portfolio": { "returnPct": 22.41, "volPct": 18.9, "maxDrawdownPct": 12.07, "sharpe": 1.18, "curve": [100, 101.3, 99.8, 104.2, 108.6, 115.9, 122.41] }, "benchmark": { "symbol": "SPY", "returnPct": 14.22, "volPct": 12.68, "maxDrawdownPct": 8.31, "sharpe": 1.05, "curve": [100, 100.9, 100.1, 102.8, 105.4, 110.7, 114.22] } }}Errors
Section titled “Errors”| Status | Body | Cause |
|---|---|---|
| 400 | {"error":"Invalid request body."} |
Malformed JSON, non-array allocations, unknown symbol, or a weightPct outside 0 to 100. |
| 200 | {"backtest": null} |
Covered weight fell below 50%, or SPY history was missing. Check for null before reading stats. |
On a non-null result, excluded names any legs dropped for having no public series.
© 2026 Aibora · Documentation interface. Original Monvera materials retain their upstream attribution andMIT license.