3. 数据源与采集:常用金融数据源、API 数据获取与数据清洗基础
在组合风险归因建模中,数据的质量与广度直接决定模型的有效性。本章聚焦于金融数据的三大核心环节:数据源选择、API 自动化采集以及数据清洗基础。我们将以 Wind、Bloomberg、Quandl 为例,对比其适用场景,并通过代码示例演示如何从 API 获取数据,最后建立标准化的清洗流程。
3.1 常用金融数据源对比
不同数据源在覆盖范围、更新频率、成本与易用性上存在显著差异。下表总结了三大主流数据源的核心特征,帮助你在实际项目中做出合理选择。
| 数据源 | 覆盖范围 | 更新频率 | API 支持 | 典型成本 | 适用场景 |
|---|---|---|---|---|---|
| Wind | 中国 A 股、债券、期货、基金、宏观经济 | 实时 / 日频 / 分钟级 | WindPy (Python)、Wind API (C++) | 高(机构年费) | 国内量化投研、组合风控 |
| Bloomberg | 全球股票、债券、外汇、衍生品、另类数据 | 实时 / 日频 / 历史 tick | Bloomberg API (blpapi)、BQL | 极高(终端 + 数据许可) | 跨国组合归因、多资产风险模型 |
| Quandl | 全球宏观、期货、指数、另类数据集 | 日频 / 周频 / 月频 | RESTful API (JSON/CSV) | 低(部分免费 / 订阅制) | 回测研究、学术分析、快速原型 |
选型建议:若主要面向 A 股市场且团队已具备 Wind 终端,优先使用 WindPy;若涉及全球多资产且预算充足,Bloomberg 是行业标准;Quandl 适合初创团队或学术研究,其免费数据集(如 FRED、Yahoo Finance)可快速验证模型。
3.2 API 数据获取实战
API(应用程序接口)是实现数据自动化采集的核心手段。以下以 Quandl 和 WindPy 为例,展示如何通过 Python 获取金融时间序列数据。
3.2.1 使用 Quandl RESTful API
Quandl 提供简洁的 REST 接口,支持 JSON 和 CSV 格式。你需要先注册获取 API Key。
import requests
import pandas as pd
# 配置 API Key 与数据集代码
API_KEY = "your_quandl_api_key"
DATASET = "WIKI/AAPL" # 苹果公司历史股价
# 发送 GET 请求
url = f"https://www.quandl.com/api/v3/datasets/{DATASET}.json?api_key={API_KEY}"
response = requests.get(url)
data = response.json()
# 转换为 DataFrame
df = pd.DataFrame(
data['dataset']['data'],
columns=data['dataset']['column_names']
)
df['Date'] = pd.to_datetime(df['Date'])
df.set_index('Date', inplace=True)
print(df.head())
关键参数说明:
api_key:身份验证,免费版有调用频率限制(通常 50 次/分钟)。dataset:格式为“数据库/代码”,如WIKI/AAPL表示 Wiki 数据库中的苹果股票。- 返回 JSON 结构包含
dataset.data(二维数组)和dataset.column_names(列名)。
3.2.2 使用 WindPy 获取 A 股数据
WindPy 是 Wind 金融终端的 Python 接口,需先安装并启动 Wind 客户端。
from WindPy import w
import pandas as pd
# 启动 Wind 连接
w.start()
# 获取贵州茅台(600519.SH)2023年日频收盘价
data = w.wsd(
"600519.SH", # 证券代码
"close", # 指标:收盘价
"2023-01-01", # 开始日期
"2023-12-31", # 结束日期
"Fill=Previous" # 缺失值填充方式:前值填充
)
# 解析返回结果
df = pd.DataFrame(
data.Data,
index=data.Fields,
columns=data.Times
).T
df.index = pd.to_datetime(df.index)
print(df.head())
注意事项:
- WindPy 必须在安装了 Wind 客户端的 Windows 机器上运行,且需保持终端登录状态。
w.wsd返回的是WindData对象,包含Times(时间列表)、Data(数据列表)、Fields(字段列表)。- 参数
Fill=Previous用于处理停牌或非交易日导致的缺失值,避免数据断裂。
3.3 数据清洗基础
原始金融数据通常存在缺失值、异常值、重复记录、格式不一致等问题。数据清洗是风险归因建模前的必要步骤。以下展示一个标准化的清洗流程。
3.3.1 清洗流程概览
下图展示了从原始数据到可用特征集的清洗流水线,包含五个核心阶段。
3.3.2 缺失值处理示例
金融时间序列的缺失值通常由停牌、非交易日或数据源中断引起。以下代码演示了三种常用填充策略。
import pandas as pd
import numpy as np
# 模拟含缺失值的日频数据
dates = pd.date_range('2023-01-01', periods=10, freq='D')
prices = [100, 101, np.nan, 103, 104, np.nan, np.nan, 107, 108, 109]
df = pd.DataFrame({'close': prices}, index=dates)
# 策略 1:前值填充(适用于停牌场景)
df['ffill'] = df['close'].fillna(method='ffill')
# 策略 2:线性插值(适用于短期缺失)
df['interp'] = df['close'].interpolate(method='linear')
# 策略 3:删除缺失率超过 30% 的列
threshold = 0.3
if df['close'].isna().mean() > threshold:
df.drop(columns=['close'], inplace=True)
print("列 'close' 因缺失率过高被删除")
print(df)
3.3.3 异常值检测:IQR 法
异常值会严重扭曲风险指标(如波动率、VaR)。IQR 法是一种稳健的检测方法,不假设数据服从正态分布。
def detect_outliers_iqr(series, factor=1.5):
"""
使用 IQR 法检测异常值
:param series: pandas Series
:param factor: IQR 倍数,默认 1.5(标准),3.0(严格)
:return: 布尔型 Series,True 表示异常值
"""
Q1 = series.quantile(0.25)
Q3 = series.quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - factor * IQR
upper_bound = Q3 + factor * IQR
return (series < lower_bound) | (series > upper_bound)
# 示例:检测收益率异常值
returns = pd.Series([0.01, -0.02, 0.03, -0.15, 0.02, 0.01, -0.03, 0.04, -0.12, 0.02])
outliers = detect_outliers_iqr(returns, factor=1.5)
print("异常值索引:", returns[outliers].index.tolist())
print("异常值:", returns[outliers].values)
清洗最佳实践:
- 始终保留原始数据副本,清洗操作在副本上进行,便于回溯。
- 对收益率序列进行异常值检测时,建议使用
factor=3.0避免过度剔除。 - 多资产组合中,先按时间戳对齐,再统一清洗,避免因频率不一致导致偏差。
3.4 数据源与清洗的集成架构
在实际生产环境中,数据采集与清洗通常被封装为可复用的 ETL 管道。下图展示了从多源采集到清洗入库的完整架构。
3.5 本章小结
- 数据源选择需综合考虑覆盖范围、成本与 API 易用性:Wind 适合 A 股、Bloomberg 适合全球多资产、Quandl 适合快速原型。
- API 采集应封装为可重用的函数,处理认证、限流、重试与错误日志。
- 数据清洗是风险归因建模的基石,缺失值填充、异常值检测、时间戳对齐是三大核心操作。
- 建议将采集与清洗流程集成到 ETL 管道中,确保数据质量可追溯、可复现。