本章学习目标
- 了解主流免费和付费数据源的特点与适用场景
- 学会使用Python获取股票、指数和基金数据
- 掌握数据清洗的核心技能——处理缺失值、异常值和复权问题
- 理解前复权与后复权的本质区别及其对策略的影响
- 建立"数据质量决定策略质量"的核心认知
那个因为数据错误而亏钱的教训
2022年夏天,量化爱好者小陈终于完成了他的第一个策略——"双均线金叉做多"。回测结果非常漂亮:年化收益35%,最大回撤12%。小陈兴奋不已,投入了20万实盘资金。
两个月后,账户亏损了15%。小陈百思不得其解:回测明明很好啊?他开始逐一排查问题,最终在数据环节发现了致命错误:他使用的数据源没有正确处理分红除权。
某只股票在他回测期间进行了一次大额分红,股价从50元跳到了45元。他的数据源把这5元的下跌记录为了真实的"价格下跌",策略于是在"大跌"后发出了买入信号。但在实盘中,这根本不是价格下跌,而是除权!
这个教训让小陈深刻理解了量化交易中的一条铁律:Garbage In, Garbage Out(垃圾进,垃圾出)。数据质量是一切的基础,数据错了,再好的策略也是空中楼阁。
数据源全景图:从免费到付费
在开始写代码之前,我们需要先了解有哪些数据可以用、从哪里获取。以下是量化交易中常用的数据源对比。
| 数据源 | 覆盖市场 | 费用 | 数据质量 | 适用人群 |
|---|---|---|---|---|
| yfinance | 美股、ETF | 免费 | 中等 | 美股初学者 |
| AKShare | A股、港股、期货 | 免费 | 中等 | A股量化初学者 |
| Tushare | A股、港股、美股 | 积分制 | 较高 | 进阶A股开发者 |
| Baostock | A股 | 免费 | 中等 | A股初学者 |
| Wind | 全市场 | 付费(昂贵) | 极高 | 机构用户 |
| Choice | 全市场 | 付费 | 高 | 专业个人/小型机构 |
选择数据源的黄金法则
对于本书的读者,我们建议的学习路径是:
- 入门阶段:使用 yfinance(美股)或 AKShare(A股)。免费、易用、社区活跃,遇到问题容易找到答案
- 进阶阶段:升级到 Tushare 的积分版。数据更稳定、字段更丰富、支持更多高级接口
- 专业阶段:考虑 Wind 或 Choice。当你管理较大资金或需要实时数据时,付费数据源的可靠性值得投资
AI辅助的数据获取
无论你选择哪个数据源,都可以让AI帮你生成获取代码。以下是向AI询问数据获取的标准Prompt模板:
请帮我写一段Python代码,使用[数据源名称]获取[标的名称]
从[开始日期]到[结束日期]的[日线/分钟线]数据。
要求:
1. 数据包含开、高、低、收、成交量五个字段
2. 数据要进行前复权处理
3. 保存为CSV文件,文件名为[文件名]
4. 添加错误处理机制,如果获取失败打印友好提示
5. 代码要有注释,说明每一步在做什么
数据清洗:量化交易中最被低估的技能
获取数据只是第一步。原始数据往往充满问题:缺失值、异常值、格式不统一、时间戳错位……如果不处理这些问题,你的策略将建立在沙地上。
常见问题1:缺失值
缺失值是最常见的问题。原因包括:节假日休市、停牌、数据接口暂时故障等。处理方式取决于缺失值的性质:
- 交易日缺失:用前一个交易日的收盘价填充(前向填充),或者直接删除该日期
- 个别字段缺失:如果是开盘价缺失,可以用前一个收盘价近似;如果是成交量缺失,可以用近期平均成交量估计
- 大量连续缺失:如果某只股票连续多日没有数据,可能意味着停牌或退市。这种情况下,最好将该股票从分析中排除
常见问题2:异常值
异常值是指明显偏离正常范围的数据点。比如某只股票某天涨幅达到50%(没有涨停限制的市场),或者成交量突然是平时的100倍。
异常值可能是真实的(如重大利好/利空),也可能是数据错误(如小数点错位)。判断方法:
- 对比多个数据源:如果只有某个数据源显示异常,很可能是数据错误
- 查看当日新闻:如果有重大事件,异常可能是真实的
- 统计方法:用3-sigma原则(偏离均值超过3倍标准差)自动标记潜在异常值
常见问题3:复权处理
这是A股量化中最容易出错的地方。当股票分红、送股、配股时,交易所会调整股价,使其在除权除息日出现"跳空"。如果不处理这种跳空,策略会错误地认为股价真的大跌了。
有两种复权方式:
- 前复权(Forward Adjusted):以当前价格为基准,调整历史价格。优点是近期价格真实,适合回测
- 后复权(Backward Adjusted):以上市价格为基准,调整后续价格。优点是历史价格真实,适合计算总收益率
对于策略回测,强烈建议使用前复权数据。否则,策略会在每次除权后产生错误的交易信号。
前向填充/删除] B -->|否| D{是否有异常?} C --> D D -->|是| E[异常值检测
3-sigma/对比验证] D -->|否| F{是否已复权?} E --> F F -->|否| G[复权处理
前复权/后复权] F -->|是| H[清洗完成] G --> H style A fill:#0a0e1a,stroke:#f97316,color:#e8ecf4 style H fill:#0a0e1a,stroke:#06b6d4,color:#e8ecf4 style B fill:#1e293b,stroke:#7b8ba3,color:#c8cfe0 style D fill:#1e293b,stroke:#7b8ba3,color:#c8cfe0 style F fill:#1e293b,stroke:#7b8ba3,color:#c8cfe0 style C fill:#1e293b,stroke:#7b8ba3,color:#c8cfe0 style E fill:#1e293b,stroke:#7b8ba3,color:#c8cfe0 style G fill:#1e293b,stroke:#7b8ba3,color:#c8cfe0
与AI一起练习:数据获取与清洗实战
练习1:用AKShare获取A股数据
AKShare是目前最活跃的A股免费数据源之一。以下代码展示了如何获取某只A股的日线数据。
import akshare as ak
import pandas as pd
# 获取贵州茅台的日线数据
# akshare的股票代码格式:去掉后缀的纯数字
stock_code = "600519"
stock_name = "贵州茅台"
print(f"正在获取 {stock_name}({stock_code}) 的历史数据...")
# 使用 akshare 获取日线数据
df = ak.stock_zh_a_hist(
symbol=stock_code,
period="daily",
start_date="20230101",
end_date="20241231",
adjust="qfq" # 前复权
)
print(f"\n✅ 数据获取成功!共 {len(df)} 条记录")
print("\n数据预览:")
print(df.head(10))
# 检查数据质量
print("\n📊 数据质量报告:")
print(f"日期范围: {df['日期'].min()} 至 {df['日期'].max()}")
print(f"缺失值统计:\n{df.isnull().sum()}")
print(f"\n价格范围:")
print(f" 最高: {df['最高'].max():.2f}")
print(f" 最低: {df['最低'].min():.2f}")
print(f" 平均成交量: {df['成交量'].mean():.0f}")
# 保存为CSV
df.to_csv(f"{stock_code}_{stock_name}_daily.csv", index=False, encoding='utf-8-sig')
print(f"\n💾 数据已保存至: {stock_code}_{stock_name}_daily.csv")
练习2:数据清洗实战——异常值检测
获取数据后,我们需要对数据进行质量检查。以下代码实现了自动化的数据质量报告。
import numpy as np
def data_quality_report(df):
"""生成数据质量报告"""
report = []
# 1. 检查缺失值
missing = df.isnull().sum()
if missing.sum() > 0:
report.append(f"⚠️ 发现缺失值:\n{missing[missing > 0]}")
else:
report.append("✅ 无缺失值")
# 2. 检查零成交量
zero_volume = df[df['成交量'] == 0]
if len(zero_volume) > 0:
report.append(f"⚠️ 发现 {len(zero_volume)} 天成交量为0(可能停牌)")
# 3. 检查涨跌幅异常(超过20%的涨跌幅需要关注)
df['涨跌幅_pct'] = df['收盘'].pct_change() * 100
extreme_moves = df[abs(df['涨跌幅_pct']) > 20]
if len(extreme_moves) > 0:
report.append(f"⚠️ 发现 {len(extreme_moves)} 天涨跌幅超过20%:")
for _, row in extreme_moves.iterrows():
report.append(f" {row['日期']}: {row['涨跌幅_pct']:.2f}%")
# 4. 检查价格逻辑错误(最高价 < 最低价等)
logic_errors = df[df['最高'] < df['最低']]
if len(logic_errors) > 0:
report.append(f"❌ 发现 {len(logic_errors)} 条逻辑错误记录!")
# 5. 检查开盘价是否在开高低范围内
open_errors = df[(df['开盘'] > df['最高']) | (df['开盘'] < df['最低'])]
if len(open_errors) > 0:
report.append(f"❌ 发现 {len(open_errors)} 条开盘价超出范围!")
# 6. 检查收盘价是否在开高低范围内
close_errors = df[(df['收盘'] > df['最高']) | (df['收盘'] < df['最低'])]
if len(close_errors) > 0:
report.append(f"❌ 发现 {len(close_errors)} 条收盘价超出范围!")
# 7. 统计摘要
report.append(f"\n📈 数据统计摘要:")
report.append(f" 平均日收益率: {df['涨跌幅_pct'].mean():.3f}%")
report.append(f" 收益率标准差: {df['涨跌幅_pct'].std():.3f}%")
report.append(f" 最大单日涨幅: {df['涨跌幅_pct'].max():.2f}%")
report.append(f" 最大单日跌幅: {df['涨跌幅_pct'].min():.2f}%")
return "\n".join(report)
# 运行质量检查
print(data_quality_report(df))
练习3:让AI帮你调试数据问题
当你遇到数据问题时,最有效的求助方式就是直接把问题描述和数据样本发给AI。
我在获取股票数据时遇到了以下问题:
[描述你的问题,比如"某天的收盘价高于最高价"]
以下是我的数据样本:
[粘贴出问题的几行数据]
以下是我的代码:
[粘贴相关代码]
请帮我:
1. 分析问题的原因
2. 给出修复代码
3. 建议如何预防类似问题
这个Prompt模板几乎可以解决80%的数据问题。关键是提供足够的信息:问题描述、数据样本、相关代码。信息越完整,AI的诊断越准确。
动手实践:建立你的数据工作流
现在,轮到你了。请完成以下任务,建立属于你自己的数据获取和清洗工作流。
- 选择数据源:根据你关注的股票市场,选择合适的数据源(A股推荐AKShare,美股推荐yfinance)
- 获取数据:下载3只你感兴趣的股票最近两年的日线数据
- 运行质量检查:使用上面的data_quality_report函数检查数据质量
- 处理缺失值:如果发现缺失值,用合适的方法处理(前向填充或删除)
- 验证复权:检查数据中是否包含分红除权事件,确认复权处理是否正确
- 保存清洗后的数据:将清洗后的数据保存为CSV,作为后续策略开发的"原料"
建立标准化的数据工作流,是专业量化交易者和业余玩家的重要区别。每次获取数据都按同样的流程检查,可以避免90%的数据问题。
思考题
- 找一只你持有的股票,下载它过去一年的日线数据,运行数据质量检查。你发现了什么问题?
- 对比前复权和后复权的数据,计算同一时间段的总收益率。两种复权方式的结果是否相同?为什么?
- 思考:如果你用未复权的数据回测一个"跌破20日均线卖出"的策略,除权事件会对策略产生什么影响?