大小调整器 - 智能押注#

一个 策略 提供了交易的方法,例如: buysellclose 。我们来看一下 buy 的签名:

def buy(self, data=None,
        size=None, price=None, plimit=None,
        exectype=None, valid=None, tradeid=0, **kwargs):

请注意,如果调用者没有指定, size 的默认值为 None 。这就是 大小调整器 发挥重要作用的地方:

  • size=None 请求 策略 向其 * 大小调整器* 请求实际的押注量

显然, 策略*有一个 大小调整器 :是的,确实!背后的机制会为 * 策略 * 添加一个默认的大小调整器,如果用户未添加大小调整器。默认添加到 * 策略 的大小调整器是 SizerFix 。定义的初始行:

class SizerFix(SizerBase):
    params = (('stake', 1),)

很容易猜到,这个 大小调整器 只是使用 1 单位的押注量进行 买入/卖出 (无论是股份还是合同…)

使用 大小调整器#

来自 Cerebro ============可以通过 Cerebro 使用两种不同的方法添加 Sizers

  • addsizer(sizercls, *args, **kwargs)

    添加一个 Sizer ,该 Sizer 将应用于添加到 cerebro 的任何策略。这可以说是默认 Sizer 。示例:

    cerebro = bt.Cerebro()
    cerebro.addsizer(bt.sizers.SizerFix, stake=20)  # 策略的默认sizer
    
  • addsizer_byidx(idx, sizercls, *args, **kwargs)

    Sizer 仅添加到“idx”所引用的 Strategy

    可以从 addstrategy 的返回值获得此“idx”。如下所示:

    cerebro = bt.Cerebro()
    cerebro.addsizer(bt.sizers.SizerFix, stake=20)  # 策略的默认sizer
    
    idx = cerebro.addstrategy(MyStrategy, myparam=myvalue)
    cerebro.addsizer_byidx(idx, bt.sizers.SizerFix, stake=5)
    
    cerebro.addstrategy(MyOtherStrategy)在这个例子中:
    
  • 系统中已添加了一个默认的 Sizer 。这个 Sizer 适用于所有没有指定特定 Sizer 的策略

  • 对于 MyStrategy ,在收集了其插入位置 idx 之后,添加了一个特定的 Sizer (更改了 stake 参数)

  • 添加了第二个策略 MyOtherStrategy 到系统中。对于它没有添加特定的 Sizer

  • 这意味着:

    • MyStrategy 最终将拥有一个内部的特定 Sizer

    • MyOtherStrategy 将获得默认的 Sizer

注意

default 并不意味着策略共享一个单一的 Sizer 实例。每个 strategy 都会收到一个不同的实例来代表 default sizer

要共享一个单一的实例,需要将要共享的 sizer 定义为一个单例类。如何定义单例类超出了 backtrader 的范围

来自 Strategy**Strategy 类提供了一个 API: setsizergetsizer (还有一个 property sizer )来管理 Sizer 。它们的签名如下:

  • def setsizer(self, sizer) :接受一个已经实例化的 Sizer

  • def getsizer(self) :返回当前的 Sizer 实例

  • sizer 是一个可直接 get/set 的属性

在这种情况下, Sizer 可以是以下的其中一种情况:

  • 作为参数传递给策略

  • __init__ 中使用属性 sizersetsizer 进行设置,例如:

    class MyStrategy(bt.Strategy):

    params = ((‘sizer’, None),)

    def __init__(self):
    if self.p.sizer is not None:

    self.sizer = self.p.sizer

    这样的做法可以在与 cerebro 调用发生的相同级别上创建一个 Sizer ,并将其作为参数传递给系统中的所有策略,从而实现共享一个 SizerSizer 开发

*** ** ** ** ** ** **

做这件事很容易:

  1. 继承自 backtrader.Sizer

    这将使你能够访问 self.strategyself.broker ,虽然在大多数情况下不需要这样做。可以通过 broker 访问以下内容

    • 使用 self.strategy.getposition(data) 获取数据的仓位

    • 通过 self.broker.getvalue() 获取完整的投资组合价值

      当然,也可以使用 self.strategy.broker.getvalue()

    其他一些参数已经在下面了

  2. 重写方法 _getsizing(self, comminfo, cash, data, isbuy)

    • comminfo : CommissionInfo 实例,包含关于数据的佣金信息,允许计算仓位价值、操作成本、操作的佣金

    • cash : 经纪人当前可用的现金- data : 操作的目标

  • isbuy : 对于“买入”操作将为 True ,对于“卖出”操作将为 False

该方法返回“买入/卖出”操作的所需 size

返回的符号无关紧要,即:如果操作是“卖出”操作( isbuy 为`False ),该方法可以返回 5`或`-5`。只有绝对值将被“卖出”操作使用。

Sizer 已经去到了 broker 并请求给定 data佣金信息 ,实际的 现金*水平并提供对作为操作目标的 data*的直接引用

现在来看看 FixedSize sizer的定义:

import backtrader as bt

class FixedSize(bt.Sizer):
    params = (('stake', 1),)

    def _getsizing(self, comminfo, cash, data, isbuy):
        return self.params.stake

这很简单,因为 Sizer 没有进行计算,参数只是存在其中。但是这种机制应该允许构建复杂的 尺寸*(也被称为 定位*)系统来管理进入/退出市场时的风险。

另一个例子: 一个位置颠倒者

```python class FixedReverser(bt.FixedSize):

def _getsizing(self, comminfo, cash, data, isbuy):

position = self.broker.getposition(data) size = self.p.stake * (1 + (position.size != 0)) return size

```

这个示例是在现有的 FixedSize 基础上构建的,以继承 params ,并且重写了 _getsizing 方法:

  • 通过 broker 属性获取 dataposition

  • 使用 position.size 来决定是否增加固定的股份

  • 返回计算出的值

这样一来,决定是否反向或开仓的负担就从 策略*移交给了 Sizer ,*Sizer 处于控制之中,可以随时替换而不影响逻辑。

实际的 Sizer 适用性 **********************不考虑复杂的大小算法,可以使用两种不同的大小调整器来将策略从”仅做多”调整为”做多做空”。只需在”cerebro”执行中更改”Sizer”,策略的行为就会改变。一个非常简单的 close 交叉 SMA 算法:

```python class CloseSMA(bt.Strategy):

params = ((‘period’, 15),)

def __init__(self):

sma = bt.indicators.SMA(self.data, period=self.p.period) self.crossover = bt.indicators.CrossOver(self.data, sma)

def next(self):
if self.crossover > 0:

self.buy()

elif self.crossover < 0:

self.sell()

```

注意,策略并不考虑当前的 持仓*(通过查看 ``self.position`` )来决定是否需要实际执行 买入 或*卖出 。只考虑 CrossOver 的*信号*。大小调整器将负责所有事情。

这个大小调整器将在只有当已经存在一个仓位时,卖出时返回一个*非零*的大小:

```python class LongOnly(bt.Sizer):

params = ((‘stake’, 1),)

def _getsizing(self, comminfo, cash, data, isbuy):
if isbuy:

return self.p.stake

# 卖出情况 position = self.broker.getposition(data) if not position.size:

return 0 # 如果没有仓位则不卖出

` ```python return self.p.stake `

将所有内容放在一起(假设已经导入了 backtrader 并且已经向系统添加了一个 data ):

...
cerebro.addstrategy(CloseSMA)
cerebro.addsizer(LongOnly)
...
cerebro.run()
...

图表(来自于源代码中的示例,用于测试此代码)。

只需将 Sizer 更改为上面显示的 FixedReverser 即可实现 Long-Short 版本:

...
cerebro.addstrategy(CloseSMA)
cerebro.addsizer(FixedReverser)
...
cerebro.run()
...

输出的图表。

注意这些区别: - *交易*次数已经增加了

  • 现金水平永远不会回到原本的 价值,因为策略总是处于* 市场*中

这两种方法都是负面的,但这只是一个例子。

bt.Sizer 参考


class backtrader.Sizer(*args, **kwargs)#

This is the base class for Sizers. Any sizer should subclass this and override the _getsizing method

Member Attribs:

  • strategy: will be set by the strategy in which the sizer is working

    Gives access to the entire api of the strategy, for example if the actual data position would be needed in _getsizing:

    position = self.strategy.getposition(data)
    
  • broker: will be set by the strategy in which the sizer is working

    Gives access to information some complex sizers may need like portfolio value, ..

_getsizing(comminfo, cash, data, isbuy)#

This method has to be overriden by subclasses of Sizer to provide the sizing functionality

Params:
  • comminfo: The CommissionInfo instance that contains information about the commission for the data and allows calculation of position value, operation cost, commision for the operation

  • cash: current available cash in the broker

  • data: target of the operation

  • isbuy: will be True for buy operations and False for sell operations

The method has to return the actual size (an int) to be executed. If 0 is returned nothing will be executed.

The absolute value of the returned value will be used