API reference

Accounts & sessions API

Create accounts, open sessions, advance the clock, 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 day at a time through sessions. 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 sessions 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. 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 when a session 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 session 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 session is open (409 session_open) — cash carry is driven by settlement, never by deletion.

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.

Session endpoints

A session is one trading day under an account. Orders, positions, transactions, and history all live under a session.

Method Path Purpose
GET /accounts/{id}/sessions List trading days (newest first)
GET /accounts/{id}/sessions/current The open live session, or null
GET /accounts/{id}/sessions/head Default landing session, or null
GET /accounts/{id}/sessions/{sid} Get one session
POST /accounts/{id}/sessions Open (or re-open) today's session
GET /accounts/{id}/sessions/{sid}/history Per-second financial history

/current returns the open live session for a live / broker account, or null. /head is the default landing session — the open day if any, else the latest settled day.

Open a session

A live_sim account opens today's session — the body is empty and the date is implicitly today:

curl -s https://api.0dtespx.com/accounts/$ACCT/sessions \
  -X POST -H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
  -d '{}'
  • Live (live_sim) — opens today's session at the latest tick with the carried cash balance, gated on market hours and fresh market data (400 if the market is closed or live data is unavailable). Re-requesting today's session while it is open returns it (200).
  • Broker engines409 engine_not_available.

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

The response is the session 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 session also carries closing_balance (the cash it settled to, carried into the account) and settled_at.
  • While a live day is open it carries settles_at — when the day 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 session'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}); account sessions expose no clock endpoint. Once a live day settles you can review it read-only via the ?at= query parameter (see live trading).

Per-second history

curl -s "https://api.0dtespx.com/accounts/$ACCT/sessions/$SID/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 account session 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}/sessions/{sid}/…). 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 session, see live trading.