For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/
Example indicator to build larger timeframe from minute data feed (smaller timeframe), without using replay
-
I had difficulties using
replay
in the case of multiple symbols (multiple feeds) and different timeframes. For example:- the strategy runs 10 stocks (10 feeds)
- the feed files use 1 minute data (need 1 minute resolution)
- but the indicator is calculated on the daily timeframe (instead of 1 minute)
A simple solution that I'm using:
- always use only the 1 minute feed (no
resample
, noreplay
) - in the indicator, build the larger timeframe using the 1 minute feed (for example build the daily or weekly from minute)
- calculate the indicator “by hand” using python lists
Maybe not a “pretty” solution, but it solved my problem.
Sample code if someone needs to do something similar:
import datetime as dt import backtrader as bt import statistics class BaseMinuteIndicator(bt.Indicator): params = (('larger_timeframe', 'D1'), ) def init(self): self.open = list() self.high = list() self.low = list() self.close = list() self.datetime = list() self.last_period = list() def process_new_item(self): add_new_item = False current_period = None if self.p.larger_timeframe == 'D1': current_period = self.data.datetime.datetime(0).day elif self.p.larger_timeframe == 'H1': current_period = self.data.datetime.datetime(0).hour elif self.p.larger_timeframe == 'W1': current_period = self.data.datetime.date(0).isocalendar()[1] if len(self.last_period) != 0: if self.last_period[-1] == current_period: if self.data.high[0] > self.high[-1]: self.high[-1] = self.data.high[0] if self.data.low[0] < self.low[-1]: self.low[-1] = self.data.low[0] self.close[-1] = self.data.close[0] else: add_new_item = True else: add_new_item = True if add_new_item: self.open.append(self.data.open[0]) self.high.append(self.data.high[0]) self.low.append(self.data.low[0]) self.close.append(self.data.close[0]) self.datetime.append(self.data.datetime[0]) self.last_period.append(current_period) return add_new_item class MeanIndicatorExample(BaseMinuteIndicator): lines = ('l_mean', ) params = (('period', 200), ) plotlines = dict(l_mean=dict(ls='-', color='cyan'), ) plotinfo = dict(subplot=False) def __init__(self): self.init() self.mean = list() def next(self): # New item add_new_item = self.process_new_item() if add_new_item: self.mean.append(0) # Mean i = len(self.datetime) - 1 if len(self.datetime) >= self.p.period: self.mean[i] = statistics.mean(self.close[-self.p.period:]) self.l.l_mean[0] = self.mean[i] class TestStrategy(bt.Strategy): def __init__(self): self.indicator = MeanIndicatorExample( ) def next(self): pass def run(): csv_file = '...' data = bt.feeds.GenericCSVData(dataname=csv_file, timeframe=bt.TimeFrame.Minutes, fromdate = dt.datetime(2012, 1, 1), todate=dt.datetime(2022, 1, 1)) cerebro = bt.Cerebro() cerebro.adddata(data) cerebro.addstrategy(TestStrategy, ) cerebro.run() cerebro.plot() if __name__ == '__main__': run()
-
Some details to clarify the difficulties I had with the use of replay (and why I implemented the solution above). I need to take volume into account because sometimes I use low volume instruments where the use of a
Volume Filler
makes big difference.I was using minute
replay
on daily chart: it worked fine in my simulation initially, but the issues I had were :- The main issue: I needed to access the volume of each minute but calculate indicators on the daily chart (I need minute volume information for my custom Volume Filler). In replay mode, the volume is the accumulated volume for the day, not the volume of the minute (that I needed).
- The chart in replay mode shows daily bars, but as I do multiple trades a day, I couldn't see the details. With minute chart it is possible.
- I had a lot of trouble debugging when I made a multi-symbol version using replay. The version with minute chart is now simpler for me to debug, because multi symbol in replay can become complex. For example: the complexity of syncing feeds with missing minutes in history, where one feed may have fewer minute bars than others.