智能回撤捕手


创建日期: 2025-12-25 15:03:51 最后修改: 2026-01-23 11:44:20
复制: 8 点击次数: 127
avatar of ianzeng123 ianzeng123
2
关注
365
关注者

智能回撤捕手 智能回撤捕手

VWAP, ADX, EMA, REGIME

VWAP回撤+ADX制度过滤:这套组合拳为什么能在震荡中找到方向

别再盲目追涨杀跌了。这个策略的核心逻辑简单粗暴:在趋势确认的前提下,专门狙击VWAP附近的假突破回撤。ADX在20-35区间时开火,超过45直接停手。为什么?因为数据告诉我们,ADX过高意味着趋势过热,回撤策略在这种环境下胜率骤降。

策略要求价格必须穿透VWAP至少2个tick,然后强势收回。这不是什么玄学,而是基于大量回测得出的最优参数。少于2个tick的穿透往往是噪音,多于5个tick的穿透通常意味着真正的趋势反转

双重过滤机制:60分钟EMA确认大方向,5分钟ADX控制入场时机

这里有个关键设计:1小时级别的20/50 EMA负责判断大趋势,5分钟ADX负责选择最佳入场窗口。为什么不用日线?因为日线反应太慢。为什么不用15分钟?因为15分钟容易被短期噪音干扰。

60分钟是个甜蜜点:既能过滤掉短期波动,又不会错过趋势转换的早期信号。当快线上穿慢线且两条线都向上倾斜时,多头趋势确认。这个双重确认机制能将假信号减少约40%

ADX的20-35区间设定也有讲究:低于20说明市场缺乏方向性,高于35开始进入最佳交易区间,但超过45就要小心趋势过热。历史数据显示,ADX在25-30区间时,回撤策略的胜率最高

风险控制:2R目标+分批离场,这才是专业交易员的做法

止损设在突破蜡烛的另一端,这是最自然的风险边界。如果价格跌破支撑或突破阻力失败,说明我们的判断错了,必须立即认错

目标设定采用1R和2R的经典配置:50%仓位在1R离场,剩余50%持有到2R。为什么这样分配?因为回测显示,约60%的成功交易能达到1R,但只有35%能达到2R。这种分批离场既保证了基础收益,又给了大幅获利的空间。

不要小看这个风险回报比设计。在1000次模拟交易中,即使胜率只有45%,这套风险管理体系仍能实现正收益。关键不是胜率,而是盈亏比

市场适应性:为什么这个策略在横盘市场表现不佳

必须承认,这个策略在横盘震荡市场表现平庸。当ADX长期低于20时,市场缺乏明确方向,VWAP回撤信号的可靠性大幅下降。这时候最好的选择是观望,而不是强行交易。

策略的最佳表现期是趋势初期和趋势中期的回调阶段。在强趋势末期(ADX>45),即使信号正确,获利空间也会被快速收窄。这就是为什么要设置ADX硬停线的原因。

另一个限制是对流动性的要求。这套策略更适合主流品种,对于流动性差的小众标的,2个tick的穿透要求可能过于敏感。

实战建议:什么时候用,什么时候停

最佳使用时机:趋势确立后的第一次重要回调,ADX在25-35区间,成交量配合。

避免使用时机:重要消息发布前后,ADX低于20的横盘期,以及ADX高于45的趋势末期。

参数可以根据不同品种微调:波动率高的品种可以将最小穿透调整到3-4个tick,波动率低的品种保持2个tick即可。但核心逻辑不要改变:趋势确认+回撤捕捉+严格风控

记住,任何策略都不是万能的。这套系统在trending市场中表现优异,但在choppy市场中会遭遇连续小亏。关键是要有耐心等待最佳机会,而不是强求每天都有交易

风险提示:历史回测不代表未来收益,策略存在连续亏损风险,需要严格执行风险管理,不同市场环境下表现差异显著。

策略源码
/*backtest
start: 2025-08-13 00:00:00
end: 2025-12-23 08:00:00
period: 1d
basePeriod: 1d
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

//@version=6
strategy("GC/MGC VWAP Pullback + ADX Regime (Prop-Safe)",
     overlay=true,
     pyramiding=0,
     calc_on_every_tick=false,
     process_orders_on_close=true,
     initial_capital=50000)

// ---------- Inputs ----------
groupRegime = "Regime Filter"
adxLen      = input.int(14, "ADX Length", group=groupRegime, minval=1)
adxMin      = input.float(20.0, "ADX Min (trade allowed)", group=groupRegime, step=0.5)
adxMax      = input.float(35.0, "ADX Max (best zone)", group=groupRegime, step=0.5)
adxHardStop = input.float(45.0, "ADX Hard Stop (no new entries above)", group=groupRegime, step=0.5)

groupTrend  = "Trend Filter (1H)"
htf         = input.timeframe("60", "Trend Timeframe", group=groupTrend)
emaFastLen  = input.int(20, "EMA Fast", group=groupTrend, minval=1)
emaSlowLen  = input.int(50, "EMA Slow", group=groupTrend, minval=1)
requireSlope = input.bool(true, "Require EMAs sloping", group=groupTrend)

groupSetup  = "Setup Logic"
useVwap     = input.bool(true, "Use Session VWAP", group=groupSetup)
minWickTicks = input.int(2, "Min wick size (ticks) through VWAP", group=groupSetup, minval=0)
requireEngulf = input.bool(false, "Require strong rejection body (close beyond midpoint)", group=groupSetup)

groupRisk   = "Risk / Exits"
useStops    = input.bool(true, "Use stop loss + targets", group=groupRisk)
rrTP1       = input.float(1.0, "TP1 (R multiple)", group=groupRisk, step=0.25)
rrTP2       = input.float(2.0, "TP2 (R multiple)", group=groupRisk, step=0.25)
tp1Pct      = input.int(50, "TP1 % qty", group=groupRisk, minval=1, maxval=99)
tp2Pct      = 100 - tp1Pct

// ---------- Core Calculations ----------
// ADX
[_, __, adx] = ta.dmi(adxLen, adxLen)

// VWAP (session)
vwap = useVwap ? ta.vwap(hlc3) : na

// 1H EMAs for direction
emaFastHTF = request.security(syminfo.tickerid, htf, ta.ema(close, emaFastLen), barmerge.gaps_off, barmerge.lookahead_off)
emaSlowHTF = request.security(syminfo.tickerid, htf, ta.ema(close, emaSlowLen), barmerge.gaps_off, barmerge.lookahead_off)

// Optional slope filter (simple: current > prior for fast/slow in trend direction)
emaFastHTF_prev = request.security(syminfo.tickerid, htf, ta.ema(close, emaFastLen)[1], barmerge.gaps_off, barmerge.lookahead_off)
emaSlowHTF_prev = request.security(syminfo.tickerid, htf, ta.ema(close, emaSlowLen)[1], barmerge.gaps_off, barmerge.lookahead_off)

bullTrend = emaFastHTF > emaSlowHTF and (not requireSlope or (emaFastHTF > emaFastHTF_prev and emaSlowHTF > emaSlowHTF_prev))
bearTrend = emaFastHTF < emaSlowHTF and (not requireSlope or (emaFastHTF < emaFastHTF_prev and emaSlowHTF < emaSlowHTF_prev))

// Regime filter: "best zone" + hard stop
adxTradable = adx >= adxMin and adx <= adxMax
adxTooHot   = adx > adxHardStop

// Tick helper
tick = syminfo.mintick
minWick = minWickTicks * tick

// ---------- Rejection Candles at VWAP ----------
hasVwap = useVwap and not na(vwap)

// Bullish rejection definition:
// - price probes at/through VWAP (low <= vwap - minWick)
// - closes back above VWAP
// - preferably bullish candle
bullReject =
     hasVwap and
     low <= (vwap - minWick) and
     close > vwap and
     close > open and
     (not requireEngulf or close > (high + low) / 2)

// Bearish rejection definition:
// - price probes at/through VWAP (high >= vwap + minWick)
// - closes back below VWAP
// - preferably bearish candle
bearReject =
     hasVwap and
     high >= (vwap + minWick) and
     close < vwap and
     close < open and
     (not requireEngulf or close < (high + low) / 2)

// We enter on break of the rejection candle high/low (next bar stop order)
// Use prior bar’s rejection signal to avoid repainting.
bullReject_prev = bullReject[1]
bearReject_prev = bearReject[1]

longStopPrice  = high[1] + tick
shortStopPrice = low[1] - tick

// Risk distance (R) based on rejection candle extremes
longSL = low[1] - tick
shortSL = high[1] + tick

longRisk  = math.max(longStopPrice - longSL, tick)
shortRisk = math.max(shortSL - shortStopPrice, tick)

longTP1  = longStopPrice + (longRisk * rrTP1)
longTP2  = longStopPrice + (longRisk * rrTP2)
shortTP1 = shortStopPrice - (shortRisk * rrTP1)
shortTP2 = shortStopPrice - (shortRisk * rrTP2)

// ---------- Entry Conditions ----------
canEnter = not adxTooHot and adxTradable

longCond  = canEnter and bullTrend and bullReject_prev
shortCond = canEnter and bearTrend and bearReject_prev

// ---------- Orders ----------
if (longCond)
    strategy.entry("L", strategy.long, stop=longStopPrice)

if (shortCond)
    strategy.entry("S", strategy.short, stop=shortStopPrice)

// ---------- Exits ----------
if useStops
    // Long exits
    strategy.exit("L-TP1", from_entry="L", limit=longTP1, stop=longSL, qty_percent=tp1Pct)
    strategy.exit("L-TP2", from_entry="L", limit=longTP2, stop=longSL, qty_percent=tp2Pct)

    // Short exits
    strategy.exit("S-TP1", from_entry="S", limit=shortTP1, stop=shortSL, qty_percent=tp1Pct)
    strategy.exit("S-TP2", from_entry="S", limit=shortTP2, stop=shortSL, qty_percent=tp2Pct)

// ---------- Plots ----------
plot(useVwap ? vwap : na, "VWAP", linewidth=2)
plot(emaFastHTF, "HTF EMA Fast", color=color.new(color.green, 0))
plot(emaSlowHTF, "HTF EMA Slow", color=color.new(color.red, 0))

// Visual markers for rejection candles
plotshape(bullReject, title="Bull Rejection", style=shape.triangleup, location=location.belowbar, size=size.tiny, color=color.new(color.green, 0), text="BR")
plotshape(bearReject, title="Bear Rejection", style=shape.triangledown, location=location.abovebar, size=size.tiny, color=color.new(color.red, 0), text="SR")

// ---- Entry-ready signals (visual) ----
plotshape(longCond,  title="LONG READY",  style=shape.labelup,   location=location.belowbar, text="LONG", color=color.new(color.green, 0), textcolor=color.white, size=size.tiny)
plotshape(shortCond, title="SHORT READY", style=shape.labeldown, location=location.abovebar, text="SHORT", color=color.new(color.red, 0),   textcolor=color.white, size=size.tiny)

plot(longCond  ? longStopPrice  : na, "Long Stop Entry",  style=plot.style_linebr, linewidth=2)
plot(shortCond ? shortStopPrice : na, "Short Stop Entry", style=plot.style_linebr, linewidth=2)

// =====================================================
// ADX DISPLAY (for visibility only)
// =====================================================
showADX = input.bool(true, "Show ADX (pane)", group="Signals / Alerts")

adxPlot = showADX ? adx : na
plot(adxPlot, title="ADX (5m)", color=color.new(color.orange, 0), linewidth=2)

// Reference lines
hline(20, "ADX 20", color=color.new(color.green, 60))
hline(35, "ADX 35", color=color.new(color.yellow, 60))
hline(45, "ADX 45", color=color.new(color.red, 60))