Skip to main content

Documentation 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.

The backtest-kit-redis-mongo-docker integration replaces backtest-kit’s default file-based persistence with a production-grade dual-store system. MongoDB acts as the source of truth for every write, while Redis holds a compact O(1) ID cache that short-circuits the most frequent read operations. Thirty-three IoC-wired services—grouped into base infrastructure, 15 cache services, and 15 DB services—collaborate behind a clean adapter interface so that trading strategies remain completely unaware of the underlying persistence mechanism.

Three-Layer Architecture

The system is organized into three distinct layers that sit between backtest-kit’s abstract adapter contracts and the physical data stores.
┌─────────────────────────────────────────────────────────┐
│              backtest-kit Adapter Layer                  │
│  IPersistSignalInstance · IPersistRiskInstance · ...     │
│  (15 adapters, one per data domain)                      │
└────────────────────────┬────────────────────────────────┘
                         │ read / write
┌────────────────────────▼────────────────────────────────┐
│                  IoC Service Layer                        │
│  ┌─────────────────────┐   ┌──────────────────────────┐ │
│  │  15 DB Services      │   │  15 Cache Services       │ │
│  │  (BaseCRUD + Mongo)  │   │  (BaseMap + Redis)       │ │
│  └──────────┬──────────┘   └────────────┬─────────────┘ │
└─────────────┼──────────────────────────┼───────────────┘
              │                          │
┌─────────────▼──────────┐  ┌────────────▼──────────────┐
│     MongoDB 8.0.4       │  │      Redis 7.4.1           │
│  (source of truth)      │  │  (O(1) ID cache)           │
└────────────────────────┘  └───────────────────────────┘

Layer 1 — Adapter Interface

backtest-kit defines IPersist* interfaces (e.g., IPersistSignalInstance, IPersistRiskInstance) with two core async methods per domain: readXData() and writeXData(). Each concrete adapter class registered via PersistXAdapter.usePersistXAdapter(...) is instantiated per trading context (symbol, strategy, exchange). Adapters delegate every I/O operation to the IoC service layer—they contain no direct database calls.

Layer 2 — IoC Service Layer

The central ioc container exposes exactly 33 services:
export const ioc = {
  // base infrastructure (3)
  loggerService, mongoService, redisService,

  // cache services — Redis (15)
  candleCacheService, signalCacheService, scheduleCacheService, riskCacheService,
  partialCacheService, breakevenCacheService, storageCacheService, notificationCacheService,
  logCacheService, measureCacheService, intervalCacheService, memoryCacheService,
  recentCacheService, stateCacheService, sessionCacheService,

  // db services — MongoDB (15)
  candleDbService, signalDbService, scheduleDbService, riskDbService,
  partialDbService, breakevenDbService, storageDbService, notificationDbService,
  logDbService, measureDbService, intervalDbService, memoryDbService,
  recentDbService, stateDbService, sessionDbService,
};
Each DB service extends BaseCRUD (Mongoose model operations) and each cache service extends BaseMap (Redis string key/value operations). Both base classes are produced by a dependency-injection factory so they receive the logger and connection clients automatically.

Layer 3 — Infrastructure

Three Docker-managed services provide the runtime:
ServiceImageRole
MongoDBmongo:8.0.4Durable document store, compound indexes
Redisredis:7.4.1In-memory ID cache, SCAN-based iteration
Node.jsnode:22-alpineStrategy runner, IoC container host

Data Flow

Write Path

Every write goes to MongoDB first, then synchronously updates Redis before the adapter method returns. This guarantees that the next read—from any code path—sees the freshly written value.
writeXData(payload, when)

  ├─► xDbService.upsert(...)
  │     └─► MongoDB findOneAndUpdate (upsert: true, new: true)
  │           returns the saved document (with _id)

  └─► xCacheService.setXId(document)
        └─► Redis SET  signal_cache:exchange:strategy:symbol  →  <_id>

Read Path

Reads attempt the Redis cache first. A cache hit resolves to a MongoDB findById lookup by _id—an O(log n) index seek on the primary key. Only on a cache miss does the system fall back to a compound-field query, after which it immediately backfills the cache.
readXData(when)

  ├─► xCacheService.getXId(symbol, strategy, exchange)
  │     │
  │     ├─ HIT  ──► MongoDB findById(_id)  ──► return document
  │     │
  │     └─ MISS ──► MongoDB findByFilter({ symbol, strategy, exchange })
  │                   └─► xCacheService.setXId(document)  [backfill]
  │                         └─► return document

  └─► adapter returns payload to backtest-kit

Infrastructure Initialization Gate

Before any adapter performs its first I/O, a singleshot gate ensures both MongoDB and Redis connections are fully established—regardless of how many adapter instances are constructed concurrently:
const waitForInfra = singleshot(
  async () => {
    await Promise.all([
      ioc.mongoService.waitForInit(),
      ioc.redisService.waitForInit(),
    ]);
  }
);
singleshot (from functools-kit) guarantees the inner function executes exactly once, caching the resulting Promise so every concurrent caller awaits the same initialization sequence.

Explore Further

Persistence Layer

Deep dive into the 15 MongoDB adapter implementations, BaseCRUD methods, and the upsert pattern.

Redis Cache

How BaseMap namespaces keys, manages TTL, and provides O(1) ID lookups for all 15 cache services.

Atomicity

Read-after-write guarantees, look-ahead bias protection, and the when: Date timestamp contract.

Build docs developers (and LLMs) love