过滤器
此功能是对backtrader的较新添加,必须安装到现有内部。这使得它不是如所希望的那样灵活和 100%功能齐全,但在许多情况下它仍然可以达到目的。
尽管该实现试图允许即插即用过滤器链接,但现有的内部结构使其难以确保始终能够实现。因此,一些过滤器可能被链接,而另一些过滤器可能没有链接。
意图
- 转换数据馈送提供的值,以传递不同的数据馈送
开始实现是为了简化两个明显过滤器的实现,这两个过滤器可以通过大脑API 直接使用。这些是:
- 
重采样 cerebro.resampledata这里,过滤器转换传入的数据馈送的 timeframe和compression。例如:py (Seconds, 1) -> (Days, 1)这意味着原始数据馈送是分辨率为1 秒的传送条。重采样过滤器截取数据并对其进行缓冲,直到它能够发送1 天条。当看到第二天的1 秒条时,就会发生这种情况。 
- 
回放( cerebro.replaydata)对于与上述相同的时间段,过滤器将使用1 秒分辨率条重建1 天条。 这意味着1 天条的发送次数与1 秒条的发送次数相同,更新后包含最新信息。 例如,这模拟了实际交易日的发展过程。 笔记 只要天没有改变,数据的长度 len(data)和策略的长度就保持不变。
过滤器在工作
给定现有数据源/数据源,您使用数据源的addfilter方法:
data = MyDataFeed(dataname=myname)
data.addfilter(filter, *args, **kwargs)
cerebro.addata(data) 
即使它恰好与重采样/重放过滤器兼容,也可以执行以下操作:
data = MyDataFeed(dataname=myname)
data.addfilter(filter, *args, **kwargs)
cerebro.replaydata(data) 
过滤器接口
filter必须符合给定的接口,即:
- 
接受此签名的可调用函数: py callable(data, *args, **kwargs)
或
- 
一个可以实例化和的类,称为 - 实例化时,__init__方法必须支持签名:
 py def __init__(self, data, *args, **kwargs)- __call__方法具有以下签名:
 py def __call__(self, data, *args, **kwargs)对于来自数据馈送的每个新传入值,都将调用该实例。 \*args和\*kwargs传递给__init__的内容相同返回值: ``py *True`: the inner data fetching loop of the data feed must retry fetching data from the feed, becaue the length of the stream was manipulated- Falseeven if data may have been edited (example: changed- closeprice), the length of the stream has remain untouched ```
 对于基于类的过滤器,可以实现 2 个附加方法 - last签字如下:
 py def last(self, data, *args, **kwargs)当数据馈送结束时,将调用此函数,允许筛选器传递其可能具有的数据,例如缓冲数据。典型的情况是重采样,因为在看到下一个时间段的数据之前,会缓冲一个条。当数据馈送结束时,没有新数据将缓冲数据推出。 last提供了将缓冲数据推出的机会。
- 实例化时,
笔记
很明显,如果过滤器完全不支持参数,并且添加时不支持任何参数,那么签名可以简化为:
def __init__(self, data, *args, **kwargs) -> def __init__(self, data) 
样品过滤器
非常快速的过滤器实现:
class SessionFilter(object):
    def __init__(self, data):
        pass
    def __call__(self, data):
        if data.p.sessionstart <= data.datetime.time() <= data.p.sessionend:
            # bar is in the session
            return False  # tell outer data loop the bar can be processed
        # bar outside of the regular session times
        data.backwards()  # remove bar from data stack
        return True  # tell outer data loop to fetch a new bar 
此筛选器:
- 
使用 data.p.sessionstart和data.p.sessionend(标准数据馈送参数)确定某个条是否在会话中。
- 
如果会话中的返回值为 False,表示没有做任何事情,当前条的处理可以继续
- 
如果不在会话中,则从流中删除该条, True返回,表示必须提取新条。笔记 data.backwards()使用LineBuffer接口。这深入挖掘了反向交易者的内部。
此过滤器的使用:
- 一些数据源包含超出正常交易时间的数据,交易员可能对此不感兴趣。使用此过滤器时,只考虑会话中的条。
用于过滤器的数据伪 API
在上面的示例中,已经展示了过滤器如何调用data.backwards()从流中删除当前条。来自数据馈送对象的有用调用是作为过滤器的伪 API 的:
- 
data.backwards(size=1, force=False):通过向后移动逻辑指针,从数据流中删除大小条(默认为1)。如果为force=True,则物理存储也会被移除。移除物理存储是一项微妙的操作,只意味着对内部操作的攻击。 
- 
data.forward(value=float('NaN'), size=1):向前移动大小栏位,根据需要增加物理存储量,并填充value
- 
data._addtostack(bar, stash=False):将bar添加到堆栈中,以便后续处理。bar是一个 iterable,包含的值与lines的数据馈送值一样多。如果 stash=False添加到堆栈中的条将在下一次迭代开始时立即被系统消耗。如果 stash=True条将经历整个循环处理,包括可能被过滤器重新解析
- 
data._save2stack(erase=False, force=False):将当前数据条保存到堆栈中,以便以后处理。如果是erase=True,则调用data.backwards并接收参数force
- 
data._updatebar(bar, forward=False, ago=0):使用 iterablebar中的值覆盖数据流ago位置中的值。默认情况下ago=0将更新当前栏。使用-1,前一个。
另一个例子:粉红鱼过滤器
这是一个可以链接到另一个过滤器(即重播过滤器)的过滤器示例。粉红鱼的名字来自图书馆,它在主页上描述了这一想法:使用每日数据执行操作,而这只能通过日内数据实现。
要达到这一效果:
- 
一根每日棒将被分成两部分: OHL和C。
- 
这两个片段通过重播链接,在流中发生以下事件: py With Len X -> OHL With Len X -> OHLC With Len X + 1 -> OHL With Len X + 1 -> OHLC With Len X + 2 -> OHL With Len X + 2 -> OHLC ...
逻辑:
- 
当收到一个 OHLC条时,它被复制到一个互动表中,并被分解成:- 
酒吧。因为这个概念实际上并不存在,收盘价被开盘价取代,真正形成一个 OHLO条。
- 
一个 C酒吧也不存在。事实是,它将像滴答声一样发出CCCC
- 
如果在两个部分之间分配,则为体积 
- 
当前条将从流中删除 
- 
OHLO部件被放在堆栈上以便立即处理
- 
CCCC部分放入仓库,以便下一轮加工
- 
因为堆栈具有即时处理功能,所以过滤器可以返回 False来指示它。
 
- 
此过滤器与以下部件一起工作:
- 重播过滤器,将OHLO和CCCC部分组合在一起,最终交付OHLC条。
用例:
- 如果今天的最大值是过去 20 个交易日中的最高值,则会发出一个Close指令,该指令会在第 2和个勾号的情况下执行。
守则:
class DaySplitter_Close(bt.with_metaclass(bt.MetaParams, object)):
    '''
 Splits a daily bar in two parts simulating 2 ticks which will be used to
 replay the data:
 - First tick: ``OHLX``
 The ``Close`` will be replaced by the *average* of ``Open``, ``High``
 and ``Low``
 The session opening time is used for this tick
 and
 - Second tick: ``CCCC``
 The ``Close`` price will be used for the four components of the price
 The session closing time is used for this tick
 The volume will be split amongst the 2 ticks using the parameters:
 - ``closevol`` (default: ``0.5``) The value indicate which percentage, in
 absolute terms from 0.0 to 1.0, has to be assigned to the *closing*
 tick. The rest will be assigned to the ``OHLX`` tick.
 **This filter is meant to be used together with** ``cerebro.replaydata``
 '''
    params = (
        ('closevol', 0.5),  # 0 -> 1 amount of volume to keep for close
    )
    # replaying = True
    def __init__(self, data):
        self.lastdt = None
    def __call__(self, data):
        # Make a copy of the new bar and remove it from stream
        datadt = data.datetime.date()  # keep the date
        if self.lastdt == datadt:
            return False  # skip bars that come again in the filter
        self.lastdt = datadt  # keep ref to last seen bar
        # Make a copy of current data for ohlbar
        ohlbar = [data.lines[i][0] for i in range(data.size())]
        closebar = ohlbar[:]  # Make a copy for the close
        # replace close price with o-h-l average
        ohlprice = ohlbar[data.Open] + ohlbar[data.High] + ohlbar[data.Low]
        ohlbar[data.Close] = ohlprice / 3.0
        vol = ohlbar[data.Volume]  # adjust volume
        ohlbar[data.Volume] = vohl = int(vol * (1.0 - self.p.closevol))
        oi = ohlbar[data.OpenInterest]  # adjust open interst
        ohlbar[data.OpenInterest] = 0
        # Adjust times
        dt = datetime.datetime.combine(datadt, data.p.sessionstart)
        ohlbar[data.DateTime] = data.date2num(dt)
        # Ajust closebar to generate a single tick -> close price
        closebar[data.Open] = cprice = closebar[data.Close]
        closebar[data.High] = cprice
        closebar[data.Low] = cprice
        closebar[data.Volume] = vol - vohl
        ohlbar[data.OpenInterest] = oi
        # Adjust times
        dt = datetime.datetime.combine(datadt, data.p.sessionend)
        closebar[data.DateTime] = data.date2num(dt)
        # Update stream
        data.backwards(force=True)  # remove the copied bar from stream
        data._add2stack(ohlbar)  # add ohlbar to stack
        # Add 2nd part to stash to delay processing to next round
        data._add2stack(closebar, stash=True)
        return False  # initial tick can be further processed from stack 

