Navigation

    Backtrader Community

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

    Multiple Data Feeds - Only buy()'s on the first two feeds though

    Indicators/Strategies/Analyzers
    2
    4
    121
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Michael Tomlin
      Michael Tomlin last edited by

      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))
      
      
      
      run-out 1 Reply Last reply Reply Quote 0
      • run-out
        run-out @Michael Tomlin last edited by

        @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 the cash 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.

        RunBacktest.com

        Michael Tomlin 1 Reply Last reply Reply Quote 0
        • Michael Tomlin
          Michael Tomlin @run-out last edited by

          @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

          run-out 1 Reply Last reply Reply Quote 0
          • run-out
            run-out @Michael Tomlin last edited by

            @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.

            RunBacktest.com

            1 Reply Last reply Reply Quote 0
            • 1 / 1
            • First post
              Last post
            Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors