Backtrader:找到某个指标对某只股票的合适参数

英文代码地址: Quickstart Guide - Backtrader

这个功能太有意思了。

我的理解是每个指标都是有计算周期的,每只股票都有其振动频率(这个我们都可以看到),只要频率匹配就会发生共振,形成较大振幅,也就是较大盈利。股票的频率我们无法左右,但是我们可以通过回测找到指标的最佳参数。

测试一下这个极简单的策略:收盘价在简单移动平均价格之上买入,收盘价在简单移动均线价格之下卖出。

首先使用002169这只股票,测试结果:

2020-03-31, (均线周期 10)期末资金 998.00
2020-03-31, (均线周期 11)期末资金 1004.10
2020-03-31, (均线周期 12)期末资金 993.30
2020-03-31, (均线周期 13)期末资金 983.10
2020-03-31, (均线周期 14)期末资金 987.30
2020-03-31, (均线周期 15)期末资金 982.70
2020-03-31, (均线周期 16)期末资金 984.40
2020-03-31, (均线周期 17)期末资金 969.80
2020-03-31, (均线周期 18)期末资金 972.80
2020-03-31, (均线周期 19)期末资金 986.70
2020-03-31, (均线周期 20)期末资金 989.10
2020-03-31, (均线周期 21)期末资金 986.00
2020-03-31, (均线周期 22)期末资金 993.20
2020-03-31, (均线周期 23)期末资金 986.80
2020-03-31, (均线周期 24)期末资金 989.30
2020-03-31, (均线周期 25)期末资金 1001.90
2020-03-31, (均线周期 26)期末资金 1010.00
2020-03-31, (均线周期 27)期末资金 1003.80
2020-03-31, (均线周期 28)期末资金 1008.50
2020-03-31, (均线周期 29)期末资金 1009.80
2020-03-31, (均线周期 30)期末资金 990.40

可以看到均线周期如果设置为26为最佳。

再试一下300141这只股票:

2020-03-31, (均线周期 10)期末资金 1001.20
2020-03-31, (均线周期 11)期末资金 998.60
2020-03-31, (均线周期 12)期末资金 1002.90
2020-03-31, (均线周期 13)期末资金 1001.80
2020-03-31, (均线周期 14)期末资金 1001.70
2020-03-31, (均线周期 15)期末资金 997.00
2020-03-31, (均线周期 16)期末资金 996.40
2020-03-31, (均线周期 17)期末资金 998.40
2020-03-31, (均线周期 18)期末资金 1000.20
2020-03-31, (均线周期 19)期末资金 1005.40
2020-03-31, (均线周期 20)期末资金 1007.60
2020-03-31, (均线周期 21)期末资金 1003.60
2020-03-31, (均线周期 22)期末资金 1004.30
2020-03-31, (均线周期 23)期末资金 1000.40
2020-03-31, (均线周期 24)期末资金 999.80
2020-03-31, (均线周期 25)期末资金 1002.70
2020-03-31, (均线周期 26)期末资金 1003.80
2020-03-31, (均线周期 27)期末资金 994.80
2020-03-31, (均线周期 28)期末资金 1003.40
2020-03-31, (均线周期 29)期末资金 1005.60
2020-03-31, (均线周期 30)期末资金 997.80
大体确定19和29为最佳参数。

但是如果修改一下回测日期区间:

start=datetime(2020, 1, 31)
end=datetime(2022, 3, 31)

结果会发生变化:

2020-03-31, (均线周期 10)期末资金 1001.20
2020-03-31, (均线周期 11)期末资金 998.60
2020-03-31, (均线周期 12)期末资金 1002.90
2020-03-31, (均线周期 13)期末资金 1001.80
2020-03-31, (均线周期 14)期末资金 1001.70
2020-03-31, (均线周期 15)期末资金 997.00
2020-03-31, (均线周期 16)期末资金 996.40
2020-03-31, (均线周期 17)期末资金 998.40
2020-03-31, (均线周期 18)期末资金 1000.20
2020-03-31, (均线周期 19)期末资金 1005.40
2020-03-31, (均线周期 20)期末资金 1007.60
2020-03-31, (均线周期 21)期末资金 1003.60
2020-03-31, (均线周期 22)期末资金 1004.30
2020-03-31, (均线周期 23)期末资金 1000.40
2020-03-31, (均线周期 24)期末资金 999.80
2020-03-31, (均线周期 25)期末资金 1002.70
2020-03-31, (均线周期 26)期末资金 1003.80
2020-03-31, (均线周期 27)期末资金 994.80
2020-03-31, (均线周期 28)期末资金 1003.40
2020-03-31, (均线周期 29)期末资金 1005.60
2020-03-31, (均线周期 30)期末资金 997.80

代码:

# -*- coding: utf-8 -*-
"""
Created on Mon Feb  7 14:20:07 2022

"""

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

from datetime import datetime  # For datetime objects
import pandas as pd
import tushare as ts
import backtrader as bt

# Create a Stratey
class TestStrategy(bt.Strategy):
    params = (
        ('maperiod', 15),
        ('printlog', False),
    )

    def log(self, txt, dt=None, doprint=False):
        ''' 记录执行日志'''
        if self.params.printlog or doprint:
            dt = dt or self.datas[0].datetime.date(0)
            print('%s, %s' % (dt.isoformat(), txt))

    def __init__(self):
        # 保持对收盘价的引用
        self.dataclose = self.datas[0].close

        # 跟踪挂单
        self.order = None
        self.buyprice = None
        self.buycomm = None

        # 加入均线指标
        self.sma = bt.indicators.SimpleMovingAverage(
            self.datas[0], period=self.params.maperiod)
		
    # 订单状态通知,买入卖出都是下单
    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            return

        # Check if an order has been completed
        # Attention: broker could reject order if not enough cash
        if order.status in [order.Completed]:
            if order.isbuy():
                self.log(
                    '已买入, 价格: %.2f, 费用: %.2f, 佣金 %.2f' %
                    (order.executed.price,
                     order.executed.value,
                     order.executed.comm))

                self.buyprice = order.executed.price
                self.buycomm = order.executed.comm
            else:  # Sell
                self.log('已卖出, 价格: %.2f, 费用: %.2f, 佣金 %.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('订单取消/保证金不足/拒绝')

        # Write down: no pending order
        self.order = None

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

        self.log('交易利润, 毛利润 %.2f, 净利润 %.2f' %
                 (trade.pnl, trade.pnlcomm))

    def next(self):
        # 记录收盘价
        self.log('收盘, %.2f' % self.dataclose[0])

        # 如果有订单正在挂起,不操作
        if self.order:
            return

        # 如果没有持仓则买入
        if not self.position:

            # 收盘价在简单移动平均价格之上 ...
            if self.dataclose[0] > self.sma[0]:

                # 买入!!! (with all possible default parameters)
                self.log('买入单, %.2f' % self.dataclose[0])

                # 跟踪订单避免重复
                self.order = self.buy()

        else:
            # 如果已经持仓,收盘价在简单移动均线价格之下
            if self.dataclose[0] < self.sma[0]:
                # 全部卖L!!! (with all possible default parameters)
                self.log('SELL CREATE, %.2f' % self.dataclose[0])

                # 跟踪订单避免重复
                self.order = self.sell()
				
    # 策略结束函数,多用于参数调优
    def stop(self):
        self.log('(均线周期 %2d)期末资金 %.2f' %
                 (self.params.maperiod, self.broker.getvalue()), doprint=True)

def get_data(code,start='2010-01-01',end='2020-03-31'):
    df=ts.get_k_data(code,autype='qfq',start=start,end=end)
    df.index=pd.to_datetime(df.date)
    df['openinterest']=0
    df=df[['open','high','low','close','volume','openinterest']]
    return df

dataframe=get_data('300141')

start=datetime(2020, 1, 31)
end=datetime(2022, 3, 31)

if __name__ == '__main__':
    # Create a cerebro entity
    cerebro = bt.Cerebro()

    # Add a strategy
    strats = cerebro.optstrategy(
        TestStrategy,
        maperiod=range(10, 31))

    # 取得股票历史数据
    data = bt.feeds.PandasData(dataname=dataframe, fromdate=start, todate=end)

    # Add the Data Feed to Cerebro
    cerebro.adddata(data)

    # Set our desired cash start
    cerebro.broker.setcash(1000.0)

    # Add a FixedSize sizer according to the stake
    cerebro.addsizer(bt.sizers.FixedSize, stake=10)

    # Set the commission
    cerebro.broker.setcommission(commission=0.0)

    # Run over everything
    cerebro.run(maxcpus=1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值