Backtrader量化&回测8——手续费

Backtrader官方文档:https://www.backtrader.com/docu/commission-schemes/commission-schemes/

手续费是交易中必不可少的,尤其当调试策略参数时,结果都差不多,但不同参数导致的换手次数不同,此时手续费的影响就很大了,因此在backtrader中需要在计算时添加手续费

Backtrader 手续费

Backtrader有两种手续费的设置方法:

  1. 常见于期货等

    此时commission表示每一单位(每一手)的佣金,margin为保证金,mult为杠杆率,name代表是哪一个标的,不同标的可以设置不同的保证金或杠杆率

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

    上面的代码表示:每一手要交2块钱佣金,2000的保证金,此时带的是10倍杠杆

  2. 常见于股票,按照交易金额的百分比收取手续费,如下为万2.5的手续费

    cerebro.broker.setcommission(commission=0.0025)
    

在策略中获得手续费、保证金的各项数值

  • 在策略中使用self.broker.comminfo[None].p得到setcommission()的各项数值
  • 在订单中使用order.executed.comm得到订单执行的手续费金额

示例代码

下面的代码会显示保证金(手续费)不足,订单状态是order.Margin

from datetime import datetime
import backtrader
from loguru import logger
import matplotlib.pyplot as plt
import pandas as pd
import efinance


def get_k_data(stock_code, begin: datetime, end: datetime) -> pd.DataFrame:
    """
    根据efinance工具包获取股票数据
    :param stock_code:股票代码
    :param begin: 开始日期
    :param end: 结束日期
    :return:
    """
    # stock_code = '600519'  # 股票代码,茅台
    k_dataframe: pd.DataFrame = efinance.stock.get_quote_history(
        stock_code, beg=begin.strftime("%Y%m%d"), end=end.strftime("%Y%m%d"))
    k_dataframe = k_dataframe.iloc[:, :9]
    k_dataframe.columns = ['name', 'code', 'date', 'open', 'close', 'high', 'low', 'volume', 'turnover']
    k_dataframe.index = pd.to_datetime(k_dataframe.date)
    k_dataframe.drop(['name', 'code', 'date'], axis=1, inplace=True)
    return k_dataframe


class MyStrategy1(backtrader.Strategy):  # 策略
    def __init__(self):
        # 初始化交易指令、买卖价格和手续费
        self.close_price = self.datas[0].close  # 这里加一个数据引用,方便后续操作
        self.sma = backtrader.indicators.SimpleMovingAverage(self.datas[0], period=5)  # 借用这个策略,计算5日的均线

    def notify_order(self, order):  # 固定写法,查看订单情况
        # 查看订单情况
        if order.status in [order.Submitted, order.Accepted]:  # 接受订单交易,正常情况
            return
        if order.status in [order.Completed]:
            if order.isbuy():
                logger.debug('已买入, 购入金额 %.2f' % order.executed.price)
            elif order.issell():
                logger.debug('已卖出, 卖出金额 %.2f' % order.executed.price)
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            logger.debug('订单取消、保证金不足、金额不足拒绝交易')

    def next(self):  # 固定的函数,框架执行过程中会不断循环next(),过一个K线,执行一次next()
        if self.close_price[0] > self.sma[0]:
            # 执行买入
            logger.debug("buy 500 in {}, 预期购入金额 {}, 剩余可用资金 {}", self.datetime.date(), self.data.close[0],
                         self.broker.getcash())
            self.buy(size=self.broker.getcash() / self.datas[0].open[1], price=self.data.close[0])
        # 执行卖出条件已有持仓,且收盘价格跌破5日均线
        if self.position:
            if self.close_price[0] < self.sma[0]:
                # 执行卖出
                logger.debug("sell in {}, 预期卖出金额 {}, 剩余可用资金 {}", self.datetime.date(), self.data.close[0],
                             self.broker.getcash())
                self.sell(size=500, price=self.data.close[0])


if __name__ == '__main__':
    # 获取数据
    start_time = datetime(2020, 1, 1)
    end_time = datetime(2021, 1, 1)
    dataframe = get_k_data('600519', begin=start_time, end=end_time)
    # =============== 为系统注入数据 =================
    # 加载数据
    data = backtrader.feeds.PandasData(dataname=dataframe, fromdate=start_time, todate=end_time)
    # 初始化cerebro回测系统
    cerebral_system = backtrader.Cerebro()  # Cerebro引擎在后台创建了broker(经纪人)实例,系统默认每个broker的初始资金量为10000
    # 将数据传入回测系统
    cerebral_system.adddata(data)  # 导入数据,在策略中使用 self.datas 来获取数据源
    # 将交易策略加载到回测系统中
    cerebral_system.addstrategy(MyStrategy1)
    # =============== 系统设置 ==================
    # 设置启动资金为 100000
    start_cash = 1000000
    cerebral_system.broker.setcash(start_cash)
    # 设置手续费 万2.5
    cerebral_system.broker.setcommission(commission=0.00025)
    logger.debug('初始资金: {} 回测期间:from {} to {}'.format(start_cash, start_time, end_time))
    # 运行回测系统
    cerebral_system.run()
    # 获取回测结束后的总资金
    portvalue = cerebral_system.broker.getvalue()
    pnl = portvalue - start_cash
    # 打印结果
    logger.debug('净收益: {}', pnl)
    logger.debug("总资金: {}", portvalue)

所以在处理交易的时候需要预留一部分手续费,比如,修改购买的代码为:

size = self.broker.getcash() * (1 - self.broker.comminfo[None].p.commission) / self.datas[0].close

这样就可以用减去手续费后的钱购买

from datetime import datetime
import backtrader
from loguru import logger
import pandas as pd
import efinance


def get_k_data(stock_code, begin: datetime, end: datetime) -> pd.DataFrame:
    """
    根据efinance工具包获取股票数据
    :param stock_code:股票代码
    :param begin: 开始日期
    :param end: 结束日期
    :return:
    """
    # stock_code = '600519'  # 股票代码,茅台
    k_dataframe: pd.DataFrame = efinance.stock.get_quote_history(
        stock_code, beg=begin.strftime("%Y%m%d"), end=end.strftime("%Y%m%d"))
    k_dataframe = k_dataframe.iloc[:, :9]
    k_dataframe.columns = ['name', 'code', 'date', 'open', 'close', 'high', 'low', 'volume', 'turnover']
    k_dataframe.index = pd.to_datetime(k_dataframe.date)
    k_dataframe.drop(['name', 'code', 'date'], axis=1, inplace=True)
    return k_dataframe


class MyStrategy1(backtrader.Strategy):  # 策略
    def __init__(self):
        # 初始化交易指令、买卖价格和手续费
        self.close_price = self.datas[0].close  # 这里加一个数据引用,方便后续操作
        self.sma = backtrader.indicators.SimpleMovingAverage(self.datas[0], period=5)  # 借用这个策略,计算5日的均线

    def notify_order(self, order):  # 固定写法,查看订单情况
        # 查看订单情况
        if order.status in [order.Submitted, order.Accepted]:  # 接受订单交易,正常情况
            return
        if order.status in [order.Completed]:
            if order.isbuy():
                logger.debug('已买入, 购入金额 {} 手续费 {}', order.executed.value, order.executed.comm)
            elif order.issell():
                logger.debug('已卖出, 卖出金额 {} 手续费 {}', order.executed.value, order.executed.comm)
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            logger.debug('订单取消、保证金不足、金额不足拒绝交易')

    def next(self):  # 固定的函数,框架执行过程中会不断循环next(),过一个K线,执行一次next()
        if self.close_price[0] > self.sma[0]:
            # 执行买入
            # 获得默认的保证金:self.broker.comminfo[None].p.commission
            size = self.broker.getcash() * (1 - self.broker.comminfo[None].p.commission) / self.datas[0].close
            self.buy(size=size, price=self.data.close[0])
        # 执行卖出条件已有持仓,且收盘价格跌破5日均线
        if self.position:
            if self.close_price[0] < self.sma[0]:
                # 执行卖出
                self.sell(size=self.getpositionbyname(self.data._name).size, price=self.data.close[0])


if __name__ == '__main__':
    # 获取数据
    start_time = datetime(2020, 1, 1)
    end_time = datetime(2021, 1, 1)
    dataframe = get_k_data('600519', begin=start_time, end=end_time)
    # =============== 为系统注入数据 =================
    # 加载数据
    data = backtrader.feeds.PandasData(dataname=dataframe, fromdate=start_time, todate=end_time)
    # 初始化cerebro回测系统
    cerebral_system = backtrader.Cerebro()  # Cerebro引擎在后台创建了broker(经纪人)实例,系统默认每个broker的初始资金量为10000
    # 将数据传入回测系统
    cerebral_system.adddata(data)  # 导入数据,在策略中使用 self.datas 来获取数据源
    # 将交易策略加载到回测系统中
    cerebral_system.addstrategy(MyStrategy1)
    # =============== 系统设置 ==================
    # 设置启动资金为 100000
    start_cash = 1000000
    cerebral_system.broker.setcash(start_cash)
    # 设置手续费 万2.5
    cerebral_system.broker.setcommission(commission=0.00025)
    logger.debug('初始资金: {} 回测期间:from {} to {}'.format(start_cash, start_time, end_time))
    # 运行回测系统
    cerebral_system.run()
    # 获取回测结束后的总资金
    portvalue = cerebral_system.broker.getvalue()
    pnl = portvalue - start_cash
    # 打印结果
    logger.debug('净收益: {}', pnl)
    logger.debug("总资金: {}", portvalue)
backtrader是一个功能强大的量化回测框架,可以用于多股票的回测。文献提到了如何加载多只股票数据并构建交易组合进行量化回测。通过backtrader,你可以将多只股票的数据加载到回测系统中,并使用自定义的交易策略对这些股票进行回测。在回测过程中,你可以根据自己的需求设置参数阈值、优化策略,以及评估交易的业绩指标。同时,backtrader也可以进行股票组合的优化和回测,通过合理的选股范围和交易周期等参数设置,可以对股票组合的回报率、回撤、夏普比率等指标进行评估。然而,文献指出,回测实例的结果只是供参考,并不构成任何交易建议。因此,在使用backtrader进行多股票回测时,需要根据具体的情况进行参数设置和策略优化,以达到更好的回测效果。另外,文献也提到了一些建议,如如何正确实现滚动回测的算法和处理多股场景的问题等。总之,backtrader是一款非常强大的量化回测框架,可以满足多股票回测的需求,但在使用过程中需要根据具体情况进行参数设置和策略优化,才能得到更好的回测结果。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [如何用backtrader对股票组合进行量化回测?](https://blog.csdn.net/ndhtou222/article/details/106416802)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [backtrader高级walk forward多股滚动回测](https://blog.csdn.net/qtbgo/article/details/125225612)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呆萌的代Ma

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值