量化交易学习(四十二)backtrader文档——定时器

今天这篇是backtrader文档的学习笔记。主要介绍了定时器。

官方文档链接:https://www.backtrader.com/docu/timers/timers/

定时器

定时器允许在给定时间点回调notify_timer(在CerebroStrategy中可用),并具有细粒度的最终用户控制。

功能

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

  • 时区规范,无论是直接的还是通过pytz兼容对象或通过数据源的结束时间

  • 相对于指定时间的起始偏移量

  • 重复间隔

  • 工作日过滤器(带结转选项)

  • 月份过滤器(带结转选项)

  • 自定义回调过滤器

使用方式

在子类中Cerebro和Strategy子类中,计时器回调都将在以下方法中接收。

1
2
3
4
5
6
7
8
9
def notify_timer(self, timer, when, *args, **kwargs):
'''Receives a timer notification where ``timer`` is the timer which was
returned by ``add_timer``, and ``when`` is the calling time. ``args``
and ``kwargs`` are any additional arguments passed to ``add_timer``

The actual ``when`` time can be later, but the system may have not be
able to call the timer before. This value is the timer value and not the
system time.
'''

通过策略添加计时器

通过方法实现

1
2
3
4
5
6
7
8
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 ,还会通知系统中运行的所有策略。

1
2
3
4
5
6
7
8
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实例。

定时器什么时候被调用

If cheat=False

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

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

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

  • 在重新计算指标之前(因为这是由策略触发的)

  • 在调用任何策略的next方法之前

If cheat=True

在以下情况,将调用计时器:

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

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

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

例如,可以使用每日柱线来实现以下场景:

  • 在经纪商使用新柱之前,调用计时器

  • 指标具有前一天收盘价,可用于生成入场/出场信号(或者在上次执行next期间可能已设置标志)

  • 因为新的价格是可用的,所以可以用开盘价来计算股份。这假设一个人通过观察开盘竞价可以很好地了解开盘情况。

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实例。
    • Nonewhen按表面值进行解释(这意味着将其视为 UTC,即使它不是)
    • pytz实例:when将被解释为指定时区的本地时间。
    • data feed实例:when将被解释为在数据源实例的tz参数指定的时区。

注意!!!

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

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

  • cheat(默认False)如果为True,经纪商有机会执行订单之前调用计时器。这提供了根据开盘价发出订单的机会,例如在交易时段开始之前

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

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

附加场景

计时器允许通过传递天数列表(遵循 iso 规范的整数,其中 Mon=1 且 Sun=7)来指定必须执行的日期,如下所示

  • weekdays=[5] 定时器仅在周五有效

如果周五是非交易日并且计时器应在下一个交易日启动,则可以添加weekcarry=True

与此类似,人们可以决定在每月 15 日启动:

  • monthdays=[15]

如果 15号恰巧是非交易日,并且计时器应该在下一个交易日启动,则可以添加monthcarry=True

对于以下情况没有具体的实现方案:3 月、6 月、9 月和 12 月的第 3个星期五(期货/期权到期),但可以通过以下方式实施规则:

  • allow=callable允许可调用对象接受datetime.date实例。请注意,这不是一个datetime.datetime实例,因为允许可调用仅用于决定给定的一天是否适合定时器。

要实施类似上面列出的规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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.friday == 3: # 3rd Friday
return True # timer allowed

return False # timer disallowed

然后我们在创建计时器时设置allow=FutOpeExp()

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

江达小记