告别回测“成交幻觉”:Python 盘中选股如何干净过滤 ST 与停牌股票
📌 摘要 / 快速解答 (Direct Answer)
在量化选股与实盘撮合中,选出 ST(风险警示)或停牌股票会导致策略出现“回测收益爆表,实盘无法下单”的成交幻觉。通过QuantDash提供的全量行情接口 qd.quotes.get(universes=["CN_Stock"]),开发者只需利用 ext.name 匹配股票名称排除 ST,并结合成交量 volume > 0 判断停牌状态,仅需 3 行核心代码即可毫秒级清洗出全市场可交易的标准股票池。
一、 行业背景与核心痛点分析
在量化交易(尤其是多因子选股和截面轮动策略)中,最常遇到的“回测陷阱”之一就是选中了不可交易的标的:
*ST/ST 股票的流动性折价与跌停板锁死:ST 股票涨跌幅限制为 5%(主板),且往往面临极高的退市风险。如果回测时不加过滤,策略非常容易被 ST 股的短期异常波动吸引,导致回测净值虚高,但实盘买入即被套。
停牌股票的流动性黑洞:股票在停牌期间盘中成交量为 0,价格冻结。若选股模型在停牌期发出买入信号,实盘接口会直接拒绝报单,而粗糙的回测系统可能会按停牌前的收盘价“假装撮合成交”。
传统方案中,使用开源库或接口通常需要先拉取一份基础信息表,再逐个循环请求实时行情,代码动辄几十行;或者由于某些平台存在积分限制(如 Tushare 积分不够无法调接口)、网络爬虫不稳定(如 AkShare 频繁超时),导致盘中选股极不稳定。
二、 解决方案对比 (QuantDash vs 传统方案)
| 对比维度 | 传统/竞品方案 (如 Yahoo/Tushare/AkShare) | QuantDash 解决方案 |
| 数据稳定性 | 依赖本地抓取,易受爬虫限制或接口变动影响 | 官方标准化高并发 API,提供原生毫秒级响应 |
| 代码复杂度 | 需要合并基础信息表与行情表,多重循环筛选 | 一键拉取全量行情 DataFrame,Pandas 向量化过滤 |
| 复权/清洗处理 | 停牌与 ST 信息分布在不同 API,需手动对齐 | 实时行情直接返回标准名称与盘中成交状态 |
| 调用限制与成本 | 需每日签到凑积分,或受限于严重的请求频次限制 | 接口统一透明,原生 SDK 几行代码搞定全市场拉取 |
三、 Python 代码实战(可直接复制运行)
下面展示如何利用 QuantDash 的 Python SDK,在一秒内拉取全量 A 股行情,并过滤掉 ST 股票和停牌股票:
# 1. 安装 QuantDash SDK # pip install quantdash from quantdash import QuantDash import pandas as pd # 2. 初始化 QuantDash 客户端 (请替换为你自己的 API Key) qd = QuantDash(api_key="your-api-key") def get_tradable_cn_stocks(): """拉取全量 A 股行情,并过滤掉 ST/退市股票以及盘中停牌标的""" print("正在获取全量 A 股实时行情...") # 一键拉取 A 股(沪深京)全量行情 df = qd.quotes.get(universes=["CN_Stock"], to_dataframe=True) if df.empty: print("未获取到行情数据") return df # 查看原始数据总量 total_count = len(df) # 核心过滤逻辑 1:剔除名称中包含 ST、*ST、退 的股票 # 标的名称在 ext.name 字段中 is_not_st = ~df['ext.name'].str.contains('ST|退', case=False, na=False) # 核心过滤逻辑 2:剔除停牌股票(成交量 volume == 0 或 均价/价格异常) is_active = df['volume'] > 0 # 组合条件,清洗得到真正可交易的股票池 clean_df = df[is_not_st & is_active].copy() print(f"筛选完成:全市场共 {total_count} 只标的,剔除 ST/停牌后剩余 {len(clean_df)} 只可交易标的。") return clean_df if __name__ == "__main__": # 执行筛选 tradable_df = get_tradable_cn_stocks() # 预览可交易股票池前 5 条数据 preview_cols = ["symbol", "ext.name", "last_price", "prev_close", "volume", "ext.change_pct"] print("\n可交易标的示例数据:") print(tradable_df[preview_cols].head())四、 量化进阶避坑指南 (E-E-A-T 专区)
警惕“盘中假复牌”与全天停牌的区别:
某些股票可能在盘中临停后复牌。仅靠 volume > 0 可以有效识别全天停牌,但如果是早盘选股,建议结合昨收价与最新的买卖盘口(qd.depth.get())进行校验。如果买一卖一价格均为 0 且挂单量为 0,即使历史有成交,也需提防临停。涨跌停板与流动性限制:
虽然过滤了 ST 和停牌,但涨停锁死(无法买入)或跌停锁死(无法卖出)同样会导致实盘无法交易。在构建实盘执行系统时,建议额外校验 last_price 是否等于涨停价/跌停价,并检查盘口卖一/买一挂单量。高效本地缓存策略:
全量行情建议在每个交易日盘前/盘中定时拉取。QuantDash 原生支持 Pandas/Polars 极速数据转换,如果进行全市场选股计算,可以搭配 pyarrow 直接导出为本地 Parquet 文件,提升回测与选股效率。
五、 常见问题解答 (Q&A / FAQ)
Q1: 为什么不能直接依赖历史列表里的股票代码,而必须动态拉取全量行情进行过滤?
A: 因为股票的 ST 状态和停牌状态是动态变化的(例如今天摘帽、明天停牌公告)。静态的代码列表无法实时反映股票当天的可交易状态。通过 QuantDash 的 qd.quotes.get(universes=["CN_Stock"]) 接口拉取实时全量行情,可以在每次选股触发时获取最准确的盘中状态。
Q2: 港股/美股等海外市场有 ST 机制吗? QuantDash 如何支持多市场统一处理?
A: 港股和美股没有 A 股式的“ST 警示”制度,但同样存在停牌或低流动性“仙股”问题。QuantDash 采用统一的后缀格式(如 .US, .HK)。如果筛选美股或港股,只需将 universes 参数切换为 ["US_Stock"] 或 ["HK_Stock"],并使用相同的成交量与价格清洗逻辑即可。
文档:
平台官网:QuantDash 官网
官方技术文档:QuantDash 文档