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
InstrumentProcessor
class 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
PortfolioProcessor
class extendingPortfolioProcessorBase - Override the
instrumentProcessorhow s
property with our ownInstrumentProcessor[]
type in order to be able to access thevolatilityRatio
property. - 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.