Skip to content

7.8 向量化思维:别写慢循环

课前导读

本章目标

学完能解决什么困惑:为什么量化研究里经常说“少写逐行循环”,以及如何用 pandas 的整列计算表达投资规则。

适合谁学习:已经知道 DataFrame 是表格,准备把 A 股观察规则写成可重复计算流程的读者。

难度:intermediate

预计用时:约 35 分钟

本节的关键词不是“炫技”。

我们不追求把代码写得让人看不懂。

我们追求的是:同一条规则,每次都能用同一种方式计算。

你可以把向量化想成“整列计算,而不是逐格手算”。

在表格软件里,如果你想算 1000 行收盘价涨跌幅,你不会一个格子一个格子按计算器。

你会写一个公式,然后向下填充整列。

pandas 的向量化思维也是类似。

它让你对一整列数据说话。

知识正文

1. 类比:逐格手算与整列盖章

想象你在整理 500 只 A 股的日线数据。

每只股票有几百个交易日。

如果你逐行问:“今天收盘价比昨天高吗?”

你的大脑很快会被细节淹没。

逐行循环就像拿着印章,一个格子一个格子盖。

向量化就像把整列名单交给机器,一次性按规则处理。

它不是偷懒。

它是把注意力从“怎么挪到下一行”,转回“规则本身是否正确”。

量化研究最怕的不是慢一点。

更怕的是循环里藏着状态错误、漏算第一行、错用未来数据。

向量化能减少这类人为细节。

2. 术语铺垫:什么是向量化

向量可以先理解成“一整列数字”。

向量化就是对一整列或多列数字一次性运算。

在 pandas 里,一列通常叫 Series

一张表叫 DataFrame

当你写 df["close"].pct_change() 时,你不是只计算某一天。

你是在告诉 pandas:请对整列收盘价计算相邻两天的变化。

当你写 df["close"] > df["ma20"] 时,得到的不是一个答案。

得到的是一整列 True 或 False。

这列 True 或 False 可以被理解成“条件检查清单”。

它也常被叫作布尔条件。

新手不必急着记英文。

你只要理解:规则可以变成整列的是与否。

3. loop 写法:像逐行翻账本

先看一个直观但容易变长的写法。

python
returns = []
last_close = None

for close in df["close"]:
    if last_close is None:
        returns.append(0)
    else:
        returns.append(close / last_close - 1)
    last_close = close

df["return_loop"] = returns

这段代码能表达每日收益率。

它的优点是接近人类一步一步想问题。

但当规则变多时,循环里会出现很多临时变量。

比如昨天的收盘价、前天的信号、是否持仓、累计费用。

新手很容易在某一行忘记更新状态。

也容易把今天才知道的信息用到昨天。

这不是说循环不能用。

而是说入门阶段应优先把简单规则写成整列计算。

4. vectorized 写法:像整列填公式

同样的收益率,用 pandas 可以更直接。

python
df["return_vec"] = df["close"].pct_change().fillna(0)
df["ma20"] = df["close"].rolling(20).mean()
df["ma60"] = df["close"].rolling(60).mean()
df["trend_ok"] = df["ma20"] > df["ma60"]

pct_change() 表示计算相邻两行的百分比变化。

rolling(20).mean() 表示用最近 20 个交易日求平均。

df["ma20"] > df["ma60"] 表示整列比较。

这几行代码读起来像规则说明。

收盘价计算每日收益。

收盘价计算 20 日均线。

收盘价计算 60 日均线。

20 日均线高于 60 日均线时,趋势条件为真。

这种写法更接近研究记录。

别人读代码时,更容易复现你的规则。

5. 为什么少写慢循环

第一,少写循环能减少人为状态错误。

量化研究里,今天、昨天、上一个信号、当前持仓都很容易混在一起。

整列计算把时间关系暴露在列名和函数里。

第二,少写循环通常更快。

pandas 和 NumPy 底层已经针对整列计算做了很多优化。

但本课程不把速度当作炫耀点。

对于新手来说,更重要的是规则清楚。

第三,整列计算更方便复查。

当你生成 ma20ma60trend_ok 这些列后,可以直接抽样检查。

你可以问:某一天为什么信号为真。

答案就在同一行附近。

第四,整列计算更容易画图和写报告。

上一节的收益曲线、回撤曲线,都依赖清晰的列。

如果所有逻辑都藏在循环内部,复盘会困难很多。

6. A 股案例:均线条件的一列化表达

假设你想观察一个非常朴素的条件。

当某只 A 股的 20 日均线高于 60 日均线时,认为中期趋势较强。

这不是买入建议。

它只是一个可计算的观察条件。

用向量化表达时,你可以这样拆分。

python
df["ma20"] = df["close"].rolling(20).mean()
df["ma60"] = df["close"].rolling(60).mean()
df["trend_filter"] = df["ma20"] > df["ma60"]
df["usable_signal"] = df["trend_filter"].shift(1).fillna(False)

这里最值得注意的是 shift(1)

它表示把信号往后挪一天。

为什么要挪?

因为当天收盘后你才知道当天的收盘价和均线结果。

如果你假装当天开盘前就知道当天收盘后的条件,就偷看了未来。

在 A 股研究里,这个错误非常常见。

向量化不是只为了快。

它也能帮助你明确“信息在什么时候可见”。

风险提醒

没有 shift() 的信号不一定错,但你必须解释清楚它代表何时可见,不能把收盘后才知道的结果当成盘中已经知道。

7. 从规则到列的思考顺序

请把一句投资观察话,拆成几列。

“最近趋势较强”可以拆成 ma20ma60ma20 > ma60

“今日放量”可以拆成 volumevolume_ma5volume > volume_ma5

“避免 ST 股票”可以拆成 is_stis_st == False

“只看成交额较高的股票”可以拆成 amountamount > threshold

每个条件变成一列后,复查就容易了。

你能看到哪一天满足,哪一天不满足。

你也能解释为什么某只股票被筛入或筛出。

这就是可复现性的来源。

不是因为代码高级。

而是因为每一步都有证据。

mermaid
flowchart TD
    A[一句观察想法] --> B[拆成数据列]
    B --> C[整列计算条件]
    C --> D[生成信号列]
    D --> E[抽样复查]
    E --> F[写入研究记录]

8. 常用向量化工具清单

pct_change():计算相邻两行的涨跌幅。

rolling().mean():计算滚动平均,例如 20 日均线。

shift():把数据向后或向前移动,用来处理信息可得时间。

where():按条件保留或替换数据。

布尔条件:用一整列 True 或 False 表达规则是否满足。

列间运算:让收盘价、成交量、均线等列互相比较。

这些工具不需要一次全背下来。

你只需要在研究中反复使用。

用多了,就会自然形成“先想列,再想规则”的习惯。

9. 常见误区

误区一:为了不用循环,写出自己也看不懂的一长串代码。

可读性永远优先。

如果一行代码太长,可以拆成几列。

误区二:把所有问题都硬塞进向量化。

有些复杂状态以后会在回测系统中处理。

本章只学习研究表格的基础表达。

误区三:忘记处理第一行和缺失值。

例如 pct_change() 第一行没有前一天,会产生缺失。

这时要明确填 0、保留缺失,还是从第二行开始观察。

误区四:忽略索引对齐。

不同股票、不同日期的数据合并时,日期必须对齐。

否则计算结果可能看起来正常,实际已经错位。

交互实验室

练习 1:把一句话改写成列

请把下面这句话拆成列名。

“只观察收盘价高于 20 日均线,并且成交量高于 5 日平均成交量的交易日。”

你至少应该写出这些列。

close:收盘价。

ma20:20 日均线。

volume:成交量。

volume_ma5:5 日平均成交量。

price_ok:收盘价是否高于 20 日均线。

volume_ok:成交量是否高于 5 日平均成交量。

final_ok:两个条件是否同时满足。

练习 2:比较 loop 与向量化

请阅读下面两句。

“我每天看一行,判断今天是否满足条件。”

“我先把所有日期的条件一次性算成一列,再抽样检查。”

写下你认为第二种方式的两个好处。

一个好处应和复查有关。

另一个好处应和减少人为错误有关。

练习 3:检查信息可得性

假设你用当天收盘价计算信号。

请回答:这个信号最早应该用于当天,还是下一个交易日。

如果你选择下一个交易日,请在代码里写出 shift(1)

如果你选择当天,请写出你的合理假设。

这个练习不是考语法。

它是在训练你不要偷看未来。

核心复盘

知识清单

  • [ ] 我理解向量化是整列计算,不是为了写得更神秘。
  • [ ] 我能看懂 pct_change()rolling()shift() 在量化研究中的基本用途。
  • [ ] 我知道布尔条件可以把投资规则变成一列 True 或 False。
  • [ ] 我会用 shift(1) 思考信号何时可见,避免偷看未来。
  • [ ] 我知道可读性优先,必要时可以把复杂表达拆成多列。

金句总结

少写循环,不是为了炫耀速度,而是为了让规则更清楚、更容易复查。

本文档仅供学习交流用途,不构成任何投资建议。股市有风险,入市需谨慎。