查看全课大纲(第 19 / 38 节)
- 1.课程介绍与学习目标
- 2.量化交易的发展
- 3.主观投资与量化投资的关系
- 4.认识一个量化策略(1)
- 5.认识一个量化策略(2)
- 6.理工生做量化的优势
- 7.量化交易系统的基本架构
- 8.必要的交易基础知识及参考资料
- 9.上节课思考题及本节课内容介绍
- 10.技术指标简介
- 11.基于MACD的交易信号开发(1)
- 12.基于MACD的交易信号开发(2)
- 13.基于RSI的交易信号开发
- 14.基于BOLL的交易信号开发
- 15.基于分形的交易信号开发
- 16.总结及下节课预告
- 17.开发环境安装
- 18.获取指数和个股历史行情数据
- 19.修复行情数据中的问题
- 20.抓取财报数据、数据定时更新
- 21.实现低PE股票池(1)
- 22.实现低PE股票池(2)
- 23.实现低PE策略的回测(1)
- 24.实现低PE策略的回测(2)
- 25.风险控制综述
- 26.风险控制中的止盈止损
- 27.仓位管理的工程实现
- 28.完整策略介绍-基础策略
- 29.策略的分步优化
- 30.策略评价指标(1)
- 31.策略评价指标(2)
- 32.如何评价策略
- 33.如何优化策略
- 34.tick数据的意义
- 35.tick数据的介绍及使用
- 36.Hans123策略
- 37.经典买入三法策略
- 38.量化系统的完整研发和系统搭建
修复行情数据中的问题
约 13 分钟
修复行情数据中的问题:停牌与缺失值治理
小象实战讲义 · 初阶量化交易:策略编写及系统搭建 本讲义仅用于编程与量化方法教学,不构成任何投资建议。
在量化投资的回测与实盘工程体系中,数据质量治理(Data Cleaning & Fixing) 直接决定了回测结果的信度。A 股市场中,上市公司因重大事项重组、财报披露延迟等原因频繁出现停牌(Suspension)。原始财经数据接口(如 Tushare)在股票停牌期间直接不生成当天的记录,导致时间序列发生物理断裂。
若未经过严格的数据修复处理,回测引擎在遍历到停牌日时将无法读取持仓股票的最新价格,导致当日持仓市值被错误归零或触发异常报错,引发资产净值曲线的灾难性失真。本讲系统讲解基于标准交易日历的停牌断点对齐算法、价格前向填充(Forward Fill)规范与 is_trading 状态标记工程。
💡 核心导读
- 停牌数据缺失在回测中的致命危害:
- 净值断崖失真:若组合持有某重仓股,该股停牌一个月,回测引擎若缺失数据将无法评估持仓市值,导致账户净值虚假暴跌,复牌后又虚假暴涨;
- 指标序列截断:滑动窗口函数(如
rolling(20))若遇到日期跳空,会把相隔数月的两根 K 线当成连续交易日计算均线,彻底破坏技术指标的数理意义。
- 标准停牌数据修复规范(Daily Fixing 规则):
- 日历对齐(Calendar Reindex):以大盘指数的标准交易日历为轴,全量补齐个股缺失的日期;
- 价格前向填充(Forward Fill):将停牌日的高、开、低、收(OHLC)四个价格统一设置为停牌前最后一个有效交易日的收盘价(
last_close); - 成交量与状态标识:停牌日成交量显式设为
volume = 0,新增布尔字段is_trading = False(正常交易日为True); - 预热前收价(
pre_close):自动计算上一交易日有效收盘价,方便后续秒级计算当日收益率。
- 双集合同步治理原则:
- 停牌修复必须在不复权集合(
daily)与后复权集合(daily_hfq)中同时执行,确保回测与信号计算两端基准一致。
- 停牌修复必须在不复权集合(
┌─────────────────────────────────────────────────────────────┐
│ 行情数据停牌断点清洗与修复流程 │
└──────────────────────────────┬──────────────────────────────┘
│
┌─────────────────────────────┼─────────────────────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ 1. 交易日历对齐 │ │ 2. 价格前向填充 │ │ 3. 状态与成交置零│
├──────────────────┤ ├──────────────────┤ ├──────────────────┤
│ • 以指数日历为轴 │ │ • OHLC 统一设为 │ │ • volume = 0 │
│ • 扫描个股缺失日 │ │ 停牌前末日收盘 │ │• is_trading=False│
│ • 补齐空缺文档 │ │• 保持持仓市值平滑│ │• 锁定不可交易状态│
└──────────────────┘ └──────────────────┘ └──────────────────┘一、停牌断点对量化系统的冲击机制
在真实交易中,股票停牌期间投资者无法买入或卖出,但持仓市值依然存在,并以停牌前收盘价计算资产总值。
若未做数据补齐,查询结果为 None,若回测系统处理不当直接赋予 0,将产生不可接受的逻辑回撤。
二、Daily Fixing 修复算法与实现逻辑
标准修复脚本 daily_fixing.py 核心执行四步转换:
- 获取全集交易日:从
daily集合中查询index=True的全量有序日期列表; - 扫描个股缺失记录:比对个股实际记录日期与标准日历集合;
- 构造补齐文档:对每个缺失日,继承前一日收盘价,成交量填 0,
is_trading设为False; - 批量更新入库:使用
bulk_write执行批量 Upsert。
⚠️ 数据修复即回测假设:停牌日”前向填充价格、成交量置 0”是一种建模约定而非事实——它隐含”停牌期间按停牌前价格估值、复牌后才能成交”的假设,而真实复牌常伴随大幅跳空,回测必须配合不可成交约束(见 3.7)。本讲演示为内置合成样例;真实停牌与除权数据可通过 Tushare 等接口获取(需注册凭证,规则以官方文档为准)。
📌 合规自查(回测/策略类内容的通用前提):本讲数值来自内置合成数据或人为设定的假设输入,仅用于演示方法,不代表真实交易结果;幸存者偏差(回测样本必须包含当时在市、日后可能退市的全部标的,只在幸存股票上计算会虚增表现);前视偏差(未来函数)(当日收盘后得到的信息只能用于其后的交易决策,财报须按实际披露日对齐);参数过拟合(周期、阈值等参数贴合某段历史不代表样本外有效,须用滚动样本外验证,见 5.4)。
📝 动手练一练
下面我们用 Python 编写一段 标准行情停牌数据清洗与断点修复脚本,演示如何基于标准交易日历自动补齐停牌断点并平滑持仓市值:
import pandas as pd
# 保证宽表在任意终端宽度下都完整打印(不折叠中间列),使输出确定、可复现
pd.set_option("display.width", 250)
pd.set_option("display.max_columns", None)
pd.set_option("display.max_rows", None)
import numpy as np
def fix_suspension_and_missing_data(stock_df, calendar_dates):
"""
行情数据修复管线 (Daily Fixing):
1. 基于标准交易日历对齐补齐停牌断点
2. 停牌日高开低收以前一交易日收盘价 (Forward Fill) 填充
3. 成交量置 0, 标记 is_trading = False
4. 填充 pre_close 字段
"""
stock_df = stock_df.copy()
code = stock_df['code'].iloc[0]
# 1. 重新索引至全量交易日历
fixed_df = stock_df.reindex(calendar_dates)
fixed_df['code'] = code
# 2. 识别真实交易日与停牌日
fixed_df['is_trading'] = ~fixed_df['close'].isna()
# 3. 前向填充收盘价 (停牌期间保持停牌前持仓价值不变)
fixed_df['close'] = fixed_df['close'].ffill()
fixed_df['open'] = fixed_df['open'].fillna(fixed_df['close'])
fixed_df['high'] = fixed_df['high'].fillna(fixed_df['close'])
fixed_df['low'] = fixed_df['low'].fillna(fixed_df['close'])
fixed_df['volume'] = fixed_df['volume'].fillna(0.0)
# 4. 填充上一日有效收盘价 pre_close
fixed_df['pre_close'] = fixed_df['close'].shift(1)
return fixed_df
def test_fixing_run():
# 构造全市场标准 5 天交易日历 (周一至周五)
calendar_dates = pd.date_range("2023-06-01", periods=5, freq="B")
# 模拟某股票在第 3、4 天停牌 (原始数据中直接缺失 6月5日与6月6日)
raw_dates = pd.to_datetime(["2023-06-01", "2023-06-02", "2023-06-07"])
raw_df = pd.DataFrame({
'code': '600000',
'open': [10.0, 10.2, 10.8],
'high': [10.5, 10.6, 11.0],
'low': [9.8, 10.1, 10.5],
'close': [10.2, 10.5, 10.9],
'volume': [10000, 15000, 12000]
}, index=raw_dates)
fixed_res = fix_suspension_and_missing_data(raw_df, calendar_dates)
print("================ 行情数据停牌断点清洗与修复 ================")
print(fixed_res[['code', 'open', 'close', 'volume', 'is_trading', 'pre_close']])
print("----------------------------------------------------------")
suspended_count = (~fixed_res['is_trading']).sum()
print(f"成功识别并补齐停牌天数: {suspended_count} 天 (成交量置 0, 价格平滑 ffill)")
print("==========================================================")
test_fixing_run()
运行结果输出:
================ 行情数据停牌断点清洗与修复 ================
code open close volume is_trading pre_close
2023-06-01 600000 10.0 10.2 10000.0 True NaN
2023-06-02 600000 10.2 10.5 15000.0 True 10.2
2023-06-05 600000 10.5 10.5 0.0 False 10.5
2023-06-06 600000 10.5 10.5 0.0 False 10.5
2023-06-07 600000 10.8 10.9 12000.0 True 10.5
----------------------------------------------------------
成功识别并补齐停牌天数: 2 天 (成交量置 0, 价格平滑 ffill)
==========================================================👉 点击查看解析
该练习演示了行情数据中停牌导致的连续缺失与异常值治理方法,核心在于先识别停牌区间并区分“真实无交易”与“数据缺失”,再通过前向填充或规则插值修复,避免生成虚假的连续价格或成交量信号。
本章小结
- 防范停牌净值失真:停牌数据缺失是导致回测市值断裂的核心诱因,必须以全量交易日历为标尺进行前向填充。
- 规范字段标准:停牌日价格继承前收盘、成交量设为 0、
is_trading明确置为False。 - 确保双端一致性:对
daily与daily_hfq集合统一执行清洗,为后续选股与撮合回测奠定无瑕数据基础。
📋 行动清单 (Action Checklist)
- 运行本讲停牌修复代码,观察 6 月 5 日与 6 月 6 日被自动补齐并标记为
is_trading=False; - 在本地 MongoDB 中验证修复逻辑,确保清洗后无断点跳空;
- 预习下一讲,学习如何抓取东方财富财务报表数据并实现定时增量更新。
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。