Backtrader 教程:佣金计划

  |  

不可知论

在继续之前,让我们记住, backtrader者试图对数据代表什么保持不可知论。不同的佣金方案可以应用于相同的数据集。

让我们看看它是如何做到的。

使用代理快捷方式

这使最终用户远离CommissionInfo对象,因为可以通过单个函数调用创建/设置佣金方案。在常规的cerebro创建/设置过程中,只需在broker成员属性上添加对setcommission的调用。以下调用设置了与盈透证券合作时Eurostoxx50期货的常规佣金计划:

cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0)

由于大多数用户通常只测试一个仪器,这就是它的全部。如果您已为数据馈送name ,因为图表上同时考虑了多个工具,此调用可以稍微扩展为如下所示:

cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0, name='Eurostoxxx50')

在这种情况下,这种即时佣金计划将仅适用于名称与Eurostoxx50匹配的工具。

setcommission参数的含义

  • commission (默认值: 0.0

    以绝对或百分比计算的货币单位每项行动的成本。

    在上面的例子中,每份buy合约 2.0 欧元, sell每份合约 2.0 欧元。

    这里的重要问题是何时使用绝对值或百分比值。

    • 如果margin评估为False (例如为False 、0 或 None),则认为commission表示price乘以size操作值的百分比

    • 如果margin是其他东西,则认为该操作是在诸如工具之类的futures上进行的,而commissionsize合约的固定价格

  • margin (默认值: None

    使用诸如工具之类的futures进行操作时需要保证金。如上所述

    • 如果设置了margin ,则commission将被理解为以百分比表示并应用于buysell操作的price * size组成部分

    • 如果设置了margincommission将被理解为乘以buysell操作的size分量的固定值

  • mult (默认值:1.0)

    对于future类似的工具,这决定了应用于损益计算的乘数。

    这就是使期货同时具有吸引力和风险的原因。

  • name (默认:无)

    将佣金计划的应用限制在与name匹配的工具上

    这可以在创建数据馈送期间进行设置。

    如果未设置,该方案将适用于系统中存在的任何数据。

现在举两个例子:股票与期货

上面的期货示例:

cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0)

股票的一个例子:

cerebro.broker.setcommission(commission=0.005)  # 0.5% of the operation value

笔记

第二种语法不设置保证金,并且 mult 和 backtrader尝试一种聪明的方法,将佣金考虑为基于%

要完全指定佣金方案,需要创建CommissionInfo的子类

创建永久佣金计划

可以通过直接使用CommissionInfo类来创建更永久的佣金方案。用户可以选择在某处有这个定义:

import backtrader as bt

commEurostoxx50 = bt.CommissionInfo(commission=2.0, margin=2000.0, mult=10.0)

稍后使用addcommissioninfo将其应用到另一个 Python 模块中:

from mycomm import commEurostoxx50

...

cerebro.broker.addcommissioninfo(commEuroStoxx50, name='Eurostoxxx50')

CommissionInfo是一个使用params声明的对象,就像backtrader环境中的其他对像一样。因此,上述也可以表示为:

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 Crossover 进行“真实”比较

使用 SimpleMovingAverage 交叉作为进入/退出信号,相同的数据集将使用类似futures的佣金计划进行测试,然后使用类似的stocks进行测试。

笔记

期货头寸不仅可以被赋予进入/退出行为,而且在每种情况下都可以被赋予反转行为。但是这个例子是关于比较佣金计划的。

代码(完整策略见底部)是相同的,可以在定义策略之前选择方案。

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即可与stocks类似方案一起运行。

添加了一些日志代码来评估不同佣金方案的影响。让我们只关注前两个操作。

对于期货:

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

嘿!!委员会已经完全吞噬了stocks业务的任何利润,但对futures业务的影响只是很小的一部分。

第二次操作:

  • 买入(运行)-> 3863.57 / 卖出(运行)-> 3389.24

    • 期货盈亏(含佣金):- -247.30

    • 股票损益(含佣金):- -62.84

这种对futures的负面操作的影响明显更大

但:

  • 期货累计净损益: 324.00 + (-247.30) = 76.70

  • 股票累计净损益: (-4.91) + (-62.84) = -67.75

累积效应可以在下面的图表中看到,从图中也可以看出,在全年结束时,期货产生了更大的利润,但也遭受了更大的回撤(水下更深)

但重要的是:无论是futures还是stocks ……都可以进行回测。

期货佣金

股票佣金

编码

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()

参考

类反向交易者 .CommInfoBase ()

佣金计划的基类。

参数:

  • commission (定义: 0.0 ):以百分比或货币单位表示的基本佣金值

  • mult (def 1.0 ): 应用于资产价值/利润的乘数

  • margin (定义: None ):打开/持有操作所需的货币单位数量。仅当类中的最终_stocklike属性设置为False时才适用

  • automargin (def: False ): 由方法get_margin用于自动计算以下策略所需的保证金/保证

    • 如果 param automargin评估为False ,则使用 param margin

    • 如果automargin < 0 ,则使用 param mult并使用mult * price

    • 使用参数automargin并使用automargin * price如果automargin > 0

  • commtype (def: None ): 支持的值为CommInfoBase.COMM_PERC (佣金被理解为 %)和CommInfoBase.COMM_FIXED (佣金被理解为货币单位)

    None的默认值是支持的值,以保持与旧CommissionInfo对象的兼容性。如果commtype设置为 None,则以下内容适用:

    • marginNone :内部_commtype设置为COMM_PERC并且_stocklike设置为True (使用股票操作 %-wise)

    • margin不是None : _commtype设置为COMM_FIXED_stocklike设置为False (使用期货固定往返佣金操作)

    如果此参数设置为None以外的其他值,则它将传递给内部_commtype属性,并且对参数stocklike和内部属性_stocklike

  • stocklike (def: False ): 指示该工具是 Stock-like 还是 Futures-like (参见上面的commtype讨论)

  • percabs (def: False ): commtype设置为 COMM_PERC 时,参数commission是否必须理解为 XX% 或 0.XX

    如果此参数为True :0.XX 如果此参数为False :XX%

  • interest (def: 0.0 )

    如果该值非零,则为持有卖空头寸收取的年利息。这主要用于股票卖空

    公式: days * price * abs(size) * (interest / 365)

    必须以绝对值指定:0.05 -> 5%

    笔记

    可以通过覆盖方法来更改行为: _get_credit_interest

  • interest_long (def: False )

    某些产品(例如 ETF)会收取空头和多头头寸的利息。如果 ths 为Trueinterest不为零,则将在两个方向上收取利息

  • leverage (def: 1.0 )

    与所需现金相关的资产杠杆率

- ``_stocklike``()

用于股票类/期货类行为的最终值

-``_commtype``()

用于 PERC 与 FIXED 佣金的最终价值

这两个在内部使用而不是声明的参数来激活()

上述对遗留 ``CommissionInfo``() 的兼容性检查

目的()

类反向交易者 .CommissionInfo ()

实际佣金计划的基类。

创建 CommInfoBase 是为了支持backtrader提供的原始、不完整的支持。新的佣金计划派生自此类,它是CommInfoBase的子类。

percabs的默认值也更改为True

参数:

  • percabs (def: True ): commtype设置为 COMM_PERC 时,参数commission是否必须理解为 XX% 或 0.XX

    如果此参数为:0.XX 如果此参数为:XX%

获取杠杆()

返回此佣金计划允许的杠杆水平

getsize(价格,现金)

返回在给定价格下满足现金操作所需的大小

获取运营成本(大小,价格)

返回操作所需的现金金额

getvaluesize(大小,价格)

返回给定价格的大小值。对于类似未来的对象,它固定为size * margin

获取价值(位置,价格)

返回给定价格的头寸值。对于类似未来的对象,它固定为size * margin

get_margin(价格)

返回给定价格的单个资产项目所需的实际保证金/担保。默认实现具有此策略:

  • 如果 param automargin评估为False ,则使用 param margin

  • 使用参数mult ,即mult * price if automargin < 0

  • 使用参数automargin ,即automargin * price if automargin > 0

获得佣金(尺寸,价格)

计算给定价格的操作佣金

_getcommission(大小,价格,伪运行)

计算给定价格的操作佣金

pseudoexec:如果为True ,则操作尚未运行

盈亏(大小、价格、新价格)

返回头寸的实际盈亏

现金调整(大小,价格,新价格)

计算给定价格差异的现金调整

get_credit_interest(数据,pos,dt)

计算卖空或特定产品的信用额度

_get_credit_interest(数据、大小、价格、天数、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

dt0dt1在默认实现中不使用,并作为覆盖方法的额外输入提供

推荐阅读

相关文章

Backtrader砖块

Renko Bricks 是呈现价格演变的另一种方式,其中价格比时间发挥更重要的作用。这已在1.9.54.122的1.9.54.122版本中作为过滤器引入Stockcharts 对 Renko Bricks 有很好的参考。

Backtrader教程:仓位

资产的头寸通常从策略中检查: position (财产)或 getposition(data=None, broker=None) 这将返回策略在默认broker状态下datas[0]的位置,由cerebro 仓位只是指示: 资产被持有size 平均价格price 它作为一种状态,

Backtrader实际使用方式

最后,似乎已经付出了开发 backtrader是值得的。 在观察 last 周的欧洲市场时,似乎世界末日了,一位朋友问我是否可以看看我们图表包中的数据,看看与以前类似情况相比,下跌幅度如何。 当然可以,但我说我可以做的不仅仅是查看图表,因为我可以快速: 创建一个快速LegDown 指示器来测量跌落的范围。

Backtrader唐钟斯10天连胜

它已成为新闻。DJI正在创下历史新高,已经连续10个上涨日和9个历史高点。例如,请参阅: 当然,许多人已经注意到道琼斯指数处于这样的状态,这篇文章只是告诉我们它正在成为主流。

Backtrader数据同步

在最新版本中,次要编号已从 8 移至 9,以指示即使已考虑兼容性,也可能会对行为产生一些影响。 在 1.9.0.99 版中,使用 datetime 同步多个数据的整个机制已经重新设计(适用于下一个和一次模式)。

Backtrader节省内存

1.3.1.92版本已经重新设计并完全实现了以前到位的内存节省方案,尽管没有太多的吹捧和使用。

Backtrader开场作弊

“发布”1.9.44.116 添加了对 Cheat-On-Open的支持。这似乎是那些全力以赴的人的需求功能,他们在酒吧 close 后进行了计算,但希望与 open 价格相匹配。 当开盘价跳空(上涨或下跌,取决于是否buysell有效)并且现金不足以进行全面运营时,这样的用例就会失败。这将强制代理拒绝该操作。

Backtrader教程:分析仪 - PyFolio

注意 从(至少)2017-07-25pyfolio 开始,API已更改,不再 create_full_tear_sheet 具有 gross_lev 作为命名参数的参数。

Backtrader多重交易

即使在相同的数据上运行,现在也可以为每笔交易添加唯一标识符。根据Tick Data and Resampling 版本backtrader的请求,支持“MultiTrades”,即:为订单分配tradeid的能力。此 id 被传递给Trades ,这使得有可能拥有不同类别的交易并同时打开它们。

Backtrader 教程:佣金计划 - 扩展

佣金和相关功能由单个类CommissionInfo管理,该类主要通过调用broker.setcommission进行实例化。该概念仅限于具有保证金和每份合约固定佣金的期货以及具有基于价格/规模百分比的佣金的股票。不是最灵活的计划,即使它已经达到了目的。