交易明细审计:从逐笔记录里挖回测的破绽
我的掌握程度:
先掌握这个答案
六项检查:①成交价合理性:每笔价格应在当日高低价区间内、非停牌日;②约束执行:跌停日无卖出、涨停日无买入、T+1满足(买入次日前的卖出不该出现);③持仓守恒:每日持仓=昨日持仓+今日交易(对账恒等式——幽灵持仓=明细加总对不上持仓表);④换手结构:单日换手分布(>50%的天数=异常)、买卖对称性;⑤盈亏分布:单笔盈亏的偏度与极端值(单笔>20%的可疑性——涨跌停制度下不该出现);⑥时间戳:信号时间与成交时间的一致(q-063的前视在明细里的显形)。三类经典破绽:幽灵持仓(守恒被破)、完美逃顶(卖在最高价的系统性偏差——用了收盘价成交假设的痕迹)、复利幻觉(收益计算用了加法)。
先读结论,再核对下方使用边界;数字例题为教学情景,涉及实际行情与规则时请核对原始来源。
从理解到使用
每一笔收益都要有可执行时间线
打印数据截止时间、信号时间、订单时间、成交时间和持仓收益区间。收盘信号不能乘当天完整收益;次日开盘成交也不能无条件拿到整个close-to-close收益。用原始价格判断可成交性,并纳入停牌与涨跌停。
本阶段实战方法 · 回测先过时序、成交与成本三道关
把信号变成策略收益时,每笔收益必须能追溯到当时能下出的订单。
- 画出时间线:数据可得→算信号→下单→成交→持仓收益;收盘信号不能倒用当天完整收益。
- 明确交易价格、T+1可卖数量、停牌涨跌停、仓位上限和实际成交量约束。
- 同时报告毛/净收益、换手、最大回撤、基准和样本外结果;对滑点与费率做压力测试。
使用边界
- 简单shift(1)只解决部分时序问题;次日开盘成交还要区分隔夜和日内收益。
- 成本应按成交金额与买卖方向计提;0.15%是教学情景,不是统一市场费率。
背景 · 从一个困惑说起
q-503把交易明细列为"最诚实的文件";这一题是它的深度使用手册——逐笔审计的具体技术。多数回测的破绽不藏在指标里、藏在明细的缝隙里:不可能的价格、失踪的约束、幽灵般的持仓。
由此产生的问题
逐笔审计的六项检查(成交价合理性/约束执行/持仓守恒/换手结构/盈亏分布/时间戳)各怎么做?"幽灵持仓"等三类经典破绽的特征?
核心概念
六项检查:①成交价合理性:每笔价格应在当日高低价区间内、非停牌日;②约束执行:跌停日无卖出、涨停日无买入、T+1满足(买入次日前的卖出不该出现);③持仓守恒:每日持仓=昨日持仓+今日交易(对账恒等式——幽灵持仓=明细加总对不上持仓表);④换手结构:单日换手分布(>50%的天数=异常)、买卖对称性;⑤盈亏分布:单笔盈亏的偏度与极端值(单笔>20%的可疑性——涨跌停制度下不该出现);⑥时间戳:信号时间与成交时间的一致(q-063的前视在明细里的显形)。三类经典破绽:幽灵持仓(守恒被破)、完美逃顶(卖在最高价的系统性偏差——用了收盘价成交假设的痕迹)、复利幻觉(收益计算用了加法)。
为什么需要它
①明细是回测的"原始发票":指标是报表、明细是凭证——审计从凭证开始;②守恒检查的威力:一条恒等式(持仓=累积交易)能抓住引擎类的系统性bug——这是成本最低收益最高的单点检查;③"完美逃顶"的统计识别:卖出价在当日高低位分的分布应均匀——若系统性偏高(平均70分位以上),成交假设在作弊。
实际应用场景
审计函数六件套写成check_trades(trades, holdings)——每次回测自动跑、异常打印行号;发现破绽的处置:定位引擎对应模块→修→重跑并记录("破绽-修复"对照进项目文档)。
与之前知识的联系
q-503(五件套)第二件的展开;②回扣q-042/044(约束们);⑥回扣q-063(前视);守恒检查是自写引擎(q-504)的标准单元测试。
⚠ 常见误解
- 引擎跑通就不用审计:跑通=语法对;守恒与合理性=语义对——两回事
- 抽查几笔就行:守恒类bug是系统性的(每天错一点)——全量恒等式检查一分钟跑完,没理由抽查
- 明细审计是上线前的事:研究期每次回测都跑——破绽越早发现,浪费的迭代越少
🛠 本题实践任务
为你的引擎写audit_trades(至少实现守恒+价格区间两项),在最新回测上跑一遍;如果全绿,人为注入一个bug(改一行)验证审计器能抓到。
💡 提示(先自己想5分钟再看)
注入测试是审计器的单元测试——抓不到注入bug的审计器不可信。
✅ 验收标准
一个能自证的审计器+一次全量审计记录。你的回测有了内审部门。
在你的浏览器内运行。首次需联网下载 Python;支持标准库和 Pyodide 提供的 NumPy、pandas 等包。行情抓取、系统文件访问和部分第三方库请在本地环境运行。输出自检仅比对结果关键词,不代替逻辑审查。
Python · 独立工作线程 · 执行限时10秒
🤖 AI导师
导师会先检查你的前置知识(五件套阅读、成交约束),再用案例、公式和代码帮助理解。此功能需要先启用服务。