Skip to main content

Documentation 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.

The Broker singleton is the bridge between Backtest Kit’s internal position state and a real exchange. Every commit operation (open, close, partial, trailing, breakeven, DCA) is intercepted before the corresponding internal state mutation. If the adapter method throws — for example, because an exchange order was rejected, the network timed out, or the fill poll exceeded the maximum attempts — the mutation is skipped and the framework retries automatically on the next tick. This gives the adapter full transactional control: the internal state is never ahead of what the exchange has actually confirmed. All broker calls are silently skipped in backtest mode (payload.backtest === true).

Registration and Lifecycle

Broker.useBrokerAdapter

Register a class constructor or plain object implementing Partial<IBroker>.
Broker.useBrokerAdapter(broker: TBrokerCtor | Partial<IBroker>): void
broker
TBrokerCtor | Partial<IBroker>
required
A class constructor (called with new) or an already-instantiated object. Only the methods you need to implement are required — the rest are no-ops via BrokerBase defaults.
Must be called before Broker.enable().

Broker.enable

Subscribe the broker to the internal syncSubject event bus. This wires signal-open and signal-close events automatically. Call once at startup.
Broker.enable(): () => void
Returns a dispose function equivalent to calling Broker.disable(). Protected by a singleshot guard — calling it multiple times is a no-op.

Broker.disable

Unsubscribe from syncSubject and reset the singleshot guard. Safe to call multiple times.
Broker.disable(): void

IBroker Interface

Implement any subset of these methods. BrokerBase provides default no-op implementations that log each event.

waitForInit

Called once before the first event. Use to authenticate with the exchange, load API keys, or establish connections.
waitForInit(): Promise<void>

onSignalOpenCommit

Called when a scheduled limit order is confirmed filled and the position becomes active. Routed automatically via syncSubject when Broker.enable() is active.
onSignalOpenCommit(payload: BrokerSignalOpenPayload): Promise<void>
Key fields in BrokerSignalOpenPayload:
FieldTypeDescription
symbolstringTrading pair symbol
costnumberDollar cost of entry
position"long" | "short"Trade direction
priceOpennumberEntry price
priceTakeProfitnumberTake-profit price
priceStopLossnumberStop-loss price
pnlIStrategyPnLCurrent PNL snapshot
backtestbooleanAlways false when adapter is invoked
context{ strategyName, exchangeName, frameName? }Routing context

onSignalCloseCommit

Called when a position is closed (TP/SL/manual). Routed automatically via syncSubject.
onSignalCloseCommit(payload: BrokerSignalClosePayload): Promise<void>
Additional fields beyond the open payload: currentPrice, totalEntries, totalPartials, closeReason.

onPartialProfitCommit

Called explicitly before commitPartialProfit mutates state.
onPartialProfitCommit(payload: BrokerPartialProfitPayload): Promise<void>
Payload includes percentToClose, cost (dollar value of the portion), currentPrice, priceTakeProfit, priceStopLoss, position.

onPartialLossCommit

Called explicitly before commitPartialLoss mutates state.
onPartialLossCommit(payload: BrokerPartialLossPayload): Promise<void>
Same shape as BrokerPartialProfitPayload.

onTrailingStopCommit

Called before commitTrailingStop / commitTrailingStopCost mutates state.
onTrailingStopCommit(payload: BrokerTrailingStopPayload): Promise<void>
Includes percentShift, currentPrice, newStopLossPrice (absolute), takeProfitPrice, position.

onTrailingTakeCommit

Called before commitTrailingTake / commitTrailingTakeCost mutates state.
onTrailingTakeCommit(payload: BrokerTrailingTakePayload): Promise<void>
Includes percentShift, currentPrice, newTakeProfitPrice (absolute), takeProfitPrice, position.

onBreakevenCommit

Called before commitBreakeven mutates state. newStopLossPrice equals effectivePriceOpen.
onBreakevenCommit(payload: BrokerBreakevenPayload): Promise<void>
Includes currentPrice, newStopLossPrice, newTakeProfitPrice, position.

onAverageBuyCommit

Called before commitAverageBuy mutates state.
onAverageBuyCommit(payload: BrokerAverageBuyPayload): Promise<void>
Includes currentPrice, cost, priceTakeProfit, priceStopLoss, position.

BrokerBase

Extend BrokerBase instead of implementing IBroker from scratch. It provides default no-op implementations that log every event, so you only need to override the methods you care about.
import { BrokerBase, Broker, BrokerSignalOpenPayload } from 'backtest-kit';

class MyBroker extends BrokerBase {
  async waitForInit() {
    await this.exchange.connect();
  }

  async onSignalOpenCommit(payload: BrokerSignalOpenPayload) {
    const { symbol, cost, priceOpen, position } = payload;
    await this.exchange.placeOrder({ symbol, side: position, qty: cost / priceOpen });
  }

  async onSignalCloseCommit(payload) {
    await this.exchange.closePosition(payload.symbol);
  }
}

Broker.useBrokerAdapter(MyBroker);
Broker.enable();

Transactional Guarantee

If any adapter method throws, the corresponding internal state mutation is not applied. Backtest Kit will call the same adapter method again on the next tick, effectively retrying the operation until it succeeds. This means:
  • Exchange rejections do not corrupt internal position state.
  • Network timeouts cause automatic retry rather than stale state.
  • Partial fills can be detected, rolled back, and the operation retried cleanly.
Adapter methods that succeed but then throw during post-processing (e.g., after placing an entry order but before placing the TP/SL) can leave the exchange position unprotected. Implement proper rollback logic (close via market) in your adapter for these cases.

Build docs developers (and LLMs) love