3. 因子数据源接入:股票行情数据、财务数据、另类数据(舆情、新闻)的接入与清洗
因子数据源是量化因子库的“原材料”。不同数据源在接入方式、更新频率、数据质量上差异显著。本章聚焦三类核心数据源——股票行情数据、财务数据、另类数据(舆情/新闻),讲解其接入架构、清洗流程与质量校验方法。
3.1 数据源接入架构总览
一个稳健的因子库需要统一的数据接入层。下图展示了从原始数据到因子计算引擎的标准化接入流程。
架构要点:行情数据通常通过量化数据供应商 API 获取;财务数据依赖定期财报披露,需处理报告期与发布日期的时间差;另类数据非结构化程度高,需先做文本解析再进入清洗流程。
3.2 股票行情数据接入与清洗
3.2.1 接入方式
| 数据粒度 | 典型来源 | 接入协议 | 更新频率 |
|---|---|---|---|
| 日线 | Wind / Tushare / AKShare | HTTP API / SDK | 每日盘后 |
| 分钟线 | 聚宽 / 米筐 / 掘金 | WebSocket / REST | 实时或盘后批量 |
| Tick 级 | 交易所 Level-2 / 量化平台 | TCP / 文件推送 | 实时流式 |
3.2.2 清洗核心步骤
- 复权处理:前复权与后复权选择。因子计算通常使用后复权价格,避免未来函数。
- 缺失值处理:停牌日填充前收盘价,新股上市首日特殊标记。
- 异常值过滤:涨跌幅超过阈值(如 ±20%)需人工复核或剔除。
- 时间对齐:不同交易所(沪深/北交所)交易日历统一,非交易日数据置空。
# 行情数据清洗示例(Python)
import pandas as pd
def clean_market_data(df: pd.DataFrame) -> pd.DataFrame:
# 1. 复权调整(假设已获取复权因子)
df['adj_close'] = df['close'] * df['adj_factor']
# 2. 停牌填充
df['adj_close'] = df['adj_close'].fillna(method='ffill')
# 3. 异常涨跌幅过滤
df['pct_chg'] = df['adj_close'].pct_change()
df.loc[df['pct_chg'].abs() > 0.2, 'adj_close'] = None
# 4. 按交易日对齐
df = df[df['is_trading_day'] == True]
return df.reset_index(drop=True)
3.3 财务数据接入与清洗
3.3.1 数据特点与接入难点
财务数据具有低频、滞后、跨表关联的特点。主要难点在于:
- 报告期与发布日期不一致:季报通常在季度结束后 1 个月内发布,年报最长可延至 4 月底。
- 会计准则差异:新旧准则(如新金融工具准则)导致科目名称和口径变化。
- 合并报表与母公司报表:因子计算需明确使用哪个口径。
3.3.2 清洗流程
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 科目标准化 | 将不同来源的科目名映射到统一字典 |
| 2 | 时间戳对齐 | 使用报告期(fiscal_period)而非发布日期 |
| 3 | TTM 计算 | 滚动十二个月数据,用于估值类因子 |
| 4 | 异常值检测 | 营收/利润突变超过 5 倍标准差需标记 |
| 5 | 行业中性化 | 财务指标按行业做标准化处理 |
# 财务数据 TTM 计算示例
def calc_ttm(fin_df: pd.DataFrame, current_q: str) -> dict:
"""
计算指定报告期的 TTM 值
current_q: 当前报告期,如 '2024Q3'
"""
# 获取最近四个季度数据
quarters = get_last_4_quarters(current_q)
ttm_data = {}
for col in ['revenue', 'net_profit']:
ttm_data[col] = fin_df.loc[fin_df['quarter'].isin(quarters), col].sum()
return ttm_data
避坑提示:财务数据中的“净利润”需区分“归母净利润”与“净利润总额”。多数估值因子(如 PE)使用归母口径。务必在清洗阶段明确标注口径来源。
3.4 另类数据(舆情/新闻)接入与清洗
3.4.1 数据源分类
- 新闻数据:财经门户(东方财富、新浪财经)、专业金融终端(Bloomberg、Reuters)
- 舆情数据:社交媒体(微博、雪球、股吧)、研报摘要
- 另类结构化数据:卫星图像、电商数据、供应链数据(需专业供应商)
3.4.2 接入与清洗流程
另类数据清洗的核心是从非结构化文本中提取情感分数和实体关联。
3.4.3 情感分数计算示例
# 基于词典的舆情情感打分(简化版)
import re
positive_words = ['利好', '增长', '突破', '盈利', '超预期']
negative_words = ['利空', '下跌', '亏损', '风险', '不及预期']
def sentiment_score(text: str) -> float:
pos_count = sum(1 for w in positive_words if w in text)
neg_count = sum(1 for w in negative_words if w in text)
total = pos_count + neg_count
if total == 0:
return 0.0 # 中性
return (pos_count - neg_count) / total # 范围 [-1, 1]
# 示例
news = "公司三季度盈利超预期,营收增长突破历史新高"
print(f"情感分数: {sentiment_score(news):.2f}") # 输出: 0.60
3.5 数据质量校验清单
无论哪种数据源,接入后必须通过以下质量校验:
| 校验项 | 行情数据 | 财务数据 | 另类数据 |
|---|---|---|---|
| 完整性 | 交易日无缺失 | 报告期连续 | 时间戳完整 |
| 一致性 | OHLC 关系校验(H≥L, C≥L) | 资产负债表平衡 | 同一新闻去重 |
| 时效性 | 盘后 30 分钟内更新 | 财报发布后 24h 入库 | 实时流延迟 < 5s |
| 异常值 | 涨跌幅 > 20% 标记 | 营收突变 > 5σ 标记 | 情感分数超出 [-1,1] 剔除 |
最佳实践:建立数据质量监控看板,每日自动运行校验脚本,异常数据通过邮件/钉钉告警。因子库的可靠性取决于数据清洗的严谨程度,而非因子数量。