IActionCallbacks

Lifecycle and event callbacks for action handlers.

Provides hooks for initialization, disposal, and event handling. All callbacks are optional and support both sync and async execution.

Use cases:

  • Resource initialization (database connections, file handles)
  • Resource cleanup (close connections, flush buffers)
  • Event logging and monitoring
  • State persistence
onInit: (actionName: string, strategyName: string, frameName: string, backtest: boolean) => void | Promise<void>

Called when action handler is initialized.

Use for:

  • Opening database connections
  • Initializing external services
  • Loading persisted state
  • Setting up subscriptions
onDispose: (actionName: string, strategyName: string, frameName: string, backtest: boolean) => void | Promise<void>

Called when action handler is disposed.

Use for:

  • Closing database connections
  • Flushing buffers
  • Saving state to disk
  • Unsubscribing from observables
onSignal: (event: IStrategyTickResult, actionName: string, strategyName: string, frameName: string, backtest: boolean) => void | Promise<void>

Called on signal events from all modes (live + backtest).

Triggered by: StrategyConnectionService via signalEmitter Frequency: Every tick/candle when strategy is evaluated

onSignalLive: (event: IStrategyTickResult, actionName: string, strategyName: string, frameName: string, backtest: boolean) => void | Promise<void>

Called on signal events from live trading only.

Triggered by: StrategyConnectionService via signalLiveEmitter Frequency: Every tick in live mode

onSignalBacktest: (event: IStrategyTickResult, actionName: string, strategyName: string, frameName: string, backtest: boolean) => void | Promise<void>

Called on signal events from backtest only.

Triggered by: StrategyConnectionService via signalBacktestEmitter Frequency: Every candle in backtest mode

onBreakevenAvailable: (event: BreakevenContract, actionName: string, strategyName: string, frameName: string, backtest: boolean) => void | Promise<void>

Called when breakeven is triggered (stop-loss moved to entry price).

Triggered by: BreakevenConnectionService via breakevenSubject Frequency: Once per signal when breakeven threshold is reached

onPartialProfitAvailable: (event: PartialProfitContract, actionName: string, strategyName: string, frameName: string, backtest: boolean) => void | Promise<void>

Called when partial profit level is reached (10%, 20%, 30%, etc).

Triggered by: PartialConnectionService via partialProfitSubject Frequency: Once per profit level per signal (deduplicated)

onPartialLossAvailable: (event: PartialLossContract, actionName: string, strategyName: string, frameName: string, backtest: boolean) => void | Promise<void>

Called when partial loss level is reached (-10%, -20%, -30%, etc).

Triggered by: PartialConnectionService via partialLossSubject Frequency: Once per loss level per signal (deduplicated)

onPingScheduled: (event: SchedulePingContract, actionName: string, strategyName: string, frameName: string, backtest: boolean) => void | Promise<void>

Called during scheduled signal monitoring (every minute while waiting for activation).

Triggered by: StrategyConnectionService via schedulePingSubject Frequency: Every minute while scheduled signal is waiting

onScheduleEvent: (event: ScheduleEventContract, actionName: string, strategyName: string, frameName: string, backtest: boolean) => void | Promise<void>

Called on scheduled signal lifecycle events (creation / cancellation).

Triggered by: StrategyConnectionService via scheduleEventSubject Frequency: Once on creation (action "scheduled") and once on cancellation before activation (action "cancelled": timeout / price_reject / user). The scheduled -> active transition is NOT reported here — activation surfaces as an "opened" signal instead.

Manual wiring — EVENT-BASED (driving the exchange from an action registered via addActionSchema)

An action is the alternative to a Broker adapter for binding the framework to a real exchange: both run inside the strategy tick, so the commit-functions from src/function/strategy.ts are callable here and take effect on the next tick. On event.action === "scheduled" place the real resting/limit order (tag it with event.data.id) and, if it resolves at once, call commitActivateScheduled(event.symbol, { id }); on a reject call commitCancelScheduled(event.symbol, { id }). On event.action === "cancelled" (the strategy has already dropped the scheduled signal) cancel the matching exchange order; event.reason says why. For ongoing polling of the resting order use onPingScheduled (every tick).

onPendingEvent: (event: SignalEventContract, actionName: string, strategyName: string, frameName: string, backtest: boolean) => void | Promise<void>

Called on pending signal lifecycle events (open / close).

Triggered by: StrategyConnectionService via signalEventSubject Frequency: Once when a pending position is opened (action "opened": new signal / immediate / scheduled or user activation) and once when it is closed (action "closed" with closeReason take_profit / stop_loss / time_expired / closed).

Manual wiring — EVENT-BASED (driving the exchange from an action registered via addActionSchema)

Alternative to a Broker adapter — the commit-functions from src/function/strategy.ts are callable here (same tick context) and apply on the next tick. On event.action === "opened" place the real entry + protective TP/SL orders; on event.action === "closed" (the strategy has already removed the signal) flatten the real position and cancel leftover orders.

Note: onPendingEvent fires only at open/close — it is NOT a per-tick monitor. To translate intra-position exchange fills into commitCreateTakeProfit / commitCreateStopLoss / commitClosePending on every tick, use onPingActive (fires each tick while the position is open).

onPingActive: (event: ActivePingContract, actionName: string, strategyName: string, frameName: string, backtest: boolean) => void | Promise<void>

Called during active pending signal monitoring (every minute while position is active).

Triggered by: StrategyConnectionService via activePingSubject Frequency: Every minute while pending signal is active

onPingIdle: (event: IdlePingContract, actionName: string, strategyName: string, frameName: string, backtest: boolean) => void | Promise<void>

Called every tick when no signal is active (idle state).

Triggered by: StrategyConnectionService via idlePingSubject Frequency: Every tick while no signal is pending or scheduled

onRiskRejection: (event: RiskContract, actionName: string, strategyName: string, frameName: string, backtest: boolean) => void | Promise<void>

Called when signal is rejected by risk management.

Triggered by: RiskConnectionService via riskSubject Frequency: Only when signal fails risk validation (not emitted for allowed signals)

onOrderSync: (event: OrderSyncContract, actionName: string, strategyName: string, frameName: string, backtest: boolean) => void | Promise<void>

Called when framework attempts to open or close a position via limit order. Return false (or throw) to reject the operation — framework will retry on next tick.

NOTE: Unlike other callbacks, exceptions from this method are NOT swallowed. They propagate up to CREATE_SYNC_FN which catches them and returns false. Throw to reject the operation — framework will retry on next tick.

MANUAL WIRING — EXCEPTION-BASED GATE: the action-side equivalent of the Broker onSignalOpenCommit / onSignalCloseCommit gate. Throwing (or returning false) on event.action === "signal-open" rolls the open back to idle (a scheduled activation is cancelled); on "signal-close" it skips the close and leaves the position open — retried next tick. Rides the same syncSubject emission as the Broker commit hooks, so a throw from either is collapsed to false by CREATE_SYNC_FN. Backtest short-circuits the gate to true (live-only).

onOrderCheck: (event: OrderCheckContract, actionName: string, strategyName: string, frameName: string, backtest: boolean) => void | Promise<void>

Called on every live tick while a pending signal is monitored, BEFORE TP/SL/time evaluation, to confirm the order is still pending (open) on the exchange.

Fires for both monitored states, discriminated by event.type: "active" — pending signal (open position); "schedule" — scheduled signal (resting entry order awaiting activation).

Query the exchange by event.signalId and THROW ONLY when the order is NOT FOUND by that id (filled, cancelled, or liquidated externally) — the framework then closes the position with closeReason "closed" (type "active") or cancels the scheduled signal with reason "user" (type "schedule").

CRITICAL: swallow transient/network errors (timeout, 5xx, rate limit, disconnect) — return normally instead of throwing, otherwise a connectivity blip would wrongly close an open position. Throw exclusively on a confirmed "order not found by id" result.

NOTE: Like onOrderSync, exceptions from this method are NOT swallowed. They propagate up to CREATE_SYNC_PENDING_FN which catches them and returns false.

MANUAL WIRING — EXCEPTION-BASED GATE: the action-side equivalent of the Broker onOrderCheck. A THROW on a confirmed "order not found by id" closes the position with closeReason "closed" (retried via CREATE_SYNC_PENDING_FN). This is the throw-driven alternative to the imperative commitClosePending (call it from pingActive instead) — pick one, not both, for the same "order gone" condition. Backtest short-circuits the gate (live-only).