金融数据获取一直是量化交易和数据分析的基础环节。对于期权交易者而言,深交所、上交所和中金所的数据格式各异,处理起来往往费时费力。本文将带你用Python的akshare和pandas库,构建一个高效、可复用的数据获取管道,5分钟内完成三大交易所期权数据的本地化存储。
在开始编码前,我们需要明确各交易所数据的特点:
安装所需库:
bash复制pip install akshare pandas requests openpyxl lxml
提示:建议使用Python 3.8+环境,某些库在新版本中可能有兼容性问题
三大交易所API对比:
| 交易所 | 数据格式 | 编码 | 主要挑战 |
|---|---|---|---|
| 深交所 | xlsx | UTF-8 | 直接读取乱码 |
| 上交所 | csv | GBK | 需要处理流式响应 |
| 中金所 | xml | UTF-8 | 复杂节点解析 |
使用akshare获取交易日历是第一步,这能确保我们只请求有效交易日的数据:
python复制import akshare as ak
import pandas as pd
def get_trade_days(start_date='2019-01-01'):
"""获取从指定日期开始的交易日历"""
trade_dates = ak.tool_trade_date_hist_sina()
return trade_dates[trade_dates['trade_date'] >= start_date]
深交所数据最常见的痛点是xlsx乱码问题。以下是经过优化的解决方案:
python复制def fetch_szse_data(date):
"""获取深交所期权数据并解决乱码问题"""
url = f'http://www.szse.cn/api/report/ShowReport?SHOWTYPE=xlsx&CATALOGID=option_hyfxzb&TABKEY=tab1&txtSearchDate={date}'
response = requests.get(url)
# 使用BytesIO避免临时文件
with BytesIO(response.content) as bio:
# 解决编码问题的关键参数
df = pd.read_excel(bio, engine='openpyxl')
df['trade_date'] = date # 添加交易日期列
return df
注意:engine='openpyxl'参数是解决乱码的关键,比直接保存为文件再读取效率更高
上交所数据量可能较大,采用流式处理更高效:
python复制def fetch_sse_data(date):
"""流式处理上交所期权数据"""
date_str = pd.to_datetime(date).strftime('%Y%m%d')
url = f'http://query.sse.com.cn/derivative/downloadRisk.do?trade_date={date_str}&productType=0'
headers = {'Referer': 'http://www.sse.com.cn/'} # 必须的请求头
response = requests.get(url, headers=headers, stream=True)
# 使用生成器逐行处理
lines = (line.decode('gbk') for line in response.iter_lines())
return pd.DataFrame([row.split(',') for row in lines if row])
中金所的XML数据结构复杂,使用lxml高效解析:
python复制from lxml import etree
def fetch_cffex_data(date):
"""解析中金所XML格式期权数据"""
ym = pd.to_datetime(date).strftime('%Y%m')
d = pd.to_datetime(date).strftime('%d')
url = f'http://www.cffex.com.cn/sj/hqsj/rtj/{ym}/{d}/index.xml?id=39'
response = requests.get(url)
root = etree.fromstring(response.content)
data = []
for daily in root.xpath('//dailydata'):
record = {child.tag: child.text for child in daily.getchildren()}
data.append(record)
return pd.DataFrame(data)
将不同来源的数据统一格式后存储:
python复制def standardize_data(df, exchange):
"""统一不同交易所的数据格式"""
df['exchange'] = exchange
# 统一日期格式
if 'trade_date' not in df.columns:
df['trade_date'] = pd.to_datetime(df['tradingday'])
return df
def save_to_csv(df, filename):
"""追加模式保存数据,避免重复"""
header = not os.path.exists(filename)
df.to_csv(filename, mode='a', header=header, index=False)
将各模块组合成完整解决方案:
python复制def main(start_date='2019-01-01'):
trade_days = get_trade_days(start_date)
for date in trade_days['trade_date']:
print(f"Processing {date}")
# 并行获取三大交易所数据
szse_data = standardize_data(fetch_szse_data(date), 'SZSE')
sse_data = standardize_data(fetch_sse_data(date), 'SSE')
cffex_data = standardize_data(fetch_cffex_data(date), 'CFFEX')
# 合并保存
combined = pd.concat([szse_data, sse_data, cffex_data], ignore_index=True)
save_to_csv(combined, 'options_data.csv')
对于需要处理大量历史数据的情况,可以考虑以下优化:
python复制# 内存优化示例
dtype_mapping = {
'openprice': 'float32',
'closeprice': 'float32',
'volume': 'int32'
}
def optimize_memory(df):
for col, dtype in dtype_mapping.items():
if col in df.columns:
df[col] = df[col].astype(dtype)
return df
在实际项目中,我发现深交所数据偶尔会有请求限制,简单的解决方法是添加随机延迟:
python复制import random
import time
# 在请求之间添加随机延迟
time.sleep(random.uniform(0.5, 1.5))
对于需要实时监控的场景,可以将上述代码封装为类,并添加定时任务功能。我在一个期权波动率分析项目中,使用APScheduler实现了每15分钟自动更新数据的机制,大大提高了研究效率。