BackTrader 中文文档(九)(2)https://developer.aliyun.com/article/1505289
Fillers
当涉及使用成交量执行订单时,backtrader 经纪人模拟具有默认策略:
- 忽略成交量
这基于两个前提:
- 在足够流动的市场中进行交易,以完全吸收一次性 买入/卖出 订单
- 真实成交量匹配需要真实世界
快速示例是Fill or Kill
订单。即使到了 tick 分辨率并且有足够的 fill 量,backtrader 经纪人也无法知道市场上有多少额外的参与者,以区分这样一个订单是否会匹配以坚持Fill
部分,或者该订单是否应该Kill
。
但是 broker 可以接受 Volume Fillers,这些填充器确定在给定时间点使用多少成交量用于 order matching。
填充器的签名
backtrader 生态系统中的一个 filler 可以是符合以下签名的任何 callable:
callable(order, price, ago)
其中:
order
是将要执行的订单
此对象提供对data
对象的访问,该对象是操作目标,创建大小/价格,执行价格/大小/剩余大小和其他细节- 订单将被执行的
price
ago
是在其中查找体积和价格元素的 order 中的data
的索引
在几乎所有情况下,这将是0
(当前时间点),但在一种角落情况下,以涵盖Close
订单,这可能是-1
例如,要访问条形图的体积:
barvolume = order.data.volume[ago]`
可调用对象可以是函数,也可以是例如支持 __call__
方法的类的实例,如:
class MyFiller(object): def __call__(self, order, price, ago): pass
向经纪人添加一个 Filler
最直接的方法是使用 set_filler
:
import backtrader as bt cerebro = Cerebro() cerebro.broker.set_filler(bt.broker.fillers.FixedSize())
第二种选择是完全替换 broker
,虽然这可能只适用于已重写部分功能的 BrokerBack
的子类:
import backtrader as bt cerebro = Cerebro() filler = bt.broker.fillers.FixedSize() newbroker = bt.broker.BrokerBack(filler=filler) cerebro.broker = newbroker
示例
backtrader 源代码中包含一个名为 volumefilling
的示例,允许测试一些集成的 fillers
(最初全部)
参考资料
class backtrader.fillers.FixedSize()
使用 百分比 的体积返回给定订单的执行大小。
此百分比通过参数 perc
设置
参数:
size
(默认值:None
)要执行的最大尺寸。如果执行时的实际体积小于大小,则也是限制
如果此参数的值计算为 False,则整个条的体积将用于匹配订单
class backtrader.fillers.FixedBarPerc()
使用条中体积的 百分比 返回给定订单的执行大小。
此百分比通过参数 perc
设置
参数:
perc
(默认值:100.0
)(有效值:0.0 - 100.0
)
用于执行订单的成交量栏的百分比
class backtrader.fillers.BarPointPerc()
返回给定订单的执行大小。成交量将在 high-low 范围内均匀分布,使用 minmov
进行分区。
从给定价格的分配交易量中,将使用 perc
百分比
参数:
minmov
(默认值:0.01
)
最小价格变动。用于将范围 高-低 分割,以在可能的价格之间按比例分配交易量perc
(默认值:100.0
)(有效值:0.0 - 100.0
)
分配给订单执行价格的交易量百分比,用于匹配
位置
通常在策略内部检查资产的持仓情况:
position
(一种属性)或getposition(data=None, broker=None)
这将返回由 cerebro 提供的默认broker
中策略的datas[0]
上的位置
一个持仓只是指:
- 资产持有
size
- 平均价格为
price
它作为一种状态,并且例如可以用来决定是否要发出订单(例如:仅在没有持仓时才进入多头持仓)
参考:Position
类 backtrader.position.Position(size=0, price=0.0)
保持并更新持仓的大小和价格。该对象与任何资产无关,仅保留大小和价格。
成员属性:
* size (int): current size of the position * price (float): current price of the position
Position 实例可以使用 len(position) 进行测试,以查看大小是否为空
交易
交易的定义:
- 当仪器中的持仓从 0 变为大小 X 时,交易就开始了,大小可以是正的/负的,分别表示多头/空头仓位)
- 当仓位从 X 变为 0 时,交易关闭。
以下两个动作:
- 正至负
- 负至正
实际上被视为:
- 一个交易已经关闭(仓位从 X 变为 0)
- 一个新交易已经打开(仓位从 0 变为 Y)
交易仅用于信息提供,没有用户可调用的方法。
参考:交易
类 backtrader.trade.Trade(data=None, tradeid=0, historyon=False, size=0, price=0.0, value=0.0, commission=0.0)
跟踪交易的生命周期:大小、价格、佣金(和价值?)
一个交易从 0 开始,可以增加和减少,并且如果回到 0 可以被视为关闭。
交易可以是多头(正大小)或空头(负大小)
一个交易不打算被反转(逻辑上不支持)
成员属性:
ref
: 唯一的交易标识符status
(int
): Created、Open、Closed 中的一个tradeid
: 在创建订单时传递给订单的交易 id 分组,默认为 0size
(int
): 交易的当前大小price
(float
): 交易的当前价格value
(float
): 交易的当前价值commission
(float
): 当前累积佣金pnl
(float
): 交易的当前利润和损失(毛利润)pnlcomm
(float
): 交易的当前利润和损失减去佣金(净利润)isclosed
(bool
): 记录最后一个更新是否关闭了交易(将大小设置为 null)isopen
(bool
): 记录是否有任何更新打开了交易justopened
(bool
): 如果交易刚刚开启baropen
(int
): 开启此交易的柱dtopen
(float
): 交易开启的浮点编码日期时间
- 使用方法
open_datetime
获取 Python datetime.datetime 或使用平台提供的num2date
方法
barclose
(int
): 关闭此交易的柱dtclose
(float
): 交易关闭的浮点编码日期时间
- 使用方法
close_datetime
获取 Python datetime.datetime 或使用平台提供的num2date
方法
barlen
(int
): 此交易持续的周期数historyon
(bool
): 是否记录历史记录history
(list
): 每个“update”事件更新的列表,包含更新后的状态和更新中使用的参数
历史记录中的第一个条目是开仓事件,最后一个条目是关闭事件
佣金方案
佣金:股票与期货
原文:
www.backtrader.com/docu/commission-schemes/commission-schemes/
不可知论
在继续之前,让我们记住,backtrader
试图保持对数据表示的不可知性。 可以将不同的佣金方案应用于相同的数据集。
让我们看看如何做到这一点。
使用经纪商快捷方式
这使得最终用户远离了CommissionInfo
对象,因为可以通过单个函数调用来创建/设置佣金方案。 在常规的cerebro
创建/设置过程中,只需在broker
成员属性上添加一个对setcommission
的调用。 以下调用为使用Interactive Brokers时的Eurostoxx50期货设置了一个常规佣金方案:
cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0)
由于大多数用户通常只会测试单个工具,因此这就是所有的内容。 如果您已为数据源(data feed
)指定了name
,因为在图表上同时考虑了多个工具,因此可以稍微扩展此调用,如下所示:
cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0, name='Eurostoxxx50')
在这种情况下,此即时佣金方案仅适用于名称匹配为Eurostoxx50
的工具。
设置佣金参数的含义
commission
(默认值:0.0
)绝对或百分比单位中每个操作的货币单位成本。在上面的示例中,每个buy
合约的费用为 2.0 欧元,每个sell
合约再次为 2.0 欧元。这里重要的问题是何时使用绝对值或百分比值。
- 如果
margin
评估为False
(例如为 False、0 或 None),则会认为commission
表示price
乘以size
操作值的百分比 - 如果
margin
是其他值,则认为操作发生在类似期货
的工具上,commission
是每个size
合约的固定价格
margin
(默认值:None
)使用期货
类似工具时需要的保证金。 如上所述
- 如果未设置**
margin
**,则将理解commission
为以百分比表示,并应用于buy
或sell
操作的price * size
组件。 - 如果设置了
margin
,则将理解commission
为固定值,该值将乘以buy
或sell
操作的size
组件。
mult
(默认值:1.0)
对于类似期货
的工具,这确定要应用于利润和损失计算的乘数。
这就是期货同时具有吸引力和风险的原因。name
(默认值:无)
将佣金方案的应用限制为与name
匹配的工具
这可以在数据源创建期间设置。
如果未设置,该方案将适用于系统中的任何数据。
现在有两个示例:股票 vs 期货
上述期货示例:
cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0)
以股票为例:
cerebro.broker.setcommission(commission=0.005) # 0.5% of the operation value
注意
第 2 种语法不设置margin和mult,backtrader会尝试通过考虑佣金为%
来采用智能方法。
要完全指定佣金方案,需要创建CommissionInfo
的子类
创建永久委员会方案
可以通过直接使用CommissionInfo
类来创建更永久的佣金方案。用户可以选择在某处定义此定义:
import backtrader as bt commEurostoxx50 = bt.CommissionInfo(commission=2.0, margin=2000.0, mult=10.0)
稍后在另一个 Python 模块中应用它与 addcommissioninfo
:
from mycomm import commEurostoxx50 ... cerebro.broker.addcommissioninfo(commEuroStoxx50, name='Eurostoxxx50')
CommissionInfo
是一个对象,它使用与backtrader
环境中的其他对象相同的params
声明。因此,以上内容也可以表示为:
import backtrader as bt class CommEurostoxx50(bt.CommissionInfo): params = dict(commission=2.0, margin=2000.0, mult=10.0)
以后:
from mycomm import CommEurostoxx50 ... cerebro.broker.addcommissioninfoCommEuroStoxx50(), name='Eurostoxxx50')
现在进行与 SMA 交叉的“真实”比较
使用简单移动平均线交叉作为入/出信号,同一数据集将使用期货
类似的佣金方案进行测试,然后使用类似于股票
的方案进行测试。
注意
期货持仓不仅可以给出进入/退出行为,还可以在每次发生时进行反转行为。但这个示例是关于比较委员会方案的。
代码(请参阅底部的完整策略)是相同的,可以在定义策略之前选择方案。
futures_like = True if futures_like: commission, margin, mult = 2.0, 2000.0, 10.0 else: commission, margin, mult = 0.005, None, 1
只需将futures_like
设置为 false 即可运行类似于股票
的方案。
已添加一些记录代码以评估不同佣金方案的影响。让我们只关注前两个操作。
对于期货:
2006-03-09, BUY CREATE, 3757.59 2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 2000.00, Comm 2.00 2006-04-11, SELL CREATE, 3788.81 2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 2000.00, Comm 2.00 2006-04-12, OPERATION PROFIT, GROSS 328.00, NET 324.00 2006-04-20, BUY CREATE, 3860.00 2006-04-21, BUY EXECUTED, Price: 3863.57, Cost: 2000.00, Comm 2.00 2006-04-28, SELL CREATE, 3839.90 2006-05-02, SELL EXECUTED, Price: 3839.24, Cost: 2000.00, Comm 2.00 2006-05-02, OPERATION PROFIT, GROSS -243.30, NET -247.30
对于股票:
2006-03-09, BUY CREATE, 3757.59 2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 3754.13, Comm 18.77 2006-04-11, SELL CREATE, 3788.81 2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 3786.93, Comm 18.93 2006-04-12, OPERATION PROFIT, GROSS 32.80, NET -4.91 2006-04-20, BUY CREATE, 3860.00 2006-04-21, BUY EXECUTED, Price: 3863.57, Cost: 3863.57, Comm 19.32 2006-04-28, SELL CREATE, 3839.90 2006-05-02, SELL EXECUTED, Price: 3839.24, Cost: 3839.24, Comm 19.20 2006-05-02, OPERATION PROFIT, GROSS -24.33, NET -62.84
第一次操作的价格如下:
- 买入(执行)-> 3754.13 / 卖出(执行)-> 3786.93
- 期货利润与损失(含佣金):324.0
- 股票利润与损失(含佣金):-4.91
嘿!!佣金完全吃掉了股票
操作的任何利润,但对期货
操作只是造成了小小的影响。
第二次操作:
- 买入(执行)->
3863.57
/ 卖出(执行)->3389.24
- 期货利润与损失(含佣金):
-247.30
- 股票利润与损失(含佣金):
-62.84
对于这个负操作,期货
的影响更大。
但:
- 期货累积净利润与损失:
324.00 + (-247.30) = 76.70
- 股票累积净利润与损失:
(-4.91) + (-62.84) = -67.75
累积效果可以在下面的图表中看到,在完整年份结束时,期货产生了更大的利润,但也遭受了更大的回撤(更深的亏损)
但重要的是:无论是期货
还是股票
…… 都可以进行回测。
期货佣金
股票佣金
代码
from __future__ import (absolute_import, division, print_function, unicode_literals) import backtrader as bt import backtrader.feeds as btfeeds import backtrader.indicators as btind futures_like = True if futures_like: commission, margin, mult = 2.0, 2000.0, 10.0 else: commission, margin, mult = 0.005, None, 1 class SMACrossOver(bt.Strategy): def log(self, txt, dt=None): ''' Logging function fot this strategy''' dt = dt or self.datas[0].datetime.date(0) print('%s, %s' % (dt.isoformat(), txt)) def notify(self, order): if order.status in [order.Submitted, order.Accepted]: # Buy/Sell order submitted/accepted to/by broker - Nothing to do return # Check if an order has been completed # Attention: broker could reject order if not enougth cash if order.status in [order.Completed, order.Canceled, order.Margin]: if order.isbuy(): self.log( 'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) self.buyprice = order.executed.price self.buycomm = order.executed.comm self.opsize = order.executed.size else: # Sell self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) gross_pnl = (order.executed.price - self.buyprice) * \ self.opsize if margin: gross_pnl *= mult net_pnl = gross_pnl - self.buycomm - order.executed.comm self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' % (gross_pnl, net_pnl)) def __init__(self): sma = btind.SMA(self.data) # > 0 crossing up / < 0 crossing down self.buysell_sig = btind.CrossOver(self.data, sma) def next(self): if self.buysell_sig > 0: self.log('BUY CREATE, %.2f' % self.data.close[0]) self.buy() # keep order ref to avoid 2nd orders elif self.position and self.buysell_sig < 0: self.log('SELL CREATE, %.2f' % self.data.close[0]) self.sell() if __name__ == '__main__': # Create a cerebro entity cerebro = bt.Cerebro() # Add a strategy cerebro.addstrategy(SMACrossOver) # Create a Data Feed datapath = ('../../datas/2006-day-001.txt') data = bt.feeds.BacktraderCSVData(dataname=datapath) # Add the Data Feed to Cerebro cerebro.adddata(data) # set commission scheme -- CHANGE HERE TO PLAY cerebro.broker.setcommission( commission=commission, margin=margin, mult=mult) # Run over everything cerebro.run() # Plot the result cerebro.plot()
参考
类 backtrader.CommInfoBase()
基于委员会方案的基类。
参数:
commission
(默认值:0.0
):以百分比或货币单位表示的基础佣金值mult
(默认为1.0
):应用于资产的值/利润的乘数margin
(默认值:None
):需要开设/持有操作的货币单位金额。只有当类中的最终_stocklike
属性设置为False
时才适用automargin
(默认:False
):方法get_margin
使用的,用于自动计算以下策略所需的保证金/担保
- 如果参数
automargin
的评估为False
,则使用参数margin
- 如果
automargin < 0
,则使用参数mult
和mult * price
- 如果
automargin > 0
,则使用参数automargin
和automargin * price
commtype
(默认:None
):支持的值为CommInfoBase.COMM_PERC
(将佣金理解为%)和CommInfoBase.COMM_FIXED
(将佣金理解为货币单位)None
的默认值是支持的值,以保持与传统的CommissionInfo
对象的兼容性。 如果commtype
设置为 None,则适用以下规则:
margin
是None
:内部_commtype
设置为COMM_PERC
,_stocklike
设置为True
(与股票的百分比方式运作)- 如果
margin
不是None
:_commtype
设置为COMM_FIXED
,_stocklike
设置为False
(使用期货的固定来回佣金)
- 如果此参数设置为
None
之外的内容,则它将传递给内部的_commtype
属性,并且参数stocklike
和内部属性_stocklike
也是如此 stocklike
(默认:False
):指示工具是否类似于股票或期货(参见上文的commtype
讨论)percabs
(默认:False
):当commtype
设置为 COMM_PERC 时,参数commission
是否必须理解为 XX%或 0.XX
如果此参数为True
:0.XX 如果此参数为False
:XX%interest
(默认:0.0
)
如果这不是零,则这是持有空头卖出头寸所收取的年息。 这主要是针对股票的空头卖出
公式:days * price * abs(size) * (interest / 365)
必须用绝对值指定:0.05 -> 5%
注意
通过覆盖方法_get_credit_interest
可以更改行为interest_long
(默认:False
)
某些产品,如 ETF,对空头和多头头寸收取利息。 如果这是True
并且interest
不为零,则将在两个方向上收取利息leverage
(默认:1.0
)
对于所需现金的资产的杠杆倍数
- _stocklike
()
用于类 Stock-like/Futures-like 行为的最终值
- _commtype
()
PERC vs FIXED 佣金的最终值
这两个参数在内部使用,而不是声明的参数,以启用该方法
描述的兼容性检查适用于遗留的CommissionInfo
()
对象()
类 backtrader.CommissionInfo()
实际佣金方案的基类。
CommInfoBase 被创建以保持对backtrader提供的原始,不完整的支持的支持。新的佣金方案派生自这个类,这些类是CommInfoBase
的子类。
percabs
的默认值也更改为True
参数:
percabs
(默认:True):当commtype
设置为 COMM_PERC 时,参数commission
是否必须理解为 XX%或 0.XX
如果此参数为 True:0.XX 如果此参数为 False:XX%
get_leverage()
返回此佣金方案允许的杠杆水平
getsize(price, cash)
返回在给定价格下执行现金操作所需的大小
getoperationcost(size, price)
返回操作将花费的现金金额
getvaluesize(size, price)
返回给定价格的大小值。对于类似期货的对象,它在size * margin
处固定。
getvalue(position, price)
返回给定价格的位置值。对于类似期货的对象,它在size * margin
处固定。
get_margin(price)
返回给定价格下资产单个项目所需的实际保证金/担保。默认实现具有以下策略:
- 如果参数
automargin
评估为False
,则使用参数margin
- 使用参数
mult
,即如果automargin < 0
,则为mult * price
。 - 使用参数
automargin
,即如果automargin > 0
,则为automargin * price
。
getcommission(size, price)
计算给定价格的操作的佣金
_getcommission(size, price, pseudoexec)
计算给定价格的操作的佣金
pseudoexec:如果为 True,则操作尚未执行
profitandloss(size, price, newprice)
返回位置的实际盈亏
cashadjust(size, price, newprice)
计算给定价格差异的现金调整
get_credit_interest(data, pos, dt)
计算卖空或特定产品的信用费用
_get_credit_interest(data, size, price, days, dt0, dt1)
此方法返回经纪人收取的信用利息成本。
如果size > 0
,则仅在类的参数interest_long
为True
时才调用此方法。
计算信用利率的公式为:
公式:days * price * abs(size) * (interest / 365)
参数:
* `data`: data feed for which interest is charged * `size`: current position size. > 0 for long positions and < 0 for short positions (this parameter will not be `0`) * `price`: current position price * `days`: number of days elapsed since last credit calculation (this is (dt0 - dt1).days) * `dt0`: (datetime.datetime) current datetime * `dt1`: (datetime.datetime) datetime of previous calculation
dt0
和dt1
在默认实现中未被使用,并作为覆盖方法的额外输入提供
BackTrader 中文文档(九)(4)https://developer.aliyun.com/article/1505292