金融数据获取 (Financial Data APIs)


章节概述

没有数据就没有量化分析。本章介绍两个核心金融数据获取库:akshare(中国 A 股/基金/期货)和 yfinance(国际市场)。你将学会获取历史 OHLCV 行情、实时行情、基本面数据,以及如何将不同来源的数据标准化为统一的 DataFrame 格式。数据的”形状一致性”是所有量化策略的基石。

核心理念:量化分析的第一步永远是”数据工程”——获取、清洗、对齐、存储。用 C 程序员的思维来理解:数据就位(struct 对齐)后,才能开始计算。Python 的生态优势在于有现成的 API 库帮你完成数据获取,你只需要调用几个函数,就能得到结构化的 DataFrame。但要注意:每个数据源的格式都略有不同,标准化是必须做的工作。


第一节:akshare —— A 股数据的一站式入口

1.1 安装与基础架构

pip install akshare --upgrade

akshare 提供 500+ 个数据接口,覆盖股票、基金、期货、债券、宏观数据等。所有接口返回 Pandas DataFrame。

1.2 获取 A 股历史行情(OHLCV)

python -c "
import akshare as ak
 
# 获取平安银行(000001)的历史日线数据
df = ak.stock_zh_a_hist(
 symbol='000001',
 period='daily',
 start_date='20240101',
 end_date='20241231',
 adjust='qfq' # 前复权
)
print('Columns:', list(df.columns))
print('Shape:', df.shape)
print(df.head())
print()
print('dtypes:')
print(df.dtypes)
"

关键参数说明:

参数含义可选值
symbol股票代码6 位数字字符串,如 '000001'
period数据周期daily, weekly, monthly
start_date起始日期'YYYYMMDD' 格式
end_date结束日期'YYYYMMDD' 格式
adjust复权方式''(不复权)、'qfq'(前复权)、'hfq'(后复权)

1.3 获取实时行情

python -c "
import akshare as ak
 
# A 股实时行情(全部股票)
# df = ak.stock_zh_a_spot_em() # 东方财富源
# print(df.columns)
# print(df[['代码', '名称', '最新价', '涨跌幅', '成交量', '成交额']].head())
 
# 特定股票的实时数据
df = ak.stock_zh_a_hist(symbol='000001', period='daily', start_date='20260801', end_date='20260807')
print(df.tail())
"

1.4 获取基金与指数数据

python -c "
import akshare as ak
 
# 获取沪深300指数成分股
# df = ak.index_stock_cons(symbol='000300')
# print(df.head())
 
# 获取指数历史数据
# df = ak.stock_zh_index_daily(symbol='sh000300')
# print(df.tail())
 
# 获取基金历史净值
# df = ak.fund_open_fund_info_em(symbol='000001', indicator='单位净值走势')
# print(df.head())
print('akshare provides 500+ data interfaces for Chinese markets')
"

1.5 财报数据获取

python -c "
import akshare as ak
 
# 获取某股票的资产负债表
# df = ak.stock_financial_balance_sheet_by_report_em(symbol='000001')
# print(df.columns)
 
# 获取利润表
# df = ak.stock_financial_profit_sheet_by_report_em(symbol='000001')
# print(df.columns)
print('Financial statements available via akshare EM interfaces')
"

第二节:yfinance —— 国际市场数据

2.1 安装与基础用法

pip install yfinance --upgrade

yfinance 通过 Yahoo Finance API 获取全球市场的股票、ETF、指数、外汇、加密货币数据。

python -c "
import yfinance as yf
 
# 下载苹果公司历史数据
aapl = yf.download('AAPL', start='2024-01-01', end='2024-12-31')
print('Columns:', list(aapl.columns))
print('Shape:', aapl.shape)
print(aapl.tail())
"

2.2 多股票同时下载

python -c "
import yfinance as yf
 
# 同时下载多只股票
tickers = ['AAPL', 'GOOGL', 'MSFT', 'TSLA']
data = yf.download(tickers, start='2024-01-01', end='2024-06-30')
print('Shape:', data.shape) # Multi-level columns
print('Column levels:', data.columns.names)
print()
# 只看收盘价
print('Close prices:')
print(data['Close'].tail())
"

2.3 获取基本面数据

python -c "
import yfinance as yf
 
msft = yf.Ticker('MSFT')
 
# 基本信息
info = msft.info
print('Market Cap:', info.get('marketCap'))
print('PE Ratio:', info.get('trailingPE'))
print('Sector:', info.get('sector'))
 
# 获取财务报表
# balance_sheet = msft.balance_sheet
# income_stmt = msft.financials
# cashflow = msft.cashflow
"

2.4 获取分红和拆股数据

python -c "
import yfinance as yf
 
aapl = yf.Ticker('AAPL')
dividends = aapl.dividends
splits = aapl.splits
 
print('Recent dividends:')
print(dividends.tail())
print()
print('Stock splits:')
print(splits)
"

第三节:数据标准化 —— 统一不同数据源的格式

3.1 为什么需要标准化

不同数据源的列名、日期格式、复权方式各有不同。写策略之前必须将所有输入标准化为统一的格式:

python -c "
import pandas as pd
import numpy as np
 
# 定义标准 OHLCV 列名映射
COLUMN_MAP = {
 'open': ['open', 'Open', 'OPEN', '开盘'],
 'high': ['high', 'High', 'HIGH', '最高'],
 'low': ['low', 'Low', 'LOW', '最低'],
 'close': ['close', 'Close', 'CLOSE', '收盘'],
 'volume': ['volume', 'Volume', 'VOLUME', '成交量'],
 'date': ['date', 'Date', '日期', 'trade_date'],
 'symbol': ['symbol', '代码', '股票代码'],
 'adj_factor': ['adj_factor', '复权因子'],
}
 
def standardize_columns(df, symbol=None):
 # 列名小写化
 df.columns = [c.lower().strip() for c in df.columns]
 # 映射到标准列名
 rename = {}
 for std_name, aliases in COLUMN_MAP.items():
 for alias in aliases:
 if alias.lower() in df.columns:
 rename[alias.lower()] = std_name
 df = df.rename(columns=rename)
 if 'symbol' not in df.columns and symbol:
 df['symbol'] = symbol
 return df
 
print('Column standardization function defined')
"

3.2 日期格式统一化

python -c "
import pandas as pd
 
# 不同数据源的日期格式
samples = ['2024-01-15', '20240115', '2024/01/15', '2024年01月15日']
 
for s in samples:
 try:
 dt = pd.to_datetime(s)
 print(f'{s:20s} -> {dt.strftime(\"%Y-%m-%d\")}')
 except Exception as e:
 print(f'{s:20s} -> ERROR: {e}')
"

3.3 复权处理标准化

python -c "
import numpy as np
import pandas as pd
 
# 如果数据源只提供不复权价格,手动计算复权
# 复权因子通常是累计的
def apply_adjustment(df):
 '''
 使用复权因子将不复权价格转换为前复权价格
 '''
 if 'adj_factor' not in df.columns:
 # 若无复权因子,以后复权因子为 1.0
 df['adj_factor'] = 1.0
 
 # 前复权:当前价格不变,历史价格按累计因子调整
 last_factor = df['adj_factor'].iloc[-1]
 for col in ['open', 'high', 'low', 'close']:
 if col in df.columns:
 df[col] = df[col] * df['adj_factor'] / last_factor
 return df
 
print('Adjustment function defined')
print('Forward adjustment: historical prices adjusted down, latest unchanged')
"

3.4 数据完整性检查

python -c "
import pandas as pd
import numpy as np
 
def validate_ohlcv(df):
 checks = {}
 
 # 基本检查
 checks['has_columns'] = all(
 c in df.columns for c in ['open', 'high', 'low', 'close', 'volume']
 )
 checks['no_nan'] = df[['open', 'high', 'low', 'close', 'volume']].notna().all().all()
 checks['high_ge_low'] = (df['high'] >= df['low']).all()
 checks['high_ge_open'] = (df['high'] >= df['open']).all()
 checks['high_ge_close'] = (df['high'] >= df['close']).all()
 checks['low_le_open'] = (df['low'] <= df['open']).all()
 checks['low_le_close'] = (df['low'] <= df['close']).all()
 checks['non_negative'] = (df[['open', 'high', 'low', 'close', 'volume']] >= 0).all().all()
 checks['sorted_by_date'] = df['date'].is_monotonic_increasing
 
 all_passed = all(checks.values())
 for check, result in checks.items():
 status = 'OK' if result else 'FAIL'
 print(f' [{status}] {check}')
 
 return all_passed
 
print('Validation function defined')
"

第四节:数据本地存储 —— HDF5 与 Parquet

4.1 为什么本地存储

量化回测会反复读取历史数据。每次都从网络获取不仅慢,而且不稳定。把数据缓存在本地才是正道。

4.2 HDF5 存储(Pandas 原生支持)

python -c "
import pandas as pd
import numpy as np
import os
 
# 创建示例数据
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=100, freq='B')
df = pd.DataFrame({
 'open': 100 + np.cumsum(np.random.randn(100) * 2),
 'high': 100 + np.cumsum(np.random.randn(100) * 2),
 'low': 100 + np.cumsum(np.random.randn(100) * 2),
 'close': 100 + np.cumsum(np.random.randn(100) * 2),
 'volume': np.random.randint(1e6, 5e6, 100),
}, index=dates)
 
# 写入 HDF5
h5_path = '/tmp/market_data.h5'
df.to_hdf(h5_path, key='AAPL', mode='w')
print(f'Data written to {h5_path}')
 
# 读取
df_loaded = pd.read_hdf(h5_path, key='AAPL')
print(f'Loaded {len(df_loaded)} rows')
 
# 文件大小
print(f'File size: {os.path.getsize(h5_path)} bytes')
"

4.3 Parquet 格式(列式存储,更快)

python -c "
import pandas as pd
import numpy as np
import os
 
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=1000, freq='B')
df = pd.DataFrame({
 'symbol': ['AAPL'] * 1000,
 'date': dates,
 'close': 150 + np.cumsum(np.random.randn(1000) * 2),
 'volume': np.random.randint(1e6, 1e7, 1000),
})
 
parquet_path = '/tmp/market_data.parquet'
df.to_parquet(parquet_path, index=False)
loaded = pd.read_parquet(parquet_path)
print(f'Parquet: {os.path.getsize(parquet_path)} bytes, {len(loaded)} rows')
"

存储格式对比:

格式压缩率读取速度Pandas 支持跨语言支持
CSV
HDF5一般
Parquet极好极快(pyarrow)
Feather极快(pyarrow)


练习

以下题目用于验证本章所学内容:

题号题目链接涉及知识点
本章无对应力扣题请用动手练习题自检