4. 数据获取与预处理实战
资金集中度与领涨股贡献拆分,本质上是对“资金在个股之间如何分布、谁在推动指数”的量化刻画。这个刻画是否可信,取决于底层数据是否干净、口径是否一致、量纲是否可比。本节围绕三个核心字段——个股资金流(主力净流入)、成交额、涨跌幅——完整走一遍从获取到可计算面板数据的处理链路,并给出可直接复用的清洗规则与标准化方法。
一、明确字段口径与数据来源
在动手取数之前,先把每个字段的“定义”钉死,否则后续对齐会反复返工。本课程采用如下口径(示例设定,实际以所用数据源文档为准):
- 主力净流入(net_mf):单位统一为“元”,按大单与特大单的主动买入额减主动卖出额计算;不同数据源对“大单”的金额门槛不同,必须全样本使用同一来源,不可混用。
- 成交额(amount):单位统一为“元”,取当日成交金额而非成交量,避免股价差异带来的量纲失真。
- 涨跌幅(pct_chg):单位统一为“%”,采用前复权价格计算,保证除权除息日不出现虚假跳空。
- 交易日(trade_date):统一为
YYYY-MM-DD字符串或日期类型,禁止混用20240105与2024-01-05。
数据获取通常有两种路径:一是通过行情接口按交易日循环拉取全市场快照,二是直接下载历史日频文件。前者适合增量更新,后者适合一次性回补。无论哪种,都要保留原始字段名与原始单位,另建一张“标准字段映射表”,把 netamount、turnover、pct_change 之类的源字段统一映射为 net_mf、amount、pct_chg,这样换数据源时只需改映射,不动下游逻辑。
二、清洗:缺失、异常与重复
原始数据最常见的三类问题及处理规则如下:
| 问题类型 | 典型表现 | 处理规则 |
|---|---|---|
| 缺失 | 停牌日无记录、字段为空 | 停牌日不插值,保留“无交易”状态;单字段偶发空值用同日同股其他源补齐,补不齐则剔除该股当日记录 |
| 异常值 | 涨跌幅绝对值超过 21%(非注册制新股)、成交额为 0 但涨跌幅非 0 | 标记为可疑,回查原始行情;确认错误则置空,不参与当日计算 |
| 重复 | 同一 trade_date + code 出现多条 | 按主键去重,保留最新拉取时间的一条 |
需要特别提醒:不要对停牌日做前向填充。停牌期间资金流为 0、成交额为 0,若用前一日数值填充,会人为制造“资金持续流入”的假象,直接污染集中度指标。正确做法是保留停牌标记,在计算截面指标时把该股从当日样本中排除,而不是给它一个虚假的数值。
三、对齐:让三张表在同一坐标系下
资金流、成交额、涨跌幅往往来自不同接口,交易日历和股票代码格式可能不一致。对齐分两步:
- 代码对齐:统一为带交易所后缀的格式,如
600519.SH、000001.SZ,剔除已退市、B 股、停牌超过阈值(如连续 60 个交易日)的标的,避免样本漂移。 - 日期对齐:以交易日历为主键做内连接,只保留三个字段都存在的记录;对“当日有行情但资金流缺失”的个股,标记为
mf_missing=1,在后续计算中单独处理,而不是简单丢弃整行。
对齐完成后,得到一张长表:每行是“某股某日”,列为 code、trade_date、net_mf、amount、pct_chg。这张表就是后续所有指标的唯一数据底座。
四、标准化:把不同量纲拉到同一把尺子上
清洗对齐之后,net_mf 是“元”、amount 是“元”、pct_chg 是“%”,三者量级差异巨大,直接加权没有意义。标准化要解决两个问题:量纲可比与截面可比。
第一步做量纲统一。把资金流和成交额都换算成“亿元”,涨跌幅保留百分数。这一步只是单位换算,不改变分布形状。
第二步做截面标准化。资金集中度关心的是“当日资金在个股间的相对分布”,因此更适合用截面排名百分位而非全样本 Z-Score。具体做法:对每个交易日,把当日所有有效个股的 net_mf 升序排名,再除以当日有效样本数,得到 0~1 之间的分位值 mf_rank;成交额同理得到 amt_rank。这样做的优点是抗极端值——某只股票单日净流入 50 亿不会把整列均值拉爆,排名法只关心它排第几。
如果确实需要 Z-Score,务必按日分组计算,而不是对整段历史一次性标准化。跨日标准化会把“牛市整体放量”误判为个股异常,这是资金流分析里最常见的隐性错误。
# 示例:按日截面排名标准化(pandas 伪代码,参数为示例设定)
df["mf_rank"] = df.groupby("trade_date")["net_mf"].rank(pct=True)
df["amt_rank"] = df.groupby("trade_date")["amount"].rank(pct=True)
# 仅对有效样本排名,缺失资金流的个股不参与
valid = df["mf_missing"] == 0
df.loc[valid, "mf_rank"] = df.loc[valid].groupby("trade_date")["net_mf"].rank(pct=True)
五、结果检查:怎么判断预处理过关了
处理完不要急着算指标,先做三项体检:
- 覆盖率:每个交易日有效样本数应稳定在合理区间(例如全市场 5000 只左右,剔除停牌后不低于 4500),若某日骤降,说明对齐或代码映射出了问题。
- 分布合理性:
pct_chg的日均值应接近 0,标准差在 1.5%~2.5% 量级(示例参考值);mf_rank应近似均匀分布在 0~1,若严重偏斜,检查是否有大量重复值或缺失被误当 0 处理。 - 抽样核对:随机抽 3~5 只股票、3 个交易日,把处理后的
net_mf、amount、pct_chg与原始行情逐条比对,确认单位、正负号、复权口径无误。
至此,我们得到了一张干净、对齐、可比的个股日频面板。它不直接回答“资金集中度是多少”,但它是回答这个问题的前提——后续所有集中度计算与贡献拆分,都建立在这张表的字段口径与样本范围之上。