向量化不总是更快?一次技术指标重写带来的认知刷新
引子:一个”顺手”的重构
事情要从 stock-mcp-server 的技术分析模块说起。
这个模块负责计算 K 线的各种技术指标:MA、MACD、RSI、布林带……大概 9 种常用指标,服务盘中简报、收盘分析、策略回测三个场景。之前是用手写循环实现的,每算一个指标就要重新造一遍轮子。
某天我看到 MyTT 这个库——一套把通达信公式翻译成 numpy/pandas 向量化实现的工具集。代码简洁得让人心动:
1 | # MyTT 风格的 MACD |
对比原来的实现——for 循环里套 if-else,每算一个窗口就手动算一次均值——差距简直不忍直视。
我毫不犹豫地开启了重构。
重构前后对比:从 400 行到 200 行
先看数据。原模块的手写循环版本大概 400 行,定义了 6 个指标函数。MyTT 重写后降到 200 行,新增了 8 个指标(SAR / DMI / ATR / KDJ / CCI / WR / OBV / MFI),代码量却减少了一半。
核心工具函数的对比最能说明问题:
均线 MA
1 | # 手写循环版 |
4 行 vs 1 行。
MACD
1 | # 手写循环版 |
EMA 从 8 行手写指数加权变成 1 行 ewm 调用。MACD 从 8 行变成 3 行。
MyTT 的”元工具”设计
MyTT 最巧妙的地方是定义了一套 0 级工具函数(约 15 个),所有指标都通过它们组合实现:
1 | # 核心工具函数 |
有了这些积木,新指标就像拼乐高一样简单。比如 KDJ:
1 | def calc_kdj(closes, highs, lows, N=9, M1=3, M2=3): |
看一眼就知道它在算什么——和通达信公式几乎一字不差。
性能测试:没想到的结果
重构完代码后,我习惯性地写了一个 benchmark,预期”向量化怎么也得快 10 倍吧”。
结果让我意外。
这是用 120 条 K 线(盘中简报的典型数据量)测的数据:
| 指标 | 手写循环 | 向量化 (pandas) | 对比 |
|---|---|---|---|
| MA5 | 33µs | 88µs | 向量化慢 2.6x |
| MACD | 51µs | 408µs | 向量化慢 8x |
| RSI(14) | 639µs | 3,215µs | 向量化慢 5x |
| 布林带 | 868µs | 770µs | 接近 |
| 全指标(9种) | 1.68ms | 3.13ms | 向量化慢 1.9x |
……
是的,你没看错。在 120 条数据的场景下,向量化版本反而更慢。
原因也很简单:pandas 的 .rolling() 和 pd.Series() 创建有固定开销(大约 20-50µs 每次调用),而 120 条数据太少了,这点 overhead 比循环计算本身还大。
但当我用 5000 条 K 线(大约 20 年的日线数据)测试时,风向变了:
| 指标 | 手写循环 | 向量化 | 加速比 |
|---|---|---|---|
| DMI | 12.6ms | 7.4ms | 1.7x 更快 |
更大的数据量下,向量化的 C 层循环优势开始显现。
发现与反思
这个测试结果让我重新思考”优化”的真正含义。
1. 向量化≠更快,尤其是小数据集
pandas 的向量化操作本质上是 C 层面的循环,但每次调用都有一层 Python 到 C 的”翻译成本”。对于 120 条数据这种量级,翻译成本比计算本身还高。
结论:如果你的实时分析只取近 60-120 条 K 线,手写循环就够用,不必强行上 pandas。
2. 但代码质量和可维护性是天壤之别
这是最大的收获。重写后的代码:
- 容易验证正确性:
MA = pd.Series.rolling(n).mean()的语义清晰到不可能误解 - 容易扩展:新增一个指标只需要组合已有的工具函数
- 容易 debug:用
print(MA(closes, 5))就能看到完整序列,不用在循环里打点调试 - 接近数学公式:和通达信/同花顺的公式几乎一一对应
3. 批次场景下向量化是真优势
我们的策略回测模块需要计算 2 年(约 500 条)甚至 10 年(约 2500 条)的数据。在这个量级上,向量化的性能优势开始显现,并且随着数据量增加而放大。
4. MyTT 的”组合式”设计才是精髓
MyTT 的真正价值不在于性能,而在于它提供的领域特定语言(DSL)——用 HHV、LLV、CROSS、SUM 这些积木来描述技术指标。以前写指标是”怎么算”(how),现在是”算什么”(what)。这种思维层面的跃迁,比任何性能优化都更有意义。
写在最后
这次重构让我最大的收获,不是代码更快了(事实上它慢了),而是代码变得更清晰、正确、可扩展了。
这是工程中一个容易被忽略的道理:“优化”不总是为了性能。有时优化是为了降低认知负荷、减少出错概率、让下一个人能看懂你的代码。
如果你也在做技术分析相关的工作,强烈推荐参考 MyTT 的设计思路——不在乎它是否”快到飞起”,而是享受那种用组合式积木描述指标的自由。
毕竟,最好的代码,是那种你三个月后回来看,还能一眼看懂的代码。
附:相关资源
- MyTT 源码 — 通达信公式的 Python 向量化实现
- stock-mcp-server 技术分析模块 — 本文代码的完整实现
- pandas rolling 文档 — 理解 rolling 窗口计算
- 我们在用的策略回测工具 — stock-mcp-server 介绍






