커널 평활 다중 이동 평균을 기반으로 하는 적응형 추세 추적 시스템

MA RSI ATR MAs NW 趋势追踪 核平滑 滑动止损
생성 날짜: 2025-03-28 15:13:28 마지막으로 수정됨: 2025-03-28 15:13:28
복사: 2 클릭수: 336
avatar of ianzeng123 ianzeng123
2
집중하다
319
수행원

커널 평활 다중 이동 평균을 기반으로 하는 적응형 추세 추적 시스템 커널 평활 다중 이동 평균을 기반으로 하는 적응형 추세 추적 시스템

개요

이 핵 평준화 다중 평균에 기반한 자기 적응 트렌드 추적 시스템은 5개의 사용자 정의 이동 평균, 다층 필터 및 확인 메커니즘을 통합하여 지속적인 시장 추세를 식별하고 활용하는 고급 정량 거래 전략입니다. 이 전략은 전통적인 이동 평균 대신 핵 평준화 기술을 적용하여 다양한 시장 조건과 시간 프레임에 적응할 수 있는 더 유연한 평준화 효과와 자기 적응력을 제공합니다.

핵심 기능은 다음과 같습니다: 5 개의 이동 평균으로 구성된 “평균 선 줄”을 사용하여 현재 시장 추세를 시각화; RSI 필터, 트렌드 강도 필터 및 트렌드 확인 기간을 통해 소음 및 가짜 신호를 줄이; 특정 조건이 충족되면만 입문 신호를 유발; 그리고 여러 가지 탈퇴 옵션을 사용하여 위험을 관리하고 수익을 보호하십시오 (퍼센트 추적 스톱, ATR 추적 스톱, ATR 수익 목표 및 하드 스톱).

전략 원칙

이 전략의 핵심 논리는 다음과 같은 핵심 구성 요소를 중심으로 펼쳐진다.

  1. 핵 평평한 이동 평균전략: 표준 이동 평균을 대신하여 핵 평준화 기술을 사용하여 전통적인 MA보다 더 유연하고 적응 가능한 평준화를 제공합니다. 3 가지 핵 유형을 지원합니다.

    • 베타 핵: 가장 강력한 옵션으로alpha그리고beta매개 변수가 독립적으로 제어되어 있고, 이는 MA가 가격 상승과 하락에 대해 다르게 반응하도록 한다.
    • 고스쿠어: 시계 모양의 무게를 만들어,bandwidth파라미터는 시계 곡선의 폭을 제어한다.
    • 에파네치니코프 핵: 고스 핵과 비슷하지만 모양이 약간 다르며 사용도 동일하다bandwidth변수
  2. 평행선: 다섯 개의 MA는 “평등 선”을 형성하며, 그 배열과 상대적인 위치가 트렌드 강도와 방향을 시각적으로 나타냅니다.

  3. 크로스 테스트: 전략 모니터링 평선 대역에서 연속 MA 사이의 교차, 사용자는 잠재적인 신호를 생성하는 데 필요한 교차 수를 지정할 수 있습니다.

  4. RSI 필터: 시장의 과도한 연장 상태에서 입시를 피하는 데 도움이됩니다. 다중 입장은 RSI가 초과 판매 수준보다 낮아야하며, 공백 입장은 초과 구매 수준보다 높아야합니다.

  5. 트렌드 강도 필터트렌드 강도를 측정하기 위해 이동 평균의 RSI를 사용하여 강력한, 확립된 트렌드 방향으로 거래하는 것을 보장합니다.

  6. 트렌드 확인가짜 신호를 더욱 줄이기 위해 입시 조건 (MA 교차, RSI 및 트렌드 강도) 을 요구합니다. 거래가 실제로 시작되기 전에 지정된 수의 K 라인을 연속으로 충족해야합니다.

  7. 논리에서 탈퇴전략은 다음과 같은 순서로 퇴출을 우선시합니다: 하드 스톱, 추적 스톱 (%) 또는 ATR 기반) 및 수익 (%) 이익 (ATR 기반). 이것은 손실을 최소화하고 수익을 보호하는 것을 보장합니다.

전략적 이점

  1. 고도 사용자 정의 핵 평평: 코어 매끄러운 (특히 베타 코어) 를 사용하여 표준 MA에서 사용할 수 없는 MA에 대한 반응성의 제어 수준을 제공합니다. 이것은 트렌드 추적에 대해 더 적응적이고 세밀한 방법을 사용할 수 있습니다.

  2. 트렌드 강도와 확인트렌드 강도 필터 (MA의 RSI를 사용하는) 와 트렌드 확인 기간의 조합은 단순한 MA 교차 또는 RSI 독서를 넘어 강력한 필터링 메커니즘을 제공합니다. 이것은 약한 트렌드 및 흔들림 상황을 필터링하는 데 도움이됩니다.

  3. 여러 우선순위 탈퇴 옵션전략의 탈퇴 논리는 매우 복잡하며, 고정 및 동적 중지 및 수익 수준의 조합을 제공합니다. 우선 순위는 가장 보수적인 탈퇴를 보장합니다.

  4. 전체 입력 그룹: 모든 입자는 제어 전략의 특정 측면으로 분류되어 있으며, 사용자가 입자를 쉽게 빠르게 위치하고 조정할 수 있다.

  5. 거래 방향 제어이 전략은 많은 전략들과는 달리, 독립적으로 다중 헤드 및 공백 거래를 활성화 또는 비활성화 할 수 있습니다.

  6. 전방위 트렌드 시스템이 지표는 거래에 필요한 여러 측면을 결합합니다: 입문 신호, 중지 손실 계산, 이익 계산.

전략적 위험

  1. 매개 변수 최적화 도전: 전략이 많은 변수를 가지고 있기 때문에 과도한 적합성의 위험이 발생할 수 있습니다. 너무 세밀하게 변수를 조정하면 전략이 재검토에서 잘 작동하지만 실제 거래에서는 실패 할 수 있습니다.

  2. 트렌드 변화 반응 지연: 전략은 지속되는 트렌드를 식별하는 데 목적이 있지만, 시장이 급격하게 변하면 충분히 빠르게 반응하지 않을 수 있으며, 일부 회수로 이어질 수 있습니다. MA 길이를 조정하고 핵심 매개 변수를 조정하여 트렌드 변화에 대한 민감성과 소음에 대한 필터링 능력을 균형을 잡을 수 있습니다.

  3. MA 교차 가짜 신호: 여러 계층의 필터링이 있더라도 흔들리는 시장에서 가짜 신호가 발생할 수 있습니다. 확인된 트렌드 시장에서이 전략을 사용하거나 가짜 신호를 줄이기 위해 트렌드 확인 기간을 늘리는 것이 좋습니다.

  4. 조기 발사된 정지: 큰 변동 시장에서, 중단은 너무 일찍 촉발 될 수 있으며, 그 후의 가격 회귀와 추세 회복을 놓치게 될 수 있습니다. ATR 기반의 중단을 고려하고 시장의 변동성에 맞게 적절하게 조정할 수 있습니다.

  5. 복잡성의 위험정책의 복잡성은 문제 해결과 실시간 모니터링을 어렵게 만들 수 있습니다. 간단한 구성에서 시작하여 복잡한 기능을 점차 추가하여 각 구성 요소의 역할을 충분히 이해하도록 권장합니다.

전략 최적화 방향

  1. 시간 프레임의 적응성: 현재 정책은 다른 시간 프레임에 따라 자동으로 매개 변수를 조정할 수 있도록 추가적으로 최적화 할 수 있습니다. 예를 들어, 시간 프레임에 따라 자동 매개 변수를 조정하는 기능을 추가하여 정책은 일선, 시간선 또는 분선 차트에서 효과적으로 작동 할 수 있습니다.

  2. 시장 환경 탐지: 시장 환경에 대한 자동 검출 메커니즘을 추가하고, 검출 결과에 따라 거래 매개 변수를 조정합니다. 예를 들어, 분기 시장에서 필터링 강도를 높이거나 수익 목표를 조정하고, 추세 시장에서 필터링 조건을 완화합니다.

  3. 동적 RSIRSI의 오버 바이 오버 시드 값은 동적으로 설계되어 있지 않고, 최근 시장의 변동성에 따라 자동으로 조정됩니다. 이것은 다양한 시장 조건에 대한 전략의 적응성을 향상시킬 수 있습니다.

  4. 통합된 양적 변동 지표전략과 변동성 지표 (Bollinger Bandwidth와 같은) 를 통합하여 높은 변동성 환경에서 중지 및 수익 목표를 조정하여 유효한 추세에 노출 될 위험을 줄입니다.

  5. 다중 시간 프레임 확인: 더 높은 시간 프레임의 트렌드 확인을 추가하여 거래 방향이 더 큰 트렌드와 일치하도록합니다. 예를 들어, 당일 일선 트렌드가 시간선 트렌드 방향과 일치하는 경우에만 거래합니다.

  6. 성능 모니터링 및 적응: 전략적 성능을 구현하는 실시간 모니터링 시스템, 승률, 손해비율 및 최대 회수와 같은 지표를 추적하고, 성능 지표가 기본 값 이하로 떨어지면 자동으로 매개 변수를 조정하거나 거래를 중지한다.

  7. 기계 학습 강화: 기계 학습 알고리즘을 변수 최적화 과정에 통합하여 전략이 역사 데이터에서 최적의 변수 조합을 학습하고 새로운 데이터의 축적과 함께 계속 개선 할 수 있도록 탐구.

요약하다

코어 평준화 다중 평균에 기반한 적응 트렌드 추적 시스템은 강력한, 유연한 트렌드 추적 도구로, 이동 평균의 시각적 명확성과 코어 평준화, RSI, 트렌드 강도 및 다양한 탈퇴 옵션의 고급 필터링 및 위험 관리 기능을 결합합니다. 그것은 지속 시장 추세를 식별하고 거래하기 위해 사용자 정의 할 수 있고 강력한 도구를 원하는 거래자를 위해 고안되었습니다.

이 전략의 가장 큰 장점은 다양한 시장 조건에 적응할 수 있도록 높은 사용자 정의와 자기 적응성입니다. 핵 평준화 기술을 통해 전통적인 이동 평균보다 더 미세한 제어를 제공 할 수 있으며, 여러 층의 필터링 및 확인 메커니즘은 가짜 신호를 줄이는 데 도움이됩니다. 동시에, 통합 된 위험 관리 시스템은 손실을 최소화하고 수익을 보호하는 다양한 탈퇴 전략을 제공합니다.

그러나 사용자는 매개 변수를 최적화하는 도전을 주의해야하며, 과도한 적응을 피하고, 특정 시장 환경에 따라 전략을 조정해야합니다. 충분한 반추와 전향 테스트를 통해 전략이 다양한 시장 조건에서 안정적으로 작동 할 수 있도록 권장됩니다.

전략 소스 코드
/*backtest
start: 2024-03-28 00:00:00
end: 2025-03-27 00:00:00
period: 1d
basePeriod: 1d
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}]
*/

//@version=5
strategy("B4100 - NW Trend Ribbon Strategy", overlay=true, default_qty_type = strategy.percent_of_equity, default_qty_value = 100, commission_type = strategy.commission.percent, commission_value = 0.02)

// === Optimized Functions ===
f_calculate_beta_kernel(length, alpha, beta) =>
    kernel = array.new_float(length, 0)
    sum = 0.0
    for i = 0 to length - 1
        x = i / (length - 1)
        w = math.pow(x, alpha - 1) * math.pow(1 - x, beta - 1)
        array.set(kernel, i, w)
        sum += w
    for i = 0 to length - 1
        array.set(kernel, i, array.get(kernel, i) / sum)
    kernel

f_calculate_gaussian_kernel(length, bandwidth) =>
    kernel = array.new_float(length, 0)
    sum = 0.0
    for i = 0 to length - 1
        x = i / (length - 1)
        w = math.exp(-0.5 * math.pow((x - 0.5) / bandwidth, 2))
        array.set(kernel, i, w)
        sum += w
    for i = 0 to length - 1
        array.set(kernel, i, array.get(kernel, i) / sum)
    kernel

f_calculate_epanechnikov_kernel(length, bandwidth) =>
    kernel = array.new_float(length, 0)
    sum = 0.0
    for i = 0 to length - 1
        x = i / (length - 1)
        w = math.max(0.0, 1 - math.pow((x - 0.5) / bandwidth, 2))
        array.set(kernel, i, w)
        sum += w
    for i = 0 to length - 1
        array.set(kernel, i, array.get(kernel, i) / sum)
    kernel

f_apply_kernel_ma(src, kernel, length) =>
    sum = 0.0
    for i = 0 to length - 1
        sum += src[i] * array.get(kernel, i)
    sum

f_trend_strength(ma, length) =>
    ts = ta.rsi(ma, length) / 100
    ts

// === Inputs ===
src = input.source(close, title="Price Source", tooltip="Select the price data used for calculations.  'Close' is the most common, but you can also use 'Open', 'High', 'Low', 'HL2' (typical price), etc.")

// MA Parameters
maGroup = "Moving Average Settings"
maCrossoverGroup = "Moving Average Crossover Settings"
rsiFilterGroup = "RSI Filter Settings"
trendStrengthGroup = "Trend Strength Filter Settings"
trendConfirmGroup = "Trend Confirmation Settings"
trailingStopGroup = "Trailing Stop Settings"
atrTrailingStopGroup = "ATR Trailing Stop Settings"
atrTakeProfitGroup = "ATR Take Profit Settings"
hardStopGroup = "Hard Stop Loss Settings"
tradeDirectionGroup = "Trade Direction Control"

length1 = input.int(20, title="MA1 Length", minval=1, tooltip="Number of bars used to calculate the first Moving Average.", group=maGroup)
kernelType1 = input.string(title="MA1 Kernel Type", defval="Beta", options=["Beta", "Gaussian", "Epanechnikov"], tooltip="Select the type of smoothing kernel for MA1.  'Beta' allows for lag adjustment. 'Gaussian' and 'Epanechnikov' use a bandwidth.", group=maGroup)
alpha1  = input.float(3.0, title="MA1 Beta Kernel +Lag", minval=1, maxval=10, tooltip="For Beta kernel only: Higher values increase *positive* lag (MA reacts *slower* to price increases).", group=maGroup)
beta1   = input.float(3.0, title="MA1 Beta Kernel -Lag", minval=1, maxval=10, tooltip="For Beta kernel only: Higher values increase *negative* lag (MA reacts *slower* to price decreases).", group=maGroup)
bandwidth1 = input.float(0.3, title="MA1 Bandwidth", minval=0.1, maxval=10.0, tooltip="For Gaussian/Epanechnikov kernels:  Smaller values create a *tighter* fit to the price (more sensitive). Larger values create a *smoother*, less sensitive MA.", group=maGroup)

length2 = input.int(100, title="MA2 Length", minval=1, tooltip="Number of bars for the second Moving Average.", group=maGroup)
kernelType2 = input.string(title="MA2 Kernel Type", defval="Gaussian", options=["Beta", "Gaussian", "Epanechnikov"], tooltip="Kernel type for MA2 (see MA1 Kernel Type for details).", group=maGroup)
alpha2  = input.float(3.0, title="MA2 Beta Kernel +Lag", minval=1, maxval=10, tooltip="Beta kernel positive lag for MA2 (see MA1 Beta Kernel +Lag for details).", group=maGroup)
beta2   = input.float(3.0, title="MA2 Beta Kernel -Lag", minval=1, maxval=10, tooltip="Beta kernel negative lag for MA2 (see MA1 Beta Kernel -Lag for details).", group=maGroup)
bandwidth2 = input.float(0.3, title="MA2 Bandwidth", minval=0.1, maxval=10.0, tooltip="Bandwidth for MA2 (see MA1 Bandwidth for details).", group=maGroup)

length3 = input.int(150, title="MA3 Length", minval=1, tooltip="Number of bars for the third Moving Average.", group=maGroup)
kernelType3 = input.string(title="MA3 Kernel Type", defval="Epanechnikov", options=["Beta", "Gaussian", "Epanechnikov"], tooltip="Kernel type for MA3.", group=maGroup)
alpha3  = input.float(3.0, title="MA3 Beta Kernel +Lag", minval=1, maxval=10, tooltip="Beta kernel positive lag for MA3.", group=maGroup)
beta3   = input.float(3.0, title="MA3 Beta Kernel -Lag", minval=1, maxval=10, tooltip="Beta kernel negative lag for MA3.", group=maGroup)
bandwidth3 = input.float(0.3, title="MA3 Bandwidth", minval=0.1, maxval=10.0, tooltip="Bandwidth for MA3.", group=maGroup)

length4 = input.int(200, title="MA4 Length", minval=1, tooltip="Number of bars for the fourth Moving Average.", group=maGroup)
kernelType4 = input.string(title="MA4 Kernel Type", defval="Beta", options=["Beta", "Gaussian", "Epanechnikov"], tooltip="Kernel type for MA4.", group=maGroup)
alpha4  = input.float(3.0, title="MA4 Beta Kernel +Lag", minval=1, maxval=10, tooltip="Beta kernel positive lag for MA4.", group=maGroup)
beta4   = input.float(3.0, title="MA4 Beta Kernel -Lag", minval=1, maxval=10, tooltip="Beta kernel negative lag for MA4.", group=maGroup)
bandwidth4 = input.float(0.3, title="MA4 Bandwidth", minval=0.1, maxval=10.0, tooltip="Bandwidth for MA4.", group=maGroup)

length5 = input.int(250, title="MA5 Length", minval=1, tooltip="Number of bars for the fifth Moving Average.", group=maGroup)
kernelType5 = input.string(title="MA5 Kernel Type", defval="Gaussian", options=["Beta", "Gaussian", "Epanechnikov"], tooltip="Kernel type for MA5.", group=maGroup)
alpha5  = input.float(3.0, title="MA5 Beta Kernel +Lag", minval=1, maxval=10, tooltip="Beta kernel positive lag for MA5.", group=maGroup)
beta5   = input.float(3.0, title="MA5 Beta Kernel -Lag", minval=1, maxval=10, tooltip="Beta kernel negative lag for MA5.", group=maGroup)
bandwidth5 = input.float(0.3, title="MA5 Bandwidth", minval=0.1, maxval=10.0, tooltip="Bandwidth for MA5.", group=maGroup)

// Entry Logic
maCrossoversRequired = input.int(3, title="MA Crossovers Required", minval=1, maxval=5, tooltip="How many moving averages must cross each other to generate a potential trade signal.  A higher number means a stronger (but potentially later) signal.", group=maCrossoverGroup)
useRsiFilter         = input.bool(true, title="Use RSI Filter", tooltip="If enabled, the RSI must also be in overbought/oversold territory for a signal to be valid.", group=rsiFilterGroup)
rsiLength           = input.int(7, title="RSI Length", minval=2, tooltip="Number of bars used to calculate the RSI.", group=rsiFilterGroup)
rsiOverbought       = input.int(60, title="RSI Overbought", minval=50, maxval=100, tooltip="RSI level considered overbought (for short entries).", group=rsiFilterGroup)
rsiOversold         = input.int(40, title="RSI Oversold", minval=0, maxval=50, tooltip="RSI level considered oversold (for long entries).", group=rsiFilterGroup)

// Trend Strength Filter
useTrendStrengthFilter = input.bool(true, title="Use Trend Strength Filter", tooltip="If enabled, the trend strength (measured by the RSI of a selected MA) must be above/below a threshold.", group=trendStrengthGroup)
trendStrengthLength   = input.int(7, title="Trend Strength Length", minval=1, tooltip="Number of bars for the trend strength calculation (RSI of the selected MA).", group=trendStrengthGroup)
trendStrengthMa       = input.int(1, title="Trend Strength MA", minval=1, maxval=5, tooltip="Which moving average (1-5) to use for calculating trend strength. 1 = MA1, 2 = MA2, etc.", group=trendStrengthGroup)
minTrendStrength     = input.float(0.5, title="Min Trend Strength (Longs)", minval=0.0, maxval=1.0, step=0.01, tooltip="Minimum trend strength (0.0 - 1.0) required for long entries. 0.5 means the selected MA's RSI must be above 50.", group=trendStrengthGroup)
maxTrendStrength     = input.float(0.5, title="Max Trend Strength (Shorts)", minval=0.0, maxval=1.0, step=0.01, tooltip="Maximum trend strength (0.0 - 1.0) required for short entries. 0.5 means the selected MA's RSI must be below 50.", group=trendStrengthGroup)

// Trend Confirmation
trendConfirmationPeriod = input.int(4, title="Trend Confirmation Period", minval=1, tooltip="Number of consecutive bars the entry conditions must be met before a trade is taken. This helps filter out false signals.", group=trendConfirmGroup)


// Exit Logic
useTrailingStop = input.bool(true, title="Use Percentage Trailing Stop", tooltip="Enable a percentage-based trailing stop loss.", group=trailingStopGroup)
trailingStopActivationPercent = input.float(2.0, title="Activation (%)", minval=0.1, step=0.1, tooltip="Percentage above/below the entry price at which the trailing stop activates.", group=trailingStopGroup) / 100
trailingStopOffsetPercent     = input.float(1.0, title="Offset (%)", minval=0.1, step=0.1, tooltip="Percentage offset from the highest/lowest price reached since entry. This determines how tightly the stop trails the price.", group=trailingStopGroup) / 100

useAtrTrailingStop    = input.bool(true, title="Use ATR Trailing Stop", tooltip="Enable a trailing stop based on the Average True Range (ATR).", group=atrTrailingStopGroup)
atrTrailingStopLength = input.int(1, title="ATR Length", minval=1, tooltip="Number of bars used to calculate the ATR.", group=atrTrailingStopGroup)
atrTrailingStopMult   = input.float(200.0, title="ATR Multiplier", minval=0.1, tooltip="Multiplier for the ATR value.  A larger multiplier creates a wider stop.", group=atrTrailingStopGroup)

useAtrTakeProfit              = input.bool(false, title="Use ATR Take Profit", tooltip="Enable a take profit level based on the Average True Range (ATR).", group=atrTakeProfitGroup)
atrTakeProfitLength           = input.int(14, title="ATR Length", minval=1, tooltip="Number of bars used to calculate the ATR for take profit.", group=atrTakeProfitGroup)
atrTakeProfitMultiplier       = input.float(3.0, title="ATR Multiplier", minval=0.1, tooltip="Multiplier for the ATR value. A larger multiplier sets a further take profit target.", group=atrTakeProfitGroup)

useHardStopLoss     = input.bool(false, title="Use Hard Stop Loss", tooltip="Enable a fixed stop loss.", group=hardStopGroup)
hardStopLossPercent = input.float(0.0, title="Hard Stop Loss (%)", minval=0.0, step=0.1, tooltip="Percentage below (long) or above (short) the entry price for the hard stop loss.", group=hardStopGroup) / 100
useAtrHardStopLoss  = input.bool(false, title="Use ATR Hard Stop Loss", tooltip="Use ATR to calculate hard stop loss", group=hardStopGroup)
atrHardStopLossLength = input.int(14, title="ATR Hard Stop Loss Length", minval=1, tooltip="Length of the ATR for the hard stop loss", group=hardStopGroup)
atrHardStopLossMult   = input.float(1.5, title="ATR Hard Stop Loss Multiplier", minval=0.1, tooltip="Multiplier of ATR for the hard stop loss", group=hardStopGroup)

// *** Trade Direction Control ***
enableLongs  = input.bool(true, title="Enable Long Trades", group=tradeDirectionGroup)
enableShorts = input.bool(true, title="Enable Short Trades", group=tradeDirectionGroup)

// === Pre-calculate kernels (do this only once) ===
var kernel1 = array.new_float(length1, 0.0)
var kernel2 = array.new_float(length2, 0.0)
var kernel3 = array.new_float(length3, 0.0)
var kernel4 = array.new_float(length4, 0.0)
var kernel5 = array.new_float(length5, 0.0)

if barstate.isfirst
    if kernelType1 == "Beta"
        kernel1 := f_calculate_beta_kernel(length1, alpha1, beta1)
    else if kernelType1 == "Gaussian"
        kernel1 := f_calculate_gaussian_kernel(length1, bandwidth1)
    else // Epanechnikov
        kernel1 := f_calculate_epanechnikov_kernel(length1, bandwidth1)

    if kernelType2 == "Beta"
        kernel2 := f_calculate_beta_kernel(length2, alpha2, beta2)
    else if kernelType2 == "Gaussian"
        kernel2 := f_calculate_gaussian_kernel(length2, bandwidth2)
    else // Epanechnikov
        kernel2 := f_calculate_epanechnikov_kernel(length2, bandwidth2)

    if kernelType3 == "Beta"
        kernel3 := f_calculate_beta_kernel(length3, alpha3, beta3)
    else if kernelType3 == "Gaussian"
        kernel3 := f_calculate_gaussian_kernel(length3, bandwidth3)
    else // Epanechnikov
        kernel3 := f_calculate_epanechnikov_kernel(length3, bandwidth3)

    if kernelType4 == "Beta"
        kernel4 := f_calculate_beta_kernel(length4, alpha4, beta4)
    else if kernelType4 == "Gaussian"
        kernel4 := f_calculate_gaussian_kernel(length4, bandwidth4)
    else // Epanechnikov
        kernel4 := f_calculate_epanechnikov_kernel(length4, bandwidth4)

    if kernelType5 == "Beta"
        kernel5 := f_calculate_beta_kernel(length5, alpha5, beta5)
    else if kernelType5 == "Gaussian"
        kernel5 := f_calculate_gaussian_kernel(length5, bandwidth5)
    else // Epanechnikov
        kernel5 := f_calculate_epanechnikov_kernel(length5, bandwidth5)

// === Apply pre-calculated kernels to data ===
nw_ma1 = f_apply_kernel_ma(src, kernel1, length1)
nw_ma2 = f_apply_kernel_ma(src, kernel2, length2)
nw_ma3 = f_apply_kernel_ma(src, kernel3, length3)
nw_ma4 = f_apply_kernel_ma(src, kernel4, length4)
nw_ma5 = f_apply_kernel_ma(src, kernel5, length5)

// MA Array for easier iteration
ma_array = array.new_float(5)
array.set(ma_array, 0, nw_ma1)
array.set(ma_array, 1, nw_ma2)
array.set(ma_array, 2, nw_ma3)
array.set(ma_array, 3, nw_ma4)
array.set(ma_array, 4, nw_ma5)

// Calculate ATR values *unconditionally*
atrTrailingValue = ta.atr(atrTrailingStopLength)
atrTakeProfitValue = ta.atr(atrTakeProfitLength)
atrHardStopLossValue = ta.atr(atrHardStopLossLength)

// Calculate Trend Strength *unconditionally* (and only once)
trendStrengthValue = useTrendStrengthFilter ? f_trend_strength(array.get(ma_array, trendStrengthMa - 1), trendStrengthLength) : 0.0

// === Entry Logic ===

// MA Crossovers
longMaCrossovers  = 0
shortMaCrossovers = 0

for i = 0 to 3
    if array.get(ma_array, i) > array.get(ma_array, i + 1)
        longMaCrossovers  := longMaCrossovers  + 1
    if array.get(ma_array, i) < array.get(ma_array, i + 1)
        shortMaCrossovers := shortMaCrossovers + 1

longCrossoverCondition  = longMaCrossovers  >= maCrossoversRequired
shortCrossoverCondition = shortMaCrossovers >= maCrossoversRequired

// RSI Filter
rsiValue = ta.rsi(src, rsiLength)
longRsiCondition  = not useRsiFilter or (rsiValue < rsiOversold)
shortRsiCondition = not useRsiFilter or (rsiValue > rsiOverbought)

// Trend Strength Filter - Simplified Logic
longTrendStrengthCondition  = not useTrendStrengthFilter or trendStrengthValue >= minTrendStrength
shortTrendStrengthCondition = not useTrendStrengthFilter or trendStrengthValue <= maxTrendStrength


// --- Trend Confirmation Logic ---
var int long_confirm_count = 0
var int short_confirm_count = 0
var bool confirmedLong = false
var bool confirmedShort = false

// Update confirmation counters
if longCrossoverCondition and longRsiCondition and longTrendStrengthCondition
    long_confirm_count := long_confirm_count + 1
    short_confirm_count := 0  // Reset opposite counter
else
    long_confirm_count := 0

if shortCrossoverCondition and shortRsiCondition and shortTrendStrengthCondition
    short_confirm_count := short_confirm_count + 1
    long_confirm_count := 0 // Reset opposite counter
else
    short_confirm_count := 0

// Check for confirmed trend
confirmedLong := long_confirm_count >= trendConfirmationPeriod
confirmedShort := short_confirm_count >= trendConfirmationPeriod

// Combined Entry Conditions (using confirmed trend)
longCondition = confirmedLong  and enableLongs // Added trade direction check
shortCondition = confirmedShort and enableShorts // Added trade direction check

// === Exit Logic ===
var float longTrail = na
var float shortTrail = na
var float longTakeProfitPrice = na
var float shortTakeProfitPrice = na
var float longHardStopLossPrice = na
var float shortHardStopLossPrice = na

// Hard Stop Loss and Take Profit calculation on entry
if longCondition or shortCondition
    // Calculate Hard Stop Loss
    if useHardStopLoss
        if useAtrHardStopLoss
            longHardStopLossPrice  := close - (atrHardStopLossValue * atrHardStopLossMult)
            shortHardStopLossPrice := close + (atrHardStopLossValue * atrHardStopLossMult)
        else
            longHardStopLossPrice  := close * (1 - hardStopLossPercent)
            shortHardStopLossPrice := close * (1 + hardStopLossPercent)
    else
        longHardStopLossPrice := na
        shortHardStopLossPrice := na

    // Calculate Take Profit
    if useAtrTakeProfit
        longTakeProfitPrice  := close + (atrTakeProfitValue * atrTakeProfitMultiplier)
        shortTakeProfitPrice := close - (atrTakeProfitValue * atrTakeProfitMultiplier)
    else
        longTakeProfitPrice := na
        shortTakeProfitPrice := na

// Trailing Stop Logic - updated for each bar
if strategy.position_size > 0
    // Calculate trailing stop
    float tempTrail = na

    if useTrailingStop
        if close > strategy.position_avg_price * (1 + trailingStopActivationPercent)
            tempTrail := close * (1 - trailingStopOffsetPercent)
            if na(longTrail) or tempTrail > longTrail
                longTrail := tempTrail

    if useAtrTrailingStop
        float atrTrail = close - (atrTrailingValue * atrTrailingStopMult)
        if na(longTrail) or atrTrail > longTrail
            longTrail := atrTrail

if strategy.position_size < 0
    // Calculate trailing stop
    float tempTrail = na

    if useTrailingStop
        if close < strategy.position_avg_price * (1 - trailingStopActivationPercent)
            tempTrail := close * (1 + trailingStopOffsetPercent)
            if na(shortTrail) or tempTrail < shortTrail
                shortTrail := tempTrail

    if useAtrTrailingStop
        float atrTrail = close + (atrTrailingValue * atrTrailingStopMult)
        if na(shortTrail) or atrTrail < shortTrail
            shortTrail := atrTrail

// === Strategy Execution ===
if longCondition
    strategy.entry("Long", strategy.long)
    longTrail := na  // Reset on new entry
    shortTrail := na // Reset on new entry

if shortCondition
    strategy.entry("Short", strategy.short)
    shortTrail := na // Reset on new entry
    longTrail := na  // Reset on new entry

// Unified exit logic with proper ordering
if strategy.position_size > 0
    // Define effective stop level (combining hard stop and trailing stop)
    float effectiveStopLevel = na

    if not na(longHardStopLossPrice) and useHardStopLoss
        effectiveStopLevel := longHardStopLossPrice

    if not na(longTrail) and (useTrailingStop or useAtrTrailingStop)
        if na(effectiveStopLevel) or longTrail > effectiveStopLevel
            effectiveStopLevel := longTrail

    // Combined exit strategy with proper parameters
    strategy.exit("Long Exit", "Long",
                 limit = useAtrTakeProfit ? longTakeProfitPrice : na,
                 stop = effectiveStopLevel)

if strategy.position_size < 0
    // Define effective stop level (combining hard stop and trailing stop)
    float effectiveStopLevel = na

    if not na(shortHardStopLossPrice) and useHardStopLoss
        effectiveStopLevel := shortHardStopLossPrice

    if not na(shortTrail) and (useTrailingStop or useAtrTrailingStop)
        if na(effectiveStopLevel) or shortTrail < effectiveStopLevel
            effectiveStopLevel := shortTrail

    // Combined exit strategy with proper parameters
    strategy.exit("Short Exit", "Short",
                 limit = useAtrTakeProfit ? shortTakeProfitPrice : na,
                 stop = effectiveStopLevel)

// === Plotting ===
plotColorMa1 = nw_ma1 > nw_ma1[1] ? color.rgb(100, 250, 120) : color.rgb(255, 100, 120)
plotColorMa2 = nw_ma2 > nw_ma2[1] ? color.rgb(100, 250, 120) : color.rgb(255, 100, 120)
plotColorMa3 = nw_ma3 > nw_ma3[1] ? color.rgb(100, 250, 120) : color.rgb(255, 100, 120)
plotColorMa4 = nw_ma4 > nw_ma4[1] ? color.rgb(100, 250, 120) : color.rgb(255, 100, 120)
plotColorMa5 = nw_ma5 > nw_ma5[1] ? color.rgb(100, 250, 120) : color.rgb(255, 100, 120)

plot(nw_ma1, title="NW MA 1", color=plotColorMa1, linewidth=2)
plot(nw_ma2, title="NW MA 2", color=plotColorMa2, linewidth=2)
plot(nw_ma3, title="NW MA 3", color=plotColorMa3, linewidth=2)
plot(nw_ma4, title="NW MA 4", color=plotColorMa4, linewidth=2)
plot(nw_ma5, title="NW MA 5", color=plotColorMa5, linewidth=2)