Skip to content

Go-live checklist

Repeat this list whenever the binary, config or strategy parameters change. Operational background is in Run in production; the economics of the shipped strategies are in Economics.

The build and the strategy

  • The release build passes its tests: ctest --preset release.
  • You have run a backtest with the parameters you will use, for example ./build/release/bin/fastmm-backtest --config configs/backtest-example.toml --data synthetic with your [strategy.params].
  • It survives the simulated exchange with faults. Enable [sim.faults] in a copy of configs/sim.toml (market-data drop, order-channel drop, skipped depth update, delayed acks, rejects, rate limits; see fastmm-sim-exchange) and run ./scripts/run-sim.sh --duration 5m --sim-config <your sim config>. Each ERROR line in the engine log follows from an injected fault, and the shutdown line says cancel_all ok.
  • A restart restores the position: kill -9 a keyed session with a position, start it again, and check that it logs restored position and ends at the venue's position with no orders the dead process left (What survives a restart).
  • The strategy is deterministic: record a backtest with ./build/release/bin/fastmm-backtest --config <your config> --data synthetic --out - --journal-out runs/bt/session.fmj and replay it with ./build/release/bin/fastmm-replay --journal runs/bt/session.fmj --verify, which must exit with code 0.
  • A live session replays: replay the journal of the simulator run above (the log names it, journal: <path>) with ./build/release/bin/fastmm-replay --journal <path> --verify, which must print replay MATCH and exit with code 0 (Journals, replay and PnL). Do the same for a journal of the practice session below.

A practice session

  • A Binance Demo or testnet session of at least 24 h with the same strategy and risk settings (Run on a testnet).
  • Each ERROR line (grep ' ERROR ' runs/demo-1/engine.log) has a known cause (Troubleshooting).
  • The session's PnL reconciles with the account (Journals, replay and PnL).
  • Your PnL estimate includes fees (example).

Risk limits

A [risk] limit is off when it is 0 or missing (Configuration).

  • max_order_qty and max_order_notional (settlement currency: the base coin for an inverse contract).
  • max_position per instrument, which counts same-side open orders.
  • max_open_orders per instrument.
  • price_collar_bps and fat_finger_bps.
  • stale_md_ms.
  • max_gross_notional and max_net_notional across instruments.
  • max_loss, sized to what you accept losing: the budget carries across restarts until --clear-kill (The latched loss budget). [engine] on_kill decides what the process does when it trips (Kill switch and shutdown).
  • With instruments in more than one settlement currency, [accounting] names the reporting currency and a rate source for each other currency; without it a start with max_loss exits 3 (Configuration).
  • orders_per_sec and burst below the venue's order rate limit for your account. The connectors also limit themselves (Bybit orders_per_second, Deribit matching_engine_rate and matching_engine_burst, Binance from exchangeInfo).
  • stp = true unless you trade against yourself on purpose.

Keys and access

  • Keys come only from the environment (Configuration), and you never run with --allow-inline-secrets.
  • The key has trading permission only: no withdrawals, no transfers.
  • The key is restricted to your server's IP addresses where the venue supports it.
  • One key per running engine, so that revoking one stops one engine.

The host

  • On bare metal, [engine] cpu and net_cpus pin the engine and network threads to isolated cores with spin_mode = "busy". Shared machines use "adaptive"; WSL2 and laptops also cpu = -1. The topology: lines at start show no warning (Cores). fastmm init --profile production writes a Binance Spot config for a dedicated host, and configs/profiles/production-latency.toml has the same [engine] table: busy spinning on isolated cores, timer_slack_ns = 1, lock_memory = true (raise ulimit -l, LimitMEMLOCK=infinity under systemd) and a larger journal ring.
  • The engine and network cores are isolated (isolcpus, nohz_full and rcu_nocbs, or a cpuset), the CPU governor is performance, NIC interrupts go to other cores, and transparent huge pages are madvise or always (/sys/kernel/mm/transparent_hugepage/enabled): the order and book tables ask for 2 MiB pages.
  • Latency figures in fastmm-top and the log are not zero. Hosts without nonstop_tsc (many cloud VMs) take wall time from clock_gettime and still time intervals with the TSC when constant_tsc is present.
  • The system clock is synchronised (chrony or systemd-timesyncd), and the status line's clock_offset_ms stays well below recv_window_ms.
  • The journal directory has room for the session (Journal files); check with df -h runs.
  • The status file works: ./build/release/bin/fastmm-top --name <engine name> --once prints a frame while the engine runs.
  • The log goes to a file (--log), and warnings are mirrored to stderr ([logging] mirror_level).

Stopping

  • You have done a kill-switch drill with this config: a keyed session with open orders, Ctrl-C, shutdown took <n> ms (cancel_all ok) (Reading the last lines), and no open orders on the venue.
  • After every stop, including one that logged cancel_all ok, the venue's open-orders page shows no orders. The shutdown cancel-all sends one request per subscribed instrument, and ok means those requests succeeded; it does not cover orders on other instruments, orders the venue accepted after the request, or a --dry-run (which skips it).
  • You know where the venue's own "cancel all" is on its website, and you have read When cancel_all failed.
  • [engine] on_kill is set, and whatever starts fastmm-live or python -m fastmm run alerts on exit codes 5, 6 and 7, which the shipped systemd unit does not restart (Deploy).
  • Someone watches the first live session from start to finish.