Stratégie de trading d'arbitrage avec oscillateur stochastique à double intervalle de temps

STOCH KDJ MA RSI VWMA SMA EMA WMA
Date de création: 2025-06-18 13:47:35 Dernière modification: 2025-06-18 13:47:35
Copier: 0 Nombre de clics: 318
2
Suivre
319
Abonnés

Stratégie de trading d’arbitrage avec oscillateur stochastique à double intervalle de temps Stratégie de trading d’arbitrage avec oscillateur stochastique à double intervalle de temps

Aperçu

La stratégie est basée sur un Stochastic Oscillator, un système de négociation à haute fréquence, qui utilise deux paramètres différents pour générer et confirmer des signaux de négociation sur un délai de 15 secondes. La logique principale est d’identifier les points d’entrée potentiels à travers le croisement des lignes %K et %D de l’indicateur principal, tout en se référant à la valeur %D de l’indicateur secondaire.

Principe de stratégie

La stratégie utilise un double système d’indicateurs aléatoires de secousse, appelés indicateurs principaux et indicateurs de référence respectivement:

  1. Les principaux paramètres d’indicateurs de secousse aléatoires:

    • Durée: 15 secondes
    • Longueur de la ligne K: 12
    • K: douceur de la ligne: 12
    • D longueur de ligne: 12
  2. Pour plus d’informations, consultez les paramètres de l’indicateur de secousse aléatoire:

    • Durée: 15 secondes
    • Longueur de la ligne K: 12
    • La ligne de K est lisse: 15
    • D longueur de ligne: 30

La logique d’entrée a été conçue avec une grande précision et une validité de signal à plusieurs niveaux:

  • Les conditions d’admission sont les suivantes:

    • La ligne %D est traversée par la ligne %K de l’indicateur principal et
    • une valeur de l’indicateur de référence %D ≥ 50 ou < 20, ou
    • L’indicateur principal %K est proche de l’indicateur de référence %D (différence dans les 0,15)
    • Le prix est au-dessus de la moyenne mobile (si le filtre MA est activé)
    • Les heures de négociation doivent se dérouler aux heures normales du marché (09:30 AM - 4:00 PM ET)
  • Conditions d’entrée à vide:

    • Le principal indicateur %K traverse la ligne %D, et
    • situé dans la plage de tolérance de l’indicateur de référence %D, ou répondant à des conditions d’usure spécifiques
    • Les prix sont en dessous de la moyenne mobile
    • Les heures de négociation doivent se faire aux heures normales du marché

La logique de sortie est basée sur une combinaison de signaux temporels et techniques:

  • La date de sortie:
    • Les marchés sont organisés à 15h30 ET (avant la fin de l’heure normale de marché).
  • Le départ technique:
    • Position à plusieurs têtes: lorsque l’indicateur principal %K est en dessous de l’indicateur de référence %D
    • Position de tête vide: lorsque l’indicateur principal %K est porté sur l’indicateur de référence %D et que l’indicateur de référence %D est > 20

La stratégie intègre également une fonctionnalité de reconnaissance de formes:

  • Forme de point bas plus élevé: la valeur %K du point de pénétration actuel est supérieure à la valeur %K du point de pénétration précédent (forme de continuation de la courbe de pointage)
  • Forme de pic plus bas: la valeur %K du point de rupture actuel est inférieure à la valeur %K du point de rupture précédent (Forme de poursuite de la baisse)

Avantages stratégiques

  1. Mécanisme de vérification à plusieurs niveaux: Confirmation mutuelle par deux indicateurs de tremblement aléatoires de configuration différente, réduction du faux signal généré par un seul indicateur et amélioration de la fiabilité du signal.

  2. Règles précises d’entrée et de sortieLa stratégie définit clairement les conditions d’entrée et de sortie, élimine la subjectivité des décisions de négociation et permet une négociation totalement systématisée.

  3. Reconnaissance de la formeIl s’agit d’une fonctionnalité que de nombreuses stratégies simples ne peuvent pas réaliser: la capacité à identifier les formes “plus haut, plus bas” et “plus bas, plus haut” du marché et à saisir les opportunités de prolongation de la tendance.

  4. Filtreur de tempsEn limitant le temps de négociation aux heures normales du marché, on évite les périodes de forte volatilité et de faible liquidité avant l’ouverture et la fermeture du marché, ce qui réduit les points de glissement et les coûts.

  5. Filtre des moyennes mobiles: La fonctionnalité de filtrage des moyennes mobiles optionnelles ajoute une couche de confirmation de tendance pour s’assurer que la direction des transactions est conforme à la tendance globale.

  6. Différences de prix et paramètres de capacitéLa stratégie introduit plusieurs paramètres pour contrôler l’amplitude des fluctuations des prix et la portée des variations de l’indicateur, filtrant efficacement les signaux de bruit générés par de petites fluctuations.

  7. Conversion logique dynamiqueLe système est capable de s’adapter aux conditions de conversion de polype à polype et de polype à polype en fonction de la dynamique du marché.

  8. Un système d’alerte completLa stratégie intègre de nombreuses conditions d’alerte pour la surveillance et l’exécution des transactions en temps réel.

Risque stratégique

  1. Risques de trading à haute fréquence sur des périodes courtesLa stratégie consiste à utiliser un délai de 15 secondes pour générer des signaux excessifs, ce qui entraîne une fréquence accrue des transactions, un coût accru des transactions et une quantité importante de faux signaux en cas de forte volatilité du marché.

  2. Manque de mécanisme de préventionLe manque de contrôle des risques est l’une des principales faiblesses de la stratégie.

  3. Paramètre Sensibilité: Les paramètres précis utilisés par la stratégie (par exemple, une différence de 0,15 de seuil, une différence de 0,1% de plafond de prix, etc.) peuvent être trop sensibles aux différentes conditions du marché et doivent être ajustés fréquemment.

  4. Coût d’opportunité lié à la duréeLes transactions effectuées uniquement pendant les heures normales du marché peuvent laisser passer d’importantes opportunités avant et après la clôture, en particulier lorsque les marchés réagissent à des informations importantes.

  5. La dépendance à la liquidité: les stratégies à haute fréquence peuvent être confrontées à des problèmes de points de glissement dans les marchés à faible liquidité, et les prix d’exécution réels peuvent être significativement différents de ceux de la génération du signal.

  6. Décalage des indicateurs techniquesLes indicateurs de choc aléatoire ont eux-mêmes une certaine latence et peuvent ne pas être capables de saisir les points de basculement en temps opportun, en particulier dans les marchés qui tournent rapidement.

  7. Le risque d’une suradaptationLes paramètres stratégiques finement ajustés peuvent entraîner une suradaptation aux données historiques et une mauvaise performance dans les conditions futures du marché.

Orientation de l’optimisation de la stratégie

  1. Augmentation du mécanisme de prévention des pertesLe point d’optimisation le plus important est la mise en œuvre d’un système de stop-loss intelligent, qui peut être considéré comme une stratégie de stop-loss basée sur l’ATR, ou en utilisant le niveau de la technologie comme point d’arrêt pour limiter la perte maximale d’une seule transaction.

  2. Introduction à la gestion des positions: Adaptation dynamique de la taille des transactions en fonction de la volatilité du marché et de la tolérance au risque du compte, utilisation de différentes configurations de positions pour différentes intensités de signal afin d’optimiser le taux d’utilisation des fonds et le rapport entre le risque et le rendement.

  3. Ajouter une confirmation de transaction: l’intégration des indicateurs de trafic dans le système exige que les signaux d’entrée importants aient un support de trafic suffisant pour filtrer les signaux qui ne sont pas fiables dans un environnement de faible trafic.

  4. Fusion de plusieurs indicateursConsidérer d’autres indicateurs de dynamique et de tendance, tels que le RSI, le MACD ou les bandes de Brent, pour construire une perspective plus globale du marché et améliorer la stabilité du système.

  5. Optimisation du calendrier: tester différents intervalles de temps de base, tels que 1 minute ou 5 minutes, qui peuvent réduire le bruit tout en conservant suffisamment d’opportunités de transaction, pour trouver le meilleur équilibre entre la qualité et la quantité de signaux.

  6. Ajout d’un suivi statistique- la mise en place d’indicateurs de performance de retrait plus complets, tels que le maximum de rétractation, le taux de Sharpe, le taux de victoire, le taux de gain et de perte, etc., afin d’évaluer plus scientifiquement la performance de la stratégie;

  7. Paramètres d’adaptation: transformer les paramètres fixes en paramètres d’adaptation basés sur la dynamique de la volatilité du marché, permettant ainsi à la stratégie de s’adapter aux différentes conditions du marché.

  8. Augmenter le filtrage des conditions du marché: ajout de VIX (indice de volatilité) ou d’indicateurs similaires comme conditions de filtrage de l’environnement du marché, pour ajuster les paramètres de stratégie ou suspendre les transactions dans un environnement à forte volatilité.

Résumer

La stratégie d’arbitrage des indices de choc aléatoires à deux fuseaux horaires est un système de négociation à haute fréquence à court terme soigneusement conçu, qui améliore la fiabilité des signaux de négociation grâce à des mécanismes de confirmation à plusieurs niveaux tels que les indices de choc aléatoires doubles, le filtrage des moyennes mobiles et le filtrage temporel. La stratégie identifie les points de basculement et la continuation de la tendance à court terme dans les périodes de marché régulières, adaptée aux marchés suffisamment liquides et modérément volatils.

Malgré une structure de conception stratégique parfaite, il y a encore un manque de mécanisme de gestion des risques clés tels que les risques inhérents aux transactions à haute fréquence et l’absence de stop-loss. Afin d’améliorer la stabilité et la rentabilité à long terme de la stratégie, il est recommandé d’ajouter des mesures d’optimisation telles que le stop-loss, le système de gestion de position, la confirmation du volume de transaction et l’intégration de plusieurs indicateurs.

Avec une compréhension approfondie et une optimisation continue de la stratégie par les traders, ce système de négociation a le potentiel de devenir un composant efficace dans la boîte à outils de day trading, particulièrement adapté aux traders qui ont une compréhension approfondie des indicateurs techniques et qui peuvent surveiller le marché en temps opportun.

Code source de la stratégie
/*backtest
start: 2025-01-01 00:00:00
end: 2025-06-17 00:00:00
period: 4h
basePeriod: 4h
exchanges: [{"eid":"Binance","currency":"ETH_USDT"}]
*/

//@version=6
strategy("Dual TF Stochastic Strategy", overlay=false)

// Input parameters with updated defaults
primaryLen = input.int(12, "Primary Stoch K Length", minval=1)  // Changed from 14 to 12
primarySmooth = input.int(12, "Primary Stoch K Smoothing", minval=1)  // Changed from 3 to 12
primaryDLen = input.int(12, "Primary Stoch D Length", minval=1)  // Changed from 3 to 12
primaryRes = input.timeframe("15S", "Primary Timeframe")  // Changed from "" to "15S"

refLen = input.int(12, "Reference Stoch K Length", minval=1)  // Changed from 14 to 12
refSmooth = input.int(15, "Reference Stoch K Smoothing", minval=1)  // Changed from 3 to 15
refDLen = input.int(30, "Reference Stoch D Length", minval=1)  // Changed from 3 to 30
refRes = input.timeframe("15S", "Reference Timeframe")  // Changed from "D" to "15S"

tolerance = input.float(0.1, "Ref D Tolerance %", minval=0.1, maxval=10.0, step=0.1)  // Changed from 1.0 to 0.1
maxPriceDiff = input.float(0.1, "Maximum Price % Difference", minval=0.1, maxval=5.0, step=0.1)  // Changed from 1.0 to 0.1
closeKThreshold = input.float(0.7, "Close %K Tolerance %", minval=0.1, maxval=10.0, step=0.1)  // Changed from 5.0 to 0.7
minPriceDiffShort = input.float(0.1, "Min Price % Diff for Close %K Short", minval=0.1, maxval=5.0, step=0.1)  // Changed from 0.5 to 0.1
showLabels = input.bool(true, "Show Crossover/Crossunder Labels")

// Time Filters (America/New_York timezone, UTC-4)
is_premarket = hour(time, "America/New_York") < 9
is_postmarket = hour(time, "America/New_York") >= 16
is_regular_hours = hour(time, "America/New_York") >= 9 and hour(time, "America/New_York") < 16
is_exit_time = hour(time, "America/New_York") >= 15 and minute(time, "America/New_York") >= 30  // 3:30 PM ET

// Moving Average Settings
useMAFilter = input.bool(true, "Use Moving Average Filter")
maLength = input.int(200, "Moving Average Length", minval=1)
maType = input.string("SMA", "Moving Average Type", options=["SMA", "EMA", "WMA", "VWMA"])
maTimeframe = input.timeframe("", "Moving Average Timeframe")

// Stochastic Calculations
primaryHighest = ta.highest(high, primaryLen)
primaryLowest = ta.lowest(low, primaryLen)
primaryK_raw = 100 * (close - primaryLowest) / (primaryHighest - primaryLowest)
primaryK = ta.sma(primaryK_raw, primarySmooth)
primaryD = ta.sma(primaryK, primaryDLen)
[primaryK_tf, primaryD_tf] = request.security(syminfo.tickerid, primaryRes, [primaryK, primaryD])

refHighest = ta.highest(high, refLen)
refLowest = ta.lowest(low, refLen)
refK_raw = 100 * (close - refLowest) / (refHighest - refLowest)
refK = ta.sma(refK_raw, refSmooth)
refD = ta.sma(refK, refDLen)
[refK_tf, refD_tf] = request.security(syminfo.tickerid, refRes, [refK, refD])

// Calculate Moving Average
var float ma = na
if useMAFilter
    if maType == "SMA"
        ma := request.security(syminfo.tickerid, maTimeframe, ta.sma(close, maLength))
    else if maType == "EMA"
        ma := request.security(syminfo.tickerid, maTimeframe, ta.ema(close, maLength))
    else if maType == "WMA"
        ma := request.security(syminfo.tickerid, maTimeframe, ta.wma(close, maLength))
    else if maType == "VWMA"
        ma := request.security(syminfo.tickerid, maTimeframe, ta.vwma(close, maLength))

// Price relative to MA
priceAboveMA = not useMAFilter or close > ma
priceBelowMA = not useMAFilter or close < ma

// Crossover Detection and Tracking
crossOver = ta.crossover(primaryK_tf, primaryD_tf)
crossUnder = ta.crossunder(primaryK_tf, primaryD_tf)

// Separate tracking for crossover and crossunder %K and price
var float lastCrossOverK = na
var float lastCrossOverPrice = na
var float currentCrossOverK = na
var float currentCrossOverPrice = na

var float lastCrossUnderK = na
var float lastCrossUnderPrice = na
var float currentCrossUnderK = na
var float currentCrossUnderPrice = na

// Update crossover tracking variables
if crossOver
    lastCrossOverK := nz(currentCrossOverK, primaryK_tf[1])
    lastCrossOverPrice := nz(currentCrossOverPrice, close[1])
    currentCrossOverK := primaryK_tf
    currentCrossOverPrice := close

// Update crossunder tracking variables
if crossUnder
    lastCrossUnderK := nz(currentCrossUnderK, primaryK_tf[1])
    lastCrossUnderPrice := nz(currentCrossUnderPrice, close[1])
    currentCrossUnderK := primaryK_tf
    currentCrossUnderPrice := close

// Calculate differences separately
crossOverPriceDiffPercent = math.abs((currentCrossOverPrice - lastCrossOverPrice) / lastCrossOverPrice * 100)
crossOverKDiffPercent = math.abs((currentCrossOverK - lastCrossOverK) / lastCrossOverK * 100)
crossUnderPriceDiffPercent = math.abs((currentCrossUnderPrice - lastCrossUnderPrice) / lastCrossUnderPrice * 100)
crossUnderKDiffPercent = math.abs((currentCrossUnderK - lastCrossUnderK) / lastCrossUnderK * 100)

isKCloseCrossUnder = crossUnderKDiffPercent <= closeKThreshold and not na(lastCrossUnderK)

// New condition for long entry based on %K and refD_tf difference
kAndRefDDiffClose = crossOver and math.abs(currentCrossOverK - refD_tf) <= 0.15

// Labels for crossover and crossunder (optional)
if showLabels
    if crossOver
        diffKandRefD = math.abs(currentCrossOverK - refD_tf)
        label.new(bar_index, 50, "CrossOver\nDiff K-RefD: " + str.tostring(diffKandRefD, "#.###"), color=color.green, textcolor=color.black, style=label.style_label_up)
    if crossUnder
        diffKandRefD = math.abs(currentCrossUnderK - refD_tf)
        label.new(bar_index, 50, "CrossUnder\nDiff K-RefD: " + str.tostring(diffKandRefD, "#.###"), color=color.red, textcolor=color.black, style=label.style_label_down)

// Entry Conditions
longKCondition = crossOver and (na(lastCrossOverK) or currentCrossOverK > lastCrossOverK)
shortKCondition = crossUnder and (crossUnderPriceDiffPercent <= maxPriceDiff)
closeKShortCondition = crossUnder and isKCloseCrossUnder and (crossUnderPriceDiffPercent > minPriceDiffShort)
crossUnderBetween50and45 = crossUnder and currentCrossUnderK <= 50 and currentCrossUnderK > 45

// Long to Short if crossunder %K > 80 OR < 60
longToShortCondition = crossUnder and (currentCrossUnderK > 80 or currentCrossUnderK < 60) and strategy.position_size > 0 and is_regular_hours

upperLimit = refD_tf * (1 + tolerance/100)
lowerLimit = refD_tf * (1 - tolerance/100)
withinToleranceLong = primaryK_tf >= lowerLimit and primaryK_tf <= upperLimit
withinToleranceShort = primaryK_tf >= lowerLimit and primaryK_tf <= upperLimit

// Final Entry Conditions with MA filter
longCondition = ((longKCondition and (refD_tf >= 50 or refD_tf < 20)) or kAndRefDDiffClose) and is_regular_hours and not is_exit_time and priceAboveMA
shortCondition = (shortKCondition or (crossUnder and withinToleranceShort and (crossUnderPriceDiffPercent <= maxPriceDiff)) or closeKShortCondition or longToShortCondition or crossUnderBetween50and45) and is_regular_hours and not is_exit_time and priceBelowMA

// Short-to-Long Transition Condition with MA filter
shortToLongCondition = crossOver and currentCrossOverK < 25 and strategy.position_size < 0 and is_regular_hours and not is_exit_time and priceAboveMA

// Tracking for %K crossing under refD_tf
var float lastPrimaryKCrossUnderRefD = na
var float currentPrimaryKCrossUnderRefD = na
var bool isPrimaryKCrossUnderRefD = false

// Check if primary %K crosses under reference %D
isPrimaryKCrossUnderRefD := ta.crossunder(primaryK_tf, refD_tf)

// Update tracking for %K crossing under refD
if isPrimaryKCrossUnderRefD
    lastPrimaryKCrossUnderRefD := currentPrimaryKCrossUnderRefD
    currentPrimaryKCrossUnderRefD := primaryK_tf

// Exit Conditions
if is_exit_time
    strategy.close("Long")
    strategy.close("Short")
else if isPrimaryKCrossUnderRefD and not na(lastPrimaryKCrossUnderRefD) and currentPrimaryKCrossUnderRefD < lastPrimaryKCrossUnderRefD
    strategy.close("Long")
else if (ta.crossunder(primaryK_tf, primaryD_tf) and primaryK_tf < refD_tf and refD_tf < 60)
    strategy.close("Long")

if (ta.crossover(primaryK_tf, primaryD_tf) and primaryK_tf > refD_tf and refD_tf > 20) and not is_exit_time
    strategy.close("Short")

// Track if crossunder happens above 85
var bool crossUnderAbove85 = false

// Detect crossunder above 85
if crossUnder and currentCrossUnderK > 85
    crossUnderAbove85 := true

// Reset condition if %K crosses over %D
if ta.crossover(primaryK_tf, primaryD_tf)
    crossUnderAbove85 := false

// Track previous crossover/crossunder values for Higher Low/Lower High detection
var float prevCrossOverK = na
var float prevCrossUnderK = na

// Update previous values on new crossovers/crossunders
if crossOver
    prevCrossOverK := currentCrossOverK
if crossUnder
    prevCrossUnderK := currentCrossUnderK

// Higher Low and Lower High conditions
higherLowCondition = crossOver and not na(prevCrossOverK) and currentCrossOverK > prevCrossOverK
lowerHighCondition = crossUnder and not na(prevCrossUnderK) and currentCrossUnderK < prevCrossUnderK

// Strategy Entries and Transitions
if longCondition
    strategy.entry("Long", strategy.long)

if shortCondition
    if strategy.position_size > 0  // If in a long position, close it first
        strategy.close("Long")
    strategy.entry("Short", strategy.short)

if shortToLongCondition
    strategy.close("Short")
    if ((longKCondition and (refD_tf >= 50 or refD_tf < 20)) or kAndRefDDiffClose)  // Check full longCondition minus time (already checked)
        strategy.entry("Long", strategy.long)

// Add label for Short to Long Transition
if shortToLongCondition
    label.new(bar_index, na, "T", color=color.green, textcolor=color.white, style=label.style_label_up)

// Add label for Long to Short Transition
if longToShortCondition
    label.new(bar_index, na, "T", color=color.red, textcolor=color.white, style=label.style_label_down)

// Plotting
plot(primaryK_tf, "Primary %K", color=color.white, linewidth=1)
plot(primaryD_tf, "Primary %D", color=color.orange, linewidth=1)
plot(refK_tf, "Reference %K", color=color.navy, linewidth=1)
plot(refD_tf, "Reference %D", color=color.rgb(33, 233, 243), linewidth=2)

// Plot current and last %K only for crossUnder when isKCloseCrossUnder is true and currentCrossUnderK < lastCrossUnderK
plot(crossUnder and isKCloseCrossUnder and currentCrossUnderK < lastCrossUnderK ? currentCrossUnderK : na, "Current CrossUnder %K (Close)", color=color.green, style=plot.style_cross, linewidth=2)
plot(crossUnder and isKCloseCrossUnder and currentCrossUnderK < lastCrossUnderK ? lastCrossUnderK : na, "Last CrossUnder %K (Close)", color=color.red, style=plot.style_cross, linewidth=2)

h0 = hline(85, "Upper Band", color=color.rgb(242, 187, 21))
hline(50, "Middle Band", color=#eaff04)
h1 = hline(20, "Lower Band", color=color.rgb(242, 187, 21))
h2 = hline(40, "Lower Band", color=#787B86)
h3 = hline(60, "Lower Band", color=#787B86)
h = hline(0, "Lower Band", color=#787B86)
h5 = hline(100, "Lower Band", color=#787B86)
fill(h0, h1, color=color.rgb(33, 150, 243, 90), title="Background")
fill(h, h1, color=#1be2781d, title="Background")
fill(h0, h5, color=#e21b742d, title="Background")

// Plot the MA if enabled
plot(useMAFilter ? ma : na, "Moving Average", color=color.yellow, linewidth=2)

// Add plot for visualization (optional)
plot(isPrimaryKCrossUnderRefD ? primaryK_tf : na, "Primary %K CrossUnder RefD", color=color.purple, style=plot.style_cross, linewidth=2)
plot(isPrimaryKCrossUnderRefD and not na(lastPrimaryKCrossUnderRefD) ? lastPrimaryKCrossUnderRefD : na, "Last Primary %K CrossUnder RefD", color=color.fuchsia, style=plot.style_cross, linewidth=2)

// Add new alert conditions
alertcondition(higherLowCondition, title="Stoch Higher Low", message="Stoch Higher Low Pattern Detected")
alertcondition(lowerHighCondition, title="Stoch Lower High", message="Stoch Lower High Pattern Detected")

// Plot markers for Higher Low and Lower High patterns
plot(higherLowCondition ? currentCrossOverK : na, "Higher Low", color=color.green, style=plot.style_cross, linewidth=2)
plot(lowerHighCondition ? currentCrossUnderK : na, "Lower High", color=color.red, style=plot.style_cross, linewidth=2)

// Alert conditions
alertcondition(crossOver, title="Stochastic %K Crossed Over %D", message="Stochastic %K crossed over %D")
alertcondition(crossUnder, title="Stochastic %K Crossed Under %D", message="Stochastic %K crossed under %D")
alertcondition(crossOver and primaryK_tf > 50, title="Stochastic %K Crossed Over %D Above 50", message="Stochastic %K crossed over %D above 50")
alertcondition(crossOver and primaryK_tf > refD_tf, title="Stochastic %K Crossed Over %D Above Reference %D", message="Stochastic %K crossed over %D above Reference %D")
alertcondition(longCondition, title="Long Entry Signal", message="Long entry signal triggered")
alertcondition(shortCondition, title="Short Entry Signal", message="Short entry signal triggered")
alertcondition(shortToLongCondition, title="Short to Long Transition", message="Short to Long transition triggered")
alertcondition(longToShortCondition, title="Long to Short Transition", message="Long to Short transition triggered")
alertcondition(isPrimaryKCrossUnderRefD, title="Primary %K Crossed Under Reference %D", message="Primary %K crossed under Reference %D")
alertcondition(crossOver and primaryK_tf > refD_tf, title="Bullish Crossover Above Ref %D", message="Bull: Dual Stoch")
alertcondition(crossUnder and primaryK_tf < refD_tf, title="Bearish Crossunder Below Ref %D", message="Bear: Dual Stoch")