Every persistent data domain in backtest-kit—signals, candles, risk positions, schedules, and eleven others—maps to a dedicated MongoDB collection managed by a typed DB service. Each service extendsDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/theonetrade/backtest-kit-redis-mongo-docker/llms.txt
Use this file to discover all available pages before exploring further.
BaseCRUD, which provides a consistent set of Mongoose-backed query methods. Adapters registered with backtest-kit delegate all reads and writes to these services, keeping strategy code entirely decoupled from the database. The upsert pattern (findOneAndUpdate with { upsert: true, new: true }) is applied consistently across all 15 domains to guarantee read-after-write consistency in a single round-trip.
The IPersist* Adapter Contract
backtest-kit exposes oneIPersist*Instance interface per data domain. Each interface requires at minimum:
waitForInit(initial: boolean)— called before the first I/O; used to block on infrastructure readinessreadXData(when?: Date)— fetch the current persisted value for this adapter’s contextwriteXData(value, when: Date)— durably write a new value; must be visible to subsequent reads
PersistXAdapter.usePersistXAdapter(...) factory. The adapter constructor receives the context tuple (e.g., symbol, strategyName, exchangeName) that uniquely identifies one row in the underlying MongoDB collection.
BaseCRUD: Foundation for All DB Services
All 15 DB services inherit fromBaseCRUD, a dependency-injection factory class that wraps a Mongoose Model with a standard query API:
| Method | Description |
|---|---|
create | Insert a new document |
update | Patch an existing document by _id |
findById | Primary-key lookup; O(log n) on the _id index |
findByFilter | Compound-field query; used as the cache-miss fallback |
findAll | Retrieve up to FIND_ALL_LIMIT (1 000) documents matching a filter |
iterate | Async generator that streams documents in batches |
paginate | Offset/limit pagination with optional sort |
The Upsert Pattern
All mutable DB services usefindOneAndUpdate with an upsert to combine create and update into a single atomic operation:
upsert: true— creates the document if no match is found, using thefilterfields as the initial key setnew: true— returns the document after the write, not the pre-write snapshot; this is what makes the read-after-write guarantee possiblesetDefaultsOnInsert: true— applies Mongoose schema defaults to newly inserted documents only, leaving existing documents unchanged
Because
new: true is set on every upsert, the returned document always reflects the state that was just written. Any subsequent readXData() call on the same adapter context will see the same value.Insert-Only Pattern: Candle Immutability
Candle (OHLCV) data is immutable once recorded—a candle for a given symbol, interval, and timestamp should never be overwritten by later ingestion.CandleDbService enforces this with $setOnInsert instead of $set:
$setOnInsert is a no-op when a matching document already exists. Combined with the filter on { symbol, interval, timestamp }, this makes candle ingestion fully idempotent—running the same candle through the pipeline twice produces exactly one stored document.
Infrastructure Initialization Gate
Before any adapter issues its first database call, it must wait for both MongoDB and Redis to complete their connection handshakes. ThewaitForInfra helper uses singleshot to ensure this initialization runs exactly once regardless of concurrent adapter construction:
waitForInit(initial), guarding on the initial flag so infrastructure initialization is only attempted on the very first invocation:
The readTransform Utility
Every DB service appliesreadTransform to raw Mongoose document output before returning it to callers or writing to the cache. This utility normalizes the document shape by spreading all existing fields and adding an id string property derived from _id, so that all upstream code can reference the primary key as id instead of the raw _id ObjectId.
toJSON() is called before readTransform to serialize the Mongoose document into a plain JavaScript object. This step is required; passing a Mongoose document directly would carry prototype methods and internal state that readTransform does not expect.15 MongoDB Collections at a Glance
Each DB service owns one collection. The table below maps each service to its compound key fields—the fields used in thefindOneAndUpdate filter that form the logical primary key for upsert operations.
| DB Service | Collection | Compound Key Fields |
|---|---|---|
candleDbService | candles | symbol, interval, timestamp |
signalDbService | signals | symbol, strategyName, exchangeName |
scheduleDbService | schedules | symbol, strategyName, exchangeName |
riskDbService | risks | riskName, exchangeName |
partialDbService | partials | symbol, strategyName, exchangeName, signalId |
breakevenDbService | breakevens | symbol, strategyName, exchangeName, signalId |
storageDbService | storages | backtest, signalId |
notificationDbService | notifications | backtest, notificationId |
logDbService | logs | entryId |
measureDbService | measures | bucket, entryKey |
intervalDbService | intervals | bucket, entryKey |
memoryDbService | memories | signalId, bucketName, memoryId |
recentDbService | recents | symbol, strategyName, exchangeName, frameName, backtest |
stateDbService | states | signalId, bucketName |
sessionDbService | sessions | strategyName, exchangeName, frameName |