在本篇博文中,我们要向回测系统中加入佣金、印花税、过户费等交易成本项目,使我们的回测系统更加真实。
根据当前A股市场税费的规定,主要有以下三项:
在原始的BackTrader中,只允许收取固定比例佣金,所以我们需要扩展其功能。我们首先扩展CommInfoBase类:
# A股手续类,主要包括三项:
# 1. 佣金:交易额的0.3%,起征点5元,双向收取;
# 2. 过户费:交易额的0.02%,双向收取;
# 3. 印花税:交易额的0.1%,卖出方收取;
# 其他如经手费和证管费通常都包含在佣金中收取
import backtrader as bt
class AshareCommInfo(bt.CommInfoBase):
def __init__(self, comm_rate=0.003, trans_rate=0.0002, stamp_duty_tax=0.001):
super(AshareCommInfo, self).__init__()
self.comm_rate = comm_rate # 佣金
self.trans_rate = trans_rate # 过户费
self.stamp_duty_tax_rate = stamp_duty_tax # 印花税
def _getcommission(self, size, price, pseudoexec):
amount = abs(size) * price
comm_fee = amount * self.comm_rate
if comm_fee < 5.0:
comm_fee = 5.0
trans_fee = amount * self.trans_rate
stamp_duty_tax_fee = 0.0
if size < 0:
#卖出
stamp_duty_tax_fee = amount * self.stamp_duty_tax_rate
return comm_fee + trans_fee + stamp_duty_tax_fee
def getcommission(self, size, price):
'''
计算交易费用方法
'''
return self._getcommission(size, price, pseudoexec=True)
我们需要修改backtrader/broker.py中的BrokerBase类,添加如下方法:
class BrokerBase(with_metaclass(MetaBroker, object)):
def set_commission_obj(self, comm_obj, name=None):
self.comminfo[name] = comm_obj
我们在程序启动时,加入这个手续费类即可,如下所示:
class BtApp(object):
def startup(self, code='sh601318', start_cash=100000.0):
print('回测系统示例程序 v0.0.2')
cerebro = bt.Cerebro()
cerebro.addstrategy(TestStrategy)
stock_zh_a_daily_df = ak.stock_zh_a_daily(
symbol=code, adjust="hfq"
) # 通过 AkShare 获取需要的数据
data = bt.feeds.PandasData(dataname=stock_zh_a_daily_df) # 规范化数据格式
cerebro.adddata(data) # 将数据加载至回测系统
cerebro.broker.setcash(start_cash) # 设置初始资金
cerebro.broker.set_commission_obj(AshareCommInfo())
print(cerebro.broker)
print('期初净值: {0:.2f}'.format(cerebro.broker.getvalue()))
cerebro.run()
print('期末净值: {0:.2f}'.format(cerebro.broker.getvalue()))
到此为止,我们就有了一个基本的A股市场回测系统了。但是运行这个程序,大家会发现,我们从原来的略有盈利,由于有手续费的存在,变为亏损3000元了,所以我们需要改进我们的策略。