How to Backtest a Trading Bot with BackTrader.
Tags: python, datascience, beginners
Published: 2022-03-21 19:15:35

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

```

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()
```


![BTC data ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jx3ugipr0cbdacuamh3l.png)

---





### 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))
```


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




```
def __init__(self):
 self.dataclose = self.datas[0].close
 self.order = None
```

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
```

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()
```


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())
```



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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8kpzhdlokjjj03l7748d.png)



**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()

```


![Backtesting plot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/os8wmeqyqyvy5d05tue5.png)


### 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](https://dev.to/shadyshafik/algorithmic-trading-how-to-build-a-trading-bot-with-python-and-sqlite-4h55) but without the backteting part, and today we did.


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