[QMT量化交易小白入门]-二十五、DeepSeek生成的年化收益率54%的动量因子评分算法(含代码解析)

server/2025/2/24 14:13:03/

本专栏主要是介绍QMT的基础用法,常见函数,写策略的方法,也会分享一些量化交易的思路,大概会写100篇左右。
QMT的相关资料较少,在使用过程中不断的摸索,遇到了一些问题,记录下来和大家一起沟通,共同进步,自己淋过雨了,希望大家都有一把伞。

文章目录

      • 相关阅读
      • 一、函数概述:优化后的ETF评分函数
      • 二、数据准备与质量检查
      • 三、数据处理:实际时间序列与线性回归分析
      • 四、年化收益率计算优化
      • 五、改进的R²计算
      • 六、动态权重调整(波动率调整)
      • 七、综合评分(加入动量因子)
      • 八、创建评分DataFrame与标准化处理
      • 九、个人观点

相关阅读

小白也能做量化:零门槛QMT、Ptrade免费送
量化交易入门:如何在QMT中配置Python环境,安装第三方依赖包
年化收益达到了70%,增加了动态仓位权重调整后的全球核心资产轮动策略(含python代码解析)


上一篇文章中我们复刻了策略:量化交易python代码解析:年化收益47%的全球核心资产轮动策略

我们仍然用之前的方法,让DeepSeek帮助我们优化评分算法,他的思考逻辑如下:
在这里插入图片描述

回测的结果年化收益率达到了54%:

在这里插入图片描述
主要从以下几个方面进行了优化:
主要优化点说明:

  1. 数据质量增强
  • 增加数据长度校验(允许最多20%数据缺失)
  • 使用实际时间戳而非简单递增序号
  • 采用固定回溯窗口(避免幸存者偏差)
  1. 模型改进
  • 改用scipy的稳健线性回归
  • 使用sklearn的r2_score函数(更准确的计算)
  • 加入动量因子(21日收益率)
  1. 风险调整
  • 引入波动率调整后的夏普比率
  • 动态权重分配(收益质量与动量结合

接下来我们就具体看下优化后的评分算法

一、函数概述:优化后的ETF评分函数

def calculate_etf_scores(etf_pool, mkdict, lookback_window=63):"""优化后的ETF评分函数:param etf_pool: ETF列表:param mkdict: 包含各ETF历史数据的字典:param lookback_window: 观察窗口(默认63个交易日,约3个月):return: 排序后的ETF评分DataFrame"""

这个函数是整个策略的核心,它接收三个参数:etf_pool是待评估的ETF列表;mkdict是一个包含了各ETF历史数据的字典,通过它可以获取每个ETF的历史价格等信息;lookback_window是观察窗口,默认设置为63个交易日,大约相当于3个月的时间。函数的返回值是一个经过排序的ETF评分DataFrame,通过这个评分,可以确定哪些ETF更具投资价值。

二、数据准备与质量检查

score_list = []
valid_etfs = []for etf in etf_pool:try:#df = mkdict[etf].tail(63)  # 使用固定时间窗口df = mkdict[etf]# 数据质量检查if len(df) < lookback_window * 0.8:  # 允许20%数据缺失continue

初始化两个空列表score_listvalid_etfs,分别用于存储ETF的评分和有效的ETF名称。然后,通过循环遍历etf_pool中的每个ETF。在循环内部,注释掉的代码df = mkdict[etf].tail(63)原本是使用固定时间窗口来获取数据,但这里直接使用了df = mkdict[etf]获取整个历史数据,这可能是为了更全面地分析ETF的表现。接着进行数据质量检查,如果某个ETF的数据长度小于观察窗口的80%,即允许最多20%的数据缺失,那么就跳过该ETF,继续处理下一个。这一步确保了只对数据质量较好的ETF进行分析,避免了因数据不完整而导致的错误结果。

三、数据处理:实际时间序列与线性回归分析

# 使用实际时间序列(处理非交易日问题)
x = pd.to_numeric(df.index).values.reshape(-1, 1)
y = np.log(df['close'].values)# 稳健线性回归(处理异常值)
slope, intercept, r_value, _, _ = stats.linregress(x.flatten(), y)

为了处理非交易日的问题,将ETF的时间索引转换为数值型,并重塑为二维数组x,同时将ETF的收盘价取对数后赋值给y。这样处理的好处是,通过对数变换可以使数据更加稳定,减少极端值的影响。接下来,使用stats.linregress函数进行稳健线性回归分析。这个函数可以有效地处理异常值,得到回归直线的斜率slope、截距intercept以及相关系数r_value等参数。这些参数将为后续的分析和计算提供重要依据。

四、年化收益率计算优化

# 年化收益率计算优化
daily_growth = np.exp(slope) - 1
annualized_returns = (1 + daily_growth) ** 252 - 1  # 使用实际交易日数

根据线性回归得到的斜率slope,首先计算出每日增长率daily_growth,其计算公式为np.exp(slope) - 1。然后,使用实际交易日数(252天)来计算年化收益率annualized_returns,公式为(1 + daily_growth) ** 252 - 1。这种计算方法考虑了实际的交易天数,相比简单地按照日历天数计算更加准确和合理,能够更真实地反映ETF的年化收益情况。

五、改进的R²计算

# 改进的R²计算
y_pred = slope * x.flatten() + intercept
r_squared = r2_score(y, y_pred)

通过线性回归得到的斜率slope和截距intercept,可以计算出预测值y_pred。然后,使用r2_score函数计算决定系数r_squared,它衡量了模型对数据的拟合程度。与传统的R²计算方法相比,这里的改进之处在于它是在线性回归的基础上进行的,能够更准确地反映ETF价格与时间之间的线性关系,从而为评估ETF的表现提供了更可靠的依据。

六、动态权重调整(波动率调整)

# 动态权重调整(波动率调整)
volatility = np.std(np.diff(y)) * np.sqrt(252)
risk_adjusted_return = annualized_returns / (volatility + 1e-6)  # 防止除零

波动率是衡量资产风险的重要指标之一。在这里,通过计算ETF收盘价对数收益率的差分的标准差,并乘以一年的实际交易日数的平方根,得到年化波动率volatility。然后,用年化收益率annualized_returns除以波动率加上一个极小的值(1e-6,用于防止除零错误),得到风险调整后的收益率risk_adjusted_return。这种动态权重调整的方法考虑了ETF的风险因素,使得不同风险水平的ETF能够在一个相对公平的基础上进行比较。

七、综合评分(加入动量因子)

# 综合评分(加入动量因子)
momentum = df['close'].pct_change(21).iloc[-1]  # 1个月动量
score = (risk_adjusted_return * r_squared) + (0.3 * momentum)

除了考虑风险调整后的收益率和决定系数外,还加入了动量因子。动量因子反映了ETF价格变化的趋势,通常认为具有正动量的ETF在未来一段时间内表现可能会更好。这里,计算了ETF在过去1个月内的收盘价的百分比变化作为动量因子momentum。将风险调整后的收益率与决定系数的乘积加上动量因子的0.3倍作为综合评分score。这种综合评分方法综合考虑了多个因素,能够更全面地评估ETF的投资价值。

八、创建评分DataFrame与标准化处理

# 创建评分DataFrame
df_score = pd.DataFrame(index=valid_etfs, data={'score': score_list})# 分数标准化
df_score['score'] = (df_score['score'] - df_score['score'].mean()) / df_score['score'].std()
df_score = df_score.sort_values(by='score', ascending=False)

将所有有效的ETF及其对应的评分组合成一个DataFrame df_score。为了使评分具有可比性,对分数进行了标准化处理,即减去分数的均值再除以分数的标准差。这样处理后,得分越高的ETF表示其综合表现越好。按照分数从高到低对DataFrame进行排序,得到最终的ETF评分结果。

九、个人观点

整体年化收益率比原本提升了15%,DeepSeek用来做算法优化能力还是很强的,不涉及平台相关的行情和交易函数,写出来的代码出错明显少了很多,给出的优化建议也是从专业量化出发。要注意的是,他给出的代码没有第三方库的引入来源,需要自行甄别。目前策略回测还是比较大的,后面还需要加上仓位动态管理。


http://www.ppmy.cn/server/170344.html

相关文章

redis的缓存击穿,雪崩,穿透

缓存击穿&#xff08;Cache Breakdown&#xff09; 指某个热点数据在缓存中过期或失效的瞬间&#xff0c;大量请求直接打到数据库上&#xff0c;导致数据库压力骤增。 原因&#xff1a; 热点数据在缓存中过期。 大量并发请求同时访问该数据。 解决方法&#xff1a; 永不过期…

【Python爬虫(56)】解锁Scrapy超能力:多库集成实战

【Python爬虫】专栏简介&#xff1a;本专栏是 Python 爬虫领域的集大成之作&#xff0c;共 100 章节。从 Python 基础语法、爬虫入门知识讲起&#xff0c;深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑&#xff0c;覆盖网页、图片、音频等各类数据爬取&#xff…

【Gee】Day7:错误恢复

Day7&#xff1a;错误恢复 今天的任务是&#xff1a; 实现错误处理机制。 panic 在 Golang 中&#xff0c;较为常见的错误处理方式是返回 error&#xff0c;由调用者决定后续如何处理。但如果是无法恢复的错误&#xff0c;可以手动触发 panic&#xff0c;当然如果在程序运行…

大语言模型中的 Token如何理解?

在大语言模型中&#xff0c;Token 是文本处理的基本单元&#xff0c;类似于“文字块”&#xff0c;模型通过将文本分割成Token来理解和生成内容。举一个形象一点的例子&#xff0c;可以理解为 AI 处理文字时的“最小积木块”。就像搭乐高时&#xff0c;每块积木是基础单位一样&…

【C】队列与栈的相互转换

栈与队列是两种特点相反的数据结构&#xff0c;一个特点是后进先出&#xff0c;一个特点是先进先出&#xff0c;但是他们之间是可以相互转换的。 目录 1 用队列实现栈 1&#xff09; 题目解析 2&#xff09; 算法解析 &#xff08;1&#xff09; 结构(MyStack) &#xff…

Amazon Lex:AI对话引擎重构企业服务新范式

在数字化转型浪潮中&#xff0c;智能交互能力正成为企业服务升级的核心竞争力。全球某头部电商平台曾面临日均10万的客服咨询压力&#xff0c;传统人工客服响应慢、成本高&#xff0c;而基于规则的传统聊天机器人又难以理解复杂需求。通过部署Amazon Lex&#xff0c;该企业仅用…

响应式数据ref()和reactive()的使用

官方网址&#xff1a;响应式基础 | Vue.js 在 Vue 3 中&#xff0c;ref 和 reactive 是用于创建响应式数据的两个核心 API。它们的用法和适用场景有所不同&#xff0c;以下是它们的详细说明和使用方法。 ref ref 用于创建一个响应式的基本类型或对象类型的数据。它会将数据包装…

《Restormer:高效Transformer架构用于高分辨率图像恢复》学习笔记

paper&#xff1a;2111.09881 GitHub&#xff1a;swz30/Restormer&#xff1a; [CVPR 2022--Oral] Restormer&#xff1a; 高分辨率图像修复的高效转换器。SOTA 用于运动去模糊、图像去模糊、去噪&#xff08;高斯/真实数据&#xff09;和去焦去模糊。 复现&#xff1a;Resto…