The 16 persist adapters inDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/theonetrade/backtest-kit-redis-postgres-pgpool-docker/llms.txt
Use this file to discover all available pages before exploring further.
backtest-kit-redis-postgres-pgpool-docker replace the framework’s default file-based ./dump/ layer with a production-grade PostgreSQL + Redis backend. Each adapter implements the corresponding IPersist*Instance interface from backtest-kit and is registered once at startup via a static usePersist*Adapter(class ...) call. Strategy code, runners, and the CLI entrypoint are completely unchanged — only the persistence layer is swapped.
What Is a Persist Adapter?
A persist adapter is a class that satisfies aIPersist*Instance contract defined by backtest-kit. The framework instantiates a fresh adapter object for every unique context key (e.g., one per (symbol, strategyName, exchangeName) tuple) and calls waitForInit(initial) the first time it touches that instance. You register your implementation globally by passing it to the static factory method on the corresponding adapter class:
when: Date look-ahead-bias column.
Infrastructure Gate: waitForInfra()
All adapters share a single waitForInfra() promise, wrapped with singleshot from functools-kit. The very first adapter instance to be touched triggers a parallel Promise.all that waits for both PostgreSQL (via TypeORM synchronize) and Redis to be ready. Every subsequent waitForInit call resolves instantly because singleshot memoises the result.
TypeORM runs with
synchronize: true on first connect, so all 16 tables and their unique indexes are created automatically against an empty database. There is no manual migration step.The 16 Adapters at a Glance
| Adapter | Table | Context Key (= unique index) | Purpose |
|---|---|---|---|
| Candle | candle-items | (symbol, interval, timestamp) | OHLCV cache; immutable inserts |
| Signal | signal-items | (symbol, strategyName, exchangeName) | Live signal state per context |
| Schedule | schedule-items | (symbol, strategyName, exchangeName) | Pending scheduled signal |
| Strategy | strategy-items | (symbol, strategyName, exchangeName) | Persistent strategy state per context |
| Risk | risk-items | (riskName, exchangeName) | Active risk positions snapshot |
| Partial | partial-items | (symbol, strategyName, exchangeName, signalId) | Partial profit/loss levels per signal |
| Breakeven | breakeven-items | (symbol, strategyName, exchangeName, signalId) | Breakeven reached flag |
| Storage | storage-items | (backtest, signalId) | Closed/opened signal log per mode |
| Notification | notification-items | (backtest, notificationId) | Event notifications |
| Log | log-items | (entryId) | Strategy log entries |
| Measure | measure-items | (bucket, entryKey) | LLM/API response cache (soft-delete) |
| Interval | interval-items | (bucket, entryKey) | Once-per-interval markers (soft-delete) |
| Memory | memory-items | (signalId, bucketName, memoryId) | Per-signal memory store (soft-delete) |
| Recent | recent-items | (symbol, strategyName, exchangeName, frameName, backtest) | Last public signal per context |
| State | state-items | (signalId, bucketName) | Per-signal state buckets |
| Session | session-items | (strategyName, exchangeName, frameName, symbol, backtest) | One session per running strategy |
Atomicity Model
Every write goes through a singleINSERT … ON CONFLICT … DO UPDATE … RETURNING * statement — no separate read then write. The conflict target is always the unique index whose columns exactly match the context key fields. PostgreSQL serialises concurrent inserts on that key at the storage-engine level; the loser of a race takes the DO UPDATE branch instead of throwing, so no unique-constraint violation ever surfaces to the application. The RETURNING * clause yields the just-written row in the same statement, and that row is fed directly to the Redis cache — never from a follow-up SELECT that could hit a lagging read replica.
Soft-delete adapters (Measure, Interval, Memory) use a parallel atomic pattern — a single server-side UPDATE with jsonb_set rather than a read-modify-write cycle:
Redis O(1) ID Cache
Each domain has a companion*CacheService that stores only the row’s UUID in Redis, keyed by a namespaced context string. On read, the adapter checks Redis first; on a cache hit it fires a primary-key lookup on PostgreSQL (both O(1)); on a miss it falls back to a full filter query and backfills Redis. After every upsert, the cache is updated synchronously from the RETURNING row — never from a subsequent SELECT.
Look-Ahead Bias Protection
Adapters that affect signal logic carry awhen column (bigint, epoch milliseconds) representing the logical simulation timestamp. The epochTransformer value transformer keeps the JS-visible value a plain number since the pg driver returns bigint as a string. The affected adapters are: Risk, Partial, Breakeven, Recent, State, Session, Memory, and Interval. The Measure adapter is intentionally exempt — it caches LLM responses where look-ahead bias does not apply.
Adapter Detail Pages
Candle
Immutable OHLCV cache with no-op conflict update
Signal, Schedule, Strategy
Three adapters sharing the
(symbol, strategyName, exchangeName) context keyRisk, Partial, Breakeven
Three look-ahead-bias–protected adapters carrying the
when columnStorage, Notification, Log
Audit-trail adapters for signal logs, events, and strategy output
Measure, Interval, Memory
Soft-delete adapters using server-side
jsonb_setRecent, State, Session
Strategy lifecycle adapters with look-ahead bias protection