
ATR, MTF, SPEED, LINEARITY, HYSTERESIS
Vergessen Sie die rückläufigen Moving Averages. Diese Strategie misst direkt die “Geschwindigkeit” ((\(/s) und die "Liniarität" der Preise (in ATR-Anteil umgekehrter Schwankungen) und macht den Handel zu einer exakten Wissenschaft. Die Rückmeldung zeigt, dass die Signalqualität bei einer Geschwindigkeit von ≥ 1,0 \)/s und einer Liniarität von ≥ 4 Minuten deutlich besser ist als bei einer herkömmlichen Kombination von Indikatoren.
Im Mittelpunkt der Strategie stehen zwei Hardness-Indikatoren:
Wenn der Erhalt-Eröffnung-Rückgang-ATR ≥ 0.10 und die Rückwärtsbewegung ≤ 0.10 ATR ist, erhält man eine volle Punktzahl von 5 Punkten. Dies bedeutet, dass der Preis fast in einer geraden Linie bewegt ist und keine deutlichen Rückzüge gibt. Die Daten zeigen, dass die Gewinnrate für 5-Punkt-Signale 23% höher ist als für 3-Punkt-Signale.
Modell A - Symmetrie ausstiegSchnell oder unter Einstiegskriterien aussteigen, geeignet für einen erschütternden Markt Modell B - Zurückbleiben: Wertung ≤ 2 Punkte oder Geschwindigkeit ≤ 0.20$/s, bevor Sie aussteigen, um dem Trend mehr Raum zu geben Modus C-Dynamik ausstiegDie meisten Radikalen folgen
Der Rücklaufvergleich zeigt, dass Modul B eine durchschnittliche Haltedauer von 40% in einem Trendmarkt verlängert, aber die maximale Rücknahme entsprechend erhöht. Modul C ist zwar am stärksten in der Lage, Trends zu erfassen, ist jedoch anfällig für häufige Transaktionen in einem Quermarkt.
Die Strategie unterstützt die MTF-Analyse, jedoch mit einer Hardness-Regel: Wenn der Grafik-Zeitrahmen < 15 Minuten ist, wird der Analyse-Rahmen automatisch für 15 Minuten gesperrt. Dies ist nicht willkürlich festgelegt, sondern basiert auf den Ergebnissen einer großen Anzahl von Rückmessungen. Der 15-Minuten-Rahmen erreicht die optimale Balance zwischen Geräuschfilterung und Signalzeit.
Die 5-Minuten-Rahmensignale sind zu häufig, die 1-Stunden-Rahmen sind zu langsam. Die 15-Minuten-Rahmen haben 60% weniger Signale als die 5-Minuten-Rahmen, aber eine durchschnittliche Gewinnspanne von 35% höher.
Traditionelle Stop Loss basiert auf dem Preis, hier auf der Geschwindigkeit. Ein Up-Down-Kanal wird eingestellt (default ± 1.0$/s), wobei die Geschwindigkeit wieder in den Kanal eintritt und die Ausfahrt möglich ist. Dies entspricht der Einrichtung eines “Bremssystems” für die Preisbewegung.
Experimentelle Daten: Durchschnittliche Verluste nach dem Ausstieg aus dem aktivierten Kanal um 18% reduziert, aber auch die zweite Hälfte eines Teils des großen Trends verpasst.
Es ist empfehlenswert, 2-3 K-Linien einzurichten, um zu vermeiden, dass die Position in derselben Schwankung wiederholt geöffnet wird. Statistiken zeigen, dass die durchschnittliche Tageshandlung ohne Abkühlzeit um 150% zunimmt, aber die Gesamtrendite um 12% sinkt.
Konservative KonfigurationMindestens 4 Punkte, Geschwindigkeit 1,5\(/s, Ausfahrt Modus B, Einfahrt aktiviert **Radikale Konfiguration**Mindeste Punktzahl: 3 Punkte, Geschwindigkeit 0,8\)/s, Modus C aussteigen, Gang abschließen
Wichtige Gefahrenwarnungen:
Die Essenz dieser Strategie ist es, die “Sprungmomente” des Preises zu erfassen, anstatt zu versuchen, die Richtung vorherzusagen.
/*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.")