Documentation Index
Fetch the complete documentation index at: https://mintlify.com/backtest-kit/backtest-kit-redis-mongo-docker/llms.txt
Use this file to discover all available pages before exploring further.
Storage, Notification, and Log are the three audit trail adapters. Rather than influencing ongoing position management, they record what has already happened — closed and opened signals, fired notifications, and timestamped log lines. These adapters are append-oriented: writes upsert by a unique ID so that re-sending an already-persisted record is idempotent.
Storage and Notification are partitioned by the backtest: boolean constructor field. Documents written during a backtest run are stored separately from paper/live documents, so historical simulations never pollute — or read from — real trading data.
Storage adapter
Stores every signal that has been opened or closed, indexed by its signalId. The full signal payload is persisted, making it possible to reconstruct the complete trade history for a given mode.
Context key: (backtest: boolean, signalId: string)
Collection: storage-items
IPersistStorageInstance implementation
PersistStorageAdapter.usePersistStorageAdapter(class implements IPersistStorageInstance {
constructor(readonly backtest: boolean) {}
async waitForInit(initial: boolean) {
if (!initial) return;
await waitForInfra();
}
async readStorageData(): Promise<StorageData> {
const rows = await ioc.storageDbService.listByMode(this.backtest);
return rows.map((row) => row.payload);
}
async writeStorageData(signals: StorageData): Promise<void> {
for (const signal of signals) {
await ioc.storageDbService.upsert(this.backtest, signal.id, signal);
}
}
});
readStorageData calls listByMode(backtest) which returns all documents for the given mode — findAll({ backtest }) under the hood.
Storage Mongoose schema
From src/schema/Storage.schema.ts:
import mongoose, { Document, Schema } from "mongoose";
import { IStorageSignalRow } from "backtest-kit";
interface IStorageDto {
backtest: boolean;
signalId: string;
payload: IStorageSignalRow;
}
interface StorageDocument extends IStorageDto, Document {}
interface IStorageRow extends IStorageDto {
id: string;
createDate: Date;
updatedDate: Date;
}
const StorageSchema: Schema<StorageDocument> = new Schema(
{
backtest: { type: Boolean, required: true, index: true },
signalId: { type: String, required: true, index: true },
payload: { type: Schema.Types.Mixed, required: true },
},
{ timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false }
);
StorageSchema.index({ backtest: 1, signalId: 1 }, { unique: true });
const StorageModel = mongoose.model<StorageDocument>("storage-items", StorageSchema);
StorageDbService — upsert and list by mode
public upsert = async (
backtest: boolean,
signalId: string,
payload: IStorageSignalRow,
): Promise<void> => {
const filter = { backtest, signalId };
const document = await StorageModel.findOneAndUpdate(
filter,
{ $set: { payload } },
{ upsert: true, new: true, setDefaultsOnInsert: true },
);
const result = readTransform(document.toJSON()) as unknown as IStorageRow;
await this.storageCacheService.setStorageId(result);
};
public listByMode = async (backtest: boolean): Promise<IStorageRow[]> => {
return await super.findAll({ backtest }) as IStorageRow[];
};
There is no when column — storage records represent the authoritative final state of a signal and are not subject to look-ahead filtering.
Notification adapter
Stores event notifications raised by strategies. Notifications are returned in reverse chronological order (newest first), and the service caps the list at 200 entries per read.
Context key: (backtest: boolean, notificationId: string)
Collection: notification-items
IPersistNotificationInstance implementation
PersistNotificationAdapter.usePersistNotificationAdapter(
class implements IPersistNotificationInstance {
constructor(readonly backtest: boolean) {}
async waitForInit(initial: boolean) {
if (!initial) return;
await waitForInfra();
}
async readNotificationData(): Promise<NotificationData> {
const rows = await ioc.notificationDbService.listByMode(this.backtest);
return rows.map((row) => row.payload).reverse();
}
async writeNotificationData(notifications: NotificationData): Promise<void> {
for (const notification of notifications) {
await ioc.notificationDbService.upsert(
this.backtest, notification.id, notification,
);
}
}
}
);
listByMode already sorts by createDate descending; readNotificationData then reverses the result so the caller receives oldest-first order, matching the in-memory list layout expected by backtest-kit.
Notification Mongoose schema
From src/schema/Notification.schema.ts:
import mongoose, { Document, Schema } from "mongoose";
import { NotificationModel as NotificationPayload } from "backtest-kit";
interface INotificationDto {
backtest: boolean;
notificationId: string;
payload: NotificationPayload;
}
interface NotificationDocument extends INotificationDto, Document {}
interface INotificationRow extends INotificationDto {
id: string;
createDate: Date;
updatedDate: Date;
}
const NotificationSchema: Schema<NotificationDocument> = new Schema(
{
backtest: { type: Boolean, required: true, index: true },
notificationId: { type: String, required: true, index: true },
payload: { type: Schema.Types.Mixed, required: true },
},
{ timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false }
);
NotificationSchema.index({ backtest: 1, notificationId: 1 }, { unique: true });
const NotificationModel = mongoose.model<NotificationDocument>(
"notification-items", NotificationSchema,
);
NotificationDbService — list with sort and limit
const LIST_LIMIT = 200;
public listByMode = async (backtest: boolean): Promise<INotificationRow[]> => {
const documents = await NotificationModel.find({ backtest })
.sort({ createDate: -1 })
.limit(LIST_LIMIT);
return documents.map((doc) => readTransform(doc.toJSON())) as unknown as INotificationRow[];
};
The 200-document limit prevents unbounded memory growth during long backtest runs with frequent notifications.
Log adapter
Stores free-form strategy log entries. Unlike Storage and Notification, the Log adapter has no backtest partition — all runs share the same log-items collection. Entries are returned newest-first, also capped at 200.
Context key: (entryId: string) — effectively a global singleton per run
Collection: log-items
IPersistLogInstance implementation
PersistLogAdapter.usePersistLogAdapter(class implements IPersistLogInstance {
async waitForInit(initial: boolean) {
if (!initial) return;
await waitForInfra();
}
async readLogData(): Promise<LogData> {
const rows = await ioc.logDbService.listAll();
return rows.map((row) => row.payload).reverse();
}
async writeLogData(entries: LogData): Promise<void> {
for (const entry of entries) {
await ioc.logDbService.upsert(entry.id, entry);
}
}
});
The Log adapter has no constructor arguments — there is no constructor at all. It is a true singleton adapter.
Log Mongoose schema
From src/schema/Log.schema.ts:
import mongoose, { Document, Schema } from "mongoose";
import { ILogEntry } from "backtest-kit";
interface ILogDto {
entryId: string;
payload: ILogEntry;
}
interface LogDocument extends ILogDto, Document {}
interface ILogRow extends ILogDto {
id: string;
createDate: Date;
updatedDate: Date;
}
const LogSchema: Schema<LogDocument> = new Schema(
{
entryId: { type: String, required: true, unique: true, index: true },
payload: { type: Schema.Types.Mixed, required: true },
},
{ timestamps: { createdAt: "createDate", updatedAt: "updatedDate" }, minimize: false }
);
const LogModel = mongoose.model<LogDocument>("log-items", LogSchema);
entryId has unique: true declared directly on the field definition (in addition to the default index), rather than via a separate compound index call, because the context key is a single scalar.
LogDbService — upsert by entryId and list all
const LIST_LIMIT = 200;
public upsert = async (entryId: string, payload: ILogEntry): Promise<void> => {
const filter = { entryId };
const document = await LogModel.findOneAndUpdate(
filter,
{ $set: { payload } },
{ upsert: true, new: true, setDefaultsOnInsert: true },
);
const result = readTransform(document.toJSON()) as unknown as ILogRow;
await this.logCacheService.setLogId(result);
};
public listAll = async (): Promise<ILogRow[]> => {
const documents = await LogModel.find({})
.sort({ createDate: -1 })
.limit(LIST_LIMIT);
return documents.map((doc) => readTransform(doc.toJSON())) as unknown as ILogRow[];
};