A strategy file lives underDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/theonetrade/backtest-monorepo-parallel/llms.txt
Use this file to discover all available pages before exploring further.
./content/<name>.strategy/ and is loaded at runtime by @backtest-kit/cli. The CLI receives the file path as a CLI argument, evaluates it in the running Node process, and registers all schemas declared inside it — no bundler step, no imports from @pro/*, and no changes to the monorepo itself.
Directory Convention
Every strategy lives in its own directory following the<name>.strategy/ pattern:
modules/backtest.module.ts file registers addExchangeSchema and addFrameSchema alongside the strategy. All three schemas must be registered before the runner starts.
Constants
The strategy declares its tuning knobs as module-level constants at the top of the file, making them easy to locate and adjust without hunting through callback bodies:Keeping all tuning constants at the module top level lets you diff parameter changes in version control without reading through callback logic.
addStrategySchema
addStrategySchema is the entry point for every strategy. It registers the strategy under a unique strategyName string and provides a getSignal callback that the engine calls once per symbol to open the initial position:
| Parameter | Type | Description |
|---|---|---|
strategyName | string | Unique identifier referenced by Backtest.background() and listStrategySchema() |
symbol | string | Trading pair e.g. "BTCUSDT" |
when | Date | Current backtest timestamp (or wall-clock time in live mode) |
currentPrice | number | Last close price of the current candle |
getSignal Return Value
| Field | Description |
|---|---|
position | "long" or "short" — direction of the trade |
...Position.moonbag(...) | Spreads stopLoss and takeProfit levels calculated from percentStopLoss |
minuteEstimatedTime | Expected hold time in minutes; the engine uses this for timeout-based exits |
cost | Initial USDT amount to spend on the first entry |
listenActivePing — Laddering Handler
The firstlistenActivePing implements a DCA (dollar-cost averaging) ladder. It is called on every tick where the symbol has an active position:
getPositionEntries(symbol)returns all open DCA entries. If the ladder is already atLADDER_MAX_STEPS, skip.getPositionEntryOverlap(symbol, currentPrice, { upperPercent, lowerPercent })checks whethercurrentPricefalls within±LADDER_UPPER_STEP/±LADDER_LOWER_STEPpercent of any existing entry. If so, skip — this prevents buying the same price zone twice.commitAverageBuy(symbol, LADDER_STEP_COST)places a new buy atcurrentPriceforLADDER_STEP_COSTUSDT, widening the average entry.
listenActivePing — Profit-Taking Handler
The secondlistenActivePing handles the exit condition. Multiple handlers for the same event run in registration order:
getPositionPnlPercent(symbol) returns the current unrealised PnL as a percentage. When it crosses TARGET_PROFIT (3%), commitClosePending closes the position and attaches a Markdown note to the log record.
listenError — Global Error Handler
listenError registers a catch-all sink for any unhandled error thrown during strategy execution:
errorData and getErrorMessage come from functools-kit and serialize the error object into a plain record safe for JSONL logging.
Zero-Import Pattern
Strategy files import only frombacktest-kit and functools-kit. They never import from @pro/* packages. Any workspace service (e.g. core.candleDbService) is reached through globalThis.core, which is typed via the root tsconfig.json path aliases pointing at rolled-up types.d.ts bundles.
This means strategy files are evaluated by @backtest-kit/cli without any knowledge of the internal DI container — yet they still have full type-safe access to every service at runtime.
Complete Strategy File vs. Test Variant
The production strategy and the test variant share the same schema name and DCA logic. The test file strips the laddering listener and setsminuteEstimatedTime: Infinity to keep the position open indefinitely, making it easier to validate profit-taking logic in isolation.