Стратегия усреднения трендовой лестницы: как элегантно «лежать ровно», когда рынок идет вбок?

ADX EMA ATR DI
Дата создания: 2025-10-09 14:28:37 Последнее изменение: 2025-10-09 14:28:37
Копировать: 0 Количество просмотров: 268
2
Подписаться
319
Подписчики

Стратегия усреднения трендовой лестницы: как элегантно «лежать ровно», когда рынок идет вбок? Стратегия усреднения трендовой лестницы: как элегантно «лежать ровно», когда рынок идет вбок?

Почему традиционные стратегии отслеживания трендов часто “переворачиваются” во время потрясений?

Как практикующему квантовую торговлю, мне часто задают вопрос: почему стратегии, которые хорошо работают в трендовых рынках, начинают резко отступать после потрясений?

Ответ прост: большинство стратегий отслеживания трендов страдают от синдрома трендообсессивности: они всегда пытаются поддерживать высокую частоту торговли в любой рыночной обстановке, игнорируя один основополагающий факт:Рынок находится в пассивном состоянии 70% времени.

Сегодня мы рассмотрим так называемую “стратегию трендовых усреднений”, которая предлагает интересное решение этой проблемы:Активный отслеживание в трендовых рынках, “элегантное лежание” в рыночных волнах

Что такое среднее по лестнице и как оно может изменить определение трендового отслеживания?

Традиционные стратегии с движущимися средними имеют один смертельный недостаток: они постоянно меняются. Будь то рынок с сильной тенденцией или с горизонтальной колебательностью, средняя линия постоянно корректируется с колебаниями цен, что приводит к большому количеству ложных сигналов.

Идея, лежащая в основе лестничного среднего, заключается в следующем:Заморозить равновесие при определенных условиях

Логика реализации следующая:

  1. Обнаружение состояния трендаПоказатель ADX: как оценить силу рыночных тенденций

    • ADX > 25: рынки с высоким трендом
    • Средний уклон < 0,3%: горизонтальный рынок
  2. Динамический равнолинейный переключатель

    • При сильной тенденции: нормально следить за EMA 21
    • При горизонтальном положении: равнолинейная “заморозка” в горизонтальном положении, образующая поддержку/сопротивление

Именно в этом и заключается хитрость дизайна:Это позволяет стратегии проявлять различные “персонажи” в разных рыночных условиях.“Все, что мы делаем, - мы делаем для того, чтобы люди не чувствовали себя виноватыми.

Как реализовать систему “поймания тенденций”?

В дополнение к базовому механизму лестничного среднего, стратегия также включает в себя модуль “захвата тенденций”, который я считаю наиболее инновационным:

Быстрый обратный механизм

  • В то время, когда только что свернувшиеся позиции пошли в обратную сторону.
  • Быстрое создание новых позиций за 3 цикла
  • Условие: ADX > 30 и разница между DI+ и DI- > 10

Этот дизайн решает важную проблему традиционной стратегии:Как быстро скорректировать позиции в начале обратного тренда

Представьте себе такой сценарий: вы только что ликвидировали несколько позиций из-за остановки убытков, и в результате рынки сразу же начали сильную нисходящую тенденцию. Традиционная стратегия может потребовать ожидания подтверждения новых сигналов, но эта система “захвата тенденций” способна быстро создать пустые позиции в течение 3 циклов.

Управление рисками: почему нужно различать состояние рынка?

В этой стратегии лучше всего учиться тому, чтоДифференцированный механизм управления рисками

Управление рисками на горизонтальных рынках

  • Стоп-линия скорректирована вблизи средней лестничной линии
  • Снижение ATR-коэффициента и ужесточение убытков
  • Настройки целевого бита более консервативны

Управление рисками на трендовых рынках

  • Применение стандартного ATR-множественного стоп-лоста
  • Включить лестничную подвижную остановку
  • Допускает большую колебательность цен

В этом проекте отражена важная философия торговли:Разные рыночные условия требуют разных предпочтений в отношении риска│ │в кривых рынках мы должны быть более осторожными; в трендовых рынках нам нужно дать больше свободы для прибыли │

“Ладеровый подвижной стоп: как сбалансировать сохранение прибыли и отслеживание тенденций?”

Традиционный мобильный стоп часто бывает слишком механизированным, либо слишком напряженным, что приводит к преждевременному выходу из игры, либо слишком слабым, чтобы эффективно защитить прибыль. Лестничный мобильный стоп этой стратегии предлагает более разумное решение:

Логика установки лестницы

  • Динамический расчет лестничного расстояния ATR
  • Настройка до 5 уровней лестницы
  • Каждый прорыв в лестнице, стоп-падеж соответствующим образом повышается

Преимущества такого дизайна заключаются в следующем:Это позволяет сохранить прибыль и дать тренду достаточно пространства для развития.

Что нужно учитывать в практическом применении?

Основываясь на моем опыте работы на местах, следует учитывать следующие моменты при использовании таких стратегий:

  1. Ловушки оптимизации параметровНе переоптимизируйте порог ADX: значения между 25 и 30 стабильны на большинстве рынков

  2. Рыночная адаптивностьЭта стратегия лучше подходит для рынков с умеренной волатильностью, где может потребоваться корректировка ATR-множителя в условиях крайней волатильности.

  3. Управление деньгамиРекомендуется, чтобы одна позиция не превышала 10% от общего капитала, особенно при включенной функции захвата трендов.

  4. Отслеживание ловушекОсобое внимание следует уделить влиянию сбойных точек и комиссий, особенно при частом совершении сделок на нестабильных рынках.

Где инновационная ценность этой стратегии?

С точки зрения развития количественной стратегии, эта стратегия представляет собой важный эволюционный шаг:Переход от единой логики к многостадийной адаптации

Традиционные стратегии, которые пытаются использовать определенный набор логики для всех рыночных ситуаций, демонстрируют мудрость “приспособления к местности”:

  • По-моему, это не так уж и важно, но я думаю, что это не так уж и важно.
  • Как консервативный трейдер в условиях бурного рынка

Эта концепция имеет важное значение для разработчиков стратегий:Мы должны сделать так, чтобы наши стратегии были “поняты рынком”, а не слепо следовать за фиксированной логикой.

Наконец, следует подчеркнуть, что ни одна стратегия не является универсальной. Эта стратегия усреднения, хотя и элегантна в теории, в практическом применении все еще требует корректировки в сочетании с конкретными рыночными условиями и личными предпочтениями в отношении риска.Лучшая стратегия - всегда та, которая подходит именно вам.

Исходный код стратегии
/*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")