数据源接入策略:行情数据源、财务数据源、另类数据源与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())
我的经验:Tushare的分钟线数据偶尔会有缺失。我曾经跑一个高频策略,发现某天下午14:30-14:35的数据全空了。后来我加了一层校验逻辑——如果某时间段数据为空,自动用前向填充+插值补全。

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())
注意:AKShare的数据源依赖爬虫,节假日或网站维护时可能拿不到数据。我建议你在接入层加一个重试机制,最多重试3次,每次间隔5秒。

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. 财务数据接入的坑

我曾经踩过的坑:
  1. 数据滞后:年报截止日是4月30日,但有些公司拖到最后一天才发布。如果你在3月就用最新数据回测,相当于用了未来信息。
  2. 会计调整:企业合并、资产重组会导致历史数据重述。我建议你保留原始数据,不要用调整后的数据做回测。
  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')
接口设计的关键点:
  1. 所有数据源返回的DataFrame列名必须统一(比如日期列都叫'date',收盘价都叫'close')
  2. 日期格式统一为YYYY-MM-DD
  3. 缺失值处理策略一致(比如统一用NaN表示缺失)

五、知识体系总览

下面这张图是我自己梳理的数据源接入策略框架。你看一眼,基本就能明白整个体系长什么样。

数据源接入策略知识体系 统一API接口层 行情数据源 财务数据源 另类数据源 Tushare AKShare Wind 东方财富 核心接入能力 缓存机制 | 错误重试 | 版本管理 | 数据校验 | 格式统一 标准化DataFrame输出 → 回测引擎

这张图的核心逻辑很简单:底层不管有多少数据源,上层只认统一接口。你想想看,如果每个策略都直接调用Tushare、Wind、AKShare的原始API,哪天数据源换了,策略代码全得改。但有了统一接口层,你只需要改一行配置——切换数据源实现类。

避坑指南:我曾经在统一接口里忘了处理时区问题。Tushare返回的是北京时间,Wind返回的是东八区时间但格式不同,AKShare返回的是字符串。结果回测时日期对不上,亏了三天时间排查。后来我在接口层强制统一为UTC时间戳,再转成北京时间字符串。

好了,数据源接入策略就聊到这儿。记住一句话:数据质量决定回测上限,接口设计决定开发效率。这两件事做好了,后面的策略开发才能事半功倍。

专注资料整理