I have just started working with backtrader and tried a simple strategy but I'm getting very weird results.
I'm using the addwriter
method to save the trade results to a csv file:
cerebro.addwriter(bt.WriterFile, csv=True, out="trade_history.csv")
and I'm also setting the commission to zero.
This is part of the csv results

at the 85th iteration (don't know if it's the right term, correct me please) I buy ~500$ worth of BTC at a price of ~13400$, which means I have ~0.037 BTC which is then sold at ~13760. My calculations are telling me that after selling my cash balance should be more than what I had before entering the trade because the trade is clearly profitable but it is not the case and I'm in a 3$ loss! Could someone please explain what is going on?
I have attached my script
import backtrader as bt
import backtrader.feeds as btfeed
import numpy as np
class custom_csv(btfeed.GenericCSVData):
params = (
('datetime', 0),
('time', -1),
('open', 1),
('high', 2),
('low', 3),
('close', 4),
('volume', 5),
('openinterest', -1),
('timeframe', bt.TimeFrame.Minutes),
('compression', 1),
)
from datetime import datetime
class ma_strat(bt.Strategy):
params = (
('fast_ma', 12),
('slow_ma', 60),
)
def __init__(self):
self.dataclose = self.datas[0].close
self.volume = self.datas[0].volume
self.latest_order = None
self.first_run = True
self.order_size = 0
self.trade_number = 0
self.fast_ma = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.fast_ma)
self.slow_ma = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.slow_ma)
self.fast_ma.csv = True
self.slow_ma.csv = True
def notify_order(self, order):
if order.isbuy():
order_type = 'Buy'
else:
order_type = 'Sell'
if order.status == order.Completed:
self.latest_order = None
return
elif order.status == order.Canceled:
print(order_type + ' order canceled')
self.latest_order = None
return
elif order.status == order.Margin:
current_cash_balance = self.broker.get_cash()
print(order_type + ' order cancled due to margin Error')
print('Current balance: ' + str(current_cash_balance))
# Write down: no pending order
self.latest_order = None
return
elif order.status == order.Rejected:
print(order_type + ' order rejected')
# Write down: no pending order
self.latest_order = None
return
def notify_trade(self, trade):
if trade.isopen:
self.trade_number += 1
print('Opening trade #%02d with size %.4f commision %.4f value %.2f price %.2f' %
(self.trade_number, trade.size, trade.commission, trade.value, trade.price))
elif trade.isclosed:
print('Closing trade #%02d gross %.2f, net %.2f' %
(self.trade_number, trade.pnl, trade.pnlcomm))
def next(self):
# Check if an order is pending ... if yes, we cannot send a 2nd one
if self.latest_order is not None:
return
if not self.position:
if self.fast_ma[0] > self.slow_ma[0]:
current_cash_balance = self.broker.get_cash()
self.order_size = 0.5 * current_cash_balance / float(self.dataclose[0])
# place the buy order
self.latest_order = self.buy(size=self.order_size)
else:
if self.fast_ma[0] < self.slow_ma[0]:
self.latest_order = self.sell(size=self.order_size)
if __name__ == '__main__':
# Create a cerebro entity
cerebro = bt.Cerebro()
cerebro.addwriter(bt.WriterFile, csv=True, out="trade_history.csv")
csv_data_file = 'bitfinex_btc_price.csv'
bitfinex_minut_data = custom_csv(dataname=csv_data_file)
# Add the Data Feed to Cerebro
unknown = cerebro.adddata(bitfinex_minut_data)
cerebro.addstrategy(ma_strat)
cerebro.broker.setcash(1000.0)
print('Starting Portfolio Value: %.2f USD$' % cerebro.broker.getvalue())
cerebro.run()
print('Final Portfolio Value: %.2f USD$' % cerebro.broker.getvalue())