3. 市场关注度数据源与融合

主题热度不能只靠价格涨跌来判断。价格是结果,而市场关注度更多体现在“钱往哪里走、交易有多活跃、杠杆资金是否参与、外资是否在买、舆论是否在升温”。本节把市场侧数据源分成五类,并重点解决一个工程问题:这些数据频率不同、时间戳不同、实体命名不同,如何对齐成一张可用于量化打分的宽表。

五类市场侧数据源及其关注度含义

资金流向通常按主动买入与主动卖出成交额之差统计,常见字段为主力净流入、超大单净流入。它反映的是“有方向的钱”,对短期热度敏感,但单日噪声大,一般取5日、10日累计值平滑。成交量与换手率是活跃度的基础度量:成交量是绝对量,换手率=成交量/流通股本,做了股本归一化,因此跨股票可比性更好,是热度打分中最常用的活跃度代理变量。融资融券提供杠杆情绪:融资余额上升说明看多资金加杠杆,融券余额上升则反映做空或对冲需求,常用融资买入额占成交额比例衡量杠杆参与强度。北向资金(沪股通、深股通)代表外资偏好,需注意2024年8月起交易所已停止披露实时净买入额,改为按季度披露持股数据,因此历史回测中该因子存在数据口径断点,必须做分段处理。舆情情绪来自新闻、股吧、研报标题等文本,经情感模型输出[-1,1]的情绪分与提及量,属于高频但噪声最大的数据。

多源数据融合:频率统一 → 时间对齐 → 实体映射 → 热度宽表 资金流向 日频 · 收盘后 成交量/换手率 日频 · 收盘后 融资融券 日频 · T+1晚 北向资金 季度持股 · 口径断点 舆情情绪 分钟级 · 情绪分 频率统一 分钟舆情 → 日聚合 均值 + 提及量 季度持股 → 日 前向填充 ffill 日频 → 周频 按周取均值 统一目标频率: 交易日日频 缺失值标记 NaN 不盲目补零 时间对齐 统一交易日历 剔除停牌日 T+1 数据错位 融资融券滞后一日 防未来函数: 只用 T 日及之前 时区统一为 Asia/Shanghai 对齐键: (日期, 证券代码) 实体映射 600519.SH 贵州茅台 / 茅台 → 统一证券ID 舆情文本实体识别 别名表 + 模糊匹配 处理改名/借壳 保留历史映射 行业/板块标签 同步挂接 热度 宽表 日期 证券ID 净流入 换手率 融资占比 北向持股 情绪分 提及量 对齐结果示例(教学假设数据,非实测) 日期 证券ID 主力净流入(万元) 换手率(%) 融资买入占比(%) 情绪分 提及量 2025-03-03 600519.SH +8,200 0.62 9.4 +0.41 1,260 2025-03-04 600519.SH -3,100 0.55 8.9 +0.18 980

频率统一:先定目标频率,再决定聚合方式

融合的第一步不是急着合并,而是确定目标频率。热度打分通常以交易日日频为基准,因为绝大多数行情与资金数据都是日频。对高于日频的数据(如分钟级舆情),按交易日聚合为当日情绪均值与当日提及总量;对低于日频的数据(如季度北向持股),采用前向填充,即每个交易日沿用最近一次已披露的持股数量,并额外记录“距上次披露天数”,避免把陈旧数据当作新鲜信号。对周频数据则按自然周取均值后回填到该周各交易日。

一个关键原则是:缺失值不要盲目补零。停牌日没有成交,补零会制造“活跃度骤降”的假信号;融资融券T+1披露,当日缺失应标记为NaN并在打分时跳过该因子,而不是填0。补零与缺失在语义上完全不同,混淆会直接污染热度分数。

时间对齐:交易日历与防未来函数

对齐键统一为(trade_date, sec_id)。具体做法是:以交易所交易日历为主表,左连接各数据源;停牌日保留行但因子置NaN;所有T+1披露的数据(融资融券、部分资金流向)在合并时整体后移一个交易日,确保T日打分只使用T日收盘后可知的信息。这一步是回测可信度的生命线——若把T+1的融资数据错配到T日,热度因子会获得不应有的“预知能力”,回测虚高而实盘失效。

import pandas as pd

# 各源均已整理为 index=(trade_date, sec_id) 的 DataFrame
cal = trade_calendar[["trade_date"]]          # 交易日历主表
df = cal.merge(flow,   on="trade_date", how="left") \
        .merge(turnover, on=["trade_date","sec_id"], how="left") \
        .merge(margin.shift(1), on=["trade_date","sec_id"], how="left") \
        .merge(northbound.ffill(), on=["trade_date","sec_id"], how="left") \
        .merge(sentiment, on=["trade_date","sec_id"], how="left")

# 标记北向数据陈旧度
df["nb_stale_days"] = df.groupby("sec_id")["nb_hold"].apply(
    lambda s: s.isna().groupby((~s.isna()).cumsum()).cumcount()
).reset_index(level=0, drop=True)

实体映射:让“茅台”和“600519.SH”指向同一行

结构化数据用证券代码,舆情文本却常出现简称、别名甚至错别字。实体映射需要一张别名表:以统一证券ID为主键,维护正式简称、常用别名、历史名称。舆情文本先做实体识别,命中别名表则映射到证券ID,未命中再走模糊匹配并人工抽检。特别要注意改名与借壳:同一代码在不同时期对应不同公司名称,映射表必须带生效日期区间,否则会把旧名称的舆情错误归到新主体上。行业与板块标签也应在此阶段挂接,便于后续做行业中性化。

融合后的质量校验

宽表生成后至少做三项检查:一是覆盖率,统计每个因子在各交易日的非空比例,低于阈值(如80%)的因子应降权或剔除;二是量纲一致性,换手率是百分比、净流入是万元,打分前必须做横截面标准化(如Z-score或分位数排名);三是断点检查,北向资金口径变更前后分别统计因子分布,确认没有因口径切换导致的系统性跳变。完成这三步,多源数据才算真正“融合”为可用于热度量化的输入。