API reference

Accounts & trading days API

Create accounts, read trading days, run practice sessions, and read financials.

A trading account is a persistent, named, cash-only container with a continuous balance that carries day to day. Each account has a fixed engine, and you trade it one trading day at a time — a day that opens by itself on your first order, never through a call of its own. See Accounts & trading days for the concepts. All endpoints require authentication.

Replaying past trading days is account-less — it lives under /practice/sessions, not under an account. Accounts are for live and bot trading only.

Engines

Engine What it does
live_sim Paper-trades today's real session against the live tick stream. Past trading days are frozen. The only engine that ships now.
broker_* Reserved for upcoming broker integrations — not yet available. Creating one returns 409 engine_not_available.

Every account on /accounts is a live (manual) account — the type field is always manual. Trading bots are a separate surface, created and managed via the Bots API; they never appear in the /accounts list or resolve through /accounts/{id}. Trading is 0DTE SPX-index options only: cash-only, no equities, no overnight positions, so the carried balance is a single number.

Account endpoints

Method Path Purpose
GET /accounts List your live accounts
POST /accounts Create an account
GET /accounts/{id} Get one account
PATCH /accounts/{id} Rename or archive an account
DELETE /accounts/{id} Delete an account
GET /accounts/{id}/analytics Trade-level analytics over settled days

GET /accounts

Lists your accounts and saves the first one's id as {{account_id}} for the blocks below.

Interactive — run this request from the docs

Create an account

curl -s https://api.0dtespx.com/accounts \
  -X POST -H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
  -d '{
    "name": "Iron condors",
    "engine": "live_sim",
    "description": "Defined-risk study account"
  }'

POST /accounts

Creates a real paper-trading account on your profile. Names are unique among your active accounts, so edit `name` before a second run (409 name_taken otherwise).

Interactive — run this request from the docs

Every account starts with a fixed $100,000 — there is no capital to choose. name is required (≤ 80 chars) and must be unique among your active accounts. description is optional free-form text (≤ 1000 chars). engine is fixed for the account's life. type defaults to manual. The response is the account object:

{
	"id": "f3c88e1a-…",
	"name": "Iron condors",
	"description": "Defined-risk study account",
	"engine": "live_sim",
	"type": "manual",
	"starting_capital": "100000",
	"cash_balance": "100000",
	"current_value": "100000",
	"status": "active",
	"created_at": "2026-06-21T12:00:00Z"
}
  • cash_balance is the settled carry — it never folds in unsettled intraday P&L.
  • current_value is the live net-liquidation value while a trading day is open on the account, otherwise it equals cash_balance.

GET /accounts/{{account_id}}

Reads one account back. {{account_id}} is filled in by either block above.

Interactive — run this request from the docs

Creating a broker_* account returns 409 engine_not_available; a bot type returns 400.

Edit or archive an account

PATCH accepts any of name, description, status — only the fields you send are applied.

# rename
curl -s -X PATCH https://api.0dtespx.com/accounts/$ACCT \
  -H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
  -d '{"name":"Condors — 2025"}'

# archive (blocked while a trading day is open)
curl -s -X PATCH https://api.0dtespx.com/accounts/$ACCT \
  -H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
  -d '{"status":"archived"}'

PATCH /accounts/{{account_id}}

Renames the account {{account_id}} points at. Only the fields you send are applied.

Interactive — run this request from the docs

DELETE /accounts/{id} soft-deletes the account and returns 204. It is blocked while a trading day is open (409 day_open) — cash carry is driven by settlement, never by deletion. PATCHing the account to archived is refused the same way.

Trading analytics

GET /accounts/{id}/analytics reviews the account's settled trading days: it folds their transactions into round-trip trades and returns trade-level aggregate metrics, per-day trade counts, and the trade log. Read-only and unmetered.

curl -s "https://api.0dtespx.com/accounts/$ACCT/analytics" \
  -H "Authorization: $TOKEN" \
  | jq '{trades: .summary.total_trades, win_rate: .summary.win_rate_pct, pf: .summary.profit_factor, net: .summary.total_net_profit_loss}'

GET /accounts/{{account_id}}/analytics

Folds this account's settled trading days into round-trip trades and returns the aggregate metrics, per-day counts, and trade log. Read-only and unmetered — an account with no settled days answers with empty aggregates.

Interactive — run this request from the docs

{
	"summary": {
		"total_trades": 42,
		"win_count": 25,
		"loss_count": 15,
		"scratch_count": 2,
		"win_rate_pct": 62.5,
		"profit_factor": "1.84",
		"total_net_profit_loss": "1234.56",
		"total_fees": "94.67",
		"average_winner": "180.2",
		"average_loser": "-210.1",
		"largest_winner": "540",
		"largest_loser": "-410",
		"average_hold_seconds": 3841,
		"sessions_analyzed": 17
	},
	"days": [{ "date": "2026-07-10", "trade_count": 3 }],
	"trades_truncated": false,
	"trades": [
		{
			"date": "2026-07-10",
			"session_id": "…",
			"opened_at": "2026-07-10T13:35:02Z",
			"closed_at": "2026-07-10T15:12:44Z",
			"hold_seconds": 5862,
			"direction": "short",
			"legs": [{ "instrument": "SPXW  260710C06300000", "quantity": "1", "open_side": "sell to open" }],
			"net_profit_loss": "125.4",
			"fees": "4.51",
			"outcome": "win"
		}
	]
}

Round trips. A trade is a position opened and flattened within one settled day. Legs opened by a single order are grouped as one trade — a vertical or condor placed as one combo order is one trade — and additions to an already-open position count toward that position's trade. Legs legged in under separate orders, and a re-open after a position went flat, are separate trades.

  • net_profit_loss (per trade) and total_net_profit_loss (summary) are net of fees — the gross figure is net_profit_loss + fees. outcome is win, loss, or scratch on the sign of net_profit_loss (scratch is exactly zero).
  • win_rate_pct is 100 × win_count / (win_count + loss_count)scratches are excluded — and is null when there are no decided (win or loss) trades. profit_factor is the winners' net P&L over the absolute losers' net P&L, null when there are no losers. average_winner / average_loser / largest_winner / largest_loser are null when their set is empty. These nullable fields are always present, carrying null rather than being omitted.
  • summary and days always cover every trade. The trades log is newest first and capped at the most recent 1000; trades_truncated is true when older trades were cut (the aggregates still count them).

Only manual live accounts have analytics here; a settled day appears once it has settled after the 4:00 PM ET close. Use Replay to scrub any single settled day moment by moment.

Trading days

A trading day is one day of one account. Orders, positions, transactions, and history all belong to a day — but you rarely name one: the account-level routes resolve it for you, and the day itself opens on your first order. The full trading surface is on the live-trading page; this is the day resource.

Method Path Purpose
GET /accounts/{id}/days Every trading day, newest date first
GET /accounts/{id}/days/current The open day, or null
GET /accounts/{id}/days/{date} One day, by YYYY-MM-DD
GET /accounts/{id}/days/{date}/history Per-second financial history for that day
GET /accounts/{id}/history The same series, for the active day

/days is the settled archive plus the open day, if one is open. /days/current is the "is this account tradeable right now" probe: it answers the open day while one is open and null the moment that day settles. /days/{date} answers 400 for a malformed date and 404 day_not_found for a date this account never traded.

There is no call that opens a day, and none that closes one. A live_sim account's day opens by itself inside your first order of the day — a dry-run opens it too — and it settles at the 4:00 PM ET close. The day is opened by that call alone: the exchange is told about it in the background, so the placement never waits on the exchange to acknowledge the day. A broker engine refuses the placement with 409 engine_not_available; an archived account with 409 account_archived; and an account whose earlier day is still open and unsettled with 409 previous_session_unsettled — today's opening balance is not final until that day settles, so retry later.

To replay a past trading day, use the account-less /practice/sessions endpoints instead.

The trading day object

{
	"id": "9a1b…",
	"account_id": "f3c88e1a-…",
	"date": "2025-01-15",
	"opening_balance": "100000",
	"starting_capital": "100000",
	"status": "open",
	"ended": false,
	"time": "2025-01-15T14:30:00Z",
	"start_time": "2025-01-15T14:30:00Z",
	"end_time": "2025-01-15T21:00:00Z",
	"available_buying_power": "100000",
	"net_liquidation_value": "100000",
	"unrealized_profit_loss": "0",
	"realized_profit_loss": "0",
	"profit_loss": "0"
}
  • opening_balance is the carried cash the day opened with; starting_capital mirrors it for wire continuity (net_liquidation_value = starting_capital + intraday P&L).
  • status is the authoritative lifecycle field — open, settled, or abandoned. ended is a convenience flag that is true once the day is terminal (settled or abandoned); closing_balance and settled_at are present only after a successful settlement.
  • A settled day also carries closing_balance (the cash it settled to, carried into the account) and settled_at.
  • While a day is open it carries settles_at — when it is expected to settle (end_time plus a few minutes). After the close, that's when the settled, replayable day should appear in the list.

The full object also breaks financials out into equities vs equity-options components — the equities half is always zero for SPX-only trading, the equity-options half carries the numbers.

A live day's clock is the real clock — it has no clock control. Advancing or rewinding a replay is a practice-only feature (PATCH /practice/sessions/{sid}); a trading day exposes no clock endpoint. Once it settles you can review it read-only with the ?at= query parameter (see live trading).

Per-second history

curl -s "https://api.0dtespx.com/accounts/$ACCT/days/2025-01-15/history?interval=5" \
  -H "Authorization: $TOKEN"

# …or the active day, without naming a date
curl -s "https://api.0dtespx.com/accounts/$ACCT/history?interval=5" -H "Authorization: $TOKEN"

Returns an array of per-second snapshots (net liquidation value, P&L, buying power, fees, …) for charting. Sampled at 1-second resolution; pass ?interval=<seconds> to down-sample. The series extends as the trading day progresses; on a practice session it is rebuilt whenever you place or delete an order.

Practice sessions (account-less)

Replaying past trading days doesn't use an account. Practice is a one-click, account-less $100,000 sandbox: each day opens at the market open (9:30 AM ET) with a fixed $100,000. The endpoints mirror the trading-day shapes but carry no account_id — there is no account object to create or carry a balance between days.

Method Path Purpose
GET /practice/sessions List your practice days (newest first)
POST /practice/sessions Open a new practice day
GET /practice/sessions/{sid} Get one session
PATCH /practice/sessions/{sid} Advance/rewind the replay clock
DELETE /practice/sessions/{sid} Delete a practice day
GET /practice/sessions/{sid}/history Per-second financial history
GET /practice/sessions/{sid}/orders List orders
POST /practice/sessions/{sid}/orders Place an order
GET /practice/sessions/{sid}/orders/{orderId} Get one order
DELETE /practice/sessions/{sid}/orders/{orderId} Delete an order (and its trades)
GET /practice/sessions/{sid}/positions List positions
GET /practice/sessions/{sid}/transactions List transactions

Open a practice day

curl -s https://api.0dtespx.com/practice/sessions \
  -X POST -H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
  -d '{"date":"2025-01-15"}'

date can be any past trading date, opened in any order. Each POST opens a new, independent session, so a day can hold multiple simulations (each returns 201). Practice days don't carry into one another, so deleting one never disturbs the others. To edit a previously settled day, rewind its clock with PATCH (below).

The response is a session object without account_id, with the fixed $100,000:

{
	"id": "9a1b…",
	"date": "2025-01-15",
	"opening_balance": "100000",
	"starting_capital": "100000",
	"status": "open",
	"ended": false,
	"time": "2025-01-15T14:30:00Z",
	"start_time": "2025-01-15T14:30:00Z",
	"end_time": "2025-01-15T21:00:00Z",
	"available_buying_power": "100000",
	"net_liquidation_value": "100000",
	"unrealized_profit_loss": "0",
	"realized_profit_loss": "0",
	"profit_loss": "0"
}

Advance the clock

A fresh practice session is parked at start_time. Move the clock with PATCH:

curl -s https://api.0dtespx.com/practice/sessions/$SID \
  -X PATCH -H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
  -d '{"time":"2025-01-15T17:00:00Z"}'

The response is the same shape, with time filled in and all financials recomputed at the new moment. time must satisfy start_time ≤ time ≤ end_time, or you get 400. Moving time never trades for you — it saves the new clock, then recomputes positions, delta, P&L, and buying power from existing trades; it never writes orders or transactions. Setting it backward reverts not-yet-filled orders to live and hides later trades; rewinding a settled day back before the close re-opens it for editing.

Setting the clock to end_time (the 4:00 PM ET close) finalizes the day: it settles, status becomes settled (ended flips to true).

Orders, positions, transactions, and history

The /practice/sessions/{sid}/orders, /positions, and /transactions sub-resources are identical in shape to their account-session counterparts — see orders and positions & transactions for the request/response bodies; only the path prefix differs (/practice/sessions/{sid}/… instead of /accounts/{id}/…). Per-second history behaves the same as above and accepts ?interval=<seconds> to down-sample.

Next: place orders and read positions. For today's real-time trading day, see live trading.