Oracle backtest & data query
This page covers the single-strategy subset of the Oracle engine plus the corpus query API. For multi-strategy parameter sweeps see Experiments; for the curated paper-trading dashboard surface see Deployed strategies.Disambiguation.
/api/v1/oracle/backtest* (this page) runs through Mangrove Oracle — a faster engine optimised for batch and sweep workloads. /api/v1/backtesting/run (see Backtesting) runs through the legacy MangroveAI strategy engine. New code should target the Oracle endpoints documented here./api/v1/oracle/ on the MangroveAI gateway and are forwarded to the Oracle engine with your identity attached. Auth: API key or JWT, same as the rest of /api/v1/*.
Endpoints
| Method | Path | Meter | x402 price | Quota counter |
|---|---|---|---|---|
POST | /api/v1/oracle/backtest | oracle_backtest | $0.10 | api_calls |
POST | /api/v1/oracle/backtest/async | oracle_backtest | $0.10 | api_calls |
GET | /api/v1/oracle/backtest/async/{backtest_id} | oracle_backtest | $0.10 | api_calls |
POST | /api/v1/oracle/backtest/bulk | oracle_backtest | $0.10 | api_calls |
POST | /api/v1/oracle/data/query | oracle_data_query | $0.10 | oracle_data_queries |
MangroveAI/src/MangroveAI/domains/backtesting/oracle_proxy.py::_PROXY_METERING.
Synchronous single-strategy backtest
POST /api/v1/oracle/backtest
Runs one strategy against one (asset, interval) pair and blocks until the engine returns the full result. Typical wall-clock is 30–120 seconds depending on the date range; the SDK default HTTP timeout is 180s.
Required fields
18 top-level fields, enforced byMangroveOracle/src/api/routes/backtest.py::run_backtest:
asset,strategy_json— what to runinitial_balance,min_balance_threshold,min_trade_amount— accountmax_open_positions,max_trades_per_day,max_risk_per_trade,max_units_per_trade,max_trade_amount— risk capsvolatility_window,target_volatility,volatility_mode,enable_volatility_adjustment— volatility-aware sizingcooldown_bars,daily_momentum_limit,weekly_momentum_limit— circuit breakers
execution_config containing at least position_size_calc: "v2" (v1 legacy also accepted). The engine raises position_size_calc is required if it’s missing.
Optional: interval (defaults to "1h"), lookback_months, start_date, end_date (provide one of: explicit start+end, start alone (defaults end to now), or lookback_months).
Defaults filled by the gateway. Since MangroveAI v3.10.12 the proxy merges canonical values from trading_defaults.json into any /backtest* request body before forwarding to Oracle — customer-supplied values always win (customer-wins-only setdefault). So in practice you can call /backtest with just asset, strategy_json, and a date range; the 15 risk-mgmt fields and a full execution_config will be populated for you. Pull the same canonical values explicitly via GET /api/v1/oracle/exec-config/defaults when you want to inspect or override them.
Request
Response
denied_signals lists signals the engine refused (unsupported columns, malformed params). Check this when trade_count is lower than expected.
Example
Async submit + poll
When 30-120s of blocking is too long — dispatching dozens of backtests from a worker that should stay responsive — use the async pair.Submit
POST /api/v1/oracle/backtest/async
Same request body as the sync endpoint (same 18 required fields + top-level execution_config). Returns immediately:
Poll
GET /api/v1/oracle/backtest/async/{backtest_id}
status transitions queued → running → completed (or failed). Once completed, the response carries the full metrics/trade_history/trade_count shape of the synchronous response. Polling is metered at the same rate as submit ($0.10 / api_calls unit per call); space your polls accordingly.
Example
Python (SDK)
Bulk
POST /api/v1/oracle/backtest/bulk
Evaluates many strategies over a shared date range with shared OHLCV fetches. One HTTP call, one bill, up to 99 strategies.
The right endpoint when you have a SIEVE-filtered shortlist and want to score the survivors quickly — see End-to-end workflow with SIEVE for the full workflow.
Required fields
17 top-level fields + EXACTLY ONE ofstrategy_ids OR strategy_configs (not both), per MangroveOracle/src/api/routes/backtest.py::run_bulk_backtest:
start_date,end_date— required (nolookback_monthsshortcut on bulk)- The same 15 risk-mgmt fields as sync (sans
strategy_jsonsince each item carries its own) - Top-level
execution_configwithposition_size_calc
strategy_configs is [{asset, interval, strategy_json}, …]; strategy_ids is [uuid, …] referencing strategies already created via client.strategies.create(...) in MangroveAI.
Request
Response
results[i].success == false carries an error field and an empty metrics, but the rest of the batch continues. Input order is preserved: results[i] corresponds to strategy_configs[i]. data_fetches reports how many unique OHLCV fetches the engine did across the strategy list (strategies sharing (asset, interval) reuse one fetch).
Example
Python (SDK)
Data query (corpus)
POST /api/v1/oracle/data/query
A constrained DSL over Oracle’s BigQuery-backed corpus of historical backtest results and OHLCV bars. The proxy enforces server-side: table whitelist, column whitelist, filter-op whitelist, tenancy injection (WHERE org_id = <caller>), and a per-query maximum_bytes_billed cap. Customers cannot send raw SQL.
Request
| Field | Type | Notes |
|---|---|---|
table | "results" | "ohlcv" | required |
select | list[str] | required, 1-80 columns, whitelisted per table |
filters | list[{col, op, value}] | optional, ≤20 filters |
order_by | list[{col, dir}] | optional, ≤5 orderings; dir is "asc" or "desc" |
limit | int | optional, 1-1000, default 100 |
page_token | str | optional; opaque continuation token from a prior response |
filters[].op accepts: "=", "!=", ">", ">=", "<", "<=", "in", "between" (per MangroveOracle/src/models/data_query.py::FilterOp).
Response
total_bytes_billed is the actual BigQuery-billed scan in bytes (used to compute the customer’s invoice line for this call); cost_estimate_usd is that figure converted at GCP’s published per-TB rate. next_page_token is non-null when more rows are available — pass it back as page_token to continue.
Example
Costs (summary)
| Endpoint | Per-call | Quota counter |
|---|---|---|
All four /backtest* endpoints | $0.10 x402 / 1 unit | api_calls |
/data/query | $0.10 x402 / 1 unit | oracle_data_queries |
MangroveAI/src/MangroveAI/domains/backtesting/oracle_proxy.py::_PROXY_METERING.
Related
- Experiments — parameter-space sweeps where the engine generates its own candidates (
create→validate→launch→ poll). Different from bulk: bulk backtests a list you supply; a sweep searches a space. Sweep size is tier-bounded, not capped at the bulk 99. - SIEVE classifier — score 1-99 candidate strategies in milliseconds before paying for backtests.
- Deployed strategies — live execution state for curated paper-trading strategies, not for ad-hoc backtests.
- End-to-end workflow with SIEVE — end-to-end demo: list-signals, SIEVE screen, bulk-backtest the survivors, adopt.
mangroveaiPython SDK — typed wrappers for all/backtest*endpoints land in SDK v1.4.0; the historical risk-mgmt gap is now closed at the gateway (MangroveAI v3.10.12). The/data/queryPython example below still uses raw HTTP pending SDK#16 / SDK#17.