Experiments

The experiment API takes a strategy template plus a parameter space and the engine generates its own candidates — grid enumeration or random/Monte-Carlo sampling — backtests each, and ranks the results. Use it for hyperparameter sweeps, dataset comparisons, or signal-combination searches. You do not hand it a list of strategies; you describe the space and the engine searches it. (Screening a fixed candidate list is a different workflow — see SIEVEbulk backtest.) Sweep size is bounded by your subscription tier’s max_backtests_per_sweep (enforced at validate), not a fixed limit — see Limits & caps. All endpoints sit under /api/v1/oracle/ on the MangroveAI gateway and are forwarded to the Oracle backtest engine with your identity attached. Auth: API key or JWT (same as the rest of /api/v1/*).

Endpoints

MethodPathPurposeBillable
POST/api/v1/oracle/experimentsCreate an experiment (the parameter grid + strategy template)oracle_experiment (1 unit; x402 $0.25)
POST/api/v1/oracle/experiments/{id}/validateValidate a draft + return total_runs (required before launch; enforces the tier per-sweep cap)free (metadata)
POST/api/v1/oracle/experiments/{id}/launchFan the validated parameter space out into backtests (async)oracle_experiment
GET/api/v1/oracle/resultsRead results as they materialize. Pass ?experiment_id=<id> to scope to a single sweep, or omit it for the cross-experiment view.oracle_results_read (1 unit; x402 $0.01)
POST/api/v1/oracle/simulate/runSimulate a single strategy run without persistingoracle_simulate (1 unit; x402 $0.10)
POST/api/v1/oracle/simulate/generateLLM-backed strategy candidate generationoracle_simulate
GET/api/v1/oracle/simulate/presetsReady-made simulate request templatesfree (metadata)
GET/api/v1/oracle/simulate/historyList recent simulate runs (caller-scoped)free (metadata)
GET/api/v1/oracle/datasetsList the datasets the engine can run againstfree (metadata)
GET/api/v1/oracle/signalsList signals available to experimentsfree (metadata)
GET/api/v1/oracle/exec-config/defaultsExecution-defaults metadata (fees, slippage, position sizing, risk limits)free (metadata)
GET/api/v1/oracle/templatesPredefined strategy templates you can seed an experiment fromfree (metadata)
GET/api/v1/oracle/leaderboardCurated persona roster — wrappers for the deployed strategies on the public dashboard. See Deployed strategies for live execution state.free (metadata)
GET/api/v1/oracle/deployed/strategiesList the live-running curated strategies with current account value, open positions, etc.free (metadata)
Each POST /experiments + subsequent /launch counts as ONE billable HTTP call against your tier, regardless of how many strategies the grid fans out to.

Typical flow

1

Pick a dataset and a template

curl -H "Authorization: Bearer $MANGROVE_API_KEY" \
  https://api.mangrovedeveloper.ai/api/v1/oracle/datasets

curl -H "Authorization: Bearer $MANGROVE_API_KEY" \
  https://api.mangrovedeveloper.ai/api/v1/oracle/templates
2

Create an experiment

Body is the ExperimentConfig: a datasets array (whole dataset OBJECTS from /datasets, not filenames), entry_signals / exit_signals (each signal needs a signal_type; entry needs ≥1 filter), and a params_sweep on each signal defining the space (values: [...] or {min, max, step}). The binary SIEVE pre_filter skips configs predicted not to trade.
curl -X POST https://api.mangrovedeveloper.ai/api/v1/oracle/experiments \
  -H "Authorization: Bearer $MANGROVE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "BTC 1h MACD momentum sweep",
    "search_mode": "grid",
    "kind": "single",
    "datasets": [ { /* a whole object from GET /datasets */ } ],
    "entry_signals": {
      "triggers": [
        {"name": "macd_bullish_cross", "signal_type": "TRIGGER",
         "params": {"window_sign": 9},
         "params_sweep": {"window_fast": {"values": [8, 12, 16]},
                          "window_slow": {"values": [21, 26, 30]}}}
      ],
      "filters": [
        {"name": "is_above_sma", "signal_type": "FILTER",
         "params_sweep": {"window": {"values": [50, 100]}}}
      ],
      "min_filters": 1, "max_filters": 1
    },
    "exit_signals": {
      "triggers": [
        {"name": "macd_bearish_cross", "signal_type": "TRIGGER",
         "params": {"window_fast": 12, "window_slow": 26, "window_sign": 9}}
      ],
      "filters": []
    },
    "grid_signals": {"n_param_combos": 24},
    "execution_config": {"base": {}, "sweep_axes": []},
    "pre_filter": {"enabled": true, "confidence_threshold": 0.8}
  }'
Returns an experiment_id with status: "draft". Sweep size is controlled by grid_signals.n_param_combos (grid) or n_random (random) — the engine samples up to that many configs from the space.
3

Validate

curl -X POST https://api.mangrovedeveloper.ai/api/v1/oracle/experiments/<id>/validate \
  -H "Authorization: Bearer $MANGROVE_API_KEY"
Returns { "valid": bool, "total_runs": int, "errors": [...], "warnings": [...] }. Required before launch. This is where the tier per-sweep cap is enforced: if total_runs exceeds your tier’s max_backtests_per_sweep, validate fails with HTTP 403 — shrink the size or upgrade. There is no fixed 99 limit.
4

Launch

curl -X POST https://api.mangrovedeveloper.ai/api/v1/oracle/experiments/<id>/launch \
  -H "Authorization: Bearer $MANGROVE_API_KEY"
The engine starts evaluating the space asynchronously (in the cloud). A 429 here means you’ve hit your tier’s concurrent-sweep cap — wait for the running sweep to finish, then launch.
Launch is non-idempotent and can return a 504 gateway timeout even though the launch succeeded server-side. Do not re-send the launch on a 504 (a retry can hit the single-flight 409 or the concurrent-cap 429). Instead, poll GET /api/v1/oracle/experiments/{id} and treat any status past validated (e.g. preparing/running) as success. The Python SDK’s oracle.launch_experiment_and_wait() handles this for you; raw HTTP clients should not auto-retry the launch POST.
5

Poll for results

curl "https://api.mangrovedeveloper.ai/api/v1/oracle/results?experiment_id=<id>" \
  -H "Authorization: Bearer $MANGROVE_API_KEY"
Results materialize as the sweep completes. The response is paginated; ranked metrics (Sharpe, max drawdown, etc.) come back per strategy.

Costs

Per the developer pricing page, every POST /experiments + /launch HTTP call counts as 1 unit against your API quota. x402 callers pay $0.25 per kickoff. The engine fans out to many individual backtests internally; you are not charged per fanout child. GET /results is billed separately at 1 unit per call ($0.01 x402). Polling efficiently matters — prefer larger paged reads to short-interval polling.

Limits & caps

Two tier-dependent caps govern sweeps (there is no fixed 99-strategy limit — that 99 is SIEVE’s per-call cap, a different feature):
  • Per-sweep sizemax_backtests_per_sweep for your tier (e.g. Pro 5,000 / Startup 20,000 / Enterprise 100,000). Enforced at validate (HTTP 403 if total_runs exceeds it). Control your size with grid_signals.n_param_combos or n_random.
  • Concurrent sweepsconcurrent_sweep_cap (often 1 on lower tiers). Enforced at launch (HTTP 429). Wait for the in-flight sweep to finish, then launch the next.
  • SIEVE classifier — a separate workflow: cheaply score a fixed candidate shortlist (≤99 per call) and hand the survivors to a bulk backtest. It does NOT gate or feed a sweep. (A sweep’s own built-in binary SIEVE pre_filter is a different mechanism — it skips dead configs during the search.)
  • Backtesting API — single-strategy synchronous and async backtests, useful when you already know the parameters you want to test.
  • Deployed strategies — live execution state of curated paper-trading strategies (the surface formerly implied by “leaderboard”).
  • mangroveai Python SDKclient.oracle.create_experiment(...), client.oracle.simulate_run(...), client.oracle.list_results(...), and the full deployed/leaderboard surface are typed wrappers as of SDK v1.4.0.