这个股票数据API系列写到第19篇,前面把行情、财务、资金流、龙虎榜这些接口都拆得差不多了,今天说一个很有实战价值但经常被马虎处理的数据:次新股池数据。之所以单独把它拎出来讲,是因为次新股池不是一个简单的“上市时间小于N天”的静态清单,它背后牵扯到上市日期口径、交易日历、流通市值过滤、行情快照关联等一系列问题。而且你自己从股票软件里肉眼翻次新股,和直接在策略系统里通过API动态维护一个次新股池,完全是两码事。这篇文章把我实际搭建次新股池的过程、接口字段设计思路、踩过的坑都写出来,适合正在做量化选股、事件驱动策略或者短线交易工具的朋友参考。
1. 次新股池数据到底在解决什么问题
1.1 次新股的定义,其实没有统一标准
很多人以为次新股有一个官方定义,真不是这样。交易所和行情软件里,“次新股”更多是一个市场约定俗成的概念。常见的划分方式有这么几类:
- 按上市自然日划分:上市时间不足1年,甚至严格一点的只看365天内。
- 按交易日划分:上市后交易不满250个交易日。
- 按市场情绪划分:开板之后一段时间内,通常是上市后20到120个交易日之间。
- 按流通市值划分:流通盘小、上市时间短,常被归入“小盘次新”。
不同口径会直接影响池子里的股票数量。比如上市1年的口径,在注册制环境下,一年IPO数量几百家,池子里可能同时存在三四百只股票;但如果把窗口压缩到上市后30到180天,可能就只剩一两百只。做策略的人如果没想清楚自己要哪种口径,后面所有分析都是飘的。
我自己的习惯是:把“次新股”拆成“新”和“次新”两段。上市后20个交易日内的叫新股,20到250个交易日之间的叫次新。新股阶段波动太大,流动性不稳定,很多量化模型根本进不了场;次新阶段筹码开始沉淀,数据特征相对稳定,做因子挖掘更有意义。这个划分不是拍脑袋,而是根据换手率的衰减曲线来的——绝大多数股票上市首月换手率极高,随后逐步回落到常规水平。
1.2 动态池子的价值:时间窗口与滚动更新
次新股池和普通概念板块最大的区别在于:它是动态滚动的。
今天还是次新股,明天可能就“毕业”了;下周又有新股上市进来。如果靠人工维护,每天去更新一遍上市日期清单,费时费力还容易漏。用API维护一个动态池子,相当于做了一个自动进出场机制:
- 进入条件:股票上市日期落在设定的时间窗口内。
- 退出条件:股票上市时间超过窗口上限,自动从池子里移除。
- 特殊处理:停牌、退市整理、ST股票需要单独标记。
这个滚动逻辑表面上看只是加减一个日期判断,真正落地的难点在于“进入时机”和“退出时机”要和策略信号同步。比如一个打板策略,可能在股票上市第5天就开始关注,而一个偏基本面的策略,可能等到上市满120天才纳入观察。同一个池子,给不同策略用,窗口参数就得做成可配置的,不能写死。
1.3 谁在消费这种数据
我接触到的几类典型用户,需求侧重点各不相同:
- 量化策略研究员:把次新股池作为选股宇宙,在此基础上叠加量价因子、财务因子。
- 短线交易工具开发者:需要监控次新股的涨跌停、换手率异动,池子更新的实时性要求高。
- 资讯类产品:把“次新股”当成概念板块标签,做行情列表归类。
- 数据终端维护者:做每日快照,保留历史池子用于回测和复盘。
这里面有个容易被忽略的细节:做回测时,必须用“历史某一天的次新股池”,而不是用今天拉到的池子去回测历史。因为次新股池是时间敏感的,今天看某只股票是次新,但它在三个月前可能还没上市,直接套进历史回测会引入严重的未来函数。所以我才一直强调,次新股池数据必须带日期快照存储,这一点后面会专门讲。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 搭建次新股池的接口方案与字段设计
2.1 数据源选型,我走过的弯路
做A股数据的人,基本绕不开 Tushare、AKShare、Baostock 这三家,外加各种券商量化终端的内部数据。我自己先后都试过,这几种方案的取舍很有意思:
- Tushare 的数据质量相对稳定,但获取新股列表和上市日期通常需要一定积分门槛,很多免费积分只够拉基础行情,不够拉新股明细。
- Baostock 胜在规范,但覆盖面偏窄,新股和次新股相关字段不多,做股票池稍微吃力。
- AKShare 免费且更新快,接口能直接返回新股和次新股列表,适合做原型和中小规模策略,但接口偶尔会因为上游数据源改版而出问题。
我的做法是:主数据源用 AKShare,辅助校验用 Tushare。换句话说,日常跑批用免费源,每周末用另一个源做一次交叉验证,检查有没有漏掉的新上市股票。这个组合在实际使用中还是比较稳的。
在动手写代码之前,还有一件事必须先做清楚:明确你需要的字段。
2.2 核心字段,不只是“代码+上市日期”
一个能派上用场的次新股池,至少要有下面几类信息:
| 字段类别 | 具体字段 | 用途说明 |
|---|---|---|
| 股票标识 | 证券代码、证券简称、市场 | 唯一定位股票 |
| 上市信息 | 上市日期、上市板块 | 计算上市时长、区分沪深/北交所 |
| 股本信息 | 总股本、流通股本 | 判断盘子大小 |
| 市值信息 | 总市值、流通市值 | 过滤微盘股和超大市值股 |
| 行情快照 | 最新价、涨跌幅、换手率 | 即时监控异动 |
| 状态标记 | 是否ST、是否停牌、是否次新 | 策略过滤 |
这里我特别提醒一句:流通市值比总市值在次新场景下更重要。很多股票上市初期有大量限售股,总股本看着不小,但实际流通盘很小,股价极易被资金推动。如果只用总市值做筛选,会漏掉很多真正的活跃次新股。
2.3 关于上市日期和交易日期的一个坑
接口返回的“上市日期”是自然日,不是交易日。比如某只股票1月1日上市,如果中间停牌了30个交易日,到12月底实际交易天数只有200天出头。如果策略以“上市满250个交易日”作为次新池退出条件,按自然日计算就会提前把股票移出池子。
更麻烦的是节假日和调休。A股交易日历不是简单的周一至周五,每年还有临时休市。我踩过这个坑之后,现在的方案是引入交易日历表,把“上市天数”统一换算成“实际交易天数”。交易日历可以手工维护一份,也可以从AKShare的交易日历接口拉取,量不大,存到本地数据库里做一张单独的表就行。
3. 实操案例:用 AKShare 构建一个可运行的次新股池
3.1 环境准备与首次拉取
先安装依赖,我自己用的环境是 Python 3.10 以上:
bash复制pip install akshare pandas
然后试试拉取新股和次新股列表:
python复制import akshare as ak
import pandas as pd
df = ak.stock_zh_a_new()
print(df.head())
print(df.columns)
这里我要先说明一点,AKShare 的接口结构在上游数据源改版时可能会变,所以如果你看到列名和我写的不完全一样,不要慌,先打印 df.columns 看看实际字段。我编写本文时,返回结果主要包括证券代码、证券简称、上市日期这些基本字段。
拿到原始数据后,先统一数据类型:
python复制df["LISTING_DATE"] = pd.to_datetime(df["LISTING_DATE"])
这个动作看起来多余,实际上很有必要。上游返回的日期有时候是 datetime 类型,有时候是字符串 "2024-07-15",不统一直接做日期运算会报错。我建议所有从接口拿到的日期字段,一律先 pd.to_datetime,养成习惯。
3.2 核心筛选逻辑:多维度过滤
接下来就是把原始列表加工成真正的次新股池。我的筛选条件分三层:
- 第一层,上市时间窗口:上市满20个交易日,且不超过250个交易日。
- 第二层,行情状态过滤:剔除停牌股、ST股、退市整理股。
- 第三层,市值过滤:流通市值在10亿到200亿之间,过滤掉偏离常规范围的标的。
代码大致长这样:
python复制from datetime import datetime
# 拿到新股列表后,计算上市天数
df["上市天数"] = (datetime.now() - df["LISTING_DATE"]).dt.days
# 第一层:按上市时间窗口过滤
time_window = df[(df["上市天数"] >= 20) & (df["上市天数"] <= 250)].copy()
# 第二层:剔除ST和停牌,这里用简称判断ST
time_window = time_window[~time_window["SECURITY_NAME_ABBR"].str.contains("ST")]
# 第三层:流通市值过滤,注意先确保数值类型正确
time_window["流通市值"] = pd.to_numeric(time_window["流通市值"], errors="coerce")
pool = time_window[(time_window["流通市值"] >= 10e9) & (time_window["流通市值"] <= 200e9)]
print(pool)
注意市值单位,AKShare 返回值里的单位不同接口可能不一样,有的是元,有的是亿元。我建议在拿到数据后先做一次描述性统计,看一下最小值和最大值,确认单位再动手过滤,不然很容易把亿当元,一次性把整个池子筛空。
3.3 关联实时行情,给池子加上最新状态
只有股票代码和上市日期的池子,还不能直接用于盘中监控。我们通常还要知道每只股票今天的价格、涨跌幅、换手率。这一步我使用全市场行情快照接口来补充:
python复制spot_df = ak.stock_zh_a_spot_em()
这个接口返回全A股实时快照,包含代码、名称、最新价、涨跌幅、总市值、流通市值、换手率等。需要注意的是,这种全市场快照接口调用频率不能太高,否则容易被临时限流。我自己跑批的时候会做大间隔,比如每30分钟取一次快照,盘中需要更高频的话,就用逐个查询的方式,但那样请求次数会明显增加,要掂量清楚。
将两个表合并的关键代码:
python复制merged = pool.merge(
spot_df[["代码", "最新价", "涨跌幅", "换手率", "流通市值"]],
left_on="SECURITY_CODE", right_on="代码", how="left"
)
合完之后,务必检查一下 merged 的行数是否和池子行数一致。如果不一致,说明有部分股票在行情快照里没有匹配上,常见原因是停牌或数据源覆盖不全。这种问题不会报错,但会悄悄让你的池子少掉几只股票。
3.4 做一个定时更新任务
池子不能每天手动跑一遍。我平时用 APScheduler 做一个每日定时任务,收盘后执行一次更新,保存当日快照:
python复制from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime
def update_pool():
# 核心更新逻辑
pool = build_pool()
pool["date"] = datetime.now().strftime("%Y-%m-%d")
# 写入数据库或CSV
pool.to_csv(f"pool_{datetime.now().strftime('%Y-%m-%d')}.csv", index=False)
print("done")
scheduler = BlockingScheduler()
scheduler.add_job(update_pool, "cron", hour=15, minute=30, day_of_week="mon-fri")
scheduler.start()
如果想更简单,直接丢到服务器的 crontab 里跑也行。但不管是哪种方式,我都建议加上异常通知——任务跑失败了,至少发个告警,而不是第二天打开发现池子还是昨天的。
4. 数据清洗与入库,别让接口数据带病进库
4.1 我遇到的脏数据形态
免费数据源最大的问题不是没有数据,而是数据“偶尔脏”。我在做次新股池的过程中,碰到的典型情况有下面几种:
第一,空值和小数点后位数混乱。市值字段经常出现 NaN,换手率有时候是百分比数值,有时候是小数。处理办法是用 pd.to_numeric 统一转换,配合 errors="coerce" 把非法值置空,之后再统一过滤。
第二,退市整理期股票混入列表。有些股票已经进入退市整理期,但某些上游接口并不会及时移除。我的处理办法是维护一份退市股黑名单,做每日比对。虽然有点原始,但有的时候手工维护反而比自动逻辑可靠。
第三,字符串前后有空格或全角字符。股票简称里偶尔混入空格,ST的写法可能是 ST XX,也可能是 *ST XX。做过滤时直接用 str.contains("ST") 会把两者都匹配上,但如果想区分,得用更细的正则。
4.2 用 SQLite 做轻量级快照存储
次新股池数据量不大,没必要上重型数据库,SQLite 足够。我的一张核心表结构大概是这样:
sql复制CREATE TABLE sub_new_pool (
date TEXT,
code TEXT,
name TEXT,
listing_date TEXT,
trading_days INTEGER,
total_market_cap REAL,
float_market_cap REAL,
pct_chg REAL,
turnover_rate REAL,
is_st INTEGER,
is_suspended INTEGER,
PRIMARY KEY (date, code)
);
主键用 (date, code),天然防止同一天重复写入。每天跑批时执行 INSERT OR REPLACE,既能保留历史快照,又能保证同一天数据不会堆积多份。
这里有一个关键体会:一定要保留历史快照,而不是每天覆盖一个 latest_pool。因为大多数值得做的策略,都需要验证“如果三个月前用当时的池子选股,效果怎么样”。没有历史快照,就等于根本没有做过回测的数据基础。
4.3 把次新股池和财务、板块数据做二次关联
上市日期和行情快照只是次新股池的基础层。实际使用中,我会再关联几类数据:
- 所属行业:用于判断次新股属于哪个赛道,比如半导体、医药、新能源。
- 概念板块:观察次新股是否有当前热点概念叠加。
- 财务指标:上市初期财务数据相对干净,联合使用可以做基本面筛选。
这里要注意,概念板块数据的时效性和稳定性都比较差。不同数据商对“次新股”这个概念的定义就不完全一样,你拉到的概念板块成分股可能和你的池子对不上。我做二次关联时,从来不把概念数据作为硬过滤条件,只作为软标签,方便观察和分组。
5. 常见问题与排查技巧实录
5.1 高频问题排查表
以下是我在实际使用中整理的一份问题速查表,分享一下:
| 问题现象 | 可能原因 | 处理方案 |
|---|---|---|
| 接口返回为空 | 上游数据源接口改版或临时异常 | 检查 AKShare 版本,升级到最新版;等待后重试 |
| 上市日期全部是同一天 | 列名判断错误,选错了字段 | 打印 columns 检查实际字段名,不要照抄旧文档 |
| 池子数量骤减 | 市值单位判断错误 | 查看数据最小值和最大值,核实单位后再过滤 |
| 部分股票匹配不上行情 | 停牌、退市、代码带后缀 | 左连接后检查 NaN,单独打印未匹配列表 |
| 回测结果异常好 | 用了未来函数 | 确认池子是历史快照,不能拿当前池子回测过去 |
| 定时任务没有执行 | 服务器时区、节假日 | 检查系统时区,换 cron 时确认用的是本地时间 |
5.2 一个容易忽略的“开板”问题
新股上市后,前几个交易日往往有连续涨停,买不进也卖不出,这个时候把它纳入次新股池,对很多短线策略来说没有实际意义。更合理的做法是引入“开板天数”这个概念——也就是从首次打开涨停板那天开始计算。这个字段接口不一定直接给,需要自己根据每日涨跌幅和涨停价去推算。
我比较简单的实现逻辑是:对于次新股池里的股票,如果上市前20天内某天收盘价低于当日涨停价,就认为该日开板,开板后就标记为“可交易状态”。这个方法不算精确,但用来过滤不可交易阶段已经够用。
5.3 独家建议:维护一份自己的上市日历
我再推荐一个小技巧:不要完全依赖接口的“新股列表”,自己维护一份新股上市日历。原因很简单,接口可能延迟更新,而新股上市是有明确公告的,我可以每天花几分钟从公开渠道把未来一周的上市安排录进一张小表里。
这份日历怎么用?两个场景:
- 接口拉不到数据时,至少知道最近有新股票池要加进来。
- 做预测和排期时,能提前知道未来有多少新股会进入窗口,评估池子规模变化。
我试过很多“全自动”方案,最后发现“接口为主、人工补充”是最稳的组合。纯自动化的系统看着省心,出了问题排查起来反而更费心。
6. 一点体会,关于次新股池的后续玩法
先说一个我个人的真实感受:次新股池这东西,单独拉出来看只是一个列表,但它必须和行情节奏绑定才有灵魂。市场情绪好的时候,次新股池里的活跃股票数量明显上升,涨停家数也多;情绪差的时候,池子里的股票更多是阴跌和无量横盘。我现在做池子的时候,会顺手把涨停家数、平均换手率、平均涨跌幅统计出来,当成一个情绪指标用。
如果后续想扩展,我建议从两个方向入手。一个方向是做“次新+概念”的叠加分析,也就是找出同时具备当前热点概念和次新属性的股票,做事件驱动研究;另一个方向是给池子里的每只股票打上“开板天数”标签,配合高换手率监控,研究次新股从情绪高点到价值回归的路径。这两个方向我都试过,值得深入做下去。
最后照例提醒一句风险:次新股波动幅度远超常规股票,很多策略在历史回测里表现漂亮,实盘一上就变形。用次新股池做研究没问题,但真金白银参与之前,务必做足回测和压力测试,仓位控制别放松。工具能帮你把数据理清楚,但决策永远要留给自己。
