Stratégie de moyenne d'échelle de tendance : comment rester élégamment à plat lorsque le marché évolue latéralement ?

ADX EMA ATR DI
Date de création: 2025-10-09 14:28:37 Dernière modification: 2025-10-09 14:28:37
Copier: 0 Nombre de clics: 268
2
Suivre
319
Abonnés

Stratégie de moyenne d’échelle de tendance : comment rester élégamment à plat lorsque le marché évolue latéralement ? Stratégie de moyenne d’échelle de tendance : comment rester élégamment à plat lorsque le marché évolue latéralement ?

Pourquoi les stratégies traditionnelles de suivi des tendances sont-elles souvent “reversées” dans les villes en crise ?

En tant que praticien du trading quantitatif, on me pose souvent la question suivante: pourquoi des stratégies qui fonctionnent bien dans un marché en tendance commencent-elles à se retirer de manière spectaculaire en cas de choc ?

La réponse est simple: la plupart des stratégies de suivi de tendances souffrent d’un “obsession de la tendance” et essaient toujours de maintenir une fréquence élevée de transactions dans n’importe quel environnement de marché, sans tenir compte d’un fait fondamental:Le marché est en mouvement horizontal 70% du temps.

La stratégie de la moyenne en échelle tendancielle, que nous analyserons aujourd’hui, propose une solution intéressante à ce problème:Suivre activement les tendances, rester “ élégant ” dans les bouleversements

Qu’est-ce qu’une moyenne échelonnée et comment peut-elle redéfinir le suivi des tendances ?

Les stratégies traditionnelles de moyennes mobiles ont un défaut mortel: elles changent constamment. Qu’il s’agisse d’une forte tendance ou d’une oscillation horizontale, la moyenne s’ajuste constamment aux fluctuations des prix, ce qui entraîne une quantité importante de faux signaux.

Le but de la méthode est d’aider les gens à comprendre ce qu’ils font et ce qu’ils ne font pas.L’équilibre est “gelé” dans certaines conditions.

La logique de mise en œuvre est la suivante:

  1. Détection de l’état de tendanceLa force des tendances du marché est mesurée par l’indicateur ADX:

    • ADX >25: le marché est en forte tendance
    • La courbe moyenne est inférieure à 0,3%: marché horizontal
  2. Basculement dynamique

    • En période de forte tendance: suivi normal de l’EMA
    • À l’horizontale: l’équilibre “glace” en position horizontale pour former un support/résistance

Le plus intéressant dans cette conception est que:Il permet aux stratégies de montrer une “personnalité” différente dans différents contextes de marché.Il est sensible aux tendances et stable aux tremblements.

Comment mettre en place un système de capture de tendances ?

En plus du mécanisme de base de la moyenne échelonnée, la stratégie intègre un module de “capture de tendance”, qui est ce que je trouve le plus innovant:

Le mécanisme de retour rapide

  • La tendance à l’inflation a commencé à se renforcer alors que la tendance à l’inflation était en train de s’apaiser.
  • Créer rapidement de nouvelles positions en 3 cycles
  • Condition: ADX > 30 et différence entre DI+ et DI- > 10

Cette conception résout un problème important avec les stratégies traditionnelles:Comment se positionner rapidement au début d’une inversion de tendance

Imaginez un scénario: vous venez d’effacer plusieurs positions de tête à cause d’un stop loss, et le résultat est une forte tendance à la baisse immédiate. Les stratégies traditionnelles peuvent avoir besoin d’attendre la confirmation de nouveaux signaux, mais ce système de “capture de tendance” peut rapidement établir des positions de tête creuses en 3 cycles.

Gestion des risques: pourquoi faire la distinction entre les états du marché ?

La meilleure chose à apprendre de cette stratégie est queLes mécanismes de gestion des risques différenciés

Contrôle des risques dans les marchés horizontaux

  • Le stop loss est ajusté à proximité de la ligne moyenne de l’échelle.
  • Réduire le coefficient ATR et resserrer les arrêts de perte
  • Les paramètres de la cible sont plus conservateurs.

Contrôle des risques dans les marchés tendances

  • Stop-loss avec un ATR standard
  • Activer le stop de déplacement en échelle
  • Laissez plus de place aux fluctuations des prix

La conception de ce type d’appareil traduit une philosophie de négociation importante:Différents environnements de marché nécessitent des préférences de risque différentes│ dans les marchés horizontaux, nous devrions être plus prudents; dans les marchés tendances, nous devons donner plus de marge de manœuvre aux bénéfices │

Le stop-loss mobile en échelle: comment équilibrer la protection des bénéfices et le suivi des tendances ?

Les arrêts mobiles traditionnels sont souvent trop mécanisés, soit trop serrés pour entraîner des départs prématurés, soit trop relâchés pour protéger efficacement les bénéfices. Les arrêts mobiles échelonnés de cette stratégie offrent une solution plus intelligente:

La logique de mise en place de l’échelle

  • Distance de l’escalier calculée sur la base de l’ATR dynamique
  • Configurer jusqu’à 5 échelons
  • Chaque fois qu’une échelle est franchie, le stop-loss augmente.

Les avantages de cette conception sont les suivants:Il permet de donner à la tendance suffisamment d’espace pour se développer tout en protégeant ses profits.

Quelles sont les précautions à prendre dans la pratique ?

D’après mon expérience sur le terrain, il y a plusieurs choses à garder à l’esprit lorsque vous utilisez de telles stratégies:

  1. Les pièges de l’optimisation paramétriqueNe sur-optimisez pas les seuils ADX, les valeurs entre 25 et 30 sont stables dans la plupart des marchés

  2. Adaptation au marchéCette stratégie est plus adaptée aux marchés modérés et volatiles, et peut nécessiter une adaptation des multiples ATR dans des environnements extrêmement volatiles.

  3. Gestion des fondsIl est recommandé de ne pas dépasser 10% du capital total, en particulier lorsque la fonction de capture de tendances est activée.

  4. Le piège de la détectionAttention aux effets des points de glissement et des frais de transaction, en particulier lors de transactions fréquentes sur des marchés en crise.

Où est l’innovation dans cette stratégie ?

Cette stratégie représente une évolution importante du point de vue de l’évolution des stratégies de quantification:Transition de la logique unique vers l’adaptation à plusieurs états

Les stratégies traditionnelles tentent souvent de répondre à toutes les situations du marché avec un ensemble de logiques fixes, alors que cette stratégie fait preuve de la sagesse de la géolocalisation:

  • Il est devenu un observateur de tendances radical dans un marché de tendances.
  • Il a fait preuve d’une grande prudence dans la négociation de ses positions sur les marchés en crise.

Cette conception a une signification d’inspiration importante pour les développeurs de stratégies:Nous devrions donner à la stratégie la capacité de “percevoir le marché” plutôt que d’appliquer aveuglément une logique fixe.

Enfin, il est important de souligner qu’aucune stratégie n’est universelle. Bien que cette stratégie d’échelle moyenne soit élégante en théorie, elle doit être adaptée à l’environnement de marché et aux préférences personnelles en matière de risque.La meilleure stratégie est toujours celle qui vous convient le mieux.

Code source de la stratégie
/*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")