量化投资之工具篇:Backtrader从入门到精通(11)交易 ...

论坛 期权论坛 期权     
期权匿名问答   2022-5-1 18:26   13158   1
上一篇文章中,介绍了交易系统的详细信息,但是对于commission的介绍还是比较简单,尤其是系统缺省的方案并不适用于中国。本文增加增加更多的信息,提供自定义开发的方法,并提供一个适用于国内的佣金方案代码。
佣金(CommInfoBase)

佣金方案的基本信息

佣金是交易系统中成本模型的一部分。不过,这种成本是固定且可预计的,因此较为简单,只要根据和券商的协议填写清楚即可,由于这个类过程并不复杂,不解读代码了,看几个关键点。
看过之前Cerebro代码就会知道,咱们在实例化Cerebro之后,就可以直接设置佣金方案了,如下:
     cerebro.broker.setcommission(commission=0.001)#设定交易费用(买卖都收)Cerebro获取broker(broker的实例化和初始化在2.1节描述),然后调用setcommission设置佣金:
def setcommission(self,
                       commission=0.0, margin=None, mult=1.0,
                       commtype=None, percabs=True, stocklike=False,
                       interest=0.0, interest_long=False, leverage=1.0,
                       automargin=False,
                       name=None):

         comm = CommInfoBase(commission=commission, margin=margin, mult=mult,
                             commtype=commtype, stocklike=stocklike,
                             percabs=percabs,
                             interest=interest, interest_long=interest_long,
                             leverage=leverage, automargin=automargin)
         self.comminfo[name] = commBroker首先会实例化一个CommInfoBase,然后记录到commoninfo字典中,这里要注意,一个Broker可以拥有多个CommInfoBase,通过name来区分,name和Data类创建时候使用的名字一致,也就是不同的数据源(对应不同的资产)佣金方案可以不同。在相同的券商中,针对不同的资产不同的佣金,例如对于股票和ETF佣金不同,这里就可以设置不同佣金方案。
CommInfoBase就是设置相关参数,关键参数具体解释如下表:
参数缺省值含义
commission0.0就是基本佣金数据,按照百分比或者绝对值。百分比就是按照交易的金额(size*price)的百分比提取佣金。绝对值,就是针对size提取固定佣金。比如一笔交易(或者一个合约)收取2元钱。至于具体是采取百分比还是固定值,参见下面参数说明。
commtypeNone就是用来指定佣金数据是百分比还是固定值。
ommInfoBase.COMM_PERC:按照百分比解释。
CommInfoBase.COMM_FIXED:按照固定值解释。
None:这种情况下系统根据资产情况自动判决,判决方法参见参数margin。为啥搞得这么复杂,主要是为了兼容原来老的类(CommissionInfo),原来是通过margin来判断是否百分比。
percabsFalse当 commtype 设置为 COMM_PERC 时,指定百分比数字的填写方法。比如说,你要设置0.1%,这个值设置为True,那么commission参数填写为0.001,为False的话,只要填写为0.1.为啥搞得怎么麻烦?还是兼容性问题。因为老版本中commission填写值不包括百分号,还记得咱们之前的例子要求“0.1% ,需要除以100然后去掉%”,这个就是老的处理方式,在新版本中,如果保持老的处理方式,percaps需要填写为TRUE。
stocklikeFalse这里用来指示股票类资产还是期权类资产,在老的CommissionInfo,是通过margin来确定是否期权。在新的类中,当commtype为None的时候,可以通过这个参数来指示是否股票类资产。
marginNone保证金,港股称为孖展。Margin为0或者None,那么commission就是按照百分比解释。否则按照固定值解释。主要是因为Margin主要针对的期货/期权产品,通常按合约计价。这个是之前类的使用方法,新版本不再采用。
mult1.0杠杆比,这个主要应用于期权等可以加杠杆的资产,Backtrader据此计算盈利和亏损。
name前面已叙及。
interest0.0利息。有些情况下,需要对所持资产计算利息,例如借入证券卖空,由于这些对于我们个人交易者使用不多,咱们先忽略。
interest_longFalse是否对多头仓位收取利息,和上面一样,是否对做多的资产收取利息,先忽略。
系统佣金方案还有缺点,从代码来看,有些常用的场景就无法支持,比如常用的一笔交易最小5块,这个咋设置,还有除了佣金费用之外,港股/美股还需要平台费,也没法模拟。这就需要我们进行定制。
佣金方案的开发

如前所述,在不同国家,甚至不同券商,都可能有不同的佣金方案,Backtrader提供了定制佣金方案,而且由于采用了面向对象的机制,所以定制方案也很很简单。
如何定制呢?首先,继承CommInfoBase类。然后根据你的需求修改参数和重载函数。
先说简单的自定义方法,修改参数。比如,对于股票和期权,参数不一样,那么,我们可以分别定义参数重写针对股票和期权的佣金方案。
比如,对于期权,我们定义固定值的佣金方案:
class CommInfo_Futures_Fixed(CommInfoBase):
     params = (
         ('stocklike', False),
         ('commtype', CommInfoBase.COMM_FIXED),
     )这样的话,使用的时候只需要输入commision就行了,其他参数就不要关注了。
同样的,也可以定义百分比的股票佣金方案:
class CommInfo_Stocks_Perc(CommInfoBase):
     params = (
         ('stocklike', True),
         ('commtype', CommInfoBase.COMM_PERC),
     )假如你希望按照老方法输入0.001表示0.1%,那么还可以这样定义:
class CommInfo_Stocks_PercAbs(CommInfoBase):
     params = (
         ('stocklike', True),
         ('commtype', CommInfoBase.COMM_PERC),
         ('percabs', True),
     )如果你还需要修改佣金的计算方法,那么你可以重载_getcommission 函数,定义如下,这个后面会给你一个详细的实例。
def _getcommission(self, size, price, pseudoexec):
    '''根据规模以及价格计算佣金
       '''定义好佣金方案之后,下一步就是通过Cerebro调用broker.addcommissioninfo添加这个方案。注意不是使用setcommission,因为这个函数只是针对老把能的CommissionInfo对象。如下示例:
...

comminfo = CommInfo_Stocks_PercAbs(commission=0.001)  # 0.1%
cerebro.broker.addcommissioninfo(comminfo)addcommissioninfo函数定义如下:
   def addcommissioninfo(self, comminfo, name=None):
         '''Adds a ``CommissionInfo`` object that will be the default for all assets if
         ``name`` is ``None``'''
         self.comminfo[name] = comminfo注意,可以设定佣金方案对应的资产,通过data的name来指定。
好了,来一个实战的案例,比如在咱们国内,佣金方案可能如下:

  • 佣金按照百分比。
  • 每一笔交易有一个最低值,比如5块,当然有些券商可能会免5.
  • 卖出股票还需要收印花税。
  • 可能有的平台还需要收平台费。
按照这个需求,可以定义佣金方案如下:
class MyStockCommissionScheme(bt.CommInfoBase):
    '''
    1.佣金按照百分比。
    2.每一笔交易有一个最低值,比如5块,当然有些券商可能会免5.
    3.卖出股票还需要收印花税。
    4.可能有的平台还需要收平台费。  
    '''
    params = (
        ('stampduty', 0.005),  # 印花税率
        ('commission', 0.005),  # 佣金率
        ('stocklike', True),#股票类资产,不考虑保证金
        ('commtype', bt.CommInfoBase.COMM_PERC),#按百分比
        ('minCommission', 5),#最小佣金
        ('platFee', 0),#平台费用
    )

    def _getcommission(self, size, price, pseudoexec):
        '''
        size>0,买入操作。
        size<0,卖出操作。
        '''
        if size > 0:  # 买入,不考虑印花税,需要考虑最低收费
            return max(size * price * self.p.commission,self.p.minCommission)+platFee
        elif size < 0:  # 卖出,考虑印花税。
            return max(abs(size) * price * self.p.commission,self.p.minCommission)+abs(size) * price * self.p.stampduty+platFee
        else:
            return 0  # 防止特殊情况下size为0.注释很清楚,就不解释了。
有的同学眼尖,_getcommission看到还有一个参数pseudoexec,这个干嘛的? 这个参数用于指示当前的调用是否用于真正的订单执行过程。那么怎么还会有不是订单的执行过程呢?大家还记得前面订单有一个参数checksubmit,这个参数就是要求在提交订单前检查现金是否足够,这个检查过程中也会调用_getcommission计算佣金,那么,在一次买/卖交易中,可能多次计算佣金。为了区分哪一次调用是真正的为了订单实际执行的调用,就需要使用这个参数。
区分订单实际执行有啥作用呢?比如这样一个场景,你和券商商量好了,如果交易量(规模)超过5000,就给我佣金50%的折扣。这交易量的计算就得使用这个参数来区分,否则的话,系统将一些并未执行的佣金计算也计入的话,券商也不认啊。
下面给个例子(官网)来看看。
import backtrader as bt

class CommInfo_Fut_Discount(bt.CommInfoBase):
     params = (
       ('stocklike', False),  # 期权类资产
       ('commtype', bt.CommInfoBase.COMM_FIXED),  # 固定佣金

       # 优惠方案参数
       ('discount_volume', 5000),  # 优惠所需最小合约数量
       ('discount_perc', 50.0),  # 50.0% 的折扣
     )

     negotiated_volume = 0  # 跟踪实际交易合约规模的参数

     def _getcommission(self, size, price, pseudoexec):
         if self.negotiated_volume > self.p.discount_volume:#超过最小次数,这设置折扣50%
            actual_discount = self.p.discount_perc / 100.0
         else:
            actual_discount = 0.0#没有超过,那么没有折扣

         commission = self.p.commission * (1.0 - actual_discount)#打折后的实际佣金
         commvalue = size * price * commission#计算折扣后的佣金。这里的size应该是abs(size),不然有负数。

         if not pseudoexec:
            # 跟踪实际交易合约规模
            self.negotiated_volume += size #如果实际执行(非伪执行),这需要累加本次实际交易的规模。

         return commvalue通过上述例子,应该就很容易理解pseudoexec参数的使用了。
分享到 :
0 人收藏

1 个回复

倒序浏览
2#
期权匿名回答  16级独孤 | 2022-5-1 18:27:27 发帖IP地址来自 中国
[赞同][赞同][赞同]
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:400157
帖子:80032
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP