Live trading bots fail. Processes crash, servers reboot, deployments happen mid-trade. Backtest Kit handles all of this through a persist-and-restart pattern: every state-changing operation writes to disk before returning, so if the process dies at any moment, the next restart reads the last known state from disk and resumes monitoring exactly where it left off — no duplicate signals, no orphaned positions, no missed closes.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/theonetrade/backtest-kit/llms.txt
Use this file to discover all available pages before exploring further.
How it works
The core of crash recovery isPersistSignalAdapter — a singleton utility class that manages one IPersistSignalInstance per (symbol, strategyName, exchangeName) triple. Each instance writes the current ISignalRow (or null on close) as a JSON file using writeFileAtomic, which performs a write-then-rename so the file is never left in a corrupt state.
File path convention:
ClientStrategy.waitForInit() runs once (via singleshot) to load the persisted signal before the first getSignal call. If the file exists, the signal is restored and the strategy immediately starts monitoring TP/SL as if the position had just been opened.
waitForInit and the singleshot pattern
singleshot ensures the initialization runs exactly once per ClientStrategy instance, no matter how many ticks arrive concurrently. In backtest mode the read is skipped entirely — backtest always starts from a clean slate for speed.
In backtest mode,
setPendingSignal always writes null (a no-op to disk). Persistence is disabled automatically — there is no configuration needed.setPendingSignal — centralized state updates
All signal changes go through a single method:
setPendingSignal. This means the on-disk state is always in sync with in-memory state. There is no separate “flush” step to remember.
Custom persistence adapter
The default adapter writes JSON files to./dump/. You can replace it with any storage backend — Redis, MongoDB, PostgreSQL — by implementing IPersistSignalInstance and calling usePersistSignalAdapter():
IPersistSignalInstance interface requires three methods. Alternatively, you can extend the PersistBase class (which uses readValue(entityId) and writeValue(entityId, entity) for raw key-value storage) and wrap it inside your IPersistSignalInstance implementation — the same pattern used by the built-in PersistSignalInstance.
Initialize the storage backend for this context. Called once before first use via
singleshot.Read and return the persisted signal for this
(symbol, strategyName, exchangeName) triple, or null if none exists.Write (or clear, if
null) the signal state for this triple. Must be atomic to prevent corruption on crash.Recovery flow
The full restart sequence is:waitForInit loads persisted state
On the first tick,
ClientStrategy.waitForInit() reads the signal JSON from disk (or your custom adapter). If a signal exists, _pendingSignal is set before getSignal is ever called.Strategy resumes monitoring
The strategy immediately starts checking TP/SL on every tick, exactly as if the position had just been opened.
getSignal will not be called again until the position closes.Production recommendations
The default file-based storage works well for single-symbol bots but has two limitations at scale:- Concurrent writes — multiple symbols writing simultaneously can saturate filesystem I/O
- No query capability — finding all open positions across strategies requires scanning the directory
findOneAndUpdate with unique compound indexes handles concurrent writes, and Redis provides O(1) per-context lookups so each tick is effectively a single GET + one findById, with no B-tree scans on the hot path.