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
-
@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 on2018-01-03
(even if we cannot see because you don't log it. The order is executed on2018-01-04
. At this point aTrade
is opened and you change the value of thep.tradeopen
parameter in the indicator.On the next day:
2018-01-05
(only 1 day later) the indicator uses thep.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)))
-
@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 theopen
price.
But you could issue a
Close
orLimit
order which would execute against theclose
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/barBut 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 thatself.data[-1]
does exist and you will record it if it's actually the highest. -
-
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])