Runtime Model
A Botmain strategy consists of two user-implemented Typescript classes, InstrumentProcessor and PortfolioProcessor, extending and , 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
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
- Create a
InstrumentProcessorclass extendingInstrumentProcessorBase - Inject an
OrderProcessor - Inject any desired
Indicators - Implement a handler for one or more
Events - Handle events, and send orders
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, has a reference to all the instances:
export class PortfolioProcessorBase {
protected instrumentProcessors: InstrumentProcessorBase [];
}
Likewise, internally, the has a reference to the parent instance:
export class InstrumentProcessorBase {
protected portfolioProcessors: PortfolioProcessorBase ;
}
To handle portfolio-level events, users can implement a PortfolioProcessor class extending 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
PortfolioProcessorclass extendingPortfolioProcessorBase - Override the
instrumentProcessorhow sproperty with our ownInstrumentProcessor[]type in order to be able to access thevolatilityRatioproperty. - Add an event handler for
onAfterBarClose(), which fires after all instrument-levelonAfterBarClose()handlers have been called (where be presumably calculate thevolatilityRatio; - 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.