Documentation Index
Fetch the complete documentation index at: https://mintlify.com/theonetrade/backtest-kit-minio-s3-docker/llms.txt
Use this file to discover all available pages before exploring further.
These three adapters manage the live trading state for each (symbol, strategy, exchange) context. Signal holds the current active position row for a running strategy. Schedule holds a pending scheduled signal that should be committed at a future candle. Strategy holds the deferred commit queue snapshot — the serialized state of any signals queued for deferred processing. Together they give backtest-kit a consistent, crash-resumable picture of what each strategy context was doing at the moment it was last written.
All three adapters use upsert semantics. Every write is a single idempotent PUT to a deterministic MinIO object key. There is no insert-or-update logic — a write always replaces whatever was stored at that key, so the last writer always wins.
Writing null to either the Signal or Schedule adapter effectively clears that slot. The literal JSON null is stored in the MinIO object’s payload field, and the read path returns null when it deserialises it. This is how backtest-kit signals “no active position” or “no pending scheduled signal” without deleting the object.
Signal adapter
Purpose
The Signal adapter persists the single active ISignalRow for a (symbol, strategyName, exchangeName) triple. It is written whenever backtest-kit opens, updates, or closes a position, and read on strategy startup to restore the in-memory signal state. One MinIO object per trading context stores the entire row as JSON.
The underlying service stores objects under the backtest-kit/signal-items/ prefix (bucket backtest-kit, folder signal-items).
Constructor context
PersistSignalAdapter.usePersistSignalAdapter(class implements IPersistSignalInstance {
constructor(
readonly symbol: string,
readonly strategyName: string,
readonly exchangeName: string,
) {}
// ...
});
These three fields uniquely identify a trading context and are passed by backtest-kit when it instantiates the adapter for each running strategy.
Object key pattern
{symbol}/{strategyName}/{exchangeName}
For example, a TRXUSDT strategy called Jan2026Strategy on the ccxt exchange resolves to:
backtest-kit/signal-items/TRXUSDT/Jan2026Strategy/ccxt
Interface methods
async readSignalData(): Promise<ISignalRow | null> {
const row = await ioc.signalDataService.findByContext(
this.symbol,
this.strategyName,
this.exchangeName,
);
return row ? row.payload : null;
}
async writeSignalData(signalRow: ISignalRow | null): Promise<void> {
await ioc.signalDataService.upsert(
this.symbol,
this.strategyName,
this.exchangeName,
signalRow,
);
}
readSignalData returns the stored ISignalRow payload, or null if no object exists yet. writeSignalData accepts the full row (or null to clear) and performs a blind upsert — no read-before-write.
Schema
src/schema/Signal.schema.ts
import { ISignalRow } from "backtest-kit";
interface ISignalDto {
symbol: string;
strategyName: string;
exchangeName: string;
payload: ISignalRow; // the backtest-kit signal row; null when slot is cleared
}
interface ISignalRowDoc extends ISignalDto {
id: string; // equals the object key: symbol/strategyName/exchangeName
createDate: Date;
updatedDate: Date;
}
The payload field carries the ISignalRow type exported by backtest-kit. The envelope fields (id, createDate, updatedDate) are managed by SignalDataService and are not surfaced through the adapter interface.
Adapter registration
PersistSignalAdapter.usePersistSignalAdapter(class implements IPersistSignalInstance {
constructor(
readonly symbol: string,
readonly strategyName: string,
readonly exchangeName: string,
) {}
async waitForInit(initial: boolean) {
if (!initial) {
return;
}
await waitForInfra();
}
async readSignalData(): Promise<ISignalRow | null> {
const row = await ioc.signalDataService.findByContext(
this.symbol,
this.strategyName,
this.exchangeName,
);
return row ? row.payload : null;
}
async writeSignalData(signalRow: ISignalRow | null): Promise<void> {
await ioc.signalDataService.upsert(
this.symbol,
this.strategyName,
this.exchangeName,
signalRow,
);
}
});
waitForInfra() is a singleshot guard that waits for Redis to be ready. It runs only once per process lifetime (initial === true on the very first instantiation).
Schedule adapter
Purpose
The Schedule adapter persists one IScheduledSignalRow per (symbol, strategyName, exchangeName) context. A scheduled signal represents a deferred entry that backtest-kit wants to commit at a specific future candle — for example, a limit order waiting for a price level. The object is written when a signal is queued for scheduling and cleared (written as null) when the scheduler either commits or cancels it.
The underlying service stores objects under the backtest-kit/schedule-items/ prefix.
Constructor context
PersistScheduleAdapter.usePersistScheduleAdapter(class implements IPersistScheduleInstance {
constructor(
readonly symbol: string,
readonly strategyName: string,
readonly exchangeName: string,
) {}
// ...
});
Identical constructor shape to the Signal adapter — the same three context fields uniquely address the single schedule slot for a trading context.
Object key pattern
{symbol}/{strategyName}/{exchangeName}
For example:
backtest-kit/schedule-items/TRXUSDT/Jan2026Strategy/ccxt
Interface methods
async readScheduleData(): Promise<IScheduledSignalRow | null> {
const row = await ioc.scheduleDataService.findByContext(
this.symbol,
this.strategyName,
this.exchangeName,
);
return row ? row.payload : null;
}
async writeScheduleData(scheduleRow: IScheduledSignalRow | null): Promise<void> {
await ioc.scheduleDataService.upsert(
this.symbol,
this.strategyName,
this.exchangeName,
scheduleRow,
);
}
readScheduleData returns the pending IScheduledSignalRow, or null if no scheduled signal is waiting. writeScheduleData passes null to disarm the schedule slot after the scheduled signal has fired.
Schema
src/schema/Schedule.schema.ts
import { IScheduledSignalRow } from "backtest-kit";
interface IScheduleDto {
symbol: string;
strategyName: string;
exchangeName: string;
payload: IScheduledSignalRow; // the scheduled signal row; null when slot is cleared
}
interface IScheduleRow extends IScheduleDto {
id: string; // equals the object key: symbol/strategyName/exchangeName
createDate: Date;
updatedDate: Date;
}
IScheduledSignalRow is the backtest-kit type that describes a deferred signal commitment, including the trigger conditions and the signal data to commit when those conditions are met.
Adapter registration
PersistScheduleAdapter.usePersistScheduleAdapter(class implements IPersistScheduleInstance {
constructor(
readonly symbol: string,
readonly strategyName: string,
readonly exchangeName: string,
) {}
async waitForInit(initial: boolean) {
if (!initial) {
return;
}
await waitForInfra();
}
async readScheduleData(): Promise<IScheduledSignalRow | null> {
const row = await ioc.scheduleDataService.findByContext(
this.symbol,
this.strategyName,
this.exchangeName,
);
return row ? row.payload : null;
}
async writeScheduleData(scheduleRow: IScheduledSignalRow | null): Promise<void> {
await ioc.scheduleDataService.upsert(
this.symbol,
this.strategyName,
this.exchangeName,
scheduleRow,
);
}
});
Strategy adapter
Purpose
The Strategy adapter persists the deferred commit queue snapshot — a StrategyData value representing any signals that have been staged for deferred processing by the strategy engine. backtest-kit writes this snapshot after every queue mutation and reads it on startup to resume pending deferred work. Like Signal and Schedule, exactly one object per (symbol, strategyName, exchangeName) context is maintained.
The underlying service stores objects under the backtest-kit/strategy-items/ prefix.
Constructor context
PersistStrategyAdapter.usePersistStrategyAdapter(class implements IPersistStrategyInstance {
constructor(
readonly symbol: string,
readonly strategyName: string,
readonly exchangeName: string,
) {}
// ...
});
Same three-field context as Signal and Schedule. All three adapters share an identical constructor shape because they all address a single per-context slot.
Object key pattern
{symbol}/{strategyName}/{exchangeName}
For example:
backtest-kit/strategy-items/TRXUSDT/Jan2026Strategy/ccxt
Interface methods
async readStrategyData(): Promise<StrategyData | null> {
const row = await ioc.strategyDataService.findByContext(
this.symbol,
this.strategyName,
this.exchangeName,
);
return row ? row.payload : null;
}
async writeStrategyData(strategyRow: StrategyData | null): Promise<void> {
await ioc.strategyDataService.upsert(
this.symbol,
this.strategyName,
this.exchangeName,
strategyRow,
);
}
readStrategyData returns the stored StrategyData payload or null if no snapshot has been written yet. writeStrategyData replaces the stored snapshot atomically via a single MinIO PUT.
Schema
src/schema/Strategy.schema.ts
import { StrategyData } from "backtest-kit";
interface IStrategyDto {
symbol: string;
strategyName: string;
exchangeName: string;
payload: StrategyData; // the deferred commit queue snapshot; null to clear
}
interface IStrategyRow extends IStrategyDto {
id: string; // equals the object key: symbol/strategyName/exchangeName
createDate: Date;
updatedDate: Date;
}
StrategyData is exported by backtest-kit and represents the serialized deferred work queue for a strategy context.
Adapter registration
PersistStrategyAdapter.usePersistStrategyAdapter(class implements IPersistStrategyInstance {
constructor(
readonly symbol: string,
readonly strategyName: string,
readonly exchangeName: string,
) {}
async waitForInit(initial: boolean) {
if (!initial) {
return;
}
await waitForInfra();
}
async readStrategyData(): Promise<StrategyData | null> {
const row = await ioc.strategyDataService.findByContext(
this.symbol,
this.strategyName,
this.exchangeName,
);
return row ? row.payload : null;
}
async writeStrategyData(strategyRow: StrategyData | null): Promise<void> {
await ioc.strategyDataService.upsert(
this.symbol,
this.strategyName,
this.exchangeName,
strategyRow,
);
}
});