• 量化交易学习笔记(2) 优化突破策略


    量化交易学习笔记(2) 突破策略

    前言

    在上一篇文章中,通过收盘价与SMA设计的突破策略回测结果表现十分糟糕,在本文将引入突破比例,和优化器对策略进行优化,改进策略,使其变的可以持续盈利。

    策略思想

    指标

    使用SMA和收盘价

    参数说明

    X: 周期
    S: 止盈比
    L:止损比
    C: 回调比
    B:上涨幅度
    N: k线数量

    买入信号

    连续N根k线下跌
    收盘价>smaX
    当日收盘价>昨日收盘价
    当日涨幅>B

    订单

    交易价格:昨日收盘价*(1 - C)
    止损价格: 交易价格*(1 - L)
    止盈价格:交易价格*(1 + S)

    回测结果

    参数

    周期:X =100
    止损:S= 0.05
    止损比:L=0.01
    回调比:C= 0.02
    上涨幅度:B=0.04
    K线数量:N=5

    回测Value
    初始资金10000
    期货品种ETH
    时间级别1H
    回测时间2017.7.15 - 2022.7.24
    倍数1
    手续费1%
    总盈利-813.7949095640433

    在这里插入图片描述
    优化后的参数

    周期:X =100
    止损:S= 0.05
    止损比:L=0.01
    回调比:C= 0.02
    上涨幅度:B=0.04
    K线数量:N=5

    回测Value
    初始资金10000
    期货品种ETH
    时间级别1H
    回测时间2017.7.15 - 2022.7.24
    倍数1
    手续费1%
    总盈利-813.7949095640433

    核心代码

    加载数据

    def load_csv_data(data_path, size=None, start=None, end=None):
        return bt.feeds.GenericCSVData(
            dataname=data_path,
            nullvalue=0.0,
            fromdate=start,
            todate=end,
            dtformat="%Y-%m-%d %H:%M:%S",
            timeframe=bt.TimeFrame.Minutes,
            datetime=0,
            high=1,
            low=2,
            open=3,
            close=4,
            volume=5,
            openinterest=-1
        )
    
    def create_cerebro(cash=10000.0, commission=0.01, stake=1, strategy=None):
        """
        :param data: 数据
        :param cash: 初始资金
        :param commission: 佣金率
        :param stake: 交易单位大小
        :param strategy: 交易策略
        :return:
        """
        cerebro = bt.Cerebro()
        # 设置启动资金
        cerebro.broker.setcash(cash)
        # 设置交易单位大小
        cerebro.addsizer(bt.sizers.FixedSize, stake=stake)
        # 设置佣金率为千分之一
        cerebro.broker.setcommission(commission)
        # 显示回测过程中的买入和卖出信号
        cerebro.addobserver(bt.observers.Value)
        # 显示了回测过程中的买入和卖出信号
        cerebro.addobserver(bt.observers.BuySell)
        return cerebro
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    突破策略

    import datetime
    import backtrader as bt
    from utils import load_csv_data
    class BreakthroughStrategy(bt.Strategy):
        """
        """
        params = dict(
            break_through=0.04,  # 上涨幅度
            callback=0.02,  # 价格回调比例
            period=100,  # sma周期
            down_day=5,  # 连续下跌天数
            stop_loss=0.05,  # 止损比例
            take_profit=0.1,  # 止盈比例
            validity_day=3,  # 订单有效期
            expired_day=1000,  # 订单失效期
        )
    
        def notify_order(self, order):
            if order.status == order.Completed:
                self.holdstart = len(self)
            if not order.alive() and order.ref in self.orefs:
                self.orefs.remove(order.ref)
    
        def __init__(self):
            self.holdstart = None
            self.dataclose = self.datas[0].close  # 收盘价
            self.sma = bt.ind.SMA(period=self.p.period, plot=True)  # SMA
            self.orefs = list()  # order列表,用于存储尚未执行完成的订单
    
        def next(self):
            # 有尚未执行的订单
            if self.orefs:
                return
                # 尚未进场
                # 获取近几日收盘价用于判断是否连续下跌
            last_closes = list()
            for i in range(1, self.p.down_day + 1):
                last_closes.append(self.dataclose[-i])
            if not self.position:
    
                # 判断十分突破
                is_break = False
                if self.dataclose[0] > self.dataclose[-1] \
                        and (self.data.high[0] - self.dataclose[0]) / self.dataclose[0] > self.p.break_through:
                    is_break = True
                # 连续N日下跌 在 sma上方
                if last_closes == sorted(last_closes, reverse=False) and is_break and self.dataclose[0] > self.sma[0]:
                    p1 = self.dataclose[0] * (1.0 - self.p.callback)
                    p2 = p1 - self.p.stop_loss * p1
                    p3 = p1 + self.p.take_profit * p1
                    # 计算订单有效期
                    validity_day = datetime.timedelta(self.p.validity_day)
                    expired_day = valid3 = datetime.timedelta(self.p.expired_day)
                    size = min(self.broker.getcash() / self.data.high[0], self.data.volume)
                    os = self.buy_bracket(size=size,
                                          price=p1, valid=validity_day,
                                          stopprice=p2, stopargs=dict(valid=expired_day),
                                          limitprice=p3, limitargs=dict(valid=valid3), )
                    # 保存激活的的订单
                    self.orefs = [o.ref for o in os]
    
        def set_params(self, params):
            """
            设置参数
            :param params:
            :return:
            """
            self.params = params
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    优化器

    class BreakThroughStrategyOptimizer:
        # 策略参数
        params = dict(
            break_through=0.04,  # 上涨幅度
            callback=0.02,  # 价格回调比例
            period=100,  # 周期
            down_day=5,  # 连续下跌天数
            stop_loss=0.05,  # 止损比例
            take_profit=0.1,  # 止盈比例
        )
        # 调优参数范围
        optimize_params = dict(
            break_through=[0.01, 0.4],  # 上涨幅度
            callback=[0.01, 0.4],  # 价格回调比例
            period=[20, 200],  #
            down_day=[2, 20],  # 连续下跌天数
            stop_loss=[0.01, 0.4],  # 止损比例
            take_profit=[0.01, 0.4],  # 止盈比例
        )
    
        def __init__(self):
            # 数据
            self.data = None
            # 持有现金
            self.cash = 10000.0
            # 资产
            self.value = 10000.0
            self.cerebro = None
            # 迭代次数
            self.event_num = 500
    
        def log(self, txt):
            """
            日志记录
            :param txt:
            :return:
            """
            print(txt)
    
        def set_data(self, feed_data):
            """
            设置数据
            :param feed_data:
            :return:
            """
            self.data = feed_data
    
        def get_data(self):
            """
            获取数据
            :return:
            """
            return self.data
    
        def get_optimize_total_assets(self):
            """
            获取最优策略回测后的总资产
            :return:
            """
            self.params_optimize(
                break_through=self.params['break_through'],
                callback=self.params['callback'],
                down_day=self.params['down_day'],
                period=self.params['period'],
                stop_loss=self.params['stop_loss'],
                take_profit=self.params['take_profit'],
                is_show_plot=False
            )
            return self.cerebro.broker.getvalue()
    
        def params_optimize(self, break_through, callback, down_day, period, stop_loss=0.05, take_profit=0.01):
            """
            参数优化
            :param down_day: 连续下跌天数
            :param period: 周期
            :param break_through: 突破比例
            :param callback: 价格回撤
            :param stop_loss: 盈亏比 止损
            :param take_profit: 盈亏比 止盈
            """
            kines = self.get_data()
            self.cerebro = create_cerebro(self.cash)
            self.cerebro.adddata(kines)
            self.cerebro.addstrategy(BreakthroughStrategy,
                                     break_through=break_through,
                                     callback=callback,
                                     period=int(period),
                                     down_day=int(down_day),
                                     stop_loss=stop_loss,
                                     take_profit=take_profit)
            self.cerebro.run()
            rate = self.cerebro.broker.getvalue() / self.cash
            if rate > 10:
                self.log(f"rate: {rate} ")
                self.log(f"break_through:{break_through}")
                self.log(f"callback: {callback}")
                self.log(f"period: {period}")
                self.log(f"down_day: {down_day}")
                self.log(f"stop_loss: {stop_loss}")
                self.log(f"take_profit: {take_profit}")
                self.log(f"asset: {(self.cerebro.broker.getvalue())}")
                self.log("")
            return self.cerebro.broker.getvalue()
    
        def run(self):
            opt = optunity.maximize(
                f=self.params_optimize,
                num_evals=self.event_num,
                solver_name='particle swarm',
                break_through=[0.01, 0.4],
                callback=[0.01, 0.4],
                down_day=[2, 20],
                period=[20, 200],
                stop_loss=[0.02, 0.2],
                take_profit=[0.02, 0.4]
            )
            optimal_pars, details, _ = opt  # optimal_pars 最优参数组合
            self.params = optimal_pars
            print(self.params)
    
        def get_trader_info(self):
            return 'Starting Portfolio Value: %.2f \n' % self.cash \
                   + 'Final Portfolio Value: %.2f \n' % self.cerebro.broker.getvalue() \
                   + f'Final Portfolio Percentage: {100.0 * self.cerebro.broker.getvalue() / self.cash} %\n' \
                   + f'PARAMS {self.params}\n'
    
        def get_optimal_parameters(self):
            """
            获取最优参数
            :return:
            """
            return self.params
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132

    运行策略

    if __name__ == '__main__':
        path = "D:\\work\\git\\Tools\\static\\data\\ETHUSDT_1h.csv"
        data = load_csv_data(path)
        cerebro = create_cerebro()
        cerebro.adddata(data)
        cerebro.addstrategy(BreakthroughStrategy)
        cerebro.run()
        print(".2f" % cerebro.broker.getvalue() - 10000)
        cerebro.plot()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    运行优化器

    if __name__ == '__main__':
        path = "D:\\work\\git\\Tools\\static\\data\\ETHUSDT_1h.csv"
        k_data = load_csv_data(path, datetime.datetime(2021, 1, 1), datetime.datetime(2021, 12, 1))
        optimizer = BreakThroughStrategyOptimizer()
        optimizer.set_data(k_data)
        optimizer.run()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    总结

    目前从回测结果来看,该策略在破产的的边缘试探,表现结果十分糟糕。下一篇文章会对该策略进行优化,使策略可持续盈利,加油!!!!

  • 相关阅读:
    python内置模块subprocess 模块,创建和管理子进程
    Apollo学习笔记(27)李群、李代数
    三大基础排序 -选择排序、冒泡排序、插入排序
    什么蓝牙耳机的音质比较好?2022最适合听歌的蓝牙耳机推荐
    labview串口大数据量报错的一种解决思路(通过tcp进行写入和读取串口数据)
    【ML特征工程】第 1 章 :机器学习管道
    微信小程序备案内容常见问题汇总
    基于BP神经网络、RBF神经网络以及PSO优化的RBF神经网络进行数据的预测(Matlab代码实现)
    使用cpolar发布群晖NAS博客网站 4(7.X版)
    【Leetcode】140.单词拆分II(Hard)
  • 原文地址:https://blog.csdn.net/weixin_43688870/article/details/126132259