在金融数据分析领域,可转债因其股债双重属性成为量化研究的重要标的。而分时数据作为最细粒度的市场微观结构反映,其采集质量直接影响策略回测的准确性。本文将基于AkShare工具链,分享如何构建一个健壮、可维护的沪深可转债分时数据采集系统。
许多初学者习惯将数据采集写成线性脚本,但随着需求复杂化,这种模式会迅速变得难以维护。我们需要用软件工程的思维重构整个流程。
一个完整的数据采集系统应包含以下核心组件:
python复制class BondDataPipeline:
def __init__(self):
self.config = load_config()
self.logger = setup_logger()
def fetch_spot(self):
"""实时行情数据获取"""
def fetch_history(self, symbol, frequency):
"""历史分时数据获取"""
def storage_engine(self, df, metadata):
"""数据存储引擎"""
def monitor(self):
"""任务监控与报警"""
这种架构的优势在于:
将易变参数抽离为独立配置文件:
yaml复制# config.yaml
api:
retry_times: 3
timeout: 10
rate_limit: 0.5 # 请求间隔秒数
storage:
root_path: ./data
structure:
- by_date: YYYY-MM-DD
- by_bond: {bond_code}
通过OmegaConf等工具加载配置,避免硬编码带来的维护成本:
python复制from omegaconf import OmegaConf
conf = OmegaConf.load('config.yaml')
传统按文件存储的方式在长期运维中会暴露诸多问题,我们需要更专业的存储策略。
采用"日期+品种"的混合分区方案:
code复制data/
├── 2023-07-01/
│ ├── 113542.SH.parquet
│ └── 123456.SZ.parquet
├── 2023-07-02/
│ ├── 113542.SH.parquet
│ └── 123456.SZ.parquet
└── metadata.parquet
实现代码示例:
python复制def save_partitioned(df, date, bond_code):
path = f"{conf.storage.root_path}/{date}/{bond_code}.parquet"
df.to_parquet(path, engine='pyarrow')
相比CSV,Parquet格式具有显著优势:
| 特性 | CSV | Parquet |
|---|---|---|
| 读取速度 | 慢 | 快3-5x |
| 存储空间 | 100% | 30-50% |
| 列裁剪 | 不支持 | 支持 |
| 类型推断 | 需要 | 内置 |
转换方法:
python复制df = ak.bond_zh_hs_cov_min(symbol="123456", period="5")
df.to_parquet("data.parquet")
金融数据采集对稳定性要求极高,需要建立完善的容错体系。
基础重试机制容易陷入无效循环,改进方案:
python复制from tenacity import retry, stop_after_attempt, wait_exponential
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=4, max=10)
)
def fetch_with_retry(symbol):
return ak.bond_zh_hs_cov_min(symbol=symbol)
通过检查点机制保存采集状态:
python复制class Checkpoint:
def __init__(self):
self.state_file = "progress.json"
def save(self, bond_code, date):
with open(self.state_file, 'w') as f:
json.dump({"last_code": bond_code, "last_date": date}, f)
def load(self):
try:
with open(self.state_file) as f:
return json.load(f)
except FileNotFoundError:
return None
当需要采集全市场可转债数据时,效率成为关键考量。
使用aiohttp实现异步请求:
python复制import aiohttp
import asyncio
async def fetch_concurrently(symbols):
async with aiohttp.ClientSession() as session:
tasks = []
for symbol in symbols:
task = asyncio.create_task(
fetch_one(session, symbol))
tasks.append(task)
return await asyncio.gather(*tasks)
对于大规模数据,可采用分块处理:
python复制chunk_size = 100 # 每批处理100只转债
for i in range(0, len(code_list), chunk_size):
chunk = code_list[i:i + chunk_size]
process_chunk(chunk)
del chunk # 显式释放内存
实际项目中,这套系统每天可稳定采集全市场400+可转债的1分钟级数据,存储占用控制在2GB/月以内,完全满足量化研究的需要。关键在于建立规范化的工程标准,而非追求单次脚本的快速实现。