Strategies are deliberately isolated from the monorepo’s compiled packages. TheDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/backtest-kit/backtest-monorepo-parallel/llms.txt
Use this file to discover all available pages before exploring further.
@backtest-kit/cli runner loads a strategy file by path at startup, evaluates it, and discovers the schemas registered inside it — no bundling, no imports into @pro/*, and no DI wiring required on the monorepo side. Adding a new strategy is purely additive: create a directory, write two files, and pass the path to the CLI.
Directory structure
Every strategy lives in its own directory under./content/:
<my>.strategy/) is a readable label only; the CLI cares about the file path you pass as the positional argument.
The strategy file
The strategy file callsaddStrategySchema(...) and attaches any number of listenActivePing and listenError handlers. It can call core.* services freely because globalThis.core is already initialised when the CLI evaluates it.
Required: addStrategySchema
getSignal returns the initial position specification for the strategy. It runs once per new position opened on that symbol.
Optional: listenActivePing
Registers a callback that fires on every candle tick while a position is active. Multiple listenActivePing calls stack — all registered handlers run in registration order.
Optional: listenError
Registers a global error handler for uncaught errors during the backtest run.
The module file
The module file wires the exchange adapter and the replay time-window:| Call | Purpose |
|---|---|
addExchangeSchema(...) | Registers candle fetcher, order-book fetcher, aggregated-trade fetcher, and price/quantity formatters |
addFrameSchema(...) | Sets the replay interval, startDate, and endDate |
setConfig(...) | Overrides engine config constants (stoploss distance, breakeven threshold, etc.) |
The apr_2026 strategy walkthrough
The repository ships with a complete reference strategy at ./content/apr_2026.strategy/. It implements a dollar-cost-averaging ladder that averages into a position on every tick where price has moved outside a proximity band, then closes the entire position once the aggregate PnL crosses a profit target.
Constants
addStrategySchema — initial signal
Position.moonbag constructs a long position with a hard stop-loss at HARD_STOP percent below the entry price. minuteEstimatedTime: 50 tells the engine how long to hold the position open before re-evaluating:
First listenActivePing — ladder averaging
On every active tick, the handler checks two guard conditions before adding a new ladder rung:
- Step cap — if the position already has
LADDER_MAX_STEPSentries, skip. - Proximity guard — if the current price is within
±LADDER_UPPER_STEP/LADDER_LOWER_STEPpercent of any existing entry price, skip (avoids buying into the same price band repeatedly).
commitAverageBuy places a new $100 buy:
Second listenActivePing — PnL close
A separate handler checks the aggregate profit-and-loss. If the position has not yet reached TARGET_PROFIT percent, it returns early. Once the threshold is crossed, commitClosePending closes all open entries and writes a log note:
listenError — error logging
Module file — exchange + frame
backtest.module.ts registers a Binance spot exchange adapter via ccxt and sets the replay window to the full month of April 2026 at 1-minute resolution:
Running the strategy
- Mode B — single symbol (recommended for development)
- Mode A — 9 symbols in parallel
Mode B skips the Use
--entry gate in backtest.ts and hands control to the @backtest-kit/cli runner. It runs a single backtest for the symbol configured inside the strategy’s module file.--noCache during development so candles are fetched fresh on every run. Remove --ui to suppress the web dashboard on :60050.Checklist for a new strategy
Full checklist
Full checklist
Directory
-
./content/<my>.strategy/directory created -
./content/<my>.strategy/<my>.strategy.tsexists -
./content/<my>.strategy/modules/backtest.module.tsexists
-
addStrategySchema({ strategyName, getSignal })is called -
getSignalreturns a valid signal object (position, stop-loss, cost) - At least one
listenActivePinghandler is registered -
listenErroris registered for debugging
-
addExchangeSchema(...)is called with a uniqueexchangeName -
addFrameSchema(...)is called with a uniqueframeName,interval,startDate,endDate -
setConfig(...)is called if any engine defaults need overriding
- Mode B (single symbol,
--noCache) runs to completion without errors - Mode A (
--entry --cache) runs and produces output for all symbols