计时器#

backtrader 中,版本 1.9.44.116 新增了 计时器 这个功能,这个功能可以在指定的时间点通过 notify_timer (在 CerebroStrategy 中可用)回调,并提供了细粒度的终端用户控制。

注意

1.9.46.116 中进行了一些修正

选项#

  • 基于绝对时间输入的计时器,或者基于会话的开始/结束时间

  • 时间规范的时区指定,可以直接指定或使用 pytz 兼容对象或数据源会话结束时间

  • 与指定时间的起始偏移量

  • 重复间隔

  • 周日筛选器(带有传递选项)

  • 月日筛选器(带有传递选项) - 自定义回调过滤器

使用模式#

CerebroStrategy 子类中,计时器回调将会在以下方法中接收到。

def notify_timer(self, timer, when, *args, **kwargs):
    '''接收一个计时器通知,其中 ``timer`` 是由 ``add_timer`` 返回的计时器,
    ``when`` 是调用时间。 ``args`` 和 ``kwargs`` 是传递给 ``add_timer`` 的
    任何附加参数。

    实际的 ``when`` 时间可能稍晚,但系统可能未能在此之前调用计时器。该值是计时器的
    时间值而不是系统时间。
    '''

添加计时器 - 通过策略#

使用以下方法进行操作

def add_timer(self, when,
              offset=datetime.timedelta(), repeat=datetime.timedelta(),
              weekdays=[], weekcarry=False,
              monthdays=[], monthcarry=True,
              allow=None,
              tzdata=None, cheat=False,
              *args, **kwargs):
    '''

它返回一个创建的 Timer 实例。

请参阅下文以了解参数的解释。添加计时器 - 通过Cerebro#

使用相同的方法并添加了参数 strats 。如果设置为 True ,计时器不仅会通知 cerebro ,还会通知系统中所有运行的策略。

def add_timer(self, when,
              offset=datetime.timedelta(), repeat=datetime.timedelta(),
              weekdays=[], weekcarry=False,
              monthdays=[], monthcarry=True,
              allow=None,
              tzdata=None, cheat=False, strats=False,
              *args, **kwargs):
    '''

它返回创建的 Timer 实例。

计时器何时被调用#

如果 cheat=False#

这是默认设置。在这种情况下,计时器将在以下情况下被调用:

  • 在数据源加载当前柱的新值之后

  • 在经纪人评估订单并重新计算投资组合价值之后

  • 在指标重新计算之前(因为这是由策略触发的) - 在调用任何策略的 next 方法之前

如果 cheat=True#

在这种情况下会调用一个计时器:

  • 在数据源加载了当前K线的新值之后

  • 在经纪人评估订单并重新计算投资组合价值之前

  • 因此,在指标重新计算和调用任何策略的 next 方法之前

这允许例如以下日线K线的情景:

  • 在经纪人评估新的K线之前,计时器被调用

  • 指标在前一天的收盘价上有值,并可以用于生成入场/出场信号(或在上次“next”评估时可能已设置标志)

  • 由于新的价格可用,可以使用开盘价计算头寸。这假设例如通过观察开盘竞价可以获得关于开盘价的良好指示。使用日线进行回测


示例 scheduled.py 默认使用 backtrader 分发的标准日线进行回测。策略的参数如下:

class St(bt.Strategy):
    params = dict(
        when=bt.timer.SESSION_START,
        timer=True,
        cheat=False,
        offset=datetime.timedelta(),
        repeat=datetime.timedelta(),
        weekdays=[],
    )

数据的交易时间如下:

  • 开市时间:09:00

  • 收市时间:17:30

只使用特定时间进行回测

$ ./scheduled.py --strat when='datetime.time(15,30)'

strategy notify_timer with tid 0, when 2005-01-03 15:30:00 cheat False
1, 2005-01-03 17:30:00, 第1周, 第1天, 开盘价 2952.29, 最高价 2989.61, 最低价 2946.8, 收盘价 2970.02
strategy notify_timer with tid 0, when 2005-01-04 15:30:00 cheat False
2, 2005-01-04 17:30:00, 第1周, 第2天, 开盘价 2969.78, 最高价 2979.88, 最低价 2961.14, 收盘价 2971.12
strategy notify_timer with tid 0, when 2005-01-05 15:30:00 cheat False
3, 2005-01-05 17:30:00, 第1周, 第3天, 开盘价 2969.0, 最高价 2969.0, 最低价 2942.69, 收盘价 2947.19
strategy notify_timer with tid 0, when 2005-01-06 15:30:00 cheat False
...

如上所述,定时器在 15:30 开始计时。没有什么意外。现在我们加上一个偏移量,为30分钟。

$ ./scheduled.py --strat when='datetime.time(15,30)',offset='datetime.timedelta(minutes=30)'策略 notify_timer,tid 0,在 2005-01-03 16:00:00 时触发,作弊为 False

1,2005-01-03 17:30:00,第一周,第一天,O 2952.29,H 2989.61,L 2946.8,C 2970.02 策略 notify_timer,tid 0,在 2005-01-04 16:00:00 时触发,作弊为 False 2,2005-01-04 17:30:00,第一周,第二天,O 2969.78,H 2979.88,L 2961.14,C 2971.12 策略 notify_timer,tid 0,在 2005-01-05 16:00:00 时触发,作弊为 False …

并且计时器的时间从 15:30 改为 16:00 。没有意外。让我们尝试相同的操作,但是使用会话开始时间作为参照。

$ ./scheduled.py --strat when='bt.timer.SESSION_START',offset='datetime.timedelta(minutes=30)'

策略 notify_timer,tid 0,在 2005-01-03 09:30:00 时触发,作弊为 False
1,2005-01-03 17:30:00,第一周,第一天,O 2952.29,H 2989.61,L 2946.8,C 2970.02
策略 notify_timer,tid 0,在 2005-01-04 09:30:00 时触发,作弊为 False
2,2005-01-04 17:30:00,第一周,第二天,O 2969.78,H 2979.88,L 2961.14,C 2971.12
...

Et voilá! 回调函数被调用的时间是 09:30 。而会话开始时间,如上所述,是 09:00 。这使得可以简单地说,希望在会话开始后 30分钟 执行一个动作。

让我们加上重复:

$ ./scheduled.py --strat when='bt.timer.SESSION_START',offset='datetime.timedelta(minutes=30)',repeat='datetime.timedelta(minutes=30)'

策略 notify_timer,tid 0,在 2005-01-03 09:30:00 时触发,作弊为 False
1,2005-01-03 17:30:00,第一周,第一天,O 2952.29,H 2989.61,L 2946.8,C 2970.02
策略 notify_timer,tid 0,在 2005-01-04 09:30:00 时触发,作弊为 False
2,2005-01-04 17:30:00,第一周,第二天,O 2969.78,H 2979.88,L 2961.14,C 2971.12
策略 notify_timer,tid 0,在 2005-01-05 09:30:00 时触发,作弊为 False
...

没有重复发生 。原因是价格的分辨率是每天一次。计时器在 09:30 第一次调用,与之前的示例相同。但当系统获取下一批价格时,它们发生在下一天。计时器只能调用一次,这是显而易见的。所以需要更低的分辨率。

但在降低分辨率之前,让我们通过在会话结束之前调用计时器来作弊。 ::```

$ ./scheduled.py –strat when=’bt.timer.SESSION_START’,cheat=True

strategy notify_timer with tid 1, when 2005-01-03 09:00:00 cheat True – 2005-01-03 创建购买订单 strategy notify_timer with tid 0, when 2005-01-03 09:00:00 cheat False 1, 2005-01-03 17:30:00, 第一周,第一天,开盘价 2952.29,最高价 2989.61,最低价 2946.8,收盘价 2970.02 strategy notify_timer with tid 1, when 2005-01-04 09:00:00 cheat True strategy notify_timer with tid 0, when 2005-01-04 09:00:00 cheat False – 2005-01-04 以 2969.78 的价格执行购买交易 2, 2005-01-04 17:30:00, 第一周,第二天,开盘价 2969.78,最高价 2979.88,最低价 2961.14,收盘价 2971.12 strategy notify_timer with tid 1, when 2005-01-05 09:00:00 cheat True strategy notify_timer with tid 0, when 2005-01-05 09:00:00 cheat False …

```

该策略添加了一个 cheat=True 的第二个计时器。这是第二个添加的计时器,因此将获得第二个计时器ID( timer id ),即 1 (请参考上面的示例,分配的计时器ID为 0 )。

而且,由于这个计时器在系统中许多事件发生之前进行“作弊检测”,它会在其他计时器之前被调用(请参考上面的解释)。

由于价格的*每日*分辨率不会有太大的差异,除了:

  • 该策略还会在开盘前下达订单……并且下一个交易日将与开盘价匹配

    即使通过在开盘前行动进行作弊,这仍然是正常的行为,因为在经纪人中也没有激活 开盘作弊(cheating-on-open)

同样,对于经纪人,我们使用 coo=True 的命令: ```

$ ./scheduled.py –strat when=’bt.timer.SESSION_START’,cheat=True –broker coo=True

strategy notify_timer with tid 1, when 2005-01-03 09:00:00 cheat True – 2005-01-03 创建购买订单 strategy notify_timer with tid 0, when 2005-01-03 09:00:00 cheat False – 2005-01-03 以 2952.29 的价格执行购买交易 1, 2005-01-03 17:30:00, 第一周,第一天,开盘价 2952.29,最高价 2989.61,最低价 2946.8,收盘价 2970.02 strategy notify_timer with tid 1, when 2005-01-04 09:00:00 cheat True strategy notify_timer with tid 0, when 2005-01-04 09:00:00 cheat False 2, 2005-01-04 17:30:00, 第一周,第二天,开盘价 2969.78,最高价 2979.88,最低价 2961.14,收盘价 2971.12 strategy notify_timer with tid 1, when 2005-01-05 09:00:00 cheat True strategy notify_timer with tid 0, when 2005-01-05 09:00:00 cheat False …

```还有一些变化。

  • 在作弊计时器上发布的命令是于 2005-01-03 生效的

  • 在以开盘价执行的命令是于 2005-01-03 生效的

    实际上,就好像在市场真正开盘前几秒钟做了开盘竞价价格一样。

运行5分钟间隔的数据#

示例 scheduled-min.py 默认以 backtrader 发行版中提供的标准5分钟间隔数据运行。策略参数扩展了 monthdayscarry 选项

class St(bt.Strategy):
    params = dict(
        when=bt.timer.SESSION_START,
        timer=True,
        cheat=False,
        offset=datetime.timedelta(),
        repeat=datetime.timedelta(),
        weekdays=[],
        weekcarry=False,
        monthdays=[],
        monthcarry=True,
    )

数据具有相同的交易时间:

  • 开始时间:09:00

  • 结束时间:17:30

让我们做一些实验。首先是一个单一的计时器。 :: $ ./scheduled-min.py --strat when='datetime.time(15, 30)'

1, 2006-01-02 09:05:00, 第 1 周, 第 1 天, 开盘价 3578.73, 最高价 3587.88, 最低价 3578.73, 收盘价 3582.99 2, 2006-01-02 09:10:00, 第 1 周, 第 1 天, 开盘价 3583.01, 最高价 3588.4, 最低价 3583.01, 收盘价 3588.03 … 77, 2006-01-02 15:25:00, 第 1 周, 第 1 天, 开盘价 3599.07, 最高价 3599.68, 最低价 3598.47, 收盘价 3599.68 策略通知计时器,ID 0,在 2006-01-02 15:30:00,作弊模式 没有 78, 2006-01-02 15:30:00, 第 1 周, 第 1 天, 开盘价 3599.64, 最高价 3599.73, 最低价 3599.0, 收盘价 3599.67 … 179, 2006-01-03 15:25:00, 第 1 周, 第 2 天, 开盘价 3634.72, 最高价 3635.0, 最低价 3634.06, 收盘价 3634.87 策略通知计时器,ID 0,在 2006-01-03 15:30:00,作弊模式 没有 180, 2006-01-03 15:30:00, 第 1 周, 第 2 天, 开盘价 3634.81, 最高价 3634.89, 最低价 3634.04, 收盘价 3634.23 …

计时器按照要求在 15:30 开始执行。日志显示了它在前两天期间执行的情况。

15分钟 的重复加入到混合中

$ ./scheduled-min.py --strat when='datetime.time(15, 30)',repeat='datetime.timedelta(minutes=15)'

...
74, 2006-01-02 15:10:00, 第 1 周, 第 1 天, 开盘价 3596.12, 最高价 3596.63, 最低价 3595.92, 收盘价 3596.63
75, 2006-01-02 15:15:00, 第 1 周, 第 1 天, 开盘价 3596.36, 最高价 3596.65, 最低价 3596.19, 收盘价 3596.65
76, 2006-01-02 15:20:00, 第 1 周, 第 1 天, 开盘价 3596.53, 最高价 3599.13, 最低价 3596.12, 收盘价 3598.9
77, 2006-01-02 15:25:00, 第 1 周, 第 1 天, 开盘价 3599.07, 最高价 3599.68, 最低价 3598.47, 收盘价 3599.68
策略通知计时器,ID 0,在 2006-01-02 15:30:00,作弊模式 没有
78, 2006-01-02 15:30:00, 第 1 周, 第 1 天, 开盘价 3599.64, 最高价 3599.73, 最低价 3599.0, 收盘价 3599.67
79, 2006-01-02 15:35:00, 第 1 周, 第 1 天, 开盘价 3599.61, 最高价 3600.29, 最低价 3599.52, 收盘价 3599.92
80, 2006-01-02 15:40:00, 第 1 周, 第 1 天, 开盘价 3599.96, 最高价 3602.06, 最低价 3599.76, 收盘价 3602.05
策略通知计时器,ID 0,在 2006-01-02 15:45:00,作弊模式 没有
81, 2006-01-02 15:45:00, 第 1 周, 第 1 天, 开盘价 3601.97, 最高价 3602.07, 最低价 3601.45, 收盘价 3601.83
82, 2006-01-02 15:50:00, 第 1 周, 第 1 天, 开盘价 3601.74, 最高价 3602.8, 最低价 3601.63, 收盘价 3602.8
83, 2006-01-02 15:55:00, 第 1 周, 第 1 天, 开盘价 3602.53, 最高价 3602.74, 最低价 3602.33, 收盘价 3602.61
策略通知计时器,ID 0,在 2006-01-02 16:00:00,作弊模式 没有
84, 2006-01-02 16:00:00, 第 1 周, 第 1 天, 开盘价 3602.58, 最高价 3602.75, 最低价 3601.81, 收盘价 3602.14
85, 2006-01-02 16:05:00, 第 1 周, 第 1 天, 开盘价 3602.16, 最高价 3602.16, 最低价 3600.86, 收盘价 3600.96
86, 2006-01-02 16:10:00, 第 1 周, 第 1 天, 开盘价 3601.2, 最高价 3601.49, 最低价 3600.94, 收盘价 3601.27
...
策略通知计时器,ID 0,在 2006-01-02 17:15:00,作弊模式 没有
99, 2006-01-02 17:15:00, 第 1 周, 第 1 天, 开盘价 3603.96, 最高价 3603.96, 最低价 3602.89, 收盘价 3603.79
100, 2006-01-02 17:20:00, 第 1 周, 第 1 天, 开盘价 3603.94, 最高价 3605.95, 最低价 3603.87, 收盘价 3603.91
101, 2006订单的创建时间是 ``09:05:00`` ,执行时间是 ``09:10:00`` ,因为经纪人不处于 *cheat-on-open* 模式。让我们设置它...
$ ./scheduled-min.py --strat when='bt.timer.SESSION_START',cheat=True --broker coo=True

strategy notify_timer with tid 1, when 2006-01-02 09:00:00 cheat True
-- 2006-01-02 09:05:00 创建买入订单
strategy notify_timer with tid 0, when 2006-01-02 09:00:00 cheat False
-- 2006-01-02 09:05:00 买入执行 @ 3578.73
1, 2006-01-02 09:05:00, 第一周, 第一天, 开盘价 3578.73, 最高价 3587.88, 最低价 3578.73, 收盘价 3582.99
2, 2006-01-02 09:10:00, 第一周, 第一天, 开盘价 3583.01, 最高价 3588.4, 最低价 3583.01, 收盘价 3588.03
...

发行时间和执行时间都是 09:05:00 ,执行价是 09:05:00 的开盘价。

附加场景#

通过传递一个包含星期几的列表(按照 ISO 规范,星期一为 1,星期日为 7)来指定定时器在哪些天执行,例如:

  • weekdays=[5] 表示定时器仅在星期五有效。

如果星期五不是交易日,定时器应在下一个交易日开始计时,可以添加 weekcarry=True

类似地,可以决定在每个月的第15天采取行动,例如:

  • monthdays=[15] 如果第15天恰好是非交易日,计时器应该在下一个交易日启动,可以添加 monthcarry=True

对于类似于“三月、六月、九月和十二月的第三个星期五”(期货/期权到期日)这样的事物,还没有实现,但可以通过传递以下规则来实现:

  • 传入 allow=callable ,其中可调用的函数接受 datetime.date 实例。请注意,这不是一个 datetime.datetime 实例,因为 allow 可调用函数只是为了决定某一天是否适合用作计时器。

要实现以上规则的功能,可以使用以下代码:

```python class FutOpExp(object):

def __init__(self):

self.fridays = 0 self.curmonth = -1

def __call__(self, d):

_, _, isowkday = d.isocalendar()

if d.month != self.curmonth:

self.curmonth = d.month self.fridays = 0

# Mon=1 … Sun=7 if isowkday == 5 and self.curmonth in [3, 6, 9, 12]:

self.fridays += 1

if self.fridays == 3: # 第三个星期五

return True # 允许计时器

return False # 不允许计时器

```创建计时器时,可以将``allow=FutOpeExp()``传递给它的参数。

这将允许在那些月份的第三个星期五启动计时器,并且可能在期货到期前平仓。

add_timer 的参数#

  • when :可以是

    • datetime.time 实例(参见下面的 tzdata

    • bt.timer.SESSION_START 用于引用会话开始

    • bt.timer.SESSION_END 用于引用会话结束

  • offset 必须是一个 datetime.timedelta 实例

    用于对 when 的值进行偏移。它在与 SESSION_STARTSESSION_END 组合使用时具有有意义的用途,可以表示会话开始后的 15分钟 调用计时器。

  • repeat 必须是一个 datetime.timedelta 实例

    指示在第一次调用后,进一步的调用是否将在同一会话中按预定的 repeat 时间间隔进行安排。

    一旦计时器超过会话结束,它将重置为 when 的初始值。 - weekdays :一个 有序 的可迭代对象,其中的整数表示计时器可以在哪些日子(ISO代码,星期一为1,星期日为7)实际被调用

    如果没有指定,计时器将在所有日子上都有效

  • weekcarry (默认为 False )。如果为 True ,并且未观察到工作日(如交易假期),则计时器将在下一个工作日执行(即使是新的一周)

  • monthdays : 一个 有序 的可迭代对象,其中的整数表示计时器必须在每个月的哪些日子执行。例如,始终在每个月的第15天执行

    如果没有指定,计时器将在所有日子上都有效

  • monthcarry (默认为 True )。如果未观察到该天(周末,交易假期),则计时器将在下一个可用日执行。

  • allow (默认为 None )。一个回调函数,接收一个 datetime.date 实例,并在该日期允许计时器时返回 True ,否则返回 False

  • tzdata 可以是 None (默认值), pytz 实例或 data feed 实例。

    None : when 按面值解释(即使它的时区不是UTC,也会将其视为UTC)

    pytz 实例: when 将被解释为由时区实例指定的本地时间。“data feed” 实例: when 将被解释为数据源实例的 tz 参数指定的本地时间。

注意 :如果 whenSESSION_STARTSESSION_END 并且 tzdataNone ,则系统中的第一个 数据源 (即 self.data0 )将被用作查找会话时间的参考。

  • strats (默认值: False )同时调用策略的 notify_timer

  • cheat (默认值: False )如果为 True ,则计时器将在经纪人有机会评估订单之前被调用。这为在会话开始之前基于开盘价发出订单提供了机会。

  • *args :任何额外的 args 将传递给 notify_timer

  • **kwargs :任何额外的 kwargs 将传递给 notify_timer

示例用法 scheduled.py#

$ ./scheduled.py --help
用法:scheduled.py [-h] [--data0 DATA0] [--fromdate FROMDATE]
                  [--todate TODATE] [--cerebro kwargs] [--broker kwargs]
                  [--sizer kwargs] [--strat kwargs] [--plot [kwargs]]

样例框架

可选参数:
  -h, --help           显示帮助信息并退出
  --data0 DATA0        要读取的数据(默认值:../../datas/2005-2006-day-001.txt)
  --fromdate FROMDATE  以YYYY-MM-DD[THH:MM:SS]格式的日期[时间](默认值:)
  --todate TODATE      以YYYY-MM-DD[THH:MM:SS]格式的日期[时间](默认值:)
  --cerebro kwargs     以 key=value 格式的 kwargs(默认值:)
  --broker kwargs      以 key=value 格式的 kwargs(默认值:)
  --sizer kwargs       以 key=value 格式的 kwargs(默认值:)
  --strat kwargs       以 key=value 格式的 kwargs(默认值:)
  --plot [kwargs]      以 key=value 格式的 kwargs(默认值:)

示例用法 scheduled-min.py#

:: $ ./scheduled-min.py –help
用法: scheduled-min.py [-h] [–data0 DATA0] [–fromdate FROMDATE]

[–todate TODATE] [–cerebro kwargs] [–broker kwargs] [–sizer kwargs] [–strat kwargs] [–plot [kwargs]]

定时器测试日内

可选参数:
-h, --help

显示帮助信息并退出

--data0 DATA0

要读取的数据 (默认: ../../datas/2006-min-005.txt)

--fromdate FROMDATE

日期[时间],格式为YYYY-MM-DD[THH:MM:SS] (默认: )

--todate TODATE

日期[时间],格式为YYYY-MM-DD[THH:MM:SS] (默认: )

--cerebro kwargs

以key=value格式的kwargs (默认: )

--broker kwargs

以key=value格式的kwargs (默认: )

--sizer kwargs

以key=value格式的kwargs (默认: )

--strat kwargs

以key=value格式的kwargs (默认: )

–plot [kwargs] 以key=value格式的kwargs (默认: )

样例源码 scheduled.py#

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import argparse
import datetime

import backtrader as bt


class St(bt.Strategy):
    params = dict(
        when=bt.timer.SESSION_START,
        timer=True,
        cheat=False,
        offset=datetime.timedelta(),
        repeat=datetime.timedelta(),
        weekdays=[],
    )

    def __init__(self):
        bt.ind.SMA()
        if self.p.timer:
            self.add_timer(
                when=self.p.when,
                offset=self.p.offset,
                repeat=self.p.repeat,
                weekdays=self.p.weekdays,
            )
        if self.p.cheat:
            self.add_timer(
                when=self.p.when,
                offset=self.p.offset,
                repeat=self.p.repeat,
                cheat=True,
            )

        self.order = None

    def prenext(self):
        self.next()

    def next(self):
        _, isowk, isowkday = self.datetime.date().isocalendar()
        txt = '{}, {}, Week {}, Day {}, O {}, H {}, L {}, C {}'.format(
            len(self), self.datetime.datetime(),
            isowk, isowkday,
            self.data.open[0], self.data.high[0],
            self.data.low[0], self.data.close[0])

        print(txt)

    def notify_timer(self, timer, when, *args, **kwargs):
        print('strategy notify_timer with tid {}, when {} cheat {}'.
              format(timer.p.tid, when, timer.p.cheat))

        if self.order is None and timer.p.cheat:
            print('-- {} Create buy order'.format(self.data.datetime.date()))
            self.order = self.buy()

    def notify_order(self, order):
        if order.status == order.Completed:
            print('-- {} Buy Exec @ {}'.format(
                self.data.datetime.date(), order.executed.price))


def runstrat(args=None):
    args = parse_args(args)

    cerebro = bt.Cerebro()

    # Data feed kwargs
    kwargs = dict(
        timeframe=bt.TimeFrame.Days,
        compression=1,
        sessionstart=datetime.time(9, 0),
        sessionend=datetime.time(17, 30),
    )

    # Parse from/to-date
    dtfmt, tmfmt = '%Y-%m-%d', 'T%H:%M:%S'
    for a, d in ((getattr(args, x), x) for x in ['fromdate', 'todate']):
        if a:
            strpfmt = dtfmt + tmfmt * ('T' in a)
            kwargs[d] = datetime.datetime.strptime(a, strpfmt)

    # Data feed
    data0 = bt.feeds.BacktraderCSVData(dataname=args.data0, **kwargs)
    cerebro.adddata(data0)

    # Broker
    cerebro.broker = bt.brokers.BackBroker(**eval('dict(' + args.broker + ')'))

    # Sizer
    cerebro.addsizer(bt.sizers.FixedSize, **eval('dict(' + args.sizer + ')'))

    # Strategy
    cerebro.addstrategy(St, **eval('dict(' + args.strat + ')'))

    # Execute
    cerebro.run(**eval('dict(' + args.cerebro + ')'))

    if args.plot:  # Plot if requested to
        cerebro.plot(**eval('dict(' + args.plot + ')'))


def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description=(
            'Sample Skeleton'
        )
    )

    parser.add_argument('--data0', default='../../datas/2005-2006-day-001.txt',
                        required=False, help='Data to read in')

    # Defaults for dates
    parser.add_argument('--fromdate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--todate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--cerebro', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--broker', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--sizer', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--strat', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--plot', required=False, default='',
                        nargs='?', const='{}',
                        metavar='kwargs', help='kwargs in key=value format')

    return parser.parse_args(pargs)


if __name__ == '__main__':
    runstrat()

样例源码 scheduled-min.py#

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import argparse
import datetime

import backtrader as bt


class St(bt.Strategy):
    params = dict(
        when=bt.timer.SESSION_START,
        timer=True,
        cheat=False,
        offset=datetime.timedelta(),
        repeat=datetime.timedelta(),
        weekdays=[],
        weekcarry=False,
        monthdays=[],
        monthcarry=True,
    )

    def __init__(self):
        bt.ind.SMA()
        if self.p.timer:
            self.add_timer(
                when=self.p.when,
                offset=self.p.offset,
                repeat=self.p.repeat,
                weekdays=self.p.weekdays,
                weekcarry=self.p.weekcarry,
                monthdays=self.p.monthdays,
                monthcarry=self.p.monthcarry,
                # tzdata=self.data0,
            )
        if self.p.cheat:
            self.add_timer(
                when=self.p.when,
                offset=self.p.offset,
                repeat=self.p.repeat,
                weekdays=self.p.weekdays,
                weekcarry=self.p.weekcarry,
                monthdays=self.p.monthdays,
                monthcarry=self.p.monthcarry,
                # tzdata=self.data0,
                cheat=True,
            )

        self.order = None

    def prenext(self):
        self.next()

    def next(self):
        _, isowk, isowkday = self.datetime.date().isocalendar()
        txt = '{}, {}, Week {}, Day {}, O {}, H {}, L {}, C {}'.format(
            len(self), self.datetime.datetime(),
            isowk, isowkday,
            self.data.open[0], self.data.high[0],
            self.data.low[0], self.data.close[0])

        print(txt)

    def notify_timer(self, timer, when, *args, **kwargs):
        print('strategy notify_timer with tid {}, when {} cheat {}'.
              format(timer.p.tid, when, timer.p.cheat))

        if self.order is None and timer.params.cheat:
            print('-- {} Create buy order'.format(
                self.data.datetime.datetime()))
            self.order = self.buy()

    def notify_order(self, order):
        if order.status == order.Completed:
            print('-- {} Buy Exec @ {}'.format(
                self.data.datetime.datetime(), order.executed.price))


def runstrat(args=None):
    args = parse_args(args)
    cerebro = bt.Cerebro()

    # Data feed kwargs
    kwargs = dict(
        timeframe=bt.TimeFrame.Minutes,
        compression=5,
        sessionstart=datetime.time(9, 0),
        sessionend=datetime.time(17, 30),
    )

    # Parse from/to-date
    dtfmt, tmfmt = '%Y-%m-%d', 'T%H:%M:%S'
    for a, d in ((getattr(args, x), x) for x in ['fromdate', 'todate']):
        if a:
            strpfmt = dtfmt + tmfmt * ('T' in a)
            kwargs[d] = datetime.datetime.strptime(a, strpfmt)

    # Data feed
    data0 = bt.feeds.BacktraderCSVData(dataname=args.data0, **kwargs)
    cerebro.adddata(data0)

    # Broker
    cerebro.broker = bt.brokers.BackBroker(**eval('dict(' + args.broker + ')'))

    # Sizer
    cerebro.addsizer(bt.sizers.FixedSize, **eval('dict(' + args.sizer + ')'))

    # Strategy
    cerebro.addstrategy(St, **eval('dict(' + args.strat + ')'))

    # Execute
    cerebro.run(**eval('dict(' + args.cerebro + ')'))

    if args.plot:  # Plot if requested to
        cerebro.plot(**eval('dict(' + args.plot + ')'))


def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description=(
            'Timer Test Intraday'
        )
    )

    parser.add_argument('--data0', default='../../datas/2006-min-005.txt',
                        required=False, help='Data to read in')

    # Defaults for dates
    parser.add_argument('--fromdate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--todate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--cerebro', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--broker', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--sizer', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--strat', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--plot', required=False, default='',
                        nargs='?', const='{}',
                        metavar='kwargs', help='kwargs in key=value format')

    return parser.parse_args(pargs)


if __name__ == '__main__':
    runstrat()