Navigation

    Backtrader Community

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    1. Home
    2. cnimativ
    3. Posts
    For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/
    C
    • Profile
    • Following 0
    • Followers 0
    • Topics 9
    • Posts 36
    • Best 2
    • Groups 0

    Posts made by cnimativ

    • More Backtrader way to calculate covariance matrix

      I am trying to calculate the covariance matrix of a multiple asset portfolio at every period with N lookback period. My current implementation uses a counter to wait out the lookback period and then runs all calculations in the def next() method.

       def next(self):
              if (self.counter < self.p.rperiod):
                  self.counter += 1
              else:
                  # stack the closes together into a numpy array for ease of calculation
                  closes = np.stack(([d.close.get(0,self.p.rperiod).tolist() for d in self.datas]))
                  rets = np.diff(np.log(closes))
                  # covariance matrix. will be shoved somewhere else for records
                  cov = np.cov(rets)
      

      While it 'works', what is the more backtrader way of doing the same thing?

      For example, there's the PctChange indicator for calculating percentage change that is defined at __init__.

      def __init__(self):
          rets = [bt.ind.PctChange(d, period=self.p.rperiod) for d in self.datas]
      

      But how do I access the value of N periods of indicators, run calculations, and then construct cross-asset indicators? And would it be possible to have, say, an indicator that contains multidimensional matrices?

      Much thanks!

      posted in Indicators/Strategies/Analyzers
      C
      cnimativ
    • RE: Unexpected additional orders created/rejected

      @backtrader
      ? I am already placing trade w/ price=self.data.open[0] and cheat_on_open. Backtrader will reject those orders internally due to reasons outlined above.

      On another thought, how do we place orders on margin? Adding those margin cash will be another way to solve this issue. Does backtrader keep track of gross exposure vs net exposure/unlevered? I can tolerate trading on a few points of margin to get fill in both live/simulation but need a wy to track gross/net exposure.

      posted in General Code/Help
      C
      cnimativ
    • RE: Unexpected additional orders created/rejected

      @backtrader
      Again, order_target_percent does not work properly. It is always calculating position target value with the wrong basis using the previous close, regardless if you look-ahead or using the new cheat_on_open.

      If you look at broker.py line 365 def _get_value(), its very clear that the portfolio values is only calculated at close. order_target_percent thenh calculates account/position value with the current close (see line 1301, strategy.py, target *= self.broker.getvalue()).

      What should've happened, for cheat_on_open, is that broker.get_value() should return the value of the account at the current open for calculating sizing but instead it uses previous close for sizing.

      It is not obvious if you trade only a single security at 10% sizing but errors will show up all over the place if you trade more than one security with more than 95% of the portfolio invested.

      The intellectually lazy way to bypass this bug is to never allocate over 90-95% of the whole portfolio to prevent this problem from showing up. The downside of salvaging backtesting this way is that it is difficult to verify backtrader output verified against Excel or simple vectorized backtests.

      posted in General Code/Help
      C
      cnimativ
    • RE: Unexpected additional orders created/rejected

      @backtrader

      Got it. Back to spaghetti code try except lookahead.

      In your mind, what would be the best way to structure the code for live trading for 100% invested allocation purposes, aside from leaving 1-1.5% cash to make sure everyhing fills?

      posted in General Code/Help
      C
      cnimativ
    • RE: Unexpected additional orders created/rejected

      @backtrader

      You are still using the close price for the calculation of the stake, which may be significantly different of the open. If you don't specifically specify the opening price it will not be used for the calculation. The calculation of the percent doesn't know you are cheating and are able to act before the broker

      So in other words, cheat_on_open doesn't really cheat on open for open_target_percent?

      posted in General Code/Help
      C
      cnimativ
    • RE: Unexpected additional orders created/rejected

      Here's the log if I print out the orders when they got canceled/margin/rejected.

      On 2006-07-18, it bought 100 shares of SPY at $99.04, for $9904, with $96 cash left.

      On 2007-07-26, it sold 100 shares of SPY at $122.45, account balance should be $12,245 + prior cash of $96, and broker.getcash() reports $12,341. So selling w/ order_target_percent had no problem to sell at open with cheat_on_open.

      But then the problem came. It turns around and buys 171 shares of AGG for $72.45 for $12,388.95, which is greater than $12,341 account value and got rejected. Thus the rejection. Which means, order_target_percent with cheat_on_open does not work for allocation trades.

      Two bugs here:

      1. Since the notify_order logged buying AGG before selling SPY, It could be that if we used order_target_percent sizer, the trade ordering will be mixed up and backtrader ends up placing buy before sell.

      2. On the other hand, the order_target_percent sizer also did not size AGG properly if we look at the account values alone. It seemed to be using prior close of SPY ($123.61) to calculate account value. Previous close is $123.61 x 100 shares + $91 cash = $12452 account value > $12388.95, which can buy at max 171 share of AGG at $72.45. But in reality, backtrader order_target_percent sizer with cheat_on_open needs to be and should only be calculating account size using Open only. Never previous close.

      2006-07-18, VIX: 17.74, VIX_VXV:1.03 SPY BUY EXECUTED, 100 shares at $99.04
      2007-07-26, VIX: 20.74, VIX_VXV:1.09 AGG Order Canceled/Margin/Rejected, 171 shares
      12341.0
      123.61
      Ref: 13711
      OrdType: 0
      OrdType: Buy
      Status: 7
      Status: Margin
      Size: 171
      Price: 72.47
      Price Limit: None
      TrailAmount: None
      TrailPercent: None
      ExecType: 0
      ExecType: Market
      CommInfo: None
      End of Session: 732883.9999999999
      Info: AutoOrderedDict()
      Broker: None
      Alive: False
      2007-07-26, VIX: 20.74, VIX_VXV:1.09 SPY SELL EXECUTED, -100 shares at 122.45
      2007-09-18, VIX: 20.35, VIX_VXV:0.99 SPY BUY EXECUTED, 99 shares at $121.34
      2007-10-22, VIX: 21.64, VIX_VXV:1.01 SPY SELL EXECUTED, -99 shares at 121.94
      2007-10-22, VIX: 21.64, VIX_VXV:1.01 AGG Order Canceled/Margin/Rejected, 166 shares
      12400.4
      Ref: 13714
      OrdType: 0
      OrdType: Buy
      Status: 7
      Status: Margin
      Size: 166
      Price: 74.8
      Price Limit: None
      TrailAmount: None
      TrailPercent: None
      ExecType: 0
      ExecType: Market
      CommInfo: None
      End of Session: 732971.9999999999
      Info: AutoOrderedDict()
      Broker: None
      Alive: False
      2007-10-23, VIX: 20.41, VIX_VXV:0.97 SPY BUY EXECUTED, 99 shares at $124.07
      2007-11-01, VIX: 23.21, VIX_VXV:1.03 AGG Order Canceled/Margin/Rejected, 168 shares
      12548.9
      Ref: 13717
      OrdType: 0
      OrdType: Buy
      Status: 7
      Status: Margin
      Size: 168
      Price: 74.92
      Price Limit: None
      TrailAmount: None
      TrailPercent: None
      ExecType: 0
      ExecType: Market
      CommInfo: None
      End of Session: 732981.9999999999
      Info: AutoOrderedDict()
      Broker: None
      Alive: False
      2007-11-01, VIX: 23.21, VIX_VXV:1.03 SPY SELL EXECUTED, -99 shares at 125.57
      2007-11-02, VIX: 23.01, VIX_VXV:0.99 SPY BUY EXECUTED, 101 shares at $124.13
      2007-11-05, VIX: 24.31, VIX_VXV:1.01 AGG Order Canceled/Margin/Rejected, 167 shares
      12392.35
      Ref: 13720
      OrdType: 0
      OrdType: Buy
      Status: 7
      Status: Margin
      Size: 167
      Price: 74.93
      Price Limit: None
      TrailAmount: None
      TrailPercent: None
      ExecType: 0
      ExecType: Market
      CommInfo: None
      End of Session: 732985.9999999999
      Info: AutoOrderedDict()
      Broker: None
      Alive: False
      2007-11-05, VIX: 24.31, VIX_VXV:1.01 SPY SELL EXECUTED, -101 shares at 122.58
      2007-11-06, VIX: 21.39, VIX_VXV:0.96 SPY BUY EXECUTED, 99 shares at $123.58
      2007-11-07, VIX: 26.49, VIX_VXV:1.05 AGG Order Canceled/Margin/Rejected, 166 shares
      12358.69
      Ref: 13723
      OrdType: 0
      OrdType: Buy
      Status: 7
      Status: Margin
      Size: 166
      Price: 74.85
      Price Limit: None
      TrailAmount: None
      TrailPercent: None
      ExecType: 0
      ExecType: Market
      CommInfo: None
      End of Session: 732987.9999999999
      Info: AutoOrderedDict()
      Broker: None
      Alive: False
      2007-11-07, VIX: 26.49, VIX_VXV:1.05 SPY SELL EXECUTED, -99 shares at 123.24
      2007-11-13, VIX: 24.10, VIX_VXV:0.98 SPY BUY EXECUTED, 101 shares at $119.08
      2007-11-14, VIX: 25.94, VIX_VXV:1.01 SPY SELL EXECUTED, -101 shares at 122.24
      

      Here's the code that produces these two bugs:

      import backtrader as bt
      import backtrader.feeds as btfeeds
      from datetime import datetime
      import pandas_datareader.data as web
      import matplotlib.pylab as pylab
      pylab.rcParams['figure.figsize'] = 20, 10  # that's default image size for this interactive session
      pylab.rcParams["font.size"] = "20"
      
      class vix_ratio(bt.Strategy):
      
          def log(self, txt):
              print("%s, VIX: %.2f, VIX_VXV:%.2f %s" % (self.vix.datetime.date(0).isoformat(), self.vix.close[0], self.vix_ratio, txt))       
                    
          def __init__(self):
              self.spy = self.data0
              self.agg = self.data1
              self.vix = self.data2
              self.vxv = self.data3
              self.vix_ratio = 0
              self.flag = True
          
          def notify_order(self, order):
              if order.status in [order.Submitted, order.Accepted]:
                  # Buy/Sell order submitted/accepted to/by broker - Nothing to do
                  return
              
              # Check if an order has been completed
              # Attention: broker could reject order if not enougth cash
              if order.status in [order.Completed]:
                  if order.isbuy():
                      self.log(order.data._name + ' BUY EXECUTED, %.0f shares at $%.2f' % (order.size, order.executed.price))
                  elif order.issell():
                      self.log(order.data._name + ' SELL EXECUTED, %.0f shares at %.2f' % (order.size, order.executed.price))
      
                  self.bar_executed = len(self)
      
              elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                  self.log(order.data._name + ' Order Canceled/Margin/Rejected, %.0f shares' % (order.size))#, order.price))
                  print(self.broker.getcash())
                  print(self.spy[-1])
                  print(order)
              # Write down: no pending order
              self.order = None
              
          def next_open(self): # Using cheat-on-open
              self.vix_ratio = self.vix.close[0]/self.vxv.close[0]
              
              if (self.vix_ratio <= 1 or self.vix.close[0] <= 20) and self.flag:
                  self.flag = False
                  self.order = self.order_target_percent(data=self.agg, target=0)
                  self.order = self.order_target_percent(data=self.spy, target=1)
              elif (self.vix_ratio > 1 and self.vix.close[0] > 20) and not self.flag:
                  self.flag = True
                  self.order = self.order_target_percent(data=self.spy, target=0)
                  self.order = self.order_target_percent(data=self.agg, target=1)
                  
      def run_strat():
          start = datetime(2006,7,17)
          end = datetime(2017,4,21)   
          
          spy = btfeeds.YahooFinanceData(dataname="SPY", fromdate=start, todate=end)
          agg = btfeeds.YahooFinanceData(dataname="AGG", fromdate=start, todate=end)
          vix = btfeeds.YahooFinanceData(dataname="^VIX", fromdate=start, todate=end)
          vxv = btfeeds.YahooFinanceData(dataname="^VXV", fromdate=start, todate=end)
          #vix = bt.feeds.PandasData(dataname=web.DataReader("^VIX", 'yahoo', start, end), name='VIX')
          #vxv = bt.feeds.PandasData(dataname=web.DataReader("^VXV", 'yahoo', start, end), name='VXV')
          
          cerebro = bt.Cerebro(cheat_on_open=True)
          
          cerebro.adddata(spy, name="SPY")
          cerebro.adddata(agg, name="AGG")
          cerebro.adddata(vix, name="VIX")
          cerebro.adddata(vxv, name="VXV")
          
          cerebro.addstrategy(vix_ratio)
          cerebro.broker.setcash(10000.0)
          #cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio')
          
          results = cerebro.run()
              
          cerebro.plot()
          """strat = results[0]
          pyfoliozer = strat.analyzers.getbyname('pyfolio')
          returns, positions, transactions, gross_lev = pyfoliozer.get_pf_items()
      
          return returns, positions, transactions
          """
      if __name__ == '__main__':
          #r, p, t = run_strat()
          run_strat()
      
      posted in General Code/Help
      C
      cnimativ
    • RE: Unexpected additional orders created/rejected

      @backtrader
      Okay. I changed the code to using the new cheat_on_open param and loaded the data using backtrader internal btfeeds. With the new cheat_on_open combined with order_target_percent, the model shouldn't be out of cash. And w/ import backtrader.feeds as btfeeds, it should take care of data alignment problem. I've also made sure I place order_target_percent(target = 0) ahead of order_target_percent(target = 1) so I never buy before I sell.

      With these above feature combined, I shouldn't be getting get order rejected/margin/etc errors. But I am still getting those, big time, throwing the whole backtest result off.

      But I am still getting a whole lot of errors on trade log errors. Here are the first several lines:

      2006-07-18, VIX: 17.74, VIX_VXV:1.03 SPY BUY EXECUTED, 100 shares at $99.04
      2007-07-26, VIX: 20.74, VIX_VXV:1.09 AGG Order Canceled/Margin/Rejected, 171 shares
      2007-07-26, VIX: 20.74, VIX_VXV:1.09 SPY SELL EXECUTED, -100 shares at 122.45
      2007-09-18, VIX: 20.35, VIX_VXV:0.99 SPY BUY EXECUTED, 99 shares at $121.34
      2007-10-22, VIX: 21.64, VIX_VXV:1.01 SPY SELL EXECUTED, -99 shares at 121.94
      2007-10-22, VIX: 21.64, VIX_VXV:1.01 AGG Order Canceled/Margin/Rejected, 166 shares
      2007-10-23, VIX: 20.41, VIX_VXV:0.97 SPY BUY EXECUTED, 99 shares at $124.07
      2007-11-01, VIX: 23.21, VIX_VXV:1.03 AGG Order Canceled/Margin/Rejected, 168 shares
      2007-11-01, VIX: 23.21, VIX_VXV:1.03 SPY SELL EXECUTED, -99 shares at 125.57
      2007-11-02, VIX: 23.01, VIX_VXV:0.99 SPY BUY EXECUTED, 101 shares at $124.13
      2007-11-05, VIX: 24.31, VIX_VXV:1.01 AGG Order Canceled/Margin/Rejected, 167 shares
      2007-11-05, VIX: 24.31, VIX_VXV:1.01 SPY SELL EXECUTED, -101 shares at 122.58
      2007-11-06, VIX: 21.39, VIX_VXV:0.96 SPY BUY EXECUTED, 99 shares at $123.58
      2007-11-07, VIX: 26.49, VIX_VXV:1.05 AGG Order Canceled/Margin/Rejected, 166 shares
      

      The full code is as follows, run on Jupyter Notebook.

      import backtrader as bt
      import backtrader.feeds as btfeeds
      from datetime import datetime
      import pandas_datareader.data as web
      import matplotlib.pylab as pylab
      pylab.rcParams['figure.figsize'] = 20, 10  # that's default image size for this interactive session
      pylab.rcParams["font.size"] = "20"
      
      class vix_ratio(bt.Strategy):
      
          def log(self, txt):
              print("%s, VIX: %.2f, VIX_VXV:%.2f %s" % (self.vix.datetime.date(0).isoformat(), self.vix.close[0], self.vix_ratio, txt))       
                    
          def __init__(self):
              self.spy = self.data0
              self.agg = self.data1
              self.vix = self.data2
              self.vxv = self.data3
              self.vix_ratio = 0
              self.flag = True
          
          def notify_order(self, order):
              if order.status in [order.Submitted, order.Accepted]:
                  # Buy/Sell order submitted/accepted to/by broker - Nothing to do
                  return
              
              # Check if an order has been completed
              # Attention: broker could reject order if not enougth cash
              if order.status in [order.Completed]:
                  if order.isbuy():
                      self.log(order.data._name + ' BUY EXECUTED, %.0f shares at $%.2f' % (order.size, order.executed.price))
                  elif order.issell():
                      self.log(order.data._name + ' SELL EXECUTED, %.0f shares at %.2f' % (order.size, order.executed.price))
      
                  self.bar_executed = len(self)
      
              elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                  self.log(order.data._name + ' Order Canceled/Margin/Rejected, %.0f shares' % (order.size))
              # Write down: no pending order
              self.order = None
              
          def next_open(self): # Using cheat-on-open
              self.vix_ratio = self.vix.close[0]/self.vxv.close[0]
              
              if (self.vix_ratio <= 1 or self.vix.close[0] <= 20) and self.flag:
                  self.flag = False
                  self.order = self.order_target_percent(data=self.agg, target=0)
                  self.order = self.order_target_percent(data=self.spy, target=1)
              elif (self.vix_ratio > 1 and self.vix.close[0] > 20) and not self.flag:
                  self.flag = True
                  self.order = self.order_target_percent(data=self.spy, target=0)
                  self.order = self.order_target_percent(data=self.agg, target=1)
                  
      def run_strat():
          start = datetime(2006,7,17)
          end = datetime(2017,4,21)   
          
          spy = btfeeds.YahooFinanceData(dataname="SPY", fromdate=start, todate=end)
          agg = btfeeds.YahooFinanceData(dataname="AGG", fromdate=start, todate=end)
          vix = btfeeds.YahooFinanceData(dataname="^VIX", fromdate=start, todate=end)
          vxv = btfeeds.YahooFinanceData(dataname="^VXV", fromdate=start, todate=end)
          
          cerebro = bt.Cerebro(cheat_on_open=True)
          
          cerebro.adddata(spy, name="SPY")
          cerebro.adddata(agg, name="AGG")
          cerebro.adddata(vix, name="VIX")
          cerebro.adddata(vxv, name="VXV")
          
          cerebro.addstrategy(vix_ratio)
          cerebro.broker.setcash(10000.0)
          
          results = cerebro.run()        
          cerebro.plot()
      
      if __name__ == '__main__':
          run_strat()
      
      posted in General Code/Help
      C
      cnimativ
    • RE: order_target_percent calculation problems.

      @backtrader Very nice! One step closer to the quality of R's Quantstrat package. Will test it soon

      posted in General Code/Help
      C
      cnimativ
    • Unexpected additional orders created/rejected

      Hi All,

      Trying to port a simple volatility based strategy from Excel to Backtrader and I ran into some very unexpected behaviors regarding order creation/execution. Specifically, sometimes the orders are created twice in a given day with the first one executed and the second order cancelled/rejected?

      Here's the log output:

      2006-07-18, VIX: 17.74, VIX_VXV:1.03 SPY Order Canceled/Margin/Rejected, 96 shares
      Ref: 17069
      OrdType: 0
      OrdType: Buy
      Status: 7
      Status: Margin
      Size: 96
      Price: 98.75
      Price Limit: None
      TrailAmount: None
      TrailPercent: None
      ExecType: 0
      ExecType: Market
      CommInfo: None
      End of Session: 732509.9999999999
      Info: AutoOrderedDict()
      Broker: None
      Alive: False
      2007-05-17, VIX: 13.51, VIX_VXV:0.95 SPY SELL EXECUTED, -1 shares at 122.89
      2007-05-17, VIX: 13.51, VIX_VXV:0.95 SPY SELL EXECUTED, -1 shares at 122.89
      

      Here's the code

      
      class vix_ratio(bt.Strategy):
      
          def log(self, txt):
              print("%s, VIX: %.2f, VIX_VXV:%.2f %s" % (self.vix.datetime.date(0).isoformat(), self.vix.close[0], self.vix_ratio, txt))       
                    
          def __init__(self):
              self.vix_ratio = 0
              
              self.spy = self.data0
              self.agg = self.data1
              self.vix = self.data2
              self.vxv = self.data3
              return None
          
          def notify_order(self, order):
              if order.status in [order.Submitted, order.Accepted]:
                  # Buy/Sell order submitted/accepted to/by broker - Nothing to do
                  return
              
              # Check if an order has been completed
              # Attention: broker could reject order if not enougth cash
              if order.status in [order.Completed]:
                  if order.isbuy():
                      self.log(order.data._name + ' BUY EXECUTED, %.0f shares at $%.2f' % (order.size, order.executed.price))
                  elif order.issell():
                      self.log(order.data._name + ' SELL EXECUTED, %.0f shares at %.2f' % (order.size, order.executed.price))
      
                  self.bar_executed = len(self)
      
              elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                  self.log(order.data._name + ' Order Canceled/Margin/Rejected, %.0f shares' % (order.size))#, order.price))
                  print(order)
              # Write down: no pending order
              self.order = None
              
          def next(self):
              #print(self.sma1[0], self.sma2[0])
              self.vix_ratio = self.vix.close[0]/self.vxv.close[0]
              
              if self.vix_ratio > 1 and self.vix.close[0] > 20:
                  self.order = self.order_target_percent(data=self.spy, target=0)
                  self.order = self.order_target_percent(data=self.agg, target=0.99)
              else: 
                  self.order = self.order_target_percent(data=self.agg, target=0)
                  self.order = self.order_target_percent(data=self.spy, target=0.99)
                      
      def run_strat():
          start = datetime(2006,7,17)
          end = datetime(2017,4,21)   
          
          spy = btfeeds.YahooFinanceData(dataname="SPY", fromdate=start, todate=end)
          agg = btfeeds.YahooFinanceData(dataname="AGG", fromdate=start, todate=end)
          vix = bt.feeds.PandasData(dataname=web.DataReader("^VIX", 'yahoo', start, end), name='VIX')
          vxv = bt.feeds.PandasData(dataname=web.DataReader("^VXV", 'yahoo', start, end), name='VXV')
          
          cerebro = bt.Cerebro(runonce=False)
          
          cerebro.adddata(spy, name="SPY")
          cerebro.adddata(agg, name="AGG")
          cerebro.adddata(vix, name="VIX")
          cerebro.adddata(vxv, name="VXV")
          
          cerebro.addstrategy(vix_ratio)
          cerebro.broker.setcash(10000.0)
          cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio')
          
          results = cerebro.run()
      
      posted in General Code/Help
      C
      cnimativ
    • RE: Multi-asset ranking and rebalancing

      @ab_trader @backtrader Thanks! I will try that.

      posted in General Discussion
      C
      cnimativ
    • RE: How to buy maximum stakes?

      I think your trades are not getting filled. I assume you created those orders with data.open[1]. Print out the number of shares in the created order ticket will help you find out what's going on.

      Not sure how we can make guarantee fill w/ market order at open w/ Backtrader.

      posted in General Code/Help
      C
      cnimativ
    • RE: Multi-asset ranking and rebalancing

      @backtrader Thank you for the tip. I think the underlying design concept of a 'trading idea' is true for technical indicators but is not applicable to other quantitative strategies.

      How about for backtesting securities with long date ranges where Adj Close is materially different than Close? For example, At the end of the series, Adj Close == Close, but at the beginning of the series, Close is much higher than Adj Close for companies offering dividends.

      Correct me if I am wrong, Backtrader simulates using unadjusted Open and Close data, not Adj Close (or adjusted open when trading at the open), so the long-term results will differ materially. I am having a hard time reconciling between Excel and Backtrader, where my Excel is the ground Truth for simple strategies.

      Maybe I should backwardly adjust my pricing data before feeding it into Backtrader.

      posted in General Discussion
      C
      cnimativ
    • RE: Multi-asset ranking and rebalancing

      @backtrader Is there a way reference orders w/ tickers? Right now it seems that the orders are placed based on data series, not by name of the ticker/security.

      It is very hard to figure out the fill and share count for each ticker especially during rebalancing.

      posted in General Discussion
      C
      cnimativ
    • RE: Get name of data inside the indicator

      @backtrader

      I do not understand how does that work.

      Say, I have added 2 security OHLCV data and 4 other data of passed into cerebros by cerebro.adddata(), how do I assign/construct signals from those 4 data to trade on the 2 securities?

      Right now I am just manually coding the strategy w/o using SignalStrategy or indicators and I am still having a hard time keeping track of everything especially orders since I can't seem to find the ticker name from the order object.

      posted in Indicators/Strategies/Analyzers
      C
      cnimativ
    • RE: Problem with BT Signal and Sizer

      @backtrader Good catch! I will find a better name for it. I think for the frequency and nature of the strategy, it works perfectly fine, especially when dividend/reinvestments are taken into account in these slow moving strategies.

      posted in General Code/Help
      C
      cnimativ
    • Pyfolio integration in backtrader 1.9.36

      It seems there been a change to pyfolio integration in Backtrader 1.9.36 that's not yet updated in the documentation.

      I was able to put together an analyzer for pyfolio using similar code to the pyfolio integration doc:

          ...
          results = cerebro.run()
              
          strat = results[0]
          pyfoliozer = strat.analyzers.getbyname('pyfolio')
          returns, positions, transactions, gross_lev = pyfoliozer.get_pf_items()
          
          return returns, positions, transactions
      if __name__ == '__main__':
          r, p, t = runstrat()
      

      But after updating to 1.9.36, the above code throws the following error:

      ---------------------------------------------------------------------------
      ValueError                                Traceback (most recent call last)
      <ipython-input-24-01fb6dde8296> in <module>()
           72 
           73 if __name__ == '__main__':
      ---> 74     r, p, t = run_strat()
           75     #run_strat()
      
      <ipython-input-24-01fb6dde8296> in run_strat()
           66     cerebro.plot()
           67     strat = results[0]
      ---> 68     pyfoliozer = strat.analyzers.getbyname('pyfolio')
           69     returns, positions, transactions, gross_lev = pyfoliozer.get_pf_items()
           70 
      
      C:\Anaconda3\envs\backtrader\lib\site-packages\backtrader\metabase.py in getbyname(self, name)
          322 
          323     def getbyname(self, name):
      --> 324         idx = self._names.index(name)
          325         return self._items[idx]
      
      ValueError: 'pyfolio' is not in list
      

      I noticed that pyfolio.py has been added to the analyzers directory. So I made some change to the code using bt.analyzers.

          cerebro.addanalyzer(bt.analyzers.pyfolio, _name='pyfolio')
          
          results = cerebro.run()
              
          cerebro.plot()
          strat = results[0]
          returns, positions, transactions, gross_lev = strat.analyzers.pyfolio.get_pf_items()
          
          return returns, positions, transactions
      
      if __name__ == '__main__':
          r, p, t = run_strat()
      

      However, the modifed code throws me the following error. It seems that backtrader is having trouble adding bt.analyzers.pyfolio to the list of analyzers? What's the right way to add pyfolio analyzer?

      ---------------------------------------------------------------------------
      TypeError                                 Traceback (most recent call last)
      <ipython-input-4-96f6582b3ef5> in <module>()
           70 
           71 if __name__ == '__main__':
      ---> 72     r, p, t = run_strat()
           73     #run_strat()
      
      <ipython-input-4-96f6582b3ef5> in run_strat()
           61     cerebro.addanalyzer(bt.analyzers.pyfolio, _name='pyfolio')
           62 
      ---> 63     results = cerebro.run()
           64 
           65     cerebro.plot()
      
      C:\Anaconda3\envs\backtrader\lib\site-packages\backtrader\cerebro.py in run(self, **kwargs)
          792             # let's skip process "spawning"
          793             for iterstrat in iterstrats:
      --> 794                 runstrat = self.runstrategies(iterstrat)
          795                 self.runstrats.append(runstrat)
          796         else:
      
      C:\Anaconda3\envs\backtrader\lib\site-packages\backtrader\cerebro.py in runstrategies(self, iterstrat, predata)
          894 
          895                 for ancls, anargs, ankwargs in self.analyzers:
      --> 896                     strat._addanalyzer(ancls, *anargs, **ankwargs)
          897 
          898                 sizer, sargs, skwargs = self.sizers.get(idx, defaultsizer)
      
      C:\Anaconda3\envs\backtrader\lib\site-packages\backtrader\strategy.py in _addanalyzer(self, ancls, *anargs, **ankwargs)
          220         nsuffix = next(self._alnames[anname])
          221         anname += str(nsuffix or '')  # 0 (first instance) gets no suffix
      --> 222         analyzer = ancls(*anargs, **ankwargs)
          223         self.analyzers.append(analyzer, anname)
          224 
      
      TypeError: 'module' object is not callable
      
      posted in General Code/Help
      C
      cnimativ
    • RE: Problem with BT Signal and Sizer

      Hacked together a solution. Here's my code. It's probably not the best way to use Backtrader. Instead of using self.signal_add(bt.SIGNAL_LONG, bt.ind.CrossOver(sma1, sma2)), I pulled out sma1 and sma2 and use them directly in next. Also, I've peaked ahead for next day open price to order_target_percent as I would've done in a manual rebalacning process and used try except to catch errors.

      Please advise if there's a more 'backtrader' way of implementing the same thing.

      import backtrader as bt
      from datetime import datetime
      import pandas_datareader.data as web
      import matplotlib.pylab as pylab
      pylab.rcParams['figure.figsize'] = 30, 20  # that's default image size for this interactive session
      pylab.rcParams["font.size"] = "100"
      
      class SmaCross2(bt.SignalStrategy): 
          params = (('pfast', 50), ('pslow', 200),)
          def __init__(self):
              self.sma1 = bt.ind.SMA(self.data0, period=self.p.pfast)
              self.sma2 = bt.ind.SMA(self.data0, period=self.p.pslow)
              
          def next(self):
              if self.sma1[0] >= self.sma2[0]: # SPY bull cross
                  try:
                      self.order = self.order_target_percent(data=self.data0,
                                                              target=1,
                                                              exectype=bt.Order.Limit,
                                                              price=self.data0.open[1])
                      self.order = self.order_target_percent(data=self.data1,
                                                              target=0,
                                                              exectype=bt.Order.Limit,
                                                              price=self.data1.open[1])
                  except IndexError:
                      self.order = self.order_target_percent(data=self.data0,
                                                             target=1)
                      self.order = self.order_target_percent(data=self.data1,
                                                             target=0)
              else:
                  try:
                      self.order = self.order_target_percent(data=self.data0,
                                                              target=0,
                                                              exectype=bt.Order.Limit,
                                                              price=self.data0.open[1])
                      self.order = self.order_target_percent(data=self.data1,
                                                              target=1,
                                                              exectype=bt.Order.Limit,
                                                              price=self.data1.open[1])
                  except IndexError:
                      self.order = self.order_target_percent(data=self.data0,
                                                             target=0)
                      self.order = self.order_target_percent(data=self.data1,
                                                             target=1)
      
      def run_strat():
          start = datetime(2002,7,30)
          end = datetime(2017,1,2)
          spy = bt.feeds.PandasData(dataname=web.DataReader("SPY", 'yahoo', start, end), name='SPY')
          tlt = bt.feeds.PandasData(dataname=web.DataReader("TLT", 'yahoo', start, end), name='TLT')
          
          cerebro = bt.Cerebro()
          cerebro.adddata(spy)
          cerebro.adddata(tlt)
          cerebro.addstrategy(SmaCross2)
          cerebro.broker.setcash(10000.0)
          
          results = cerebro.run()
              
          cerebro.plot()
          strat = results[0]
      
      if __name__ == '__main__':
          run_strat()
      

      0_1490941110507_4.png

      posted in General Code/Help
      C
      cnimativ
    • RE: Problem with BT Signal and Sizer
      • The final portfolio value in both cases is 767892.24 (from the chart). Some elaboration may be needed here.

      One is 767892.34, the other one is 76892.24. Very minor differences, but given the same data set and same signal, it should be exactly the same.

      • Some extra elaboration here would also help.

      The same issue that trades are not being executed when sizer uses previous day close to calculate # of shares in an order, sees a gap up next day open, determines there's not enough cash balance, and abandons the whole trade.

      For now I am just setting sizer to 99% to make sure trade fills.

      • Override notify_trade and store the notifications.
      • Or access the _trades attribute of the strategy, where the trades are kept in a list.

      How do I access _trades after backtesting? I've looked into cerebro.strats[0][0][0]._trades and it says AttributeError: type object 'SmaCross' has no attribute '_trades'.

      • The signal in the example calculates SMAs over data0 (because nothing else is specified)
      • Specify to the SMA to which dataX pertains

      How do you specify/attach an SMA calculation over data A to a security data B, where A doesn't have to be the same as B?
      When I look at SMA it reads,

          def __init__(self):
              # Before super to ensure mixins (right-hand side in subclassing)
              # can see the assignment operation and operate on the line
              self.lines[0] = Average(self.data, period=self.p.period)
      
              super(MovingAverageSimple, self).__init__()
      

      so it appears to be that I can use the following to add sma signal for self.data0 this way?

      sma1, sma2 = bt.ind.SMA(self.data0, period=self.p.pfast), bt.ind.SMA(self.data0, period=self.p.pslow)
      

      However, when I look at strategy.py, it doesn't appear that I can specify which signal to attach to which data when I do signal_add(...)?

          def signal_add(self, sigtype, signal):
              self._signals[sigtype].append(signal)
      

      Basically, what I am trying to do is to place trades for security B based on signal derived from security A. The other case is to close A, buy B based on -crossover signal in A and to buy A, close B on +crossover signal in A.

      Thanks for the help.

      posted in General Code/Help
      C
      cnimativ
    • RE: IPython Notebook plot sizing

      Yes,

      I've tried the following with some success. It seems that I can control the plot size, but not plot font size. I've varied "font.size" from "20" all the way to "100" and they just don't seem to change.

      import matplotlib.pylab as pylab
      pylab.rcParams['figure.figsize'] = 30, 20  # that's default image size for this interactive session
      pylab.rcParams['font.family'] = 'sans-serif'
      pylab.rcParams['font.sans-serif'] = ['Bitstream Vera Sans']
      pylab.rcParams['font.serif'] = ['Bitstream Vera Sans']
      pylab.rcParams["font.size"] = "100"
      

      0_1490887198390_3.png

      posted in General Code/Help
      C
      cnimativ
    • Problem with BT Signal and Sizer

      Hi all,

      I wrote two different scripts using the SMA strategy on the website front page with some slight modifications and ran into two problems.

      • Data from the same source loaded in different ways resulted in different portfolio value.
      • SMA CrossOver signal Sizer issue that is documented somewhat here

      Here's my questions:

      • How do I inspect trade history in BT?
      • How do I apply the same signal across several different securities?
      • How do I pull the result/portfolio balance time series out from BT?

      The following are the codes and their respective outputs:

      import backtrader as bt
      import backtrader.filters as btfilters
      from datetime import datetime
      import pandas_datareader.data as web
      
      class SmaCross(bt.SignalStrategy): 
          # using bt.SignalStrategy instead of bt.Strategy to utilize signals
          params = (('pfast', 50), ('pslow', 200),)
          def __init__(self):
              sma1, sma2 = bt.ind.SMA(period=self.p.pfast), bt.ind.SMA(period=self.p.pslow)
              self.signal_add(bt.SIGNAL_LONG, bt.ind.CrossOver(sma1, sma2))
      
      def runstrat():
          start = datetime(1950,1,1)
          end = datetime(2017,1,2)
          spx = web.DataReader("^GSPC", 'yahoo', start, end)
          data = bt.feeds.PandasData(dataname=spx)
          
          cerebro = bt.Cerebro()
          cerebro.adddata(data)
          cerebro.addstrategy(SmaCross)
          cerebro.broker.setcash(10000.0)
          cerebro.addsizer(bt.sizers.PercentSizer, percents=99)
          
          results = cerebro.run()
              
          cerebro.plot()
          strat = results[0]
              
      if __name__ == '__main__':
          runstrat()
      

      0_1490834552182_1.png

      from datetime import datetime
      import backtrader as bt
          
      class SmaCross(bt.SignalStrategy):
          params = (('pfast', 50), ('pslow', 200),)
          def __init__(self):
              sma1, sma2 = bt.ind.SMA(period=self.p.pfast), bt.ind.SMA(period=self.p.pslow)
              self.signal_add(bt.SIGNAL_LONG, bt.ind.CrossOver(sma1, sma2))
          
      cerebro = bt.Cerebro()
          
      data = bt.feeds.YahooFinanceData(dataname='^GSPC', fromdate=datetime(1950, 1, 1),
                                           todate=datetime(2017, 1, 2))
      cerebro.adddata(data)
      
      cerebro.addstrategy(SmaCross)
      cerebro.broker.setcash(10000.0)
      cerebro.addsizer(bt.sizers.PercentSizer, percents=99)
      cerebro.run()
      cerebro.plot()
      

      0_1490834571814_2.png

      posted in General Code/Help
      C
      cnimativ
    • 1
    • 2
    • 1 / 2