Sizers
Smart Staking
A Strategy offers methods to trade, namely: buy
, sell
and close
. Let’s see the signature of buy
:
策略提供了交易的方法,即:买入、卖出和成交。让我们看看buy的签名:
def buy(self, data=None, size=None, price=None, plimit=None, exectype=None, valid=None, tradeid=0, **kwargs):
Notice that size
has a default value of None
if the caller does not specify it. This is where Sizers play an important role:
请注意,如果调用方未指定大小,则size的默认值为None。这就是Sizers发挥重要作用的地方:
size=None
requests that the Strategy asks its Sizer for the actual stake
size=None请求策略向其Sizer请求实际的股份
This obviously implies that Strategies have a Sizer: Yes, indeed!. The background machinery adds a default sizer to a Strategy if the user has not added one. The default Sizer added to a strategy is SizerFix
. The initial lines of the definition:
这显然意味着策略里面有Sizer:是的,确实如此!。如果用户没有添加,则后台机制将默认的大小写器添加到策略中。添加到策略中的默认Sizer是SizerFix。定义的初始行:
class SizerFix(SizerBase): params = (('stake', 1),)
It is easy to guess that this Sizer simply buys/sells using a stake
of 1
units (be it shares, contracts, …)
很容易猜测,这个规模的Sizer只是用1个单位的股份进行买卖(无论是股票、合同等)
Using Sizers
From Cerebro
Sizers can be added via Cerebro with 2 different methods:
Sizers有两个不同的方法通过Cerebro添加
addsizer(sizercls, *args, **kwargs)
Adds a Sizer that will be applied to any strategy added to cerebro. This is, so to to say, the default Sizer. Example:
添加一个Sizer,它将应用于添加到大脑的任何策略。可以说,这是默认的Sizer。例子:
cerebro = bt.Cerebro() cerebro.addsizer(bt.sizers.SizerFix, stake=20) # default sizer for strategies
addsizer_byidx(idx, sizercls, *args, **kwargs)
The Sizer will only be added to the Strategy referenced by idx
Sizer只能通过策略返回的参考idx被添加
This idx
can be gotten as return value from addstrategy
. As in:
这个idx可以作为addstrategy的返回值获得。如:
cerebro = bt.Cerebro() cerebro.addsizer(bt.sizers.SizerFix, stake=20) # default sizer for strategies idx = cerebro.addstrategy(MyStrategy, myparam=myvalue) cerebro.addsizer_byidx(idx, bt.sizers.SizerFix, stake=5) cerebro.addstrategy(MyOtherStrategy)
In this example:
A default Sizer has been added to the system. This one applies to all strategies which don’t have a specific Sizer assigned
系统中已添加了一个默认的Sizer。这一条适用于所有没有指定特定尺寸的策略。
For MyStrategy and after collecting its insertion idx, a specific sizer (changing the stake
param) is added
对于MyStrategy,在收集其插入idx之后,将添加一个特定的sizer(更改stake param)
A 2nd strategy, MyOtherStrategy, is added to the system. No specific Sizer is added for it
第二个策略,MyOtherStrategy,被添加到系统中。没有为它添加特定的Sizer
This means that:
这意味着
MyStrategy will finally have an internal specific Sizer
MyStrategy最终会有一个内部特定的Sizer
MyOtherStrategy will get the default sizer
MyOtherStrategy将获得默认Sizer
Note
default doesn’t mean that that the strategies share a single Sizer instance. Each strategy receives a different instance of the default sizer
默认并不意味着这些策略共享一个Sizer实例。每个策略都会收到一个不同的默认sizer实例
To share a single instance, the sizer to be shared should be a singleton class. How to define one is outside of the scope of backtrader
要共享单个实例,要共享的sizer应该是一个singleton类(单例)。如何定义一个超出了backtrader的范围
From Strategy
The Strategy class offers an API: setsizer
and getsizer
(and a property sizer
) to manage the Sizer. The signatures:
Strategy类提供了一个API: setzer和getsizer(以及一个属性sizer)来管理sizer。签名:
def setsizer(self, sizer)
: it takes an already instantiated Sizer
def setzer(self,sizer):它需要一个已经实例化的sizer
def getsizer(self)
: returns the current Sizer instance
def getsizer(self):返回当前的Sizer实例
sizer
it is the property which can be directly get/set
sizer它是可以直接获取/设置的属性
In this scenario the Sizer can be for example:
在这种情况下,Sizer可以是:
Passed to the strategy as a parameter
作为参数传递给策略
Be set during __init__
using the property sizer
or setsizer
as in:
在初始化期间使用属性sizer或setsizer进行设置,如下所示:
class MyStrategy(bt.Strategy): params = (('sizer', None),) def __init__(self): if self.p.sizer is not None:
# 通过property进行设置 self.sizer = self.p.sizer
This would for example allow to create a Sizer at the same level as the cerebro calls are happening and pass it as a parameter to all strategies that go in the system, which effectevily allows sharing a Sizer
例如,这将在发生大脑调用的同一时间级别创建一个Sizer,并将其作为参数传递给系统中的所有策略,从而有效地允许共享一个Sizer。
Sizer Development
Doing it is easy:
这样做很容易:
Subclass from backtrader.Sizer
子类来自于backtrader.Sizer
This gives you access to self.strategy
and self.broker
although it shouldn’t be needed in most cases. Things that can be accessed with the broker
这样你就可以访问self.strategy
以及self.broker
虽然在大多数情况下不需要它。这个可以让你访问broker
data’s position with self.strategy.getposition(data)
数据的position通过self.strategy.getposition取到(数据)
complete portfolio value through self.broker.getvalue()
完整的投资价值通过self.broker.getvalue()
Notice this could of course also be done with self.strategy.broker.getvalue()
注意这当然也可以用self.strategy.broker.getvalue()
Some of the other things are already below as arguments
其他一些事情已经作为论据在下面了
Override the method _getsizing(self, comminfo, cash, data, isbuy)
重写方法_getsizing(self, comminfo, cash, data, isbuy)
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
comminfo:包含数据佣金信息的CommissionInfo实例,允许计算头寸值、操作成本、操作佣金
cash
: current available cash in the broker
cash:当前经纪人可用的现金
data
: target of the operation
data:操作的目标
isbuy
: will be True
for buy operations and False
for sell operations
isbuy:对于buy操作为True,对于sell操作为False
This method returns the desired size
for the buy/sell operation
此方法返回buy/sell操作所需的大小
The returned sign is not relevant, ie: if the operation is a sell operation (isbuy
will be False
) the method may return 5
or -5
. Only the absolute value will be used by the sell operation.
返回的符号与此无关,即:如果操作是sell操作(isbuy将为False),则该方法可能返回5或-5。只有绝对值将被sell操作使用。
Sizer
has already gone to the broker
and requested the commission information for the given data, the actual cash level and provides a direct reference to the data which is the target of the operation
Sizer已经去找经纪人,要求提供给定数据的佣金信息、实际现金水平,并提供直接参考数据,这是操作的目标
Let’s go for the definition of the FixedSize
sizer:
让我们定一个FixedSize
import backtrader as bt class FixedSize(bt.Sizer): params = (('stake', 1),) def _getsizing(self, comminfo, cash, data, isbuy): return self.params.stake
This is pretty simple in that the Sizer makes no calculations and the parameters are just there.
这很简单,因为Sizer不进行计算,参数就在那里。
But the mechanism should allow the construction of complex sizing (aka positioning) systems to manage the stakes when entering/exiting the market.
但该机制应允许构建复杂的规模(又名定位)系统,以便在进入/退出市场时管理股权。
Another example: A position rerverser:
另一个例子:位置变换器:
class FixedRerverser(bt.FixedSize): def _getsizing(self, comminfo, cash, data, isbuy): position = self.broker.getposition(data) size = self.p.stake * (1 + (position.size != 0)) return size
This one builds on the existing FixedSize
to inherit the params
and overrides _getsizing
to:
它基于现有的FixedSize来继承参数,并重写_getsizing以:
Get the position
of the data via the attribute broker
通过data的属性broker来获取持仓情况
Use position.size
to decide if to double the fixed stake
使用position.size来计算是否需要两倍的量
Return the calculated value
返回计算好的值
This would remove the burden from the Strategy to decide if a position has to be reversed or opened, the Sizer is in control and can at any time be replaced without affecting the logic.
这将减轻策略的负担,以决定是否必须逆转或打开头寸,规模大小是在控制之中,可以随时替换而不影响逻辑。
Practical Sizer Applicability
实际Sizer的适用性
Wihtout considering complex sizing algorithms, two different sizers can be used to turn a strategy from Long-Only to Long-Short. Simply by changing the Sizer in the cerebro execution, the strategy will change behavior. A very simple close
crosses SMA
algorithm:
在不考虑复杂的大小调整算法的情况下,可以使用两种不同的大小sizers来将策略从“仅多单”转换为“多空单”。通过改变执行策略,大脑的行为会改变。一种非常简单的收盘价超越SMA算法:
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()
Notice how the strategy doesn’t consider the current position (by looking at self.position
) to decide whether a buy or sell has to actually be done. Only the signal from the CrossOver
is considered. The Sizers will be in charge of everything.
注意策略是不考虑当前仓位(通过查看自我定位)决定到底是买还是卖。只考虑来自交叉口的信号。所有的事都由Sizers负责。
This sizer will take care of only returning a non-zero size when selling if a position is already open:
如果仓位已经打开,则在卖出时,此sizer只负责返回非零大小:
class LongOnly(bt.Sizer): params = (('stake', 1),) def _getsizing(self, comminfo, cash, data, isbuy): if isbuy: return self.p.stake # Sell situation,空仓不给卖空 position = self.broker.getposition(data) if not position.size: return 0 # do not sell if nothing is open return self.p.stake
Putting it all together (and assuming backtrader has already been imported and a data has been added to the system):
把它们放在一起(假设backtrader已经导入,并且数据已经添加到系统中):
... cerebro.addstrategy(CloseSMA) cerebro.addsizer(LongOnly) ... cerebro.run() ...
The chart (from the sample included in the sources to test this).
图表(源代码中包含的样本)。
The Long-Short version simply changes the Sizer to be the FixedReverser
shown above:
长短版只需将Sizer更改为上面显示的FixedReverser:
... cerebro.addstrategy(CloseSMA) cerebro.addsizer(FixedReverser) ... cerebro.run() ...
The output chart.
Notice the differences:
注意区别:
The number of trades has duplicated
交易数量重复了
The cash level never goes back to be the value because the strategy is always in the market
现金水平永远不会回到价值,因为策略总是在市场上
Both approaches are anyhow negative, but this is only an example.
这两种方法都是消极的,但这只是一个例子。
bt.Sizer Reference
class backtrader.Sizer()
This is the base class for Sizers. Any sizer should subclass this and override the _getsizing
method
这是sizer的基类。任何sizer都应将其子类化并重写_getsizing方法
Member Attribs:
成员属性:
strategy
: will be set by the strategy in which the sizer is working
策略:在任意一个sizer的工作状态都可以设置策略
Gives access to the entire api of the strategy, for example if the actual data position would be needed in_getsizing
:
提供对策略的整个api的访问权限,例如,如果需要实际的数据仓位就可以通过_getsizing方法
position = self.strategy.getposition(data)
broker
: will be set by the strategy in which the sizer is working
broker:在任意一个sizer的工作状态都可以设置broker
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
此方法必须由Sizer的子类重写才能提供调整功能
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.
该方法必须返回要执行的实际大小(int)。如果返回的是0,则不执行任何操作。
The absolute value of the returned value will be used
将使用返回值的绝对值