Backtrader Community

    • 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/

    dynamic indicator missing first day trade opened?

    General Code/Help
    2
    5
    748
    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.
    • M
      momentum last edited by

      Hi,

      I wonder regarding the dynamic indicator missing the first day a trade is opened since indicators are called before notify_order and notify_trade that are being called before next. On the following example where indicator records the highest close value for a trade, the indicator's value is updated on the 2nd day after buy. Setting coc to True does not really change something. Is there any way to change this behavior?

      Thanks

      import backtrader as bt
      import pandas as pd
      
      class DynamicHighest(bt.Indicator):
          lines = ('dyn_highest',)
          params = dict(tradeopen=False)
      
          def next(self):
              print("Indicator next")
              if self.p.tradeopen:
                  self.lines.dyn_highest[0] = max(self.data[0], self.dyn_highest[-1])
      
      
      class MyStrategy(bt.Strategy):
          def __init__(self):
              self.dyn_highest = DynamicHighest(data=self.datas[0].lines.close)
      
          def notify_order(self, order):
              if order.status == order.Completed:
                  if order.isbuy():
                      print("NOTE: BUY EXECUTED, {} shares at {}".format(order.size, order.executed.price))
                  elif order.issell():
                      print("NOTE: SELL EXECUTED, {} shares at {}".format(order.size, order.executed.price))
      
          def notify_trade(self, trade):
              self.dyn_highest.p.tradeopen = trade.isopen
              if trade.justopened:
                  print("NOTE: Opening trade at price {} on date {}".format(trade.price, bt.utils.num2date(trade.dtopen)))
      
          def next(self):
              print("Before:{}, {}, {}, {}, {}".format(self.datas[0].datetime.date(0),
                                                   self.datas[0].lines.close[0],
                                                   self.datas[0].lines.high[0],
                                                   self.dyn_highest.p.tradeopen,
                                                   self.dyn_highest.lines.dyn_highest[0]))
              if self.datas[0].lines.high[0] > 0:
                  self.buy()
              print("After:{}, {}, {}, {}, {}".format(self.datas[0].datetime.date(0),
                                                   self.datas[0].lines.close[0],
                                                   self.datas[0].lines.high[0],
                                                   self.dyn_highest.p.tradeopen,
                                                   self.dyn_highest.lines.dyn_highest[0]))
      
      
      if __name__ == '__main__':
      
          # Create a cerebro entity
          cerebro = bt.Cerebro()
      
          cerebro.addstrategy(MyStrategy)
      
          df1 = pd.DataFrame({'Close': [1,2,3,4,100,5,7,6], 'Open': [0,1,2,3,99,4,6,5], 'High': [0,0,0,1,0,0,0,0]}, index=pd.date_range(start='1/1/2018', periods=8))
          cerebro.adddata(bt.feeds.PandasData(dataname=df1, nocase=True))
      
          # Set our desired cash start
          cerebro.broker.setcash(100000.0)
          #cerebro.broker.set_coc(True)
      
          cerebro.run(runonce=False, preload=True)
      
      Indicator next
      Before:2018-01-01, 1.0, 0.0, False, nan
      After:2018-01-01, 1.0, 0.0, False, nan
      Indicator next
      Before:2018-01-02, 2.0, 0.0, False, nan
      After:2018-01-02, 2.0, 0.0, False, nan
      Indicator next
      Before:2018-01-03, 3.0, 0.0, False, nan
      After:2018-01-03, 3.0, 0.0, False, nan
      Indicator next
      Before:2018-01-04, 4.0, 1.0, False, nan
      After:2018-01-04, 4.0, 1.0, False, nan
      Indicator next
      NOTE: BUY EXECUTED, 1 shares at 99.0
      NOTE: Opening trade at price 99.0 on date 2018-01-05 00:00:00
      Before:2018-01-05, 100.0, 0.0, True, nan
      After:2018-01-05, 100.0, 0.0, True, nan
      Indicator next
      Before:2018-01-06, 5.0, 0.0, True, 5.0
      After:2018-01-06, 5.0, 0.0, True, 5.0
      Indicator next
      Before:2018-01-07, 7.0, 0.0, True, 7.0
      After:2018-01-07, 7.0, 0.0, True, 7.0
      Indicator next
      Before:2018-01-08, 6.0, 0.0, True, 7.0
      After:2018-01-08, 6.0, 0.0, True, 7.0
      
      1 Reply Last reply Reply Quote 0
      • B
        backtrader administrators last edited by

        @momentum said in dynamic indicator missing first day trade opened?:

        I wonder regarding the dynamic indicator missing the first day a trade is opened since indicators are called before notify_order and notify_trade that are being called before next

        You should change the convention. An Indicator is not called, it is asked to (re)calculate itself for the current conditions. Obviously a method of the indicator is being called, but you should see it that way.

        @momentum said in dynamic indicator missing first day trade opened?:

        On the following example where indicator records the highest close value for a trade, the indicator's value is updated on the 2nd day after buy

        Convention it is important and what you say is therefore wrong. You create a buy order on 2018-01-03 (even if we cannot see because you don't log it. The order is executed on 2018-01-04. At this point a Trade is opened and you change the value of the p.tradeopen parameter in the indicator.

        On the next day: 2018-01-05 (only 1 day later) the indicator uses the p.tradeopen = True value you set.

        Why is this the right behavior? (i.e. Indicator Calculation => notify_order => notify_trade)

        Because you could issue a new order during notify_order / notify_trade based on the value calculated by the indicators. As simple as that. A reaction to a completed order can be a counter stop-loss order, which uses the value of an indicator.

        1 Reply Last reply Reply Quote 0
        • M
          momentum last edited by

          Thanks for your prompt reply and help.

          I am not questioning whether this is the right behaviour from a design/conceptual point of view.

          However, it seems to me that if you want to record the highest close value since the trade opened, it is not possible with a dynamic indicator. Order executed on open of 2018-01-05 (based on signal on previous day), trade opens, p.tradeopen set to True, close on that day is 100 and on 2018-01-06 the dynamic indicator has as highest close value the value of 2018-01-06 which is 5 (instead of 100).

          Unless of course, I try something like this

              def notify_trade(self, trade):
                  self.dyn_highest.p.tradeopen = trade.isopen
                  if trade.justopened:
                      self.dyn_highest.lines.dyn_highest[0] = self.data[0]
                      print("NOTE: Opening trade at price {} on date {}".format(trade.price, bt.utils.num2date(trade.dtopen)))
          
          B 1 Reply Last reply Reply Quote 0
          • B
            backtrader administrators @momentum last edited by

            @momentum said in dynamic indicator missing first day trade opened?:

            I am not questioning whether this is the right behaviour from a design/conceptual point of view.

            In fact you are, but challenging things is how progress is made, when progress can be made.

            @momentum said in dynamic indicator missing first day trade opened?:

            However, it seems to me that if you want to record the highest close value since the trade opened, it is not possible with a dynamic indicator

            It is possible. The definition "highest close since the trade opened" as done by the indicator is ok. You argue that 1 day (1 bar actually, because the indicator doesn't care about timeframes) is mssing, but see:

            • "since the trade opened"

              Some people, as you do, argue that "since" means on the same day. Because you know that with a Market order, the execution price will be the open price.

            But you could issue a Close or Limit order which would execute against the close of the day. In that case the 1st close to take into account would the next one as done by the indicator. And you could also argue that "since" refers to the date/bar and you want to start counting from the next date/bar

            But regardless of definitions ...

            @momentum said in dynamic indicator missing first day trade opened?:

            Unless of course, I try something like this

            You can do it a lot easier:

            class DynamicHighest(bt.Indicator):
                lines = ('dyn_highest',)
                params = dict(tradeopen=False)
            
                def next(self):
                    print("Indicator next")
                    if self.p.tradeopen:
                        self.lines.dyn_highest[0] = max(self.data[-1], self.data[0], self.dyn_highest[-1])
            

            Because you know that tradeopen will first be noticed when the order has been executed the day before, you can guarantee that self.data[-1] does exist and you will record it if it's actually the highest.

            1 Reply Last reply Reply Quote 1
            • B
              backtrader administrators last edited by

              In any case and to avoid that a dynamic indicator is run in vectorized mode, you can define it this way, which ensures step-b-step (i.e.: event-based) calculation

              class DynamicHighest(bt.Indicator):
                  _nextforce = True  # indicator needs that the system runs in event based mode (step by step calculation)
              
                  lines = ('dyn_highest',)
                  params = dict(tradeopen=False)
              
                  def next(self):
                      print("Indicator next")
                      if self.p.tradeopen:
                          self.lines.dyn_highest[0] = max(self.data[-1], self.data[0], self.dyn_highest[-1])
              
              1 Reply Last reply Reply Quote 0
              • 1 / 1
              • First post
                Last post
              Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors