Стратегия двух осей с линейностью скорости


Дата создания: 2026-01-27 09:31:11 Последнее изменение: 2026-03-16 17:33:20
Копировать: 5 Количество просмотров: 154
2
Подписаться
435
Подписчики

Стратегия двух осей с линейностью скорости Стратегия двух осей с линейностью скорости

ATR, MTF, SPEED, LINEARITY, HYSTERESIS

Это не традиционный технический анализ, это физика движения цен.

Забудьте о задержанных скользящих средних. Эта стратегия напрямую измеряет “скорость” (в \(/с) и "линейность" цены (в % обратной колебательности ATR), превращая торговлю в точную науку. Отзывы показывают, что качество сигнала значительно превосходит традиционную комбинацию индикаторов, когда скорость ≥1.0\)/с и линейная оценка ≥4.

Двойной механизм фильтрации: порог скорости + оценка линейности

В основе стратегии лежат два показателя жесткости:

  • Скорость падает: режим твердого диска 1.0\(/с, режим обратной связи 0.001\)/с ((избегать шума торгов)
  • Рейтинг линейности1-5 баллов, основанных на пропорции ATR к обратным колебаниям

Получить полный балл 5 баллов, когда коэффициент приема-открытия и/или ATR ≥ 0.10 и обратная волатильность ≤ 0.10 ATR. Это означает, что цена движется практически по прямой линии, без заметного отступления. Данные показывают, что победный коэффициент 5-балльного сигнала на 23% выше, чем 3-балльного.

Три способа выхода, адаптирующиеся к ритму рынка

Модуль A - симметрия выхода: скорость или рейтинг ниже стандарта для выхода, подходящий для рынка содроганий Модель B - отставание: оценка ≤ 2 балла или скорость ≤ 0.20$/с, чтобы выйти, чтобы дать тренду больше пространства Модуль C - выход из движения: выходит, когда многоголовый скорость ≤ 0, наиболее радикальные тенденции следуют

Отзывная контрастность показывает, что модель B увеличивает среднее время удержания позиции на 40% в трендовых рынках, но соответственно увеличивает максимальные отступления. Модель C, хотя и обладает наибольшей способностью улавливать тенденции, легко генерирует частые сделки в рыночных рынках.

Анализ многократных временных рамок: 15 минут - оптимальный баланс

Политика поддерживает MTF-анализ, но с одним жестким правилом: когда графический временной рамок <15 минут, аналитический рамок автоматически блокируется на 15 минут. Это не произвольная настройка, а основанная на большом количестве ретроспективных выводов: 15-минутный рамок обеспечивает оптимальный баланс между шумовой фильтрацией и своевременностью сигнала.

5-минутные фрейм-сигналы слишком часто, 1-часовые фрейм-сигналы слишком задерживаются. В 15-минутных фрейм-сигналах количество сигналов уменьшается на 60% по сравнению с 5-минутными, но средняя прибыльность повышается на 35%.

Канал скорости: инновации в управлении рисками

Традиционный стоп основан на цене, здесь основан на скорости. Устанавливается верхний и нижний канал (по умолчанию ± 1.0$/с), когда скорость вновь входит в канал, можно выбрать выход. Это эквивалентно установке “системы торможения” на движение цены.

Экспериментальные данные: после выхода из открытого коридора средняя потеря снижается на 18%, но также пропускает часть последней половины большого тренда.

Механизм “охлаждения”: избегайте чрезмерной торговли

Минимальное количество K-линий, которое можно установить в интервале между сигналами, 0 означает закрытие. Рекомендуется установить 2-3 K-линии, чтобы избежать повторного открытия позиции в одном и том же колебании. Статистика показывает, что среднесуточная торговля увеличивается на 150% в период без охлаждения, но общая доходность, наоборот, снижается на 12%

Рекомендации по боевым параметрам и предупреждения о рисках

Консервативная конфигурацияМинимальный балл: 4 балла, скорость: \(1,5 в секунду, режим B выходит, открывается проход **Радикальная конфигурация**Минимальный балл: 3 балла, скорость 0,8\)/с, режим C - выход, закрытие коридора

Важное предупреждение

  • Сигналы в условиях низкой волатильности могут быть скудными и не дать возможности для торговли в течение нескольких часов.
  • Высокая скорость, хотя и улучшает качество сигнала, может пропустить умеренную тенденцию
  • Исторический отсчет не отражает будущий доход, изменения в структуре рынка могут повлиять на эффективность стратегии
  • Рекомендуется строго контролировать одноразовые позиции, чтобы избежать чрезмерного наращивания позиций при последовательных убытках.

Суть этой стратегии заключается в том, чтобы поймать “порыв” цены, а не пытаться предсказать направление. Это ваш инструмент, когда рынок показывает четкую скорость и направление.

Исходный код стратегии
/*backtest
start: 2025-01-27 00:00:00
end: 2026-01-25 08:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT","balance":500000,"fee":[0,0]}]
args: [["v_input_string_1",1]]
*/

//@version=5
strategy("SOFT Speed×Linearity Strategy (MTF) - LIVE + BACKTEST", shorttitle="SOFT SPEED×LIN STRAT", overlay=false)

// =====================================================
// MODE
// =====================================================
grp_mode = "Mode"
modeRun = input.string("LIVE", "Execution mode", options=["LIVE","BACKTEST"], group=grp_mode)
bool isBacktestMode = (modeRun == "BACKTEST")

// =====================================================
// TIMEFRAME
// =====================================================
grp_tf = "Timeframe"
lockToChartTF = input.bool(false, "Lock analysis TF to chart TF", group=grp_tf)
tfInput = input.timeframe("15", "Analysis timeframe (MTF)", group=grp_tf)

// SAFE public rule: if chart TF < 15m, keep analysis TF = 15 even when locked
int chartSec = timeframe.in_seconds(timeframe.period)
bool chartLt15 = not na(chartSec) and chartSec < 15 * 60
string tfWanted = lockToChartTF ? timeframe.period : tfInput
string tfUse = (lockToChartTF and chartLt15) ? "15" : tfWanted
bool analysisEqualsChart = (tfUse == timeframe.period)

// Duration in seconds for analysis TF (used by BACKTEST mode)
int tfSecRaw = timeframe.in_seconds(tfUse)
int tfSec = na(tfSecRaw) ? 900 : tfSecRaw
tfSec := math.max(tfSec, 1)

// =====================================================
// CORE
// =====================================================
grp_core = "Core"
atrLen = input.int(14, "ATR length", minval=1, group=grp_core)
minProgAtr = input.float(0.10, "Min progress (|C-O|) in ATR", minval=0.0, step=0.01, group=grp_core)

grp_score = "Linearity thresholds (% ATR adverse)"
thr5 = input.float(0.10, "Score 5 if adverse <= x ATR", minval=0.0, step=0.01, group=grp_score)
thr4 = input.float(0.20, "Score 4 if adverse <= x ATR", minval=0.0, step=0.01, group=grp_score)
thr3 = input.float(0.35, "Score 3 if adverse <= x ATR", minval=0.0, step=0.01, group=grp_score)
thr2 = input.float(0.50, "Score 2 if adverse <= x ATR", minval=0.0, step=0.01, group=grp_score)

// =====================================================
// DISPLAY
// =====================================================
grp_disp = "Display"
speedSmooth = input.int(1, "Speed smoothing EMA", minval=1, group=grp_disp)
speedMult = input.float(100.0, "Panel multiplier", minval=0.1, step=0.1, group=grp_disp)
paintBg = input.bool(true, "Background by linearity", group=grp_disp)

// =====================================================
// ENTRIES
// =====================================================
grp_ent = "Entries"
tradeMode = input.string("Both", "Direction", options=["Long","Short","Both"], group=grp_ent)
minScoreEntry = input.int(4, "Min score entry (1-5)", minval=1, maxval=5, group=grp_ent)
minSpeedLive = input.float(1.0, "Min speed REALTIME ($/s)", minval=0.0, step=0.01, group=grp_ent)
minSpeedBT = input.float(0.001, "Min speed CLOSE-BAR ($/s)", minval=0.0, step=0.0001, group=grp_ent)
useWeightedForEntry = input.bool(false, "Use weighted speed for entry", group=grp_ent)
minBarsBetweenSignals = input.int(0, "Cooldown bars (0=off)", minval=0, group=grp_ent)

// =====================================================
// EXITS
// =====================================================
grp_exit = "Exits"
exitMode = input.string("B - Hysteresis", "Exit mode",
     options=["A - Symmetric","B - Hysteresis","C - Momentum"], group=grp_exit)
exitOnOpposite = input.bool(true, "Exit on opposite signal", group=grp_exit)
exitMinScore = input.int(2, "B: Exit if score <=", minval=1, maxval=5, group=grp_exit)
exitMinSpeed = input.float(0.20, "B: Exit if |speed| <= ($/s)", minval=0.0, step=0.01, group=grp_exit)

// =====================================================
// SPEED CHANNEL
// =====================================================
grp_ch = "Speed Channel"
useChannel = input.bool(true, "Enable channel", group=grp_ch)
chUpper = input.float(1.0, "Upper channel ($/s)", minval=0.0, step=0.01, group=grp_ch)
chLower = input.float(1.0, "Lower channel ($/s)", minval=0.0, step=0.01, group=grp_ch)
exitOnChannelReentry = input.bool(false, "Exit when re-entering channel", group=grp_ch)

// =====================================================
// ALERTS
// =====================================================
grp_al = "Alerts"
alertBuy = input.bool(true, "Alert BUY", group=grp_al)
alertSell = input.bool(true, "Alert SELL", group=grp_al)
alertExit = input.bool(true, "Alert EXIT", group=grp_al)
alertChannel = input.bool(true, "Alert channel breakout", group=grp_al)
alertAll = input.bool(false, "Alert ALL events", group=grp_al)

// =====================================================
// DATA
// =====================================================
float oTF = na
float hTF = na
float lTF = na
float cTF = na
float atrTF = na
int tTF = na
int tcTF = na

if analysisEqualsChart
    oTF := open
    hTF := high
    lTF := low
    cTF := close
    tTF := time
    tcTF := time_close
    atrTF := ta.atr(atrLen)
else
    oTF := request.security(syminfo.tickerid, tfUse, open, barmerge.gaps_off, barmerge.lookahead_off)
    hTF := request.security(syminfo.tickerid, tfUse, high, barmerge.gaps_off, barmerge.lookahead_off)
    lTF := request.security(syminfo.tickerid, tfUse, low, barmerge.gaps_off, barmerge.lookahead_off)
    cTF := request.security(syminfo.tickerid, tfUse, close, barmerge.gaps_off, barmerge.lookahead_off)
    tTF := request.security(syminfo.tickerid, tfUse, time, barmerge.gaps_off, barmerge.lookahead_off)
    tcTF := request.security(syminfo.tickerid, tfUse, time_close, barmerge.gaps_off, barmerge.lookahead_off)
    atrTF := request.security(syminfo.tickerid, tfUse, ta.atr(atrLen), barmerge.gaps_off, barmerge.lookahead_off)

// =====================================================
// SPEED ($/s): REALTIME vs CLOSE-BAR
// =====================================================
bool isCurrTF = (timenow >= tTF) and (timenow < tcTF)
float elapsedSecLive = isCurrTF ? ((timenow - tTF) / 1000.0) : float(tfSec)
elapsedSecLive := math.max(elapsedSecLive, 1.0)

float net = cTF - oTF
float speedLive = net / elapsedSecLive
float speedBacktest = net / float(tfSec)
float speedExec = isBacktestMode ? speedBacktest : speedLive

float speedSm = ta.ema(speedExec, speedSmooth)

// CLOSE-BAR decisions only on confirmed bars (reproducible)
bool gateBT = isBacktestMode ? barstate.isconfirmed : true

// =====================================================
// LINEARITY SCORE (1..5)
// =====================================================
float atrSafe = math.max(atrTF, syminfo.mintick)
float adverseLong = math.max(0.0, oTF - lTF)
float adverseShort = math.max(0.0, hTF - oTF)
float adverse = net >= 0 ? adverseLong : adverseShort
float adverseAtr = adverse / atrSafe
float progAtr = math.abs(net) / atrSafe

int score = 1
score := progAtr < minProgAtr ? 1 : score
score := progAtr >= minProgAtr and adverseAtr <= thr2 ? 2 : score
score := progAtr >= minProgAtr and adverseAtr <= thr3 ? 3 : score
score := progAtr >= minProgAtr and adverseAtr <= thr4 ? 4 : score
score := progAtr >= minProgAtr and adverseAtr <= thr5 ? 5 : score

// Weighted speed
float speedWeighted = speedSm * (score / 5.0)
float speedPanel = speedWeighted * speedMult

// =====================================================
// COLORS
// =====================================================
color col = score == 5 ? color.lime : score == 4 ? color.green : score == 3 ? color.yellow : score == 2 ? color.orange : color.red
color txtCol = score >= 3 ? color.black : color.white
bgcolor(paintBg ? color.new(col, 88) : na)

// =====================================================
// ENTRY LOGIC
// =====================================================
float minSpeedUse = isBacktestMode ? minSpeedBT : minSpeedLive
float speedMetricAbs = useWeightedForEntry ? math.abs(speedWeighted) : math.abs(speedSm)

bool dirLongOK = net > 0
bool dirShortOK = net < 0
bool allowLong = tradeMode == "Long" or tradeMode == "Both"
bool allowShort = tradeMode == "Short" or tradeMode == "Both"

var int lastSigBar = na
bool cooldownOK = minBarsBetweenSignals <= 0 ? true : (na(lastSigBar) ? true : (bar_index - lastSigBar >= minBarsBetweenSignals))

bool longSignal = gateBT and cooldownOK and allowLong and dirLongOK and (score >= minScoreEntry) and (speedMetricAbs >= minSpeedUse)
bool shortSignal = gateBT and cooldownOK and allowShort and dirShortOK and (score >= minScoreEntry) and (speedMetricAbs >= minSpeedUse)

if longSignal
    strategy.entry("LONG", strategy.long)
if shortSignal
    strategy.entry("SHORT", strategy.short)
if longSignal or shortSignal
    lastSigBar := bar_index

// =====================================================
// EXIT LOGIC (3 MODES)
// =====================================================
bool inLong = strategy.position_size > 0
bool inShort = strategy.position_size < 0

bool oppForLong = shortSignal
bool oppForShort = longSignal

// Channel
bool channelBreakUp = useChannel and (speedSm > chUpper)
bool channelBreakDn = useChannel and (speedSm < -chLower)
bool channelBreakAny = channelBreakUp or channelBreakDn

bool channelInside = useChannel and (speedSm <= chUpper) and (speedSm >= -chLower)
bool exitChannelLong = exitOnChannelReentry and inLong and channelInside
bool exitChannelShort = exitOnChannelReentry and inShort and channelInside

bool exitBaseLong = false
bool exitBaseShort = false

// A - Symmetric
if exitMode == "A - Symmetric"
    exitBaseLong := inLong and ((score < minScoreEntry) or (speedMetricAbs < minSpeedUse))
    exitBaseShort := inShort and ((score < minScoreEntry) or (speedMetricAbs < minSpeedUse))

// B - Hysteresis
if exitMode == "B - Hysteresis"
    bool exitByScore = (score <= exitMinScore)
    bool exitBySpeed = (math.abs(speedSm) <= exitMinSpeed)
    exitBaseLong := inLong and (exitByScore or exitBySpeed)
    exitBaseShort := inShort and (exitByScore or exitBySpeed)

// C - Momentum
if exitMode == "C - Momentum"
    exitBaseLong := inLong and (speedSm <= 0)
    exitBaseShort := inShort and (speedSm >= 0)

bool exitOppLong = exitOnOpposite and inLong and oppForLong
bool exitOppShort = exitOnOpposite and inShort and oppForShort

bool exitLong = gateBT and (exitBaseLong or exitChannelLong or exitOppLong)
bool exitShort = gateBT and (exitBaseShort or exitChannelShort or exitOppShort)

if exitLong
    strategy.close("LONG")
if exitShort
    strategy.close("SHORT")

// =====================================================
// PLOTS
// =====================================================
plot(speedPanel, title="Speed (weighted)", style=plot.style_columns, linewidth=3, color=col)
hline(0.0, "Zero", linestyle=hline.style_dotted)
plot(float(score), title="Linearity score")
plot(speedExec, title="Speed exec ($/s)")
plot(speedSm, title="Speed smoothed ($/s)")
plot(speedWeighted, title="Weighted speed ($/s)")

// =====================================================
// ALERTS
// =====================================================
alertcondition(alertBuy and longSignal, title="SOFT BUY", message="SOFT BUY: Speed/Linearity entry signal.")
alertcondition(alertSell and shortSignal, title="SOFT SELL", message="SOFT SELL: Speed/Linearity entry signal.")
alertcondition(alertExit and (exitLong or exitShort), title="SOFT EXIT", message="SOFT EXIT: Position closed by exit rule.")
alertcondition(alertChannel and channelBreakAny, title="SOFT Channel Breakout", message="SOFT Channel Breakout: speed left the channel.")
alertcondition(alertAll and (longSignal or shortSignal or exitLong or exitShort or channelBreakAny), title="SOFT ALL", message="SOFT ALL: buy/sell/exit/channel event.")