Système de suivi des tendances adaptatif basé sur des moyennes mobiles multiples lissées par noyau

MA RSI ATR MAs NW 趋势追踪 核平滑 滑动止损
Date de création: 2025-03-28 15:13:28 Dernière modification: 2025-03-28 15:13:28
Copier: 2 Nombre de clics: 336
2
Suivre
319
Abonnés

Système de suivi des tendances adaptatif basé sur des moyennes mobiles multiples lissées par noyau Système de suivi des tendances adaptatif basé sur des moyennes mobiles multiples lissées par noyau

Aperçu

Ce système de suivi des tendances auto-adaptatif basé sur des moyennes multiples de compensation nucléaire est une stratégie de négociation quantitative avancée qui intègre cinq moyennes mobiles personnalisées, des filtres multicouches et des mécanismes de confirmation pour identifier et exploiter les tendances persistantes du marché. La stratégie utilise la technologie de compensation nucléaire plutôt que les moyennes mobiles traditionnelles, offrant une plus grande flexibilité de compensation et d’auto-adaptation, capable de s’adapter à une variété de conditions de marché et de délais.

Les fonctionnalités principales comprennent: la visualisation de la tendance actuelle du marché à l’aide d’une “bande de ligne uniforme” constituée de cinq moyennes mobiles; la réduction du bruit et des faux signaux par le biais de filtres RSI, de filtres de force de tendance et de confirmation de tendance; le déclenchement d’un signal d’entrée uniquement si des conditions spécifiques sont remplies; et la gestion des risques et la protection des bénéfices grâce à une variété d’options de sortie (par exemple, stop loss suivi par pourcentage, stop loss suivi par ATR, but de profit ATR et stop loss dur).

Principe de stratégie

La logique centrale de la stratégie s’articule autour des composants clés suivants:

  1. Moyenne mobile de l’équilibre nucléaire: Stratégie utilise la technologie de l’aplatissement nucléaire pour remplacer la moyenne mobile standard, offrant un effet d’aplatissement plus flexible et plus adaptatif que la moyenne mobile traditionnelle. Supporte trois types de noyaux:

    • Le nucléaire bêta: l’option la plus puissante qui permette de passeralphaetbetaLes paramètres sont contrôlés indépendamment par un décalage positif et négatif, ce qui permet à la MA de réagir différemment aux hausses et aux baisses des prix.
    • Ghosn: Créer une pesanteur en forme de clochebandwidthLes paramètres contrôlent la largeur de la courbe clochard.
    • Noyau d’Epanechnikov: semblable au noyau de Gauss mais de forme légèrement différente, utilisé de la même manièrebandwidthParamètres
  2. Bande de la ligne égale: les cinq MA forment les “bandes d’équilibre” du graphique, dont l’arrangement et la position relative fournissent une indication visuelle de la force et de la direction de la tendance.

  3. Tests croisés: Stratégie de surveillance des intersections entre des MA successifs dans une bande homogène, l’utilisateur peut spécifier le nombre d’intersections nécessaires pour générer un signal potentiel.

  4. Filtre RSI: Aide à éviter l’entrée en situation de sur-extension du marché. En cas d’entrée en position multiple, le RSI doit être inférieur au niveau de survente; en cas d’entrée en position vide, il doit être supérieur au niveau de survente.

  5. Filtre d’intensité de tendance: Utilisez le RSI d’une moyenne mobile pour mesurer la force d’une tendance et assurez-vous de négocier dans le sens d’une tendance forte et établie.

  6. Confirmation de la tendance: pour réduire encore plus les faux signaux, les conditions d’entrée (cross-MA, RSI et force de tendance) doivent être satisfaites en continu sur un nombre spécifié de lignes K avant de déclencher une transaction.

  7. Sortie de la logiqueLa stratégie consiste à donner la priorité aux sorties dans l’ordre suivant: Stop loss durable, Stop loss suivi (en pourcentage ou basé sur l’ATR) et Gain (en fonction de l’ATR). Cela assure la minimisation des pertes et la protection des bénéfices.

Avantages stratégiques

  1. Plafond de hauteur personnalisableL’utilisation d’un noyau lisse (en particulier un noyau bêta) fournit un niveau de contrôle de la réactivité de l’AM qui n’est pas disponible dans l’AM standard. Cela permet une approche plus adaptative et plus fine du suivi des tendances.

  2. L’intensité et la confirmation de la tendanceLe filtrage de la force de la tendance (RSI avec MA) et la combinaison de la période de confirmation de la tendance fournissent un puissant mécanisme de filtrage, qui va au-delà des simples lectures de MA croisées ou RSI. Cela aide à filtrer les tendances faibles et les mouvements de choc.

  3. Options de désinscription à plusieurs priorités: La logique de sortie de la stratégie est très complexe, offrant une combinaison de niveaux de stop-loss et de profit fixes et dynamiques. La priorité est d’assurer la sortie la plus conservatrice (stop-loss de dureté) d’abord déclenchée, suivie par le suivi du stop-loss, et finalement l’objectif de profit.

  4. Groupe des entrées: toutes les entrées sont classées dans des groupes pour des aspects spécifiques de la stratégie de contrôle, et l’utilisateur peut facilement localiser et ajuster rapidement les entrées.

  5. Contrôle de la directionLa stratégie permet d’activer ou de désactiver de manière indépendante les transactions à plusieurs et à vide.

  6. Système de tendance globaleL’indicateur combine plusieurs aspects nécessaires à la négociation: le signal d’entrée, le calcul du stop-loss, le calcul du profit.

Risque stratégique

  1. Défi d’optimisation des paramètresIl est recommandé d’effectuer des vérifications croisées robustes et des tests hors échantillon pour s’assurer que le paramétrage est universel.

  2. La réaction retardée au changement de tendance: Bien que la stratégie vise à identifier les tendances persistantes, il est possible de ne pas réagir assez rapidement en cas de revers brusque du marché, ce qui entraîne une reprise partielle. La sensibilité aux changements de tendance et la capacité de filtrage du bruit peuvent être équilibrées en ajustant la longueur de la MA et les paramètres de base.

  3. MA est un faux signal croisé.Il est recommandé d’utiliser cette stratégie dans les marchés en tendance déterminée, ou d’augmenter la période de confirmation de la tendance pour réduire les faux signaux.

  4. Le déclenchement prématuré de l’arrêt des dommages: Dans les marchés à forte volatilité, les arrêts peuvent être déclenchés prématurément, ce qui entraîne la perte d’un redressement de prix et d’une reprise de tendance. Des arrêts basés sur l’ATR peuvent être envisagés et adaptés de manière appropriée à la volatilité du marché.

  5. Risque de complexité: La complexité des stratégies peut rendre difficile la dépannage et la surveillance en temps réel. Il est recommandé de commencer par une configuration simple et d’ajouter progressivement des fonctionnalités complexes, en veillant à bien comprendre le rôle de chaque composant.

Orientation de l’optimisation de la stratégie

  1. Adaptabilité du calendrier: La stratégie actuelle peut être optimisée pour ajuster automatiquement les paramètres en fonction des différentes périodes. Par exemple, la fonctionnalité d’ajustement automatique des paramètres en fonction des périodes peut être ajoutée pour que la stratégie fonctionne efficacement sur les graphiques de jour, d’heure ou de minute.

  2. Détection de l’environnement du marché: augmenter le mécanisme de détection automatique de l’environnement du marché (trend, intervalle ou forte volatilité) et ajuster les paramètres de négociation en fonction des résultats détectés. Par exemple, augmenter l’intensité de filtrage ou ajuster l’objectif de rentabilité dans les marchés intermédiaires et assouplir les conditions de filtrage dans les marchés tendanciels.

  3. Le RSI est à la baisse.: la conception de la marge de survente et de survente du RSI est dynamique et non statique, elle s’ajuste automatiquement en fonction de la volatilité récente du marché. Cela améliore l’adaptabilité de la stratégie dans différentes conditions de marché.

  4. Indicateur de fluctuation quantitative intégré: intégrer la stratégie avec des indicateurs de volatilité (comme la bande passante de Bollinger) afin d’ajuster les objectifs de stop loss et de profit dans un environnement très volatil, réduisant ainsi le risque d’être détourné d’une tendance efficace.

  5. Confirmation de plusieurs périodes: ajouter une confirmation de tendance à une période plus longue pour s’assurer que la direction de la transaction est cohérente avec la tendance plus large. Par exemple, la transaction ne peut être effectuée que lorsque la tendance de la ligne journalière est cohérente avec la tendance de la ligne heure.

  6. Surveillance de la performance et adaptation: Système de surveillance en temps réel de la performance de la stratégie, suivi des indicateurs tels que le taux de victoire, le ratio de gain et de perte et le retrait maximal, ajustement automatique des paramètres ou suspension des transactions lorsque les indicateurs de performance sont inférieurs à la barre de réglage.

  7. Le renforcement de l’apprentissage automatique: explorer l’intégration d’algorithmes d’apprentissage automatique dans le processus d’optimisation des paramètres, permettant aux stratégies d’apprendre les meilleures combinaisons de paramètres à partir des données historiques et d’améliorer constamment les nouvelles données accumulées.

Résumer

Le système de suivi de tendance adaptatif basé sur des courbes d’aplatissement multiples est un outil de suivi de tendance puissant et flexible qui combine la clarté visuelle des bandes de moyennes mobiles avec des capacités de filtrage et de gestion des risques avancées de la courbe d’aplatissement, du RSI, de l’intensité de la tendance et de plusieurs options de sortie. Il est conçu pour les traders qui souhaitent disposer d’outils personnalisables et puissants pour identifier et négocier des tendances de marché persistantes.

Le plus grand avantage de cette stratégie réside dans sa haute personnalisation et son adaptabilité, qui lui permettent de s’adapter à diverses conditions de marché. Grâce à la technologie de l’aplatissement nucléaire, elle offre un contrôle plus fin que la moyenne mobile traditionnelle, tandis que des mécanismes de filtrage et de confirmation multicouches aident à réduire les faux signaux. Parallèlement, un système de gestion intégrée des risques fournit une variété de stratégies d’exit qui garantissent la minimisation des pertes et la protection des bénéfices.

Cependant, les utilisateurs doivent être attentifs aux défis liés à l’optimisation des paramètres, éviter les surajustements et adapter la stratégie en fonction des conditions spécifiques du marché. Il est recommandé de procéder à un retour d’expérience adéquat et à des tests prospectifs pour s’assurer que la stratégie fonctionne de manière stable dans toutes sortes de conditions de marché.

Code source de la stratégie
/*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)