多时间框架

多时间框架策略 #

在实际的交易中,我们常需要结合多个时间框架来制定投资决策,如在周级别评估趋势,而在日级别执行入场,或是基于 5 分钟与 60 分钟数据的对比执行交易。在 Backtrader 中要实现这个目标,需要将不同时间框架的数据组合在一起。

本节将介绍如何在 Backtrader 实现多周期交易策略。

基本规则 #

Backtrader 原生支持多时间框架的数据组合,只需遵循几个简单的规则。

第一步,最小时间框架的数据必须首先加载。较小时间框架(条数最多的数据)应当首先加载到Cerebro实例中。

第二步,数据必须按日期时间对齐。为了让平台能够正确解析数据并执行策略,必须保证各时间框架的数据时间对齐。

第三步,使用 resampledata 实现较大时间框架的重采样cerebro.resample 函数能轻松地将较大的时间框架数据添加到策略中。

在这个基础上,就可以在较短和较长时间框架上使用不同的技术指标。要注意,应用于大时间框架的指标产生的信号较少,还有,Backtrader 会考虑大时间框架的最小周期,以确保数据的准确性。

示例:如何使用多个时间框架 #

如何在 Backtrader 实现多时间周期呢?大概演示这个步骤吧。

加载数据 #

首先,加载较小时间框架的数据。

data = btfeeds.BacktraderCSVData(dataname=datapath)

将数据添加到Cerebro #

将较小时间框架数据都添加到 Cerebro 实例中。

cerebro.adddata(data)

重采样数据 #

使用 cerebro.resampledata 将数据重采样到较大的时间框架。

cerebro.resampledata(data, timeframe=tframes[args.timeframe], compression=args.compression)

运行策略 #

执行策略并生成结果。

cerebro.run()

示例 #

首先,演示每日和每周时间框架。假设我们希望在一个策略中同时使用每日和每周的时间框架。通过命令行指定时间框架为每周,并进行数据重采样:

$ ./multitimeframe-example.py --timeframe weekly --compression 1

此时,程序会加载每日数据,并将其重采样为每周数据。最终输出将包括每周和每日数据的合成图表。

继续用每日时间框架压缩。如果我们希望将每日数据压缩为每两天一条数据,可以使用以下命令:

$ ./multitimeframe-example.py --timeframe daily --compression 2

此时,Backtrader会将每日数据压缩为每两天一条数据,并生成合成图表。

还可以带简单移动平均(SMA)指标。为了展示不同时间框架对策略的影响,可以在策略中使用简单的移动平均线(SMA)指标。SMA将在较小和较大时间框架上应用,并根据它们产生不同的信号。

  • 在较小的时间框架(如每日)上,SMA将在第10个数据点后首次计算出值。
  • 在较大的时间框架(如每周)上,SMA的计算会延迟,可能需要10个周期的时间来产生有效信号。

由于Backtrader的多时间框架支持,较大时间框架会消耗多个较小时间框架的数据条目来计算指标。

在策略中使用 SMA 时,如果数据点来自较大时间框架,nextstart 方法的调用可能会有所延迟。例如,在每周时间框架下,SMA的计算需要10周的数据,而在每个周期内,我们将看到多个“nextstart”调用,因为Backtrader会等待所有数据都齐全时才开始执行策略逻辑。

代码示例 #

# 导入必要的库
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import argparse
import backtrader as bt
import backtrader.feeds as btfeeds
import backtrader.indicators as btind

# 创建SMA策略
class SMAStrategy(bt.Strategy):
    params = (
        ('period', 10),  # SMA的周期
        ('onlydaily', False),  # 是否只在每日时间框架上应用
    )

    def __init__(self):
        # 为较小时间框架添加SMA
        self.sma_small_tf = btind.SMA(self.data, period=self.p.period)
        
        # 如果选择不只应用于每日时间框架
        if not self.p.onlydaily:
            # 为较大时间框架(如每周)添加SMA
            self.sma_large_tf = btind.SMA(self.data1, period=self.p.period)

    # nextstart方法,用于输出调试信息
    def nextstart(self):
        print('--------------------------------------------------')
        print('nextstart called with len', len(self))
        print('--------------------------------------------------')
        super(SMAStrategy, self).nextstart()

# 运行策略
def runstrat():
    args = parse_args()

    # 创建Cerebro实例
    cerebro = bt.Cerebro(stdstats=False)

    # 根据用户选择的策略参数加载相应策略
    if not args.indicators:
        cerebro.addstrategy(bt.Strategy)
    else:
        cerebro.addstrategy(SMAStrategy, period=args.period, onlydaily=args.onlydaily)

    # 加载数据文件
    datapath = args.dataname or '../../datas/2006-day-001.txt'
    data = btfeeds.BacktraderCSVData(dataname=datapath)
    cerebro.adddata(data)  # 添加较小时间框架的数据

    tframes = dict(daily=bt.TimeFrame.Days, weekly=bt.TimeFrame.Weeks, monthly=bt.TimeFrame.Months)

    # 根据需要重采样数据到较大时间框架
    if args.noresample:
        datapath = args.dataname2 or '../../datas/2006-week-001.txt'
        data2 = btfeeds.BacktraderCSVData(dataname=datapath)
        cerebro.adddata(data2)
    else:
        cerebro.resampledata(data, timeframe=tframes[args.timeframe], compression=args.compression)

    # 执行策略并生成结果
    cerebro.run()

    # 绘制结果
    cerebro.plot(style='bar')

# 解析命令行参数
def parse_args():
    parser = argparse.ArgumentParser(description='Multitimeframe test')
    parser.add_argument('--dataname', default='', required=False, help='数据文件路径')
    parser.add_argument('--dataname2', default='', required=False, help='第二个数据文件路径')
    parser.add_argument('--noresample', action='store_true', help='不进行数据重采样')
    parser.add_argument('--timeframe', default='weekly', choices=['daily', 'weekly', 'monthly'], help='重采样时间框架')
    parser.add_argument('--compression', default=1, type=int, help='压缩数据条数')
    parser.add_argument('--indicators', action='store_true', help='是否使用带指标的策略')
    parser.add_argument('--onlydaily', action='store_true', help='仅在每日时间框架上应用指标')
    parser.add_argument('--period', default=10, type=int, help='指标周期')
    return parser.parse_args()

if __name__ == '__main__':
    runstrat()

结论 #

通过Backtrader的多时间框架支持,您可以轻松地将不同时间框架的数据结合在一起,从而实现更加灵活的交易策略。只需遵循上述规则,就能在多个时间框架中应用不同的指标,并根据数据的不同粒度调整策略的执行逻辑。

此外,Backtrader也允许通过nextstart方法来精确控制每个周期的数据处理逻辑,这使得您可以清晰地跟踪每个时间框架的计算过程,方便调试和优化。