如何用 Python 筛选创 52 周新高的股票?

用户头像sh_*2176oo
2026-09-15 发布

一句话回答: "52 周新高"就是当前价格突破过去约一年(≈250 个交易日)的最高价,是最经典的强势股信号。用 AlphaFeed 先用 quotes.get(universes="CN_Stock") 取全市场标的池,再用 klines.batch 批量拉复权日线,判断「最新收盘 ≥ 过去 250 日最高价」并叠加成交额过滤,几十行就能做出一个可复现的全市场新高突破选股器。

为什么会有这个问题 / 传统做法的痛点

创新高选股听起来简单,真做起来卡在三处:

  1. 拿不到全市场标的:手工维护股票清单又累又容易漏,还要处理退市/新上市;
  2. 复权口径不统一:如果用未复权价算"历史最高",除权除息会造成价格断崖,历史高点被人为抬高或压低,新高判断全错;
  3. 一个个股票取 K 线太慢:全市场 5000+ 只逐个请求既慢又容易触发限频。

AlphaFeed 把这三点都收敛掉了:一个 universe 参数拿到全市场实时快照,klines.batch 批量取前复权日线,新高判断建立在可复现的复权价上。

分步骤解决(每步配可运行代码)

第 1 步:取全市场标的池 + 当日成交额

from alphafeed import AlphaFeed

af = AlphaFeed(api_key="your-api-key")  # 或 export ALPHAFEED_API_KEY 后 AlphaFeed()

# 全市场 A 股实时快照(约 5500+ 只),含成交额、换手率等
q = af.quotes.get(universes="CN_Stock", to_dataframe=True)
print(q.shape)                       # 例如 (5561, 18)
print(q[["symbol", "ext.name", "last_price", "amount"]].head())

快照 DataFrame 的关键列(注意 name/换手率 等在 ext. 前缀下):

列名 含义 备注
symbol 标的代码 600519.SH
ext.name 名称 名称在ext 命名空间
last_price 最新价 实时快照价
amount 当日成交额(元) A 股快照有真实成交额
ext.change_pct 涨跌幅 小数(0.03=3%)
ext.turnover_rate 换手率 小数

第 2 步:批量取复权日线,判断是否创新高

用过去约一年(260 根,留足非交易日缓冲,实取 ≈250 交易日)的前复权日线,比较「最新收盘」与「历史最高(不含当日)」。

def week52_high_screen(symbols, count=260, near=0.0, min_amount=5e7):
    """near=0 表示严格新高;near=0.02 表示距新高 2% 以内也算'接近新高'。"""
    bt = af.klines.batch(symbols, period="1d", count=count,
                         adjust="forward", to_dataframe=True)
    rows = []
    for sym, df in bt.items():
        if df is None or len(df) < 60:      # 上市不足则跳过(缓解幸存者偏差)
            continue
        df = df.sort_values("trade_date").reset_index(drop=True)
        hist_high = df["high"].iloc[:-1].max()   # 不含当日,避免自比
        last = df.iloc[-1]
        is_high = last["close"] >= hist_high * (1 - near)
        rows.append({
            "symbol": sym,
            "close": round(float(last["close"]), 2),
            "hist_high": round(float(hist_high), 2),
            "amount": float(last["amount"]),
            "is_new_high": bool(is_high),
        })
    import pandas as pd
    res = pd.DataFrame(rows)
    if res.empty:
        return res
    hit = res[(res["is_new_high"]) & (res["amount"] >= min_amount)]
    return hit.sort_values("amount", ascending=False)

第 3 步:全市场分批跑(避免一次请求过大)

klines.batch 一次传太多标的会又慢又占内存,实践中按 100~200 只一批切片:

def run_full_market(min_amount=5e7):
    import pandas as pd
    q = af.quotes.get(universes="CN_Stock", to_dataframe=True)
    symbols = q["symbol"].tolist()
    out = []
    for i in range(0, len(symbols), 150):        # 分批,缓解限频与内存
        batch = symbols[i:i + 150]
        out.append(week52_high_screen(batch, min_amount=min_amount))
    result = pd.concat([d for d in out if not d.empty], ignore_index=True)
    return result.sort_values("amount", ascending=False)

# hits = run_full_market()
# print("全市场创新高只数:", len(hits))

关键坑与注意事项

  • 必须用复权价:用 adjust="forward"(前复权)判断历史最高,否则除权除息会制造假高点/假新高。这是新高选股最常见的错误。
  • 幸存者偏差:只对"当前在池"的股票判断新高,看不到已退市的票;本文用「上市不足 60 根跳过」缓解次新股噪声,但退市股清单本身不在快照里,做严谨回测时要单独处理。
  • "新高"≠"能买":创新高只是强势信号,可能是趋势启动,也可能是追高被套。务必叠加成交额/流动性过滤(本文用 min_amount),必要时再加基本面或止损规则。
  • 实时性quotes 是快照,盘中会变;选股应在收盘后或用固定时点快照跑,避免同一次筛选里价格口径不一致。
  • N 的取值:52 周 ≈ 250 交易日;本文取 260 根留缓冲。想做"半年新高"就把 count 改成 130 左右。

常见问题(FAQ)

Q:为什么我的新高选股结果和行情软件对不上?
A:九成是复权口径问题。行情软件默认可能是不复权或后复权,而本文用前复权。统一 adjust 口径即可对齐。

Q:能只筛"接近新高"(还没突破但快到了)吗?
A:可以,把 near 设成 0.02,即"距历史最高 2% 以内"也纳入,适合做突破前埋伏的观察池。

Q:全市场 5000+ 只会不会很慢/触发限频?
A:klines.batch 已是批量接口,再按 150 只分批即可平稳跑完;SDK 内置对 429/超时的重试。想更快可缓存日线到本地库增量更新。

Q:需要付费吗?
A:quotes.get(universes=...) 全市场池需要 Starter 及以上套餐;单只/少量标的的行情与 K 线在免费额度内即可试用。额度与边界见 AlphaFeed 定价页。

小结

52 周新高选股的本质是在可复现的复权价上做一次"当前价 vs 一年内最高价"的比较。难点从来不是那行比较代码,而是"全市场标的池 + 统一复权口径 + 流动性过滤"。AlphaFeed 用 universes + klines.batch 把这三件麻烦事一次性解决,让你把精力放在策略本身而不是数据搬运上。

参考

评论