股票量化交易进阶004_回测框架backtrader(四)

目录

  • 前言
  • 一、构建双均线策略
    • 1.双均线概念
    • 2. 双均线的优点
    • 3. 使用backtrader回测双均线策略
    • 4.得出双均线最优参数
    • 5.可能碰到到问题
  • 总结


前言

写一个双均线策略,使用backtrader进行回测,并选出双均线的最佳参数


一、构建双均线策略

1.双均线概念

先说单均线策略,单均线策略是量化交易策略里面比较典型的一种。单均线的交易策略逻辑比较简单:价格上穿均线时买入,价格下穿均线时卖出。

而双均线策略,顾名思义,就是两根均线:短期均线和长期均线。当短线均线上穿长期均线(金叉)时买入,当短期均线下穿长期均线(死叉)时卖出,这就是双均线策略的核心思想。

可参考:股票python量化交易020-双均线策略(上)

2. 双均线的优点

对于均线应该是众多投资者经常会用到的技术指标,但是大家也熟知在股价原有趋势发生反转时,由于均线的追踪趋势的特性,均线的行动往往过于迟缓,调头速度落后于大趋势。这也是是均线的一个极大的弱点。在市场中,单独依靠均线获取超额收益的投资者非常之少,虽然都知道金叉买入,死叉卖出,但执行起来却非常困难。

双均线策略要优于单均线策略,在股市震荡中,双均线策略的波动要小很多。

3. 使用backtrader回测双均线策略

逻辑很简单,直接上代码:


import backtrader as bt
from backtrader import cerebro

import data.stock as st
import pandas as pd


class MyStrategy(bt.Strategy):
    params = (
        ('short_ma', 10),
        ('long_ma', 120),
    )

    def log(self, txt, dt=None):
        dt = dt or self.datas[0].datetime.date(0)
        print('%s %s ' % (dt.isoformat(), txt))

    def __init__(self):

        self.order = None

        self.ma1 = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.short_ma)
        self.ma2 = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.long_ma)
        # print(self.ma1, self.ma2)

    def next(self):
        if self.order:
            return
        if not self.position:
            if self.ma1[0] > self.ma2[0]:
                print('==买入:买入时短期均线价格%.2f,长期均线价格%.2f' % (self.ma1[0], self.ma2[0]))
                self.order = self.buy(size=200)
        else:
            if self.ma1[0] < self.ma2[0]:
                print('==卖出:卖出时短期均线价格%.2f,长期均线价格%.2f' % (self.ma1[0], self.ma2[0]))
                self.order = self.sell(size=200)

    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            # 做多/做空 订单 已提交/已执行 到/被代理 - 无事可做
            return

        # 检查订单是否已经完成
        # 注意:如果没有足够资金,代理可能拒绝订单
        if order.status in [order.Completed]:
            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
            else:  # 做空
                self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))

            self.bar_executed = len(self)

        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log('Order Canceled/Margin/Rejected===可能资金不足!!!')

        self.order = None

    def notify_trade(self, trade):
        if not trade.isclosed:
            return

        self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
                 (trade.pnl, trade.pnlcomm))


def feed_data(code, startDate, endDate):
    '''
    自定义方法:backtrader喂数据
    :param code: 股票代码
    :param startDate: 开始日期
    :param endDate: 结束日期
    :return: bt.feeddata
    '''
    # 1.1 拿到数据
    data = st.get_csv_price(code, startDate, endDate)
    data['date'] = pd.to_datetime(data.index)
    print(data.head())
    # 1.2 把数据传入bt.feeds
    data = bt.feeds.PandasData(
        dataname=data,
        fromdate=pd.to_datetime(startDate),
        todate=pd.to_datetime(endDate),
        datetime='date',
        open='open',
        high='high',
        low='low',
        close='close',
        volume='volume',
        openinterest=-1
    )
    return data


# 入口函数
if __name__ == '__main__':
    '''1.喂数据'''
    code = '000001.XSHE'
    # code = '688981.XSHG'
    # code = '000799.XSHE'
    data = feed_data(code, '2016-01-01', '2022-05-26')
    '''2.构建自己的策略(见上MyStrategy)'''
    '''3.加入数据'''
    cerebro = bt.Cerebro()
    cerebro.adddata(data)
    '''4.加入策略'''
    cerebro.addstrategy(MyStrategy)
    '''5.设置初始资金'''
    startCash = 100000
    cerebro.broker.setcash(startCash)
    '''6.设置佣金'''
    cerebro.broker.setcommission(0.0002)
    '''5.运行cerebro'''
    cerebro.run()
    ''''''
    endCash = cerebro.broker.getvalue()
    print(f'剩余总资金:{round(endCash, 2)}')
    netIncome = round(endCash - startCash, 2)
    # print(f'净收益:{round(endCash-startCash,2)} ')
    print(f'净收益:{netIncome}')
    print('净收益率:%.6f' % (netIncome / startCash))
    cerebro.plot()

打印结果:
==买入:买入时短期均线价格13.48,长期均线价格13.46
2020-08-10 BUY EXECUTED, Price: 13.56, Cost: 2712.00, Comm 0.54
==卖出:卖出时短期均线价格22.29,长期均线价格22.50
2021-07-12 SELL EXECUTED, Price: 21.50, Cost: 2712.00, Comm 0.86
2021-07-12 OPERATION PROFIT, GROSS 1588.00, NET 1586.60
剩余总资金:102574.72
净收益:2574.72
净收益率:0.025747

图表展示
股票量化交易进阶004_回测框架backtrader(四)_第1张图片

4.得出双均线最优参数

在上面双均线策略代码基础上运行策略优化方案,cerebro.optstrategy(xx)函数,通过optstrategy方法,给策略设置范围值,让策略逐个执行,对比结果。

	# 普通方法添加策略
    # cerebro.addstrategy(MyStrategy)
    # 均线参数容器
    short_ma_params = [5, 10, 20, 30, 60, 120]
    long_ma_params = [10, 20, 30, 60, 120, 250]
    # 带优化参数的形式添加策略
    cerebro.optstrategy(
        MyStrategy,
        short_ma=short_ma_params,
        long_ma=long_ma_params
    )

添加打印代码:
添加结果到策略回测stop函数中,由于双均线策略,短期均线是要小于长期均线参数的,我这里是简单做了一次过滤,后面可优化。

    def stop(self):
        if self.params.short_ma < self.params.long_ma:
            self.final_profit = (self.broker.getvalue() / self.startCash) - 1
            self.log('=====(短期均线%d,长期均线%d)的初始资金:%f ,剩余资金:%f ,净收益:%f ,最终收益率: %.4f' %
                     (self.params.short_ma, self.params.long_ma, self.startCash, self.broker.getvalue(),
                      (self.broker.getvalue() - self.startCash), self.final_profit))

打印结果:
2022-05-26 =====(短期均线5,长期均线10)的初始资金:100000.000000 ,剩余资金:99378.534400 ,净收益:-621.465600 ,最终收益率: -0.0062
2022-05-26 =====(短期均线5,长期均线20)的初始资金:100000.000000 ,剩余资金:98290.735200 ,净收益:-1709.264800 ,最终收益率: -0.0171
2022-05-26 =====(短期均线5,长期均线30)的初始资金:100000.000000 ,剩余资金:99452.603200 ,净收益:-547.396800 ,最终收益率: -0.0055
2022-05-26 =====(短期均线5,长期均线60)的初始资金:100000.000000 ,剩余资金:102611.463200 ,净收益:2611.463200 ,最终收益率: 0.0261
2022-05-26 =====(短期均线5,长期均线120)的初始资金:100000.000000 ,剩余资金:103953.973600 ,净收益:3953.973600 ,最终收益率: 0.0395
2022-05-26 =====(短期均线5,长期均线250)的初始资金:100000.000000 ,剩余资金:100995.843200 ,净收益:995.843200 ,最终收益率: 0.0100
2022-05-26 =====(短期均线10,长期均线20)的初始资金:100000.000000 ,剩余资金:100495.216000 ,净收益:495.216000 ,最终收益率: 0.0050
2022-05-26 =====(短期均线10,长期均线30)的初始资金:100000.000000 ,剩余资金:97436.436800 ,净收益:-2563.563200 ,最终收益率: -0.0256
2022-05-26 =====(短期均线10,长期均线60)的初始资金:100000.000000 ,剩余资金:101794.530400 ,净收益:1794.530400 ,最终收益率: 0.0179
2022-05-26 =====(短期均线10,长期均线120)的初始资金:100000.000000 ,剩余资金:105149.440800 ,净收益:5149.440800 ,最终收益率: 0.0515
2022-05-26 =====(短期均线10,长期均线250)的初始资金:100000.000000 ,剩余资金:102120.692000 ,净收益:2120.692000 ,最终收益率: 0.0212

可以看到,平安银行这只股票,如果执行10均线与120日均线的双均线策略,达到的收益是最高的。

5.可能碰到到问题

  • 使用优化参数函数后,可能会报下面的错:

Backtrader issue - optstrategy AttributeError: module ‘collections’ has no attribute ‘Iterable’

解决办法:把 python 3.10版本降到 python 3.9 或 3.9以下版本,可解决问题

  • 如果要过滤log信息,可以安装Grep Console插件在控制台窗口中过滤输出
    Grep Console插件的使用可查阅本身的官网:https://plugins.jetbrains.com/plugin/7125-grep-console

总结

以上就是用backtrader双均线策略择优参数的方法。

之后可以自行实现遍历所有A股并分别进行双均线的择优参数回测,列出每支票的最优短期均线和长期均线参数,看是否能找到一些规律。

你可能感兴趣的:(python股票量化交易进阶,python,股票,量化交易,开发语言)