1. 这节究竟解决什么实际问题?¶
2.1 数据获取 的具体任务是:比较固定样本与在线接口,建立可追溯的数据来源记录。 本节将明确输入数据、计算过程、输出及其在后续研究中的用途。
2. 前置知识¶
掌握 DataFrame 的行、列、索引与缺失值;建议先完成收益率一节。 对公式的理解应包括变量定义、单位、时间索引和适用条件。
3. 数据说明¶
默认使用固定 CSV;在线接口只用于扩展。必须记录来源、下载日、字段定义、时区和复权方式。 运行前先检查 shape、日期范围、列名与缺失值。数据看起来能画图,不代表它适合回答研究问题。
4. 学习目标与完成标准¶
完成本节后,你应能用自己的话解释 2.1 数据获取 的输入、计算步骤、输出和局限;尤其能说明:比较固定样本与在线接口,建立可追溯的数据来源记录。 你还应能独立改动一个参数并解释结果为什么变化,指出至少一个会让结论失真的假设。
5. 核心概念与公式¶
原始数据需要先完成字段核验、缺失值处理、异常检查和来源记录,随后才能用于特征计算。
特征只由时点 t 及以前的信息计算;目标值可以在未来,但绝不能混入特征列。 看到公式时依次确认:每个符号的单位是什么?它属于哪个时点?分母会不会为零?它是描述性统计量、预测量,还是交易规则?
6. 从零实现¶
原始代码按照“准备输入 → 计算中间量 → 得到结果 → 解释输出”的顺序组织。每个单元应检查变量类型、索引、维度和代表性数值。中间变量用于核对公式实现和定位计算错误。
7. 成熟库实现¶
Pandas 负责加载、对齐和清洗;数据源库只提供原料,不保证研究可用性。 使用库函数前先阅读参数含义、默认值和返回对象;库输出一个数字,并不说明这个数字适合你的交易假设。
8. 两种实现的差异¶
从零实现便于核对公式、调试和识别隐含假设;成熟库适合在计算逻辑已验证后复用。两种实现依赖相同的数据口径与时点假设,使用库函数不会改变输入数据的质量。
9. 预期结果与检查范围¶
你应看到维度正确的 Series/DataFrame、日期单调递增的索引,以及能支持“比较固定样本与在线接口,建立可追溯的数据来源记录。”这一任务的表格或图形。金融日收益率均值通常接近 0;若出现极大的连续收益、空表、全 NaN 或索引错位,应先排查数据和对齐逻辑,而不是立即相信结果。
10. 常见错误与诊断¶
把日期当字符串排序、重复索引、错误前向填充、混用 Close 与 Adj Close、把未来标签泄漏进特征。
诊断顺序:先打印类型与 shape,再看 head()/tail(),接着检查日期和 NaN,最后才比较数值。这样能避免在错误的对象上反复改参数。
11. 研究偏差检查¶
数据修订、退市股票缺失和接口字段变化都会制造看不见的幸存者偏差或未来函数。 结果至少应与一个简单基线比较,并明确哪些部分只是教学演示。
12. 三层练习与答案¶
- 基础练习:验证一个中间变量、补全一段小计算,并用一句话解释输出。
- 研究练习:改变窗口、样本区间、约束或标的,记录哪些结论仍成立。
- 开放项目:把本节方法嵌入模块项目,报告基线、失败结果和限制。
题目在 ../exercises/02_data/01_data_sources_exercises.ipynb;完整推导、可运行参考实现与结果检查在 ../solutions/02_data/01_data_sources_solutions.ipynb。请先独立作答,再打开答案。
13. 延伸阅读¶
延伸材料:pandas 用户指南。阅读记录应包括研究假设、数据频率、样本范围和结论适用条件。
14. 下一节¶
下一节是 02_data_cleaning。本节的概念会成为理解它的输入,而不是孤立的名词。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import yfinance as yf
from pathlib import Path
# 数据存储路径
DATA_DIR = Path('../datasets')
DATA_DIR.mkdir(exist_ok=True)
print('准备就绪 ')
准备就绪
# 默认使用固定样本,保证课程与 CI 不依赖实时接口。
USE_OFFLINE_DATA = True
if USE_OFFLINE_DATA:
from pathlib import Path
import sys
docs_root = next(parent for parent in [Path.cwd(), *Path.cwd().parents]
if (parent / 'utils' / 'offline_data.py').exists())
sys.path.insert(0, str(docs_root))
from utils.offline_data import configure_yfinance_offline
configure_yfinance_offline(yf)
else:
print('在线下载为扩展步骤;请核对数据字段、时区和下载日期。')
import matplotlib.pyplot as plt
# 1. 设置系统自带的中文字体(这里使用黑体 SimHei)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 如果你想用微软雅黑,可以改成 ['Microsoft YaHei']
# 2. 解决更换字体后,负号(-)显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
1. yfinance — 美股数据¶
# ===== 单只股票 =====
ticker = yf.Ticker('AAPL')
# 日线历史数据
hist = ticker.history(start='2020-01-01', end='2024-01-01')
print('AAPL 历史数据字段:', hist.columns.tolist())
print(f'数据量: {len(hist)} 行')
hist.tail(3)
AAPL 历史数据字段: ['Open', 'High', 'Low', 'Close', 'Volume', 'Dividends', 'Stock Splits'] 数据量: 1006 行
| Open | High | Low | Close | Volume | Dividends | Stock Splits | |
|---|---|---|---|---|---|---|---|
| Date | |||||||
| 2023-12-27 00:00:00-05:00 | 190.510890 | 191.510500 | 189.125276 | 191.164093 | 48087700 | 0.0 | 0.0 |
| 2023-12-28 00:00:00-05:00 | 192.143900 | 192.658558 | 191.183873 | 191.589661 | 34049900 | 0.0 | 0.0 |
| 2023-12-29 00:00:00-05:00 | 191.906385 | 192.401245 | 189.758698 | 190.550476 | 42672100 | 0.0 | 0.0 |
# ===== 字段说明 =====
field_desc = {
'Open': '开盘价 — 当日第一笔成交价',
'High': '最高价 — 当日最高成交价',
'Low': '最低价 — 当日最低成交价',
'Close': '收盘价 — 当日最后成交价(原始)',
'Volume':'成交量 — 当日总成交股数',
'Dividends': '分红 — 当日每股分红金额',
'Stock Splits': '股票拆分倍数(2=一股变两股)'
}
for k, v in field_desc.items():
if k in hist.columns:
print(f' {k:<15}: {v}')
Open : 开盘价 — 当日第一笔成交价 High : 最高价 — 当日最高成交价 Low : 最低价 — 当日最低成交价 Close : 收盘价 — 当日最后成交价(原始) Volume : 成交量 — 当日总成交股数 Dividends : 分红 — 当日每股分红金额 Stock Splits : 股票拆分倍数(2=一股变两股)
# ===== 多只股票批量下载 =====
tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'SPY']
prices = yf.download(tickers, start='2020-01-01', end='2024-01-01',
progress=False)['Close']
# 标准化为基准100(便于比较)
normalized = prices / prices.iloc[0] * 100
fig, ax = plt.subplots(figsize=(12, 5))
for col in normalized.columns:
ax.plot(normalized.index, normalized[col], label=col, linewidth=1.3)
ax.axhline(100, color='gray', linestyle='--', alpha=0.5)
ax.set_title('科技股 vs SPY 标准化收益对比 (基准=100)', fontsize=13)
ax.set_ylabel('标准化价格')
ax.legend()
ax.grid(alpha=0.3)
plt.tight_layout()
plt.show()
# 保存到 datasets
prices.to_csv(DATA_DIR / 'sp500_sample.csv')
print(f'已保存至 {DATA_DIR / "sp500_sample.csv"}')
已保存至 ..\datasets\sp500_sample.csv
2. A 股数据获取方案¶
对于 A 股,我们推荐两个优秀的数据源:
- adata:免费开源,专注 A 股,采用多数据源融合,接口设计非常人性化。
- AKShare:提供多类公开金融数据接口;字段、频率和可用性以当前接口文档为准。
2.1 使用 adata 获取行情¶
try:
import adata
# 1. 获取所有股票代码简表
all_stocks = adata.stock.info.all_code()
print(f'当前 A 股共有 {len(all_stocks)} 只股票/指数')
# 2. 获取单只股票行情(以贵州茅台为例)
# k_type: 1.日;2.周;3.月
# adjust: 0.不复权;1.前复权;2.后复权
mt_df = adata.stock.market.get_market(stock_code='600519', start_date='2022-01-01', k_type=1, adjust=1)
print('adata 茅台数据样例:')
mt_df['trade_date'] = pd.to_datetime(mt_df['trade_date'])
mt_df.set_index('trade_date', inplace=True)
# 保存
mt_df[['open', 'high', 'low', 'close', 'volume']].to_csv(DATA_DIR / 'adata_mt.csv')
print(f'已保存 {len(mt_df)} 条数据至 adata_mt.csv')
mt_df.head()
except ImportError:
print(' adata 未安装,请运行: pip install adata')
except Exception as e:
print(f'获取数据出错: {e}')
adata 未安装,请运行: pip install adata
2.2 使用 AKShare (可选备选)¶
try:
import akshare as ak
# 获取贵州茅台(600519)日线数据
mao_tai = ak.stock_zh_a_hist(
symbol='600519',
period='daily',
start_date='20220101',
end_date='20240101',
adjust='qfq' # 前复权
)
mao_tai = mao_tai.rename(columns={
'日期': 'Date', '开盘': 'Open', '最高': 'High',
'最低': 'Low', '收盘': 'Close', '成交量': 'Volume'
})
mao_tai['Date'] = pd.to_datetime(mao_tai['Date'])
mao_tai = mao_tai.set_index('Date')
# 保存
mao_tai[['Open', 'High', 'Low', 'Close', 'Volume']].to_csv(DATA_DIR / 'stock_daily.csv')
print(f'已保存 {len(mao_tai)} 条 AKShare 数据')
mao_tai.tail(3)
except ImportError:
print(' akshare 未安装')
except Exception as e:
print(f'AKShare 获取失败: {e}')
akshare 未安装
3. 宏观数据 — 市场的“背景板”¶
为什么量化也得看宏观?
QuantEcon 强调经济环境对金融资产的底层约束。例如,当 10 年期美债收益率 飙升时,科技股往往会承压,因为资金的折现成本变高了。
我们可以运行如下代码(使用 yfinance)获取关键宏观代理指标:
^TNX: CBOE 10 Year Treasury Note Yield(10年期美债收益率)^VIX: CBOE Volatility Index(恐慌指数)GC=F: Gold Futures(黄金期货,反映避险情绪)
# 获取 10 年期美债收益率和 VIX 数据
macro_data = yf.download(['^TNX', '^VIX'], start='2020-01-01', end='2024-01-01', progress=False)['Close']
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), sharex=True)
ax1.plot(macro_data.index, macro_data['^TNX'], color='darkblue', label='10 Year Treasury Yield (%)')
ax1.set_title("无风险利率走势 (10Y Yield)")
ax1.legend()
ax1.grid(alpha=0.3)
ax2.plot(macro_data.index, macro_data['^VIX'], color='darkred', label='VIX (Market Fear)')
ax2.set_title("市场恐慌程度 (VIX)")
ax2.legend()
ax2.grid(alpha=0.3)
plt.tight_layout()
plt.show()
print("宏观数据读取完成;后续可将这些变量用于条件分析或状态过滤。")
宏观数据读取完成;后续可将这些变量用于条件分析或状态过滤。
练习¶
- 下载你感兴趣的 5 只股票,绘制标准化收益对比图。
- 使用
adata或者yfinance获取单只股票数据并保存为 CSV。 - 获取黄金期货(
GC=F)数据,观察它在 2020 年市场大恐慌期间的表现。
下一节 → 02_data_cleaning.ipynb