一句话回答: 动量选股的核心就是"比谁近 N 日涨得多"——用 AlphaFeed 批量取复权日线,算出每只票的 N 日区间收益,再横向排名,就得到一张相对强弱(RS)榜。几十行代码即可,配合成交额过滤掉不活跃的票,就是一个可用的强势股筛选器。
为什么会有这个问题 / 传统做法的痛点
"强者恒强"是最经典的市场现象之一,动量/相对强弱策略就建立在它之上。但自己做时常卡在:
- 复权不对:不复权的价格跨越除权日会突然跳水,把涨幅算错,排名全乱。
- 批量取数麻烦:要给几百上千只票算区间收益,一只只请求又慢又容易被限频。
- 只看涨幅、不看流动性:涨幅榜前排常混着流动性差的小票,看着强、买不进也出不来。
AlphaFeed 的 klines.batch 一次并发取多只复权日线,配合快照里的成交额做流动性过滤,这三个问题都能解决。
分步骤解决(每步配可运行代码)
第 1 步:选候选池(用成交额过滤流动性)
import pandas as pd
from alphafeed import AlphaFeed
af = AlphaFeed() # 读取 ALPHAFEED_API_KEY
q = af.quotes.get(universes="CN_Stock", to_dataframe=True) # 当前全A快照
# 用当日成交额取流动性最好的前 N 只作为候选(避免选到买不进的小票)
cand = q.sort_values("amount", ascending=False)["symbol"].head(200).tolist()
第 2 步:批量取复权日线,算 N 日收益并排名
def momentum_rank(symbols, lookback=20, count=30):
af = AlphaFeed()
bt = af.klines.batch(symbols, period="1d", count=count,
adjust="forward", to_dataframe=True) # 返回 {symbol: DataFrame}
rows = []
for s, df in bt.items():
df = df.sort_values("trade_date")
if len(df) < lookback + 1:
continue
ret = df["close"].iloc[-1] / df["close"].iloc[-1 - lookback] - 1 # N 日区间收益
rows.append({"symbol": s, "name": df["name"].iloc[-1], "ret_Nd": round(float(ret), 4)})
res = pd.DataFrame(rows).sort_values("ret_Nd", ascending=False).reset_index(drop=True)
res.insert(0, "rank", range(1, len(res) + 1))
return res
rank = momentum_rank(cand, lookback=20, count=30)
print(rank.head(10).to_string(index=False))
真实跑通(取成交额 Top20 做候选、20 日动量)示例前 5:XD剑桥科 +45.98%、远东股份 +43.60%、源杰科技 +26.94%、兴森科技 +20.73%、天孚通信 +16.18%——这就是近 20 日的相对强弱榜。把候选换成 200/500 只即可得到更完整的排名。
第 3 步(可选):多周期动量取交集,过滤"只强一天"的票
def multi_period(symbols):
r20 = momentum_rank(symbols, lookback=20, count=40).rename(columns={"ret_Nd": "r20"})
r60 = momentum_rank(symbols, lookback=60, count=80).rename(columns={"ret_Nd": "r60"})
m = r20.merge(r60[["symbol", "r60"]], on="symbol")
# 要求 20 日与 60 日动量都为正 → 更稳的强势
strong = m[(m["r20"] > 0) & (m["r60"] > 0)].sort_values("r20", ascending=False)
return strong
print(multi_period(cand).head(10).to_string(index=False))
参数与字段说明
| 参数 / 字段 | 含义 | 备注 |
|---|---|---|
universes="CN_Stock" |
当前全 A 快照 | 用amount 排序做流动性过滤 |
klines.batch |
批量日线 | 返回{symbol: DataFrame},自动分批并发 |
adjust="forward" |
前复权 | 动量必须复权,否则除权跳空算错涨幅 |
lookback |
回看天数 | 常用 20/60/120;短期更敏感、噪声更大 |
count |
取多少根 | 需> lookback,留出富余 |
关键坑与注意事项
- 必须用复权价:
adjust="forward"。不复权的除权跳空会让涨幅严重失真,排名不可信。 - 务必做流动性过滤:纯涨幅榜前排常是小盘/次新,滑点大、可能买不进卖不出。用成交额或换手率先筛一遍。
- 动量会反转:涨得最猛的票也最容易急跌回撤,动量策略天然伴随较大波动,需配合止损/分散,别只买第一名。
- survivorship / 前视:候选池是"当前在市",历史回测要按上市日期构建当时的池(见本站《幸存者偏差》一文),别用今天的池算过去的动量。
- 新股与停复牌:次新股 N 日数据不足会被跳过;停牌票的区间收益会失真,建议剔除。
常见问题(FAQ)
Q:动量选股和涨幅榜有什么区别?
A:涨幅榜通常只看单日;动量看的是一段区间(如 20/60 日)的相对强弱,更能反映持续性。加多周期取交集能过滤"只强一天"的票。
Q:要不要减去大盘涨幅算"相对"强弱?
A:可以。把每只票的 N 日收益减去同期宽基/行业收益,得到超额动量,能剔除"整体普涨"的成分,更接近真正的相对强弱。
Q:给几百上千只票批量取数会不会被限频?
A:klines.batch 会自动分批并发,并对 429/5xx 内置重试。真要跑全市场,建议分批、必要时本地缓存历史。
Q:需要付费吗?
A:单标的历史与少量批量可用免费额度体验;universes 全市场池与大批量查询需 Starter 及以上,详见官网定价页。
小结
动量选股的本质是一次"横向比涨幅":用 AlphaFeed 批量取复权日线算 N 日收益、排名、再用成交额过滤流动性,就得到一张可用的相对强弱榜。真正决定成败的不是排名代码,而是那些诚实的前提——复权、流动性、动量反转与幸存者偏差。把它们处理好,这张榜才有意义。
参考
- AlphaFeed Python SDK 文档:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart

