3. 资金流向数据采集与清洗实战

资金流向报表的可信度,几乎完全取决于进入报表之前的那一段数据加工。行业资金流向的原始数据通常来自行情终端导出、交易所公开接口或第三方数据服务,它们往往存在字段命名不一致、交易日缺失、行业分类口径混杂、个别股票成交额异常等问题。本节围绕一条可复现的数据处理链展开:从数据源取数,到缺失值与异常值处理,再到统一时间粒度与行业分类标准,最终得到一张可以直接用于透视和可视化的"干净底表"。

一、先明确目标数据结构

在动手取数之前,应先确定清洗后的目标表结构,否则后续每一步都会反复返工。行业资金流向分析通常需要的最小字段集为:交易日期、行业名称、行业代码、所属分类标准、主力净流入额、超大单净流入额、大单净流入额、成交额、涨跌幅。其中"主力净流入"一般定义为超大单与大单净流入之和,这一口径必须在全表统一,不能一部分数据用"主力"、另一部分用"大单+超大单"混算。

同时要约定方向符号:净流入为正、净流出为负,金额单位统一为"万元"或"亿元"并写入字段说明。参照约定一旦确定,后续所有计算、图表和结论都沿用同一套符号,避免出现"流入为正"和"流出为正"两套逻辑并存。

二、从数据源获取数据

以 Python 为例,若数据源提供 HTTP 接口,可用 requests 拉取 JSON 后转为 DataFrame;若为终端导出的 CSV/Excel,则用 pandas.read_csv 或 read_excel 读入。关键不是读取本身,而是保留原始快照:每次取数都落一份带日期戳的原始文件,清洗脚本只读原始文件、只写清洗结果,保证任何一步都可回溯重跑。

import pandas as pd

raw = pd.read_csv("raw/industry_flow_202406.csv", dtype={"行业代码": str})
raw["交易日期"] = pd.to_datetime(raw["交易日期"], errors="coerce")
raw = raw.sort_values(["交易日期", "行业代码"]).reset_index(drop=True)
print(raw.shape, raw["交易日期"].min(), raw["交易日期"].max())

读入后第一件事是做"体检":查看行数、日期范围、行业代码去重数量、各数值列的缺失比例与极值。这一步能快速暴露数据源是否漏了交易日、是否混入了非行业汇总行(如"全市场合计"),后者若不剔除,会在按行业汇总时造成重复计算。

三、缺失值处理:区分"没有数据"和"没有交易"

资金流向数据的缺失有两种性质,处理方式完全不同。第一种是结构性缺失:某行业在某交易日停牌或无成分股成交,此时净流入应视为 0 而非空值,否则求和时会被跳过,导致行业总额偏小。第二种是采集性缺失:接口超时、字段未返回造成的空值,这类应通过重取或按规则补全,不能简单填 0。

实操中可按以下规则处理:对"成交额"缺失且该行业当日无任何成分股交易的记录,将各资金字段填 0;对单列缺失但同行其他资金字段齐全的记录,用"主力净流入 = 超大单 + 大单"的关系反推校验,若三者中缺一项且另两项存在,则补齐第三项;对无法判断的缺失,标记 数据状态="待核" 并单独输出,不进入正式报表。这样既保证汇总口径完整,又保留了问题数据的可追溯性。

四、异常值识别:用业务规则而非纯统计阈值

资金流向的异常值往往有明确业务含义,纯用 3σ 或 IQR 容易误杀真实的大额流入。更稳妥的做法是组合规则:其一,逻辑校验——主力净流入绝对值不应超过当日成交额,超过则判为异常;其二,关系校验——主力净流入应等于超大单与大单之和,偏差超过 1 万元视为口径错误;其三,跨日突变校验——同一行业单日净流入相对前 20 个交易日均值的偏离超过设定倍数(示例取 8 倍)时进入人工复核队列。

对确认的异常值,处理方式应记录在案:能定位到源数据错误的,回源修正;无法修正但影响较小的,按前后交易日插值或置为缺失并标注;影响汇总结果的,直接剔除该行并计入"剔除清单"。切忌静默修改,否则报表口径无法复现。

资金流向数据采集与清洗链路 数据源 行情终端导出 公开接口 / 第三方 原始快照留存 带日期戳落盘 只读不改写 数据体检 行数 / 日期范围 缺失率 / 极值 剔除干扰行 全市场合计行 重复记录 缺失值处理 无交易 → 资金字段填 0 单列缺失 → 按关系反推 无法判断 → 标记"待核" 异常值识别与处置 净流入 ≤ 成交额 主力 = 超大单 + 大单 跨日突变 → 人工复核 统一时间与行业口径 交易日对齐、补齐日历 行业代码映射到统一标准 金额单位统一为万元 清洗后行业资金流向底表 日期 · 行业代码 · 分类标准 · 主力净流入 · 成交额 · 数据状态

五、统一时间粒度

行业资金流向报表通常以"日"为最小粒度,但数据源可能混有周度、月度汇总行,或缺失个别交易日。统一时间粒度的做法是:先构造一张完整的交易日历(可用交易日接口或从指数日线中提取日期序列),再以该日历为左表,与行业资金数据做左连接。缺失的交易日保留为空行并标注,而不是让日期跳跃,这样后续做"连续 N 日净流入"或"环比"计算时不会把跨周数据误当相邻日。

若需要周度或月度视图,应在日粒度底表之上做聚合,而不是直接使用数据源自带的周/月汇总,因为后者口径可能与本表不一致。聚合时对净流入类字段求和、对成交额求和、对涨跌幅按成交额加权,权重口径需在字段说明中写明。

六、统一行业分类标准

这是最容易出错、也最影响结论的一步。不同数据源可能分别采用申万、中信、证监会或自定义行业分类,同一家公司可能在不同标准下归属不同行业。若直接按行业名称合并,会出现"同一行业两个名字"或"同一名字含义不同"的问题。

推荐做法是建立一张行业映射表,字段包括:源行业代码、源行业名称、源分类标准、目标行业代码、目标行业名称、目标分类标准、生效日期。清洗时以"源分类标准 + 源行业代码"为键做映射,映射不上的记录进入"未映射清单"人工处理,绝不按名称模糊匹配后直接合并。映射表要带生效日期,因为行业分类会调整,历史数据应按当时口径映射,避免用新分类回溯旧数据造成口径漂移。

源行业代码 源行业名称 源分类标准 目标行业代码 目标行业名称 生效日期
801010 农林牧渔 申万 SW01 农林牧渔 2024-01-01
C01 农业 证监会 SW01 农林牧渔 2024-01-01
801080 电子 申万 SW27 电子 2024-01-01

七、清洗结果的自检

清洗完成后不要直接进入可视化,先做三项自检:其一,总量校验——各行业主力净流入之和应等于全市场主力净流入(在剔除汇总行后重新汇总比对,偏差应在可接受范围内);其二,关系校验——逐行检查主力净流入是否等于超大单与大单之和;其三,完整性校验——每个交易日的行业数量是否一致,缺失日期是否已按日历补齐并标注。三项通过后,底表才具备进入报表的资格。

建议把上述规则写成可重复执行的清洗脚本,并在输出中附带一份"清洗日志":记录原始行数、剔除行数、填补缺失数、异常处置数与未映射行业数。这份日志既是质量凭证,也是下次数据源变更时快速定位问题的依据。

至此,一条从取数到可复现底表的链路已经闭合。后续的行业资金流向透视、排名与趋势图,都应建立在这张口径统一、状态可查的底表之上,而不是每次分析都临时从原始文件重新拼凑。