【Backtrader】自定义手续费

509 阅读2分钟

前言

手续费是量化当中不可以忽视的一个参数,普通人可能可以不用太在乎,但是量化的话,动辄每年成千上万次交易,这个成本肯定是不能忽视的。

Backtrader 自身提供了 commission 的功能,但是默认的功能比较基础,无法应对咱们大 A 股的这种规则。好在它留了 自定义 的功能,那么就让我们来看下怎么自定义一个 A 股手续费的逻辑吧。

正文

先来看下 A 股主流手续费规则:

  1. 手续费率 0.5‰,即万五。不同证券公司的费率不一样,所以必须要求可自定义;
  2. 手续费最低 5 元,即不足 5 元的,按照 5 元算;
  3. 印花税 1‰,只在卖出时收取;

逻辑有了,我们来看下实现思路。首先,根据 文档 我们可以看出,要自定义一个 class,我们假设叫 CommInfo_CN。它需要继承 bt.CommInfoBase,然后做一些逻辑复写。并且在使用时要调用 addcommissioninfo 方法,而不再是 setcommission,如下:

# useage
comm_info = CommInfo_CN(commission=0.05) # 0.05%
cerebro.broker.addcommissioninfo(comm_info)

接下来就来看一下如何进行逻辑复写。看完 文档 发现,只有通过复写 _getcommission 方法,才能满足我们的需求。

该方法的参数当中有 sizeprice,很好理解,值得注意的是当卖出时,size 是负数。这意味着我们在进行手续费计算时,要取绝对值,否则卖单反而是越卖约赚钱的。另外,也可以用此来判断当前是买单还是卖单,也就是是否需要计算印花税。

另外,其还有 self 参数,可以通过 self.params(可简写为 self.p)来获取实例化时传入的参数值,比如 commission,即不同的手续费率。

最后要注意,传入的 commission 的默认单位是 %。也就是说如果传的是 1,实际上计算时是 1% 也就是 0.01。所以要是想计算「万五」的费率,应该传入 0.05。这与 setcommission 不太一样,setcommission 传入多少就是多少。

关键点都齐了,直接上代码:

class CommInfo_CN(bt.CommInfoBase):
    def _getcommission(self, size, price, pseudoexec):
        if self.p.commission == 0:
            return 0
        # basic commission
        comm = max(5, abs(size * price * self.p.commission))
        # stamp tax, only sell order
        if size < 0:
            comm -= size * price * 0.001
        return comm 

在配合上上文的使用代码,自定义手续费率的功能就完成了。

总结

总体来说不太难,但是还有个 pseudoexec 参数,笔者还没有彻底搞懂。从字面意义来看是「伪执行」。官方的 demo 中也有这样的一段逻辑:

        if not pseudoexec:
           # keep track of actual real executed size for future discounts
           self.negotiated_volume += size

大意是,如果不是「伪执行」,就将 negotiated_volume 累加更新。这个逻辑适用于「交易量达到 xx,就打 xx 折」的场景,这种场景在现实中也是普遍存在的。不过这就复杂多了,比如每月清零之类的。本文中的例子还算是简单的,不过理论上更复杂逻辑的实现也不是什么大问题,关键就要使用到 self 的各种信息了,这里就不展开了。

你执行力的上限,不会超过你对最佳实践理解的下限。——一堂 Trueman