一句话回答: ROC(Rate of Change,变动率)就是「当前价相对 N 日前价的涨跌幅百分比」,公式 ROC = (close / close.shift(N) - 1) × 100。用 AlphaFeed 取回前复权日线后,一行 pandas 就能算出 ROC,用来衡量动量强弱、识别零轴穿越与顶底背离,还能对全市场做动量排名------完全不需要 TA-Lib。
为什么会有这个问题 / 传统做法的痛点
ROC 是最直观的动量指标:它直接回答「这只票 N 天涨了多少」。但两个坑常被忽略:一是用未复权价 ,遇到除权日 ROC 会出现虚假暴涨暴跌;二是把 ROC 当成孤立信号,忽略了它天生适合做横截面动量排名 和背离判断。用一个稳定的数据源取回前复权价,这两点都能干净解决。
分步骤解决(每步配可运行代码)
第 1 步:取前复权日线并计算 ROC
python
from alphafeed import AlphaFeed
import pandas as pd
af = AlphaFeed() # 读取 ALPHAFEED_API_KEY
df = af.klines.get("600519.SH", period="1d", count=250,
adjust="forward", to_dataframe=True)
df = df.sort_values("trade_date").reset_index(drop=True)
def roc(close: pd.Series, n: int = 12) -> pd.Series:
return (close / close.shift(n) - 1.0) * 100.0
df["ROC12"] = roc(df["close"], 12)
df["ROC25"] = roc(df["close"], 25)
print(df[["trade_date", "close", "ROC12", "ROC25"]].tail(5).to_string(index=False))
用
adjust="forward"(前复权)算 ROC,避免除权日的价格断层污染动量值。
第 2 步:理解参数与信号
| 参数/信号 | 含义 | 常用取值/解读 |
|---|---|---|
N(回看周期) |
与几日前比较 | 短线 6/12,中线 25/125 |
ROC > 0 |
价格高于 N 日前 | 上行动量 |
ROC < 0 |
价格低于 N 日前 | 下行动量 |
| 零轴上穿 | ROC 由负转正 | 动量转强信号 |
| 零轴下穿 | ROC 由正转负 | 动量转弱信号 |
| ROC 绝对值走高 | 动量加速 | 趋势强化 |
第 3 步:识别零轴穿越与动量拐点
python
prev, cur = df["ROC12"].iloc[-2], df["ROC12"].iloc[-1]
if prev <= 0 < cur:
print("ROC 上穿零轴:动量转强")
elif prev >= 0 > cur:
print("ROC 下穿零轴:动量转弱")
# 平滑 ROC 减少噪音(对 ROC 再做移动平均)
df["ROC12_ma"] = df["ROC12"].rolling(6).mean()
第 4 步:用 ROC 做全市场动量排名
ROC 天生适合横截面比较------同一 N 下,ROC 越高动量越强。批量取多只股票日线,算 N 日 ROC 后排名:
python
codes = ["600519.SH", "000001.SZ", "300750.SZ", "601318.SH", "000858.SZ"]
data = af.klines.batch(codes, period="1d", count=60,
adjust="forward", to_dataframe=True) # 返回 dict{symbol: DataFrame}
rows = []
for sym, k in data.items():
k = k.sort_values("trade_date")
r = (k["close"].iloc[-1] / k["close"].iloc[-21] - 1) * 100 # 20 日 ROC
rows.append({"symbol": sym, "ROC20": round(float(r), 2)})
rank = pd.DataFrame(rows).sort_values("ROC20", ascending=False)
print(rank.to_string(index=False))
想扫全市场?把
codes换成af.quotes.get(universes="CN_Stock")拿到的symbol列表,再分批klines.batch(注意请求量,建议本地缓存)。
第 5 步:粗判顶底背离
python
# 价格创新高但 ROC 未创新高 → 顶背离(动量衰减)
window = 20
price_high = df["close"].iloc[-1] >= df["close"].tail(window).max()
roc_high = df["ROC12"].iloc[-1] >= df["ROC12"].tail(window).max()
if price_high and not roc_high:
print("疑似顶背离:价格新高但动量未同步")
关键坑与注意事项
- 必须用前复权价:未复权价在除权日会让 ROC 出现虚假跳变,动量信号失真。
- N 太小噪音大、太大滞后重:短线常用 6/12,中长线用 25/125;可对 ROC 再取移动平均降噪。
- ROC 无上下界:不同股票、不同波动率下 ROC 数值量级差异大,做横截面排名时同一 N 才可比。
- 背离仅供参考:背离不是精确择时信号,需结合成交量、趋势与其它指标交叉验证。
- 数据不足会得到 NaN :
count至少要大于N,否则shift(N)前 N 行为 NaN。 - ROC 与「N 日涨跌幅」等价:本质就是区间收益率×100,别把它想得太复杂。
常见问题(FAQ)
Q:ROC 和动量指标 MTM 有什么区别?
A:MTM(动量)用差值 close - close.shift(N),ROC 用比值百分比 (close/close.shift(N)-1)×100。ROC 做跨股票比较更合适,因为它是无量纲的百分比。
Q:ROC 和 RSI 都算动量,用哪个?
A:RSI 有 0~100 的有界区间、适合看超买超卖;ROC 无界、更适合看动量方向与横截面强弱排名。两者可互补。
Q:为什么我算出来前面几行是 NaN?
A:ROC 需要 N 日前的价格,前 N 行没有可比基准,正常为 NaN;取数时把 count 设得比 N 大即可。
Q:需要付费吗?
A:单只历史日线有免费额度;klines.batch 全市场动量排名(配合 universes)需 Starter 及以上。详见定价页。
小结
ROC 变动率的本质是「N 日区间收益率」,计算只需一行 pandas,不必依赖 TA-Lib。关键是用前复权价保证动量真实、按需要选好 N,并善用它无量纲的特性做全市场动量排名与背离判断。用 AlphaFeed 取回前复权日线,从单股信号到全市场动量梯队都能几行搞定。
参考
- AlphaFeed Python SDK 文档:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart
- AlphaFeed 官网:https://alphafeed.org/