Every trading strategy in backtest-kit-redis-postgres-pgpool-docker is a self-contained TypeScript module that registers its logic through two schema functions —Documentation 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.
addStrategySchema and addFrameSchema — and is compiled into a single CommonJS bundle that the CLI can load at runtime. The source lives in src/logic/, strategies and frames are kept in dedicated sub-folders, and src/logic/index.ts acts as the single entrypoint that imports them both.
Project Layout
src/logic/index.ts simply side-effects both modules in the correct order — frames before strategies:
Enum Registration
All three enums act as type-safe string constants that tie the strategy, frame, and exchange together. Add a new entry to each enum whenever you create a new strategy or frame.The string values —
"ccxt-exchange", "jan_2026_frame", "jan_2026_strategy" — must exactly match the exchangeName, frameName, and strategyName strings passed to the respective add*Schema calls and to Backtest.background / Live.background in src/main/.Registering a Frame with addFrameSchema
A frame defines the time window and candle resolution for a backtest run. It has no signal logic — it is pure configuration that tells the backtest engine which candles to replay and at what interval.
frameName
Must match a value in the
FrameName enum and the frameName argument passed to Backtest.background.interval
The primary candle resolution the backtest engine ticks at. Common values:
"1m", "5m", "1h", "4h", "1d".startDate / endDate
Inclusive date range that the backtest replays. Use ISO-8601 UTC strings to avoid timezone surprises.
note
Human-readable label shown in logs and the web UI.
Registering a Strategy with addStrategySchema
addStrategySchema takes a strategyName string and an async getSignal function. The engine calls getSignal once per candle tick for every symbol that is running under this strategy name.
getSignal signature
null to skip the tick. Return a signal object to open (or update) a position.
Signal object shape
Position.moonbag is a helper from backtest-kit that computes ladder take-profit levels and a stop-loss from a percentage distance, spreading the risk across multiple partial exits.
Full Strategy Example (jan_2026.strategy.ts)
The strategy below reads pre-loaded signal entries from assets/entry.jsonl, checks whether the current 1-minute close is inside the entry range, uses a 4-hour candle mid-range to decide direction, and returns the position payload.
Key backtest-kit Helpers Used
getClosePrice(symbol, interval)
getClosePrice(symbol, interval)
Returns the close price of the most recent complete candle for the given symbol and interval. In backtest mode this is the close of the current replay candle; in live mode it is the most recently cached close.
getCandles(symbol, interval, count)
getCandles(symbol, interval, count)
Returns an array of the last
count complete candles (oldest first). Each element has { open, high, low, close, volume, timestamp }.alignToInterval(date, interval)
alignToInterval(date, interval)
Floors a
Date to the start of the given candle interval. Used here to align the signal’s publishedAt timestamp to a 1-minute boundary so it can be compared to the simulation clock.getPositionPnlPercent(symbol)
getPositionPnlPercent(symbol)
Returns the current unrealised P&L percentage of the open position for the symbol, measured from entry price to the current close.
getPositionHighestProfitDistancePnlPercentage(symbol)
getPositionHighestProfitDistancePnlPercentage(symbol)
Returns the percentage drawdown from the position’s all-time profit peak to the current P&L. A positive value means the position has pulled back from its peak — used here to trigger the trailing take.
commitClosePending(symbol, { id, note })
commitClosePending(symbol, { id, note })
Marks the open position for
symbol as pending-close. The engine executes the close at the next available price. Pass an id (or "unknown") and an optional note that is persisted in the log.Log.info / Log.debug
Log.info / Log.debug
Structured logger that writes to the
log-items table via the Log persist adapter. Log.info is for operational messages visible in the web UI; Log.debug is for verbose diagnostic output.Trailing Take and Peak Staleness with listenActivePing
listenActivePing registers a callback that fires every candle tick while a position is open. Register multiple listeners; they run independently. The two listeners in jan_2026.strategy.ts handle two distinct exit conditions:
Trailing Take
Closes the position once it has pulled backTRAILING_TAKE percent from its all-time profit peak:
Peak Staleness
Closes the position if it reached a meaningful profit peak but has not advanced forPEAK_STALENESS_SINCE_MINUTES minutes since that peak:
Error Handling with listenError
listenError registers a global error handler for any exception thrown inside getSignal or listenActivePing callbacks. Always register one to avoid silent failures:
The SignalEntryModel Shape
Signal entry data loaded from assets/entry.jsonl follows this interface:
assets/entry.jsonl is one JSON object conforming to this interface. The strategy loads all of them at startup and matches each to the correct symbol and tick timestamp.
Building the Strategy Bundle
The strategy is compiled by Rollup into a single CommonJS file at./build/index.cjs. The CLI then loads that file at runtime via --ui ./build/index.cjs.
rollup -c runs rollup.config.mjs which compiles src/index.ts (which imports src/logic/index.ts) and outputs:
| Output | Format | Purpose |
|---|---|---|
build/index.cjs | CommonJS | Loaded by the backtest-kit CLI at runtime |
types.d.ts | ESM type declarations | TypeScript consumers |