The hot loop in Mode A — the per-tick path that callsDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/theonetrade/backtest-monorepo-parallel/llms.txt
Use this file to discover all available pages before exploring further.
listenActivePing → getPositionEntries → commitAverageBuy — is designed to be pure CPU and local I/O. If MongoDB does not already hold the candles for the replay window, every missing tick triggers a live ccxt HTTP fetch, introducing unpredictable network latency into the innermost loop. The --cache flag exists to eliminate that possibility entirely: it pre-populates MongoDB with all OHLCV candles for every symbol before a single runner starts, so the hot loop never blocks on HTTP.
How --cache Works
When --cache is set, backtest.ts awaits CACHE_CANDLES_FN() before the for loop that calls Backtest.background(). The function iterates every symbol in CC_SYMBOL_LIST and calls cacheCandles() for each one, using the exchange name and date range from the registered schemas:
Backtest.background() calls that follow are guaranteed to find their candles already in MongoDB.
What cacheCandles() Does
cacheCandles is exported from backtest-kit. Internally it:
Fetches OHLCV from ccxt
Opens a connection to the exchange identified by
exchangeName and pages through the OHLCV endpoint for the given symbol, interval, and date range. Each page is a batch of candles returned by the exchange’s REST API.Stores candles via CandleDbService
Each candle batch is written to MongoDB through
CandleDbService.create(), which uses a findOneAndUpdate with $setOnInsert and upsert: true. The upsert means that re-running --cache after a partial fill or after the date range has been extended is safe — no duplicate documents are created.Idempotent Upserts
The compound unique index on(symbol, interval, timestamp) is what makes the upsert strategy safe under repeated runs:
$setOnInsert upsert only writes the document body when the document does not already exist. If it does exist (i.e., the candle was written on a previous --cache run), the operation is a no-op at the application layer — MongoDB acknowledges the update without modifying any field. There is no read-modify-write cycle, no E11000 duplicate-key error loop, and no need for application-side deduplication.
The Redis Cache Layer
AfterCandleDbService has written candles to MongoDB, subsequent reads during the hot loop pass through a Redis cache layer before reaching Mongo. BaseMap stores lookup results keyed by context string:
BaseMap lookup flow
BaseMap lookup flow
- The hot loop requests a candle by
(symbol, interval, timestamp)context key. BaseMapissues a RedisGETfor the serialised key. On a warm cache this is a single round-trip tolocalhost:6379— O(1), sub-millisecond.- On a cache miss,
BaseMapfalls back to a MongoDB query, writes the result back to Redis, and returns the document. - All nine symbol contexts share the same
ioredisclient instance — no per-symbol connection overhead.
-1 (no expiry) for candle data, which is immutable once written.
--cache vs Omitting --cache
--cache is a boolean flag parsed by getArgs() in packages/main/src/helpers/getArgs.ts. There is no --noCache flag — to skip pre-warming, simply omit --cache from the command.
| Scenario | Use --cache? | Reason |
|---|---|---|
| First run against a fresh MongoDB | ✅ Yes | Prevents ccxt HTTP latency from dominating replay time |
After adding new symbols to CC_SYMBOL_LIST | ✅ Yes | New symbols have no cached candles yet |
| After extending the backtest date range | ✅ Yes | New candles for the extended window are not yet in MongoDB |
| Benchmarking throughput | ✅ Yes | Ensures measured speed reflects the hot loop, not HTTP fetch overhead |
| Testing candle fetch logic | ❌ No | Omit --cache to force every tick through the ccxt fetch path for debugging |
| Working with the latest live data | ❌ No | Omit --cache to fetch candles from the exchange in real time |
Persistence Adapter Matrix
The subsystem that benefits most from caching is the candle layer, but it is worth understanding the full adapter configuration to know where data lands in each mode. The table below reflectsconfig/setup.config.ts:
| Subsystem | Live mode | Backtest mode |
|---|---|---|
| Session | Persist (Mongo) | Local file |
| Storage | Persist (Mongo) | Memory |
| Recent | Persist (Mongo) | Memory |
| Notification | Persist (Mongo) | Memory |
| Memory | Persist (Mongo) | Local file |
| State | Persist (Mongo) | Local file |
| Markdown | Dummy (no-op) | Dummy (no-op) |
| Log | JSONL | JSONL |