Backtrader dynamically synthesizes large time granularity data by using small time granularity data: replay function

Sweeping monk backtrader technical tutorial acquisition method

===========================

Students who have read my tutorial know that the resample function can be used in backtrader to synthesize small-grained data into large-grained data, such as synthesizing 1-minute K-line data into 1-hour K-line data. For example, when all the 1-minute K-lines come out between 10:00 and 11:00, a 1-hour K-line that ends at 11:00 is synthesized. At 10:30, if you want to take the 1-hour K-line, you can only go to the previous one. That is, the 1-hour bar that ends at 10:00. In other words, a new 1-hour bar can only be synthesized when an hour is completely over.

However, some users have a requirement that the latest 1-hour line should be dynamically synthesized even if it has not been completed. For example, starting at 10 o'clock, if a 1-minute line comes in, then the 1-hour line only contains the data of this minute. , and then enter a one-minute line, and update the one-hour line to include the data of these two minutes. This dynamic update, until 11 o'clock, the bottom of this 1-hour line is finalized. In this way, at 10:30, if you visit the latest 1-hour line, it is actually the half-hour line from 10:00 to 10:30. Backtrader realizes this function by playing back replay, which is very simple. The example code is as follows:



class St(bt.Strategy):
    def start(self):
        self.counter = 0

    def next(self): # 没根分钟线触发一次
        self.counter += 1

        txt = []
        txt += ['{:04d}'.format(self.counter)]
        txt += ['{:04d}'.format(len(self))]
        txt += ['{:04d}'.format(len(self.data))] # 数据长度在整点开始的1小时内是不变的,
        txt += ['{}'.format(self.datetime.datetime().isoformat())]
        txt += ['{:.2f}'.format(self.data.close[0])]
        print(','.join(txt))

        if self.counter % 10:  # 每隔10根1分钟线,执行操作
            return

        # Enter with 1 or revers with twice the opposite (2 or -2)
        if self.position.size <= 0:
            print('-- ORDER BUY')
            self.buy(size=abs(self.position.size * 2) or 1)
        else:
            print('-- ORDER SELL')
            self.sell(size=-abs(self.position.size * 2) or -1)

    def notify_order(self, order):
        if order.status == order.Completed:
            print('-- BEGIN ORDER COMPLETED --')
            txt = []
            txt += ['{:04d}'.format(self.counter)]
            txt += ['{}'.format(self.datetime.datetime().isoformat())]
            txt += ['{:.2f}'.format(order.executed.price)]
            txt += ['{:2d}'.format(order.executed.size)]
            print(','.join(txt))
            print('-- END ORDER COMPLETED --')
            print('-- PORTFOLIO SIZE:', self.position.size)


cerebro = bt.Cerebro()

data = bt.feeds.BacktraderCSVData(  # 分钟线
    dataname='../../datas/2006-min-005.txt',
    timeframe=bt.TimeFrame.Minutes
)
cerebro.replaydata(data, timeframe=bt.TimeFrame.Days, compression=1) # 分钟线合成日线

cerebro.addstrategy(St)
cerebro.run()

Key points of the above code:

The 1-minute line triggers next, but in next, the length of len(self.data) remains unchanged within 1 hour from the hour.

2 In next, self.data.close[0], within one hour from the hour, is constantly changing as the minute line comes in (equal to the closing price of the latest minute line)

3 In next, self.data.close[-1] obtains the closing price of the last full hour, not the closing price of the previous minute.

The above functions are difficult or even impossible to achieve in other backtesting frameworks.

Guess you like

Origin blog.csdn.net/qtbgo/article/details/114656654