金融数据获取:用Tushare和Akshare搞定A股日线数据
做量化选股,第一步就是搞数据。没有数据,再牛的模型也是空中楼阁。
我个人习惯把数据获取比作「做饭前的买菜」。菜买不好,厨艺再高也白搭。今天咱们就聊聊怎么用Python把A股的日线数据拿到手,洗干净,存起来。
数据源的选择:Tushare vs Akshare
国内做量化,绕不开这两个库。我两个都用过,说说我的感受。
| 对比项 | Tushare | Akshare |
|---|---|---|
| 数据质量 | 高,经过校验 | 中等,部分数据需二次处理 |
| 获取门槛 | 需要注册,有积分限制 | 免费,无需注册 |
| 数据范围 | 全面,含财务、因子等 | 覆盖主流,更新快 |
| 稳定性 | 高,有付费保障 | 依赖源网站,偶尔波动 |
| 适合场景 | 正式回测、生产环境 | 快速原型、学习研究 |
我的建议是:学习阶段用Akshare,省心;正式项目用Tushare,靠谱。
实战:用Akshare获取日线数据
先来个最简单的。用Akshare拿贵州茅台的历史日线。
import akshare as ak
import pandas as pd
# 获取贵州茅台日线数据
df = ak.stock_zh_a_hist(symbol="600519",
period="daily",
start_date="20200101",
end_date="20231231",
adjust="qfq")
print(df.head())
print(df.info())
跑完你会发现,数据字段挺全的:开盘、收盘、最高、最低、成交量、成交额……
但注意,这里有个坑——日期列是字符串格式。我刚开始做回测时没注意,直接拿字符串去算时间差,结果全错了。
获取到的日期列是object类型,不是datetime。必须手动转换:
df['日期'] = pd.to_datetime(df['日期'])
否则后续的时间切片、滚动计算都会出问题。
数据清洗:脏数据比没数据更可怕
拿到原始数据后,别急着用。先做三件事:
- 检查缺失值——停牌日、节假日会导致数据缺失
- 检查异常值——比如某天涨幅超过100%,大概率是复权问题
- 检查重复值——同一个交易日出现两条数据
我一般这样处理:
# 1. 删除重复行
df = df.drop_duplicates(subset=['日期'])
# 2. 填充缺失值(前向填充)
df = df.fillna(method='ffill')
# 3. 过滤异常值
df = df[df['收盘'] > 0] # 剔除价格为0或负数的数据
# 4. 重置索引
df = df.reset_index(drop=True)
对于停牌日的数据,我习惯用前一天的收盘价填充。但如果你做的是高频策略,最好标记出来,别让模型以为市场在「平稳运行」。
数据存储:SQLite还是CSV?
这个问题我纠结过很久。直接说结论:
- CSV:简单粗暴,适合小规模数据、快速验证
- SQLite:结构化查询,适合多股票、多周期、需要频繁读取的场景
我个人推荐SQLite。为什么?你想想看,如果你要查「2023年所有涨幅超过5%的股票」,用CSV你得遍历所有文件,用SQLite一句SQL就搞定。
存到SQLite的代码:
import sqlite3
# 创建数据库连接
conn = sqlite3.connect('stock_data.db')
# 将DataFrame写入表
df.to_sql('daily_600519', conn, if_exists='replace', index=False)
# 验证
query_df = pd.read_sql("SELECT * FROM daily_600519 WHERE 日期 >= '2023-01-01'", conn)
print(query_df.head())
conn.close()
存成CSV更简单:
df.to_csv('600519_daily.csv', index=False, encoding='utf-8-sig')
如果你只做一两只股票的分析,CSV完全够用。但如果你要管理全A股5000多只股票的历史数据,一定要用数据库。我曾经用CSV存了3000只股票的数据,结果一个文件夹里3000个文件,找数据找到崩溃。
知识体系总览
下面这张图,概括了本章的核心逻辑:
完整代码示例:一条龙搞定
最后,给你一个可以直接跑的完整脚本。把股票代码、起止时间改一下就能用。
import akshare as ak
import pandas as pd
import sqlite3
from datetime import datetime
def fetch_and_store(symbol, start_date, end_date, db_path='stock_data.db'):
"""
获取股票日线数据并存入SQLite
"""
print(f"正在获取 {symbol} 的数据...")
# 1. 获取数据
df = ak.stock_zh_a_hist(symbol=symbol,
period="daily",
start_date=start_date,
end_date=end_date,
adjust="qfq")
# 2. 数据清洗
df['日期'] = pd.to_datetime(df['日期'])
df = df.drop_duplicates(subset=['日期'])
df = df.sort_values('日期')
df = df.reset_index(drop=True)
# 3. 存储到SQLite
conn = sqlite3.connect(db_path)
table_name = f"daily_{symbol}"
df.to_sql(table_name, conn, if_exists='replace', index=False)
conn.close()
print(f"✅ {symbol} 数据已存入 {db_path} 的 {table_name} 表")
print(f" 共 {len(df)} 条记录,时间范围:{df['日期'].min()} ~ {df['日期'].max()}")
return df
# 使用示例
if __name__ == "__main__":
df = fetch_and_store("600519", "20200101", "20231231")
print(df.head())
第一次跑的时候,先拿一只股票试试。确认数据没问题了,再批量获取。我曾经一次性跑了500只股票,结果发现复权参数设错了,全部重来……那叫一个酸爽。
好了,数据到手,清洗干净,存好地方。下一步就是怎么用这些数据构建选股模型了。不过那是后面章节的事,咱们先把基础打牢。