Estratégia de média de escada de tendência: como "ficar estável" elegantemente quando o mercado vai para os lados?

ADX EMA ATR DI
Data de criação: 2025-10-09 14:28:37 última modificação: 2025-10-09 14:28:37
cópia: 0 Cliques: 268
2
focar em
319
Seguidores

Estratégia de média de escada de tendência: como “ficar estável” elegantemente quando o mercado vai para os lados? Estratégia de média de escada de tendência: como “ficar estável” elegantemente quando o mercado vai para os lados?

Por que as estratégias tradicionais de rastreamento de tendências são usadas com frequência em mercados em turbulência?

Como praticante de negociação quantitativa, muitas vezes me perguntam: por que as estratégias que funcionam bem em mercados de tendência começam a recuar drasticamente quando as coisas ficam turbulentas?

A resposta é simples: a maioria das estratégias de acompanhamento de tendências sofre de “obsessão por tendências” e tenta manter a alta frequência de negociação em qualquer ambiente de mercado, ignorando um fato básico:O mercado está 70% do tempo em um estado de oscilação horizontal

A estratégia de média escalonada, que vamos analisar hoje, oferece uma solução interessante para esse problema:Seguir de forma ativa no mercado de tendências, “deitar-se com elegância” no mercado de turbulências

O que é uma média escalonada e como ela redefine o rastreamento de tendências?

As estratégias tradicionais de médias móveis têm um defeito fatal: elas estão sempre mudando. Seja uma forte tendência do mercado ou uma oscilação horizontal, as médias se ajustam constantemente com a oscilação dos preços, o que resulta em uma grande quantidade de falsos sinais.

A ideia central da Escadaria Média é:Deixar o equilíbrio “congelar” em determinadas condições

A lógica da implementação é a seguinte:

  1. Detecção de estado de tendênciaO indicador ADX é usado para avaliar a intensidade das tendências de mercado.

    • ADX > 25: Mercado de tendência forte
    • Escalada média < 0,3%: mercado horizontal
  2. Mudança linear dinâmica

    • Quando a tendência é forte: seguimento normal da EMA 21
    • Quando horizontal: a linha uniforme “congela” na posição horizontal, formando suporte/resistência

O que é interessante sobre este design é que:Isso permite que as estratégias apresentem diferentes “caracteres” em diferentes cenários de mercado.O Bitcoin é sensível a tendências e estável a oscilações.

Como é que um sistema de captura de tendências funciona?

Além do mecanismo básico de escalação, a estratégia também integra um módulo de “captura de tendências”, que eu considero a parte mais inovadora:

Mecanismo de retorno rápido

  • A tendência para a queda do dólar, que tinha acabado de se equilibrar, se reverteu.
  • Construir posições rápidas em 3 ciclos
  • Condição: ADX > 30 e diferença entre DI+ e DI- > 10

O projeto resolve um dos principais problemas das estratégias tradicionais:Como ajustar posições rapidamente no início de uma reversão de tendência

Imagine uma situação em que você acabou de liquidar uma posição multi-cabeça devido a um stop loss e o resultado é uma forte tendência de queda imediata. A estratégia tradicional pode precisar de esperar a confirmação de novos sinais, mas este sistema de “captura de tendências” é capaz de estabelecer rapidamente posições em branco em 3 ciclos.

Gestão de Riscos: Por que distinguir o estado do mercado?

A melhor coisa que se pode aprender com esta estratégia é queMecanismos diferenciados de gestão de riscos

Controle de risco no mercado horizontal

  • Ajustamento do ponto de parada para perto da linha média da escada
  • Redução do ATR e restrição de perdas
  • A configuração do alvo é mais conservadora

Controle de risco em mercados de tendência

  • Cessação de prejuízos com o ATR padrão
  • Ativar parada de movimentação em escala
  • Permitindo maior espaço para os preços flutuarem

O projeto reflete uma importante filosofia de negociação:Diferentes ambientes de mercado requerem diferentes preferências de riscoNo mercado horizontal, devemos ser mais cautelosos; no mercado de tendência, precisamos dar mais espaço para os lucros.

Stop Loss Moving by Ladder: Como equilibrar proteção de lucros e acompanhamento de tendências?

O tradicional stop loss móvel tende a ser muito mecanizado, ou muito apertado, o que leva a saídas prematuras, ou muito relaxado, o que não protege os lucros de forma eficaz. O stop loss móvel escalonado da estratégia oferece uma solução mais inteligente:

Localização lógica da escada

  • Escala de escadas com base em ATR dinâmico
  • Configure até 5 níveis de escadas
  • A cada escalada, o ponto de paragem aumenta.

A vantagem deste design é que:Ele pode proteger os lucros e, ao mesmo tempo, dar espaço suficiente para a tendência.

O que é preciso ter em conta na aplicação prática?

De acordo com a minha experiência no terreno, o uso de estratégias como esta exige algumas considerações:

  1. Parâmetros de optimização de armadilhasNão otimize demais os limites do ADX, os valores entre 25 e 30 são estáveis na maioria dos mercados

  2. Adaptabilidade do mercadoA estratégia é mais adequada para mercados moderadamente voláteis, que podem necessitar de ajustes nos múltiplos ATR em ambientes de extrema volatilidade.

  3. Gestão de fundosRecomenda-se que a posição individual não exceda 10% do capital total, especialmente quando a função de captura de tendências é ativada.

  4. Trapaça de detecçãoAtenção especial aos efeitos de slippage e comissões, especialmente em transações frequentes em mercados turbulentos.

Onde está o valor inovador dessa estratégia?

A estratégia de quantificação representa um importante passo em frente:Mudança de uma lógica única para uma adaptação de vários estados

A estratégia tradicional tende a tentar responder a todas as situações do mercado com um conjunto de lógicas fixas, enquanto esta mostra a sabedoria do “conveniente geográfico”:

  • Apresentar-se como um seguidor de tendências radical em um mercado de tendências
  • Como um trader de intervalos conservador em mercados turbulentos

O conceito de design é uma importante inspiração para os desenvolvedores de estratégias:Deveríamos ter estratégias capazes de “perceber o mercado”, e não executar cegamente lógica fixa.

Finalmente, é importante ressaltar que nenhuma estratégia é universal. Embora elegante em teoria, esta estratégia de média escalonada precisa ser adaptada em combinação com o ambiente de mercado específico e as preferências de risco pessoais.A melhor estratégia é sempre a estratégia que melhor se adequa a você.

Código-fonte da estratégia
/*backtest
start: 2024-10-09 00:00:00
end: 2025-10-07 08:00:00
period: 1d
basePeriod: 1d
exchanges: [{"eid":"Futures_Binance","currency":"SOL_USDT","balance":500000}]
*/

//@version=5
strategy("Trend Following Ladder Average Strategy", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=10)

// ═══════════════════════════════════════════════════════════════════════════════
// SETTINGS AND PARAMETERS
// ═══════════════════════════════════════════════════════════════════════════════

// Ladder Average Settings
ma_length = input.int(title="Average Period", defval=21, minval=5)
ma_type = input.string(title="Average Type", defval="EMA", options=["SMA", "EMA", "WMA"])

// Trend Strength Settings
adx_length = input.int(title="Trend Strength Period (ADX)", defval=14, minval=5)
trend_threshold = input.float(title="Trend Strength Threshold", defval=25.0, minval=10.0, step=5.0)
sideways_slope_threshold = input.float(title="Sideways Market Slope Threshold", defval=0.3, minval=0.1, step=0.1)

// Trend Catching Settings
enable_trend_catch = input.bool(title="Trend Catching System", defval=true)
trend_catch_adx_threshold = input.float(title="Trend Catch ADX Threshold", defval=30.0, minval=20.0, step=5.0)
trend_catch_di_diff = input.float(title="DI+ DI- Difference Threshold", defval=10.0, minval=5.0, step=2.5)
quick_entry_bars = input.int(title="Quick Entry Waiting Bars", defval=3, minval=1, maxval=10)

// ATR and Volatility Settings
atr_length = input.int(title="ATR Period", defval=14, minval=1)
atr_multiplier = input.float(title="ATR Multiplier", defval=2.0, minval=0.1, step=0.1)

// Ladder Trailing Stop Settings
ladder_step = input.float(title="Ladder Step Size (%)", defval=1.0, minval=0.1, step=0.1)
max_ladders = input.int(title="Maximum Ladder Count", defval=5, minval=2, maxval=10)

// Stop Loss and Take Profit Settings
use_stop_loss = input.bool(title="Use Stop Loss", defval=false)
use_take_profit = input.bool(title="Use Take Profit", defval=false)
use_trailing_stop = input.bool(title="Use Trailing Stop", defval=true)

sl_type = input.string(title="Stop Loss Type", defval="ATR", options=["ATR", "Percent", "Points"])
sl_atr_multiplier = input.float(title="SL ATR Multiplier", defval=2.0, minval=0.5, step=0.1)
sl_percent = input.float(title="SL Percent (%)", defval=2.0, minval=0.1, step=0.1)
sl_points = input.float(title="SL Points", defval=100, minval=1)

tp_type = input.string(title="Take Profit Type", defval="ATR", options=["ATR", "Percent", "Points", "Risk/Reward"])
tp_atr_multiplier = input.float(title="TP ATR Multiplier", defval=3.0, minval=0.5, step=0.1)
tp_percent = input.float(title="TP Percent (%)", defval=3.0, minval=0.1, step=0.1)
tp_points = input.float(title="TP Points", defval=150, minval=1)
tp_risk_reward = input.float(title="Risk/Reward Ratio", defval=2.0, minval=0.5, step=0.1)

// Horizontal Level Settings
horizontal_lookback = input.int(title="Horizontal Level Stabilization Period", defval=10, minval=3)

// ═══════════════════════════════════════════════════════════════════════════════
// INDICATORS AND CALCULATIONS
// ═══════════════════════════════════════════════════════════════════════════════

// ATR calculation
atr_value = ta.atr(atr_length)

// Moving Average calculation
ma_value = ma_type == "SMA" ? ta.sma(close, ma_length) : ma_type == "EMA" ? ta.ema(close, ma_length) : ma_type == "WMA" ? ta.wma(close, ma_length) : ta.ema(close, ma_length)

// ADX (Trend Strength) calculation - Manual calculation
tr = math.max(high - low, math.max(math.abs(high - close[1]), math.abs(low - close[1])))
plus_dm = high - high[1] > low[1] - low ? math.max(high - high[1], 0) : 0
minus_dm = low[1] - low > high - high[1] ? math.max(low[1] - low, 0) : 0
plus_di = 100 * ta.rma(plus_dm, adx_length) / ta.rma(tr, adx_length)
minus_di = 100 * ta.rma(minus_dm, adx_length) / ta.rma(tr, adx_length)
adx_value = 100 * ta.rma(math.abs(plus_di - minus_di) / (plus_di + minus_di), adx_length)

// MA slope calculation (for sideways market detection)
ma_slope = (ma_value - ma_value[5]) / ma_value[5] * 100

// Trend state detection
is_strong_trend = adx_value > trend_threshold
is_sideways_by_slope = math.abs(ma_slope) < sideways_slope_threshold
is_sideways = not is_strong_trend or is_sideways_by_slope

// Trend direction detection (DI+ vs DI-)
is_uptrend = plus_di > minus_di
is_downtrend = minus_di > plus_di
di_difference = math.abs(plus_di - minus_di)

// Strong trend momentum detection
strong_uptrend = adx_value > trend_catch_adx_threshold and plus_di > minus_di and di_difference > trend_catch_di_diff
strong_downtrend = adx_value > trend_catch_adx_threshold and minus_di > plus_di and di_difference > trend_catch_di_diff

// Position tracking system
var bool just_closed_long = false
var bool just_closed_short = false
var int bars_since_close = 0

// Position closure tracking - Fixed
if strategy.position_size == 0 and strategy.position_size[1] != 0
    if strategy.position_size[1] > 0
        just_closed_long := true
        just_closed_short := false
    else
        just_closed_short := true  
        just_closed_long := false
    bars_since_close := 0
else if strategy.position_size == 0
    bars_since_close += 1
    if bars_since_close > quick_entry_bars
        just_closed_long := false
        just_closed_short := false
else
    just_closed_long := false
    just_closed_short := false
    bars_since_close := 0

// Ladder Average System
var float ladder_ma = na
var float horizontal_level = na
var int sideways_count = 0

// Trend-following ladder average
if is_strong_trend and not is_sideways_by_slope
    // Normal MA tracking in strong trend
    ladder_ma := ma_value
    sideways_count := 0
else
    // When trend weakens or in sideways market
    sideways_count += 1
    if sideways_count >= horizontal_lookback or na(horizontal_level)
        horizontal_level := ma_value
    ladder_ma := horizontal_level

// Market state
market_state = is_strong_trend and not is_sideways_by_slope ? "TREND" : "SIDEWAYS"

// Volatility measurement
volatility = atr_value / close * 100

// ═══════════════════════════════════════════════════════════════════════════════
// STOP LOSS AND TAKE PROFIT CALCULATIONS
// ═══════════════════════════════════════════════════════════════════════════════

// Stop Loss calculation function
calculate_stop_loss(entry_price_val, is_long) =>
    sl_value = sl_type == "ATR" ? (is_long ? entry_price_val - (atr_value * sl_atr_multiplier) : entry_price_val + (atr_value * sl_atr_multiplier)) : sl_type == "Percent" ? (is_long ? entry_price_val * (1 - sl_percent / 100) : entry_price_val * (1 + sl_percent / 100)) : sl_type == "Points" ? (is_long ? entry_price_val - sl_points : entry_price_val + sl_points) : (is_long ? entry_price_val - (atr_value * sl_atr_multiplier) : entry_price_val + (atr_value * sl_atr_multiplier))
    sl_adjusted = if is_sideways
        is_long ? math.min(sl_value, ladder_ma - atr_value * 0.5) : math.max(sl_value, ladder_ma + atr_value * 0.5)
    else
        sl_value
    sl_adjusted

// Take Profit calculation function
calculate_take_profit(entry_price_val, stop_loss_val, is_long) =>
    tp_value = tp_type == "ATR" ? (is_long ? entry_price_val + (atr_value * tp_atr_multiplier) : entry_price_val - (atr_value * tp_atr_multiplier)) : tp_type == "Percent" ? (is_long ? entry_price_val * (1 + tp_percent / 100) : entry_price_val * (1 - tp_percent / 100)) : tp_type == "Points" ? (is_long ? entry_price_val + tp_points : entry_price_val - tp_points) : tp_type == "Risk/Reward" ? (is_long ? entry_price_val + (math.abs(entry_price_val - stop_loss_val) * tp_risk_reward) : entry_price_val - (math.abs(entry_price_val - stop_loss_val) * tp_risk_reward)) : (is_long ? entry_price_val + (atr_value * tp_atr_multiplier) : entry_price_val - (atr_value * tp_atr_multiplier))
    tp_adjusted = if is_sideways
        is_long ? math.max(tp_value, ladder_ma + atr_value * 1.5) : math.min(tp_value, ladder_ma - atr_value * 1.5)
    else
        tp_value
    tp_adjusted

var float current_sl = na
var float current_tp = na

// ═══════════════════════════════════════════════════════════════════════════════
// ENTRY SIGNALS
// ═══════════════════════════════════════════════════════════════════════════════

// Normal entry conditions
normal_long = strategy.position_size == 0 and ((is_strong_trend and close > ladder_ma and close[1] <= ladder_ma[1]) or (is_sideways and close < ladder_ma and close > ladder_ma - atr_value))
normal_short = strategy.position_size == 0 and ((is_strong_trend and close < ladder_ma and close[1] >= ladder_ma[1]) or (is_sideways and close > ladder_ma and close < ladder_ma + atr_value))

// Trend catching entry conditions
trend_catch_long = enable_trend_catch and strategy.position_size == 0 and just_closed_short and bars_since_close <= quick_entry_bars and strong_uptrend and close > close[1] and close > ladder_ma
trend_catch_short = enable_trend_catch and strategy.position_size == 0 and just_closed_long and bars_since_close <= quick_entry_bars and strong_downtrend and close < close[1] and close < ladder_ma

// Strong momentum entry conditions (even if no position closed, but strong trend exists)
momentum_long = enable_trend_catch and strategy.position_size == 0 and strong_uptrend and close > ladder_ma and close > close[1] and close > open
momentum_short = enable_trend_catch and strategy.position_size == 0 and strong_downtrend and close < ladder_ma and close < close[1] and close < open

// Combined entry conditions
long_condition = normal_long or trend_catch_long or momentum_long
short_condition = normal_short or trend_catch_short or momentum_short

// Entry type determination
entry_type = if trend_catch_long or trend_catch_short
    "TREND_CATCH"
else if momentum_long or momentum_short
    "MOMENTUM"
else
    market_state

// ═══════════════════════════════════════════════════════════════════════════════
// LADDER TRAILING STOP SYSTEM
// ═══════════════════════════════════════════════════════════════════════════════

var float[] ladder_levels = array.new<float>()
var float current_trailing_stop = na
var float entry_price = na

// Calculate ladder levels function
calculate_ladder_levels(entry_price_val, is_long) =>
    ladder_array = array.new<float>()
    base_level = ladder_ma
    for i = 1 to max_ladders
        level_value = if is_long
            base_level + (atr_value * atr_multiplier * i * ladder_step / 100)
        else
            base_level - (atr_value * atr_multiplier * i * ladder_step / 100)
        array.push(ladder_array, level_value)
    ladder_array

// Trailing stop update function
update_trailing_stop(entry_price_val, current_price, is_long) =>
    stop_level = if is_long
        initial_stop = is_sideways ? ladder_ma - atr_value : entry_price_val - (atr_value * atr_multiplier)
        new_stop = initial_stop
        if array.size(ladder_levels) > 0
            for i = 0 to array.size(ladder_levels) - 1
                level_value = array.get(ladder_levels, i)
                if current_price >= level_value
                    adjusted_stop = is_sideways ? ladder_ma : entry_price_val + (atr_value * atr_multiplier * (i + 1) * 0.3)
                    if adjusted_stop > new_stop
                        new_stop := adjusted_stop
        new_stop
    else
        initial_stop = is_sideways ? ladder_ma + atr_value : entry_price_val + (atr_value * atr_multiplier)
        new_stop = initial_stop
        if array.size(ladder_levels) > 0
            for i = 0 to array.size(ladder_levels) - 1
                level_value = array.get(ladder_levels, i)
                if current_price <= level_value
                    adjusted_stop = is_sideways ? ladder_ma : entry_price_val - (atr_value * atr_multiplier * (i + 1) * 0.3)
                    if adjusted_stop < new_stop
                        new_stop := adjusted_stop
        new_stop
    stop_level

// ═══════════════════════════════════════════════════════════════════════════════
// POSITION MANAGEMENT
// ═══════════════════════════════════════════════════════════════════════════════

// Long position entry
if long_condition
    strategy.entry("Long", strategy.long, comment="Long: " + market_state)
    entry_price := close
    ladder_levels := calculate_ladder_levels(close, true)
    
    // Stop Loss calculation (only if active)
    if use_stop_loss
        current_sl := calculate_stop_loss(close, true)
    
    // Take Profit calculation (only if active)
    if use_take_profit
        temp_sl = use_stop_loss ? current_sl : close - (atr_value * sl_atr_multiplier)
        current_tp := calculate_take_profit(close, temp_sl, true)
    
    // Trailing stop initialization (only if active)
    if use_trailing_stop
        current_trailing_stop := is_sideways ? ladder_ma - atr_value : close - (atr_value * atr_multiplier)

// Short position entry
if short_condition
    strategy.entry("Short", strategy.short, comment="Short: " + market_state)
    entry_price := close
    ladder_levels := calculate_ladder_levels(close, false)
    
    // Stop Loss calculation (only if active)
    if use_stop_loss
        current_sl := calculate_stop_loss(close, false)
    
    // Take Profit calculation (only if active)
    if use_take_profit
        temp_sl = use_stop_loss ? current_sl : close + (atr_value * sl_atr_multiplier)
        current_tp := calculate_take_profit(close, temp_sl, false)
    
    // Trailing stop initialization (only if active)
    if use_trailing_stop
        current_trailing_stop := is_sideways ? ladder_ma + atr_value : close + (atr_value * atr_multiplier)

// Position exit management
if strategy.position_size > 0  // Long position
    // If using fixed SL/TP
    if use_stop_loss and use_take_profit
        strategy.exit("Long Exit", "Long", stop=current_sl, limit=current_tp, comment="SL/TP")
    else if use_stop_loss and not use_take_profit
        strategy.exit("Long Exit", "Long", stop=current_sl, comment="SL Only")
    else if not use_stop_loss and use_take_profit
        strategy.exit("Long Exit", "Long", limit=current_tp, comment="TP Only")
    
    // If using trailing stop (optional)
    if use_trailing_stop
        current_trailing_stop := update_trailing_stop(entry_price, close, true)
        if close <= current_trailing_stop
            strategy.close("Long", comment="Trailing Stop")

if strategy.position_size < 0  // Short position
    // If using fixed SL/TP
    if use_stop_loss and use_take_profit
        strategy.exit("Short Exit", "Short", stop=current_sl, limit=current_tp, comment="SL/TP")
    else if use_stop_loss and not use_take_profit
        strategy.exit("Short Exit", "Short", stop=current_sl, comment="SL Only")
    else if not use_stop_loss and use_take_profit
        strategy.exit("Short Exit", "Short", limit=current_tp, comment="TP Only")
    
    // If using trailing stop (optional)
    if use_trailing_stop
        current_trailing_stop := update_trailing_stop(entry_price, close, false)
        if close >= current_trailing_stop
            strategy.close("Short", comment="Trailing Stop")



// ═══════════════════════════════════════════════════════════════════════════════
// VISUALIZATION
// ═══════════════════════════════════════════════════════════════════════════════

// Ladder Average plot
plot(ladder_ma, color=is_sideways ? color.orange : (ma_slope > 0 ? color.green : color.red), linewidth=3, title="Ladder Average")

// Horizontal level plot
plot(is_sideways ? horizontal_level : na, color=color.yellow, style=plot.style_circles, linewidth=2, title="Horizontal Level")

// ATR-based bands
upper_band = ladder_ma + atr_value
lower_band = ladder_ma - atr_value
plot(upper_band, color=color.new(color.blue, 70), title="Upper ATR Band")
plot(lower_band, color=color.new(color.blue, 70), title="Lower ATR Band")

// Stop Loss and Take Profit plots (only if active)
plot(strategy.position_size != 0 and use_stop_loss ? current_sl : na, color=color.red, style=plot.style_circles, linewidth=2, title="Stop Loss")
plot(strategy.position_size != 0 and use_take_profit ? current_tp : na, color=color.green, style=plot.style_circles, linewidth=2, title="Take Profit")

// Trailing stop plot (only if active)
plot(strategy.position_size > 0 and use_trailing_stop ? current_trailing_stop : na, color=color.orange, style=plot.style_stepline, linewidth=2, title="Long Trailing Stop")
plot(strategy.position_size < 0 and use_trailing_stop ? current_trailing_stop : na, color=color.orange, style=plot.style_stepline, linewidth=2, title="Short Trailing Stop")

// Market state background color
bgcolor(is_sideways ? color.new(color.yellow, 95) : (is_strong_trend ? color.new(color.green, 98) : color.new(color.gray, 98)), title="Market State")