Bots API
Run a saved strategy as a live bot: sessions, lifecycle, and per-session data.
A bot runs one of your saved strategies as a live paper-trading account. Under the hood a bot is a dedicated trading account — a bot-type account on the live_sim engine — with a pinned strategy and a risk policy attached. The bot id is the account id. Like every account, its balance is continuous: the carried cash compounds day to day and is never reset per session. All endpoints require authentication.
What a bot is
- A pinned strategy. You create a bot from one of your saved strategies (the
strategy_idyou pass is your saved-strategy link id — the same id you read at/strategies/{id}). The bot pins the immutable strategy that link points at, so unlinking the strategy later doesn't change what the bot runs. - A risk policy. Per-bot guardrails — a daily-loss cap, open-position / open-order limits, a data-staleness threshold, an error budget, and an auto-pause flag. Editing the policy takes effect on the next session; a session that's already running keeps the policy it froze at start.
- An automatic re-pricing policy. The bot re-prices its own unfilled limit orders so resting spread orders actually fill instead of languishing at the mid — see Automatic re-pricing below. On by default, part of the same frozen policy.
- A continuous balance. The bot's
cash_balanceis the carried cash that rolls from one session to the next (compounding). Each session opens with that balance — there is no per-session starting capital to pass.
A bot's status is idle or archived; its runtime_status (idle · scheduled · running · halted · killed · completed · failed · archived) reflects what its current or last session is doing.
Bot endpoints
| Method | Path | Purpose |
|---|---|---|
GET |
/bots |
List your bots (optionally by strategy) |
POST |
/bots |
Create a bot |
GET |
/bots/{id} |
Get a bot with its sessions |
PATCH |
/bots/{id} |
Rename, archive, or edit the risk policy |
DELETE |
/bots/{id} |
Delete a bot |
GET |
/bots/{id}/summary |
Lifetime performance roll-up |
Create a bot
curl -s https://api.0dtespx.com/bots \
-X POST -H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
-d '{
"strategy_id": "a1b2c3d4-…",
"name": "Delta-30 put spread",
"starting_capital": "25000",
"description": "Runs my 30-delta credit spread live",
"max_daily_loss_pct": "0.0200",
"max_open_positions": 4
}'
strategy_id, name, and starting_capital are required. strategy_id is your saved-strategy link id (the id at /strategies/{id}); the bot pins the strategy behind it. name is required (≤ 80 chars) and must be unique among your active bots. starting_capital ranges from 1000 to 1000000000. The engine defaults to live_sim; a broker_* engine returns 409 engine_not_available.
The risk-policy fields are optional and bounded: max_daily_loss_pct (0.0050–0.5000), max_open_positions (1–100), max_open_orders (1–50), data_staleness_threshold_ms (500–10000), auto_pause_on_error, and error_budget (0–100). When a running session's drawdown reaches max_daily_loss_pct, the bot cancels its working orders, closes any open positions at market, and ends the session halted — so a maxed-out bot is flat, not left holding risk. The optional reprice object configures automatic re-pricing; omit it to accept the defaults (on).
The response is the bot object. Errors: 400 invalid_input / invalid_strategy_id / invalid_starting_capital / invalid_reprice, 403 strategy_not_found_or_not_owned, 409 engine_not_available, 409 name_taken.
Automatic re-pricing
A bot places resting limit orders at the spread mid. Live, an order priced exactly at the mid never fills once slippage applies, and if the market drifts the order sits unfilled for the rest of the session. To fix this the bot re-prices its own unfilled limit orders: it models a resting limit as peg + concession — the peg follows the raw mid (so the order keeps its price improvement), and the concession is a one-tick-at-a-time walk toward the market maker that crosses the fill barrier. This is a live-only behavior; a backtest fills a mid-priced limit on the entry tick, so re-pricing has no effect there.
The two order-kinds behave differently, and both are on by default:
- Entries (
opening) — patient, capped. Concede one tick only while the mid is flat, up to a user-settable capmax_concessionmeasured from the intended entry price; then rest at the cap rather than overpay to enter. If the mid runs away, the order follows the mid only up to the cap and waits for it to come back. - Exits (
closing) — relentless, uncapped. Concede one tick everynudge_after_secondsunconditionally, re-pegged to the moving mid, with no cap (there is deliberately nomax_concessionon the closing block), until the order fills. This is what guarantees a bot can get out of a position. Turning exit re-pricing off is dangerous — a closing order may never fill and the position can run against you, so losses can be large.
Each block accepts enabled (default true), nudge_after_seconds (1–600; default 20 opening / 15 closing), min_interval_seconds (1–120; default 4 — the floor between replacements), and flat_tolerance_ticks (1–20; default 1 — how many ticks of movement still counts as "flat"). The opening block also accepts max_concession (a USD decimal, multiple of 0.05, 0–10; default "0.20"). Like the rest of the policy, re-pricing settings are frozen per session and take effect on the next session.
# create a bot with a tighter entry cap and default (on) exit re-pricing
curl -s https://api.0dtespx.com/bots \
-X POST -H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
-d '{
"strategy_id": "a1b2c3d4-…",
"name": "Delta-30 put spread",
"starting_capital": "25000",
"reprice": { "opening": { "max_concession": "0.10" } }
}'
List bots
curl -s "https://api.0dtespx.com/bots" -H "Authorization: $TOKEN"
# filter to bots pinned to one saved-strategy link
curl -s "https://api.0dtespx.com/bots?strategy_id=a1b2c3d4-…" -H "Authorization: $TOKEN"
?strategy_id= filters by your saved-strategy link id; an unknown link returns an empty array.
Get a bot
curl -s "https://api.0dtespx.com/bots/$BOT" -H "Authorization: $TOKEN"
Returns { "bot": …, "sessions": [...] } — the bot plus its sessions. 404 if the bot doesn't exist or isn't yours. The bot object also carries active_session (the running session, or null) and last_session.
Edit or archive a bot
PATCH accepts any of name, description, status (idle or archived), the risk-policy fields, and the reprice object — only the fields you send are applied.
# rename
curl -s -X PATCH https://api.0dtespx.com/bots/$BOT \
-H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
-d '{"name":"Delta-30 — v2"}'
# tighten the daily-loss cap (applies to the NEXT session)
curl -s -X PATCH https://api.0dtespx.com/bots/$BOT \
-H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
-d '{"max_daily_loss_pct":"0.0150"}'
Risk-policy edits only take effect on the next session — a running session keeps the policy snapshot it froze at start.
DELETE /bots/{id} removes the bot and returns 204. It is blocked while a session is running (409 bot_has_active_session) — stop the live session first.
Lifetime roll-up
curl -s "https://api.0dtespx.com/bots/$BOT/summary" -H "Authorization: $TOKEN"
Returns aggregate performance across the bot's settled sessions: total_sessions, win_count / loss_count, win_rate_pct, total_pl, best_day / worst_day, average_day_pl, and a sparkline series.
Sessions
A session is one trading day the bot ran. A bot session is its own live sim, so its reads return the same shapes as live trading. Sessions move through a lifecycle: scheduled → registering → waiting_for_data → running → settling → completed (or halted / killed / failed).
| Method | Path | Purpose |
|---|---|---|
GET |
/bots/{id}/sessions |
List sessions (newest first, ≤ 90) |
POST |
/bots/{id}/sessions |
Start today's session |
GET |
/bots/{id}/sessions/{session_id} |
Get one session |
DELETE |
/bots/{id}/sessions/{session_id} |
Stop a running session |
POST |
/bots/{id}/sessions/{session_id}/cancel-all-orders |
Cancel open orders, keep running |
GET |
/bots/{id}/sessions/{session_id}/orders |
Live orders for the session |
GET |
/bots/{id}/sessions/{session_id}/positions |
Live positions for the session |
GET |
/bots/{id}/sessions/{session_id}/transactions |
Transaction log for the session |
GET |
/bots/{id}/sessions/{session_id}/financials |
Per-second financial history (chart line) |
GET |
/bots/{id}/sessions/{session_id}/decision-log |
The strategy's decision log |
Start today's session
SID=$(curl -s -X POST "https://api.0dtespx.com/bots/$BOT/sessions" \
-H "Authorization: $TOKEN" | jq -r .session_id)
No body — the date is implicitly today. The session opens with the bot's carried cash_balance (continuous / compounding — not a per-session starting capital), freezes the current risk policy into its snapshot, and the runner registers a live sim and drives the pinned strategy.
Errors: 404 not_found; 409 bot_not_idle (the bot already has a live session today, or is archived); 409 bot_session_exists (today's session already started); 422 outside_rth (outside regular trading hours); 503 live_trading_unavailable. A live-registration failure surfaces as register_sim_failed with the relayed status.
Stop or pause a session
# stop — moves to 'killing'; the runner cancels open orders and exits at the next tick
curl -s -X DELETE "https://api.0dtespx.com/bots/$BOT/sessions/$SID" -H "Authorization: $TOKEN"
# cancel open orders but keep running
curl -s -X POST "https://api.0dtespx.com/bots/$BOT/sessions/$SID/cancel-all-orders" -H "Authorization: $TOKEN"
DELETE …/sessions/{session_id} requests a stop (the session moves to killing) and returns 202. POST …/cancel-all-orders cancels the session's open orders while leaving it running, returns 202, and returns 409 session_terminal if the session is already terminal.
Read a session's live data
curl -s "https://api.0dtespx.com/bots/$BOT/sessions/$SID" -H "Authorization: $TOKEN" # the session
curl -s "https://api.0dtespx.com/bots/$BOT/sessions/$SID/orders" -H "Authorization: $TOKEN" # orders
curl -s "https://api.0dtespx.com/bots/$BOT/sessions/$SID/positions" -H "Authorization: $TOKEN" # positions vs the latest tick
curl -s "https://api.0dtespx.com/bots/$BOT/sessions/$SID/transactions" -H "Authorization: $TOKEN" # transactions
curl -s "https://api.0dtespx.com/bots/$BOT/sessions/$SID/financials" -H "Authorization: $TOKEN" # per-second P&L history
curl -s "https://api.0dtespx.com/bots/$BOT/sessions/$SID/decision-log" -H "Authorization: $TOKEN" # ctx.log()/ctx.signal() entries
orders, positions, transactions, and financials return the same shapes as the live-trading endpoints. decision-log returns the strategy's ctx.log() / ctx.signal() entries in emission order.
A bot session's P&L and roll-up fields (final_nlv, realized_pl, total_pl, max_intraday_dd, …) are null until the session finalizes.
Next: see Accounts & sessions for the shared account model and Live trading for the session-scoped order, position, and transaction shapes a bot session reuses.