
La stratégie est un système de trading quantitatif qui combine la rupture de la plage d’ouverture (Opening Range Breakout) et l’analyse intelligente du marché (Smart Money Concepts). La stratégie est axée sur la capture des opportunités de rupture de la zone de prix formée 5 minutes après l’ouverture du marché boursier (09:30-09:35 EST) et sur la combinaison de plusieurs conditions de filtrage pour assurer la qualité du signal de négociation.
La logique de base de la stratégie de suivi de la rupture de la zone d’ouverture ATR repose sur l’importance de la zone de prix initiale après l’ouverture du marché. La stratégie capture et enregistre d’abord les hauts et les bas des prix dans une fenêtre de temps spécifique (09:30-09:35 EST), formant une “zone d’ouverture” (Opening Range). Ensuite, le système surveille le comportement de rupture des prix dans cette zone, en combinant les mécanismes clés suivants pour assurer la qualité des transactions:
Identification et vérification de rupture entre les zones ouvertes: le système enregistre les hauts et les bas des prix dans une fenêtre de temps spécifiée, puis surveille les ruptures. Les ruptures doivent être vérifiées par un double mécanisme de filtrage:
Mécanisme d’admissionLa stratégie prend en charge deux modes d’accès:
Réglages de stop-lossLe système offre deux types de stop loss:
Gestion des risquesLe système utilise un multiplicateur de risque:Reward Multiplier pour calculer automatiquement la position de l’arrêt et permettre une gestion dynamique du risque. Par exemple, un rapport de risque:Reward de 2:1 signifie que le profit potentiel est le double de la perte potentielle.
ATR pour le suivi des pertes: Une fois que les bénéfices atteignent le taux de rendement du risque prédéfini, le système peut activer un stop-loss de suivi basé sur l’ATR, bloquant une partie des bénéfices tout en permettant la poursuite de la tendance.
Deuxième chance de négociationLe système est capable de détecter automatiquement les occasions de rupture de la zone d’ouverture en cas d’arrêt ou de défaillance de la transaction initiale, permettant ainsi de réaliser des transactions bidirectionnelles le jour même.
Concentrez-vous sur des opportunités de négociation de qualitéLa stratégie réduit considérablement le nombre de faux transactions et augmente le taux de victoire grâce à des mécanismes de vérification multiples (filtrage de l’ombre, filtrage de la distance).
Une admission flexible: Prise en charge de l’entrée immédiate ou de la reprise, adaptée à différents styles de négociation et conditions de marché. L’entrée immédiate convient aux tendances fortes, tandis que l’entrée de la reprise offre un prix d’entrée plus avantageux.
Gestion des risques adaptéeLa mise en place d’un stop-loss dynamique basé sur la multiplication du risque par le rendement garantit que chaque transaction présente des caractéristiques de risque uniformes et permet une gestion de fonds standardisée.
Maximiser les bénéficesATR: la fonction de suivi des arrêts de perte permet de maintenir une tendance forte tout en protégeant les bénéfices réalisés et en évitant les sorties prématurées.
Haute visibilitéLe système fournit une fonctionnalité complète d’assistance visuelle, y compris les marqueurs d’intervalle, les étiquettes de vérification de rupture, les indications de l’état de la transaction, les marqueurs d’entrée/arrêt/arrêt, etc., pour améliorer l’intuition des décisions de transaction.
Une conception post-test sans préjugésLa stratégie a été adoptée à grande échelle.barstate.isconfirmedVeiller à ce que toutes les décisions soient basées sur des données de prix confirmées, à éviter les biais de prévision et à être conformes à l’environnement de négociation réel.
Mécanisme de deuxième chanceEn activant la fonction de trading de seconde chance, la stratégie peut s’adapter rapidement aux changements de marché, capturer les occasions de revers et améliorer l’efficacité de l’utilisation des fonds.
Optimisation de la gestion des sessionsLa fonctionnalité de clôture automatique de la session assure que les positions ne sont pas conservées pendant la nuit, réduisant ainsi le risque de nuit.
Risque de fluctuation de la période de formation de la zone: Pendant la période de formation de la zone d’ouverture (09:30-09:35), le marché peut subir des fluctuations anormales, entraînant une zone trop large ou trop étroite. Une zone trop large peut entraîner des arrêts excessifs, tandis qu’une zone trop étroite peut déclencher de fréquentes fausses ruptures. Comment faire ?Il est possible d’envisager d’augmenter les conditions de filtrage de la taille des intervalles d’ouverture pour exclure les intervalles anormaux; ou d’ajuster le filtre de date de transaction pour éviter des jours spécifiques à forte volatilité (comme les jours de publication de données économiques importantes).
Le risque d’une retraite brutale après une percée: Une rupture efficace peut entraîner une forte reprise du marché, ce qui entraîne une reprise de la tendance après le déclenchement du stop loss. Comment faire ?Considérez d’utiliser des paramètres d’arrêt de perte plus souples, tels que l’arrêt à intervalles réciproques; ou ajustez le mécanisme d’entrée pour réinitialiser l’entrée afin d’obtenir un meilleur prix d’entrée et une plus petite exposition au risque.
La qualité du signal dépend de la configuration du filtreLes paramètres de filtrage de la ligne d’ombre et de la distance des paramètres de filtrage de la vérification de la percée ont un impact significatif sur la qualité du signal. Des paramètres inappropriés peuvent filtrer de bonnes opportunités de trading ou recevoir trop de signaux de mauvaise qualité. Comment faire ?: optimiser les paramètres du filtre par la rétroaction historique pour trouver le meilleur réglage pour un marché et une variété particuliers; envisager d’utiliser des paramètres d’adaptation pour ajuster les critères de filtration en fonction de la dynamique de la volatilité du marché.
Suivre la sensibilité des paramètres de stop lossATR tracking stop loss: un paramètre trop serré peut entraîner une sortie prématurée d’un petit retournement, tandis qu’un paramètre trop relâché peut entraîner un retour excessif des bénéfices. Comment faire ?: ajustement du cycle et du multiplicateur ATR en fonction des caractéristiques de volatilité historique de la variété cible; considération de la mise en œuvre d’une stratégie de placement par lots, certaines positions utilisant des arrêts fixes et d’autres positions utilisant des arrêts de suivi.
Limite de fréquence des transactionsStratégie: Exécutez un maximum de deux transactions par jour (transactions initiale et transaction de seconde chance) et il est possible que vous ne puissiez pas profiter pleinement de toutes les opportunités de la journée. Comment faire ?Considérer des stratégies d’expansion pour surveiller les intervalles de prix importants à d’autres moments de la journée ou des stratégies de composition combinées avec d’autres indicateurs techniques pour augmenter les sources de signaux de négociation.
Adaptation à la période d’intervalle: La stratégie actuelle utilise un intervalle d’ouverture fixe de 5 minutes, la longueur de l’intervalle peut être ajustée en fonction de la dynamique de la volatilité du marché. Dans les marchés à faible volatilité, l’intervalle peut être réduit à 3 minutes, tandis que dans les marchés à forte volatilité, il peut être étendu à 10 minutes, pour mieux s’adapter aux différentes conditions du marché.
Confirmation de la quantité de transfert combinée: Augmentation des conditions de filtrage de la transaction dans le mécanisme de validation de la rupture, qui exigent que la transaction au moment de la rupture soit significativement supérieure à la transaction moyenne des cycles précédents, pour améliorer l’efficacité de la rupture. Cela peut être réalisé en calculant le rapport entre la transaction de la rupture et la moyenne des transactions des cycles précédents N.
Analyse de plusieurs périodes: introduire le filtrage de la direction de la tendance pour les périodes plus élevées, entrer en jeu uniquement lorsque la direction de la tendance de la ligne du jour ou de la ligne de l’heure correspond à la direction de la rupture, améliorer la probabilité de succès des transactions. La tendance des périodes plus élevées peut être déterminée par une simple inclinaison de la moyenne mobile ou un indicateur de tendance plus avancé.
Optimisation de la gestion des fonds: mise en place d’un mécanisme d’ajustement dynamique de la taille des positions, permettant d’ajuster automatiquement le nombre de contrats en fonction de la volatilité historique, de la taille du compte actuel et de la performance récente, pour un contrôle du risque plus précis. Par exemple, augmenter progressivement la position après une série de gains et réduire la position après une série de pertes.
Modèles d’apprentissage intégréLes caractéristiques peuvent inclure la taille de la période d’ouverture, la volatilité du marché, les mouvements de prix de la dernière journée de négociation, les modèles de temps spécifiques, etc.
Amélioration de la logique de négociation de la seconde chanceOptimiser les conditions de déclenchement des transactions de seconde chance, non seulement en fonction de l’échec des transactions initiales, mais en tenant compte des changements de la structure du marché et des nouveaux indicateurs de dynamique, afin d’améliorer le taux de réussite des transactions de seconde chance.
Paramètres de variété personnalisée: Développer un ensemble de paramètres optimisé pour les différentes variétés de négociation, en tenant compte des caractéristiques de volatilité et du comportement des prix propres à chaque variété. Par exemple, les variétés plus volatiles peuvent nécessiter des paramètres de filtrage plus souples et un ratio de retour sur risque plus conservateur.
Intégration des indicateurs de l’humeur du marchéL’introduction de l’indice VIX ou d’autres indicateurs de l’humeur du marché, l’ajustement des paramètres stratégiques ou la suspension temporaire des transactions pendant les périodes d’humeur extrême du marché, afin d’éviter un environnement de forte incertitude.
La stratégie de tracking ATR de rupture d’ouvertures est un système de trading quantitatif bien structuré qui combine habilement la rupture d’ouvertures, un mécanisme de filtrage intelligent, des options d’entrée flexibles et des fonctionnalités avancées de gestion des risques. La stratégie est particulièrement adaptée aux transactions intra-journalières sur les marchés boursiers et à terme américains et permet de réaliser des bénéfices en capturant des ruptures directionnelles après l’ouverture.
La valeur centrale de la stratégie réside dans ses mécanismes de vérification à plusieurs niveaux et son système de gestion des risques, qui réduisent considérablement les faux-transactions de rupture grâce à des filtres d’ombre et de distance, tout en utilisant un rapport de rendement des risques multiplié et un arrêt de suivi ATR pour assurer une exposition cohérente aux risques et une protection des bénéfices. La fonction de trading de seconde chance ajoute de l’adaptabilité et des opportunités de profit supplémentaires à la stratégie.
Malgré les nombreux avantages de cette stratégie, les utilisateurs doivent être conscients de l’importance de l’optimisation des paramètres, car différents marchés et variétés peuvent nécessiter des ajustements ciblés pour obtenir des résultats optimaux. Il est également recommandé aux traders d’utiliser cette stratégie dans le cadre d’un système de trading complet, en combinaison avec une analyse plus large du marché et des principes de gestion des risques.
La stratégie a le potentiel d’améliorer encore sa stabilité et sa rentabilité, en devenant un outil puissant dans la boîte à outils des traders professionnels, grâce à l’optimisation de la direction de mise en œuvre des recommandations, en particulier les paramètres d’adaptation, l’analyse des délais multiples et les systèmes de gestion de fonds améliorés.
/*backtest
start: 2025-07-18 00:00:00
end: 2025-07-30 00:00:00
period: 30m
basePeriod: 30m
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}]
*/
//@version=5
strategy("Casper SMC 5min ORB - Roboquant AI", overlay=true, default_qty_type=strategy.fixed, default_qty_value=1, max_bars_back=500, calc_on_order_fills=true, calc_on_every_tick=false, initial_capital=50000, currency=currency.USD)
// === STRATEGY SETTINGS ===
// Risk Management
contracts = input.int(1, "Contracts", minval=1, group="Risk Management")
risk_multiplier = input.float(2.0, "Risk:Reward Multiplier", minval=0.5, maxval=10.0, group="Risk Management")
sl_points = input.int(2, "Stop Loss Points Below/Above Breakout Candle", minval=1, group="Risk Management")
// Entry Settings
entry_type = input.string("Instant", "Entry Type", options=["Retracement", "Instant"], group="Entry Settings")
retracement_percent = input.float(50.0, "Retracement % of Breakout Candle Body", minval=10.0, maxval=90.0, group="Entry Settings")
// Stop Loss Settings
sl_type = input.string("Opposite Range", "Stop Loss Type", options=["Breakout Candle", "Opposite Range"], group="Stop Loss Settings")
// Second Chance Trade Settings
enable_second_chance = input.bool(false, "Enable Second Chance Trade", group="Second Chance Trade")
second_chance_info = input.string("If initial SL is hit, allow opposite breakout trade", "Info: Second Chance Logic", group="Second Chance Trade")
// Breakout Filter Settings
use_wick_filter = input.bool(false, "Use Wick Filter", group="Breakout Filter")
max_wick_percent = input.float(50.0, "Max Wick % of Candle Body", minval=10.0, maxval=200.0, group="Breakout Filter")
// Breakout Distance Filters
use_breakout_distance_filter = input.bool(true, "Use Breakout Distance Filter", group="Breakout Distance Filter")
min_breakout_multiplier = input.float(0.1, "Min Breakout Distance (OR Size * X)", minval=0.0, maxval=3.0, group="Breakout Distance Filter")
max_breakout_multiplier = input.float(1.6, "Max Breakout Distance (OR Size * X)", minval=0.5, maxval=5.0, group="Breakout Distance Filter")
// Trailing Stop Loss Settings
use_trailing_sl = input.bool(false, "Use Trailing Stop Loss", group="Trailing Stop Loss")
profit_r_multiplier = input.float(1.0, "Start Trailing After X R Profit", minval=0.5, maxval=5.0, group="Trailing Stop Loss")
atr_length = input.int(14, "ATR Length", minval=1, maxval=50, group="Trailing Stop Loss")
atr_multiplier = input.float(1.0, "ATR Multiplier for Trailing", minval=0.5, maxval=5.0, group="Trailing Stop Loss")
// Session Management
or_start_hour = input.int(9, "Opening Range Start Hour", minval=0, maxval=23, group="Session Management")
or_start_minute = input.int(30, "Opening Range Start Minute", minval=0, maxval=59, group="Session Management")
or_end_minute = input.int(35, "Opening Range End Minute", minval=0, maxval=59, group="Session Management")
session_timezone = input.string("America/New_York", "Session Timezone", group="Session Management")
force_session_close = input.bool(true, "Force Close at Session End", group="Session Management")
session_end_hour = input.int(16, "Session End Hour", minval=0, maxval=23, group="Session Management")
session_end_minute = input.int(0, "Session End Minute", minval=0, maxval=59, group="Session Management")
// Day of Week Trading Filters
trade_monday = input.bool(true, "Trade on Monday", group="Day of Week Filters")
trade_tuesday = input.bool(true, "Trade on Tuesday", group="Day of Week Filters")
trade_wednesday = input.bool(true, "Trade on Wednesday", group="Day of Week Filters")
trade_thursday = input.bool(true, "Trade on Thursday", group="Day of Week Filters")
trade_friday = input.bool(true, "Trade on Friday", group="Day of Week Filters")
// Visual Settings
high_line_color = input.color(color.green, title="Opening Range High Line Color", group="Visual Settings")
low_line_color = input.color(color.red, title="Opening Range Low Line Color", group="Visual Settings")
// Label Control Settings
show_trading_disabled_labels = input.bool(false, "Show Trading Disabled Labels", group="Label Controls")
show_breakout_validation_labels = input.bool(true, "Show Breakout Validation Labels", group="Label Controls")
show_second_chance_labels = input.bool(false, "Show Second Chance Labels", group="Label Controls")
show_trade_status_labels = input.bool(false, "Show Trade Status Labels", group="Label Controls")
show_entry_labels = input.bool(false, "Show Entry Labels", group="Label Controls")
show_sl_tp_labels = input.bool(false, "Show Stop Loss / Take Profit Labels", group="Label Controls")
// === VARIABLES ===
// ATR for trailing stop loss
atr = ta.atr(atr_length)
// === NYSE OPENING RANGE LOGIC ===
// FIXED: Using configurable hour/minute inputs with timezone
current_time = time(timeframe.period, "0000-2400:23456", session_timezone)
current_hour = hour(current_time, session_timezone)
current_minute = minute(current_time, session_timezone)
is_opening_range = current_hour == or_start_hour and current_minute >= or_start_minute and current_minute <= or_end_minute
// Check if we're at the start of a new trading day - FIXED: More reliable detection
is_new_day = ta.change(time("1D"))
// ADDED: Check if trading is allowed on current day of week (using session timezone)
current_day = dayofweek(current_time, session_timezone)
is_trading_day_allowed = (current_day == dayofweek.monday and trade_monday) or (current_day == dayofweek.tuesday and trade_tuesday) or (current_day == dayofweek.wednesday and trade_wednesday) or (current_day == dayofweek.thursday and trade_thursday) or (current_day == dayofweek.friday and trade_friday)
// Variables to store opening range high and low for current day
var float or_high = na
var float or_low = na
var bool lines_drawn = false
var bool breakout_occurred = false
var float breakout_candle_high = na
var float breakout_candle_low = na
var float breakout_price = na
var string breakout_direction = na
var int or_start_bar = na // ADDED: Store the bar index when opening range starts
// ADDED: Second chance trade variables
var bool first_trade_sl_hit = false
var string first_trade_direction = na
var bool second_chance_available = false
var bool second_trade_taken = false
var bool daily_trades_complete = false // ADDED: Prevent more than 2 trades per day
// Reset variables at the start of each trading day
if is_new_day
or_high := na
or_low := na
lines_drawn := false
breakout_occurred := false
breakout_candle_high := na
breakout_candle_low := na
breakout_price := na
breakout_direction := na
or_start_bar := na // ADDED: Reset opening range start bar
// ADDED: Reset second chance variables
first_trade_sl_hit := false
first_trade_direction := na
second_chance_available := false
second_trade_taken := false
daily_trades_complete := false // ADDED: Reset trade limit
// Capture opening range data during 09:30-09:35 EST
if is_opening_range
if na(or_high) or na(or_low)
or_high := high
or_low := low
or_start_bar := bar_index // ADDED: Store the bar index when opening range starts
else
or_high := math.max(or_high, high)
or_low := math.min(or_low, low)
// Draw lines when we're past the opening range and haven't drawn yet
if not is_opening_range and not na(or_high) and not na(or_low) and not na(or_start_bar) and not lines_drawn
// FIXED: Lines start from the actual opening range start time and extend forward
start_x = or_start_bar
end_x = bar_index + 50 // Extend lines forward for visibility
lines_drawn := true
// ADDED: Show visual indicator if trading is disabled for current day
if not is_trading_day_allowed and show_trading_disabled_labels
day_name = current_day == dayofweek.monday ? "Monday" :
current_day == dayofweek.tuesday ? "Tuesday" :
current_day == dayofweek.wednesday ? "Wednesday" :
current_day == dayofweek.thursday ? "Thursday" :
current_day == dayofweek.friday ? "Friday" : "Weekend"
label.new(x=bar_index, y=(or_high + or_low) / 2, text="Trading Disabled\n" + day_name, color=color.gray, textcolor=color.white, style=label.style_label_center, size=size.normal)
// Check for breakouts after opening range is complete (only first breakout of the day)
// FIXED: Added barstate.isconfirmed to avoid lookahead bias
if barstate.isconfirmed and not is_opening_range and not na(or_high) and not na(or_low) and lines_drawn and not breakout_occurred and not daily_trades_complete and is_trading_day_allowed
// Calculate candle body and wick percentages
candle_body = math.abs(close - open)
top_wick = high - math.max(open, close)
bottom_wick = math.min(open, close) - low
top_wick_percent = candle_body > 0 ? (top_wick / candle_body) * 100 : 0
bottom_wick_percent = candle_body > 0 ? (bottom_wick / candle_body) * 100 : 0
// ADDED: Calculate opening range size for distance filters
or_size = or_high - or_low
// Check for first breakout above opening range high
if close > or_high
// FIXED: Mark breakout as occurred FIRST (this is THE breakout candle)
breakout_occurred := true
breakout_candle_high := high
breakout_candle_low := low
breakout_price := close
breakout_direction := "long"
// ADDED: Validate this specific breakout candle against distance filter
breakout_distance_valid = true
if use_breakout_distance_filter
min_breakout_level = or_high + (or_size * min_breakout_multiplier)
max_breakout_level = or_high + (or_size * max_breakout_multiplier)
breakout_distance_valid := close >= min_breakout_level and close <= max_breakout_level
// Apply wick filter for long breakouts
wick_filter_valid = not use_wick_filter or top_wick_percent <= max_wick_percent
// Show appropriate label based on validation results
if show_breakout_validation_labels
if wick_filter_valid and breakout_distance_valid
label.new(x=bar_index, y=high, text="VALID", color=high_line_color, textcolor=color.white, style=label.style_label_down, size=size.tiny)
else
label.new(x=bar_index, y=high, text="INVALID", color=color.gray, textcolor=color.white, style=label.style_label_down, size=size.tiny)
// Mark breakout as invalid so no trade will be placed (regardless of label setting)
if not (wick_filter_valid and breakout_distance_valid)
breakout_direction := "invalid"
// Check for first breakout below opening range low
else if close < or_low
// FIXED: Mark breakout as occurred FIRST (this is THE breakout candle)
breakout_occurred := true
breakout_candle_high := high
breakout_candle_low := low
breakout_price := close
breakout_direction := "short"
// ADDED: Validate this specific breakout candle against distance filter
breakout_distance_valid = true
if use_breakout_distance_filter
min_breakout_level = or_low - (or_size * min_breakout_multiplier)
max_breakout_level = or_low - (or_size * max_breakout_multiplier)
breakout_distance_valid := close <= min_breakout_level and close >= max_breakout_level
// Apply wick filter for short breakouts
wick_filter_valid = not use_wick_filter or bottom_wick_percent <= max_wick_percent
// Show appropriate label based on validation results
if show_breakout_validation_labels
if wick_filter_valid and breakout_distance_valid
label.new(x=bar_index, y=low, text="VALID", color=low_line_color, textcolor=color.white, style=label.style_label_up, size=size.tiny)
else
label.new(x=bar_index, y=low, text="INVALID", color=color.gray, textcolor=color.white, style=label.style_label_up, size=size.tiny)
// Mark breakout as invalid so no trade will be placed (regardless of label setting)
if not (wick_filter_valid and breakout_distance_valid)
breakout_direction := "invalid"
// ADDED: Check for second chance breakout (opposite direction after initial SL hit)
// FIXED: Added barstate.isconfirmed to avoid lookahead bias
if barstate.isconfirmed and not is_opening_range and not na(or_high) and not na(or_low) and lines_drawn and second_chance_available and not second_trade_taken and not daily_trades_complete and is_trading_day_allowed
// Calculate candle body and wick percentages
candle_body = math.abs(close - open)
top_wick = high - math.max(open, close)
bottom_wick = math.min(open, close) - low
top_wick_percent = candle_body > 0 ? (top_wick / candle_body) * 100 : 0
bottom_wick_percent = candle_body > 0 ? (bottom_wick / candle_body) * 100 : 0
// ADDED: Calculate opening range size for distance filters
or_size = or_high - or_low
// If first trade was LONG and failed, look for SHORT breakout
if first_trade_direction == "long" and close < or_low
// FIXED: Mark second chance breakout as taken FIRST
second_trade_taken := true
second_chance_available := false
breakout_candle_high := high
breakout_candle_low := low
breakout_price := close
breakout_direction := "short"
// ADDED: Validate this specific breakout candle against distance filter
breakout_distance_valid = true
if use_breakout_distance_filter
min_breakout_level = or_low - (or_size * min_breakout_multiplier)
max_breakout_level = or_low - (or_size * max_breakout_multiplier)
breakout_distance_valid := close <= min_breakout_level and close >= max_breakout_level
// Apply wick filter for short breakouts
wick_filter_valid = not use_wick_filter or bottom_wick_percent <= max_wick_percent
// Show appropriate label based on validation results
if show_second_chance_labels
if wick_filter_valid and breakout_distance_valid
label.new(x=bar_index, y=low, text="2nd Chance\nOR Low Break\nVALID", color=color.orange, textcolor=color.white, style=label.style_label_up, size=size.tiny)
else
label.new(x=bar_index, y=low, text="2nd Chance\nOR Low Break\nINVALID", color=color.gray, textcolor=color.white, style=label.style_label_up, size=size.tiny)
// Mark breakout as invalid so no trade will be placed (regardless of label setting)
if not (wick_filter_valid and breakout_distance_valid)
breakout_direction := "invalid"
// If first trade was SHORT and failed, look for LONG breakout
else if first_trade_direction == "short" and close > or_high
// FIXED: Mark second chance breakout as taken FIRST
second_trade_taken := true
second_chance_available := false
breakout_candle_high := high
breakout_candle_low := low
breakout_price := close
breakout_direction := "long"
// ADDED: Validate this specific breakout candle against distance filter
breakout_distance_valid = true
if use_breakout_distance_filter
min_breakout_level = or_high + (or_size * min_breakout_multiplier)
max_breakout_level = or_high + (or_size * max_breakout_multiplier)
breakout_distance_valid := close >= min_breakout_level and close <= max_breakout_level
// Apply wick filter for long breakouts
wick_filter_valid = not use_wick_filter or top_wick_percent <= max_wick_percent
// Show appropriate label based on validation results
if show_second_chance_labels
if wick_filter_valid and breakout_distance_valid
label.new(x=bar_index, y=high, text="2nd Chance\nOR High Break\nVALID", color=color.orange, textcolor=color.white, style=label.style_label_down, size=size.tiny)
else
label.new(x=bar_index, y=high, text="2nd Chance\nOR High Break\nINVALID", color=color.gray, textcolor=color.white, style=label.style_label_down, size=size.tiny)
// Mark breakout as invalid so no trade will be placed (regardless of label setting)
if not (wick_filter_valid and breakout_distance_valid)
breakout_direction := "invalid"
// === STRATEGY LOGIC ===
// Check if we have a breakout and place retracement entry orders
var bool entry_placed = false
var bool second_entry_placed = false // ADDED: Track second trade entry separately
var float entry_price = na
var float stop_loss = na
var float take_profit = na
var float trailing_stop = na
var bool trailing_active = false
var float initial_risk = na
var bool trailing_started = false
var string current_entry_id = na // FIXED: Track which entry ID we're using
// Arrays to store historical trade boxes
var array<box> historical_trade_boxes = array.new<box>()
var array<box> historical_sl_boxes = array.new<box>()
var array<box> historical_tp_boxes = array.new<box>()
// Variables to track current active trade boxes for extending to exit
var box current_profit_box = na
var box current_sl_box = na
// ADDED: General position close detection for extending boxes - Handle timing issues
if barstate.isconfirmed and strategy.position_size == 0 and strategy.position_size[1] != 0
// Extend trade visualization boxes to exact exit point when any position closes
if not na(current_profit_box)
// Ensure minimum 8 bars width or extend to current bar, whichever is longer
box_left = box.get_left(current_profit_box)
min_right = box_left + 8
final_right = math.max(min_right, bar_index)
box.set_right(current_profit_box, final_right)
current_profit_box := na // Clear reference after extending
if not na(current_sl_box)
// Ensure minimum 8 bars width or extend to current bar, whichever is longer
box_left = box.get_left(current_sl_box)
min_right = box_left + 8
final_right = math.max(min_right, bar_index)
box.set_right(current_sl_box, final_right)
current_sl_box := na // Clear reference after extending
// ADDED: Backup safety check - extend boxes if position is closed but boxes still active
if not na(current_profit_box) and strategy.position_size == 0
box_left = box.get_left(current_profit_box)
min_right = box_left + 8
final_right = math.max(min_right, bar_index)
box.set_right(current_profit_box, final_right)
current_profit_box := na
if not na(current_sl_box) and strategy.position_size == 0
box_left = box.get_left(current_sl_box)
min_right = box_left + 8
final_right = math.max(min_right, bar_index)
box.set_right(current_sl_box, final_right)
current_sl_box := na
// Reset entry flag on new day
if is_new_day
entry_placed := false
second_entry_placed := false // ADDED: Reset second entry flag
entry_price := na
stop_loss := na
take_profit := na
trailing_stop := na
trailing_active := false
initial_risk := na
trailing_started := false
current_entry_id := na // FIXED: Reset entry ID
current_profit_box := na // ADDED: Reset current trade boxes
current_sl_box := na
// SIMPLIFIED: Detect when position closes to enable second chance (FIXED for lookahead bias)
if barstate.isconfirmed and strategy.position_size == 0 and strategy.position_size[1] != 0 and entry_placed and not first_trade_sl_hit
// A position just closed and we had an active trade
if enable_second_chance and not second_trade_taken
// Simplified logic - if position closed, enable second chance
first_trade_sl_hit := true
first_trade_direction := breakout_direction
second_chance_available := true
// Reset variables for potential second trade
entry_price := na
trailing_stop := na
trailing_active := false
initial_risk := na
trailing_started := false
current_entry_id := na
// Add visual marker
if show_trade_status_labels
label.new(x=bar_index, y=close, text="Trade Closed\nSecond Chance Available", color=color.yellow, textcolor=color.black, style=label.style_label_down, size=size.tiny)
else
// Second chance not enabled or already taken - mark day complete
daily_trades_complete := true
// ADDED: Handle case where first breakout was invalid (no trade placed)
if breakout_occurred and breakout_direction == "invalid" and enable_second_chance and not first_trade_sl_hit
// First breakout was invalid, enable second chance immediately
first_trade_sl_hit := true
// Determine what direction the invalid breakout was
first_trade_direction := breakout_price > or_high ? "long" : "short"
second_chance_available := true
if show_trade_status_labels
label.new(x=bar_index + 1, y=(or_high + or_low) / 2, text="First Breakout Invalid\nSecond Chance Available", color=color.yellow, textcolor=color.black, style=label.style_label_center, size=size.tiny)
// REMOVED: Complex historical box cleanup to avoid lookahead bias
// Historical boxes will be cleaned up automatically by Pine Script's runtime
// Place entry orders after breakout - FIXED: Add barstate.isconfirmed for consistency
if barstate.isconfirmed and not daily_trades_complete and is_trading_day_allowed and ((breakout_occurred and not entry_placed and not na(breakout_candle_high) and breakout_direction != "invalid") or (second_trade_taken and not second_entry_placed and not na(breakout_candle_high) and breakout_direction != "invalid"))
// For long breakout
if breakout_direction == "long"
// Calculate stop loss based on selected method
if sl_type == "Breakout Candle"
stop_loss := breakout_candle_low - (sl_points * syminfo.mintick)
else
// Use opposite side of opening range (below opening range low)
stop_loss := or_low - (sl_points * syminfo.mintick)
if entry_type == "Retracement"
// Calculate retracement entry price (x% of breakout candle body)
breakout_candle_body = breakout_candle_high - breakout_candle_low
retracement_amount = breakout_candle_body * (retracement_percent / 100)
entry_price := breakout_candle_high - retracement_amount
// FIXED: Store the entry ID we're using (differentiate first vs second chance)
current_entry_id := second_trade_taken ? "Long Retracement 2nd" : "Long Retracement"
// Place buy limit order at retracement level
strategy.entry(current_entry_id, strategy.long, limit=entry_price, qty=contracts)
// Add visual markers
if show_entry_labels
entry_label_text = second_trade_taken ? "BUY LIMIT (2nd)\n" + str.tostring(entry_price, "#.##") : "BUY LIMIT\n" + str.tostring(entry_price, "#.##")
label.new(x=bar_index, y=entry_price, text=entry_label_text, color=color.green, textcolor=color.white, style=label.style_label_up, size=size.tiny)
else
// Immediate entry at breakout candle close
entry_price := breakout_price
// FIXED: Store the entry ID we're using (differentiate first vs second chance)
current_entry_id := second_trade_taken ? "Instant Long 2nd" : "Instant Long"
// Place buy market order
strategy.entry(current_entry_id, strategy.long, qty=contracts)
// Add visual markers
if show_entry_labels
entry_label_text = second_trade_taken ? "BUY MARKET (2nd)\n" + str.tostring(entry_price, "#.##") : "BUY MARKET\n" + str.tostring(entry_price, "#.##")
label.new(x=bar_index, y=entry_price, text=entry_label_text, color=color.green, textcolor=color.white, style=label.style_label_up, size=size.tiny)
// Calculate take profit based on risk:reward
risk_size = entry_price - stop_loss
take_profit := entry_price + (risk_size * risk_multiplier)
// FIXED: Set exit orders with proper entry ID and always include initial stop loss
if use_trailing_sl
// Initialize trailing stop and calculate initial risk
trailing_stop := stop_loss
trailing_active := true
initial_risk := math.abs(entry_price - stop_loss)
trailing_started := false
// FIXED: Always set initial stop loss, even with trailing enabled
exit_id = second_trade_taken ? "Long Exit 2nd" : "Long Exit"
strategy.exit(exit_id, current_entry_id, stop=stop_loss, limit=take_profit)
else
// FIXED: Use stored entry ID
exit_id = second_trade_taken ? "Long Exit 2nd" : "Long Exit"
strategy.exit(exit_id, current_entry_id, stop=stop_loss, limit=take_profit)
// Create trade visualization boxes (TradingView style) - FIXED: Minimum 8 bars width
// Blue profit zone box (from entry to take profit)
// Store trade boxes for historical display - FIXED: Remove time usage
array.push(historical_trade_boxes, current_profit_box)
array.push(historical_sl_boxes, current_sl_box)
array.push(historical_tp_boxes, na) // No TP box for long trades
// Add stop loss and take profit markers
if show_sl_tp_labels
label.new(x=bar_index, y=stop_loss, text="SL\n" + str.tostring(stop_loss, "#.##"), color=color.red, textcolor=color.white, style=label.style_label_down, size=size.tiny)
label.new(x=bar_index, y=take_profit, text="TP\n" + str.tostring(take_profit, "#.##"), color=color.blue, textcolor=color.white, style=label.style_label_down, size=size.tiny)
// ADDED: Set the appropriate entry flag based on which trade this is
if second_trade_taken
second_entry_placed := true
daily_trades_complete := true
else
entry_placed := true
// For short breakout
else if breakout_direction == "short"
// Calculate stop loss based on selected method
if sl_type == "Breakout Candle"
stop_loss := breakout_candle_high + (sl_points * syminfo.mintick)
else
// Use opposite side of opening range (above opening range high)
stop_loss := or_high + (sl_points * syminfo.mintick)
if entry_type == "Retracement"
// Calculate retracement entry price (x% of breakout candle body)
breakout_candle_body = breakout_candle_high - breakout_candle_low
retracement_amount = breakout_candle_body * (retracement_percent / 100)
entry_price := breakout_candle_low + retracement_amount
// FIXED: Store the entry ID we're using (differentiate first vs second chance)
current_entry_id := second_trade_taken ? "Short Retracement 2nd" : "Short Retracement"
// Place sell limit order at retracement level
strategy.entry(current_entry_id, strategy.short, limit=entry_price, qty=contracts)
// Add visual markers
if show_entry_labels
entry_label_text = second_trade_taken ? "SELL LIMIT (2nd)\n" + str.tostring(entry_price, "#.##") : "SELL LIMIT\n" + str.tostring(entry_price, "#.##")
label.new(x=bar_index, y=entry_price, text=entry_label_text, color=color.red, textcolor=color.white, style=label.style_label_down, size=size.tiny)
else
// Immediate entry at breakout candle close
entry_price := breakout_price
// FIXED: Store the entry ID we're using (differentiate first vs second chance)
current_entry_id := second_trade_taken ? "Instant 2nd" : "Instant Short"
// Place sell market order
strategy.entry(current_entry_id, strategy.short, qty=contracts)
// Add visual markers
if show_entry_labels
entry_label_text = second_trade_taken ? "SELL MARKET (2nd)\n" + str.tostring(entry_price, "#.##") : "SELL MARKET\n" + str.tostring(entry_price, "#.##")
label.new(x=bar_index, y=entry_price, text=entry_label_text, color=color.red, textcolor=color.white, style=label.style_label_down, size=size.tiny)
// Calculate take profit based on risk:reward
risk_size = stop_loss - entry_price
take_profit := entry_price - (risk_size * risk_multiplier)
// FIXED: Set exit orders with proper entry ID and always include initial stop loss
if use_trailing_sl
// Initialize trailing stop and calculate initial risk
trailing_stop := stop_loss
trailing_active := true
initial_risk := math.abs(entry_price - stop_loss)
trailing_started := false
// FIXED: Always set initial stop loss, even with trailing enabled
exit_id = second_trade_taken ? "Short Exit 2nd" : "Short Exit"
strategy.exit(exit_id, current_entry_id, stop=stop_loss, limit=take_profit)
else
// FIXED: Use stored entry ID
exit_id = second_trade_taken ? "Short Exit 2nd" : "Short Exit"
strategy.exit(exit_id, current_entry_id, stop=stop_loss, limit=take_profit)
// Create trade visualization boxes (TradingView style) - FIXED: Minimum 8 bars width
// Store trade boxes for historical display - FIXED: Remove time usage
array.push(historical_trade_boxes, current_profit_box)
array.push(historical_sl_boxes, current_sl_box)
array.push(historical_tp_boxes, na) // No TP box for short trades
// Add stop loss and take profit markers
if show_sl_tp_labels
label.new(x=bar_index, y=stop_loss, text="SL\n" + str.tostring(stop_loss, "#.##"), color=color.red, textcolor=color.white, style=label.style_label_up, size=size.tiny)
label.new(x=bar_index, y=take_profit, text="TP\n" + str.tostring(take_profit, "#.##"), color=color.blue, textcolor=color.white, style=label.style_label_up, size=size.tiny)
// ADDED: Set the appropriate entry flag based on which trade this is
if second_trade_taken
second_entry_placed := true
daily_trades_complete := true
else
entry_placed := true
// === TRAILING STOP LOGIC ===
// FIXED: Proper trailing stop loss management
if use_trailing_sl and trailing_active and strategy.position_size != 0 and not na(current_entry_id)
if strategy.position_size > 0 // Long position
// Calculate current unrealized profit in points
current_profit = close - entry_price
profit_r = current_profit / initial_risk
// Check if we should start trailing (after X R profit)
if not trailing_started and profit_r >= profit_r_multiplier
trailing_started := true
// Start trailing from a level that's better than the initial stop
trailing_stop := math.max(trailing_stop, close - (atr * atr_multiplier))
// Update trailing stop if trailing has started
if trailing_started
// Calculate new trailing stop using ATR
potential_new_stop = close - (atr * atr_multiplier)
// Only move stop loss up (never down) and ensure it's better than initial SL
if potential_new_stop > trailing_stop and potential_new_stop > stop_loss
trailing_stop := potential_new_stop
// Update the exit order with new trailing stop
exit_id = second_trade_taken ? "Long Exit 2nd" : "Long Exit"
strategy.exit(exit_id, current_entry_id, stop=trailing_stop, limit=take_profit)
else if strategy.position_size < 0 // Short position
// Calculate current unrealized profit in points
current_profit = entry_price - close
profit_r = current_profit / initial_risk
// Check if we should start trailing (after X R profit)
if not trailing_started and profit_r >= profit_r_multiplier
trailing_started := true
// Start trailing from a level that's better than the initial stop
trailing_stop := math.min(trailing_stop, close + (atr * atr_multiplier))
// Update trailing stop if trailing has started
if trailing_started
// Calculate new trailing stop using ATR
potential_new_stop = close + (atr * atr_multiplier)
// Only move stop loss down (never up) and ensure it's better than initial SL
if potential_new_stop < trailing_stop and potential_new_stop < stop_loss
trailing_stop := potential_new_stop
// Update the exit order with new trailing stop
exit_id = second_trade_taken ? "Short Exit 2nd" : "Short Exit"
strategy.exit(exit_id, current_entry_id, stop=trailing_stop, limit=take_profit)
// === SESSION END CLOSE ===
// Force close all positions at configured session end time (optional)
// FIXED: Using configurable hour/minute with timezone
if force_session_close and current_hour == session_end_hour and current_minute == session_end_minute
// ADDED: Extend boxes immediately before session close to prevent timing issues
if not na(current_profit_box)
// Ensure minimum 8 bars width or extend to current bar, whichever is longer
box_left = box.get_left(current_profit_box)
min_right = box_left + 8
final_right = math.max(min_right, bar_index)
box.set_right(current_profit_box, final_right)
current_profit_box := na // Clear reference after extending
if not na(current_sl_box)
// Ensure minimum 8 bars width or extend to current bar, whichever is longer
box_left = box.get_left(current_sl_box)
min_right = box_left + 8
final_right = math.max(min_right, bar_index)
box.set_right(current_sl_box, final_right)
current_sl_box := na // Clear reference after extending
strategy.close_all(comment="Session End Close")
// === ALERTS ===
alert_once_long = (strategy.position_size > 0) and (strategy.position_size[1] == 0)
alert_once_short = (strategy.position_size < 0) and (strategy.position_size[1] == 0)
alertcondition(alert_once_long, title="Long Entry (Once)", message="Long Entry Signal")
alertcondition(alert_once_short, title="Short Entry (Once)", message="Short Entry Signal")