新闻详情

新闻详情

首页 / 资讯中心 / 详情

样本内外划分:量化策略防过拟合的第一道防线

发布时间:2026/9/29 16:38:04来源:尧图网络
样本内外划分:量化策略防过拟合的第一道防线
1. 为什么样本内外划分是防止过拟合的第一道防线一个量化策略在回测里跑出漂亮的净值曲线夏普比高得吓人偏偏一上模拟盘就原形毕露。这种经历做量化的人多少都遇到过。问题大概率不在策略逻辑本身有多离谱而在于你没有做样本内外划分或者划分得不够干净。样本内外划分的本质就是把数据强行切成训练题和考试题两个集合。训练题用来开发策略、确定参数考试题用来检验策略在没见过的数据上到底行不行。这个思路听着简单但大部分策略失效事故恰恰是因为训练题和考试题混在了一起或者干脆没给自己留考试题。1.1 样本内样本外的本质区别样本内数据In-SampleIS是策略开发过程中看到的全部历史行情参数优化、入场规则筛选、出场逻辑设计全都基于这段数据完成。样本外数据Out-of-SampleOOS是策略从头到尾没有参与计算、没有参与调参的数据区间它模拟的是策略上线之后的未来。两者的关系很像学生备考和模拟考试。样本内就是反复刷题的过程题刷得再多只能说明把历年真题背熟了样本外则是一套从来没见过的新题目考出来的分数才反映真实水平。量化策略如果只在样本内表现好样本外一塌糊涂那就是典型的背题背多了——模型把历史行情里的噪声当成规律记住了真实市场不给你这种好事。所以做策略评估时我心里始终有一根弦所有收益指标、回撤指标我都以样本外的为准。样本内数字再好看也只当参考因为它天然是乐观的。1.2 过拟合是怎么被调参喂出来的很多人以为过拟合是模型太复杂才导致的其实在量化策略开发里过拟合更常见的来源是参数搜索次数太多。假设你在同一段数据上尝试了50组均线参数组合每组都跑一遍回测最后留下表现最好的那组。这50组里总有一组恰好踩中这一段行情的节奏收益自然会非常高。但这个高收益里有相当大部分是运气成分而不是策略本身有优势。尝试的次数越多找到幸运参数的概率就越大。学术上管这个叫多重检验问题Multiple Testing Bias通俗说就是猴子在键盘上敲得足够久也能敲出一段像样的旋律。更隐蔽的是很多人在调参过程中还会不断修改策略规则看到回撤大就加一条过滤条件看到某年亏损就针对那一年补一个逻辑。每改一次其实都是对同一段数据做了一次新的拟合。改到最后策略在历史上处处合理、无懈可击但那段历史已经不再代表真实规律因为你的规则本身就是从这段历史里反推出来的。样本内外划分恰恰能挡住这种慢性污染。因为样本外是你反复修改规则的这段时间里一直碰不到的数据等策略完全定型了再拿出来检验如果还能保持稳定盈利那才是真有东西。1.3 不划分或者划分不清的代价直接后果就是策略实盘表现和回测表现之间出现巨大剪刀差。回测里年化30%、最大回撤5%实盘变成年化5%、最大回撤30%这种事情放在任何资金体量上都是灾难。轻则白费半年开发时间重则让交易者对自己的系统彻底失去信心在一波正常回撤里砍掉仓位反而错过后续的利润。我见过不少新手把全部数据拿来回测然后在同一条曲线上自我感动完全忽略了一个基本事实那条净值曲线的每一段都已经被你的策略参数看见过了。真实交易的时候市场永远给你出新题可你的策略只在旧题上练过。2. 主流的样本内外划分方案与取舍划分方案不是随便切一刀就完事不同方案对应不同的策略开发阶段和检验目的。我自己常用的有三种方案每种后面都有它的适用场景。2.1 按时间顺序做一次性留出划分这是最朴素也最容易理解的方法把完整时间序列按先后顺序前面一段作为样本内后面一段作为样本外。行情数据天然有序绝对不能随机打乱再切分否则样本外区间里的历史信息会被无意中泄露到训练过程里。比例上常见的做法是七三开或者八二开但这只是习惯不是铁律。两个更重要的原则一是样本外区间必须足够长长到能覆盖至少一轮完整的市场周期比如包含牛熊转换、震荡整理、趋势行情否则检验结果会有较大偶然性另一个是样本外的起点之前要留有预热区burn-in period因为很多指标在计算初期需要足够的历史数据才能稳定。一次性划分的优点是简单、快、可复现适合在策略开发早期用来做粗筛。缺点是它只检验了一次考试万一样本外那段时间恰好顺风就容易高估策略的真实水平。2.2 Walk-Forward滚动式划分Walk-Forward又叫滚动样本内外检验逻辑是模拟实盘的连续决策过程。具体做法是设定一个训练窗口和一个测试窗口比如用前36个月训练测试未来6个月测试完把训练窗口整体向后滑动6个月再训练再测试直到覆盖完整个数据区间。这种方法比一次性划分更贴近真实交易因为实盘就是在不断用最近的数据训练参数、然后用于未来交易。最终把每一段样本外的交易结果拼接成一条完整曲线这条曲线代表的是策略在未知未来上连续运行的表现可信度远高于一次性划分。代价是计算量成倍增加每滚动一次就要对全部候选参数重新做一轮优化。如果策略参数多、数据频率高比如分钟级跑下来可能要几个小时。但我觉得这笔时间是值得的尤其是进入策略中期验证阶段Walk-Forward结果几乎是我决定一个策略是否继续推进的核心依据。这里面还有个细节训练窗口用固定长度还是扩展长度固定窗口适合市场结构会漂移的品种比如商品期货太久远的数据参考意义下降扩展窗口则让训练样本越来越多适合相对稳定的标的。没有标准答案取决于你在什么市场交易。2.3 K-Fold在时序数据上的坑与替代方案很多人把机器学习里的K折交叉验证直接搬到时序数据上这是高风险操作。标准的K-Fold是随机把样本打乱后分成K份依次取一份做验证、其余做训练。但行情数据不是独立同分布的相邻两天的价格高度相关前一天的价格直接影响后一天。一旦随机打乱就会发生用未来信息训练、验证过去数据的穿越得到的验证分数虚高而且完全无法反映真实交易的时间顺序。针对时序数据业界也发展出了专门的交叉验证方式比如按时间顺序划分的TimeSeriesSplit以及解决标签重叠问题的Purged K-Fold。Purged的思想是训练集和验证集之间要留出一段禁运区不让训练集样本的标签信息通过重叠的时间窗口漏到验证集。涉及机器学习模型的策略尤其是预测第二天涨跌这类任务时这种划分非常关键。不过对大多数用规则、指标做信号的策略Walk-Forward已经是最合适的折中方案了。划分方案优点缺点主要适用场景一次性留出划分简单、快速、易复现只检验一次、偶然性高策略早期粗筛、快速试错Walk-Forward贴近实盘、可信度高计算量大、实现复杂策略中期验证、参数稳定性检查Purged K-Fold适合机器学习模型、防标签泄漏实现门槛高、不适用于规则策略预测型模型、特征工程验证3. Python实操从划分数据到完整样本外评估光讲理论不落地等于白说。下面我把一套完整的样本内外划分流程写成代码全部用Python实现环境只需要pandas、numpy和matplotlib非常轻量。3.1 数据准备与基准划分函数我习惯把数据统一整理成DataFrame索引是时间列至少包含open、high、low、close、volume。第一步就是按时间排序防止数据源本身乱序然后写一个按日期切分的函数。import pandas as pd import numpy as np from datetime import datetime def split_by_time(df: pd.DataFrame, split_ratio: float 0.7): 按时间顺序划分样本内和样本外 df: 按时间升序排列的行情数据 split_ratio: 样本内占比 df df.sort_index() split_idx int(len(df) * split_ratio) is_df df.iloc[:split_idx] oos_df df.iloc[split_idx:] return is_df, oos_df注意划分之前一定要确认数据里没有未来函数。比如计算均线指标时我的做法是保证每个K线对应的指标值都只用这个K线之前的数据计算这是最基本的底线。发生过一个真实案例我把一个信号生成代码里写成了shift(-1)等于把下一根K线的收盘价拿来计算当前信号回测成绩异常好好到我自己都不敢信一查才发现是这种低级泄漏。这类问题在样本内外划分之前就必须排查干净否则后面所有工作都是空中楼阁。3.2 实现Walk-Forward回测一次性划分代码太简单重点说Walk-Forward的实现思路。整体框架分四步切片训练窗口、优化参数、在测试窗口生成信号、拼接样本外收益。def walk_forward_backtest(data: pd.DataFrame, train_days: int 720, test_days: int 120, step_days: int 30, param_gridNone): 简化的Walk-Forward框架 train_days: 训练窗口长度K线根数 test_days: 测试窗口长度 step_days: 滚动步长 param_grid: 候选参数网格 results [] start 0 total_len len(data) while start train_days test_days total_len: train data.iloc[start:start train_days] test data.iloc[start train_days:start train_days test_days] # 在训练窗口内寻找最优参数 best_params, best_score optimize_on_in_sample(train, param_grid) # 用最优参数在测试窗口生成信号并计算收益 oos_return run_strategy(test, best_params) results.append({ train_start: train.index[0], test_end: test.index[-1], params: best_params, oos_return: oos_return }) # 窗口滚动 start step_days return pd.DataFrame(results)这个框架的精髓在嵌套循环的外层只做一件事切窗口、调参、测试然后滚动。关键是best_params的选择仅限于训练窗口内部测试窗口的数据在参数优化阶段不可见一次都不可见。实际跑的时候step_days这个参数我建议设成和test_days一致也就是把测试窗口向后完整滑动每一步测试区间不重叠。如果步长小于测试窗口那么相邻两段样本外会有重叠统计上会引入一定相关性但不是致命问题。步长大于测试窗口则中间会有空档覆盖不够完整。3.3 样本外拼接与绩效评估Walk-Forward跑完之后把每一段样本外的日收益序列拼接成一条完整的样本外资金曲线。这里有个容易踩的坑每一段测试窗口虽然不重叠但拼接时要把各段内的收益率按复利方式连接而不是简单相加。def concat_oos_returns(segment_returns: list): 把各段样本外收益序列按照时间顺序拼接并折算为净值曲线 # 先按时间排序 all_returns pd.concat(segment_returns).sort_index() # 计算累计净值 equity_curve (1 all_returns).cumprod() return equity_curve, all_returns拿到样本外净值曲线之后再算年化收益率、夏普比率、最大回撤、卡玛比率这些指标。但记住这些指标只对样本外区间有意义别再把样本内的绩效一起混进来算总账。我通常只报告样本外绩效因为那才是如果当初上线了实际会赚多少钱。还要注意一点参数网格不能太密。我在训练窗口里做参数寻优时网格粒度如果过细即使划分再严格也同样会在训练窗口内过拟合。网格的意义是选出一个稳定可行的参数区域不是选出唯一的最优点。所以候选参数我一般控制在几十组以内而不是上万组。4. 判断策略是否过拟合的几项硬指标划分数据只是手段最终目的是判断策略有没有过拟合。光看样本外赚不赚钱还不够我需要一组更细的指标来辅助判断。4.1 样本外衰减比样本外衰减比的定义很简单样本外年化收益率除以样本内年化收益率。这个比值越接近1说明策略在训练集和测试集上的表现越一致泛化能力越强。如果比值低于0.5说明样本外表现大幅衰减策略大概率过拟合了。我自己常用的阈值是0.8以上可以放心推进0.5到0.8属于需要进一步研究的状态可以调整一下参数区间或者规则但要警惕调整过程中又把样本外信息用进去0.5以下基本判死刑除非有极强的逻辑支撑否则不建议在实盘上投入。这里强调一句衰减比只反映收益率变化还要结合回撤一起看。有时候样本外收益没降多少但最大回撤翻了一倍这种策略能承受的风险和回测时预估的完全不是一回事。4.2 参数高原与参数敏感性过拟合策略的典型特征是参数敏感参数稍微偏离最优值绩效就断崖式下降。而稳定策略的特征是存在参数高原也就是在一个连续的参数区间内策略表现都比较接近即使最优参数不是唯一的随便取一个邻近值也不会差太多。实操上我会把最优参数附近邻域的绩效做成热力图来看。横轴是参数A纵轴是参数B颜色表示绩效。如果图中的高绩效区域是一块连续的、面积不小的区域说明策略有参数高原可靠如果只有一个孤立亮点周围全是深色说明这策略是靠单点运气撑起来的。这个习惯帮我否定过好几个看似完美的策略。def param_sensitivity_map(is_data, param_a_range, param_b_range, metric_func): 计算参数邻域绩效矩阵用于观察是否存在参数高原 heat_matrix np.zeros((len(param_a_range), len(param_b_range))) for i, a in enumerate(param_a_range): for j, b in enumerate(param_b_range): heat_matrix[i, j] metric_func(is_data, a, b) return heat_matrix4.3 蒙特卡洛置换检验还有一种更严格的方法用随机性做对照把策略产生的交易信号在时间轴上随机打乱很多次每次重新计算绩效得到一组纯随机运气下的绩效分布。如果真实策略的样本外绩效显著优于这个随机分布的95%分位说明策略确实有正期望如果真实绩效落在随机分布中间那基本可以断定当前收益只是运气趁早放弃。思想很朴素但代码实现要注意随机数种子固定保证结果可复现。我还习惯做1000次置换次数太少了分布不够稳定次数太多了计算压力大。信号打乱的时候要按交易方向序列打乱而不是把日收益率直接打乱。因为日收益率之间的自相关性会干扰检验结果打乱信号再按原始行情映射才能近似生成一组独立同分布假设下的随机策略。4.4 样本内外的夏普比率对比单独讲一下夏普比率因为它是很多人评估策略的第一指标。样本内夏普2.5样本外夏普0.8这中间差了1.7已经足够说明问题。我一般要求样本外夏普至少要在样本内夏普的60%以上才考虑上实盘。还有一个实战小技巧把样本外区间细分成两段分别计算夏普比率。如果前段很高、后段很低说明策略的有效性可能正在衰减或者样本外区间里恰好包含了一段与该策略适配的行情。与其纠结是哪种情况不如直接再拉长样本外数据做二次确认。5. 实操中防过拟合的典型坑与排查技巧前面讲的都是正面做法这节专门讲我在实践里踩过或见过的坑。每一条都是真金白银换来的教训。5.1 前视偏差的三种常见形式最常见的一种是特征计算时用到了未来数据。比如计算某根K线的当日动量代码里却包含了当日收盘价与次日开盘价的差。检查方法很简单把指标计算函数的输入全部shift(1)处理后再跑一遍回测如果绩效发生明显变化说明原代码里很可能存在未来函数。第二种是数据预处理阶段用全样本统计量归一化。比如用整个数据区间的均值、标准差去归一化样本内外的特征这会让样本外的特征值包含未来信息。正确做法是只用样本内数据算统计量然后把这个统计量应用到样本外。第三种是逐根K线实时计算指标时指标本身使用了未来窗口的滚动值。金融数据处理里常见的是用rolling(window, centerTrue)加了center参数等于偷看了未来数据。我习惯写一个自查清单所有指标计算完成后在某个随机日期上手动推演一遍信号看是否和程序输出一致。5.2 幸存者偏差与退市数据做股票策略时样本数据里如果只包含当前还在上市的股票就会产生幸存者偏差。那些已经退市、暴跌的股票没有进入历史回测区间策略的历史表现自然会虚高。这是样本内外划分上看不出来的问题因为划分针对的是时间维度而幸存者偏差是标的维度上的系统性遗漏。期货策略相对好一点但也有类似问题某个合约退市后如果策略在旧合约上交易历史数据是否完整、复权方式是否合理都会影响绩效。解决问题的唯一方法是选用包含退市标的的全量历史数据或者至少在文档里明确标注这一限制。5.3 在样本外反复调参等于没有样本外这个坑特别隐蔽。有些开发者做了样本外划分但第一次样本外测试结果不理想就开始动手改参数、改规则再跑一次样本外直到结果满意为止。这个循环跑上十几轮之后样本外其实已经被污染了——它不再是一套没见过的新题而是另一套被背过的旧题。为了避免这种情况我给自己立了两条规矩。第一样本外数据只允许跑一次跑完无论结果好坏这个区间都不能再参与任何调整。第二如果第一次结果不好改策略时必须使用更早的样本内数据重新做交叉验证绝不能回头动样本外。这样一来一次策略开发周期至少要准备两条独立的样本外区间一条在开发阶段检验用即使只用一次一条留着上线前最终验收用这条最终验收的数据谁都不能碰包括我自己。5.4 工具侧与实盘侧的适配问题现在不少量化终端和平台都提供了策略测评功能一键就能出回测报告非常方便。但要注意平台生成的回测曲线如果不告诉你它是基于样本内还是样本外默认就是全样本。全样本的结果参考价值有限只能当作初步评估。用Python写好本地策略再映射到无限易这类量化终端上实盘执行时我习惯把策略评估和策略执行两部分拆开评估部分只负责参数与绩效检验使用严格的样本内外流程执行部分负责落地到实盘交易完全复用评估部分确认过的参数。凡是涉及参数变化一律回到评估流程里先验证不允许在终端里凭感觉改参数。这样才能保证实盘交易参数和回测验证参数的一致不至于回测一套实盘另一套。6. 样本内外划分的进阶组合技巧基础框架熟练之后我发现有几个进阶操作能进一步提升划分的有效性这里一并分享出来。6.1 不同市场环境下分别测样本外单一时间段的样本外覆盖不了市场全部状态。我的做法是选择两到三个特征鲜明的历史区间做次级样本外比如一段趋势明显的行情和一段剧烈震荡的行情。策略在两类区间中都能保持正收益比整体样本外均值高更说明问题。这个方法可以和Walk-Forward结合在滚动的每一段测试窗口里再按波动率或者趋势强度切分子区间。不过要注意子区间长度太短的话绩效统计意义不够所以分组别贪多两到三组足够。6.2 参数稳健性检验里的Paired Testing所谓配对检验是固定其他因素、只改变一个参数观察绩效变化是否显著。比如固定均线长度把止损参数从1倍ATR调到3倍ATR逐点测试绩效。如果整个区间绩效变化平缓说明策略对止损不敏感如果绩效在某个值附近剧烈波动说明策略依赖这个止损参数的精确取值这本身就是过拟合信号。操作上我习惯把每个参数单独做一次敏感性曲线图然后像体检报告一样归档。样本外衰减比解决整体过拟合吗的问题敏感性曲线解决具体是哪个参数导致过拟合的问题两者视角不同、互相补充。6.3 指标组合的降维与精简过拟合的另一个来源是规则太多。每多一条过滤规则就多一层对历史数据的定制化。我经常在策略开发后期做减法把每个规则依次去掉测试绩效下降幅度。如果去掉某条规则后绩效几乎没变说明这条规则本就是多余的噪声果断删除。这个过程很像给代码做重构——逻辑越简单越容易在未知样本上保持稳定。减完之后重新做一遍完整的样本外验证通常会发现策略的样本外稳定性会比精简前更好。因为少了一些只在特定历史时期有效的巧合规则策略的期望收益才能体现出来。6.4 多周期数据的交叉验证如果策略横跨分钟级和日线级最好在两种周期上都做一遍样本外检验。有些策略在分钟级样本外表现很好但日线级样本外糟糕这种策略的交易频率高、容量小对交易成本极其敏感反之日线样本外好而分钟级样本外差策略可能过于迟钝。两种周期交叉验证才能对策略的运行环境有一个完整的判断。7. 我从这些实战中学到的东西样本内外划分这件事技术难度不算高真正难的是守住纪律。数据怎么切、切多大比例代码很快就写好了但样本外只许测一次这条纪律我花了很长时间才真正做到。每次看到新想法总忍不住想拿那块留存的样本外数据验证一下但每次都硬生生忍住了。因为我很清楚那块数据一旦被消耗掉之后所有的验证都失去了解释力。实际操作中我还发现一个出乎意料的好处严格执行样本外划分之后策略开发速度反而变快了。过去花大量时间在参数微调上现在知道那些微调大多是针对噪声的自嗨于是把精力更多放在策略逻辑本身。真正有优势的策略参数即使不精确样本外也能给出不错的结果需要精确到小数点后两位才能赚钱的策略我建议直接放弃因为那是巧合不是能力。对一个量化新人我的建议是别急着追求复杂模型先用一套简单的均线系统把一个品种五年日线数据按70/30划分认真跑一遍Walk-Forward再对比一下样本内外的绩效差异。这个过程做完你对过拟合的直觉会比读十本理论书都强。量化的很多问题说到底不是模型不够先进而是评估流程不够严格。样本内外划分就是那个最简单的矫正器它不保证你赚钱但能保证你亏钱之前先看清自己的策略到底几斤几两。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

MINITAB传感器寿命计算:从威布尔分布到B10可靠性工程实践 2026/9/29 19:31:12

MINITAB传感器寿命计算:从威布尔分布到B10可靠性工程实践

1. 为什么工程师必须掌握用MINITAB算传感器寿命——不是“会用软件”,而是守住产品底线你手头那批刚出厂的光电传感器,标称寿命5万小时,但客户现场用了不到2年就批量失效;产线新上的六维力传感器,在振动工况下实测MTBF…

阅读更多 →
Model-Optimizer实战指南:从量化剪枝到算子融合的模型优化全链路 2026/9/29 19:31:12

Model-Optimizer实战指南:从量化剪枝到算子融合的模型优化全链路

1. 从“模型优化器”这个热词说起:它到底在解决什么问题“Model-Optimizer”这个词最近在技术社区里出现的频率明显高了起来。很多人第一次看到它,会下意识地以为这是某个具体的开源库或者某个大厂内部工具的名字。实际上,它更像是一个功能角…

阅读更多 →
Model-Optimizer:面向落地的AI模型压缩与推理优化体系 2026/9/29 19:31:12

Model-Optimizer:面向落地的AI模型压缩与推理优化体系

1. 什么是Model-Optimizer:不是“一键加速”,而是模型瘦身的手术刀“Model-Optimizer”这个词最近在工程师群、AI项目复盘会和模型部署现场高频出现,但它绝不是某个具体软件的名字,也不是某家大厂刚发布的神秘工具。它是一类面向生…

阅读更多 →
禅道二次开发环境搭建与断点调试实战指南 2026/9/29 19:31:06

禅道二次开发环境搭建与断点调试实战指南

1. 为什么值得折腾禅道的本地开发环境很多人第一次接触禅道二次开发,都是被一个很具体的需求逼出来的:公司用禅道做项目管理和缺陷跟踪,但流程里总有几个环节跟实际业务对不上,比如想让钉钉审批通过后自动在禅道里建单&#xff0c…

阅读更多 →
Qwen-Image-2.1本地部署与API封装实战:从环境配置到服务发布 2026/9/29 19:31:06

Qwen-Image-2.1本地部署与API封装实战:从环境配置到服务发布

先把结论放在前面:如果你想在本地跑通 Qwen-Image-2.1,并且把它包装成一个可供外部调用的 API 服务,这活儿完全可行,但也谈不上轻松。你对显存、依赖版本、模型文件格式的耐心,会直接决定你是半小时跑通还是折腾一整天…

阅读更多 →
Superpowers技能体系:让AI编程助手从泛泛而谈到照着做就行 2026/9/29 19:31:06

Superpowers技能体系:让AI编程助手从泛泛而谈到照着做就行

1. 从“superpowers”这个标题说起:它到底是什么第一次看到“superpowers”这个词,很多人脑子里蹦出来的可能是超级英雄、超能力这类画面。但在开发者和技术爱好者的语境里,它指的是一套围绕 AI 编程助手构建的技能扩展体系——你可以把它理解…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉