三重障碍方法
固定时间窗口标签用起来方便,但几乎总是错的。三重障碍方法按“实际先发生什么”来打标签:止盈、止损、还是超时。标签匹配策略实际交易的方式。
三重障碍方法:标签与你的交易方式对齐
标签是一个问题。问题是:“这笔交易是赚是赔?”标签的选择决定了模型学到什么。大部分量化机器学习流水线用固定时间窗口的标签,比如“未来 5 天价格是否上涨?”这是文献中最常见的标签,但也是大多数交易策略的错配标签。
错配标签的问题很隐蔽。交叉验证看起来正常,回测夏普 1.8,模型上线后夏普掉到 0.4。你盯着实盘交易发现,策略 70% 的情况下是在止损或止盈阈值上平仓的,而不是在 5 天标记上平仓。你的标签是 5 天前向收益,但你的平仓是触发了障碍。模型学的是预测 5 天收益,策略交易的是障碍触发。这是两个不同的问题。
三重障碍方法正是为了修正这一点。它用实际先发生的事来标记每条观测:止盈障碍、止损障碍、还是纵向(时间)障碍。标签和策略的平仓逻辑一致。
参考是 AFML 第 3 章。这一章不长,实现也直接,收益却很大。
(López de Prado, M., Advances in Financial Machine Learning, 第 3 章。ISBN 978-1119482086。)
三个障碍
对每个入场时点 ,定义三个障碍。
上障碍(止盈):,其中 由价格首次触及 的时刻决定。当价格先触及这个水平时,标签是 (或你用来表示“赢”的值)。
下障碍(止损):,其中 由价格首次触及 的时刻决定。当价格先触及这个水平时,标签是 (或“亏”)。
纵向障碍(超时):,其中 是最大持仓期。当到 时两个障碍都没被触发,标签由收益 的符号决定。这就是“都没触发”的情况。
观测 的标签是:
换句话说,标签是“哪个障碍先被触发,那个触发点的价格相对入场价格的变化”的符号,上限是纵向障碍。
有一个细节:障碍可以是动态的(基于滚动波动率),也可以是静态的(固定百分比)。动态障碍会随近期波动率缩放:平静状态时障碍更紧,波动状态时更宽。这样在各个状态下标签分布更一致。静态障碍更简单,但标签分布会随波动率状态变化(波动期更多 ,平静期更多超时)。
动态形式:
其中 是时点 的滚动实现波动率, 是一个常数(通常 1.0 到 2.0)。同一个 用在两边,让障碍在波动率单位上对称。
为什么固定时间窗口标签会失败
四个理由,每一个都够用。
理由 1:它忽略了策略的实际平仓逻辑。
带固定时间窗口标签的回测假设你无论发生什么都会持有 天。如果你的实盘策略是先在止损或止盈阈值上平仓,回测就是在撒谎。固定时间窗口收益是“如果持有到期”的收益。实际策略收益是“障碍触发时的收益”。在波动状态中两者可能差非常多。
理由 2:它给出的标签分布不真实。
假设你的标签是 5 天前向收益。标签大致在 0 周围正态分布,不会偏向 。障碍标签则不同:上下障碍距离入场是固定距离,所以当纵向障碍触发时,价格的移动小于障碍允许的幅度。标签更双峰化:大量 ,大量 ,中间夹着少量小收益。
这在分类任务中很重要。障碍标签训练出的模型学的是预测“触发了哪个障碍”,这是离散事件。固定时间窗口收益训练出的模型学的是预测连续变量,是不同的问题。
理由 3:它不感知路径。
两条路径可以产生同样的 5 天收益:一条平滑上涨,或者一次急跌后反弹。固定时间窗口标签对两者一视同仁。三重障碍标签能区分它们:平滑上涨可能在第 2 天就触发上障碍(标签 ,收益被截断),先跌后涨的路径则在纵向障碍触发时只剩小正收益(标签取决于符号)。模型从两种路径中学到的是不同的东西。
理由 4:它表达不了风险管理约束。
大多数交易策略都有最大亏损阈值。如果价格朝你反向移动了 ,就平仓。固定时间窗口标签没有编码这个信息。带止损的策略会在 5 天标记之前平仓,所以 5 天收益根本没有被实现。三重障碍标签直接编码了平仓逻辑。
什么时候固定时间窗口标签也可以
三种情况下简单方法也够用。
情况 1:买入并持有策略。 如果你买入后从不平仓(或只在某个固定未来日期平仓),固定时间窗口标签是对的。没有障碍。
情况 2:横截面研究。 如果你研究的是“5 天收益的预测因子”,把它当学术问题而不是建交易策略,固定时间窗口标签是合适的。
情况 3:连续仓位管理。 如果你的策略根据预测收益连续调整仓位(而不是简单的多/空二元决策),固定时间窗口标签比障碍标签给出更平滑的训练目标。
大多数其他情况下(大多数带止损或止盈的交易策略),三重障碍是更合适的选择。
Side 与 Size:一种有用的区分
AFML 给出了一个有用的分解:标签可以分成两部分。
Side:交易方向。这是多头、空头还是无仓位?由哪个障碍先触发(或超时收益的符号)决定。
Size:仓位大小。这笔交易应该 100% 满仓、50% 半仓还是 0% 空仓?由预测的置信度决定。
主模型预测 side(方向)。次级模型(meta-labeling 模型)预测主模型是否正确。次级模型的输出成为 size。
这个分解是 meta-labeling 的基础,下一篇文章会详细讲。现在要明白的是:三重障碍标签为这两个模型都提供了天然的训练目标。
(AFML, §3.5。)
实现
一个干净的 Python 实现:
import numpy as np
import pandas as pd
def triple_barrier_labels(prices, events, pt_sl, target, min_ret=0.0):
"""Apply triple-barrier labeling.
prices: DataFrame of prices indexed by time.
events: DataFrame with columns [t1] (vertical barrier) and [trgt] (target volatility).
pt_sl: list of [profit_taking, stop_loss] multipliers on target.
target: pre-computed daily volatility series.
"""
events = events.copy()
events['t1'] = events['t1'].fillna(prices.index[-1])
# Upper and lower barriers based on target volatility
events['trgt'] = target.loc[events.index]
events['sl'] = events['trgt'] * pt_sl[0] # stop-loss width
events['pt'] = events['trgt'] * pt_sl[1] # take-profit width
# For each event, find when each barrier is hit
labels = []
for t0, t1 in events[['t1']].itertuples():
path = prices.loc[t0:t1]
# Compute path returns from entry
rets = path['close'] / path['close'].iloc[0] - 1
# Find first time upper barrier is hit
pt_hit = rets[rets >= events.loc[t0, 'pt']].index
pt_hit = pt_hit[0] if len(pt_hit) > 0 else pd.NaT
# Find first time lower barrier is hit
sl_hit = rets[rets <= -events.loc[t0, 'sl']].index
sl_hit = sl_hit[0] if len(sl_hit) > 0 else pd.NaT
# Determine which was first
first_hit = min(pt_hit, sl_hit)
# If neither, label by sign of return at t1
if pd.isna(first_hit):
label = np.sign(rets.iloc[-1]) if abs(rets.iloc[-1]) > min_ret else 0
else:
label = 1 if first_hit == pt_hit else -1
labels.append((t0, label))
return pd.DataFrame(labels, columns=['t0', 'label']).set_index('t0')
函数对每个事件时间返回 、 或 。 对应“障碍都没触发,且收益小于 min_ret”。通常你会删除这些样本,或者按策略需要把它们当成 (不交易)。
障碍宽度的选择
两个参数:止盈倍数 和止损倍数 。通常取一样的值,但也可以不一样。
对称障碍:。没有不对称先验时使用。最常见的起点。
不对称障碍:(或反之)。策略有方向性偏好时使用。纯多头策略可能用 ,愿意在上涨方向上承受两倍的目标,但下跌方向快速止损。这会让标签分布偏向策略天然的方向。
合理的起点:(障碍距离入场 1 个日波动率)。日波动率 1.5% 时,障碍是 。5 天最大持仓的纵向障碍给 5 天时间去触发任一障碍。
怎么选 :用和超参数一样的交叉验证逻辑。尝试 ,挑能让样本外夏普最大的值,同时要求标签分布不要失衡(比如没有哪个类超过 70%)。
纵向障碍
纵向障碍 是最大持仓期。它是“放弃”式平仓。如果到 时还没触发任一障碍,就在第 天的收盘价平仓。
的选择是战略决策,不是超参数。它编码了你的耐心程度。短的 (1–5 天)适合高频策略。长的 (20–60 天)适合波段或仓位策略。很长的 (几个月)适合慢动组合。
很长有两个问题:(a) 纵向障碍成为主导平仓方式,上下障碍变得无关紧要;(b) 路径依赖变得更难计算(你需要检查整条路径)。 时,可以考虑用日频抽样的检查,而不是逐 tick 检查。
有一个细节:纵向障碍应该按事件单独设置,不是全局设置。如果你的入场时点 有已知的持仓期(因为你入场是对特定信号的反应),就用那个持仓期作为纵向障碍。这让标签和具体入场时点相关。
(AFML, §3.3。)
对称与不对称的收益
一个有用的诊断:计算每种障碍触发时的平均收益。
- 上障碍触发的平均收益:大约 (按构造)。
- 下障碍触发的平均收益:大约 。
- 纵向障碍触发的平均收益:应该接近零,但不严格为零(因为路径可能先触及障碍然后反弹)。
如果纵向障碍触发的平均收益明显为正或为负,障碍设置就有问题。纵向障碍应该是“都没触发”的情况,也就是价格在两个障碍之间游走。
一个常见错误:纵向障碍设得太近,导致它是主导平仓。如果 80% 的标签都是纵向触发,障碍就太紧了。放松 ,直到三种平仓类型更平衡(比如 30% 上、30% 下、40% 纵向)。
并发与唯一性
三重障碍方法产生的标签会有重叠,唯一性权重方案(上一篇文章)正好解决这个。如果你的事件纵向障碍是 ,那么两个开始时间在 5 天以内的事件,标签窗口就会重叠。唯一性权重会给这些冗余观测降权。
一个有用的预处理:把事件过滤掉,保证没有两个事件相隔小于 天。这会彻底消除重叠,得到一个更干净的数据集。代价是丢掉一些观测;好处是每条标签都是独立的。
哪种更好:过滤事件消除重叠,还是用唯一性权重?过滤更干净但会丢数据。权重保留数据,但会在加权样本中引入相关性。对大多数流水线,数据足够时优先用过滤;数据稀缺时再用权重。
三重障碍标签能带来什么
三个具体好处。
好处 1:回测与实盘表现一致。 如果实盘策略在障碍触发时平仓,回测也应该这样。否则,回测就是假设性的。三重障碍标签让回测只在“应该假设”的部分(滑点、成交不确定性)保持假设,不会在平仓逻辑上撒谎。
好处 2:更现实的夏普估计。 基于障碍的策略,在同样的底层信号上,夏普通常低于固定时间窗口策略,因为障碍会截断盈利交易,让亏损交易跑到超时。固定时间窗口下夏普 1.5,障碍标签下可能只有 1.0。1.0 才是接近现实的那个。
好处 3:更好的特征。 当标签是 5 天收益时,基于滚动窗口的特征信息量较小;当标签是障碍触发时,信息量更大。障碍触发是离散事件,原因清晰;5 天收益是嘈杂的聚合。在障碍标签上训练出的模型,特征重要性通常更可解释。
要点是:选择标签时要匹配你实际交易的方式。如果策略有止损和止盈,标签就要有止损和止盈。如果策略持有固定时间,标签可以是固定时间。模型学到什么,取决于你告诉它学什么。
一个具体对比
考虑 那天在 \100$入场。日波动率 1.5%。设置$k = 1$,所以上障碍在 $$101.50$,下障碍在 $$98.50$。纵向障碍在第 5 天。
五种可能路径:
路径 A:价格稳步上涨,第 3 天触及 \101.50$。标签 $+1$,收益 1.5%。
路径 B:价格第 1 天跌到 \98.50$,然后反弹,到第 5 天涨到 $$103$。标签 $-1$,收益 $-1.5%$。
路径 C:价格在 5 天里在 \99$101$之间震荡。标签由第 5 天的收益决定,为$+0.5%$。标签 $+1$(收益为正,但没触发障碍)。
路径 D:价格以 \100$开盘,第 2 天跌到$$99$,第 5 天反弹到 $$100.50$。标签 $+1$(第 5 天收益 $+0.5%$,小幅正收益)。
路径 E:价格第 1 天跳空跌到 \97$。标签 $-1$,收益 $-3%$。
固定时间窗口(5 天)标签:A=,B=,C=,D=,E=。
三重障碍标签:A=,B=,C=,D=,E=。
两种打标签的方式给出了不同的信号。三重障碍方案捕捉到了策略的平仓逻辑:B 在止损上平仓,虽然 5 天收益为正。固定时间窗口方案把 B 当成赢家,但策略在它身上其实是亏钱的。
如果你的模型用固定时间窗口标签训练,而你的策略用三重障碍平仓,模型学的是错的东西。实盘夏普会让人失望。
常见错误
反复出现的五种错误。
错误 1: 设得太短。 如果 天,几乎所有标签都是纵向触发(因为上下障碍相对日波动噪声来说很紧)。标签基本就是 1 天前向收益。你还不如直接用固定时间窗口标签。
错误 2: 设得太小。 如果 ,障碍在入场价格的 。大部分路径几分钟内就会触发障碍。标签噪声很大。
错误 3:用了错误的波动率估计。 如果用长期波动率(比如 5 年均值)而不是滚动波动率,障碍不会随状态变化。用 20–60 天滚动波动率。
错误 4:忽略入场和首次观测之间的时间差。 如果你在 计算特征,而 的价格已经突破上障碍,标签是 ,但路径本身有歧义。要小心入场时点的对齐。
错误 5:对所有资产用同一个 。 高波动率资产需要比低波动率资产更宽的障碍。用滚动波动率缩放的障碍,也就是 ,其中 是具体资产的。
更大的视角
三重障碍方法是“诚实打标签”大框架的一块。其他几块是:并发处理(第 4 章)、meta-labeling(第 6 章)、分数阶差分标签(第 5 章)。合起来,它们形成了一个既尊重金融数据结构、也尊重交易策略逻辑的标签流水线。
如果只从本文带走一条:你的标签应该匹配你的平仓方式。如果不匹配,你的回测就在撒谎,这个谎言会在实盘上线的第一个月暴露出来。