数据源接入策略:行情数据源、财务数据源、另类数据源与API接口设计
做量化回测,第一步就是搞定数据。我见过太多团队,策略写得天花乱坠,最后死在数据质量上。说白了,数据源接入策略就是你的地基——地基不稳,楼盖得再高也得塌。
今天咱们聊聊行情数据源、财务数据源、另类数据源,以及API接口怎么设计。嗯,这里面的坑,我基本都踩过一遍。
一、行情数据源:三大主流选择
行情数据是回测的命脉。我个人习惯把行情数据源分成三类:免费开源、商业付费、机构级。
1. Tushare(免费+积分制)
Tushare 是国内量化圈用得最多的免费数据源之一。它覆盖了股票、基金、期货、指数等行情数据,还附带一些基础财务数据。
- 通过积分制获取不同权限,基础积分免费
- 数据更新频率:日线T+1,分钟线延迟约15分钟
- 支持Python SDK,调用简单
# 我常用的Tushare接入方式
import tushare as ts
# 初始化(记得去官网注册拿token)
pro = ts.pro_api('your_token_here')
# 获取日线行情
df = pro.daily(ts_code='000001.SZ', start_date='20230101', end_date='20231231')
print(df.head())
2. AKShare(完全免费,社区驱动)
AKShare 是另一个免费选择,数据源来自各大财经网站。它的优点是覆盖面广——连加密货币、宏观经济数据都有。缺点是数据格式偶尔会变,因为上游网站改版了。
import akshare as ak
# 获取A股日线
stock_zh_a_hist_df = ak.stock_zh_a_hist(symbol="000001", period="daily",
start_date="20230101", end_date="20231231", adjust="qfq")
print(stock_zh_a_hist_df.head())
3. Wind(商业付费,机构标配)
Wind 是机构级的数据源,数据质量最高,延迟最低。但价格不菲——一年几万到几十万不等。如果你在私募或券商工作,大概率会用Wind。
Wind的API设计比较老,但功能强大。它支持C++、Python、Matlab等多种语言。我个人习惯用WindPy接口:
from WindPy import w
w.start()
# 获取行情数据
data = w.wsd("000001.SZ", "open,high,low,close,volume", "2023-01-01", "2023-12-31", "")
print(data.Data)
| 场景 | 推荐数据源 | 原因 |
|---|---|---|
| 个人学习/小规模回测 | Tushare 或 AKShare | 免费,够用 |
| 私募/机构生产环境 | Wind | 数据质量高,合规 |
| 多市场覆盖(港股、美股) | AKShare + 补充数据源 | 覆盖面广 |
二、财务数据源:比行情更头疼
财务数据比行情数据麻烦得多。为什么?因为财务数据有滞后性、调整频繁、不同会计准则口径不同。
我刚开始做基本面量化时,用Tushare的财务数据直接跑回测,结果发现某只股票的净利润数据在三个月后修正了——回测结果全变了。嗯,从那以后我养成了一个习惯:财务数据必须做版本管理。
1. 主流财务数据源
- Tushare 财务模块:提供资产负债表、利润表、现金流量表,更新频率为季频
- Wind 财务数据:数据最全,支持历史回溯(比如查看某只股票三年前的财务数据在当时是什么值)
- 东方财富 Choice:性价比高,很多私募在用
2. 财务数据接入的坑
- 数据滞后:年报截止日是4月30日,但有些公司拖到最后一天才发布。如果你在3月就用最新数据回测,相当于用了未来信息。
- 会计调整:企业合并、资产重组会导致历史数据重述。我建议你保留原始数据,不要用调整后的数据做回测。
- 口径不一致:有些数据源用合并报表,有些用母公司报表。做因子计算时一定要统一口径。
# 财务数据接入示例(带版本标记)
import pandas as pd
def get_financial_data(stock_code, report_date, version='raw'):
"""
获取财务数据,version参数控制是否使用调整后数据
"""
if version == 'raw':
# 获取原始数据(未调整)
data = fetch_from_source(stock_code, report_date, adjusted=False)
else:
# 获取调整后数据
data = fetch_from_source(stock_code, report_date, adjusted=True)
# 添加版本标记
data['data_version'] = version
data['fetch_timestamp'] = pd.Timestamp.now()
return data
三、另类数据源:超额收益的来源
行情和财务数据大家都用,很难做出差异化。另类数据才是超额收益的真正来源。说白了,别人看不到的信息,你看到了,你就赚钱。
1. 常见的另类数据
| 数据类型 | 来源 | 应用场景 |
|---|---|---|
| 舆情数据 | 新闻、社交媒体、股吧 | 事件驱动策略、情绪因子 |
| 供应链数据 | 企业公告、海关数据 | 产业链传导分析 |
| 卫星图像数据 | 商业卫星公司 | 零售客流、港口吞吐量 |
| 电商数据 | 淘宝、京东、拼多多 | 消费品销量预测 |
2. 另类数据接入的挑战
另类数据最大的问题是——非结构化。新闻是文本,卫星图像是图片,电商数据是半结构化的JSON。你需要做大量的清洗和特征工程。
# 舆情数据接入示例(简化版)
import requests
import json
def fetch_sentiment_data(keyword, date):
"""
获取某关键词的舆情情绪得分
"""
url = f"https://api.some-sentiment-source.com/search?q={keyword}&date={date}"
response = requests.get(url, headers={'Authorization': 'Bearer your_api_key'})
if response.status_code == 200:
data = response.json()
# 提取情绪得分
sentiment_score = data.get('sentiment', 0.5)
return sentiment_score
else:
# 失败时返回中性值
return 0.5
四、API接口设计:统一接入层
数据源多了以后,你会发现一个问题——每个数据源的API风格都不一样。Tushare用Python SDK,Wind用WindPy,AKShare直接返回DataFrame。如果每个策略都直接调用不同API,代码会乱成一锅粥。
我的做法是:设计一个统一的数据接入层。说白了,就是做一个中间层,把所有数据源封装成统一的接口。
1. 接口设计原则
- 统一输入输出:不管底层是哪个数据源,对外暴露的接口参数和返回格式一致
- 缓存机制:避免重复请求,尤其是付费数据源
- 错误处理:数据源挂了怎么办?自动切换备用数据源
- 版本管理:记录每次数据拉取的时间戳和数据版本
2. 代码示例:统一数据接口
from abc import ABC, abstractmethod
import pandas as pd
class DataSource(ABC):
"""数据源抽象基类"""
@abstractmethod
def get_daily_data(self, symbol: str, start_date: str, end_date: str) -> pd.DataFrame:
pass
@abstractmethod
def get_financial_data(self, symbol: str, report_date: str) -> pd.DataFrame:
pass
class TushareSource(DataSource):
def __init__(self, token):
import tushare as ts
self.pro = ts.pro_api(token)
def get_daily_data(self, symbol, start_date, end_date):
df = self.pro.daily(ts_code=symbol, start_date=start_date, end_date=end_date)
# 统一列名
df.rename(columns={'trade_date': 'date', 'vol': 'volume'}, inplace=True)
return df
def get_financial_data(self, symbol, report_date):
# 实现略
pass
class WindSource(DataSource):
def __init__(self):
from WindPy import w
w.start()
self.w = w
def get_daily_data(self, symbol, start_date, end_date):
data = self.w.wsd(symbol, "open,high,low,close,volume", start_date, end_date)
df = pd.DataFrame(data.Data, index=data.Fields).T
df['date'] = data.Times
return df
def get_financial_data(self, symbol, report_date):
# 实现略
pass
# 使用示例
def get_data(source: DataSource, symbol, start, end):
"""统一调用入口"""
return source.get_daily_data(symbol, start, end)
# 切换数据源只需改一行
# source = TushareSource('your_token')
source = WindSource()
df = get_data(source, '000001.SZ', '20230101', '20231231')
- 所有数据源返回的DataFrame列名必须统一(比如日期列都叫'date',收盘价都叫'close')
- 日期格式统一为YYYY-MM-DD
- 缺失值处理策略一致(比如统一用NaN表示缺失)
五、知识体系总览
下面这张图是我自己梳理的数据源接入策略框架。你看一眼,基本就能明白整个体系长什么样。
这张图的核心逻辑很简单:底层不管有多少数据源,上层只认统一接口。你想想看,如果每个策略都直接调用Tushare、Wind、AKShare的原始API,哪天数据源换了,策略代码全得改。但有了统一接口层,你只需要改一行配置——切换数据源实现类。
好了,数据源接入策略就聊到这儿。记住一句话:数据质量决定回测上限,接口设计决定开发效率。这两件事做好了,后面的策略开发才能事半功倍。