Every tick the engine evaluates your strategy and returns one of several states as a discriminated union. TheDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/theonetrade/backtest-kit/llms.txt
Use this file to discover all available pages before exploring further.
action field on the result object is the discriminator — use it to safely narrow the TypeScript type and access state-specific fields. Understanding the lifecycle is key to writing correct event handlers, callbacks, and monitoring logic.
The Four Core States
No signal is active. The engine calls
getSignal if the strategy’s interval has elapsed since the last call. If getSignal returns null, the state remains idle.interface IStrategyTickResultIdle {
action: "idle";
signal: null;
strategyName: StrategyName;
exchangeName: ExchangeName;
frameName: FrameName;
symbol: string;
currentPrice: number;
backtest: boolean;
createdAt: number;
}
getSignal returned a valid ISignalDto without a priceOpen field (market entry). The signal passed validation and risk checks, and a position is immediately opened at the current VWAP price.interface IStrategyTickResultOpened {
action: "opened";
signal: IPublicSignalRow; // full signal with id, position, prices
strategyName: StrategyName;
exchangeName: ExchangeName;
frameName: FrameName;
symbol: string;
currentPrice: number;
backtest: boolean;
createdAt: number;
}
The
IPublicSignalRow on signal includes priceOpen, priceTakeProfit, priceStopLoss, position, minuteEstimatedTime, pendingAt, scheduledAt, id, and derived fields like pnl, peakProfit, maxDrawdown, partialExecuted, totalEntries, and totalPartials.When
getSignal returns an ISignalDto with a priceOpen field, the signal becomes a scheduled limit order that waits for price to reach priceOpen before activating. While waiting, each tick emits IStrategyTickResultScheduled (first creation) or IStrategyTickResultWaiting (subsequent monitoring ticks). When price reaches priceOpen, the signal transitions to opened → active.The signal is open and the engine is monitoring VWAP against TP and SL on every tick. Active ticks emit unrealized PNL, percentage progress toward TP and SL, and the current signal snapshot.
interface IStrategyTickResultActive {
action: "active";
signal: IPublicSignalRow;
currentPrice: number;
percentTp: number; // 0–100, progress toward take-profit
percentSl: number; // 0–100, progress toward stop-loss
pnl: IStrategyPnL; // unrealized PNL with fees and slippage
strategyName: StrategyName;
exchangeName: ExchangeName;
frameName: FrameName;
symbol: string;
backtest: boolean;
createdAt: number;
_backtestLastTimestamp: number;
}
The signal exited. The
closeReason discriminates between the three automatic exits and one manual exit.type StrategyCloseReason = "time_expired" | "take_profit" | "stop_loss" | "closed";
interface IStrategyTickResultClosed {
action: "closed";
signal: IPublicSignalRow;
currentPrice: number;
closeReason: StrategyCloseReason;
closeTimestamp: number;
pnl: IStrategyPnL;
strategyName: StrategyName;
exchangeName: ExchangeName;
frameName: FrameName;
symbol: string;
backtest: boolean;
closeId?: string; // present for manual "closed" reason
createdAt: number;
}
Cancelled State
When a scheduled signal’sminuteEstimatedTime elapses before priceOpen is reached, or when commitCancelScheduled() is called manually, the signal transitions to cancelled with a reason field.
IStrategyPnL Shape
Thepnl field on active and closed results is always an IStrategyPnL object:
CC_PERCENT_SLIPPAGE) and fees are 0.1% per transaction (configurable via CC_PERCENT_FEE). Both apply on entry and exit.
Signal Validation
Before a signal can open,VALIDATE_SIGNAL_FN checks:
- All prices (
priceOpen,priceTakeProfit,priceStopLoss) must be finite positive numbers - For a long position:
priceTakeProfit > priceOpenandpriceStopLoss < priceOpen - For a short position:
priceTakeProfit < priceOpenandpriceStopLoss > priceOpen - TP distance must be at least
CC_MIN_TAKEPROFIT_DISTANCE_PERCENT(default 0.5%) - SL distance must be between
CC_MIN_STOPLOSS_DISTANCE_PERCENT(0.5%) andCC_MAX_STOPLOSS_DISTANCE_PERCENT(20%) minuteEstimatedTimemust be a positive number (orInfinity)
trycatch returning null — the engine logs the error and skips to the next tick without crashing.
Interval Throttling
Theinterval field on IStrategySchema enforces a minimum gap between getSignal calls. The engine tracks _lastSignalTimestamp and skips getSignal if the elapsed time is less than the configured interval:
interval: '1m') when the strategy is designed to fire at most hourly.
Scheduled Signals (Brief)
Return apriceOpen in your signal DTO to create a scheduled (limit order) signal:
scheduled state and activates when VWAP reaches priceOpen. If the timeout elapses first, the signal is cancelled with reason: "timeout".
Handling Signal Events
listenSignalOnce is available for one-shot handlers with an optional filter predicate: