Trading bot state persistence: restart without trading blind

A process restart should be boring. If a bot forgets an accepted order, an unprocessed fill, or the relationship between a position and its protective exit, a harmless crash can become duplicate exposure. State persistence is the evidence trail that lets the bot stop, compare local intent with venue reality, and recover safely.

On this page
  1. What must be durable
  2. Two sources of truth
  3. Write-before-send pattern
  4. Startup recovery sequence
  5. Crash testing
  6. FAQ

Persist evidence, not every temporary variable

Market caches, rolling indicators, and UI state can usually be reconstructed from source data. Money-moving state cannot. If a record answers “what did the bot intend, what did the venue accept, and what exposure exists?”, it belongs on durable storage before the process acknowledges success.

RecordWhy retain itKey fields
Trade intentExplains why an order existsstrategy version, signal ID, symbol, side, target size
OrderConnects retries and venue stateclient ID, venue ID, type, limit, status, timestamps
FillBuilds actual exposure and costfill ID, order ID, quantity, price, fee, event time
Position lotTracks basis and exit coveragequantity, average price, realized P&L, protection IDs
Risk statePreserves circuit breakersdaily loss, drawdown peak, cooldown, halt reason
CheckpointBounds recovery workchannel, last sequence, last successful reconciliation

Use the venue's stable identifiers and enforce uniqueness locally. A fill ID should not post twice; a client order ID should map to one intent; an order transition should be append-only or auditable. Keep raw venue payloads where practical so normalization bugs can be investigated later through trading-bot logs.

The venue and local database know different truths

The exchange or broker is authoritative for what it accepted and filled. The local system is authoritative for why it acted, which strategy owned an order, and which event it has processed. Neither view is sufficient alone:

Reconciliation joins the two views. It matches client IDs and venue IDs, imports previously unseen fills, compares open-order sets, calculates exposure from authoritative positions or balances, and flags anything it cannot explain. The order state machine in partial fills and reconciliation handles normal live transitions; persistence makes those transitions survive a restart.

Unknown is not flat

An empty local positions table after a crash does not mean the account is flat. Until the venue responds and discrepancies are resolved, block new entries and assume exposure may exist.

Record intent before sending the order

The dangerous boundary is between a local write and a remote side effect. A practical order path is:

  1. create a deterministic or unique client order ID;
  2. validate the signal through risk and precision gates;
  3. write the intent and a pending-submit record in one local transaction;
  4. send the order with that client ID;
  5. store the acknowledgement and venue order ID;
  6. consume fills idempotently and update exposure;
  7. mark the intent complete only when its lifecycle is resolved.
minimal durable order record{
  "intent_id": "meanrev-BTCUSD-20260718T101500Z",
  "client_order_id": "mr-7f3c...",
  "status": "SUBMITTING",
  "symbol": "BTC/USD",
  "side": "buy",
  "quantity": "0.0025",
  "strategy_version": "meanrev-12",
  "created_at": "2026-07-18T10:15:00.412Z"
}

If the process dies after step three, recovery can decide whether to submit. If it dies after step four, the stable client ID lets recovery query before acting. That is the core of duplicate-order prevention.

A small single-bot deployment can use an embedded transactional database with backups and integrity checks. A plain JSON file rewritten in place is fragile: a crash during write can truncate it, concurrent tasks can overwrite one another, and there is no transaction joining order intent to risk state. Technology matters less than atomic durability and testable semantics.

A safe startup recovery sequence

  1. Start read-only. Disable new entries before connecting any strategy loop.
  2. Load and validate local state. Check database integrity, schema version, strategy version, and unfinished intents.
  3. Fetch venue snapshots. Obtain current open orders, recent orders and fills, balances, and positions where the API supports them.
  4. Match identifiers. Resolve pending submissions by client ID before considering any retry.
  5. Replay unseen events. Insert fills with uniqueness constraints and recalculate exposure.
  6. Compare invariants. Position quantity, open-order coverage, reserved balance, and risk counters must agree.
  7. Quarantine exceptions. Unknown orders, unexplained positions, and missing protection require an alert and an explicit policy.
  8. Restore protection. Verify server-side stops or other approved risk-reducing orders without blindly duplicating them.
  9. Resume deliberately. Enable strategy entries only after reconciliation records a healthy checkpoint.

This sequence is part of uptime and reliability. Automatic restart without recovery merely repeats a failure faster. If reconciliation cannot establish a known state, connect the condition to the kill-switch policy and require operator review.

Crash at every awkward moment

Happy-path unit tests do not prove persistence. Use a fake venue with deterministic responses, then terminate the bot immediately before and after every durable write and network call. Include these cases:

The invariant is stronger than “the process came back”: recovery must not double an order, discard a fill, reset a risk halt, or claim healthy status while local and venue exposure disagree. Monitor time to reconcile, unknown-order count, unmatched-fill count, checkpoint age, and backup restore tests.

Not financial advice. This content is educational. Automated and algorithmic trading carries a real risk of financial loss. Never trade money you cannot afford to lose. Review the SEC investor.gov and CFTC resources before trading.

Frequently asked questions

What state should a trading bot persist?

Persist strategy identity and version, trade intent, client and venue order IDs, order transitions, fills, position lots, risk counters, protective-order relationships, and reconciliation checkpoints. Cached prices and indicators can usually be rebuilt, but evidence of money-moving actions must survive a crash.

Is the trading bot database the source of truth?

The venue is authoritative for orders, fills, balances, and positions that exist there, while the local database is authoritative for the bot's intent, decisions, and observations. Safe recovery compares both. Treating either side alone as complete can create duplicate orders or orphan exposure.

When may a restarted trading bot resume entries?

Only after loading durable state, obtaining fresh venue snapshots, matching known client and venue order IDs, importing missed fills, resolving or quarantining discrepancies, restoring protective-order coverage, and confirming risk limits. Unknown state should block new exposure.

How should trading bot persistence be tested?

Terminate the process at every boundary around order submission and fill processing, including before write, after durable intent, after network send, after venue acceptance, and before local acknowledgement. Restart against a deterministic fake venue and verify that exposure never doubles and every discrepancy produces an alert.

MB

Mustafa Bilgic

Algorithmic trading practitioner · Founder, AITradingBot.us

Mustafa builds and backtests automated trading systems and writes about them without the hype. Every tool on this site is free and runs entirely in your browser.