Backtest mode replays historical OHLCV data against a trading strategy using the PostgreSQL-backed persistence layer. The entry point isDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/backtest-kit/backtest-kit-redis-postgres-pgpool-docker/llms.txt
Use this file to discover all available pages before exploring further.
src/main/backtest.ts, which gates all execution behind two CLI flags — --entry and --backtest — so the same compiled bundle can be used for every mode without accidental cross-activation. When both flags are present, the process initializes Postgres and Redis, pre-populates the candle cache via warmCandles, then hands off to Backtest.background() to run the replay loop.
Entry Point
The full source ofsrc/main/backtest.ts is shown below. Every symbol, enum value, and date range referenced here maps directly to a registered schema — the frame for the time window and the strategy for signal generation.
| Enum | Value |
|---|---|
ExchangeName.CCXT | "ccxt-exchange" |
FrameName.Jan2026Frame | "jan_2026_frame" |
StrategyName.Jan2026Strategy | "jan_2026_strategy" |
Initialization Sequence
The startup flow is strictly ordered. Each step must succeed before the next begins — any failure throws and halts the process.CLI flags checked
getArgs() parses process.argv via Node’s built-in parseArgs. If either --entry or --backtest is absent (or false by default), main() returns immediately with no side effects. This allows backtest, live, and paper entry points to coexist in the same bundle.postgresService.waitForInit()
Resolves once the PostgreSQL connection pool (routed through PgBouncer/PgPool) is ready to accept queries. Times out after 15 seconds if the database is unreachable. All 16 persistence adapters depend on this connection being established before any read or write is attempted.
redisService.waitForInit()
Resolves once the Redis client has successfully connected and is ready for pub/sub and key-value operations. Times out after 15 seconds. Redis acts as the in-process candle cache and real-time notification bus that sits in front of PostgreSQL.
waitForReady(true)
Signals to the backtest-kit internals that services are initialized and that the runtime should operate in backtest mode (
true). This sets the backtest boolean that flows into every adapter context key — Storage, Session, Notification, and Recent — ensuring historical records are partitioned away from live or paper data.warmCandles()
Fetches every 1-minute OHLCV candle for
TRXUSDT across the full January 2026 window from the CCXT Binance exchange and writes them into the candle_items PostgreSQL table. Subsequent reads during the replay loop are served entirely from PostgreSQL (and the Redis layer in front of it), with no live exchange calls.Frame Definition
The frame tellsBacktest.background() the temporal boundaries and candle interval for the replay. The jan_2026_frame schema is registered in src/logic/frame/jan_2026.frame.ts:
interval, startDate, and endDate must exactly match the parameters passed to warmCandles. A mismatch — for example warming a 5m interval but running a 1m frame — will result in cache misses during the replay.
The warmCandles Utility
warmCandles is a backtest-kit function that pre-populates the candle_items table in PostgreSQL before the replay begins. Understanding its role is important for both correctness and performance.
Without warmup, the candle adapter would attempt a live exchange fetch on every bar during replay. This is slow, rate-limited by the exchange, and non-deterministic — the exchange may not return exactly the same candles on repeated runs.
With warmup, the complete candle history for the target symbol and date range is fetched once, written to candle_items, and from that point forward all reads during the replay are served from PostgreSQL. Redis caches the most recently accessed candles to reduce database round-trips further.
exchangeName here must match an addExchangeSchema registration — in this project, "ccxt-exchange" backed by CCXT Binance spot.
Running Backtest Mode
Two launch paths are supported: running Node directly against the dockerized infrastructure, or running everything inside Docker Compose.MODE=backtest is translated to --backtest by the container entrypoint, and ENTRY=1 maps to --entry. STRATEGY_FILE points to the compiled strategy bundle that is dynamically imported by the UI adapter.
Customizing the Backtest
To run a different symbol, time window, or strategy, adjust the following three areas in lockstep: 1. UpdatewarmCandles parameters to match the new target:
addFrameSchema, and add the new name to FrameName:
Backtest.background() call for each symbol you want to replay simultaneously:
Look-Ahead Bias Prevention
Backtest mode passes
backtest: true to every adapter call. The when: Date column attached to all persisted records represents the simulated time of the replay, not Date.now(). The adapter stack uses this value for time-ordering candle reads, position lifecycle management, and signal evaluation — ensuring that no future candle data can influence a signal that is evaluated at an earlier simulated timestamp. Strategies that call getCandles, getClosePrice, or any position utility receive only data that would have been available at that point in simulated time.