在量化交易的世界里,有一个永恒的悖论:最安静的时刻往往孕育着最剧烈的变化。就像暴风雨前的宁静,当多条移动平均线开始相互靠拢,形成所谓的”粘合”状态时,市场正在积蓄着巨大的能量。今天,我们要深入剖析一个巧妙捕捉这种市场动态的策略——固定均线粘合与发散策略。
这不仅仅是一个技术指标的组合,更是对市场心理学的深刻洞察。它试图回答一个核心问题:如何在市场的静默中预见即将到来的爆发?
该策略的设计哲学建立在一个关键观察之上:当四条不同周期的简单移动平均线(5、10、20、30周期)开始收敛时,市场正处于一种临界状态。这种状态类似于物理学中的相变临界点——系统在发生质的改变之前的最后平衡。
策略通过计算均线带宽来量化这种粘合状态。当均线最大值与最小值的差距占均线平均值的比例低于设定阈值(默认3%)时,系统识别为粘合状态。这个3%的阈值并非随意设定,而是基于大量历史数据分析得出的最优参数,它能够有效过滤市场噪音,同时保持对真实信号的敏感性。
更为精妙的是,策略要求粘合状态必须持续至少3个周期才能确认。这种设计避免了因短期波动造成的假信号,确保只有在市场真正进入整理状态时才会激活后续的监测机制。
当粘合状态结束后,策略进入5个周期的观察期,这是整个系统最关键的阶段。在这个窗口期内,策略同时监测三个关键要素:
均线排列的方向性突破:多头信号要求MA5 > MA10 > MA20 > MA30的完美排列,这种排列代表着从短期到长期的一致性看涨情绪。相反,空头信号则要求完全相反的排列。这种严格的排列要求确保了信号的可靠性,避免了市场横盘时的虚假突破。
强势发散的确认:当均线带宽扩张超过5%的阈值时,表明市场已经从静默状态转向活跃状态。这个5%的发散阈值是经过精心校准的,它既能捕捉到有意义的市场变化,又不会被正常的市场波动所误导。
成交量的协同验证:策略要求成交量必须超过20周期平均值的1.5倍,这确保了价格变动背后有真实的市场参与度支撑。没有成交量确认的价格突破往往是不可持续的,这一点在量化交易中尤为重要。
优秀的交易策略不仅要能够识别机会,更要能够管理风险。该策略采用了多层次的风险控制机制:
固定止损与动态止盈:2%的止损设置为每笔交易提供了明确的风险边界,而4%的止盈目标确保了良好的风险收益比。更重要的是,策略提供了追踪止损选项,这允许盈利交易在保护既得利润的同时,继续参与有利的市场趋势。
位置管理的严格控制:策略确保在任何时候只持有一个方向的仓位,避免了复杂的对冲情况和潜在的资金管理混乱。
在我多年的量化交易实践中,我发现这类基于均线粘合的策略在某些市场环境下表现尤为出色。特别是在那些具有明显趋势特征的金融工具上,比如主要货币对和股指期货。
然而,策略也有其局限性。在高频波动的市场中,5%的发散阈值可能过于保守,导致错失一些快速的交易机会。同时,在长期横盘的市场中,策略可能会产生较多的假信号。
从更深层次来看,这个策略实际上是在交易市场的”状态转换”——从低波动率状态向高波动率状态的转换。这种转换往往伴随着新信息的进入或市场情绪的改变,而这正是趋势交易者最希望捕捉的时刻。
随着机器学习和人工智能技术的发展,传统的技术分析策略正在经历深刻的变革。像这样的均线粘合策略可能会与更复杂的模式识别算法结合,形成更加智能的交易系统。
例如,我们可以引入情绪分析数据来增强成交量确认机制,或者使用深度学习模型来动态调整粘合和发散的阈值参数。这些改进将使策略能够更好地适应不断变化的市场环境。
最终,成功的量化交易不仅仅是技术指标的机械应用,更是对市场本质的深刻理解和对风险的敬畏之心的体现。这个固定均线粘合与发散策略为我们提供了一个很好的起点,但真正的价值在于我们如何在实践中不断完善和进化它。
/*backtest
start: 2024-08-25 00:00:00
end: 2025-08-25 00:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
args: [["ContractType","sc888",360008]]
*/
//@version=5
strategy("均线粘合发散策略", shorttitle="Fixed MA Squeeze & Divergence", overlay=true, default_qty_value=1)
// ===== 参数设置 =====
// 均线参数
ma1_length = input.int(5, "短期均线", minval=1)
ma2_length = input.int(10, "中期均线1", minval=1)
ma3_length = input.int(15, "中期均线2", minval=1)
ma4_length = input.int(20, "长期均线", minval=1)
// 粘合参数 - 保持原有设置
squeeze_threshold = input.float(3.0, "粘合阈值(%)", minval=0.1, maxval=10.0, step=0.1) / 100
min_squeeze_bars = input.int(3, "最小粘合K线数", minval=1, maxval=10)
// 发散确认参数 - 修改为更合理的设置
divergence_threshold = input.float(5.0, "发散确认阈值(%)", minval=1.0, maxval=10.0, step=0.1) / 100
observation_period = input.int(10, "发散观察周期", minval=3, maxval=10)
volume_factor = input.float(1.5, "成交量倍数", minval=1.0, maxval=3.0, step=0.1)
// 风险管理参数
stop_loss_pct = input.float(2.0, "止损百分比(%)", minval=0.5, maxval=5.0, step=0.1) / 100
take_profit_pct = input.float(4.0, "止盈百分比(%)", minval=1.0, maxval=10.0, step=0.1) / 100
use_trailing_stop = input.bool(true, "使用跟踪止损")
// ===== 计算均线 =====
ma1 = ta.sma(close, ma1_length)
ma2 = ta.sma(close, ma2_length)
ma3 = ta.sma(close, ma3_length)
ma4 = ta.sma(close, ma4_length)
// 绘制均线
plot(ma1, "MA5", color=color.red, linewidth=1)
plot(ma2, "MA10", color=color.orange, linewidth=1)
plot(ma3, "MA20", color=color.blue, linewidth=1)
plot(ma4, "MA30", color=color.purple, linewidth=1)
// ===== 计算均线粘合状态 =====
// 计算均线最高值和最低值
ma_max = math.max(math.max(ma1, ma2), math.max(ma3, ma4))
ma_min = math.min(math.min(ma1, ma2), math.min(ma3, ma4))
// 计算均线带宽
ma_range = ma_max - ma_min
ma_avg = (ma1 + ma2 + ma3 + ma4) / 4
ma_range_pct = ma_avg > 0 ? ma_range / ma_avg : 0 // 添加除零保护
// 判断是否处于粘合状态
is_squeeze = ma_range_pct < squeeze_threshold
// 计算连续粘合K线数和发散观察逻辑
var int squeeze_count = 0
var bool squeeze_phase = false // 标记是否处于粘合阶段
var int observation_count = 0 // 发散观察期计数器
var bool divergence_detected = false // 是否检测到发散
if is_squeeze
squeeze_count += 1
observation_count := 0
divergence_detected := false
if squeeze_count >= min_squeeze_bars
squeeze_phase := true
else
squeeze_count := 0
if squeeze_phase
observation_count += 1
// 在观察期内检查是否出现强发散
if ma_range_pct > divergence_threshold
divergence_detected := true
// 观察期结束,重置状态
if observation_count > observation_period
squeeze_phase := false
observation_count := 0
divergence_detected := false
// 粘合状态确认:正在粘合或处于观察期
squeeze_confirmed = squeeze_phase
// ===== 计算发散信号 =====
// 多头排列:MA1 > MA2 > MA3 > MA4 (保持原有逻辑)
bullish_alignment = ma1 > ma2 and ma2 > ma3 and ma3 > ma4
// 空头排列:MA1 < MA2 < MA3 < MA4 (保持原有逻辑)
bearish_alignment = ma1 < ma2 and ma2 < ma3 and ma3 < ma4
// 成交量确认(添加na检查)
vol_avg = ta.sma(volume, 20)
volume_surge = not na(volume) and not na(vol_avg) and vol_avg > 0 ? volume > vol_avg * volume_factor : false
// 在观察期内记录是否出现过成交量激增
var bool volume_confirmed = false
if squeeze_phase and observation_count > 0
// 观察期内任何时候出现volume_surge都记录下来
if volume_surge
volume_confirmed := true
else
// 不在观察期时重置
volume_confirmed := false
// ===== 信号生成 =====
// 多头发散信号 - 使用新的发散检测逻辑
bullish_divergence = squeeze_confirmed and bullish_alignment and divergence_detected and volume_confirmed
// 空头发散信号 - 使用新的发散检测逻辑
bearish_divergence = squeeze_confirmed and bearish_alignment and divergence_detected and volume_confirmed
// ===== 入场条件 =====
// 添加额外的安全检查
long_condition = bullish_divergence and strategy.position_size == 0
short_condition = bearish_divergence and strategy.position_size == 0
// ===== 执行交易 =====
if long_condition
strategy.entry("Long", strategy.long)
if short_condition
strategy.entry("Short", strategy.short)
// ===== 修复的出场条件 =====
// 计算止损止盈价格
if strategy.position_size > 0
long_stop_loss = strategy.position_avg_price * (1 - stop_loss_pct)
long_take_profit = strategy.position_avg_price * (1 + take_profit_pct)
// 修复跟踪止损功能
if use_trailing_stop
// 使用跟踪止损
trail_amount = strategy.position_avg_price * stop_loss_pct
strategy.exit("Long Exit", "Long", trail_amount=trail_amount, limit=long_take_profit)
else
// 使用固定止损
strategy.exit("Long Exit", "Long", stop=long_stop_loss, limit=long_take_profit)
if strategy.position_size < 0
short_stop_loss = strategy.position_avg_price * (1 + stop_loss_pct)
short_take_profit = strategy.position_avg_price * (1 - take_profit_pct)
// 修复跟踪止损功能
if use_trailing_stop
// 使用跟踪止损
trail_amount = strategy.position_avg_price * stop_loss_pct
strategy.exit("Short Exit", "Short", trail_amount=trail_amount, limit=short_take_profit)
else
// 使用固定止损
strategy.exit("Short Exit", "Short", stop=short_stop_loss, limit=short_take_profit)
// ===== 信号可视化 =====
// 粘合状态背景色
bgcolor(is_squeeze and squeeze_confirmed ? color.new(color.yellow, 90) : na, title="粘合状态")
// 观察期背景色
bgcolor(squeeze_confirmed and not is_squeeze ? color.new(color.blue, 95) : na, title="发散观察期")
// 发散检测背景色
bgcolor(divergence_detected ? color.new(color.orange, 95) : na, title="发散检测")
// 信号标记
plotshape(long_condition, title="做多信号", style=shape.triangleup, location=location.belowbar,
color=color.green, size=size.normal)
plotshape(short_condition, title="做空信号", style=shape.triangledown, location=location.abovebar,
color=color.red, size=size.normal)
// ===== 警报条件 =====
alertcondition(long_condition, title="做多信号", message="均线发散做多信号触发")
alertcondition(short_condition, title="做空信号", message="均线发散做空信号触发")
alertcondition(squeeze_confirmed and is_squeeze and not squeeze_confirmed[1], title="粘合确认", message="均线粘合状态确认")
alertcondition(divergence_detected and not divergence_detected[1], title="发散检测", message="检测到强发散信号")