MA Crossover Strategy using LNMarketBot

Moving averages crossover is one of the simplest trend following strategy. We have two moving averages, the faster one is say 9 day average, while the slower one is 21 day average. We buy when the fast MA moves above slow MA, and sell when the slow MA moves above the fast MA. This simple strategy is coded for the LNMarketBot as:

import time
import talib
import datetime
from LNMarketBot import Strategy, BacktestBroker, CSVData, Bot

class MACrossStrat(Strategy):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def init(self):
        return

    def execute(self, datas):
        dtime = datas[1].index
        close = datas[1]["close"]
        fastsma = talib.SMA(close, timeperiod=self.params["FastPeriod"])
        slowsma = talib.SMA(close, timeperiod=self.params["SlowPeriod"])

        fastOverSlow = fastsma[-1] > slowsma[-1]

        assert(self.broker is not None)
        leverage = self.params['Leverage']
        riskAmount = round(self.broker.balance*self.params["RiskPercent"])

        if self.position <= 0 and fastOverSlow:
            self.broker.notifier.notify(
                f"{dtime[-1]} Pos: {self.position} "
                f"fastsma: {fastsma[-1]:.2f} "
                f"slowsma: {slowsma[-1]:.2f}"
                )
            quantity = riskAmount // close[-1] - self.position
            self.broker.buy(
                strategy=self,
                leverage=leverage,
                quantity=quantity,
            )

        if self.position >= 0 and not fastOverSlow:
            self.broker.notifier.notify(
                f"{dtime[-1]} Pos: {self.position} "
                f"fastsma: {fastsma[-1]:.2f} "
                f"slowsma: {slowsma[-1]:.2f}"
                )
            quantity = riskAmount // close[-1] + self.position
            self.broker.sell(
                strategy=self,
                leverage=leverage,
                quantity=quantity,
                )

    def stop(self):
        self.broker.notifier.notify(f"Final Gain: {(self.broker.balance-self.initialCapital)/self.initialCapital:.2f}")
        self.broker.notifier.notify(f"Max Drawdown {self.maxdrawdown:.2f}")
        self.broker.notifier.notify(self.broker.transactions)
        self.broker.notifier.notify(f"Time taken:{time.perf_counter()-self.startTime:.2f}")

The class MACrossStrat extends the Strategy class. It needs to provide three methods init() which runs at the start of strategy execution, execute() which runs at every new price bar, and stop() which runs when the strategy has finished. The strategy takes FastPeriod and SlowPeriod, as parameters, which control the time periods of moving averages. These parameters, among others like Leverage and RiskPercent are passed when the strategy is initialised. The following code creates the broker and Data objects and attaches them to the strategy. The strategy is then given to bot to run.

broker = BacktestBroker(1.0e06)
broker.notifier.enableStdout()

dirname = '../../'
filename = 'BTCPriceData2015.csv'
filename1 = 'BTCPriceData2015Daily.csv'
csvdata = CSVData(
    dirname+filename,
    window=datetime.timedelta(days=31),
    datetime='Unnamed: 0',
)
csvdata1 = CSVData(
    dirname+filename1,
    window=datetime.timedelta(days=31),
    datetime='Unnamed: 0',
)

strategy = MACrossStrat(
    broker=broker,
    FastPeriod=9,
    SlowPeriod=21,
    RiskPercent=0.1,
    Leverage=1,
)
strategy.addData(csvdata)
strategy.addData(csvdata1)

bot = Bot()
bot.addStrategy(strategy)
bot.run()