Skip to content

Strategy

Extend Strategy and call lrvx.register with an instance.

class MyStrategy extends Strategy {
    constructor() {
        super({ exchange: 'Binance', symbols: ['BTCUSDT'] });
    }

    onTrade(ctx, trade) { ... }
    onBookUpdate(ctx, book) { ... }
    onBar(ctx, bar) { ... }
    onStart() { ... }
    onStop() { ... }
}

lrvx.register(new MyStrategy());

Constructor options

// Single exchange
super({ exchange: 'Binance', symbols: ['BTCUSDT', 'ETHUSDT'] })

// Multi-exchange (qualified names)
super({ symbols: ['Binance:BTCUSDT', 'Bybit:ETHUSDT'] })

Properties

Property Type Description
primarySymbol string First symbol name
symbols string[] All registered symbol names
hasPosition boolean Whether a position is open (primary symbol)

Callbacks

onTrade(ctx, trade)

Field Type Description
ctx.symbol string Symbol name
ctx.symbolId number Numeric symbol ID
ctx.position number Current position size
ctx.avgEntryPrice number Average entry price, NaN when the position manager reports none
ctx.book.bidPrice number \| null Best bid; null when the bid side has no level (0 is a price)
ctx.book.askPrice number \| null Best ask; null when the ask side has no level
ctx.book.midPrice number \| null Mid price; null unless both sides have a level
ctx.book.spread number \| null Spread; null unless both sides have a level
trade.symbol string Symbol name
trade.price number Trade price
trade.qty number Trade quantity
trade.side string "buy" or "sell"
trade.isBuy boolean
trade.timestampNs number Nanosecond timestamp

onBar(ctx, bar)

Field Type Description
bar.open, bar.high, bar.low, bar.close number OHLC prices
bar.volume, bar.buyVolume number Total / buy-side volume
bar.startTimeNs, bar.endTimeNs number Bar window timestamps (nanoseconds)
bar.barType, bar.barTypeParam number 0=Time, 1=Tick, ... + interval/threshold
bar.closeReason number 0=Threshold, 1=Gap, 2=Forced, 3=Warmup. The engine emits Threshold for a normal close, Gap for the bar that absorbs the remainder of a Renko price jump too wide to walk brick by brick, and Forced for a stop() flush; Warmup is never assigned by the engine itself -- it is for callers who construct their own Bar objects from historical data before calling BarMatrix::warmup().

onStart() / onStop()

Called when the strategy starts and stops.


Order methods

All methods accept an options object. symbol defaults to the primary symbol when omitted.

Market

this.marketBuy({ qty: 1.0 })
this.marketSell({ symbol: 'ETHUSDT', qty: 2.0 })

Limit

this.limitBuy({ price: 50000, qty: 0.1 })
this.limitSell({ price: 51000, qty: 0.1, tif: 'IOC' })  // tif: GTC (default) | IOC | FOK | GTD | POST_ONLY

Stop / take-profit

this.stopMarket({ side: 'sell', trigger: 48000, qty: 0.1 })
this.stopLimit({ side: 'buy', trigger: 52000, price: 52100, qty: 0.1 })
this.takeProfitMarket({ side: 'sell', trigger: 55000, qty: 0.1 })
this.takeProfitLimit({ side: 'sell', trigger: 55000, price: 54900, qty: 0.1 })

Trailing stop

this.trailingStop({ side: 'sell', offset: 100, qty: 0.1 })
this.trailingStopPercent({ side: 'sell', callbackBps: 50, qty: 0.1 })

Order management

this.cancel(orderId)
this.cancelAll()
this.modify(orderId, { price: 50100, qty: 0.2 })
this.closePosition()

Context queries

this.position()             // primary symbol
this.position('ETHUSDT')    // specific symbol
this.bestBid()              // number, or null when there is no bid
this.bestAsk()              // number, or null when there is no ask
this.midPrice()             // number, or null when either side is empty
this.lastPrice()
this.orderStatus(orderId)   // -1 if not found

bestBid, bestAsk and midPrice return null for an empty side, never a number. A price below zero is a quote -- WTI settled at -37.63 in April 2020, day-ahead power clears below zero -- and so is a price of exactly 0, so no number is free to stand for "no quote". Test with === null, not === 0.