Multiple Data Feeds - Only buy()'s on the first two feeds though
-
I'm struggling hard here. I've always used backtrader on one symbol at a time, and figured it was time to save myself some time and input multiple symbols worth of data. For now, I feed it a list of symbols, and iterates through and puts into a Pandas df. But when I iterate through in strategy.next(), it will only buy the first two symbols in symbols[]. It is getting a buy signal, because I have put a print statement to verify. Anyone have any ideas. main and strategy are posted below.
#main import backtrader as bt import multiSqueeze from alpaca_trade_api.rest import REST, TimeFrame import config import pandas as pd import quantstats api = REST(config.API_KEY, config.SECRET_KEY, base_url=config.API_URL) #*************************************** start = "2020-01-01" end = "2022-02-08" symbols=['AA','SWCH','MSFT'] strat = multiSqueeze.TTMSqueeze #*************************************** startcash = 10000 cerebro = bt.Cerebro() cerebro.addstrategy(strat) datalist=[] for i in range(len(symbols)): data=api.get_bars(symbols[i],TimeFrame.Day,start,end,adjustment='raw').df v2_data=data.drop(['trade_count','vwap'], axis=1) #DROP UNUSED COLUMNS insert = [v2_data,symbols[i]] datalist.append(insert) for i in range(len(datalist)): data = bt.feeds.PandasData(dataname=datalist[i][0]) cerebro.adddata(data, name=datalist[i][1]) cerebro.broker.setcash(startcash) strategies = cerebro.run() import pyfolio as pf cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio') portvalue = cerebro.broker.getvalue() print('Final Value: ${}'.format(round(portvalue,2))) print(" ")
strategy is next
import backtrader as bt import math class TTMSqueeze(bt.Strategy): params = (("period", 20), ("devfactor", 2), ("atr_factor",1.5), ("size", 100), ("debug", False), ("risk", 0.05), ("stop_dist", .1), ("notify", False), ('oneplot', True), ("momentum", 10)) def __init__(self): self.inds = dict() self.o = dict() # orders per data (main, stop, limit, manual-close) for i, d in enumerate(self.datas): self.inds[d] = dict() self.inds[d]['boll'] = bt.indicators.BollingerBands(d.close, period=self.p.period,devfactor=self.p.devfactor) self.inds[d]['mom'] = bt.indicators.Momentum(d.close, period=12) self.inds[d]['atr'] = bt.indicators.ATR(period=12) self.inds[d]['sma'] = bt.indicators.SimpleMovingAverage(d.close,period=20) if i > 0: #Check we are not on the first loop of data feed: if self.p.oneplot == True: d.plotinfo.plotmaster = self.datas[0] def next(self): for i, d in enumerate(self.datas): dt, dn = self.datetime.date(), d._name pos = self.getposition(d).size cash = self.broker.get_cash() stop_price = (d.close[0] * (1 - self.p.stop_dist)) qty = math.floor((cash * self.p.risk) / (d.close[0] - stop_price)) if not pos and not self.o.get(d, None): if self.inds[d]['boll'].lines.top[-1] < (self.inds[d]['sma'].sma[-1]+(self.inds[d]['atr'].lines.atr[-1]*self.p.atr_factor)) and self.inds[d]['boll'].lines.bot[-1] > (self.inds[d]['sma'].sma[-1]-(self.inds[d]['atr'].lines.atr[-1]*self.p.atr_factor)): if self.inds[d]['boll'].lines.top[0] > (self.inds[d]['sma'].sma[0]+(self.inds[d]['atr'].lines.atr[0]*self.p.atr_factor)) or self.inds[d]['boll'].lines.bot[0] < (self.inds[d]['sma'].sma[-1]-(self.inds[d]['atr'].lines.atr[0]*self.p.atr_factor)): print('Date: {} Symbol: {} Price: ${} '.format(dt,dn,round(d.close[0],2))) self.o[d] = [self.buy(data=d,size=qty)] self.sell(data=d, exectype=bt.Order.Stop,size=qty,price=stop_price,parent=self.o[d]) self.bar_executed = len(self) else: if self.inds[d]['mom'].lines.momentum[0] < self.inds[d]['mom'].lines.momentum[-1]: if self.inds[d]['mom'].lines.momentum[-1] < self.inds[d]['mom'].lines.momentum[-2]: o = self.close(data=d) self.o[d].append(o) #self.order = self.close() def notify_trade(self, trade): dt = self.data.datetime.date() if trade.isclosed: if self.p.notify: print('Date: {} Symbol: {} PnL: ${} Size: {} Days: {}'.format( dt, trade.data._name, round(trade.pnl,2), trade.size, trade.barlen))
-
@michael-tomlin said in Multiple Data Feeds - Only buy()'s on the first two feeds though:
qty = math.floor((cash * self.p.risk) / (d.close[0] - stop_price))
I'm not sure this line is doing what you want. You are taking your
cash * your risk
, so in this case.05
of thecash value
, which is fine.But then you divide by
close - stop_price
, which is a small number in the denominator, resulting in a large quantity. Your first couple of purchases likely max out your cash due to this. -
@run-out
Thanks for your input. That was definitely a mistake, but the program is still doing the same. I added in the following to test out if symbol[] of index => 2 will work. It does not.if not pos and i==2
def next(self): for i, d in enumerate(self.datas): dt, dn = self.datetime.date(), d._name pos = self.getposition(d).size cash = self.broker.get_cash() stop_price = (d.close[0] * (1 - self.p.stop_dist)) qty = math.floor((cash * self.p.risk) / d.close[0]) if not pos and i==1: if self.inds[d]['boll'].lines.top[-1] < (self.inds[d]['sma'].sma[-1]+(self.inds[d]['atr'].lines.atr[-1]*self.p.atr_factor)) and self.inds[d]['boll'].lines.bot[-1] > (self.inds[d]['sma'].sma[-1]-(self.inds[d]['atr'].lines.atr[-1]*self.p.atr_factor)): if self.inds[d]['boll'].lines.top[0] > (self.inds[d]['sma'].sma[0]+(self.inds[d]['atr'].lines.atr[0]*self.p.atr_factor)) or self.inds[d]['boll'].lines.bot[0] < (self.inds[d]['sma'].sma[-1]-(self.inds[d]['atr'].lines.atr[0]*self.p.atr_factor)): print('Date: {} Symbol: {} Price: ${} Qty: {} '.format(dt,dn,round(d.close[0],2),qty)) self.o[d] = [self.buy(data=d,size=qty)] self.sell(data=d, exectype=bt.Order.Stop,size=qty,price=stop_price,parent=self.o[d]) self.bar_executed = len(self) else: if self.inds[d]['mom'].lines.momentum[0] < self.inds[d]['mom'].lines.momentum[-1]: if self.inds[d]['mom'].lines.momentum[-1] < self.inds[d]['mom'].lines.momentum[-2]: #o = self.close(data=d) #self.o[d].append(o) self.order = self.close(data=d)
if i==0 or i==1, it outputs correctly. If i==2, it just prints out the final portfolio value. Any ideas? Thank you for the help btw
-
@michael-tomlin I would try this before anything, comment out all of your code in
next
, and try to just log/print or debug the datas list to ensure you have more than to datas in there.for i in range(len(datas)): print(i, self.datas[i].close[0])
This will tell you for sure you if you have more than two datas. If you do indeed have more, please show all of your code so I can help debug without guessing the other parts. Thanks.