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?



  • 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
    

  • administrators

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



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

  • administrators

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


  • administrators

    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])
    

 

});