Qs about behavior of once_via_next and runonce
-
Hi, new user here trying to better understand the control flow of indicators when
runonce=True
Consider a simple indicator that just copies a signal using a
next
method:class Copy(bt.Indicator): lines = ('copy',) def __init__(self): self.x = self.datas[0] def next(self): self.lines.copy[0] = self.x[0]
If I use this copy indicator on some indicator with a non-zero period and run cerebro with
runonce=True
, the ouput of the indicator becomes all nans.class TestStrategy(bt.Strategy): def __init__(self): self.signal = bt.ind.PercentChange( self.datas[0], period=1) self.signal_copy = Copy(self.signal) cerebro = bt.Cerebro(runonce=True) cerebro.adddata(data) cerebro.addstrategy(TestStrategy) strats_out = cerebro.run() strat = strats_out[0] # Id expect these two to be the same strat.signal.array[:10] strat.signal_copy.array[:10]
(However, with
run_once=False
, the results are as expected.)After reading https://community.backtrader.com/topic/242/is-backtrader-event-driven-or-vectorized/2 and realizing there is a
once_via_next
method inIndicator
, I thought that even ifrun_once=True
, the signal would be copied batch-wise after the nested signal finishes evaluating.
(Q1) Why is this copy indicator usingnext
evaluating to all nans whenrun_once=True
. It feels like maybe dependencies in nested indicators are not resolved as I'd hoped(?).
I thought it might have to do with_minperiod
not being carried over properly, but it seems_minperiod
for both thesignal
andsignal_copy
are both 2 as expected.So then I thought, OK, let me override the
once
method instead of relying ononce_via_next
like so:class Copy(bt.Indicator): lines = ('copy',) def __init__(self): self.x = self.datas[0] def next(self): self.lines.copy[0] = self.x[0] def once(self, start, end): print(f'running once: {start},{end}') for i in range(start, end): self.lines.copy.array[i] = self.x[i]
And this works... kind of. The
signal_copy
now has values fromsignal
; however, the signal is shifted by 1. Wheresignal
begins with 1 nan,signal_copy
begins with 2. I have a debug statement that prints thestart
andend
arguments outputting:running once: 1,2 running once: 2,1000
This hints at why
signal_copy
has 2 nans, but (Q2) I'm wondering whyonce
is ran before the minimum period. In the docs I see that:
once(self, start, end)
Called when the minimum period has been met. The internal array must be processed between start and end which are zero based from the start of the internal arrayAnd writing the copy indicator using the
__init__
method works as expected...class Copy(bt.Indicator): lines = ('copy',) def __init__(self): self.x = self.datas[0] self.lines.copy = self.x
but of course, I'm actually trying to write something more complex that requires the usage of
next
.Any hints would be much appreciated!
-
Sorry, meant to post this in
general-code-help
-
Looks like if you want to run your indicator in the vectorized mode, you need
__init__()
andonce()
methods to be present in the indicator.next()
is not processed in the vectorized mode.