第三章:金融数据获取——Tushare/AkShare数据接口、股票/期货/加密货币数据获取、数据存储与缓存策略
做量化交易,第一道坎就是数据。我见过太多人策略写得漂亮,结果数据源没选对,回测跑出来全是幻觉。说白了,数据是量化系统的地基,地基不稳,楼盖得再高也得塌。
这一章,我带你搞定数据获取的全流程。从接口选择到存储策略,咱们一步到位。
3.1 数据接口选型:Tushare vs AkShare
国内量化圈,Tushare和AkShare是两大主流。我个人习惯两个都备着,哪个好用用哪个。但你要搞清楚它们的区别。
| 特性 | Tushare Pro | AkShare |
|---|---|---|
| 数据源 | 自建数据库,稳定 | 爬取公开网站,实时 |
| 费用 | 积分制,部分免费 | 完全免费 |
| 速度 | 快,有缓存 | 依赖目标网站 |
| 覆盖范围 | A股、基金、指数 | A股、期货、加密货币、宏观 |
| 文档质量 | 清晰,有社区 | 更新快,但略乱 |
我在项目中遇到过一个问题:用AkShare拉期货数据,某天突然报错,原来是交易所网站改版了。所以我的建议是——关键数据用Tushare,辅助数据用AkShare,两者互补。
3.2 股票数据获取实战
先看Tushare怎么拿A股日线数据。代码很简单,但有几个坑要注意。
import tushare as ts
# 初始化,token在官网注册获取
pro = ts.pro_api('你的token')
# 获取平安银行日线数据
df = pro.daily(ts_code='000001.SZ', start_date='20240101', end_date='20240301')
print(df.head())
嗯,这里要注意:ts_code的格式是「代码.交易所」,上交所是.SH,深交所是.SZ。我刚开始写的时候,老把.SH写成.SH,结果数据死活拉不出来。
再看AkShare的写法,更直接一些:
import akshare as ak
# 获取A股日线
df = ak.stock_zh_a_hist(symbol="000001", period="daily",
start_date="20240101", end_date="20240301", adjust="qfq")
print(df.head())
AkShare默认返回前复权数据,这个很实用。你想想看,做回测如果不复权,分红送股直接让你的策略曲线失真。
3.3 期货数据获取
期货数据和股票不太一样。它有主力合约、连续合约的概念。直接拉单个合约,你会发现数据断断续续的。
我的做法是拉「主力连续」合约。AkShare里这样搞:
import akshare as ak
# 获取螺纹钢主力连续
df = ak.futures_main_sina(symbol="RB0")
print(df.tail())
这里RB0就是螺纹钢主力连续。数字0代表当前主力,1代表下一个主力。Tushare也有类似功能,用fut_mapping接口。
为什么强调主力连续?因为期货合约会到期,你不可能一直交易同一个合约。主力连续帮你自动切换,省心很多。
3.4 加密货币数据获取
加密货币这块,AkShare直接支持。但说实话,它的数据源是公开的,延迟比较大。做实时交易的话,建议用交易所API。
import akshare as ak
# 获取比特币现货数据
df = ak.cryptocurrency_hist(symbol="BTC", period="daily")
print(df.head())
这里拿的是Binance的数据。如果你需要分钟级数据,可以加period="1min"。不过要注意,加密货币是7×24小时交易,数据量巨大。我建议只拉最近30天的,别贪多。
我个人习惯把加密货币数据单独存一个库。因为它的结构和A股差异太大——没有涨跌停、没有休市、交易时间不连续。
3.5 数据存储策略
数据拉下来,存哪?我踩过不少坑。最开始用CSV,后来用SQLite,现在用InfluxDB。给你看看我的选择逻辑:
| 存储方式 | 适用场景 | 缺点 |
|---|---|---|
| CSV | 小数据量、快速验证 | 查询慢、无索引 |
| SQLite | 单机、百万级数据 | 并发差 |
| MySQL/PostgreSQL | 团队协作、千万级 | 部署麻烦 |
| InfluxDB | 时序数据、高频 | 学习成本高 |
我的建议是:个人用SQLite就够了。代码简单,不用装数据库。团队用PostgreSQL,支持窗口函数,做因子分析很方便。
import sqlite3
import pandas as pd
# 创建数据库
conn = sqlite3.connect('quant_data.db')
# 存储DataFrame
df.to_sql('stock_daily', conn, if_exists='append', index=False)
# 读取数据
df = pd.read_sql("SELECT * FROM stock_daily WHERE ts_code='000001.SZ'", conn)
3.6 缓存策略:别让接口累死
你想想看,每次回测都去拉数据,接口不封你才怪。缓存是必须的。
我的缓存策略分三层:
- 内存缓存:用Python的
lru_cache,同一个交易日的数据只拉一次。 - 文件缓存:用Parquet格式存本地,比CSV快10倍。
- 数据库缓存:存到SQLite,方便跨进程共享。
代码实现很简单:
from functools import lru_cache
import pandas as pd
@lru_cache(maxsize=128)
def get_stock_data(ts_code, start_date, end_date):
# 先查数据库,没有再拉接口
df = pd.read_sql(...)
if df.empty:
df = pro.daily(ts_code=ts_code, start_date=start_date, end_date=end_date)
df.to_sql('stock_daily', conn, if_exists='append')
return df
这里有个细节:lru_cache的参数必须是可哈希的。所以日期要转成字符串,别传datetime对象。
time.time()加个判断,超过当天15:30就重新拉。
3.7 知识体系总览
这一章的内容比较多,我画了张图帮你理清思路:
这张图把整个数据流程串起来了。从上到下,数据源→获取→存储,每一步都有讲究。你照着这个框架搭,基本不会出大问题。
最后说一句:数据获取是脏活累活,但也是最有价值的活。把这一步做扎实了,后面的策略开发就是水到渠成的事。
公众号:蓝海资料掘金营,微信deep3321