ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python+DTW:股票价格序列相似性分析实战与避坑指南

Python+DTW:股票价格序列相似性分析实战与避坑指南 简介面向Python数据分析与金融量化初学者的课程设计资源围绕股票价格序列相似性分析展开采用动态时间弯曲DTW算法能有效度量不同长度或相位偏移序列间的相似度并通过折线图直观呈现结果弥补传统欧氏距离无法对齐时间步长的不足。包内提供完整课程报告、可运行源码、演示数据与结果截图覆盖从数据读取、预处理、DTW计算到可视化输出的完整流程报告环节完整且便于作为课程设计文档参考代码结构清晰适合二次修改与扩展。压缩包共12个文件以Python脚本、Word文档、PNG图片为主另含数据库与依赖清单整体仅2.13MB下载后即可快速复现。已有613人学习浏览内容组织清晰可帮助读者快速掌握DTW在股票价格序列分析中的落地方法具有较强的实操与参考价值。1. 股票价格序列相似性分析这份 Python 资源到底能干什么做股票数据分析的同行应该都有过这种体验两只股票日 K 线拉出来一眼看上去走势很像但用欧氏距离一算相似度却低得离谱。问题不在数据而在比较方法——价格序列存在相位偏移涨跌节奏一致但时间点对不齐传统距离度量天然不适合这种场景。这份基于 Python 的股票价格序列相似性分析资源核心就是用动态时间弯曲DTW算法解决对齐问题把两段价格序列在时间轴上“拧”到最贴近的位置再用折线图把结果直观画出来。资源包含完整源码、SQLite 数据库、依赖清单和课程设计报告适合正在做量化交易入门、时间序列分析课程设计或金融数据挖掘的从业者与学生。看完这篇文章你能照着跑通完整流程也知道 DTW 的参数边界和常见的坑在哪里。2. 为什么要用 DTW从欧氏距离的局限说起2.1 欧氏距离对时间序列的三个硬伤先把问题说透。假设你有两只股票 A 和 BA 在第 10 天开始拉升B 在第 15 天才启动之后走势几乎一样。用欧氏距离逐点相减第 10 到第 14 天这几天两者的差值会非常大最终相似度被严重拉低。这就是欧氏距离的第一硬伤对相位偏移极度敏感。第二硬伤是长度不一致。现实中你取到的价格序列可能一个 240 个交易日、一个 242 个交易日欧氏距离要求两个序列等长光这一步就把很多真实数据挡在门外。第三硬伤是噪声放大——股票价格里的小幅波动会被逐点误差累积成很大的总距离导致“相似”的判断被无关紧要的毛刺干扰。DTW 算法正是冲着这三个问题来的。它允许序列在时间轴上非线性地对齐A 序列的第 10 天可以匹配 B 序列的第 15 天只要整体弯曲代价最小。计算出的 DTW 距离越小说明两条序列在形态上越相似。这个思路最早用在语音识别上现在被大量移植到金融时间序列分析里处理的就是这种“节奏一致但相位不同”的走势对比。2.2 DTW 的核心计算思路与资源文件结构DTW 的原理不复杂但实现细节容易翻车。它本质上是在构建一个累积距离矩阵设两条序列分别是 X 和 Y长度各为 m 和 n先算一个 m×n 的逐点距离矩阵 d(i,j)然后从 (0,0) 出发按“向右、向下、向右下对角”三种路径逐步累积最小距离最终值 D(m,n) 就是 DTW 距离。这个动态规划过程的时间复杂度是 O(m×n)序列越长计算越慢。拿这份资源里的代码来看它的实现思路就是按这个动态规划框架走的。先看整个压缩包的构成文件作用Main.py程序入口负责调用数据读取、相似度计算与绘图source.py核心算法模块包含 DTW 距离计算与序列预处理函数database.dbSQLite 数据库存放股票价格历史数据requirements.txt依赖清单列明需要安装的 Python 库课程报告.doc完整的设计说明文档含原理介绍和结果分析这个结构对课程设计场景很友好——报告和代码分开放算法被封装在 source.py 里Main.py 只做流程编排。你如果想替换成自己的股票数据只需要改 database.db 里的表结构或换数据源连接方式不需要动算法主逻辑。3. 代码实战从 database.db 到相似度折线图3.1 准备环境Python 安装与依赖配置拿到资源后的第一步是把环境跑起来。如果你机器上还没装 Python先去官网下载安装包安装时勾选“Add Python to PATH”这一步很重要装完在命令行输入python --version能正常输出版本号就说明成功了。之后创建一个虚拟环境避免依赖冲突python -m venv stock_env stock_env\Scripts\activate # Windows 激活虚拟环境 # Linux/Mac 用: source stock_env/bin/activate pip install -r requirements.txt参数说明虚拟环境目录名可以自定义不一定叫 stock_env但建议路径里不要带中文和空格否则后面 pip 安装库时可能因为编码问题报错。requirements.txt 里一般包含 numpy、pandas、matplotlib 这几个核心库其中 matplotlib 负责最后的折线图绘制。如果安装时遇到超时换成国内镜像源能快很多常见做法是用清华源pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple。3.2 读取股票数据与序列预处理环境准备好后打开 Main.py 你首先会看到数据读取部分。它从 database.db 里把股票价格序列取出来这里的数据库表设计一般包含日期、股票代码、收盘价这几个字段。读取后不能直接丢进 DTW 计算必须先做对齐和归一化import sqlite3 import pandas as pd import numpy as np def load_stock_data(db_path, stock_code, start_dateNone, end_dateNone): conn sqlite3.connect(db_path) query SELECT trade_date, close_price FROM stock_price WHERE stock_code ? params [stock_code] if start_date: query AND trade_date ? params.append(start_date) if end_date: query AND trade_date ? params.append(end_date) query ORDER BY trade_date ASC df pd.read_sql_query(query, conn, paramsparams) conn.close() return df这段代码做了三件事连接 SQLite 数据库、按股票代码和时间范围过滤数据、按日期升序排列。参数里的 stock_code 是股票代码字符串比如600519start_date 和 end_date 是可选参数可以用来截取特定时间区间做局部对比。注意 read_sql_query 的 params 参数必须用列表或元组传值直接拼 SQL 字符串会有注入风险虽然是自己本地的库但这个习惯值得养成。拿到原始序列后预处理很关键。价格绝对值差异大的两只股票比如 100 块的股和 5 块的股直接算 DTW距离会被股价量级主导形态相似性根本体现不出来。常见做法是做 Min-Max 归一化def normalize_series(series): min_val np.min(series) max_val np.max(series) if max_val - min_val 0: return np.zeros_like(series) return (series - min_val) / (max_val - min_val)逻辑说明这是把价格线性映射到 0 到 1 区间。为什么要这么做因为 DTW 计算的是逐点距离的累积量纲不一致会导致距离矩阵被大数值控制。归一化后100 块的股和 5 块的股在同一个尺度上比形态不再比绝对值。max_val - min_val 0这个判断是防止全序列价格相同比如停牌股时除零报错。这里踩过的坑后面会细说。3.3 DTW 距离计算与折线图可视化预处理完成后进入核心计算环节。source.py 里的 DTW 实现是最需要注意的部分直接看代码def dtw_distance(x, y): m, n len(x), len(y) d np.zeros((m, n)) for i in range(m): for j in range(n): d[i][j] abs(x[i] - y[j]) D np.zeros((m, n)) D[0][0] d[0][0] for i in range(1, m): D[i][0] D[i-1][0] d[i][0] for j in range(1, n): D[0][j] D[0][j-1] d[0][j] for i in range(1, m): for j in range(1, n): D[i][j] d[i][j] min(D[i-1][j], D[i][j-1], D[i-1][j-1]) return D[m-1][n-1]逐行拆解第一个双层循环构造逐点距离矩阵 d用的是绝对值距离然后初始化累积矩阵 D 的第一行第一列最后从左上角往右下角递推每次取左、上、左上三个方向累积值的最小值加上当前点距离。这就是标准的 DTW 动态规划写法。注意几个细节矩阵 D 的维度是 m×nd 和 D 都要先分配内存再填充递推公式里的min(D[i-1][j], D[i][j-1], D[i-1][j-1])三个方向分别表示“X 序列多走一步”、“Y 序列多走一步”、“两边同时走一步”这是 DTW 允许时间轴伸缩的核心机制。这套写法在序列长度几百以内没问题但超过一千时双重循环会明显变慢后面我会讲一个加速方案。最后是可视化部分。资源里用折线图把两条序列画在同一张图上同时标注出 DTW 距离值import matplotlib.pyplot as plt def plot_series_comparison(series_a, series_b, dtw_val, label_a, label_b): plt.figure(figsize(12, 6)) plt.plot(series_a, labellabel_a, linewidth1.5) plt.plot(series_b, labellabel_b, linewidth1.5, linestyle--) plt.title(fStock Price Series Similarity (DTW Distance: {dtw_val:.4f})) plt.xlabel(Trading Day Index) plt.ylabel(Normalized Price) plt.legend() plt.grid(True, alpha0.3) plt.show()参数说明series_a 和 series_b 是两条归一化后的价格序列长度可以不一致dtw_val 是归一化后序列的 DTW 距离做一个标准化的相似度参考linewidth 和 linestyle 控制线型粗细与虚实方便区分两条曲线。{dtw_val:.4f}是把距离值格式化为小数点后四位。这样画出来的图两条线靠得越近DTW 距离越小相似度越高。4. 避坑与排查DTW 实战中的五个典型问题这一章值得反复看下面每一条都是实际运行中能真实遇见的场景。4.1 归一化除零价格恒定的股票序列直接报错现象某只股票长期停牌价格序列全是一个值跑normalize_series时提示RuntimeWarning: invalid value encountered in divide计算出的相似度全是 nan。原因max_val 和 min_val 相等分母为零归一化公式产生除零异常。解决在归一化函数里加等值判断如果序列最大值等于最小值直接返回全零数组或全一数组表示这条序列没有波动特征做后续对比时单独标记为“无效序列”。我在代码里已经写了这个判断你自己的版本务必补上。4.2 数据库字段名不匹配KeyError: close_price现象换了自己的数据库文件后运行读取代码报KeyError: close_price或sqlite3.OperationalError: no such column。原因原项目的字段名是英文小写下划线风格你自己的表可能用的中文列名或者叫closing_price、close之类的别名。解决先用命令行或可视化工具查看数据库表结构。执行SELECT sql FROM sqlite_master WHERE typetable;看建表语句然后修改查询语句里的字段名和你本地的库对齐。如果不方便改 SQL可以在读取后用 pandas 重命名列df.rename(columns{收盘价: close_price}, inplaceTrue)。4.3 序列长度差异导致的矩阵溢出现象对比一只上市 5 年的老股和一只次新股长度差一倍以上程序运行时间突然暴增到几十秒内存占用飙升。原因双重循环的时间复杂度是 O(m×n)当 m1200、n600 时内层要跑 72 万次Python 的纯循环性能撑不住且矩阵 D 占用内存随乘积增长。解决两个方向。其一限制对比区间只取两只股票重叠时间窗口的数据比如都只取最近 250 个交易日其二用 fastdtw 库代替手写双重循环它通过粗粒度到细粒度的分层近似将复杂度降到接近线性速度提升几个数量级。血泪经验课程设计答辩演示时跑到一个 5 分钟没出结果会很尴尬我一般会加一个序列长度上限判断。4.4 可视化中文乱码标题和标签变成方块现象matplotlib 折线图画出来标题里的中文字符全部显示成方框代码不报错但图没法看。原因matplotlib 默认字体不支持中文Windows 下默认字体是 DejaVu Sans里面没有中文字形。解决在绘图代码前强制指定中文字体plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] plt.rcParams[axes.unicode_minus] False第一行是设置中文字体SimHei 是黑体Microsoft YaHei 是微软雅黑按系统可用字体选择第二行是防止负号显示异常因为设置了中文字体后坐标轴的负号可能变成乱码。这个坑几乎每个做中文可视化的同行都会踩一次尽早加上这两行能少折腾半天。4.5 DTW 距离数值过大无法直观判断相似程度现象算出 DTW 距离是 15.7换两只股票变成 32.1但不知道这个数值到底代表“像”还是“不像”。原因DTW 距离是累积值受序列长度和数据范围影响不是归一化的 0 到 1 区间指标。长度越长距离越大不同长度序列的 DTW 距离之间没有可比性。解决把 DTW 距离除以序列长度得到“平均逐点对齐距离”或者再除以两条序列自身能量做标准化。我在实际对比中会用dtw_dist / min(len_a, len_b)作为相似度参考指标这样不同长度的序列之间能大致比较。也可以在报告里写清楚DTW 距离只做同一长度序列的相对比较不要跨长度直接对比。5. 进阶技巧把相似性分析从演示变成实用工具到这里基础流程已经跑通了但课程设计交出代码只是起点。我实际做量化分析时会把这份资源扩展成多股票批量对比的筛选工具这里分享两个可行方向。第一个是批量对比。把单对序列的代码包装成一个函数传入股票代码列表两两计算 DTW 距离结果存成矩阵。这样能快速找出历史上走势最相似的股票对用于配对交易策略的标的筛选。需要注意的是一开始先限制在一个较小的股票池比如 30 只以内两两组合数是 n×(n-1)/230 只就有 435 对纯 Python 实现会很吃力建议用 fastdtw 库。第二个是参数调优。DTW 有个 band 参数比如 Sakoe-Chiba band限制对齐路径偏离对角线的最大宽度。原资源没有加这个约束实际使用时建议加上——它不仅让计算量大幅下降还能避免“风马牛不相及的极端对齐”出现。实现上是在递推时加一个窗口条件abs(i-j) band的位置直接设为无穷大。对于日线级别的股票数据band 设为序列长度的 5% 到 10% 之间就能取得不错的效果。如果你用的是 Anaconda 或 VS Code 环境建议把 Main.py 里的可视化部分改成交互式用plt.scatter在折线图上标出 DTW 对齐路径中的关键对应点能直观看出时间轴上哪段被拉伸了、哪段被压缩了。这个图放在课程设计报告里会是很亮眼的加分项。验证方法也顺手说一下跑完代码后先挑两只已知走势高度同步的股票比如同板块龙头和龙二确认 DTW 距离明显小于随机选的两只股票如果这个趋势成立说明你的计算流程可信。这步相当于回归测试能避免数据读错或归一化出错带来的“结果看着合理但实际无效”的尴尬。从那次在课程设计答辩前几小时被一个中文乱码卡住之后我养成了习惯——凡是涉及 matplotlib 的代码第一件事先把字体配置写上然后跑一组最小数据验证再放真实数据。这套顺序沿用到现在省掉的折腾时间远比多写两行代码多。希望这篇笔记帮你在 DTW 相似性分析的复现和扩展上少走弯路把精力花在真正值得研究的行情规律上。本文还有配套的精品资源点击获取
返回列表