Strategies API
Preview, save, and read content-addressed strategies and their shared backtest results.
A saved strategy is an immutable, content-addressed trading program authored with the Strategy Builder (or the AI assistant) that owns its backtest results — there is no separate backtest object and there are no /backtests* endpoints. See Strategy results for the concepts. Most endpoints require authentication; reading a public strategy (and its results) does not.
The model
- Content-addressed and shared. A form
configcompiles to a deterministic program keyed by the hash of its source. Identical configurations are one shared strategy with one set of results, reused across users. Strategies are immutable — to change one, save a new configuration (a different hash). - Private by default, public means "anyone with the link". Each saved strategy carries a per-user
privacyflag —private(the default) orpublic. Public is unlisted, not discoverable: there is no directory of public strategies, nothing crawls or lists them, and the id is a random UUID that can't be guessed — so a public strategy is reachable only by someone you hand the link to. Once they have it, any user (or an unauthenticated visitor) can view and clone it; a private strategy is visible only to you. Visibility is per-link, so making your copy public never exposes another user's private copy of the same configuration. Flip it any time withPATCH /strategies/{id}. - Results are computed once, the same way for everyone: every recorded session, run independently at a fixed $100,000 per-session capital, fee-free. Fees are applied at read time from the viewer's fee schedule (the default schedule for unauthenticated visitors), so shared results render with your costs.
- Everyone sees the full history. Results cover every recorded session, and any user can append newly recorded sessions. Every results response labels the window (
window_label— alwaysall recorded sessions— pluswindow_from,window_to). - No run credits. The backtester is protected by a per-user cap of 3 concurrently-executing runs (
429 too_many_active_backtests— retry with backoff) and a priority queue. Only the session drill-in draws rate-limit credits.
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST |
/strategies/preview |
Preview a config (a persisted, shared backtest) |
GET |
/strategies/preview/{source_hash}/results |
Read preview results (pre-save, heartbeat) |
POST |
/strategies/preview/abandon |
Abandon a preview |
GET |
/strategies |
List your saved strategies (the leaderboard) |
POST |
/strategies |
Save (publish) a strategy |
GET |
/strategies/{id} |
Get the logic + results snapshot |
PATCH |
/strategies/{id} |
Update per-user metadata |
DELETE |
/strategies/{id} |
Remove the strategy from your list |
GET |
/strategies/{id}/results/days |
Per-session day list |
GET |
/strategies/{id}/results/days.csv |
The day list as a CSV attachment |
GET |
/strategies/{id}/results/days/{date} |
Session drill-in (recomputed on demand) |
POST |
/strategies/{id}/results/update |
Append newly recorded sessions |
Preview a configuration
POST /strategies/preview is the builder's per-change action. There is no ephemeral dry-run: every distinct configuration is a real, persisted backtest. The call validates the config, ensures the backtest for its hash exists, starts (or resumes) the coverage run when sessions are missing, registers your interest, and returns the current fee-overlaid snapshot — instant for a configuration anyone has previewed or saved before.
curl -s https://api.0dtespx.com/strategies/preview \
-X POST -H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
-d '{
"config": {
"legs": [
{"direction":"sell","type":"put","qty":1,"strike":{"method":"delta","value":0.3}},
{"direction":"buy","type":"put","qty":1,"strike":{"method":"delta","value":0.2}}
],
"entry": {"times":["10:00"]},
"exit": {"profit_target_pct":50,"time":"15:55"}
}
}'
The response carries the generated artifacts and a snapshot with source_hash, exec_status, coverage counters (covered_days, total_sessions), the labeled window, and the folded metrics so far. When you change a parameter, send the new config with abandon_hash set to the previous source_hash — the superseded run stops at the next session boundary if nobody else is watching (completed sessions are kept; returning to that configuration resumes where it left off).
Errors: a malformed config returns 400 invalid_config with a fields map keyed by dotted config path — nothing is created. 429 too_many_active_backtests means you already have 3 runs executing; retry with backoff (a steady builder session never trips this, since each change abandons the previous run first).
Read preview results
GET /strategies/preview/{source_hash}/results is the pre-save read path: the full day list and folded summary, net of your fee schedule. Calling it also refreshes your interest heartbeat — an unsaved preview keeps running only while someone is watching (interest expires 15 minutes after the last fetch; saved strategies always run to completion).
curl -s https://api.0dtespx.com/strategies/preview/$HASH/results -H "Authorization: $TOKEN"
Poll until exec_status is idle and covered_days == total_sessions — or subscribe to the backtest_events WebSocket channel with the source_hash and read each frame's data.snapshot (the full fee-overlaid results snapshot; the HTTP endpoint backs the initial load + reconnect). Access requires live interest in the hash (from a recent preview) or a saved strategy of yours with this source — otherwise 404.
When you leave the builder without saving, POST /strategies/preview/abandon with {"source_hash": "…"} drops your interest explicitly (best-effort, always 204 — the 15-minute TTL backstops it).
Save (publish)
POST /strategies upserts the shared immutable strategy for the hash, links it to your account, and adopts the content-addressed backtest already running or done for that configuration — a config you just previewed publishes with its results intact, no recompute.
curl -s https://api.0dtespx.com/strategies \
-X POST -H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
-d '{"config": { … }, "privacy": "public"}'
Returns 201 with your saved-strategy id, the source_hash, the saved privacy, and the current results snapshot. Saving never trips the run cap — at the cap the coverage run is parked and starts automatically when one of your runs finishes. Re-saving a configuration you previously removed resurrects the same id; if another user already saved the identical configuration, you share their results instantly.
privacy is optional and defaults to private. Pass "public" to publish on save, or leave it off and publish later from the strategy page (or via PATCH).
List and read
curl -s https://api.0dtespx.com/strategies -H "Authorization: $TOKEN"
curl -s https://api.0dtespx.com/strategies/$STRAT_ID -H "Authorization: $TOKEN"
GET /strategies is the leaderboard: every saved strategy with its full-history headline metrics (return, Sharpe, max drawdown, win rate — net of the default fee schedule), coverage counters, and staleness (new_sessions_available), ordered by updated_at descending. The generated source is never included.
GET /strategies/{id} returns the immutable logic (config, generated description, summary, risks) plus the results snapshot over all recorded sessions, net of the viewer's fee schedule, with the labeled window. The payload also carries privacy and is_owner (whether the caller owns this link) so a client can show owner-only controls. Auth is optional: a public strategy is readable by anyone holding the link (including unauthenticated visitors) — but it's unlisted, so they have to be given the link; a private strategy you don't own returns 404 — its existence is never revealed. Viewing as an authenticated user keeps an in-flight run's interest fresh.
Per-session results
curl -s https://api.0dtespx.com/strategies/$STRAT_ID/results/days -H "Authorization: $TOKEN"
curl -sOJ https://api.0dtespx.com/strategies/$STRAT_ID/results/days.csv -H "Authorization: $TOKEN"
/results/days returns the full day list — one row per recorded session: date, status (completed | skipped | failed | halted), gross_pnl, fees (your schedule), net_pnl, and order counts — plus the window bounds. The .csv variant renders the same list as an attachment with two extra engine columns: intraday_max_dd (fee-free intraday max drawdown) and spx_close, plus error_message and halt_reason.
Drill into one session
curl -s https://api.0dtespx.com/strategies/$STRAT_ID/results/days/2025-01-15 -H "Authorization: $TOKEN"
The heavy per-session detail — orders, transactions (each with an overlay_fee from your schedule), decision log, and the second-by-second intraday equity curve — is not stored. It is recomputed by re-running that single session through the engine. Expect a response time of seconds, not milliseconds; the result is cached server-side for a few minutes and costs 10 rate-limit credits.
The drill-in's headline net_pnl is gross minus both fees (your schedule) and slippage (your per-contract slippage applied to the day's option fills). The response always includes the second-by-second intraday_curve, cost-adjusted for fees + slippage. reconciliation_warning: true means the recomputed gross P&L no longer matches the stored result — the engine changed since the session was computed, and a platform-wide results recompute will follow.
Update with new sessions
curl -s -X POST https://api.0dtespx.com/strategies/$STRAT_ID/results/update -H "Authorization: $TOKEN"
As new market days are recorded, results go stale (new_sessions_available > 0). An update runs only the missing sessions and refolds the summaries — shared with everyone on the same configuration. Returns 202 {"status":"queued"}. Any user may trigger it whenever new sessions exist (update_allowed simply mirrors that). Subject to the 3-active-runs cap (429). Failed sessions are retried by an explicit update.
Manage
curl -s -X PATCH https://api.0dtespx.com/strategies/$STRAT_ID \
-H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
-d '{"description":"my spread","privacy":"public"}'
curl -s -X DELETE https://api.0dtespx.com/strategies/$STRAT_ID -H "Authorization: $TOKEN"
PATCH (owner only) touches per-user metadata — a description override and the privacy flag ("public" publishes, "private" unpublishes); each field is applied only when present, and the logic itself is immutable (clone to change it). Publishing only makes the strategy reachable by link — it's never listed or discoverable — so to actually share it, send someone its URL (https://www.0dtespx.com/strategies/{id}); the strategy page has a share button that copies the link for you. DELETE removes the strategy from your list (soft unlink): the shared strategy and its results survive for other users, and re-saving the exact same configuration restores your entry with the same id.
Clone someone else's strategy
There is no clone endpoint — cloning is just saving a configuration you read from a public strategy. Fetch a public strategy with GET /strategies/{id}, take its config, and POST /strategies it (optionally tweaked). You get your own private link and your own results overlay; the original owner is unaffected. Because config is only returned for strategies you can read, you can only clone public strategies (or your own).
Next: track run progress over the WebSocket backtest_events channel.