简介:广发证券发布的《低延迟趋势线与交易择时》金融工程专题报告,面向技术分析者、量化策略研究员与短线交易者,针对传统移动平均线(MA)平滑性与延迟性难以兼顾的痛点,系统介绍低延迟趋势线(LLT)的构造思路与交易性择时方法。压缩包仅含1个PDF文件,大小约1.44MB,内容完整、便于直接阅读。报告从EMA与信号处理中的滤波器原理出发,说明为何选择二阶滤波器构造LLT;随后展示LLT在沪深300、上证指数、深证成指等市场指数上的择时效果,并给出ETF趋势交易策略的实证结果,同时坦诚分析趋势拐点附近可能出现的震荡与正确率下降问题。对于希望优化入场离场时机、降低趋势跟踪滞后性的投资者,这份报告提供了从理论推导到案例验证的完整参考。目前已有615人学习下载,适合作为量化择时研究的中级进阶资料。
1. 低延迟趋势线:为什么传统均线「跟不紧」,以及交易性择时的真正收益来源
2010 年之后,很多用均线做趋势跟随的人会明显感觉到信号慢半拍:30 日均线刚转头向下,指数已经跌了一段;等均线重新走平,反弹又错过了。这不是参数没调好,而是 MA 的数学结构决定的——它的延迟约等于计算周期的一半,20 日均线延迟 10 个交易日,60 日均线延迟 30 个交易日。平滑性越好,滞后越严重,这是一个绕不开的矛盾。
这次拆解的是一份券商金工专题报告,核心是低延迟趋势线 LLT:用二阶低通滤波器把延迟从约 19 个交易日压到接近零,同时保留平滑性。基于 LLT 的切线斜率择时被称作「交易性择时」——判断正确率往往低于 50%,但正确的时间占比很高,盈利来自趋势确立后的长持仓段。适合做指数择时、ETF 轮动和期货趋势策略的人,也适合想从信号处理角度重新设计趋势线的量化交易者。
2. 从 EMA 到 LLT:一阶滤波到二阶滤波的构造路径
2.1 EMA 就是一阶低通滤波器:时域递推与 Z 变换
报告首先讨论的是 EMA。EMA 的递推公式是:
EMA(T) = α·price(T) + (1-α)·EMA(T-1)
其中 α 是 0 到 1 之间的参数,α 越小,历史权重越高,曲线越平滑,延迟也越大。EMA 和 MA 的最大区别在于:MA 对窗口内所有价格等权,EMA 给靠近计算日的价格更高权重。这一加权方式在信号处理理论中有明确对应——它就是一阶低通滤波器。
要用频域角度看这个问题,需要用 Z 变换。Z 变换把离散时间序列映射到复频域,定义是 F(z) = Σ f(k)·z^(-k)。两个常用性质:线性性质和时移性质 f(k-m) ↔ z^(-m)F(z)。把 EMA 递推式两边做 Z 变换,输出是 EMA(z),输入是 price(z),得到传输函数:
H1(z) = EMA(z) / price(z) = α / [1 - (1-α)·z^(-1)]
传输函数描述的是任意频率下输出与输入的幅度比。令 z = e^(iω),ω=0 时 z=1,H1=1,常序列信号无衰减;ω=π(奈奎斯特频率)时 z=-1,H1=α/(2-α)。α=0.05 时该值约 0.0256,高频分量被大幅削减。这就是低通滤波的本质。
一阶滤波的问题在于两点:第一,通带到阻带的过渡带太长,有用的中频信号也被削弱;第二,延迟明显。按群延迟定义,α=0.05 的 EMA 在极低频附近有约 19 个交易日的延迟。EMA 用 α=2/(d+1) 换算成等效 MA 周期约 39 天,延迟和 39 日均线相当。所以 EMA 相对 MA 只是权重的改进,延迟问题没有根本解决。
2.2 修正 EMA 到二阶滤波:先构造高通,再补回低频
要构造低延迟趋势线,报告走的是另一条路:先构造高通滤波器,再从全频信号里减去高通,剩下就是低通。理论上,低通 H 和高通 1-H 是全频互补的。
直接对 EMA 的 H1 做 1-H1 会得到一个不干净的高通:因为 H1 在奈奎斯特频率处的增益不等于 0(α=0.05 时还有 0.0256),意味着高通输出没有包含全部高频。修正方法是把当日和前一日价格都取进来,递推式改为:
EMA'(T) = α·[price(T) + price(T-1)] / 2 + (1-α)·EMA'(T-1)
对应的传输函数 H2 = (α/2)(1+z^(-1)) / [1 - (1-α)z^(-1)],分子在 z=-1 时为零,所以奈奎斯特频率处增益为 0,高频完全保留。
一阶高通 H3=1-H2 的过渡带仍然偏宽。报告指出,阶数越高,截止频率附近衰减越快,但同时通带会变「不平」,靠近截止频率的信号会有放大。折中方案是二阶:串联两个一阶高通,H4=H3·H3。二阶高通在阻带衰减更快,能更好地区分高低频。由于全频互补,二阶低通就是 H5=1-H4,这就是 LLT 的构造来源。
这段涉及多个传输函数,放在一起看比较清晰:
| 符号 | 对应指标 | 角色 | 关键性质 |
|---|---|---|---|
| H1 | EMA | 一阶低通 | 奈奎斯特处增益 α/(2-α) 不归零 |
| H2 | 修正 EMA | 一阶高通的基础 | 奈奎斯特处增益为 0 |
| H3=1-H2 | —— | 一阶高通 | 过渡带偏宽 |
| H4=H3² | —— | 二阶高通 | 阻带衰减更快 |
| H5=1-H4 | LLT | 二阶低通 | 零频增益 1,延迟接近 0 |
2.3 LLT 传输函数、差分方程与 α-d 参数换算
把 H5 展开后,LLT 的传输函数写成一个二阶 IIR 滤波器的形式:
H5(z) = [α(4-α)/4 + (α²/2)·z^(-1) + (-α+3α²/4)·z^(-2)] / [1 - 2(1-α)·z^(-1) + (1-α)²·z^(-2)]
这个式子看着复杂,但它对应的时域差分方程很简洁。设 LLT(T) 是今天的趋势线值,price(T) 是今天的收盘价,则:
LLT(T) = c0·price(T) + c1·price(T-1) + c2·price(T-2) - d1·LLT(T-1) - d2·LLT(T-2)
其中 c0 = α(4-α)/4,c1 = α²/2,c2 = -α+3α²/4,d1 = -2(1-α),d2 = (1-α)²。代入 α=0.05,分子系数是 0.049375、0.00125、-0.048125,分母反馈系数是 -1.9 和 0.9025。注意 c2 是负的,这不是 bug,而是二阶高通互补后的固有结构:它让趋势线能紧跟拐点。
LLT 唯一的参数就是 α。报告给出了与 MA 的等效换算关系 α=2/(d+1),d 就是「相当于多少日 MA 周期」。α 越小,曲线越平滑,延迟相对变大,但即便 α 很小,零频附近的延迟也接近 0,远低于 MA 和 EMA。常用参数对照:
| d | α | 应用场景 |
|---|---|---|
| 20 | 0.0952 | 短周期、交易活跃 |
| 30 | 0.0645 | 上证指数择时偏优 |
| 60 | 0.0328 | 沪深 300、深证成指偏优 |
| 90 | 0.0222 | 长周期、低频过滤 |
注意 d 只是等效概念,LLT 递推并不真的使用 90 个交易日的窗口,而是每个交易日都用全部历史状态递推,这正是其延迟低的另一个原因。
3. 用 Python 复现 LLT:递推公式、切线斜率与参数扫描
3.1 从传输函数到递推:冷启动处理
LLT 是 IIR 滤波器,不能像 MA 一样用滚动窗口直接算,必须按时间步递推。核心实现如下:
import numpy as np import pandas as pd def llt(price: pd.Series, alpha: float) -> pd.Series: """低延迟趋势线 LLT:按二阶低通滤波器的差分方程递推""" c0 = alpha * (4 - alpha) / 4.0 c1 = alpha * alpha / 2.0 c2 = -alpha + 0.75 * alpha * alpha d1 = -2.0 * (1 - alpha) d2 = (1 - alpha) ** 2 out = np.full(len(price), np.nan) p = np.asarray(price, dtype=float) # 前两个点直接用价格初始化,避免递归冷启动产生尖刺 out[0] = p[0] out[1] = p[1] for i in range(2, len(price)): out[i] = ( c0 * p[i] + c1 * p[i - 1] + c2 * p[i - 2] - d1 * out[i - 1] - d2 * out[i - 2] ) return pd.Series(out, index=price.index)逻辑说明:c0、c1、c2 是输入价格的三个前馈系数,d1、d2 是历史趋势线的反馈系数。差分方程里 price 和 LLT 都要回溯两期,所以前两个点没有足够历史,直接拿价格本身初始化。有人习惯用 0 填充,这在递推初期会造成明显的下冲尖刺,滤波器要花很长时间才能稳定;用价格初始化可以让 LLT 从第一个交易日就贴在价格附近。
参数说明:alpha 可以直接传入,也可以用 alpha = 2 / (d + 1) 换算,比如 d=30 对应 alpha=0.064516。d 越大,c0 和 c2 的绝对值都会变小,曲线更平滑,递推惯性更强;d 从 20 变到 90 时,信号相位变化很小,这是 LLT 低延迟特性的直接体现。
3.2 切线斜率择时与回测骨架
LLT 在正常参数下是一条平滑曲线,可以近似看作处处可微。报告用向前差分计算切线斜率 k(T)=LLT(T)-LLT(T-1)。k>0 看多,k<0 看空,k=0 维持前向判断。回测骨架如下:
def llt_timing(price: pd.Series, alpha: float): tl = llt(price, alpha) slope = tl.diff().fillna(0.0) pos = np.where(slope > 0, 1, -1) pos = pd.Series(pos, index=price.index) # 第 T 日收盘后产生信号,作用于 T+1 日,避免未来函数 daily_ret = price.pct_change().fillna(0.0) strat_ret = daily_ret * pos.shift(1).fillna(0.0) nav = (1 + strat_ret).cumprod() return nav, tl, slope, pos逻辑说明:slope 是每天的切线斜率,np.where 把斜率映射为 +1 或 -1。pos.shift(1) 是关键一步——当天收盘价数据要等收盘后才能算出 LLT 和斜率,最快也只能次日执行,所以策略收益是「今日仓位乘明日收益」。如果不 shift,等于用了当天的未来信息,回测结果会虚高得很明显。
参数说明:这个框架做的是多空双向收益,k<0 时持空仓,k=0 按 -1 处理。原文回测没有计交易成本,目的是观察择时信号本身的价值。实盘必须把成本加进来,后面避坑部分会专门说。如果只做多头版本,把 pos 在斜率小于 0 时改为 0 即可,ETF 策略部分会用到。
3.3 参数扫描:d=20/30/60/90 的收益与回撤结构
LLT 只有一个参数 α,等价于 d。参数怎么选,直接决定信号频率和回撤结构。对同一段数据循环计算即可:
def scan_llt(price: pd.Series, d_list=(20, 30, 60, 90)): stats = {} for d in d_list: alpha = 2.0 / (d + 1) nav, tl, slope, pos = llt_timing(price, alpha) ret = nav.pct_change().fillna(0) mdd = (nav / nav.cummax() - 1).min() stats[d] = { "累积收益": nav.iloc[-1] - 1, "年化波动": ret.std() * np.sqrt(252), "最大回撤": mdd, "交易次数": int((pos.diff() != 0).sum()), } return pd.DataFrame(stats).T报告用上证指数 2005 年 9 月 6 日至 2013 年 6 月 28 日的数据回测,暂不计成本。统计结果如下:
| d | 择时次数 | 累积收益率 | 收益率标准差 | 盈利比率 | 最大回撤 | 平均盈亏比 |
|---|---|---|---|---|---|---|
| 20 | 182 | 528.27% | 8.29% | 36.81% | -26.96% | 3.32 |
| 30 | 130 | 1076.47% | 9.67% | 41.54% | -21.35% | 3.91 |
| 60 | 96 | 683.55% | 10.51% | 37.50% | -29.80% | 4.10 |
| 90 | 81 | 407.27% | 17.80% | 27.16% | -26.94% | 7.73 |
几个值得注意的点。d=30 在四项里综合最优:收益最高、回撤适中、盈亏比 3.91。d=20 交易最频繁(182 次),信号灵敏度高,但回撤也最大,因为震荡市里来回止损多。d=90 交易次数最少,单笔盈利最大(盈亏比 7.73),但正确率只有 27.16%,累积收益反而最低。这说明 LLT 参数选择不是越灵敏越好,而是要匹配标的的波动周期。
4. 交易性择时的收益结构:低正确率、高盈亏比与长期持仓
4.1 MA 择时的延迟代价:看着平滑,实则滞后
报告先跑了一组传统 MA 的切线择时作为基准。同样是上证指数、同样的回测区间、同样按收盘价成交、暂不计成本,MA20、MA30、MA60、MA90 的择时效果如下:
| 均线 | 择时次数 | 累积收益率 | 收益率标准差 | 盈利比率 | 最大回撤 | 平均盈亏比 |
|---|---|---|---|---|---|---|
| MA20 | 136 | 348.84% | 8.97% | 36.76% | -30.88% | 3.36 |
| MA30 | 118 | 324.21% | 10.55% | 33.90% | -33.45% | 4.04 |
| MA60 | 50 | 669.69% | 28.29% | 36.00% | -15.90% | 9.48 |
| MA90 | 58 | 1143.20% | 47.21% | 46.55% | -13.92% | 11.71 |
有意思的是,MA90 累积收益 1143% 是所有 MA 里最高的,回撤也只有 -13.92%,但收益率标准差高达 47.21%,几乎贴着单边持有的波动水平。原因不难理解:90 日均线信号极少,大部分时间仓位不动,择时次数只有 58 次,本质上是一个低换手的长周期过滤器。反过来,MA20、MA30 交易频繁,但每次都受延迟拖累——等斜率翻转向下时价格已经跌了一段,等斜率转上时反弹也没赶上,所以收益一般、回撤却不小。
报告还指出一个现象:2010 年之后,市场波动收窄,MA 择时的有效性显著下降。长均线延迟太大导致信号太钝,短均线又容易被噪声欺骗,传统均线的平滑性—延迟矛盾在这个阶段暴露得特别明显。对比表 1 和表 2 可以看出,LLT 在同样区间内不仅收益分布更稳定,而且在 2010 年之后的震荡环境里仍然能维持盈利,这是 MA 做不到的。
4.2 「交易性择时」的本质:正确率低于 50% 依然赚钱
对比两组统计可以发现一个反直觉的结论:LLT 择时的盈利比率全部低于 50%,d=30 只有 41.54%,但累积收益反而最高。这不是统计误差,而是这类「交易性择时」的固有结构。
原因在切线法本身。趋势拐点附近,LLT 的斜率会在零轴附近来回穿越,每次穿越都产生一次方向判断。这些判断大部分是错的,但因为斜率刚从正变负时价格往往刚开始下跌,空仓或做空的损失很小,等于内嵌了一个止损机制。一旦趋势真正确立,比如一轮持续上涨行情,LLT 斜率会稳定为正,持仓可以持续很长时间,盈利主要来自这些长持仓段。
所以交易性择时看重的不是「判断正确的次数比例」,而是「判断正确的时间占比」。用报告的话说:判断正确率相对较低时,判断正确的时间占比却往往较高,盈利主要来自这一部分贡献。这是和我平时习惯的「先看胜率再看盈亏比」完全不同的评估方式——对这类策略,平均盈亏比和正确时间占比比胜率重要得多。
4.3 不同指数有不同最优参数:上证 d=30,沪深 300 与深证成指 d=60~70
模型是否稳健,要在不同标的上验证。报告把 LLT 择时分别跑在上证指数、沪深 300 和深证成指上,结论是模型整体都有效,收益分布比 MA 更稳定,但最优参数因指数而异。沪深 300 和深证成指在 d=60~70 时累积收益最高,上证指数则在 d=30 附近较优,每个指数的最优 d 并不一样。
一个合理的解释是不同指数的波动结构不同。上证指数历史上有更多快牛慢熊和长期横盘,短周期信号更能捕捉阶段性行情;沪深 300 和深证成指里权重股占比高,趋势持续时间更长,需要较大 d 来过滤噪声。这也意味着,LLT 做策略落地时不能「一个参数打天下」。我的习惯是先把候选标的按波动特征分组,每组在样本内做 d 从 20 到 90 的滚动寻优,观察收益—回撤曲面是否平滑;如果最优参数附近邻居的表现相差很大,说明这个参数不稳健,宁可换标的也不要硬用。
5. 从指数到 ETF:LLT 择时落地与避坑清单
5.1 ETF 趋势交易结构:多空双向与只做多
报告最后把 LLT 用于 ETF 趋势交易。ETF 的交易成本结构比个股更友好,且跟踪指数、没有个股暴雷风险,非常适合做日线级别的趋势切换。策略流程不复杂:
- 取 ETF 日线收盘价,计算 LLT(d),d 先用标的所跟踪指数的历史波动特征来初选(比如宽基 ETF 从 d=30 开始试);
- 每日收盘后计算切线斜率 k=LLT(T)-LLT(T-1);
- 只做多版本:k>0 持有 ETF,k<0 空仓;
- 多空双向版本:k>0 持有或做多,k<0 做空或持有反向仓位;
- 次日执行调仓,记录每次调仓的滑点和费用。
报告对 ETF 的多空双向和只做多都做了回测,结论是两种形态都能获得良好风险收益表现。只做多版本的优势是换手低、在单边下跌市里只承受空仓的机会成本而不是做空摩擦;双向版本在震荡和下跌段收益更好,但对执行要求更高。对普通投资者,我会更推荐只做多版本,先把「空仓也是一种仓位」这个纪律练好,再考虑双向。
5.2 避坑排查:五个典型坑与解决
按遇到频率排列:
现象:LLT 递推前几十天曲线明显偏离价格,甚至出现巨大尖刺。 原因:初始化用了 0 或缺失值,二阶递归需要两个历史状态。 解决:前两个点直接用价格初始化,如 3.1 节代码所示。如果仍然不稳,可以把前 50 个交易日的 LLT 只用于热身,不产生信号。
现象:斜率和零轴纠缠,信号频繁翻转,一周内开平仓多次。 原因:切线法在拐点附近天然存在斜率零震荡,LLT 延迟低反而让噪声更容易触发信号。 解决:加斜率过滤带——当 |k| 小于某个阈值(比如最近 20 个交易日 LLT 振幅的 5%)时保持原仓位;或者对 k 再做一次 3~5 日简单平均,把微观毛刺滤掉再判断方向。
现象:回测收益高得离谱,年化翻倍往上。 原因:大概率用了未来函数,比如当天收盘后用同一根收盘价算出信号又用当天收盘价成交,等于提前知道结果。 解决:严格按「T 日收盘出信号,T+1 日执行」的时序,回测中把仓位序列 shift(1) 后再和日收益相乘。检查这一步最直接的方法是随机打乱信号序列,看收益是否应声下降。
现象:同一套参数在上证、沪深 300 和 ETF 上效果天差地别。 原因:每个标的的波动周期不同,最优 d 不同,属于正常现象,不代表模型失效。 解决:把参数寻优限制在样本内,d 的候选池固定为 20~90,选完后在样本外只跑一次。不要在同一个数据集上反复试参数,那是在拟合噪声。
现象:模拟盘稳定盈利,实盘 ETF 收益明显缩水。 原因:回测没算交易成本、滑点和冲击成本。LLT d=20 时一年可能交易上百次,每次按双边费用加一两个 tick 滑点,累积损耗非常可观。 解决:在回测里按真实能拿到的佣金率建模,滑点按 ETF 盘口最小变动价位的一半到一倍计提;如果扣完成本策略从正收益变成负收益,说明信号频率太高,加大 d 或改用只做多版本。
注意:第 2 条和第 4 条是 LLT 用得最多的地方。斜率过滤带解决的是信号抖动,参数分组解决的是标的适配,两者叠加之后,LLT 在 ETF 上的实盘体验会接近回测,而不是「回测横盘、实盘打脸」。
6. 进阶:把 LLT 作为信号预处理层,双周期确认与样本外验证
LLT 最大的价值不是直接给买卖点,而是把价格序列清洗成一条低延迟的趋势基准线。一旦有了这条基准线,很多策略组件都可以换上去。这里给一个除了切线法之外最常用的进阶用法:双周期确认。
双周期确认的思路是同时计算两条不同参数的 LLT,比如 d=30 和 d=60。只有当两条趋势线的斜率方向一致时才开仓,方向不一致时空仓或维持前向判断。这样可以过滤掉大量只有短周期信号支持、长周期还在纠结的中间状态。代价是信号次数变少,收益通常略低于单条 d=30,但回撤和交易频率都会有明显改善,实盘友好度更高。
def dual_llt_signal(price, d_fast=30, d_slow=60): fast = llt(price, 2.0 / (d_fast + 1)).diff().fillna(0) slow = llt(price, 2.0 / (d_slow + 1)).diff().fillna(0) agree = np.sign(fast) == np.sign(slow) pos = np.where(agree & (fast > 0), 1, np.where(agree & (fast < 0), -1, 0)) return pd.Series(pos, index=price.index)逻辑上,这个信号只在高频和低频趋势线同向时输出仓位,方向不一致输出 0,天然规避了切线法的零轴震荡问题。参数说明:d_fast 和 d_slow 的间隔不要超过一倍(比如 30 和 60 合适,30 和 120 就容易出现长周期一直压着短周期、信号总出不来)。用最近 3~5 年的指数数据测试,双周期版本的交易次数通常只有单周期的三分之一到二分之一,最大回撤能收窄 5 个百分点以上,代价是累积收益略低几个点。对于不愿意盯盘的交易者,这个交换很划算。
另一个要养成的习惯是样本外验证。记得有段时间我习惯把参数扫描放在全历史样本上,找出最优 d 后直接拿同一样本的回测收益做复盘,自我感觉良好;后来把同样的参数挪到后一段数据上,收益直接腰斩。从那以后我每次做 LLT 或者任何带参数的择时,都会强制把时间轴切成两段:前一段只用来选参数,后一段只跑一次、记录一次、不复调参。筛选出来的 d 值会落在 20~90 之间一个平台上而不是孤峰,我才敢拿它上模拟盘。这个习惯帮我避开了很多「回测漂亮、实盘翻车」的坑,希望帮到你。
本文还有配套的精品资源,点击获取