量化选股——基于动量因子的行业风格轮动策略(第2部分—策略回测)

news2024/11/16 9:31:36

文章目录

  • 1. 交易策略
  • 2. Backtrader回测程序
  • 3. 回测效果
    • 3.1 2020年1月1日 - 2021年1月1日
    • 3.2 2021年1月1日 — 2022年1月1日
    • 3.3 2022年1月1日 — 2023年1月1日

动量因子的概述与测算,阿隆指标测算请参考:https://blog.csdn.net/weixin_35757704/article/details/128767040

1. 交易策略

阿隆动量因子策略构建:

  1. 相同权重,满仓持有5个指数;每卖出1个或多个指数后,都会等权重用现金买入指数,始终保持5个指数的持仓
  2. 买入:AroonDown+AroonUp>50,且当AroonUp > AroonDown时买入
  3. 卖出:AroonDown+AroonUp>50,且当AroonDown > AroonUp时卖出
  4. 买入时:
    1. 计算【aroon差额】= AroonUp-AroonDown;得到上涨与下跌动量的差额
    2. 每个指数按照 aroon差额 从大到小的顺序排序;为了买入上涨动量最强,且下跌动量最弱的指数
    3. 如果多个指数的aroon差额值相同,则按照15年到20年测算的胜率高低,按照历史测算时综合胜率的先后关系排序
    4. 然后从上到下依次买入指数,最终保持始终持仓5个指数

以 1/1000 作为摩擦成本,不计算管理费

2. Backtrader回测程序

这里我们使用backtrader回测框架,回测的内容除了在 量化策略——准备3 数据、Backtrader回测框架与quantstats评价指标 中提供的一些方法外,核心的策略代码如下:

class AroonStrategy(TemplateStrategy):
    params = (("start_date", None), ('end_date', None),)

    def __init__(self):
        super().__init__()
        # 基本配置
        self.max_hold = 5
        self.this_month = self.params.start_date.month
        total_bond_code = []
        for this_data in self.datas:
            if type(this_data).__name__ == "StockData":
                total_bond_code.append(this_data._name)
        self.total_bond_code = total_bond_code
        self.vic_dict = {'801210.SI': 0, '801110.SI': 1, '801750.SI': 2, '801120.SI': 3, '801890.SI': 4, '801080.SI': 5,
                         '801200.SI': 6, '801140.SI': 7, '801160.SI': 8, '801730.SI': 9, '801010.SI': 10,
                         '801130.SI': 11, '801760.SI': 12, '801770.SI': 13, '801050.SI': 14, '801040.SI': 15,
                         '801180.SI': 16, '801720.SI': 17, '801710.SI': 18, '801030.SI': 19, '801880.SI': 20,
                         '801170.SI': 21, '801790.SI': 22, '801150.SI': 23, '801230.SI': 24, '801740.SI': 25,
                         '801950.SI': 26, '801780.SI': 27}

    def next(self):
        """最核心的触发策略"""
        hold_bond_name = [_p._name for _p in self.broker.positions if self.broker.getposition(_p).size > 0]  # 查看持仓
        # 计算指标
        _candidate_dict = {}
        for _candidate_code in self.total_bond_code:
            _candidate_dict[_candidate_code] = {
                "aroondown": self.getdatabyname(_candidate_code).aroondown[0],
                "aroonup": self.getdatabyname(_candidate_code).aroonup[0],
            }
        candidate_df = pd.DataFrame(_candidate_dict).T
        candidate_df['aroo_energy'] = candidate_df['aroondown'] + candidate_df['aroonup']
        candidate_df['aroo_mines'] = candidate_df['aroonup'] - candidate_df['aroondown']
        candidate_df = pd.merge(candidate_df, pd.DataFrame(self.vic_dict, index=['rank']).T,
                                left_index=True, right_index=True)
        candidate_df = candidate_df.sort_values(['aroo_mines', "rank"], ascending=[False, True])

        if candidate_df['aroo_energy'].sum() == 0:
            return
        if len(hold_bond_name) < self.max_hold:
            self.get_buy_bond(candidate_df, self.max_hold - len(hold_bond_name))
        # 卖出的逻辑
        for _index, _series in candidate_df.iterrows():
            if _index in hold_bond_name:
                if _series['aroonup'] < _series['aroondown']:
                    self.sell(data=_index, size=self.getpositionbyname(_index).size,
                              valid=self.getdatabyname(_index).datetime.date(1))

    def get_buy_bond(self, candidate_df, buy_num):
        hold_bond_name = [_p._name for _p in self.broker.positions if self.broker.getposition(_p).size > 0]
        for index, series in candidate_df.iterrows():
            if series["aroo_energy"] <= 50:  # 当 AroonDown + AroonUp > 50时才执行判操作
                continue
            if index in hold_bond_name:
                continue
            buy_data = self.getdatabyname(index)
            if len(buy_data) >= buy_data.buflen():
                continue
            if series['aroonup'] > series['aroondown']:
                buy_cost_value = self.broker.getcash() / (self.max_hold - len(hold_bond_name)) * (
                        1 - self.broker.comminfo[None].p.commission)
                buy_size = buy_cost_value / self.getdatabyname(index).close[0]
                self.buy(data=buy_data, size=buy_size, exectype=bt.Order.Limit,
                         price=buy_data.close[0],
                         valid=buy_data.datetime.date(1))
                logger.debug("买入 {} size:{} 预计费用:{}".format(index, buy_size, buy_cost_value))
                buy_num -= 1
            if buy_num == 0:
                break

    def stop(self):
        # 绘制净值曲线
        wealth_curve_data = {}
        for _k, _v in self.value_record.items():
            wealth_curve_data[_k] = _v / self.broker.startingcash
        self.plot_wealth_curve(wealth_curve_data, "arron_{}_{}".format(
            self.params.start_date.strftime("%Y-%m-%d"), self.params.end_date.strftime("%Y-%m-%d")))
        # 最终结果
        daily_return = cal_daily_return(pd.Series(self.value_record))
        _, record_dict = cal_rolling_feature(daily_return)
        print(record_dict)
        print('a')

在策略中,当【aroon差额】相同时,按照测算的胜率从大到小依次买入,下面的字典便是每个行业指数胜率从大到小的排名,用于辅助排序:

{'801210.SI': 0, '801110.SI': 1, '801750.SI': 2, '801120.SI': 3, '801890.SI': 4, '801080.SI': 5,
 '801200.SI': 6, '801140.SI': 7, '801160.SI': 8, '801730.SI': 9, '801010.SI': 10,
 '801130.SI': 11, '801760.SI': 12, '801770.SI': 13, '801050.SI': 14, '801040.SI': 15,
 '801180.SI': 16, '801720.SI': 17, '801710.SI': 18, '801030.SI': 19, '801880.SI': 20,
 '801170.SI': 21, '801790.SI': 22, '801150.SI': 23, '801230.SI': 24, '801740.SI': 25,
 '801950.SI': 26, '801780.SI': 27}

3. 回测效果

3.1 2020年1月1日 - 2021年1月1日

  • 最终净值:1.21
  • 复合年增长: 0.226
  • 夏普比率: 0.885
  • 索蒂诺: 1.167
  • omega: 1.175
  • 最大回撤: -0.173
  • 年波动率: 0.253
    在这里插入图片描述

3.2 2021年1月1日 — 2022年1月1日

  • 最终净值:0.909
  • 复合年增长: -0.092
  • 夏普比率: -0.481
  • 索蒂诺: -0.663
  • omega: 0.924
  • 最大回撤: -0.191
  • 年波动率: 0.205
    在这里插入图片描述

3.3 2022年1月1日 — 2023年1月1日

  • 最终净值:0.74
  • 复合年增长: -0.255
  • 夏普比率: -1.681
  • 索蒂诺: -2.073
  • omega: 0.741
  • 最大回撤: -0.258
  • 年波动率: 0.186

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/376247.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

react源码解析1.开篇介绍和面试题

怎样学习react源码 作为前端最常用的js库之一&#xff0c;熟悉react源码成了高级或资深前端工程师必备的能力&#xff0c;如果你不想停留在api的使用层面或者想在前端技能的深度上有所突破&#xff0c;那熟悉react源码将是你进步的很好的方式。 react的纯粹体现在它的api上&a…

【神经网络】LSTM

1.什么是LSTM 长短期记忆&#xff08;Long short-term memory, LSTM&#xff09;是一种特殊的RNN&#xff0c;主要是为了解决长序列训练过程中的梯度消失和梯度爆炸问题。简单来说&#xff0c;相比普通的RNN&#xff0c;LSTM能够在更长的序列中有更好的表现。 LSTM区别于RNN地方…

Java查漏补缺(09)异常概述、Java异常体系、常见的错误和异常、异常的处理、手动抛出异常对象:throw、自定义异常

Java查漏补缺&#xff08;09&#xff09;异常概述、Java异常体系、常见的错误和异常、异常的处理、手动抛出异常对象&#xff1a;throw、自定义异常本章专题与脉络1. 异常概述1.1 什么是生活的异常1.2 什么是程序的异常1.3 异常的抛出机制1.4 如何对待异常2. Java异常体系2.1 T…

【JAVA】xxl-job服务搭建

xxl-job服务搭建 1.下载xxl-job项目 https://github.com/xuxueli/xxl-job 2.数据库表创建 3.修改配置 注意&#xff1a;这是两个项目&#xff0c;一个是xxl-job前台&#xff0c;一个是xxl-job执行器&#xff0c;找到这两个项目得配置文件&#xff0c;修改配置。 配置文件地址…

day54【代码随想录】二刷数组

文章目录前言一、二分查找&#xff08;力扣724&#xff09;二、移除元素&#xff08;力扣27&#xff09;【双指针】三、有序数组的平方&#xff08;力扣977&#xff09;【双指针】四、合并两个有序数组&#xff08;力扣88&#xff09;五、长度最小的子数组&#xff08;力扣209&…

前端学习第二阶段-第3章 Flex 伸缩布局

3-1 移动端基础知识 01-移动端基础 02-视口 03-meta视口标签 04-物理像素与物理像素比 05-二倍图 06-背景缩放background-size 07-背景二倍图以及多倍图切图 08-移动端开发选择 09-移动端技术解决方案 10-移动端特殊样式 11-移动端技术选型 12-流式布局 3-2 移动端电商首页制作…

Python基础—while循环

(1)while循环&#xff1a; 语法格式&#xff1a; while 条件&#xff1a;   执行语句1……   执行语句2…… 适用条件&#xff1a;无限循环 死循环 while True:print(条件是真的&#xff01;)代码实例&#xff1a; i 0 # 创建一个计数的变量 while i < 5: # Truepr…

感知趋势,洞察发展:2023(第十届)趋势与预测大会成功举办

2023年2月23日&#xff0c;运联年会&#xff1a;2023&#xff08;第十届&#xff09;趋势与预测大会在深圳机场凯悦酒店成功闭幕。自2014年开始&#xff0c;“运联年会&#xff1a;趋势与预测”已经连续举办九届。这场大会&#xff0c;既是一次行业性的“年终总结”&#xff0c…

【Java开发】JUC基础 01:进程、线程、多线程

1 进程与线程1.1 进程开发写的代码称为程序&#xff0c;那么我们将程序运行起来&#xff0c;我们称之为进程&#xff1b;进程就是申请一块内存空间&#xff0c;将数据放到内存空间中去&#xff0c;是系统进行资源分配和调度的基本单位。&#x1f4cc; 程序与进程的区别程序是数…

QML Item

在QML中所有的可视项目都继承自Item&#xff0c;虽然Item本身没有可视化的外观&#xff0c;但它定义了可视化项目的所有属性。 Item可以作为容器使用&#xff1a; Item{Rectangle{id:retc}Rectangle{id:retc1}Rectangle{id:retc2}Rectangle{id:retc3}} item拥有children属性…

MyBatis学习笔记(七) —— 特殊SQL的执行

7、特殊SQL的执行 7.1、模糊查询 模糊查询的三种方式&#xff1a; 方式1&#xff1a;select * from t_user where username like ‘%${mohu}%’ 方式2&#xff1a;select * from t_user where username like concat(‘%’,#{mohu},‘%’) 方式3&#xff1a;select * from t_u…

DolphinScheduler跨版本升级1.3.8至3.0.1

DolphinScheduler跨版本升级1.3.8至3.0.1Refer背景基础环境依赖版本升级修改pom.xml问题解决MYSQL升级1.文件替换2.修改表结构t_ds_process_definitiont_ds_alertt_ds_process_instance3.时间参数修改4.数据库升级DOLPHIN安装zookeeper集群创建用户dolphinscheduler_env.shinst…

指针变量作为函数参数详解,形参和实参之间的值传递如何传递?如何改变指针变量所指向的变量?

函数的参数不仅可以是整型&#xff0c;浮点型&#xff0c;字符型等数据&#xff0c;还可以是指针类型&#xff1b;它的作用是将一个变量的地址传送到另一个函数中。 关于地址&#xff0c;指针&#xff0c;指针变量可以参考我的上一篇文章&#xff1a; 地址&#xff0c;指针&…

线程的基本方法

线程等待&#xff1a;wait方法 调用wait方法的线程会进入WAITING状态&#xff0c;只有等到其他线程的通知或程序被中断才会返回。调用wait方法后会释放对象的锁&#xff0c;因此 wait方法一般被用于同步方法或同步代码块中 。 线程睡眠&#xff1a;sleep方法 调用sleep方法会导…

Spring Boot 版本升级2.2.11.RELEASE至2.7.4

2.2.11.RELEASE > 2.7.4项目更新spring-boot-starter-parent 主依赖&#xff0c;导致项目跑不起了日志也没有输出有用信息&#xff0c;自己查看源码调试启动入口打断点&#xff0c;一步步进入方法定位项目停止代码我的项目执行到SpringApplication.class 的152行代码会停止项…

华为HCIE学习之Openstack Glance组件(glance对接swift)

文章目录一、Glance的结构二、服务部署流程三、将glance存储在swift中1、默认使用swift来存储2、指定可以存在swift中3、swift版本4、keystone的endpoint地址&#xff08;当glance去找swift时通过keystone去找&#xff09;5、租户名:用户名&#xff0c;用户必须拥有admin角色6、…

【C语言】自定义类型:结构体、枚举、联合

目录 1.结构体 1.1结构体类型 1.2结构体的自引用 1.3结构体的初始化 1.4结构体内存对齐 //对齐 //offsetof //修改默认对齐数 1.5结构体传参 2.位段 2.1位段的内存开辟 2.2位段的内存分配 3.枚举 4.联合&#xff08;共用体&#xff09; //判断大小端 1.结构体…

【GO】k8s 管理系统项目23[前端部分–工作负载-Pod]

k8s 管理系统项目[前端部分–工作负载-Deployment] 1. 代码部分 1.1 准备工作 由于Pod页面和Deployment内容差不多.那么就直接把Deployment的内容复制过来.再做修改. 替换Deployment为Pod替换Deploy为Pod替换deployment为pod替换deploy为pod禁用新增的按钮,删除新增方法,表…

django后端服务、logstash和flink接入VictoriaMetrics指标监控

0.简介 通过指标监控可以设置对应的告警&#xff0c;快速发现问题&#xff0c;并通过相应的指标定位问题。 背景&#xff1a;使用的 VictoriaMetrics(简称 VM) 作为监控的解决方案&#xff0c;需要将 django 服务、logstash 和 flink 引擎接入进来&#xff0c;VM 可以实时的获…

SpringBoot:SpringBoot配置文件.properties、.yml 和 .ymal(2)

SpringBoot配置文件1. 配置文件格式1.1 application.properties配置文件1.2 application.yml配置文件1.3 application.yaml配置文件1.4 三种配置文件优先级和区别2. yaml格式2.1 语法规则2.2 yaml书写2.2.1 字面量&#xff1a;单个的、不可拆分的值2.2.2 数组&#xff1a;一组按…