적응형 슈퍼트렌드 AI 전략


생성 날짜: 2026-02-25 17:53:23 마지막으로 수정됨: 2026-02-26 11:33:18
복사: 8 클릭수: 148
avatar of ianzeng123 ianzeng123
2
집중하다
413
수행원

적응형 슈퍼트렌드 AI 전략 적응형 슈퍼트렌드 AI 전략

SUPERTREND, ATR, ADX, EMA, AI

이것은 여러분이 보아온 일반적인 슈퍼트렌드 전략이 아닙니다.

전통적인 슈퍼 트렌드 전략의 가장 큰 아픔? 고정된 매개 변수는 다른 시장 환경에서 크게 달라진다. 이 AI 강화 버전은 ATR 배수를 동적으로 조정하여 높은 변동 동안 배수를 기본값의 2 배로 올리고 낮은 변동 동안 0.85 배로 줄입니다. 재검토 데이터는 이러한 적응 메커니즘이 흔들리는 시장에서 가짜 신호를 크게 줄일 수 있음을 보여줍니다.

핵심 혁신은 3 계층의 필터링 시스템입니다: 시장 상태 인식, AI 신호 점수, 다중 확인 메커니즘. 더 이상 간단한 가격이 슈퍼 트렌드 라인을 뚫고 진입하는 것이 아니라, AI 점수가 65점 이상에 달하는 것이 거래 신호를 유발합니다. 이 점수 시스템은 거래량 급증, 가격 편차 정도, 추세 일관성 등 5개의 차원을 종합적으로 고려합니다.

AI 점수 시스템: 각 신호의 신뢰도를 정량화

점수 메커니즘은 정교하게 설계되었습니다: 거래량 급증은 20의 무게를 차지하고, 가격의 SuperTrend 라인 거리 25의 무게를 차지하고, EMA 트렌드 일관성은 20의 무게를 차지하고, 시장 상태 품질은 15의 무게를 차지하고, 상기 가격과 트렌드 라인 거리 20의 무게를 차지합니다. 총 100의 무게를 차지하고, 기본 65의 무게는 고품질 신호만 필터링을 통과 할 수 있음을 의미합니다.

구체적으로, 거래량이 20주기 평균의 2.5배를 초과할 때 20점, 가격 편차가 슈퍼트렌드 선의 1.5배 이상의 ATR을 초과할 때 25점으로 풀 점수를 얻는다. 이러한 정량적인 점수는 주관적인 판단을 피하고, 각 신호에는 명확한 데이터의 뒷받침이 있다. 실제 사용에서는, 다른 품종의 특성에 따라 최소 점수 요구 사항을 조정하는 것이 좋습니다.

시장 상태 적응: 절단 변수와 작별인사

전략은 ATR 비율과 ADX 지표를 통해 세 가지 시장 상태를 식별합니다. 트렌드 기간 ((regime=1), 높은 변동 기간 ((regime=2), 불안정 기간 ((regime=0)). ATR 비율이 1.4 이상일 때 높은 변동 기간으로 판단하고, ADX가 20 미만일 때 ATR 비율이 0.9 미만일 때 불안정 기간으로 판단합니다.

자율적 인 배수 조정 논리: 높은 변동 기간의 배수는 40% × (ATR 비율 -1.0) 증가하고, 흔들림 기간의 배수는 기본 값의 85%로 감소합니다. 이는 3.0의 기본 배수가 극단적인 변동 시 4.2로 조정 될 수 있음을 의미하며, 흔들림 기간에는 2.55으로 조정됩니다. 이러한 동적 조정 메커니즘은 다양한 시장 환경에서 전략의 적응성을 크게 향상시킵니다.

리스크 관리: 세 가지의 손해 방지 모드

ATR 다이내믹 스톱은 기본 2.5배의 ATR 거리에서 정상적인 변동을 견디면서도 적시에 스톱을 할 수 있습니다. 퍼센티지 스톱은 변동률이 상대적으로 안정적인 품종에 적합하며, 슈퍼 트렌드 모드는 트렌드 역전시 즉시 평정됩니다.

스톱 설정은 리스크 수익률 모드를 지원하며, 기본 2.5:1의 리스크 수익률은 통계적으로 유리하다. 스톱을 추적하는 것을 활성화 한 후, 수익한 포지션의 스톱 라인은 2.5배의 ATR 거리에 따라 동적으로 조정되어 트렌드 상황에서 수익을 극대화한다.

다중 필터: 무효 거래 감소

EMA 트렌드 필터는 50주기 EMA 방향이 일치할 때만 진입을 보장하고 역전 거래를 피한다. 흔들림 기간 필터는 정규 = 0의 신호를 직접 건너뛰고, 일부 기회를 놓칠 수 있지만, 가짜 신호 비율을 현저히 감소시킨다.

거래량 필터링은 진입시 거래량이 20주기 평균보다 높게 거래되는 것을 요구하며, 충분한 시장 참여가 가격 돌파를 지원하도록 보장합니다. 10주기의 냉각 기간은 빈번한 거래를 방지하고 거래 비용을 절감합니다.

실전 조언: 변수 조정 및 위험 관리

암호화폐의 경우 최소 AI 점수를 70점으로 끌어올리고, 전통적인 주식의 경우 60점으로 내려놓을 수 있다. 하이프라이크 트레이더는 냉각기를 5주기로 줄일 수 있고, 롱라인 투자자는 15주기로 연장할 수 있다.

ATR 길이 파라미터 10은 최적화된 균형점으로, 너무 짧으면 과민하게 반응하고, 너무 길으면 지연된다. 기본 배수 3.0은 대부분의 품종에 적합하며, 높은 파동 품종은 3.5으로 조정할 수 있으며, 낮은 파동 품종은 2.5으로 줄일 수 있다.

중요한 위험요소: 역사적인 재검토 결과는 미래의 수익 성과를 나타내지 않는다. 전략은 극한 시장 조건에서 연속적인 손실이 발생할 수 있으며, 단일 포지션이 총 자본의 30%를 초과하지 않는 것을 엄격히 통제하는 것이 좋습니다. 다양한 시장 환경에서 전략의 성과는 현저한 차이가 있으며, 지속적인 모니터링과 변수 조정이 필요합니다.

전략 소스 코드
/*backtest
start: 2026-01-01 00:00:00
end: 2026-02-24 00:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"PAXG_USDT","balance":500000}]
*/

// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © DefinedEdge

//@version=6
strategy("SuperTrend AI Strategy [Adaptive]", "ST AI Strategy ◈",
     overlay         = true,
     initial_capital  = 10000,
     default_qty_type = strategy.percent_of_equity,
     default_qty_value= 30,
     commission_type  = strategy.commission.percent,
     commission_value = 0.06,
     slippage         = 2,
     pyramiding       = 0,
     calc_on_every_tick = false,
     max_labels_count = 500)

// ============================================================================
// INPUTS
// ============================================================================

// --- SuperTrend Core ---
GRP_ST  = "◈ SuperTrend"
i_atLen = input.int(10,    "ATR Length",        minval=1, maxval=50, group=GRP_ST)
i_bMult = input.float(3.0, "Base Multiplier",   minval=0.5, maxval=10.0, step=0.1, group=GRP_ST)
i_src   = input.source(hl2, "Source",           group=GRP_ST)

// --- Regime Detection ---
GRP_REG  = "◈ Regime Detection"
i_regLen = input.int(40,    "Regime Lookback",   minval=10, maxval=100, group=GRP_REG)
i_adxLen = input.int(14,    "ADX Length",        minval=5,  maxval=50,  group=GRP_REG)
i_adxThr = input.float(20,  "ADX Trend Threshold", minval=10, maxval=40, step=1, group=GRP_REG)
i_adapt  = input.bool(true, "Adaptive Multiplier", group=GRP_REG)

// --- AI Scoring ---
GRP_AI     = "◈ AI Engine"
i_trendLen = input.int(50,  "Trend EMA Length",  minval=10, maxval=200, group=GRP_AI)
i_volLen   = input.int(20,  "Volume MA Length",   minval=5,  maxval=50, group=GRP_AI)
i_minSc    = input.int(65,  "Min Signal Score",   minval=0,  maxval=90, group=GRP_AI, tooltip="Only enter trades when the AI score meets this threshold.")

// --- Risk Management ---
GRP_RISK = "◈ Risk Management"
i_slMode = input.string("ATR", "Stop Loss Mode", options=["ATR", "Percent", "SuperTrend"], group=GRP_RISK, tooltip="ATR: dynamic SL based on volatility. Percent: fixed %. SuperTrend: exit on trend flip.")
i_slAtr  = input.float(2.5,  "SL ATR Multiplier", minval=0.5, maxval=8.0, step=0.1, group=GRP_RISK)
i_slPct  = input.float(3.0,  "SL Percent",        minval=0.5, maxval=10.0, step=0.1, group=GRP_RISK)
i_tpMode = input.string("RR", "Take Profit Mode", options=["RR", "Percent", "None"], group=GRP_RISK, tooltip="RR: risk/reward ratio based on SL distance. Percent: fixed %. None: hold until SL or flip.")
i_tpRR   = input.float(2.5,  "TP Risk:Reward",    minval=0.5, maxval=10.0, step=0.1, group=GRP_RISK)
i_tpPct  = input.float(6.0,  "TP Percent",        minval=0.5, maxval=20.0, step=0.1, group=GRP_RISK)
i_trail  = input.bool(true,  "Trailing Stop",     group=GRP_RISK, tooltip="When enabled, SL trails in the direction of the trade after entry.")
i_trailAtr = input.float(2.5, "Trail ATR Mult",   minval=0.5, maxval=8.0, step=0.1, group=GRP_RISK)

// --- Filters ---
GRP_FLT  = "◈ Trade Filters"
i_trendF = input.bool(true,  "EMA Trend Filter",  group=GRP_FLT, tooltip="Only take longs above EMA, shorts below EMA.")
i_regF   = input.bool(true,  "Skip Ranging",      group=GRP_FLT, tooltip="Skip entries during ranging regime. Reduces whipsaws.")
i_volF   = input.bool(true,  "Volume Filter",      group=GRP_FLT, tooltip="Only enter if volume is above average.")
i_sigCD  = input.int(10,     "Cooldown (bars)",    minval=0, maxval=50, group=GRP_FLT)

// --- Backtest Window ---
GRP_BT   = "◈ Backtest"
bool inWindow = true 

// --- Visuals ---
GRP_VIS  = "◈ Visuals"
i_sGlow  = input.bool(true,  "Band Glow Effect",    group=GRP_VIS)
i_sRegBg = input.bool(true,  "Regime Background",   group=GRP_VIS)


// --- Colors ---
GRP_COL  = "◈ Colors"
i_cBull  = input.color(color.new(#089981, 0), "Bull",  inline="c1", group=GRP_COL)
i_cBear  = input.color(color.new(#f23645, 0), "Bear",  inline="c1", group=GRP_COL)

// ============================================================================
// CORE CALCULATIONS
// ============================================================================

int n = bar_index
float atr = ta.atr(i_atLen)
float safeAtr = nz(atr, 0.001)

// ── Regime Detection ──
float atrMa    = ta.sma(atr, i_regLen)
float atrRatio = atrMa > 0 ? atr / atrMa : 1.0

// ADX
float upMove   = high - high[1]
float dnMove   = low[1] - low
float plusDM    = upMove > dnMove and upMove > 0 ? upMove : 0
float minusDM  = dnMove > upMove and dnMove > 0 ? dnMove : 0
float trueR    = ta.tr
float smoothTR = ta.rma(trueR, i_adxLen)
float smoothPD = ta.rma(plusDM, i_adxLen)
float smoothND = ta.rma(minusDM, i_adxLen)
float plusDI    = smoothTR > 0 ? 100 * smoothPD / smoothTR : 0
float minusDI  = smoothTR > 0 ? 100 * smoothND / smoothTR : 0
float diSum    = plusDI + minusDI
float dx       = diSum > 0 ? 100 * math.abs(plusDI - minusDI) / diSum : 0
float adx      = ta.rma(dx, i_adxLen)

var int regime = 1
if atrRatio > 1.4
    regime := 2
else if adx < i_adxThr and atrRatio < 0.9
    regime := 0
else
    regime := 1

// ── Adaptive Multiplier ──
float adaptMult = i_bMult
if i_adapt
    if regime == 2
        adaptMult := i_bMult * (1.0 + (atrRatio - 1.0) * 0.4)
    else if regime == 0
        adaptMult := i_bMult * 0.85
adaptMult := math.max(math.min(adaptMult, i_bMult * 2.0), i_bMult * 0.5)

// ── SuperTrend ──
var float stBand  = na
var int   stDir   = 1

float upperBase = i_src + adaptMult * atr
float lowerBase = i_src - adaptMult * atr
float prevBand  = nz(stBand[1], stDir == 1 ? lowerBase : upperBase)

if stDir == 1
    stBand := math.max(lowerBase, prevBand)
    if close < stBand
        stDir  := -1
        stBand := upperBase
else
    stBand := math.min(upperBase, prevBand)
    if close > stBand
        stDir  := 1
        stBand := lowerBase

bool trendFlip = stDir != stDir[1]

// ── Trend EMA ──
float trendMa = ta.ema(close, i_trendLen)
bool  trendUp = close > trendMa
bool  trendDn = close < trendMa

// ── Volume ──
float volMa = ta.sma(volume, i_volLen)

// ============================================================================
// AI SIGNAL SCORING
// ============================================================================

scoreSignal(bool isBull) =>
    float score = 0

    // Factor 1: Volume surge (0-20)
    float vRat = volMa > 0 ? volume / volMa : 1.0
    score += vRat >= 2.5 ? 20 : vRat >= 1.5 ? 14 : vRat >= 1.0 ? 8 : 3

    // Factor 2: Displacement beyond band (0-25)
    float disp = isBull ? (close - stBand) : (stBand - close)
    float dispAtr = safeAtr > 0 ? disp / safeAtr : 0
    score += dispAtr >= 1.5 ? 25 : dispAtr >= 0.8 ? 18 : dispAtr >= 0.3 ? 12 : dispAtr > 0 ? 5 : 0

    // Factor 3: EMA trend alignment (0-20)
    bool aligned = (isBull and trendUp) or (not isBull and trendDn)
    float emaDist = math.abs(close - trendMa) / safeAtr
    score += aligned and emaDist > 0.5 ? 20 : aligned ? 14 : emaDist < 0.3 ? 8 : 2

    // Factor 4: Regime quality (0-15)
    score += regime == 1 ? 15 : regime == 2 ? 8 : 3

    // Factor 5: Band distance before flip (0-20)
    float prevDist = not na(stBand[1]) ? math.abs(close[1] - stBand[1]) / safeAtr : 0
    score += prevDist >= 2.0 ? 20 : prevDist >= 1.0 ? 14 : prevDist >= 0.5 ? 8 : 3

    int(math.min(math.round(score), 100))

// ============================================================================
// TRADE LOGIC
// ============================================================================

var int   lastEntryBar  = 0
var float entryPrice    = 0.0
var float slPrice       = 0.0
var float tpPrice       = 0.0
var int   lastSigScore  = 0
var int   lastSigDir    = 0
var bool  lastSigBright = false

bool longEntry  = false
bool shortEntry = false
int  sigScore   = 0

// Cooldown check
bool cdOk = (n - lastEntryBar) > i_sigCD

if trendFlip and inWindow and cdOk and barstate.isconfirmed
    if stDir == 1
        sigScore := scoreSignal(true)
        // Apply filters
        bool passScore = sigScore >= i_minSc
        bool passTrend = not i_trendF or trendUp
        bool passReg   = not i_regF or regime != 0
        bool passVol   = not i_volF or (volume > volMa)

        if passScore and passTrend and passReg and passVol
            longEntry := true

    else
        sigScore := scoreSignal(false)
        bool passScore = sigScore >= i_minSc
        bool passTrend = not i_trendF or trendDn
        bool passReg   = not i_regF or regime != 0
        bool passVol   = not i_volF or (volume > volMa)

        if passScore and passTrend and passReg and passVol
            shortEntry := true

// ── Calculate SL/TP Levels ──
float slDist = 0.0
if i_slMode == "ATR"
    slDist := atr * i_slAtr
else if i_slMode == "Percent"
    slDist := close * i_slPct / 100
else // SuperTrend mode
    slDist := math.abs(close - stBand)

float tpDist = 0.0
if i_tpMode == "RR"
    tpDist := slDist * i_tpRR
else if i_tpMode == "Percent"
    tpDist := close * i_tpPct / 100

// ── Execute Entries ──
if longEntry
    // Close any existing short
    if strategy.position_size < 0
        strategy.close("Short")

    entryPrice := close
    slPrice    := close - slDist
    tpPrice    := i_tpMode != "None" ? close + tpDist : na

    strategy.entry("Long", strategy.long)

    if not na(slPrice) and i_tpMode != "None" and not na(tpPrice)
        strategy.exit("Long Exit", "Long", stop=slPrice, limit=tpPrice,
             trail_points = i_trail ? slDist / syminfo.mintick : na,
             trail_offset = i_trail ? (atr * i_trailAtr) / syminfo.mintick : na)
    else if not na(slPrice)
        strategy.exit("Long SL", "Long", stop=slPrice,
             trail_points = i_trail ? slDist / syminfo.mintick : na,
             trail_offset = i_trail ? (atr * i_trailAtr) / syminfo.mintick : na)

    lastEntryBar  := n
    lastSigScore  := sigScore
    lastSigDir    := 1
    lastSigBright := sigScore >= 70

if shortEntry
    // Close any existing long
    if strategy.position_size > 0
        strategy.close("Long")

    entryPrice := close
    slPrice    := close + slDist
    tpPrice    := i_tpMode != "None" ? close - tpDist : na

    strategy.entry("Short", strategy.short)

    if not na(slPrice) and i_tpMode != "None" and not na(tpPrice)
        strategy.exit("Short Exit", "Short", stop=slPrice, limit=tpPrice,
             trail_points = i_trail ? slDist / syminfo.mintick : na,
             trail_offset = i_trail ? (atr * i_trailAtr) / syminfo.mintick : na)
    else if not na(slPrice)
        strategy.exit("Short SL", "Short", stop=slPrice,
             trail_points = i_trail ? slDist / syminfo.mintick : na,
             trail_offset = i_trail ? (atr * i_trailAtr) / syminfo.mintick : na)

    lastEntryBar  := n
    lastSigScore  := sigScore
    lastSigDir    := -1
    lastSigBright := sigScore >= 70

// ── SuperTrend Flip Exit (if SL mode is SuperTrend) ──
if i_slMode == "SuperTrend" and trendFlip
    if strategy.position_size > 0 and stDir == -1
        strategy.close("Long", comment="ST Flip")
    if strategy.position_size < 0 and stDir == 1
        strategy.close("Short", comment="ST Flip")

// ============================================================================
// VISUALS
// ============================================================================

// ── Band Color ──
color bandCore  = stDir == 1 ? i_cBull : i_cBear
color bandColor = regime == 2 ? color.new(#ffab00, 0) : regime == 0 ? color.new(#78909c, 20) : bandCore

// ── Glow ──
plot(i_sGlow ? stBand : na, "Glow Outer", color=color.new(bandColor, 85), linewidth=6, style=plot.style_linebr)
plot(i_sGlow ? stBand : na, "Glow Mid",   color=color.new(bandColor, 70), linewidth=4, style=plot.style_linebr)

// ── Band ──
plot(regime != 0 ? stBand : na, "Band (Solid)",   color=bandColor, linewidth=2, style=plot.style_linebr)
plot(regime == 0 ? stBand : na, "Band (Ranging)",  color=bandColor, linewidth=2, style=plot.style_linebr)

// ── Flip Dot ──
plot(trendFlip ? stBand : na, "Flip Dot", color=bandCore, style=plot.style_circles, linewidth=5, join=false)

// ── Regime Background ──
bgcolor(i_sRegBg and regime == 2 ? color.new(#ffab00, 95) : na, title="Volatile BG")
bgcolor(i_sRegBg and regime == 0 ? color.new(#78909c, 96) : na, title="Ranging BG")

// ── Trend EMA ──
plot(trendMa, "Trend EMA", color=color.new(trendUp ? i_cBull : i_cBear, 65), linewidth=1)

// ============================================================================
// HIDDEN PLOTS
// ============================================================================

plot(sigScore > 0 ? sigScore : na, "Signal Score", display=display.none)
plot(adaptMult,     "Adaptive Mult",  display=display.none)
plot(adx,           "ADX",            display=display.none)
plot(float(regime), "Regime",         display=display.none)
plot(float(stDir),  "Trend Direction", display=display.none)