DEV Community

shady shafik
shady shafik

Posted on • Originally published at Medium

How to Backtest a Trading Bot with BackTrader.

Hello there and welcome to another article, today I will get into one of the most important topics in algorithmic trading which is backtesting.


Backtesting is the process of testing your strategy on historical data to get a clear vision about how this strategy would performed and how it will perform in the live trading.

also, backtesting doesn’t guarantee that the strategy or the trading bot will perform as it performed on historical data for many reasons….

but still backtesting is a precious tool every algorithmic trader and traders in general need as it can transform the performance by manipulating and testing new strategies.

In this article we will develop a simple strategy and backtest it using backtrader library and I’ll use jupyter notebook.


let’s jump into it.

Import the backtrader library.

import backtrader as bt

Enter fullscreen mode Exit fullscreen mode

Download Bitcoin data from yahoo finance
starting from 2018 we’ll get the daily time frame

import yfinance as yf
data = yf.download("BTC-USD",start='2018-01-01')
data.head()
Enter fullscreen mode Exit fullscreen mode

BTC data


Now it’s time to think about the Strategy.

I will develop a Buy the dip strategy which is :

If prices drop three days in a row we buy
and sell after 2 days.

create dipStrategy class and pass strategy(a built in object inside backtrader.)


class dipStrategy(bt.Strategy):
    def log(self, txt, dt=None):

        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))
Enter fullscreen mode Exit fullscreen mode

backtrader log function *** datas[0] is the current data row.

def __init__(self):
 self.dataclose = self.datas[0].close
 self.order = None
Enter fullscreen mode Exit fullscreen mode

Keep a reference to the “close” line in the data[0] dataseries in dataclose
set order to None as there is no order yet.

def notify_order(self, order):
       if order.status in [order.Submitted, order.Accepted]:
            return
        if order.status in [order.Completed]:
            if order.isbuy():
                self.log('BUY EXECUTED,%.2f' % order.executed.price)

            elif order.issell():
                self.log('SELL EXECUTED,%.2f' %order.executed.price)

            self.bar_executed = len(self)

        elif order.status in [order.Canceled,
                                       order.Margin,order.Rejected]:
            self.log('Order Canceled/Margin/Rejected')
        self.order = None
Enter fullscreen mode Exit fullscreen mode

check order status if in submitted or accepted then return.
*** not completed yet but accepted by the broker.

len(self) will return day number

ex: if we bought at day 200 we sould keep track of that number as we’ll sell after 2 days at day 202

we save the order day in bar_executed

Check if an order has been completed

if buy order completed log the price isbuy() built in function in backtrader
if order in status in canceled… log this
if sell order completed log the price
set order to None as there is no pending order

def next(self):

        self.log('Close, %.2f' % self.dataclose[0])
        if self.order:
            return


        if not self.position :

            if self.dataclose[0] < self.dataclose[-1]:

                if self.dataclose[-1] < self.dataclose[-2]:

                    if self.dataclose[-2] < self.dataclose[-3]:

                        self.log('BUY CREATE, %.2f' % 
                                                 self.dataclose[0])
                        self.buy()

                        self.order = self.buy()


        else:

            if len(self) >= self.bar_executed + 2 :
                self.log('SELL CREATE, %.2f' % self.dataclose[0])
                self.order = self.sell()
Enter fullscreen mode Exit fullscreen mode

Simply log the closing price of the series from the reference
Check if there is pending order if yes, return
check if we have position in the market.

we’ll check if the price drops three days in a row
if today close less than yesterday till three previous days

*** [-1] is the preious day index and so on till [-3]

Now it’s time to buy as our conditions met log price
set buy order

set order to the buy order to keep track and avoid sending second order

self.buy() return buy order object


Sell conditon

after 2 days we sell our position
and we keep track by bar_executed variable
we’ve finished coding our strategy
Now it’s show time, let’s execute.

Backtesting

cerebro = bt.Cerebro()

cerebro.broker.setcash(100000.0)

cerebro.addsizer(bt.sizers.SizerFix, stake=2)
df = bt.feeds.PandasData(dataname=data)

cerebro.adddata(df)

cerebro.addstrategy(dipStrategy)

print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())

cerebro.run()

print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
Enter fullscreen mode Exit fullscreen mode

cerebro is backtrader engine we’ll create an instance

set how much cash in your account is set it to 100k

set size of your order ** how much you’ll buy i set it to 2 shares (bitcoins)

data feed in our case we have pandas data dataframe

pass data feed to cerebro with adddata() method
add our strategy to cerebro with addstrategy() method

print account cash in start

run the engine start trade

print cashe at the end


Results

we get along list with all trader executed.

Backtesting Results

We started with 100k and at the end we have 141k 🔥 👌 that’s not bad isn’t it…😁

note: of course this strategy underperformed the market but the aim is to come up with your strategy, optimize and manipulate till your reach a point where you have a strategy that can outperform the market with consistent monitoring, optimization, and idea generation.

What about visualizing our trades

cerebro.plot()

Enter fullscreen mode Exit fullscreen mode

Backtesting plot

what’s next

I think backtrader is a great tool for traders to test and manipulate their strategies

In my previous article we developed a crypto trading bot but without the backteting part, and today we did.

That’s it for this article I hope you enjoy.

Oldest comments (0)