Backtrader Community

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

    howardhh

    @howardhh

    0
    Reputation
    156
    Profile views
    8
    Posts
    0
    Followers
    0
    Following
    Joined Last Online

    howardhh Unfollow Follow

    Latest posts made by howardhh

    • RE: Multiple data feeds - different order gives different trades

      Thanks for the replies, I'll double check the code and will see what happens.

      posted in General Code/Help
      H
      howardhh
    • RE: Multiple data feeds - different order gives different trades

      @backtrader

      Apologies, I wasn't aware of that. Will definitely use it next time.

      Regarding the data feed order though, where do you think the problem is?

      Thank you.

      posted in General Code/Help
      H
      howardhh
    • Multiple data feeds - different order gives different trades

      Hi,

      I have 3 different data feeds but when I change the order of those around, my strategy gives different trades/ results. I have been able to narrow this down to the Startegy Class itself, but am not sure why this is happening.

      Can someone please help? Thanks in advance.

      import backtrader as bt
      from datetime import datetime
      from datetime import timedelta

      class maCross(bt.Strategy):
      '''
      oneplot = Force all datas to plot on the same master.
      '''
      params = (
      ('ema50', 50),
      ('ema200', 200),
      ('cci20', 20),
      ('oneplot', True)
      )

      def __init__(self):
          '''
          Create an dictionary of indicators so that we can dynamically add the
          indicators to the strategy using a loop. This mean the strategy will
          work with any number of data feeds.
          '''
          self.inds = dict()
          for i, d in enumerate(self.datas):
              self.inds[d] = dict()
              self.inds[d]['ema50'] = bt.indicators.ExponentialMovingAverage(
                  d.close, period=self.params.ema50)
              self.inds[d]['ema200'] = bt.indicators.ExponentialMovingAverage(
                  d.close, period=self.params.ema200)
              self.inds[d]['cci20'] = bt.indicators.CommodityChannelIndex(
                  period=self.params.cci20)
              self.inds[d]['crossUp'] = bt.indicators.CrossOver(self.inds[d]['cci20'], -100)  # crossup = 1
              self.inds[d]['EMAcross'] = bt.indicators.CrossOver(self.inds[d]['ema50'], self.inds[d]['ema200'])
              self.inds[d]['crossDown'] = bt.indicators.CrossOver(self.inds[d]['cci20'], 100)  # crossdown = -1
      
              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 prenext(self):
          self.next()
      
      def next(self):
          feeds_with_data = [(i, d) for i, d in enumerate(self.datas) if len(d)]
          for i, d in feeds_with_data:
              # calculate risk/ stop price
              risk = 100  # risk = 100 dollars
              stop = 0.05  # stop 5% below close
              stop_price = self.datas[i].close[0] * (1 - stop)
      
              dt, dn = self.datetime.date(), d._name
              pos = self.getposition(d).size
              if not pos:  # no market / no orders
                  if self.inds[d]['ema50'] >= self.inds[d]['ema200']:
                      if self.inds[d]['crossUp'][0] == 1:  # if CCI crosses up -100
                          if self.datas[i].close[0] - stop_price > 0:  # check if risk is acceptable/ valid
                              qty = round(risk / (self.datas[i].close[0] - stop_price), 0)  # get no. of shares to trade
                              eofthisday = self.datas[i].datetime.date()  #get current date
                              expiry = eofthisday + timedelta(days=4) #keep order active for 4 days (over weekend + public hol)
                              self.buy_bracket(data=d, limitprice=10000, price=self.datas[i].close[0],
                                               stopprice=stop_price, exectype=bt.Order.Limit, size=qty, valid=expiry)
                          else:
                              pass
                      else:
                          pass
                  else:
                      pass
      
              else:
                  if self.inds[d]['crossDown'][0] == -1:  # if cci crosses down 100
                      self.close(data=d)
      
      def notify_trade(self, trade):
          dt = self.data.datetime.date()
          if trade.isclosed:
              print('{} {} Closed: PnL Gross {}, Net {}'.format(
                  dt,
                  trade.data._name,
                  round(trade.pnl, 2),
                  round(trade.pnlcomm, 2)))
      

      datalist = [
      ('data_adjusted/CBA.AX.csv', 'CBA.AX'),
      ('data_adjusted/CSL.AX.csv', 'CSL.AX'),
      ('data_adjusted/BSL.AX.csv', 'BSL.AX'),
      ]

      For example when I swap the order of CBA and BSL with each other, the trades taken are different and I get a different end result.

      Thanks again, appreciate any help available.

      posted in General Code/Help
      H
      howardhh
    • Prenext - does something weird

      Hi,

      I'm trying to backtest over multiple stocks at once. One of the stocks was only listed after testing date and I found that adding prenext allowed the code to be run over the entire testing range. However, it does something odd regarding that stock:

      oneplot = Force all datas to plot on the same master.
      
      params = (
          ('ema50', 50),
          ('ema200', 200),
          ('cci20', 20),
          ('oneplot', True)
      )
      
      def __init__(self):
          '''
          Create an dictionary of indicators so that we can dynamically add the
          indicators to the strategy using a loop. This mean the strategy will
          work with any number of data feeds.
          '''
          self.inds = dict()
          for i, d in enumerate(self.datas):
              self.inds[d] = dict()
              self.inds[d]['ema50'] = bt.indicators.ExponentialMovingAverage(
                  d.close, period=self.params.ema50)
              self.inds[d]['ema200'] = bt.indicators.ExponentialMovingAverage(
                  d.close, period=self.params.ema200)
              self.inds[d]['cci20'] = bt.indicators.CommodityChannelIndex(
                  period=self.params.cci20)
              self.inds[d]['crossUp'] = bt.indicators.CrossOver(self.inds[d]['cci20'], -100)  # crossup = 1
              self.inds[d]['EMAcross'] = bt.indicators.CrossOver(self.inds[d]['ema50'], self.inds[d]['ema200'])
              self.inds[d]['crossDown'] = bt.indicators.CrossOver(self.inds[d]['cci20'], 100)  # crossdown = -1
      
              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 prenext(self):
          self.next()
      
      def next(self):
      
          for i, d in enumerate(self.datas):
              # calculate risk/ stop price
              risk = 100  # risk = 100 dollars
              stop = 0.05  # stop 5% below close
              stop_price = self.datas[i].close[0] * (1 - stop)
      
              dt, dn = self.datetime.date(), d._name
              pos = self.getposition(d).size
              if not pos:  # no market / no orders
                  if self.inds[d]['ema50'] >= self.inds[d]['ema200']:
                      if self.inds[d]['crossUp'][0] == 1:  # if CCI crosses up -100
                          if self.datas[i].close[0] - stop_price > 0:  # check if risk is acceptable/ valid
                              qty = round(risk / (self.datas[i].close[0] - stop_price), 0)  # get no. of shares to trade
                              eofthisday = self.datas[i].datetime.date()  #get current date
                              expiry = eofthisday + timedelta(days=4) #keep order active for 4 days (over weekend + public hol)
                              self.buy_bracket(data=d, limitprice=10000, price=self.datas[i].close[0],
                                               stopprice=stop_price, exectype=bt.Order.Limit, size=qty, valid=expiry)
                          else:
                              pass
                      else:
                          pass
                  else:
                      pass
      
              else:
                  if self.inds[d]['crossDown'][0] == -1:  # if cci crosses down 100
                      self.close(data=d)
      
      def notify_trade(self, trade):
          dt = self.data.datetime.date()
          if trade.isclosed:
              print('{} {} Closed: PnL Gross {}, Net {}'.format(
                  dt,
                  trade.data._name,
                  round(trade.pnl, 2),
                  round(trade.pnlcomm, 2)))
      

      The results I got seemed alright, except for the starred line (see below). It makes a very weird trade on the day of listing (data start) for A2M. I think this can be fixed using prenext but am not quite sure how to. Any help is greatly appreciated. Thank you.

      results:

      2014-08-29 BSL.AX Closed: PnL Gross -66.25, Net -79.45
      2014-10-21 CSL.AX Closed: PnL Gross 37.16, Net 23.96
      2015-02-03 CSL.AX Closed: PnL Gross 83.01, Net 69.81
      2015-02-25 CSL.AX Closed: PnL Gross 106.62, Net 93.42
      2015-03-30 A2M.AX Closed: PnL Gross -1894.41, Net -1907.61 ***********
      2015-07-24 CSL.AX Closed: PnL Gross 60.94, Net 47.74
      2015-10-12 CSL.AX Closed: PnL Gross 35.94, Net 22.74
      2015-11-15 CSL.AX Closed: PnL Gross 117.94, Net 104.74
      2016-04-29 CSL.AX Closed: PnL Gross 56.86, Net 43.66

      posted in General Discussion
      H
      howardhh
    • RE: Position sizing and stop losses mess up trading strategy

      @ab_trader Ahhh I see. All fixed up. Thanks heaps.

      posted in General Code/Help
      H
      howardhh
    • Position sizing and stop losses mess up trading strategy

      Hi again,

      Apologies in advance but I will likely be posting a lot of questions since I am just starting out. Thanks for the help so far.

      Instead of simply buying 10 shares at a time I've added to my code a position sizing mechanism (where I risk 100 dollars per trade with a stop loss 5% below entry price). The strategy worked fine before I added this (buy when ema50 > ema200 when cci crosses up -100, sell when cci crosses down 100) but after I added in the position sizer/ stop loss, it turns weird.

      Original working code:

      class maCross(bt.Strategy):
      '''
      For an official backtrader blog on this topic please take a look at:

      https://www.backtrader.com/blog/posts/2017-04-09-multi-example/multi-example.html
      
      oneplot = Force all datas to plot on the same master.
      '''
      params = (
      ('ema50', 50),
      ('ema200', 200),
      ('cci20', 20),
      ('oneplot', True)
      )
      def __init__(self):
          '''
          Create an dictionary of indicators so that we can dynamically add the
          indicators to the strategy using a loop. This mean the strategy will
          work with any number of data feeds.
          '''
          self.inds = dict()
          for i, d in enumerate(self.datas):
              self.inds[d] = dict()
              self.inds[d]['ema50'] = bt.indicators.ExponentialMovingAverage(
                  d.close, period=self.params.ema50)
              self.inds[d]['ema200'] = bt.indicators.ExponentialMovingAverage(
                  d.close, period=self.params.ema200)
              self.inds[d]['cci20'] = bt.indicators.CommodityChannelIndex(
                  period=self.params.cci20)
              self.inds[d]['crossUp'] = bt.indicators.CrossOver(self.inds[d]['cci20'], -100)  # crossup = 1
              self.inds[d]['EMAcross'] = bt.indicators.CrossOver(self.inds[d]['ema50'], self.inds[d]['ema200'])
              self.inds[d]['crossDown'] = bt.indicators.CrossOver(self.inds[d]['cci20'], 100)  # crossdown = -1
      
              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 prenext(self):
          self.next()
      
      def next(self):
          for i, d in enumerate(self.datas):
              dt, dn = self.datetime.date(), d._name
              pos = self.getposition(d).size
              if not pos:  # no market / no orders
                  if self.inds[d]['ema50'] >= self.inds[d]['ema200']:
                      if self.inds[d]['crossUp'][0] == 1:
                          self.buy(data=d, size=10)
                      else:
                          pass
                  else:
                      pass
      
              else:
                  if self.inds[d]['crossDown'][0] == -1:
                      self.close(data=d)
      

      I changed the next function to:

      def next(self):
      
          for i, d in enumerate(self.datas):
              #calculate risk/ stop price
              risk = 100  #risk = 100 dollars
              stop = 0.05 #stop 5% below close
              stop_price = self.datas[i].close[0]*(1-stop)
      
              dt, dn = self.datetime.date(), d._name
              pos = self.getposition(d).size
              if not pos:  # no market / no orders
                  if self.inds[d]['ema50'] >= self.inds[d]['ema200']:
                      if self.inds[d]['crossUp'][0] == 1: #if CCI crosses up -100
                          if self.datas[i].close[0] - stop_price > 0: #check if risk is acceptable/ valid
                              qty = round(risk / (self.datas[i].close[0] - stop_price),0) #get no. of shares to trade
                              self.buy_bracket(data=d, limitprice=self.datas[i].close[0], price=self.datas[i].close[0],
                                               stopprice=stop_price, exectype=bt.Order.Limit, size=qty)
                          else:
                              pass
                      else:
                          pass
                  else:
                      pass
      
              else:
                  if self.inds[d]['crossDown'][0] == -1:  #if cci crosses down 100
                      self.close(data=d)
      

      I can't tell what I've done wrong but it seems like the trades are now buy when cci20 crosses down -100 and sell when it crosses back up.

      Thanks heaps for any input.

      posted in General Code/Help
      H
      howardhh
    • RE: Not all available trades taken

      @ab_trader That was actually it haha. Thanks for that. I'm literally just starting out and it's really tough.

      But cheers for the assistance, very much appreciated.

      posted in General Code/Help
      H
      howardhh
    • Not all available trades taken

      Hi there,

      I'm a newbie to algo trading and am running into some problems. I've been trying to backtest a trend-following strategy where I enter if the EMA50 > EMA200 and when the CCI20 crosses the -100. Sell when EMA50 and EMA200 cross lower.

      The problem is that backtrader is only taking some trades and not others for some reason. Can someone please help explain why this is the case and how I can solve this?

      Thanks, Howard

      Here is my code for the strategy:

      class maCross(bt.Strategy):
      '''
      For an official backtrader blog on this topic please take a look at:

      https://www.backtrader.com/blog/posts/2017-04-09-multi-example/multi-example.html
      
      oneplot = Force all datas to plot on the same master.
      '''
      params = (
      ('ema50', 50),
      ('ema200', 200),
      ('cci20', 20),
      ('oneplot', True)
      )
      def __init__(self):
          '''
          Create an dictionary of indicators so that we can dynamically add the
          indicators to the strategy using a loop. This mean the strategy will
          work with any number of data feeds.
          '''
          self.inds = dict()
          for i, d in enumerate(self.datas):
              self.inds[d] = dict()
              self.inds[d]['ema50'] = bt.indicators.ExponentialMovingAverage(
                  d.close, period=self.params.ema50)
              self.inds[d]['ema200'] = bt.indicators.ExponentialMovingAverage(
                  d.close, period=self.params.ema200)
              self.inds[d]['cci20'] = bt.indicators.CommodityChannelIndex(
                  period=self.params.cci20)
              self.inds[d]['crossUp'] = bt.indicators.CrossOver(self.inds[d]['cci20'], -100)  # crossup = 1
              self.inds[d]['EMAcross'] = bt.indicators.CrossOver(self.inds[d]['ema50'], self.inds[d]['ema200']) #crossdown = -1
      
              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 prenext(self):
          self.next()
      
      def next(self):
          for i, d in enumerate(self.datas):
              dt, dn = self.datetime.date(), d._name
              pos = self.getposition(d).size
              if not pos:  # no market / no orders
                  if self.inds[d]['ema50'] >= self.inds[d]['ema200']:
                      if self.inds[d]['crossUp'][0] == 1:
                          self.buy(data=d, size=1000)
                      else:
                          pass
                  else:
                      pass
      
              else:
                  if self.inds[d]['EMAcross'][0] == -1:
                      self.close(data=d)
      
      def notify_trade(self, trade):
          dt = self.data.datetime.date()
          if trade.isclosed:
              print('{} {} Closed: PnL Gross {}, Net {}'.format(
                  dt,
                  trade.data._name,
                  round(trade.pnl, 2),
                  round(trade.pnlcomm, 2)))
      
      posted in General Code/Help
      H
      howardhh