SSL 채널과 웨이브 트렌드를 기반으로 한 양적 거래 전략


생성 날짜: 2024-02-19 11:47:38 마지막으로 수정됨: 2024-02-19 11:47:38
복사: 4 클릭수: 797
avatar of ChaoZhang ChaoZhang
1
집중하다
1617
수행원

SSL 채널과 웨이브 트렌드를 기반으로 한 양적 거래 전략

개요

이 전략은 주로 SSL 통로 지표와 파동 트렌드 지표에 기반하며, 다른 보조 지표와 결합하여 보다 완전한 양적 거래 전략을 구현한다. 이 전략 이름은 핵심 지표 SSL 통로와 파동 트렌드, 그리고 양적 거래의 키워드를 포함하고 있으며, 요구 사항에 부합한다.

전략 원칙

이 전략은 6개의 조건에 따라 거래가 이루어집니다. 그 중 첫 번째 두 가지는 핵심 조건입니다.

  1. SSL 혼성 지표의 기준은 파란색 ((상승) 또는 빨간색 ((하락) 이다.
  2. SSL 통로 지표 상부 포크 (상향) 또는 하부 포크 (하향)
  3. 파동 트렌드 지표 상부 포크 (상향) 또는 하부 포크 (하향)
  4. 입구 K선 높이는 임계값을 넘지 않는다.
  5. 입구 K 라인은 부린 통로 내부에 있다.
  6. 정지점은 평행선과 접촉하지 않는다.

이 6가지 조건이 동시에 충족될 때, 전략은 상장하거나 상장하지 않는다. 스톱로스 거리는 ATR 지표의 수치를 기준으로 계산되며, 스톱로스 거리는 스톱로스의 리스크 리워드 비율의 두 배이다.

이 전략은 또한 stop loss 설정, 포지션 규모 제어, 최대 철회 제어 등이 포함된 완벽한 위험 관리 메커니즘을 갖추고 있습니다. 또한, 이 전략은 차트에 보조 라인을 그리고 있으며, 각 스톱 및 스톱 포지션을 직관적으로 볼 수 있으며, 구체적인 손실 상황을 볼 수 있습니다. 이것은 분석 및 최적화 전략에 매우 도움이됩니다.

우위 분석

이 전략의 가장 큰 장점은 SSL 통로 지표를 사용하여 트렌드 방향을 판단하는 정확도가 매우 높으며, 파동 트렌드와 같은 지표와 함께 확인하면 가짜 신호를 크게 줄일 수 있다는 것입니다. 또한, 엄격한 입시 조건은 불필요한 거래를 피할 수 있으며, 따라서 거래 횟수를 줄이고 거래 비용을 줄일 수 있습니다.

또한, 이 전략의 완벽한 위험과 자금 관리 메커니즘도 큰 장점이다. 미리 설정된 좋은 중지 손실 및 중지 전략은 단일 거래의 최대 손실을 효과적으로 제어할 수 있다. 포지션 규모의 제어와 함께, 계좌의 최대 인출을 감당할 수 있는 범위 내에서 제어할 수 있다.

위험 분석

이 전략의 가장 큰 위험은, 엄격한 입시 조건으로 거래 기회를 놓치게 되고, 이로 인해 수익성에 영향을 미치게 된다. 시장이 흔들리는 상태일 때 이 전략의 수익성은 또한 할인된다.

또한, 파동 트렌드 같은 지표가 시장 트렌드를 판단하는 효과에 영향을 미치기도 한다. 이때는 파라미터를 조정하거나, 다른 지표를 추가하여 확인해야 한다.

전반적으로, 이 전략의 위험은 통제할 수 있다. 매개 변수를 조정하고 최적화함으로써 전략이 다른 시장 환경에 더 잘 적응할 수 있다.

최적화 방향

이 전략은 다음과 같은 몇 가지 측면에서 최적화될 수 있습니다.

  1. 트렌드 전환점을 더 정확하게 판단할 수 있도록 파동 트렌드의 매개 변수를 최적화합니다.

  2. KDJ, MACD 등과 같은 다른 지표를 추가하여 확인하여 가짜 돌파의 영향을 피하십시오.

  3. 다양한 품종, 다양한 주기에 따라 매개 변수를 조정하여 최적화하여 전략의 안정성을 높일 수 있습니다.

  4. 기계 학습 알고리즘을 추가하고, 역사 데이터 훈련을 사용하여 실시간 최적화 전략 매개 변수

  5. 높은 주파수 인수와 같은 알고리즘을 활용하여 전략의 거래 빈도 및 수익성을 높이는 전략

이러한 최적화 조치의 실행은 이 전략의 수익성과 안정성을 더 높은 수준으로 끌어올릴 것으로 기대된다.

요약하다

전체적으로, 이 전략은 다양한 지표와 엄격한 입시 메커니즘을 통합하고, 높은 승률을 보장하면서도, 또한 좋은 위험 통제 효과를 달성한다. 미래 최적화 방향과 결합하면, 이 전략은 큰 발전 잠재력을 가지고 있으며, 권장되는 양적 거래 전략이다.

전략 소스 코드
/*backtest
start: 2024-01-01 00:00:00
end: 2024-01-31 23:59:59
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

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

// @credits
//      - Wave Trend:               Indicator: WaveTrend Oscillator [WT] by @LazyBear
//      - SSL Channel:              SSL channel by @ErwinBeckers
//      - SSL Hybrid:               SSL Hybrid by @Mihkel00
//      - Keltner Channels:         Keltner Channels Bands by @ceyhun
//      - Candle Height:            Candle Height in Percentage - Columns by @FreeReveller
//      - NNFX ATR:                 NNFX ATR by @sueun123
// 
// Strategy: Based on the YouTube video "This Unique Strategy Made 47% Profit in 2.5 Months [SSL + Wave Trend Strategy Tested 100 Times]" by TradeSmart.


// @description
//
// Strategy incorporates the following features:
//
//      - Risk management:  Configurable X% loss per stop loss
//                          Configurable R:R ratio
//
//      - Trade entry:      Based on strategy conditions below
//
//      - Trade exit:       Based on strategy conditions below
//
//      - Backtesting:      Configurable backtesting range by date
//
//      - Chart drawings:   Each entry condition indicator can be turned on and off
//                          TP/SL boxes drawn for all trades. Can be turned on and off
//                          Trade exit information labels. Can be turned on and off
//                          NOTE: Trade drawings will only be applicable when using overlay strategies
//
//      - Alerting:         Alerts on LONG and SHORT trade entries
//
//      - Debugging:        Includes section with useful debugging techniques
//
// Strategy conditions:
//
//      - Trade entry:      LONG:   C1: SSL Hybrid baseline is BLUE
//                                  C2: SSL Channel crosses up (green on top)
//                                  C3: Wave Trend crosses up (represented by pink candle body)
//                                  C4: Entry candle height is not greater than configured threshold
//                                  C5: Entry candle is inside Keltner Channel (wicks or body depending on configuration)
//                                  C6: Take Profit target does not touch EMA (represents resistance)
//
//                          SHORT:  C1: SSL Hybrid baseline is RED
//                                  C2: SSL Channel crosses down (red on top)
//                                  C3: Wave Trend crosses down (represented by orange candle body)
//                                  C4: Entry candle height is not greater than configured threshold
//                                  C5: Entry candle is inside Keltner Channel (wicks or body depending on configuration)
//                                  C6: Take Profit target does not touch EMA (represents support)
//
//      - Trade exit:       Stop Loss: Size configurable with NNFX ATR multiplier
//                          Take Profit: Calculated from Stop Loss using R:R ratio

//@version=5
INITIAL_CAPITAL = 1000
DEFAULT_COMMISSION = 0.02
MAX_DRAWINGS = 500
IS_OVERLAY = true

strategy("SSL + Wave Trend Strategy", overlay = IS_OVERLAY, initial_capital = INITIAL_CAPITAL, currency = currency.NONE, max_labels_count = MAX_DRAWINGS, max_boxes_count = MAX_DRAWINGS, max_lines_count = MAX_DRAWINGS, default_qty_type = strategy.cash, commission_type = strategy.commission.percent, commission_value = DEFAULT_COMMISSION)

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

// ----------------------
// Trade Entry Conditions
// ----------------------
useSslHybrid        = input.bool    (true,  "Use SSL Hybrid Condition",                     group = "Strategy: Entry Conditions",   inline = "SC1")
useKeltnerCh        = input.bool    (true,  "Use Keltner Channel Condition   ",             group = "Strategy: Entry Conditions",   inline = "SC2")
keltnerChWicks      = input.bool    (true,  "Keltner Channel Include Wicks",                group = "Strategy: Entry Conditions",   inline = "SC2")
useEma              = input.bool    (true,  "Target not touch EMA Condition",               group = "Strategy: Entry Conditions",   inline = "SC3")
useCandleHeight     = input.bool    (true,  "Use Candle Height Condition",                  group = "Strategy: Entry Conditions",   inline = "SC4")
candleHeight        = input.float   (1.0,   "Candle Height Threshold     ",                 group = "Strategy: Entry Conditions",   inline = "SC5", minval = 0, step = 0.1, tooltip = "Percentage difference between high and low of a candle. Expressed as a decimal. Lowering this value will filter out trades on volatile candles.")

// ---------------------
// Trade Exit Conditions
// ---------------------
slAtrMultiplier     = input.float   (1.7,   "Stop Loss ATR Multiplier     ",                group = "Strategy: Exit Conditions",    inline = "EC1", minval = 0, step = 0.1,     tooltip = "Size of StopLoss is determined by multiplication of ATR value. Take Profit is derived from this also by multiplying the StopLoss value by the Risk:Reward multiplier.")

// ---------------
// Risk Management
// ---------------
riskReward          = input.float   (2.5,  "Risk : Reward        1 :",                      group = "Strategy: Risk Management",    inline = "RM1", minval = 0, step = 0.1,     tooltip = "Used to determine Take Profit level. Take Profit will be Stop Loss multiplied by this value.")
accountRiskPercent  = input.float   (1,    "Portfolio Risk %         ",                     group = "Strategy: Risk Management",    inline = "RM2", minval = 0, step = 0.1,     tooltip = "Percentage of portfolio you lose if trade hits SL.\n\nYou then stand to gain\n  Portfolio Risk % * Risk : Reward\nif trade hits TP.")

// ----------
// Date Range
// ----------
startYear           = input.int     (2022,  "Start Date       ",                            group = "Strategy: Date Range",     inline = "DR1", minval    = 1900, maxval = 2100)
startMonth          = input.int     (1,     "",                                             group = "Strategy: Date Range",     inline = "DR1", options   = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
startDate           = input.int     (1,     "",                                             group = "Strategy: Date Range",     inline = "DR1", options   = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31])
endYear             = input.int     (2100,  "End Date      ",                               group = "Strategy: Date Range",     inline = "DR2", minval    = 1900, maxval = 2100)
endMonth            = input.int     (1,     "",                                             group = "Strategy: Date Range",     inline = "DR2", options   = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
endDate             = input.int     (1,     "",                                             group = "Strategy: Date Range",     inline = "DR2", options   = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31])

// ----------------
// Display Settings
// ----------------
showTpSlBoxes       = input.bool    (true,  "Show TP / SL Boxes",                           group = "Strategy: Drawings",       inline = "D1",  tooltip = "Show or hide TP and SL position boxes.\n\nNote: TradingView limits the maximum number of boxes that can be displayed to 500 so they may not appear for all price data under test.")
showLabels          = input.bool    (false, "Show Trade Exit Labels",                       group = "Strategy: Drawings",       inline = "D2",  tooltip = "Useful labels to identify Profit/Loss and cumulative portfolio capital after each trade closes.\n\nAlso note that TradingView limits the max number of 'boxes' that can be displayed on a chart (max 500). This means when you lookback far enough on the chart you will not see the TP/SL boxes. However you can check this option to identify where trades exited.")

// ------------------
// Indicator Settings
// ------------------

// Indicator display options
showSslHybrid       = input.bool    (true,  "Show SSL Hybrid",                              group = "Indicators: Drawings",     inline = "ID1")
showSslChannel      = input.bool    (true,  "Show SSL Channel",                             group = "Indicators: Drawings",     inline = "ID2")
showEma             = input.bool    (true,  "Show EMA",                                     group = "Indicators: Drawings",     inline = "ID3")
showKeltner         = input.bool    (true,  "Show Keltner Channel",                         group = "Indicators: Drawings",     inline = "ID4")
showWaveTrend       = input.bool    (true,  "Show Wave Trend Flip Candles",                 group = "Indicators: Drawings",     inline = "ID5")
showAtrSl           = input.bool    (true,  "Show ATR Stop Loss Bands",                     group = "Indicators: Drawings",     inline = "ID6")

// Wave Trend Settings
n1                  = input.int     (10,     "Channel Length         ",                     group = "Indicators: Wave Trend",   inline = "WT1")
n2                  = input.int     (21,     "Average Length         ",                     group = "Indicators: Wave Trend",   inline = "WT2")
obLevel1            = input.int     (60,     "Over Bought Level 1       ",                  group = "Indicators: Wave Trend",   inline = "WT3")
obLevel2            = input.int     (53,     "Over Bought Level 2       ",                  group = "Indicators: Wave Trend",   inline = "WT4")
osLevel1            = input.int     (-60,    "Over Sold Level 1         ",                  group = "Indicators: Wave Trend",   inline = "WT5")
osLevel2            = input.int     (-53,    "Over Sold Level 2         ",                  group = "Indicators: Wave Trend",   inline = "WT6")

// SSL Channel Settings
sslChLen            = input.int     (10,     "Period             ",                         group = "Indicators: SSL Channel",  inline = "SC1")

// SSL Hybrid Settings
// Show/hide Inputs
show_color_bar      = input.bool    (false, "Show Color Bars",                              group = "Indicators: SSL Hybrid",   inline = "SH2")
// Baseline Inputs
maType              = input.string  ("HMA", "Baseline Type                       ",         group = "Indicators: SSL Hybrid",   inline = "SH3", options=["SMA", "EMA", "DEMA", "TEMA", "LSMA", "WMA", "MF", "VAMA", "TMA", "HMA", "JMA", "Kijun v2", "EDSMA", "McGinley"])
len                 = input.int     (60,    "Baseline Length                      ",        group = "Indicators: SSL Hybrid",   inline = "SH4")
src                 = input.source  (close, "Source                          ",             group = "Indicators: SSL Hybrid",   inline = "SH5")
kidiv               = input.int     (1,     "Kijun MOD Divider                     ",       group = "Indicators: SSL Hybrid",   inline = "SH6", maxval=4)
jurik_phase         = input.int     (3,     "* Jurik (JMA) Only - Phase                 ",  group = "Indicators: SSL Hybrid",   inline = "SH7")
jurik_power         = input.int     (1,     "* Jurik (JMA) Only - Power                 ",  group = "Indicators: SSL Hybrid",   inline = "SH8")
volatility_lookback = input.int     (10,    "* Volatility Adjusted (VAMA) Only - Volatility lookback length", group = "Indicators: SSL Hybrid",   inline = "SH9")
//Modular Filter Inputs
beta                = input.float   (0.8,   "Modular Filter, General Filter Only - Beta               ",   group = "Indicators: SSL Hybrid",   inline = "SH10", minval=0, maxval=1, step=0.1)
feedback            = input.bool    (false, "Modular Filter Only - Feedback",               group = "Indicators: SSL Hybrid",   inline = "SH11")
z                   = input.float   (0.5,   "Modular Filter Only - Feedback Weighting               ",     group = "Indicators: SSL Hybrid",   inline = "SH12", step=0.1, minval=0, maxval=1)
//EDSMA Inputs
ssfLength           = input.int     (20,    "EDSMA - Super Smoother Filter Length               ",         group = "Indicators: SSL Hybrid",   inline = "SH13", minval=1)
ssfPoles            = input.int     (2,     "EDSMA - Super Smoother Filter Poles               ",          group = "Indicators: SSL Hybrid",   inline = "SH14", options=[2, 3])
///Keltner Baseline Channel Inputs
useTrueRange        = input.bool    (true,  "Use True Range?",                              group = "Indicators: SSL Hybrid",   inline = "SH15")
multy               = input.float   (0.2,   "Base Channel Multiplier                   ",   group = "Indicators: SSL Hybrid",   inline = "SH16",    step=0.05)

// EMA Settings
emaLength           = input.int     (200,   "EMA Length          ",                         group = "Indicators: EMA",          inline = "E1",      minval = 1)

// Keltner Channel Settings
kcLength            = input.int     (20,    "Length              ",                         group = "Indicators: Keltner Channel",  inline = "KC1", minval=1)
kcMult              = input.float   (1.5,   "Multiplier             ",                      group = "Indicators: Keltner Channel",  inline = "KC2")
kcSrc               = input.source  (close, "Source              ",                         group = "Indicators: Keltner Channel",  inline = "KC3")
alen                = input.int     (10,    "ATR Length            ",                       group = "Indicators: Keltner Channel",  inline = "KC4", minval=1)

// Candle Height in Percentage Settings
chPeriod            = input.int     (20,    "Period             ",                          group = "Indicators: Candle Height",    inline = "CH1")

// NNFX ATR Settings
nnfxAtrLength       = input.int     (14,    "Length              ",                         group = "Indicators: NNFX ATR (Stop Loss Settings)",    inline = "ATR1", minval = 1)
nnfxSmoothing       = input.string  ("RMA", "Smoothing            ",                        group = "Indicators: NNFX ATR (Stop Loss Settings)",    inline = "ATR3", options = ["RMA", "SMA", "EMA", "WMA"])

// =============================================================================
// INDICATORS
// =============================================================================

// ----------
// Wave Trend
// ----------
ap          = hlc3
esa         = ta.ema(ap, n1)
d           = ta.ema(math.abs(ap - esa), n1)
ci          = (ap - esa) / (0.015 * d)
tci         = ta.ema(ci, n2)
wt1         = tci
wt2         = ta.sma(wt1, 4)

// Show Wave Trend crosses on chart as colour changes (pink bullish, orange bearish)
wtBreakUp   = ta.crossover  (wt1, wt2)
wtBreakDown = ta.crossunder (wt1, wt2)
barColour   = showWaveTrend ? wtBreakUp ? color.fuchsia : wtBreakDown ? color.orange : na : na
barcolor(color = barColour)

// -----------
// SSL Channel
// -----------
smaHigh             = ta.sma(high, sslChLen)
smaLow              = ta.sma(low, sslChLen)
var int sslChHlv    = na
sslChHlv           := close > smaHigh ? 1 : close < smaLow ? -1 : sslChHlv[1]
sslChDown           = sslChHlv < 0 ? smaHigh : smaLow
sslChUp             = sslChHlv < 0 ? smaLow : smaHigh

plot(showSslChannel ? sslChDown : na, "SSL Channel Down", linewidth=1, color=color.new(color.red, 30))
plot(showSslChannel ? sslChUp   : na, "SSL Channel Up",   linewidth=1, color=color.new(color.lime, 30))

// ----------
// SSL Hybrid
// ----------
//EDSMA
get2PoleSSF(src, length) =>
    PI  = 2 * math.asin(1)
    arg = math.sqrt(2) * PI / length
    a1  = math.exp(-arg)
    b1  = 2 * a1 * math.cos(arg)
    c2  = b1
    c3  = -math.pow(a1, 2)
    c1  = 1 - c2 - c3

    ssf = 0.0
    ssf:= c1 * src + c2 * nz(ssf[1]) + c3 * nz(ssf[2])
    ssf

get3PoleSSF(src, length) =>
    PI      = 2 * math.asin(1)

    arg     = PI / length
    a1      = math.exp(-arg)
    b1      = 2 * a1 * math.cos(1.738 * arg)
    c1      = math.pow(a1, 2)

    coef2   = b1 + c1
    coef3   = -(c1 + b1 * c1)
    coef4   = math.pow(c1, 2)
    coef1   = 1 - coef2 - coef3 - coef4

    ssf     = 0.0
    ssf    := coef1 * src + coef2 * nz(ssf[1]) + coef3 * nz(ssf[2]) + coef4 * nz(ssf[3])
    ssf

ma(type, src, len) =>
    float result = 0
    if type == "TMA"
        result := ta.sma(ta.sma(src, math.ceil(len / 2)), math.floor(len / 2) + 1)
        result
    if type == "MF"
        ts      = 0.
        b       = 0.
        c       = 0.
        os      = 0.
        //----
        alpha   = 2 / (len + 1)
        a       = feedback ? z * src + (1 - z) * nz(ts[1], src) : src
        //----
        b      := a > alpha * a + (1 - alpha) * nz(b[1], a) ? a : alpha * a + (1 - alpha) * nz(b[1], a)
        c      := a < alpha * a + (1 - alpha) * nz(c[1], a) ? a : alpha * a + (1 - alpha) * nz(c[1], a)
        os     := a == b ? 1 : a == c ? 0 : os[1]
        //----
        upper   = beta * b + (1 - beta) * c
        lower   = beta * c + (1 - beta) * b
        ts     := os * upper + (1 - os) * lower
        result := ts
        result
    if type == "LSMA"
        result := ta.linreg(src, len, 0)
        result
    if type == "SMA"  // Simple
        result := ta.sma(src, len)
        result
    if type == "EMA"  // Exponential
        result := ta.ema(src, len)
        result
    if type == "DEMA"  // Double Exponential
        e = ta.ema(src, len)
        result := 2 * e - ta.ema(e, len)
        result
    if type == "TEMA"  // Triple Exponential
        e = ta.ema(src, len)
        result := 3 * (e - ta.ema(e, len)) + ta.ema(ta.ema(e, len), len)
        result
    if type == "WMA"  // Weighted
        result := ta.wma(src, len)
        result
    if type == "VAMA"  // Volatility Adjusted
        /// Copyright © 2019 to present, Joris Duyck (JD)
        mid     = ta.ema(src, len)
        dev     = src - mid
        vol_up  = ta.highest(dev, volatility_lookback)
        vol_down= ta.lowest(dev, volatility_lookback)
        result := mid + math.avg(vol_up, vol_down)
        result
    if type == "HMA"  // Hull
        result := ta.wma(2 * ta.wma(src, len / 2) - ta.wma(src, len), math.round(math.sqrt(len)))
        result
    if type == "JMA"  // Jurik
        /// Copyright © 2018 Alex Orekhov (everget)
        /// Copyright © 2017 Jurik Research and Consulting.
        phaseRatio  = jurik_phase < -100 ? 0.5 : jurik_phase > 100 ? 2.5 : jurik_phase / 100 + 1.5
        beta        = 0.45 * (len - 1) / (0.45 * (len - 1) + 2)
        alpha       = math.pow(beta, jurik_power)
        jma         = 0.0
        e0          = 0.0
        e0         := (1 - alpha) * src + alpha * nz(e0[1])
        e1          = 0.0
        e1         := (src - e0) * (1 - beta) + beta * nz(e1[1])
        e2          = 0.0
        e2         := (e0 + phaseRatio * e1 - nz(jma[1])) * math.pow(1 - alpha, 2) + math.pow(alpha, 2) * nz(e2[1])
        jma        := e2 + nz(jma[1])
        result     := jma
        result
    if type == "Kijun v2"
        kijun           = math.avg(ta.lowest(len), ta.highest(len))  //, (open + close)/2)
        conversionLine  = math.avg(ta.lowest(len / kidiv), ta.highest(len / kidiv))
        delta           = (kijun + conversionLine) / 2
        result         := delta
        result
    if type == "McGinley"
        mg              = 0.0
        mg             := na(mg[1]) ? ta.ema(src, len) : mg[1] + (src - mg[1]) / (len * math.pow(src / mg[1], 4))
        result         := mg
        result
    if type == "EDSMA"
        zeros       = src - nz(src[2])
        avgZeros    = (zeros + zeros[1]) / 2
        // Ehlers Super Smoother Filter 
        ssf         = ssfPoles == 2 ? get2PoleSSF(avgZeros, ssfLength) : get3PoleSSF(avgZeros, ssfLength)
        // Rescale filter in terms of Standard Deviations
        stdev       = ta.stdev(ssf, len)
        scaledFilter= stdev != 0 ? ssf / stdev : 0

        alpha       = 5 * math.abs(scaledFilter) / len

        edsma       = 0.0
        edsma      := alpha * src + (1 - alpha) * nz(edsma[1])
        result     := edsma
        result
    result

///Keltner Baseline Channel
BBMC        = ma(maType, close, len)
Keltma      = ma(maType, src, len)
range_1     = useTrueRange ? ta.tr : high - low
rangema     = ta.ema(range_1, len)
upperk      = Keltma + rangema * multy
lowerk      = Keltma - rangema * multy

//COLORS
color_bar   = close > upperk ? #00c3ff : close < lowerk ? #ff0062 : color.gray

//PLOTS
p1          = plot(showSslHybrid ? BBMC : na, color=color.new(color_bar, 0), linewidth=4, title="MA Baseline")
barcolor(show_color_bar ? color_bar : na)

// ---
// EMA
// ---
ema         = ta.ema(close, emaLength)
plot(showEma ? ema : na, "EMA Trend Line", color.white)

// ----------------
// Keltner Channels
// ----------------
kcMa        = ta.ema(kcSrc, kcLength)

KTop2       = kcMa + kcMult * ta.atr(alen)
KBot2       = kcMa - kcMult * ta.atr(alen)

upperPlot   = plot(showKeltner ? KTop2 : na, color=color.new(color.blue, 0), title="Upper", style = plot.style_stepline)
lowerPlot   = plot(showKeltner ? KBot2 : na, color=color.new(color.blue, 0), title="Lower", style = plot.style_stepline)

// ---------------------------
// Candle Height in Percentage
// ---------------------------
percentHL   = (high - low) / low * 100
percentRed  = open > close ? (open - close) / close * 100 : 0
percentGreen= open < close ? (close - open) / open * 100 : 0

// --------
// NNFX ATR
// --------
function(source, length) =>
    if nnfxSmoothing == "RMA"
        ta.rma(source, nnfxAtrLength)
    else
        if nnfxSmoothing == "SMA"
            ta.sma(source, nnfxAtrLength)
        else
            if nnfxSmoothing == "EMA"
                ta.ema(source, nnfxAtrLength)
            else
                ta.wma(source, nnfxAtrLength)

formula(number, decimals) =>
    factor  = math.pow(10, decimals)
    int(number * factor) / factor

nnfxAtr     = formula(function(ta.tr(true), nnfxAtrLength), 5) * slAtrMultiplier

//Sell
longSlAtr   = nnfxAtrLength ? close - nnfxAtr : close + nnfxAtr
shortSlAtr  = nnfxAtrLength ? close + nnfxAtr : close - nnfxAtr

plot(showAtrSl ? longSlAtr : na,     "Long SL",  color = color.new(color.red, 35), linewidth = 1, trackprice = true, editable = true, style = plot.style_stepline)
plot(showAtrSl ? shortSlAtr : na,    "Short SL", color = color.new(color.red, 35), linewidth = 1, trackprice = true, editable = true, style = plot.style_stepline)


// =============================================================================
// FUNCTIONS
// =============================================================================

percentAsPoints(pcnt) =>
    math.round(pcnt / 100 * close / syminfo.mintick)
    
calcStopLossPrice(pointsOffset, isLong) =>
    priceOffset = pointsOffset * syminfo.mintick
    if isLong
        close - priceOffset
    else 
        close + priceOffset

calcProfitTrgtPrice(pointsOffset, isLong) =>
    calcStopLossPrice(-pointsOffset, isLong)
    
        
printLabel(barIndex, msg) => label.new(barIndex, close, msg)

printTpSlHitBox(left, right, slHit, tpHit, entryPrice, slPrice, tpPrice) => 
    if showTpSlBoxes
        box.new (left = left,   top = entryPrice,   right = right,  bottom = slPrice,   bgcolor = slHit ? color.new(color.red, 60)   : color.new(color.gray, 90), border_width = 0)
        box.new (left = left,   top = entryPrice,   right = right,  bottom = tpPrice,   bgcolor = tpHit ? color.new(color.green, 60) : color.new(color.gray, 90), border_width = 0)
        line.new(x1 = left,     y1 = entryPrice,    x2 = right,     y2 = entryPrice,    color = color.new(color.yellow, 20))
        line.new(x1 = left,     y1 = slPrice,       x2 = right,     y2 = slPrice,       color = color.new(color.red, 20))
        line.new(x1 = left,     y1 = tpPrice,       x2 = right,     y2 = tpPrice,       color = color.new(color.green, 20))
        
printTpSlNotHitBox(left, right, entryPrice, slPrice, tpPrice) => 
    if showTpSlBoxes
        box.new (left = left,   top = entryPrice,   right = right,  bottom = slPrice,   bgcolor = color.new(color.gray, 90), border_width = 0)
        box.new (left = left,   top = entryPrice,   right = right,  bottom = tpPrice,   bgcolor = color.new(color.gray, 90), border_width = 0)
        line.new(x1 = left,     y1 = entryPrice,    x2 = right,     y2 = entryPrice,    color = color.new(color.yellow, 20))
        line.new(x1 = left,     y1 = slPrice,       x2 = right,     y2 = slPrice,       color = color.new(color.red, 20))
        line.new(x1 = left,     y1 = tpPrice,       x2 = right,     y2 = tpPrice,       color = color.new(color.green, 20))
        
printTradeExitLabel(x, y, posSize, entryPrice, pnl) => 
    if showLabels
        labelStr = "Position Size: " + str.tostring(math.abs(posSize), "#.##") + "\nPNL: " + str.tostring(pnl, "#.##") + "\nCapital: " + str.tostring(strategy.equity, "#.##") + "\nEntry Price: " + str.tostring(entryPrice, "#.##")
        label.new(x = x, y = y, text = labelStr, color = pnl > 0 ? color.new(color.green, 60) : color.new(color.red, 60), textcolor = color.white, style = label.style_label_down)

// =============================================================================
// STRATEGY LOGIC
// =============================================================================

// See strategy description at top for details on trade entry/exit logis

// ----------
// CONDITIONS
// ----------

// Trade entry and exit variables
var tradeEntryBar   = bar_index
var profitPoints    = 0.
var lossPoints      = 0.
var slPrice         = 0.
var tpPrice         = 0.
var inLong          = false
var inShort         = false

// Exit calculations
slAmount            = nnfxAtr
slPercent           = math.abs((1 - (close - slAmount) / close) * 100)
tpPercent           = slPercent * riskReward
tpPoints            = percentAsPoints(tpPercent)
tpTarget            = calcProfitTrgtPrice(tpPoints, wtBreakUp)

inDateRange         = true

// Condition 1: SSL Hybrid blue for long or red for short
bullSslHybrid       = useSslHybrid ? close > upperk : true
bearSslHybrid       = useSslHybrid ? close < lowerk : true

// Condition 2: SSL Channel crosses up for long or down for short
bullSslChannel      = ta.crossover(sslChUp, sslChDown)
bearSslChannel      = ta.crossover(sslChDown, sslChUp)

// Condition 3: Wave Trend crosses up for long or down for short
bullWaveTrend       = wtBreakUp
bearWaveTrend       = wtBreakDown

// Condition 4: Entry candle heignt <= 0.6 on Candle Height in Percentage
candleHeightValid   = useCandleHeight ? percentGreen <= candleHeight and percentRed <= candleHeight : true

// Condition 5: Entry candle is inside Keltner Channel
withinCh = keltnerChWicks ? high < KTop2 and low > KBot2 : open < KTop2 and close < KTop2 and open > KBot2 and close > KBot2
insideKeltnerCh     = useKeltnerCh ? withinCh : true

// Condition 6: TP target does not touch 200 EMA
bullTpValid         = useEma ? not (close < ema and tpTarget > ema) : true
bearTpValid         = useEma ? not (close > ema and tpTarget < ema) : true

// Combine all entry conditions
goLong              = inDateRange and bullSslHybrid and bullSslChannel and bullWaveTrend and candleHeightValid and insideKeltnerCh and bullTpValid
goShort             = inDateRange and bearSslHybrid and bearSslChannel and bearWaveTrend and candleHeightValid and insideKeltnerCh and bearTpValid

// Entry decisions
openLong            = (goLong and not inLong)
openShort           = (goShort and not inShort)
flippingSides       = (goLong and inShort) or (goShort and inLong)
enteringTrade       = openLong or openShort
inTrade             = inLong or inShort

// Risk calculations
riskAmt             = strategy.equity * accountRiskPercent / 100
entryQty            = math.abs(riskAmt / slPercent * 100)  / close

if openLong
    if strategy.position_size < 0
        printTpSlNotHitBox(tradeEntryBar + 1, bar_index + 1, strategy.position_avg_price, slPrice, tpPrice)
        printTradeExitLabel(bar_index + 1, math.max(tpPrice, slPrice), strategy.position_size, strategy.position_avg_price, strategy.openprofit)
    strategy.entry("Long", strategy.long, qty = entryQty, alert_message = "Long Entry")
    enteringTrade   := true
    inLong          := true
    inShort         := false
    alert(message="BUY Trade Entry Alert", freq=alert.freq_once_per_bar_close)

if openShort
    if strategy.position_size > 0
        printTpSlNotHitBox(tradeEntryBar + 1, bar_index + 1, strategy.position_avg_price, slPrice, tpPrice)
        printTradeExitLabel(bar_index + 1, math.max(tpPrice, slPrice), strategy.position_size, strategy.position_avg_price, strategy.openprofit)
    strategy.entry("Short", strategy.short, qty = entryQty, alert_message = "Short Entry")
    enteringTrade   := true
    inShort         := true
    inLong          := false
    alert(message="SELL Trade Entry Alert", freq=alert.freq_once_per_bar_close)

if enteringTrade
    profitPoints    := percentAsPoints(tpPercent)
    lossPoints      := percentAsPoints(slPercent)
    slPrice         := calcStopLossPrice(lossPoints, openLong)
    tpPrice         := calcProfitTrgtPrice(profitPoints, openLong)
    tradeEntryBar   := bar_index

strategy.exit("TP/SL", profit = profitPoints, loss = lossPoints, comment_profit = "TP Hit", comment_loss = "SL Hit", alert_profit = "TP Hit Alert", alert_loss = "SL Hit Alert")

// =============================================================================
// DRAWINGS
// =============================================================================

// -----------
// TP/SL Boxes
// -----------

slHit           = (inShort and high >= slPrice) or (inLong  and low <= slPrice)
tpHit           = (inLong  and high >= tpPrice) or (inShort and low <= tpPrice)
exitTriggered   = slHit or tpHit
entryPrice      = strategy.closedtrades.entry_price (strategy.closedtrades - 1)
pnl             = strategy.closedtrades.profit      (strategy.closedtrades - 1)
posSize         = strategy.closedtrades.size        (strategy.closedtrades - 1)

// Print boxes for trades closed at profit or loss
if (inTrade and exitTriggered) 
    inShort    := false
    inLong     := false 
    // printTpSlHitBox(tradeEntryBar + 1, bar_index, slHit, tpHit, entryPrice, slPrice, tpPrice)
    // printTradeExitLabel(bar_index, math.max(tpPrice, slPrice), posSize, entryPrice, pnl)

// Print TP/SL box for current open trade
if barstate.islastconfirmedhistory and strategy.position_size != 0
    printTpSlNotHitBox(tradeEntryBar + 1, bar_index + 1, strategy.position_avg_price, slPrice, tpPrice)
    
// =============================================================================
// DEBUGGING
// =============================================================================

// Data window plots
plotchar(goLong,  "Enter Long",  "")
plotchar(goShort, "Enter Short", "")