Skip to content

Strategy, Runner

const { SymbolRegistry, Runner } = require('@flox-foundation/flox');

SymbolRegistry

const registry = new flox.SymbolRegistry();
Method Returns Description
addSymbol(exchange, name, tickSize) Symbol Register a symbol
symbolCount() number Number of registered symbols

Symbol

Returned by addSymbol. Coerces to a number wherever a symbol ID is expected.

Property Type Description
id number Numeric symbol ID
name string Symbol name
exchange string Exchange name
tickSize number Tick size
const btc = registry.addSymbol('binance', 'BTCUSDT', 0.01);

btc.id         // 1
btc.name       // "BTCUSDT"
Number(btc)    // 1
btc + 0        // 1
btc.toString() // "Symbol(binance:BTCUSDT, id=1)"

Strategy object

A plain JavaScript object with callback properties.

const strategy = {
    symbols: [btc],   // array of Symbol or number

    onStart() {},
    onStop() {},

    onTrade(ctx, trade, emit) { ... },
    onBookUpdate(ctx, emit) { ... },
    onBar(ctx, bar, emit) { ... },

    onFill(ctx, ev, emit) { ... },
    onOrderUpdate(ctx, ev, emit) { ... },
    onQueuePositionChange(ctx, ev, emit) { ... },
    onMarketPositionChange(ctx, ev, emit) { ... },
};

All callbacks are optional.

Order-event callbacks

Callback Fires on
onFill(ctx, ev, emit) Every fill this strategy's own orders produce (status PARTIALLY_FILLED or FILLED)
onOrderUpdate(ctx, ev, emit) Every order-lifecycle status change: NEW / ACCEPTED / CANCELED / REJECTED / REPLACED / TRIGGERED / TRAILING_UPDATED. Fills included — use onFill if you only want those
onQueuePositionChange(ctx, ev, emit) A resting limit order's queue position moved with no other lifecycle transition. ev.queueAhead / ev.queueTotal carry the snapshot. Backtest only
onMarketPositionChange(ctx, ev, emit) A resting limit order's categorical market position transitioned (best / behind_best / mid_spread / level_empty / crossed). Backtest only

ev is an OrderEventData: orderId, symbolId, side, orderType, status, fillQty, fillPrice, exchangeTsNs, rejectReason, queueAhead, queueTotal, the per-stage timestamps submittedAtNs / acceptedAtNs / firstFillAtNs / lastFillAtNs / canceledAtNs / rejectedAtNs / triggeredAtNs / expiredAtNs, plus isMaker, fillRole, marketPosition and distanceToBestTicks.

BarData (bar)

Property Type Description
open, high, low, close number OHLC prices
volume, buyVolume number Total / buy-side volume
startTimeNs, endTimeNs number Bar window timestamps (nanoseconds)
barType, barTypeParam number 0=Time, 1=Tick, ... + interval/threshold
closeReason number 0=Threshold, 1=Gap, 2=Forced, 3=Warmup

SymbolContext (ctx)

Property Type Description
position number Current position quantity
symbolId number Symbol ID
lastTradePrice number Last trade price
bestBid number Best bid
bestAsk number Best ask
midPrice number Mid price

TradeData (trade)

Property Type Description
price number Trade price
qty number Trade quantity
isBuy boolean Buy-side aggressor
side string "buy" or "sell"
timestampNs BigInt Timestamp (nanoseconds)

emit methods

Method Description
emit.marketBuy(qty) Market buy
emit.marketSell(qty) Market sell
emit.limitBuy(price, qty) Limit buy
emit.limitSell(price, qty) Limit sell
emit.provideLiquidity(priceLower, priceUpper, liquidity) Provide AMM liquidity in a price range
emit.withdrawLiquidity(liquidity) Withdraw AMM liquidity
emit.cancel(orderId) Cancel order
emit.closePosition() Close position (reduce-only)

Runner

Synchronous strategy host. Strategy callbacks fire in the caller's thread before the push call returns.

const runner = new flox.Runner(registry, onSignal);        // synchronous
const runner = new flox.Runner(registry, onSignal, true);  // Disruptor background thread

In threaded mode, events are published to a lock-free ring buffer and callbacks fire in a background C++ thread.

Method Description
addStrategy(strategy) Register a strategy object
replaceStrategy(index, strategy) Atomically swap the strategy at index. The old strategy's onStop fires before the swap, the new one's onStart after; bus subscriptions, in-flight orders and connections are untouched. Must be invoked on the V8 thread
start() Start the runner
stop() Stop and clean up
onTrade(symbol, price, qty, isBuy, tsNs) Inject a trade tick
onBookSnapshot(symbol, bidPrices, bidQtys, askPrices, askQtys, tsNs) Inject an L2 snapshot
onBar(symbol, { open, high, low, close, volume?, ... }) Inject a closed OHLC bar

symbol accepts a Symbol object or a raw number. tsNs accepts a number or a bigint.

Hook setters

Each takes a plain JS object, or null to detach.

Method Description
setPnlTracker(tracker) Attach a PnL tracker
setStorageSink(sink) Attach a signal storage sink
setRiskManager(rm) Attach a pre-trade risk manager. Sync only — allow is read inline; throws when threaded
setKillSwitch(ks) Attach a kill switch. Sync only
setOrderValidator(ov) Attach an order validator. Sync only
setMarketDataRecorder(recorder) Attach a MarketDataRecorderHook or BinaryLogRecorderHook
setExecutor(executor) Replace the executor. Sync only — capabilities() is read inline

Trace recording

Method Description
attachTraceRecorder(recorder) Auto-capture every signal into a .floxrun recorder. Sync mode only; throws otherwise
setTraceFeedTsNs(feedTsNs) Stamp every recorded signal with this feed_ts_ns until the next call
traceOrderEvent(opts) Mirror an order event into the attached recorder. No-op with no recorder attached
traceFill(opts) Mirror a fill into the attached recorder

Signal callback

function onSignal(sig) {
    // sig.side       — "buy" | "sell"
    // sig.quantity
    // sig.price      — 0 for market orders
    // sig.orderType  — "market" | "limit" | "stop_market" | ...
    // sig.orderId
}