金融数据获取:从零搭建你的数据管道

做量化交易,数据就是你的弹药库。没有数据,再牛的策略也是空中楼阁。今天咱们聊聊怎么把数据搞到手,再把它收拾得服服帖帖。

我个人习惯把数据获取分成两条线:股票用 Tushare,期货用 AkShare。为什么这么分?说白了,Tushare 在股票数据上沉淀更久,接口稳定;AkShare 覆盖面广,期货、基金、宏观数据一把抓。你想想看,要是用 Tushare 拉期货数据,那体验...嗯,我试过,不太爽。

核心观点: 数据获取不是简单的 API 调用,而是一整套工程化流程。从采集、清洗到存储,每一步都可能埋坑。
金融数据管道 数据获取 Tushare / AkShare 数据清洗 去重 / 补缺 / 对齐 数据存储 CSV / HDF5 / 数据库 股票日线 期货分钟 缺失值处理 复权处理 HDF5 存储 MySQL 入库

用 Tushare 拉股票数据

Tushare 是我最早接触的金融数据接口。记得 2018 年那会儿,我还在用它的旧版接口,每天手动更新 token。现在 pro 版方便多了,但有些坑还是得注意。

先装包:

pip install tushare

然后注册获取 token(免费版够用,但有限流)。我个人建议直接上 pro 版,积分 120 以上就能拿到大部分数据。

import tushare as ts

# 初始化
pro = ts.pro_api('你的token')

# 获取平安银行日线数据
df = pro.daily(ts_code='000001.SZ', start_date='20230101', end_date='20231231')
print(df.head())
小技巧: Tushare 的 daily 接口默认返回未复权数据。做回测时一定要用复权数据,否则你的策略会被除权除息搞得面目全非。用 adj_factor 接口自己算,或者直接用 pro.daily(adj='qfq')

我在项目中遇到过一个问题:Tushare 单次请求最多返回 5000 条记录。如果你要拉 5 年的分钟数据,得自己分页。代码大概长这样:

def fetch_stock_data(ts_code, start_date, end_date):
    df_list = []
    offset = 0
    while True:
        df = pro.daily(ts_code=ts_code, start_date=start_date, 
                       end_date=end_date, offset=offset, limit=5000)
        if df.empty:
            break
        df_list.append(df)
        offset += 5000
    return pd.concat(df_list, ignore_index=True)

用 AkShare 抓期货数据

期货数据这块,AkShare 是真心好用。它把国内各大交易所的数据都整合了,而且完全免费。不过要注意,AkShare 更新频繁,有时候接口会变。

pip install akshare

拿螺纹钢主力连续合约举例:

import akshare as ak

# 获取螺纹钢期货日线
df = ak.futures_main_sina(symbol="RB0")
print(df.tail())

你可能会问:为什么用 AkShare 而不是 Tushare 拿期货?我试过 Tushare 的期货接口,数据质量没问题,但字段命名和股票不太一致,处理起来有点别扭。AkShare 的期货数据更贴近交易所原始格式,做跨品种分析时方便很多。

注意: AkShare 的期货数据有时会包含非交易日的「幽灵数据」。我曾经因为这个原因,回测时发现策略在周末居然有交易信号...排查了半天才发现是数据问题。建议获取后先过滤掉非交易日。

数据清洗与预处理

数据拿到手,别急着用。我见过太多人直接拿原始数据跑回测,结果亏得莫名其妙。清洗这一步,省不了。

常见的坑有三个:

  1. 缺失值:停牌日、节假日、数据源漏传,都会产生 NaN
  2. 重复数据:同一个交易日出现两条记录,可能是接口重试导致的
  3. 数据对齐:不同品种的交易时间不同,比如股指期货下午 3:15 收盘,股票 3:00 收盘

我的清洗流程一般是:

def clean_data(df):
    # 1. 去重
    df = df.drop_duplicates(subset=['trade_date'])
    
    # 2. 排序
    df = df.sort_values('trade_date')
    
    # 3. 处理缺失值(前向填充)
    df = df.fillna(method='ffill')
    
    # 4. 删除极端值(比如涨跌幅超过 20% 的异常数据)
    df = df[(df['pct_chg'] > -20) & (df['pct_chg'] < 20)]
    
    return df
避坑指南: 我曾经用后向填充处理缺失值,结果把未来数据泄露到了历史数据中。回测曲线漂亮得不像话,实盘直接打脸。记住:处理时间序列数据,永远用前向填充,别用后向。

数据存储:选对工具省一半力气

数据存哪里?这得看你的使用场景。我一般分三种情况:

存储方式 适用场景 优点 缺点
CSV 小规模数据、快速验证 通用、可读性强 读写慢、无压缩
HDF5 中等规模、频繁读写 压缩率高、读写快 跨语言支持差
MySQL/PostgreSQL 生产环境、多用户访问 支持复杂查询、事务 部署维护成本高

我个人习惯:研究阶段用 HDF5,生产环境用数据库。CSV 嘛,只用来做数据交换。

HDF5 的用法很简单:

import pandas as pd

# 写入
df.to_hdf('stock_data.h5', key='daily', mode='w')

# 读取
df = pd.read_hdf('stock_data.h5', key='daily')

数据库的话,我推荐用 SQLAlchemy 做 ORM,省得写原生 SQL:

from sqlalchemy import create_engine

engine = create_engine('mysql+pymysql://user:password@localhost/quant')
df.to_sql('stock_daily', engine, if_exists='append', index=False)
注意: 数据库写入时,记得设置主键或唯一索引。否则重复运行脚本会导致数据冗余。我吃过这个亏,有一次调度脚本重复执行了三次,数据库里多了三倍的数据,回测结果直接崩了。

好了,数据获取、清洗、存储这一套流程走下来,你的数据管道基本就搭好了。记住:数据质量决定策略上限。花 80% 的时间在数据上,剩下 20% 写策略,这才是量化交易的正确姿势。


公众号:蓝海资料掘金营,微信deep3321