Building Trading Strategies Events

Events

At its core, the Botmain Automation Platform is a Complex Event Processing system (CEP). User strategies can process events at the instrument and portfolio levels, as well as events from other strategies themselves.

Instrument-level Events

To handle instrument-level events, users can implement an InstrumentProcessor class extending InstrumentProcessorBase and add handlers for desired events:

import { Bar, Inject, InstrumentProcessorBase, Market, TradingInterface, Timeframe } from '@botmain/common';
import { MovingAverage } from '@botmain/indicators';
import { MarketOrder } from '@botmain/execution';

export class InstrumentProcessor extends InstrumentProcessorBase {
  
  onBarClose(bar: Bar) {
    //called on every bar close for every instrument in the strategy
  }

  onBarUpdate(bar: Bar) { 
    //called on every tick resulting in a bar update for every instrument in the strategy
  }

  onTrade(trade: Trade) {
    //called when a trade is received for the current instrument
    console.log(`Trade received for ${this.market.symbol} for ${trade.amount}`);
  }
}

Some events are specific to certain type of data subscriptions. For example, tick-related events like onTick() only fire on strategies that have subscribed to a tick stream. The full list of instrument-level events and their required subscriptions are given in the table below:

Event nameAvailable for subscriptionEvent description
onInit()AllEvent is called on the start of strategy to perform instrument level initialization.
onExit(ExitState exitState)AllEvent is called after strategy execution to perform instrument level post processing.
onDayOpen()AllEvent is called every day when instrument exchange is opening. Not applicable to markets that never close, such as crypto
onMarketSessionResume()AllEvent is called when market session for an exchange is resuming. Not applicable to crypto
onDayClose()AllEvent is called every day when instrument exchange is closing. Not applicable to crypto
onMarketSessionPause()AllEvent is called when market session for an exchange is pausing. Not applicable to crypto
onBarClose()IntradayEvent is called when bar on instrument exchange is closing.
onBarUpdate(Bar updatedBar)DailyEvent is called when bar on instrument exchange is updated.
onTick()TickEvent is called when quote for the instrument is received.
onTrade(Trade trade)TickEvent is called when trade for the instrument is received.
onBestBid(BestBidOffer bid)TickEvent is called when bid for the instrument is received.
onBestAsk(BestBidOffer ask)TickEvent is called when ask for the instrument is received.
onFirstTrade()Intraday, TickEvent is called when first trade for the instrument occurred. If only Intraday subscription is used, event is called when first bar is received.
onBarOpen()IntradayEvent is called when bar for the instrument opens. If strategy works on tick data it is fired when first trade of a bar interval is received for an instrument. When working on intraday data the event is fired when first intraday bar of an interval is received.

Portfolio-level Events

To handle portfolio-level events, users can implement a PortfolioProcessor class extending PortfolioProcessorBase and add handlers for desired events:

import { Bar, Inject, PortfolioProcessorBase, Market, TradingInterface, Timeframe } from '@botmain/common';
import { MovingAverage } from '@botmain/indicators';
import { MarketOrder } from '@botmain/execution';

export class PortfolioProcessor extends PortfolioProcessorBase {
  
  onBarClose(bar: Bar) {
    //called on every bar close for every instrument in the strategy
  }

  onBarUpdate(bar: Bar) { 
    //called on every tick resulting in a bar update for every instrument in the strategy
  }

  onTrade(trade: Trade) {
    //called when a trade is received for the current instrument
    console.log(`Trade received for ${this.market.symbol} for ${trade.amount}`);
  }
}

The full list of portfolio-level events and their required subscriptions are given in the table below:

Event nameAvailable for subscriptionEvent description
onInit()AllEvent is called on the start of strategy to perform portfolio level initialization.
onAfterInit()AllEvent is called after onInit() event to perform additional portfolio level initialization. All instrument-level onInit events are fired by this time.
onWarmupEnd()AllThis event is called when historical data warm-up ends. This event fires in real-time mode only.
onDayOpen(Context context)AllEvent is called every day when instrument exchange is opening. Not applicable to markets that never close, such as crypto
onAfterDayOpen(Context context)AllEvent is called after onDayOpen(Context context) event to perform additional portfolio level processing. All instrument-level onDayOpen events are fired by this time. Not applicable to crypto
onDayClose(Context context)AllEvent is called every day when instrument exchange is closing. Not applicable to crypto
onAfterDayClose(Context context)AllEvent is called after onDayClose(Context context) event to perform additional portfolio level processing. All instrument-level onDayClose events are fired by this time. Not applicable to crypto
onMarketSessionPause(Context context)AllEvent is called when market session for an exchange is pausing. Not applicable to crypto
onAfterMarketSessionPause(Context context)AllEvent is called after onMarketSessionPause(Context context) event to perform additional portfolio level processing. All instrument-level onMarketSessionPause events are fired by this time. Not applicable to crypto
onMarketSessionResume(Context context)AllEvent is called when market session for an exchange is resuming. Not applicable to crypto
onAfterMarketSessionResume(Context context)AllEvent is called after onMarketSessionResume(Context context) event to perform additional portfolio level processing. All instrument-level onMarketSessionResume events are fired by this time. Not applicable to crypto
onBarClose(Context context)IntradayEvent is called when bar on at least one exchange is closing. Not applicable to crypto
onAfterBarClose(Context context)IntradayEvent is called after onBarClose(Context context) event to perform additional portfolio level processing. All instrument-level onBarClose events are fired by this time.
onExit(ExitState exitState)AllThis event is called after strategy execution to perform portfolio level post processing.

Inter-strategy Events

Multiple strategies running simultaneously can send and respond to events between each other using the EventEmitter service.

Consider the following example:

import { EventEmitter, Inject, InstrumentProcessorBase } from '@botmain/common';
import { MovingAverage } from '@botmain/indicators';
import { MarketOrder } from '@botmain/execution';

export class InstrumentProcessor extends InstrumentProcessorBase {
  static displayName = 'Trend Trader';

  @Inject() events: EventEmitter;
  @Inject() sma: SimpleMovingAverage;

  onBarClose(bar: Bar) {
    if(bar.close > sma.current) {
      this.orderProcessor.sendOrder(new MarketOrder(...));
    }
  }

  onOrderStatus(event: OrderStatusEvent) {
    if(event instanceof OrderFilledEvent) {
      this.events.emit('trendTrader.orderFilled', event.order);
    }
  }
}

In the above code, the strategy sends a MarketOrder when the bar close price crosses the simple moving average. When the order is filled, the strategy emits an event with custom name. Now consider a second strategy running simultaneously that can handle this custom event:

import { EventEmitter, Inject, InstrumentProcessorBase } from '@botmain/common';
import { Order } from '@botmain/execution';

export class InstrumentProcessor extends InstrumentProcessorBase {
  static displayName = 'Arbitrage Hedge Strategy';

  @Inject() events: EventEmitter;
  @Inject() orderProcessor: OrderProcessor;

  onInit() {
    this.events.on('trendTrader.orderFilled', (order: Order) => {
      console.log('Order filled in Trend Trader strategy', order);
    });
  }
}