Multiple datafeed with signal column
-
Hello all,
I would like to backtest on multiple data feeds that already has a 'signal' column. Here is my custom data loader and sizer.
class CustomDataLoader(btfeeds.PandasData): lines = ("signal",) params = (("signal", -1),) datafields = btfeeds.PandasData.datafields + (["signal",]) df_btc = pd.read_csv('BTC_USDT_4h_with_signal.csv', infer_datetime_format=True, parse_dates=['datetime'], dtype={ "open" : "float", "high" : "float", "low" : "float", "close" : "float", "volume": "float", "signal": "float" }, index_col=0) df_btc.index = pd.to_datetime(df_btc.datetime, format='%Y-%m-%dT%H:%M:%S.%fZ') df_btc = df_btc[['open', 'high', 'low', 'close', 'volume', 'signal']] df_eth = pd.read_csv('ETH_USDT_4h_with_signal.csv', infer_datetime_format=True, parse_dates=['datetime'], dtype={ "open" : "float", "high" : "float", "low" : "float", "close" : "float", "volume": "float", "signal": "float" }, index_col=0) df_eth.index = pd.to_datetime(df_eth.datetime, format='%Y-%m-%dT%H:%M:%S.%fZ') df_eth = df_eth[['open', 'high', 'low', 'close', 'volume', 'signal']] bt_data_btc = CustomDataLoader(dataname=df_btc) bt_data_eth = CustomDataLoader(dataname=df_eth) class ST_sizer(bt.sizers.PercentSizer): params = (('percent', 20),) def _getsizing(self, comminfo, cash, data, isbuy): position = self.broker.getposition(data) size = self.p.percent * (1 + (position.size != 0)) return size
The strategy goal is simple: if signal =1 go long elif signal = -1 go short
class TestStrategy(bt.SignalStrategy): def log(self, txt, dt=None): ''' Logging function fot this strategy''' dt = dt or self.data.datetime[0] print('%s, %s' % (bt.num2date(dt), txt)) def __init__(self): self.orderid = None self.inds = dict() for i, d in enumerate(self.datas): self.inds[d] = dict() self.inds[d]['close'] = self.data.close self.inds[d]['signal'] = self.data.signal def next(self): for i, d in enumerate(self.datas): dt, dn = bt.num2date(self.datetime[0]), d._name print(dt, dn, self.inds[d]['signal'][0]) if self.orderid: return # if an order is active, no new orders are allowed if not self.getposition(d).size: # not yet in market if self.inds[d]['signal'][0] > 0: self.buy(data=d) elif self.inds[d]['signal'][0] < 0: self.sell(data=d) else: # in the market if self.inds[d]['signal'][0] > 0 and self.getposition(d).size < 0: self.close(data=d) self.buy(data=d) elif self.inds[d]['signal'][0] < 0 and self.getposition(d).size > 0: self.close(data=d) self.sell(data=d) def notify_trade(self, trade): if trade.justopened: print('-' * 32, ' TRADE OPENED ', '-' * 32) print('{}, {}, Price: {}, Size {}, Value {}, Commission {}'.format( bt.num2date(trade.dtopen), self.data._name, round(trade.price, 2), round(trade.size, 2), round(trade.value, 2), round(trade.commission), 6)) print('-' * 70) if trade.isclosed: print('-' * 32, ' TRADE CLOSED ', '-' * 32) print('{}, {}, Duration {} days, Price: {}, Commission {}, PNL {}, PNL net {}'.format( bt.num2date(trade.dtclose), self.data._name, round((trade.barlen)*4/24,2), round(self.data.open[0], 2), round(trade.commission,2), round(trade.pnl,2), round(trade.pnlcomm,2))) print('-' * 70)
# Create a cerebro entity cerebro = bt.Cerebro() # Add data cerebro.adddata(bt_data_btc, name='btc') cerebro.adddata(bt_data_eth, name='eth') # Add sizer cerebro.addsizer(ST_sizer) # Add a strategy cerebro.addstrategy(TestStrategy) # Add broker fees cerebro.broker.setcommission(commission=0.0004) # Set our desired cash start cerebro.broker.setcash(10000.0) # Run over everything strat = cerebro.run()
When printing logs it seems that the backtest only takes 1st datafeed (btc) signal and applies it to both feed.
Signals differs slightly from each other.
2020-07-06 16:00:00 btc -1.0 2020-07-06 16:00:00 eth -1.0 2020-07-06 20:00:00 btc -1.0 2020-07-06 20:00:00 eth -1.0 2020-07-07 00:00:00 btc -1.0 2020-07-07 00:00:00 eth -1.0 2020-07-07 04:00:00 btc -1.0 2020-07-07 04:00:00 eth -1.0 2020-07-07 08:00:00 btc -1.0 2020-07-07 08:00:00 eth -1.0 2020-07-07 12:00:00 btc -1.0 2020-07-07 12:00:00 eth -1.0 2020-07-07 16:00:00 btc -1.0 2020-07-07 16:00:00 eth -1.0 2020-07-07 20:00:00 btc -1.0 2020-07-07 20:00:00 eth -1.0 2020-07-08 00:00:00 btc -1.0 2020-07-08 00:00:00 eth -1.0 2020-07-08 04:00:00 btc -1.0 2020-07-08 04:00:00 eth -1.0 2020-07-08 08:00:00 btc -1.0 2020-07-08 08:00:00 eth -1.0 2020-07-08 12:00:00 btc -1.0 2020-07-08 12:00:00 eth -1.0 2020-07-08 16:00:00 btc -1.0 2020-07-08 16:00:00 eth -1.0 2020-07-08 20:00:00 btc -1.0 2020-07-08 20:00:00 eth -1.0 2020-07-09 00:00:00 btc -1.0 2020-07-09 00:00:00 eth -1.0 2020-07-09 04:00:00 btc -1.0 2020-07-09 04:00:00 eth -1.0 2020-07-09 08:00:00 btc -1.0 2020-07-09 08:00:00 eth -1.0 2020-07-09 12:00:00 btc -1.0 2020-07-09 12:00:00 eth -1.0 2020-07-09 16:00:00 btc -1.0 2020-07-09 16:00:00 eth -1.0 2020-07-09 20:00:00 btc -1.0 2020-07-09 20:00:00 eth -1.0 2020-07-10 00:00:00 btc -1.0 2020-07-10 00:00:00 eth -1.0 2020-07-10 04:00:00 btc -1.0 2020-07-10 04:00:00 eth -1.0 2020-07-10 08:00:00 btc -1.0 2020-07-10 08:00:00 eth -1.0 2020-07-10 12:00:00 btc -1.0 2020-07-10 12:00:00 eth -1.0 2020-07-10 16:00:00 btc -1.0 2020-07-10 16:00:00 eth -1.0 2020-07-10 20:00:00 btc -1.0 2020-07-10 20:00:00 eth -1.0 2020-07-11 00:00:00 btc -1.0 2020-07-11 00:00:00 eth -1.0 2020-07-11 04:00:00 btc -1.0 2020-07-11 04:00:00 eth -1.0 2020-07-11 08:00:00 btc -1.0 2020-07-11 08:00:00 eth -1.0 2020-07-11 12:00:00 btc -1.0 2020-07-11 12:00:00 eth -1.0 2020-07-11 16:00:00 btc -1.0 2020-07-11 16:00:00 eth -1.0 2020-07-11 20:00:00 btc -1.0 2020-07-11 20:00:00 eth -1.0 2020-07-12 00:00:00 btc -1.0 2020-07-12 00:00:00 eth -1.0 2020-07-12 04:00:00 btc -1.0 2020-07-12 04:00:00 eth -1.0 2020-07-12 08:00:00 btc -1.0 2020-07-12 08:00:00 eth -1.0 2020-07-12 12:00:00 btc -1.0 2020-07-12 12:00:00 eth -1.0 2020-07-12 16:00:00 btc -1.0 2020-07-12 16:00:00 eth -1.0 2020-07-12 20:00:00 btc -1.0 2020-07-12 20:00:00 eth -1.0 2020-07-13 00:00:00 btc -1.0 2020-07-13 00:00:00 eth -1.0 2020-07-13 04:00:00 btc -1.0 2020-07-13 04:00:00 eth -1.0 2020-07-13 08:00:00 btc -1.0 2020-07-13 08:00:00 eth -1.0 2020-07-13 12:00:00 btc -1.0 2020-07-13 12:00:00 eth -1.0 2020-07-13 16:00:00 btc -1.0 2020-07-13 16:00:00 eth -1.0 2020-07-13 20:00:00 btc -1.0 2020-07-13 20:00:00 eth -1.0 2020-07-14 00:00:00 btc -1.0 2020-07-14 00:00:00 eth -1.0 2020-07-14 04:00:00 btc -1.0 2020-07-14 04:00:00 eth -1.0 2020-07-14 08:00:00 btc -1.0 2020-07-14 08:00:00 eth -1.0 2020-07-14 12:00:00 btc -1.0 2020-07-14 12:00:00 eth -1.0 2020-07-14 16:00:00 btc -1.0 2020-07-14 16:00:00 eth -1.0 2020-07-14 20:00:00 btc -1.0 2020-07-14 20:00:00 eth -1.0 2020-07-15 00:00:00 btc -1.0 2020-07-15 00:00:00 eth -1.0 2020-07-15 04:00:00 btc -1.0 2020-07-15 04:00:00 eth -1.0 2020-07-15 08:00:00 btc -1.0 2020-07-15 08:00:00 eth -1.0 2020-07-15 12:00:00 btc -1.0 2020-07-15 12:00:00 eth -1.0 2020-07-15 16:00:00 btc -1.0 2020-07-15 16:00:00 eth -1.0 2020-07-15 20:00:00 btc -1.0 2020-07-15 20:00:00 eth -1.0 2020-07-16 00:00:00 btc -1.0 2020-07-16 00:00:00 eth -1.0 2020-07-16 04:00:00 btc -1.0 2020-07-16 04:00:00 eth -1.0 2020-07-16 08:00:00 btc -1.0 2020-07-16 08:00:00 eth -1.0 2020-07-16 12:00:00 btc -1.0 2020-07-16 12:00:00 eth -1.0 2020-07-16 16:00:00 btc -1.0 2020-07-16 16:00:00 eth -1.0 2020-07-16 20:00:00 btc -1.0 2020-07-16 20:00:00 eth -1.0 2020-07-17 00:00:00 btc -1.0 2020-07-17 00:00:00 eth -1.0 2020-07-17 04:00:00 btc -1.0 2020-07-17 04:00:00 eth -1.0 2020-07-17 08:00:00 btc -1.0 2020-07-17 08:00:00 eth -1.0 2020-07-17 12:00:00 btc -1.0 2020-07-17 12:00:00 eth -1.0 2020-07-17 16:00:00 btc -1.0 2020-07-17 16:00:00 eth -1.0 2020-07-17 20:00:00 btc -1.0 2020-07-17 20:00:00 eth -1.0 2020-07-18 00:00:00 btc -1.0 2020-07-18 00:00:00 eth -1.0 2020-07-18 04:00:00 btc -1.0 2020-07-18 04:00:00 eth -1.0 2020-07-18 08:00:00 btc -1.0 2020-07-18 08:00:00 eth -1.0 2020-07-18 12:00:00 btc -1.0 2020-07-18 12:00:00 eth -1.0 2020-07-18 16:00:00 btc -1.0 2020-07-18 16:00:00 eth -1.0 2020-07-18 20:00:00 btc -1.0 2020-07-18 20:00:00 eth -1.0 2020-07-19 00:00:00 btc -1.0 2020-07-19 00:00:00 eth -1.0 2020-07-19 04:00:00 btc -1.0 2020-07-19 04:00:00 eth -1.0 2020-07-19 08:00:00 btc -1.0 2020-07-19 08:00:00 eth -1.0 2020-07-19 12:00:00 btc -1.0 2020-07-19 12:00:00 eth -1.0 2020-07-19 16:00:00 btc -1.0 2020-07-19 16:00:00 eth -1.0 2020-07-19 20:00:00 btc -1.0 2020-07-19 20:00:00 eth -1.0 2020-07-20 00:00:00 btc -1.0 2020-07-20 00:00:00 eth -1.0 2020-07-20 04:00:00 btc -1.0 2020-07-20 04:00:00 eth -1.0 2020-07-20 08:00:00 btc -1.0 2020-07-20 08:00:00 eth -1.0 2020-07-20 12:00:00 btc -1.0 2020-07-20 12:00:00 eth -1.0 2020-07-20 16:00:00 btc -1.0 2020-07-20 16:00:00 eth -1.0 2020-07-20 20:00:00 btc -1.0 2020-07-20 20:00:00 eth -1.0 2020-07-21 00:00:00 btc -1.0 2020-07-21 00:00:00 eth -1.0 2020-07-21 04:00:00 btc -1.0 2020-07-21 04:00:00 eth -1.0 2020-07-21 08:00:00 btc -1.0 2020-07-21 08:00:00 eth -1.0 2020-07-21 12:00:00 btc -1.0 2020-07-21 12:00:00 eth -1.0 2020-07-21 16:00:00 btc -1.0 2020-07-21 16:00:00 eth -1.0 2020-07-21 20:00:00 btc -1.0 2020-07-21 20:00:00 eth -1.0 2020-07-22 00:00:00 btc -1.0 2020-07-22 00:00:00 eth -1.0 2020-07-22 04:00:00 btc -1.0 2020-07-22 04:00:00 eth -1.0 2020-07-22 08:00:00 btc -1.0 2020-07-22 08:00:00 eth -1.0 2020-07-22 12:00:00 btc -1.0 2020-07-22 12:00:00 eth -1.0 2020-07-22 16:00:00 btc -1.0 2020-07-22 16:00:00 eth -1.0 2020-07-22 20:00:00 btc -1.0 2020-07-22 20:00:00 eth -1.0 2020-07-23 00:00:00 btc 1.0 2020-07-23 00:00:00 eth 1.0 -------------------------------- TRADE CLOSED -------------------------------- 2020-07-23 04:00:00, btc, Duration 25.17 days, Price: 9517.9, Commission 14.01, PNL -975.51, PNL net -989.52 ---------------------------------------------------------------------- -------------------------------- TRADE OPENED -------------------------------- 2020-07-23 04:00:00, btc, Price: 9517.9, Size 1.89, Value 17997.34, Commission 7 ---------------------------------------------------------------------- -------------------------------- TRADE CLOSED -------------------------------- 2020-07-23 04:00:00, btc, Duration 25.17 days, Price: 9517.9, Commission 0.3, PNL -66.94, PNL net -67.24 ---------------------------------------------------------------------- -------------------------------- TRADE OPENED -------------------------------- 2020-07-23 04:00:00, btc, Price: 264.32, Size 1.53, Value 405.0, Commission 0 ---------------------------------------------------------------------- 2020-07-23 04:00:00 btc 1.0 2020-07-23 04:00:00 eth 1.0 2020-07-23 08:00:00 btc 1.0 2020-07-23 08:00:00 eth 1.0
As you can see here, 'self.data._name,' is always btc... and prices seems wrong as both trades are closed at BTC price.
For exemple, ETH data switch from short to long on the '2020-07-08 16:00:00' but the switch doesn't happen until BTC switch on the '2020-07-23 04:00:00'
I would appreciate if anyone sees where my mistake is...
Let me know if data is needed to reproduce the exemple?Thanks for your help!
-
I notice you don't set self.orderid anywhere. You should set it when creating an order:
self.orderid = self.buy(.etc)
and then you also need to clear it when an order is complete at the end of self.notify_order.
Have a look here at this full code.:
https://www.backtrader.com/docu/order-creation-execution/order-creation-execution/#the-full-code
-
Found my error...
def __init__(self): self.order = None self.inds = dict() for i, d in enumerate(self.datas): self.inds[d] = dict() self.inds[d]['close'] = d.close self.inds[d]['signal'] = d.signal
I was using
self.inds[d]['close'] = self.data.close
I added the orderid as you mentionned and it works like a charm thank you!! :-)