跳到正文

研究指南

股票回测与实验

股票策略怎么滚动调参:逐窗重选并拼接样本外结果

滚动调参需要每个训练窗真正重新评价候选,锁定选择后,再运行后续未见测试窗。把一条已经生成的净值切成几段,并没有完成这些动作。交付证据应包括逐窗搜索与锁参记录,以及只含测试观测、能够勾稽的拼接链。

本文用三个合成窗口,每窗比较两个阈值,重选简单股票规则的参数。它没有训练AI模型,也没有核实CoTrading流程。单一市场、单一币种分别处理;完整滚动搜索编排属于外部扩展,不能假定参考系统已经提供。

先规定完整时间日程

写清训练起止、测试起止、候选参数域、选择目标、平局规则、成交时点和账户承接政策。还要规定每个训练结果在什么时点已经成熟。即使特征日期较早,如果标签结果在锁参之后才结束,也不能进入该窗选择。

TimeSeriesSplit官方文档介绍有序训练测试索引、可选间隔及可比期间条件。它可以帮助理解时间顺序,但切分器本身不执行候选搜索、封存赢家或账户勾稽。本文明确实现这些额外环节,不能把一个切分函数当作完整交易流水线。

先决定采用扩展历史还是固定长度滚动历史。本例每次使用四个训练结果、随后两个测试结果。真实间隔应服从标签成熟和执行要求,而不是因为库默认零就一律取零。保留交易会话日历,数据不规则时,同样行数并不代表同样经过时间。

定义可观察回报和可执行敞口

合成会话回报为[0.03,0.04,-0.02,0.01,-0.03,0.04,0.03,-0.04,0.01,0.02,-0.03],每个值解释为该期开盘至收盘回报。使用上一期已经完成的回报作特征,因而在下一期开盘前可得。当上一期回报严格高于阈值,本期敞口为一,否则为零。

活跃期在本期开盘建仓、收盘平仓,没有隔夜敞口,费用为零,并允许数学上的比例敞口。资本在拼接测试结果之间复合增长。这样避免了隔夜持仓承接歧义,但也明确说明没有模拟实际手数、流动性和交易费用,不能把结果当作真实可交易收益。

候选阈值只有零和百分之二。训练评分是该阈值实际训练敞口的复合回报,较高者胜;完全同分时选更小阈值。目标和平局规则必须先冻结,不能在最后一窗为了拯救累计结果改标准。这里是参数重选,不是模型重新学习权重,更不是自动证明策略有效。

实际执行三次独立搜索

窗口训练结果索引锁定阈值测试索引
11至40.025至6
23至60.027至8
35至809至10

索引从零开始,零位置为首个训练结果提供前一期特征。每窗都只在自己的训练位置评价两个阈值,先保存两分数,再选赢家。因此程序实际进行六次候选评价,不是只搜一次,再把赢家净值拆成三份。

第一窗中,零阈值多参与了一期亏损,百分之二阈值将其排除,因此选择后者。第二窗仍选择百分之二。第三窗两个候选训练回报序列相同,按预先规则选零。这个变化来自真实计算过的窗口分数,不是人为预设一个变化列表来假装重新搜索。

窗口记录应包含所有候选评分,包括没有入选者,不能只存最终阈值。检查者应能用同一训练输入重算评分,确认平局到底如何产生。若某候选训练失败,应保留失败状态,并按预定政策处理;不能把失败填零后与真实负收益比较,从而错误选中没有跑完的参数。

实际编排还可以把窗口编号连接到搜索任务编号、输入摘要、锁定配置和测试输出位置。测试执行器只接收该窗锁定值,不再拥有重新挑选候选的权限。若多个工作线程并行搜索,应等该窗约定候选完成或按失败政策结束,再生成封条;不能先跑测试,等看到效果后补写一个时间更早的锁参记录。这个顺序需要从运行日志复核,而不是依赖界面上显示的一个窗口名称。

先锁参,再运行后续测试

评价测试区间之前,先保存选定阈值和最后可用训练索引,断言训练结果都早于首测试结果。核心顺序如下,完整例中另外保存逐窗记录:

searched = {p: training_score(data, train, p) for p in candidates}
chosen = min(candidates, key=lambda p: (-searched[p], p))
locked = (chosen, max(train))
assert locked[1] < min(test)
test_piece = segment(data, test, locked[0])

首个测试观测可以使用最后训练期已经知道的特征,这是合法历史,不是测试结果泄漏。但不能使用测试期尚未实现的开盘至收盘回报,决定同一期的开盘敞口。真实数据接入时,必须把特征可得时点和下单可执行时点都对齐,只有整数索引先后还不够。

早期测试结果发生后,可以进入后续训练窗,这模拟了按时间更新的研究流程,并不意味着各窗统计独立。应保存实际信息时点,不要机械禁止一切历史重叠,也不要把共享历史包装成多次独立确认。模型和流程层面的选择偏差仍然存在。

按唯一测试会话拼接

六个测试位置恰为[5,6,7,8,9,10],策略回报为[0,0.03,-0.04,0,0.02,-0.03]。每个增长因子只乘一次,得到累计负百分之二点一六八一二八,即1.03 × 0.96 × 1.02 × 0.97 − 1;零收益会话的增长因子为一。

不能追加训练净值,也不能在共同边界重复计入同一会话。每段都重置相同虚构本金,再把期末余额相加,同样错误。本例资本从同一单位起点连乘;日内平仓政策则说明了窗口边界没有剩余证券敞口,资本承接和持仓承接是分别明确的。

真实策略若隔夜持仓,要规定旧参数继续管理到退出、由新参数接手,还是含成本强平。这个选择会改变实验,必须进入实际执行,而不是在图上事后修正。涉及连续账户时同时保留现金与证券状态,避免只有收益片段却无法说明资金从哪里来。

检验重复窗口和未来污染

样例故意构造一个坏日程,让测试索引六出现两次,验证器在拼接之前拒绝。这与训练期不够是另一种机制:每个窗口本身都可能训练在前、测试在后,但汇总结果仍会重复计算同一次机会。因此单窗检查与全局测试键唯一检查都需要。

另一个断言把较晚的索引九回报改为很大正数,确认第一窗搜索记录完全不变,用于验证本地代码的信息边界。它没有证明真实系统全部外部输入都满足时点正确性。数据修订、模型调用、全样本标准化等隐藏依赖,还需要各自检查,不能靠这一个断言全部放行。

scikit-learn交叉验证指南提醒测试信息参与选择的风险。看完拼接测试结果后才挑选整个滚动日程,也会产生类似问题。保存尝试过的日程;需要独立确认时,为最终冻结的整条流水线另保留未来证据,而不是反复改窗长直到满意。

实际运行与交付验收

下载完整示例。使用Python 3.12或更新版本,运行python R611.py;仅使用标准库和合成回报。输出每窗搜索次数2,2,2、阈值[0.02,0.02,0]、六个唯一测试索引和累计负百分之二点一六八一二八。断言也拒绝重复测试窗,并检查未来变化反例。

验收时,每个测试片段都应追到自己的完整搜索和锁参,所有训练目标在锁定前成熟,拼接位置无重复。归档参数域、全部评分、平局规则、敞口政策、费用与输入版本。只有一张分窗图,没有这份账,不能证明真实重新选择发生过。

再检查首窗和末窗边界:前一期特征是否存在,首个测试结果是否可交易,末期是否尚有未成熟标签或未完成订单。缺少任何一项,应明确裁剪或标未知,而不是把边界结果填零来维持六期长度。日期与输入引用共同保存,才有办法在数据更新后重建同一时间安排。

窗长、参数域、特征、数据或执行政策变化时,建立新版流水线并保留原实验。样例只证明编排和算术,没有提供真实历史盈利、模型效果或实际交易准备度证据。对CoTrading是否支持完整流程的判断仍需另行验证,不能从已有净值分析函数推断。

常见问题

把净值切成窗口就是滚动调参吗?

不是。每窗需要真实训练搜索、锁定候选及后续测试执行。

过去测试结果可以成为后来训练历史吗?

按声明时间顺序、结果已经可得时可以,但这不让窗口彼此独立。

相关阅读