第 17 课 怎样给策略一次没有看过答案的考试¶
研究数据帮助我们提出和修改规则,考试数据只负责检查。只要看过答案又回头调整,考试的独立性就已经改变。
为什么历史数据要分工¶
小岚拥有 2016 年到 2025 年的十年数据。她先在完整十年中比较均线窗口,发现 17 日表现最好;随后又把止损从 8% 调到 7%,因为后者在 2024 年少亏一次。最后,她宣布策略在十年历史上已经得到验证。问题在于,所有年份都参与了规则选择,最终展示的曲线仍是开发过程的一部分,没有一段数据承担独立考试的职责。
研究者需要数据来发现问题、修复代码、比较参数和放弃失败方案。这些活动都会从历史结果中学习。一段数据只要影响过某项决定,就已经成为开发信息。为了估计规则面对未见时期时会怎样,需要提前保留一段不参与开发的数据,等规则冻结以后再打开。
未参与规则拟合和选择、专门用来检查泛化表现的数据常被称为样本外数据。样本内则表示已经参与研究和选择的数据。样本外盈利不等于未来保证,它只是比“在同一段历史上开发并评分”多了一层独立证据。
训练、验证和测试承担三种不同职责¶
机器学习通常把数据分成训练集、验证集和测试集,传统规则策略也能使用同样分工。训练集用于理解数据、拟合模型或形成初版规则。验证集用于比较有限候选、选择参数和检查成本情景。测试集在规则、数据处理与评价指标冻结以后打开,负责最后一次独立检查。
| 数据角色 | 允许做的事情 | 不应做的事情 | 使用以后留下什么 |
|---|---|---|---|
| 训练集 | 探索、拟合、修复、形成候选 | 对最终可靠性作独立证明 | 候选规则与全部尝试记录 |
| 验证集 | 比较有限候选、选参数、做初步压力检查 | 反复扩展搜索直到曲线满意 | 被选版本与选择理由 |
| 测试集 | 冻结后独立评分一次 | 看完继续调同一版本 | 最终检查与是否继续的决定 |
规则策略没有模型训练按钮,也仍然存在训练过程。研究者看过某段价格以后决定采用二十日窗口、加入某个过滤器或删除一类资产,这些决定已经从数据中学习。把“训练集”只理解为神经网络使用的数据,会低估人工研究同样存在的选择。
最简单的时间切分先把早期放前、后期放后¶
十年数据可以先安排成下面的示意。2016 至 2021 年用于形成与拟合,2022 至 2023 年用于选择有限候选,2024 至 2025 年留作最终测试。年份不是通用比例,真实边界取决于样本长度、持有期、市场制度变化和可用交易数量,并且要在查看各段策略成绩以前确定。
| 部分 | 示例时间 | 主要任务 | 是否允许修改规则 |
|---|---|---|---|
| 训练集 | 2016 至 2021 | 数据检查、机制探索、拟合候选 | 允许,并记录全部修改 |
| 验证集 | 2022 至 2023 | 比较少量候选与压力情景 | 允许有限选择,不无限追加搜索 |
| 测试集 | 2024 至 2025 | 冻结以后进行一次独立检查 | 不允许根据结果回头优化 |
若测试失败,研究者可以记录失败并建立新版本。新版本已经使用了 2024 至 2025 年反馈,下一次独立测试需要更晚的新数据或另一个事先没有参与决定的市场。继续在原测试集上优化并不违法,但它的角色已经变成新的开发数据,报告必须如实改变称呼。
为什么金融日期不能随意随机打乱¶
普通表格任务常把所有样本洗牌后随机分组,因为它们希望每组都近似来自同一分布,样本之间也相对独立。金融时间序列具有先后依赖,邻近日期常共享相似价格、持仓和市场状态,制度与参与者结构还会随时间变化。
假设 2020 年三月连续二十个高波动日期被随机分到训练和测试两边。模型在训练时已经看过同一轮冲击的大部分形状,测试中的相邻日期便不再像一段新的市场经历。若 2025 年数据被随机放进训练,程序还可能用后期制度和波动水平帮助解释 2018 年。
时间切分把较早信息放在前面,让后期检查模拟“用过去准备、向未来运行”的真实方向。它不会自动解决所有泄漏,预处理、标签窗口和历史候选池仍要单独审查,但至少保住了最基本的时间箭头。
先画标签窗口,再决定边界¶
假设策略在每个日期使用过去六十日数据生成特征,并用未来五日收益评价信号。特征是决策时能够看到的输入,标签是随后五日才完整的结果。训练集最后一个信号若出现在 2021 年十二月三十一日,它的五日标签会读取 2022 年价格。若验证集从 2022 年一月一日开始,训练标签已经越过边界进入验证时期。
flowchart LR
accTitle: 未来五日标签跨越时间边界
accDescr: 训练末端信号虽然位于训练区间,它的未来五日标签会伸入随后验证区间,因此边界附近样本需要清除,并在两段之间留下隔离距离
history["过去六十日特征"] --> signal["训练末端信号日"]
signal --> f1["未来第 1 日"]
f1 --> f2["第 2 日"]
f2 --> f3["第 3 日"]
f3 --> f4["第 4 日"]
f4 --> f5["第 5 日标签结束"]
boundary["训练与验证边界"] -.穿过.-> f1
处理方法先从时间逻辑出发。删除所有标签区间伸入下一段的训练样本,再在两段之间保留足够空白,让持仓、标签或滚动统计不会共享边界信息。删除重叠训练样本常被称为清除,英文写作 purging;在分段之间留下空白常被称为禁运或隔离,英文写作 embargo。
术语不影响长度选择。隔离至少要覆盖会跨界的未来标签与持有期,若某些预处理还会从边界两侧共享信息,也要进一步调整。过去六十日特征通常不会向未来伸展,它只要求验证开始时已经有足够历史计算输入;未来五日标签才是向右跨越边界的主要来源。
用一张日历手算边界清除¶
为了缩小例子,假设训练集原定到 6 月 30 日,验证集从 7 月 1 日开始,标签使用未来三个交易日。6 月 28 日信号的标签需要 6 月 29 日、6 月 30 日和 7 月 1 日价格,已经读取验证期;6 月 29 日与 6 月 30 日信号跨界更多。它们都不能作为独立训练标签保留。
| 信号日 | 标签需要的未来日期 | 是否进入 7 月验证期 | 训练中怎样处理 |
|---|---|---|---|
| 6 月 27 日 | 6 月 28、29、30 日 | 没有 | 可以保留 |
| 6 月 28 日 | 6 月 29、30 日与 7 月 1 日 | 有 | 清除 |
| 6 月 29 日 | 6 月 30 日与 7 月 1、2 日 | 有 | 清除 |
| 6 月 30 日 | 7 月 1、2、3 日 | 有 | 清除 |
表格说明隔离长度不能凭感觉写一个数字。预测期限、持有期限和订单未完成周期变化时,边界规则也要更新。多资产标签若交易日历不同,还要按各自可交易日期核对。
代码切日期以前先说明输入和预期¶
下面的代码假设 data 已经按照日期建立索引并从早到晚排序。任务只是在固定边界上切出三份表,不负责清除标签重叠。运行后应该检查三个结果的最早与最晚日期,确认训练早于验证、验证早于测试,并且三段没有重复索引。
train = data.loc["2016-01-01":"2021-12-31"].copy()
validation = data.loc["2022-01-01":"2023-12-31"].copy()
test = data.loc["2024-01-01":"2025-12-31"].copy()
print(train.index.min(), train.index.max())
print(validation.index.min(), validation.index.max())
print(test.index.min(), test.index.max())
assert train.index.max() < validation.index.min()
assert validation.index.max() < test.index.min()
loc 按日期标签选择区间,copy 让三份结果成为明确的独立表。两条 assert 检查结束日期严格早于下一段开始日期。它们只能发现日期区间重叠,无法知道标签是否跨界,也无法确认全时期标准化有没有提前发生。边界清除与预处理拟合仍要根据研究卡另外实现和测试。
一次前后切分只观察到一个历史答案¶
假设策略在 2024 至 2025 年表现好,我们仍不知道它在更早的不同起点上能否重复。一次固定切分容易被某个特殊边界影响。为了观察模型在多个历史时点怎样从过去走向下一段未来,可以重复下面的过程。
第一轮用 2016 至 2018 年开发,检查 2019 年;第二轮用 2016 至 2019 年开发,检查 2020 年;第三轮继续把开发终点推进到 2020 年,再检查 2021 年。每一轮都只使用检查期以前的数据完成拟合与预处理,随后保存该轮结果,再把时间向前移动。
这种反复使用过去、检查紧随其后未来的方法叫作滚动前推验证,英文常写作 walk-forward validation。三轮例子里的时间只向前走,后一年的数据从不倒回前一轮训练,因此它比随机交叉验证更接近策略未来定期更新的过程。
flowchart TB
accTitle: 三轮滚动前推验证
accDescr: 第一轮使用早期三年开发并检查下一年,第二轮把开发窗口向前推进再检查随后一年,第三轮继续前推,每轮预处理与模型都只看该轮检查期以前的数据
fold1["第一轮<br/>2016 至 2018 开发<br/>2019 检查"] --> fold2["第二轮<br/>2016 至 2019 开发<br/>2020 检查"]
fold2 --> fold3["第三轮<br/>2016 至 2020 开发<br/>2021 检查"]
若开发区间从 2016 年开始不断扩大,称为扩展窗口。它利用全部已知历史,适合假设关系变化较慢的任务。若每次只保留最近三年,较早数据会随着时间移出,称为滚动窗口。滚动窗口更重视近期结构,也会减少样本数量,使参数更不稳定。两种方式都要根据机制、制度变化和计划中的模型更新频率选择。
每一轮都要重新学习预处理参数¶
标准化、缺失填充、异常阈值、特征选择和模型拟合都会从数据中学习。滚动前推时,它们必须在每一轮开发区间重新计算,再应用到随后检查区间。先在完整历史上标准化一次,然后进行多轮验证,仍然会把后期分布送回早期。
假设第一轮训练收益均值为 0.1%,第二轮扩展数据后均值变为 0.05%。第一轮检查数据必须使用第一轮当时得到的 0.1% 处理,不能事后用第二轮的 0.05% 重算。研究系统应保存每轮训练边界、参数、候选版本与检查结果,让任何一项预测都能追溯到当时可见信息。
滚动结果不能只汇总成一个平均分。某几轮持续失败可能对应市场结构变化,也可能暴露训练窗口太短、成本上升或数据口径改变。把每轮时间、收益、回撤、换手和参数并列展示,比一个总分更能说明模型何时失效。
参数要寻找平缓区域,不追唯一尖点¶
验证集常用于比较有限候选。假设均线窗口从 15 日到 25 日依次测试,只有 19 日得到极高结果,18 日与 20 日都接近失败,这个尖点很可能依赖少量历史转折。若 17 至 23 日大多保持相同方向,虽然 19 日仍是最高,周围的平缓区域会提供更多稳定性线索。
这种平缓区域常被形象地称为参数高原。高原不能证明策略具有真实优势,至少说明结论没有只依赖一个精确数字。选择中心附近、机制可解释的参数,通常比追逐边缘最高点更容易承受数据变化。
均线实验台允许你改变短期与长期窗口。先记录最高点,再查看它周围的参数。实验使用固定教学价格,只解释选择形状,不代表任何真实策略收益。
压力测试主动把关键条件变差¶
样本外检查为策略换了一段未参与开发的时期,压力测试则故意改变实现与环境假设。它回答的是,如果成本更高、成交更慢、最好事件缺席或市场状态改变,结论会怎样。
| 压力方向 | 可以怎样改变 | 它主要检查什么 |
|---|---|---|
| 交易成本 | 费用与滑点提高一倍或三倍 | 毛优势有没有执行余量 |
| 成交时间 | 订单延迟一日或使用更差价格 | 结果是否依赖瞬间参考价 |
| 参数 | 在有机制依据的邻近范围移动 | 结论是否只立在一个尖峰 |
| 收益来源 | 删除最好若干日或最大贡献资产 | 盈利是否过度集中 |
| 市场状态 | 分开趋势、震荡、高波动和流动性压力期 | 机制在哪些环境容易失效 |
| 候选池 | 收紧流动性与容量要求 | 纸面资产是否真的可交易 |
压力结果变差属于预期,因为研究者正在提高难度。轻微变化就让方向完全翻转,说明当前证据脆弱;结果按机制预期减弱,却仍保留一定余量,才值得继续观察。通过几项压力测试也不表示未来安全,它只排除了若干已经想到的脆弱点。
最终测试前要冻结一张封条¶
最终测试最容易在兴奋中失去独立性。打开以前,把策略版本、数据版本、候选池、预处理、信号、订单、费用、主要指标、压力情景与放弃条件写入版本记录。冻结表示这些内容在看见测试成绩前不再改变。
研究卡与代码版本
数据来源、版本和截止时间
训练与验证边界、隔离长度
固定特征、规则和参数
历史候选池与缺失处理
信号形成、订单、成交与费用假设
主要评价指标与基准
事先规定的放弃条件
允许打开最终测试的日期
打开后若发现真正的代码错误,当然应该修复。修复记录要说明错误怎样影响结果,也要承认测试数据已经被看过。修改后的策略需要等待新的未见数据,或者把当前结果明确标为修复后的开发反馈,不能继续称为首次独立考试。
结果报告要分清证据来自哪里¶
训练阶段应说明研究者怎样形成规则、处理数据并尝试多少主要方案。验证阶段要写明候选选择、参数高原和压力情景,不能只报告胜出的那一组。测试阶段要与冻结版本和基准比较,展示收益、风险、成本、归因和每个滚动时期。
若测试失败,报告应直接指出失败在哪个条件。选择放弃、收窄机制、改进数据或等待新时期,都是有效结论。继续使用同一测试期修改的新版本仍可作为探索,但不再增加一份独立样本外证据。
更诚实的句子可能是,当前版本在早期训练与验证中表现方向一致,在 2024 至 2025 年冻结测试中未超过基准,主要差异来自更高换手与一段反转行情,因此停止该参数版本。它比隐藏失败并继续调到盈利更能帮助后来的研究。
AI 可以检查时间计划,不能替研究者忘记答案¶
AI 能根据回看窗口、标签期限和持有期画出每轮边界,也能生成检测索引重叠的小测试。请求中必须说明所有时间窗口,并要求它解释每一项删除原因。
请为下面的日频研究设计三轮扩展窗口验证。特征最长回看六十个交易日,标签使用未来五日收益,持有期五日。先用时间线列出每轮开发、边界清除、隔离与检查区间,再说明哪些末端样本会跨界。随后给出能够发现日期重叠、标签跨界和全时期预处理的三个小测试。不要随机打乱日期,无法确定的市场日历标为待确认。
AI 一旦看过最终测试结果,也和研究者一样获得了答案。继续在同一对话中要求它“调到测试更好”,仍然是在使用测试反馈。研究记录应保存 AI 接触过哪些数据与结果,让自动协作不成为隐藏的额外选择通道。
用互动实验比较随机与时间切分¶
过度拟合与数据切分实验可以在同一组时间数据上切换随机分组和按时间分组。观察随机方式为什么更容易把相邻状态分到两边,再增加候选数量,查看研究阶段冠军与后期结果的差距。
容易弄错的地方¶
| 常见做法 | 它怎样破坏考试 | 合理处理 |
|---|---|---|
| 随机打乱金融日期 | 相邻行情与后期状态散落到开发和检查两边 | 按时间方向切分,并检查依赖结构 |
| 训练末端标签伸入验证期 | 开发过程读取了后期价格 | 清除跨界样本并设置隔离区 |
| 完整历史先标准化再切分 | 早期输入吸收后期分布 | 每轮只在开发区间拟合预处理 |
| 测试失败后在同一数据继续调 | 测试结果参与了新版本选择 | 改变数据角色并等待新的未见时期 |
| 只展示滚动验证平均值 | 某些持续失败时期被汇总遮住 | 保存每轮结果、参数与市场环境 |
| 追逐唯一最佳参数 | 选择可能依赖历史噪声尖峰 | 检查邻近高原与机制依据 |
一分钟自测¶
研究者看完测试集后修改一项过滤条件,再次在同一测试集报告更好结果。第二次结果仍然属于完全未见的测试吗?
查看答案
不属于。第一次结果已经影响过滤条件,测试集参与了第二版选择。第二次结果可以作为开发反馈保存,却需要新的未见数据才能获得独立考试证据。本课术语¶
| 词语 | 先这样理解 |
|---|---|
| 样本内 | 已经参与规则形成、拟合或选择的数据 |
| 样本外 | 没有参与开发、用于独立检查的数据 |
| 训练集 | 用于探索、拟合和形成候选的数据 |
| 验证集 | 用于比较有限候选与决定参数的数据 |
| 测试集 | 规则冻结后用于最后独立检查的数据 |
| 清除 | 删除标签或持有区间跨越边界的样本 |
| 隔离区 | 分段之间为防止信息重叠留下的空白 |
| 滚动前推验证 | 多次使用过去开发并检查紧随其后未来的方法 |
| 扩展窗口 | 开发起点固定、终点不断向前扩大的窗口 |
| 压力测试 | 主动把关键条件变差以检查脆弱性 |
本阶段综合作业¶
请回到第 13 课的研究卡,为它制作一份完整验证计划。画出训练、验证与测试区间,写明最长回看、标签、持有期与隔离长度。至少安排三轮滚动前推检查,并设计费用、成交延迟、参数邻近、收益集中和市场状态五项压力情景。最后填写测试封条,明确哪种结果会让当前版本停止。
成果中必须保留每个时间边界的理由。若数据太短,无法同时支持三段与多轮检查,请缩小研究复杂度或承认暂时不能完成独立测试,不能把同一时期反复改名使用。
课后练习¶
1 理解数据职责¶
训练集、验证集和测试集分别允许哪些活动?
提示
依次考虑形成候选、选择候选和冻结后独立检查。答案
训练集用于探索与拟合,验证集用于比较有限候选和选参数,测试集只在规则冻结后进行独立检查。测试结果参与修改以后,该数据就不再保持原角色。2 判断边界重叠¶
标签使用未来二十日收益,训练集最后一个信号日紧邻验证集开始日。主要风险是什么?
提示
训练标签为了得到答案,会读取哪些后续日期?答案
训练末端标签会使用验证期价格,开发过程因此获得验证信息。需要清除跨界训练样本,并按照标签与持有期留下隔离。3 比较两类窗口¶
扩展窗口与只保留最近三年的滚动窗口各有什么取舍?
提示
比较历史样本量与对近期结构的重视程度。答案
扩展窗口使用越来越多历史,估计可能更稳定,却可能混入较早结构;固定长度滚动窗口更重视近期变化,但样本较少,参数更容易波动。4 动手画三轮前推¶
使用 2018 至 2025 年数据,设计三轮“过去开发、随后一年检查”的扩展窗口,并在每轮之间预留五日标签隔离。
提示
一种安排可以从 2018 至 2020 年开发、2021 年检查开始,然后逐年扩展开发终点。答案
示例为第一轮 2018 至 2020 年开发、2021 年检查;第二轮 2018 至 2021 年开发、2022 年检查;第三轮 2018 至 2022 年开发、2023 年检查。每轮开发末端要清除未来五日标签会进入检查期的样本,后续年份可另作验证或最终测试。5 开放思考¶
怎样判断参数结果更接近稳定高原,而不是偶然尖峰?
评价要点
查看有机制依据的相邻参数是否保持方向,在不同滚动时期、费用和成交延迟下是否仍能解释。无需每个点都盈利,但不能只靠一个精确数字成立,也要记录全部搜索范围。训练集帮助形成方案,验证集比较有限候选,测试集只检查冻结后的版本。三者之间若还有未来标签跨界,就要清除末端样本并留下隔离。把自己的日期画成几轮从过去走向未来的窗口,能逐轮说明哪些数据当时可见,这份验证计划才算完整。