Building Trading Strategies Runtime Model

Runtime Model

A Botmain strategy consists of two user-implemented Typescript classes, InstrumentProcessor and PortfolioProcessor, extending InstrumentProcessorBase and PortfolioProcessorBase, respectively.

InstrumentProcessor is responsible for analyzing a single symbol while PortfolioProcessor provides methods to analyze all instruments in the portfolio. At an even higher level, entirely unrelated strategies can communicate with each other using Events [Inter-strategy Events], allowing traders to build robust, multi-strategy funds.

InstrumentProcessor

InstrumentProcessor defines an instrument level strategy and handles events at the instrument level. That is, each market in the backtest is initialized with its own InstrumentProcessor.

To build a very simple instrument-level strategy that buys or sells when the price crosses the exponential moving average, lets create an InstrumentProcessor:

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

export class InstrumentProcessor extends InstrumentProcessorBase {
  static display = "Trend Trader";

  @Inject() orderProcessor: OrderProcessor;
  @Inject() movingAverage: MovingAverage;

  onBarClose(bar: Bar) {
    this.tradeOnCrossedEMA(bar);
  }

  private async tradeOnCrossedEMA(bar: Bar) {
    const crossedUp = bar.open < this.movingAverage.last && bar.close > this.movingAverage.last;
    const crossedDown = bar.open > this.movingAverage.last && bar.close < this.movingAverage.last;
    const crossedMovingAverage = crossedUp || crossedDown;

    if (crossedMovingAverage) {
      //Trade when 2 hour bar crosses moving average.
      const side = bar.close > bar.open ? "buy" : "sell";
      const marketOrder = new MarketOrder(this.market, 0.01, side);
      const order = await this.tradingApi.sendOrder(marketOrder);

      console.log("Sent order", order);
    }
  }
}

Breaking it down

PortfolioProcessor

A PortfolioProcessor which is initialized only once per strategy and contains the functions and events to analyze the market at the portfolio level.

Internally, PortfolioProcessorBase has a reference to all the InstrumentProcessorBase instances:

export class PortfolioProcessorBase {
  protected instrumentProcessors: InstrumentProcessorBase[];
}

Likewise, internally, the InstrumentProcessorBase has a reference to the parent PortfolioProcessorBase instance:

export class InstrumentProcessorBase {
  protected portfolioProcessors: PortfolioProcessorBase;
}

To handle portfolio-level events, users can implement a PortfolioProcessor class extending PortfolioProcessorBase and add handlers for desired events. For example, below is how to build a simple portfolio-level algorithm to calculate the average basket volatility for all instruments:

import { Bar, PortfolioProcessorBase, Timeframe } from "@botmain/common";
import { sum } from "lodash";

export class PortfolioProcessor extends PortfolioProcessorBase {
  protected override instrumentProcessors: InstrumentProcessor[];

  onAfterBarClose(bar: Bar) {
    const instrumentVols = this.instrumentProcessors.map((inst) => inst.volatilityRatio);
    this.avgBasketVolatility = sum(instrumentVols) / this.instrumentProcessors.length;
  }
}

Breaking it down

  • Create a PortfolioProcessor class extending PortfolioProcessorBase
  • Override the instrumentProcessorhow s property with our own InstrumentProcessor[] type in order to be able to access the volatilityRatio property.
  • Add an event handler for onAfterBarClose(), which fires after all instrument-level onAfterBarClose() handlers have been called (where be presumably calculate the volatilityRatio;
  • If the bar closed was the day bar, calculate the average basket volatility by summing all the instrument volatilities and dividing by the number of instruments.