//+------------------------------------------------------------------+ //| Daily Sweep + M15 Turtlesoup.mq5| //| Copyright 2025, Setup Radar | //| https://www.Setup-Radar.com | //| Author: X.com/Hectorand | //|Production Ready Code | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Setup Radar" #property link "https://www.Setup-Radar.com" #property version "1.5" // Updated version with trade execution // Include sector mapping #include #include // ============================================================ // INPUT PARAMETERS // ============================================================ input int SwingLeftBars = 2; // Number of bars to check on the left for swing points input int SwingRightBars = 2; // Number of bars to check on the right for swing points input bool EnableDebugPrints = false; // Enable debug prints for screenshot and API calls input int MinCandlesForTradeableDay = 48; // Minimum M15 candles for a day to be considered "tradeable" input bool CloseChartAfterScreenshot = true; // Close temporary chart after screenshot input double LotSize = 0.01; // Lot size for trades input double MaxRiskPercent = 2.0; // Maximum risk as percentage of account balance input bool EnableAutoTrading = true; // Enable automatic trade execution string strategyName = "Daily Sweep + M15 Turtlesoup"; // Strategy identifier for alerts string strategyId = "0000001"; string allowedTimeframe[]; // Empty array, no elements // ============================================================ // CONFIGURATION CONSTANTS // ============================================================ int SwingLookbackBars = 20; // Number of bars to look back for swing detection // Server URLs for alert and screenshot delivery string alertServerURL = "https://api.setupradar.app/api/v1/strategy/webhook"; // Endpoint for alert JSON data string screenshotServerURL = "https://api.setupradar.app/api/v1/strategy/webhook"; // Endpoint for screenshot binary data // Trading session definitions string SydneySessionStart = "22:00"; // Sydney session start time (crosses midnight) string SydneySessionEnd = "00:00"; // Sydney session end time string AsiaSessionStart = "00:00"; // Asian session start time string AsiaSessionEnd = "07:00"; // Asian session end time string LondonSessionStart = "07:00"; // London session start time string LondonSessionEnd = "12:00"; // London session end time string NewYorkSessionStart = "12:00"; // New York session start time string NewYorkSessionEnd = "22:00"; // New York session end time // ============================================================ // TIME TRACKING VARIABLES // ============================================================ string currentSession = "OutsideSession"; // Current trading session name // ============================================================ // MARKET BIAS ENUMERATION AND STATE // ============================================================ enum ENUM_BIAS { BIAS_NEUTRAL, BIAS_BULLISH, BIAS_BEARISH }; // ============================================================ // TRADE STATE ENUMERATION // ============================================================ enum ENUM_TRADE_STATE { TRADE_NONE, // No trade placed TRADE_PENDING, // Alert triggered, waiting for retracement TRADE_EXECUTED // Trade has been executed }; // ============================================================ // SYMBOL-SPECIFIC DATA STRUCTURES // ============================================================ // Market bias per symbol struct BiasData { string symbol; ENUM_BIAS bias; }; // Retracement tracking per symbol (NEW - separate from main alerts) struct RetracementTracker { string symbol; // Symbol name // Bullish retracement data bool bullishBreakoutOccurred; // Flag if bullish breakout happened datetime bullishBreakoutTime; // Time of bullish breakout double bullishBreakoutPrice; // Price at breakout (A point) double bullishBPrice; // B point price double bullishSwingHigh; // Swing high price datetime bullishSwingHighTime; // Swing high time double bullishFortyPercentLevel; // 40% level for retracement double bullishLast_LL_Prices[2]; // Last two lower low prices datetime bullishLast_LL_Times[2]; // Last two lower low times double bullishLast_HH_Prices[2]; // Last two higher high prices (for reference) datetime bullishLast_HH_Times[2]; // Last two higher high times (for reference) double bullishPrevDayLow; // Previous day low datetime bullishPrevDayLowTime; // Previous day low time ENUM_TRADE_STATE bullishTradeState; // Track trade state for bullish ulong bullishTradeTicket; // Ticket of executed trade // Bearish retracement data bool bearishBreakoutOccurred; // Flag if bearish breakout happened datetime bearishBreakoutTime; // Time of bearish breakout double bearishBreakoutPrice; // Price at breakout (A point) double bearishBPrice; // B point price double bearishSwingLow; // Swing low price datetime bearishSwingLowTime; // Swing low time double bearishFortyPercentLevel; // 40% level for retracement double bearishLast_HH_Prices[2]; // Last two higher high prices datetime bearishLast_HH_Times[2]; // Last two higher high times double bearishLast_LL_Prices[2]; // Last two lower low prices (for reference) datetime bearishLast_LL_Times[2]; // Last two lower low times (for reference) double bearishPrevDayHigh; // Previous day high datetime bearishPrevDayHighTime; // Previous day high time ENUM_TRADE_STATE bearishTradeState; // Track trade state for bearish ulong bearishTradeTicket; // Ticket of executed trade }; // Alert tracking per symbol (ORIGINAL - UNCHANGED) struct AlertTracker { string symbol; // Symbol name bool bullishAlertSent; // Bullish alert sent flag for this symbol int bullishAlertCount; // Bullish alert count for this symbol bool bearishAlertSent; // Bearish alert sent flag for this symbol int bearishAlertCount; // Bearish alert count for this symbol double lastBullishSwingHigh; // Last alerted bullish swing high for this symbol double lastBearishSwingLow; // Last alerted bearish swing low for this symbol datetime bullishAlertTime; // Time when bullish alert was sent (C time - FIXED) datetime bearishAlertTime; // Time when bearish alert was sent (C time - FIXED) }; // Previous day data per symbol struct PrevDayData { string symbol; datetime lowTime; // Time of previous day's low (M15 candle) int lowIndex; // Bar index of previous day's low double lowPrice; // Price of previous day's low datetime highTime; // Time of previous day's high (M15 candle) int highIndex; // Bar index of previous day's high double highPrice; // Price of previous day's high }; // Swing point data per symbol struct SwingData { string symbol; // Dynamic arrays for storing all detected swing points double swingLowPrices[]; datetime swingLowTimes[]; double swingHighPrices[]; datetime swingHighTimes[]; // Arrays for last 2 consecutive higher highs (temporary) double HH_Prices[2]; // [0] = H1 (older), [1] = H2 (newer) datetime HH_Times[2]; // [0] = H1 time, [1] = H2 time // Arrays for last 2 consecutive lower lows (temporary) double LL_Prices[2]; // [0] = L1 (older), [1] = L2 (newer) datetime LL_Times[2]; // [0] = L1 time, [1] = L2 time // Static arrays for valid last 2 consecutive swings (persistent) double Last_HH_Prices[2]; // [0] = H1 (older), [1] = H2 (newer) - static datetime Last_HH_Times[2]; // [0] = H1 time, [1] = H2 time - static double Last_LL_Prices[2]; // [0] = L1 (older), [1] = L2 (newer) - static datetime Last_LL_Times[2]; // [0] = L1 time, [1] = L2 time - static // Bullish trading scenario variables double bullishSwingHigh; // Current bullish swing high (resistance level) - Point A datetime bullishSwingHighTime; // Time when bullish swing high occurred int bullishBarsInBetween; // Number of bars between two bullish swing lows // Bearish trading scenario variables double bearishSwingLow; // Current bearish swing low (support level) - Point A datetime bearishSwingLowTime; // Time when bearish swing low occurred int bearishBarsInBetween; // Number of bars between two bearish swing highs // Current market prices double currentAskPrice; double currentBidPrice; // Pattern validity flags bool bullishPatternValid; // Flag indicating if bullish pattern is valid based on PDL condition bool bearishPatternValid; // Flag indicating if bearish pattern is valid based on PDH condition // Enhanced validation flags bool bullishBIsLowest; // Flag: B is the lowest low between SSL and C bool bullishAIsHighest; // Flag: A is the highest high between A and C+1 bool bearishBIsHighest; // Flag: B is the highest high between BSL and C bool bearishAIsLowest; // Flag: A is the lowest low between A and C+1 }; // ============================================================ // GLOBAL ARRAYS (Symbol-Specific) // ============================================================ string Symbols[]; int SymbolsCount; datetime last_bar_time[]; datetime last_bar_time_d[]; BiasData biasData[]; AlertTracker alertTrackers[]; // ORIGINAL - UNCHANGED RetracementTracker retraceTrackers[]; // NEW - separate tracking PrevDayData prevDayData[]; SwingData swingData[]; CTrade trade; // Global trade object //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { ArrayResize(allowedTimeframe, 1); allowedTimeframe[0] = "M15"; // Initialize trade object trade.SetExpertMagicNumber(12345); trade.SetDeviationInPoints(10); // Get all symbols SymbolsCount = SymbolsTotal(true); ArrayResize(Symbols, SymbolsCount); ArrayResize(last_bar_time, SymbolsCount); ArrayResize(last_bar_time_d, SymbolsCount); // Initialize symbol-specific data arrays ArrayResize(biasData, SymbolsCount); ArrayResize(alertTrackers, SymbolsCount); // ORIGINAL ArrayResize(retraceTrackers, SymbolsCount); // NEW ArrayResize(prevDayData, SymbolsCount); ArrayResize(swingData, SymbolsCount); for(int i = 0; i < SymbolsCount; i++) { string symbol = SymbolName(i, true); Symbols[i] = symbol; // Initialize time tracking last_bar_time[i] = iTime(symbol, PERIOD_M15, 0); last_bar_time_d[i] = iTime(symbol, PERIOD_D1, 0); // Initialize bias data biasData[i].symbol = symbol; biasData[i].bias = BIAS_NEUTRAL; // Initialize alert trackers (ORIGINAL - UNCHANGED) alertTrackers[i].symbol = symbol; alertTrackers[i].bullishAlertSent = false; alertTrackers[i].bullishAlertCount = 0; alertTrackers[i].bearishAlertSent = false; alertTrackers[i].bearishAlertCount = 0; alertTrackers[i].lastBullishSwingHigh = 0; alertTrackers[i].lastBearishSwingLow = 0; alertTrackers[i].bullishAlertTime = 0; alertTrackers[i].bearishAlertTime = 0; // Initialize retracement trackers (NEW) retraceTrackers[i].symbol = symbol; retraceTrackers[i].bullishBreakoutOccurred = false; retraceTrackers[i].bullishBreakoutTime = 0; retraceTrackers[i].bullishBreakoutPrice = 0; retraceTrackers[i].bullishBPrice = 0; retraceTrackers[i].bullishSwingHigh = 0; retraceTrackers[i].bullishSwingHighTime = 0; retraceTrackers[i].bullishFortyPercentLevel = 0; retraceTrackers[i].bullishTradeState = TRADE_NONE; retraceTrackers[i].bullishTradeTicket = 0; ArrayInitialize(retraceTrackers[i].bullishLast_LL_Prices, 0); ArrayInitialize(retraceTrackers[i].bullishLast_LL_Times, 0); ArrayInitialize(retraceTrackers[i].bullishLast_HH_Prices, 0); ArrayInitialize(retraceTrackers[i].bullishLast_HH_Times, 0); retraceTrackers[i].bullishPrevDayLow = 0; retraceTrackers[i].bullishPrevDayLowTime = 0; retraceTrackers[i].bearishBreakoutOccurred = false; retraceTrackers[i].bearishBreakoutTime = 0; retraceTrackers[i].bearishBreakoutPrice = 0; retraceTrackers[i].bearishBPrice = 0; retraceTrackers[i].bearishSwingLow = 0; retraceTrackers[i].bearishSwingLowTime = 0; retraceTrackers[i].bearishFortyPercentLevel = 0; retraceTrackers[i].bearishTradeState = TRADE_NONE; retraceTrackers[i].bearishTradeTicket = 0; ArrayInitialize(retraceTrackers[i].bearishLast_HH_Prices, 0); ArrayInitialize(retraceTrackers[i].bearishLast_HH_Times, 0); ArrayInitialize(retraceTrackers[i].bearishLast_LL_Prices, 0); ArrayInitialize(retraceTrackers[i].bearishLast_LL_Times, 0); retraceTrackers[i].bearishPrevDayHigh = 0; retraceTrackers[i].bearishPrevDayHighTime = 0; // Initialize previous day data prevDayData[i].symbol = symbol; prevDayData[i].lowTime = 0; prevDayData[i].lowIndex = -1; prevDayData[i].lowPrice = 0; prevDayData[i].highTime = 0; prevDayData[i].highIndex = -1; prevDayData[i].highPrice = 0; // Initialize swing data arrays swingData[i].symbol = symbol; ArrayResize(swingData[i].swingLowPrices, 0); ArrayResize(swingData[i].swingLowTimes, 0); ArrayResize(swingData[i].swingHighPrices, 0); ArrayResize(swingData[i].swingHighTimes, 0); ArrayInitialize(swingData[i].HH_Prices, EMPTY_VALUE); ArrayInitialize(swingData[i].HH_Times, 0); ArrayInitialize(swingData[i].LL_Prices, EMPTY_VALUE); ArrayInitialize(swingData[i].LL_Times, 0); ArrayInitialize(swingData[i].Last_HH_Prices, 0); ArrayInitialize(swingData[i].Last_HH_Times, 0); ArrayInitialize(swingData[i].Last_LL_Prices, 0); ArrayInitialize(swingData[i].Last_LL_Times, 0); swingData[i].bullishSwingHigh = 0; swingData[i].bullishSwingHighTime = 0; swingData[i].bullishBarsInBetween = 0; swingData[i].bearishSwingLow = 0; swingData[i].bearishSwingLowTime = 0; swingData[i].bearishBarsInBetween = 0; swingData[i].currentAskPrice = 0; swingData[i].currentBidPrice = 0; // Initialize pattern validity flags swingData[i].bullishPatternValid = false; swingData[i].bearishPatternValid = false; // Initialize enhanced validation flags swingData[i].bullishBIsLowest = false; swingData[i].bullishAIsHighest = false; swingData[i].bearishBIsHighest = false; swingData[i].bearishAIsLowest = false; } if(EnableDebugPrints) { Print("DEBUG: Expert Advisor initialized. Monitoring ", SymbolsCount, " symbols"); Print("DEBUG: Created symbol-specific data structures for all symbols"); Print("DEBUG: 2-Alert system enabled (Breakout + Retracement)"); Print("DEBUG: Auto-trading is ", EnableAutoTrading ? "ENABLED" : "DISABLED"); } EventSetTimer(2); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Clean up all chart objects DeleteAllObjects(); EventKillTimer(); if(EnableDebugPrints) { Print("DEBUG: Expert Advisor deinitialized. Reason: ", reason); } } //+------------------------------------------------------------------+ //| Get symbol index in all data arrays | //+------------------------------------------------------------------+ int GetSymbolIndex(string symbol) { for(int i = 0; i < SymbolsCount; i++) { if(Symbols[i] == symbol) return i; } return -1; } //+------------------------------------------------------------------+ //| Expert timer function - MAIN EXECUTION LOOP | //+------------------------------------------------------------------+ void OnTimer() { for(int i = 0; i < SymbolsCount; i++) { string symbol = Symbols[i]; // Update current market prices swingData[i].currentAskPrice = SymbolInfoDouble(symbol, SYMBOL_ASK); swingData[i].currentBidPrice = SymbolInfoDouble(symbol, SYMBOL_BID); // Check if new trading day has started if(IsNewDay(i)) { biasData[i].bias = BIAS_NEUTRAL; // Reset bias for this symbol // Reset alert counters for this symbol (ORIGINAL - UNCHANGED) alertTrackers[i].bullishAlertCount = 0; alertTrackers[i].bearishAlertCount = 0; alertTrackers[i].bullishAlertSent = false; alertTrackers[i].bearishAlertSent = false; // Reset retracement trackers (NEW) retraceTrackers[i].bullishBreakoutOccurred = false; retraceTrackers[i].bearishBreakoutOccurred = false; retraceTrackers[i].bullishTradeState = TRADE_NONE; retraceTrackers[i].bearishTradeState = TRADE_NONE; // Reset pattern validity flags swingData[i].bullishPatternValid = false; swingData[i].bearishPatternValid = false; swingData[i].bullishBIsLowest = false; swingData[i].bullishAIsHighest = false; swingData[i].bearishBIsHighest = false; swingData[i].bearishAIsLowest = false; if(EnableDebugPrints) { Print("DEBUG: Daily reset for ", symbol, " - Counters and retracement flags reset"); } } // Check for new 15-minute bar formation if(IsNew15MinBar(i)) { currentSession = GetCurrentSession(); // Global session (same for all) // Calculate previous day's high and low from M15 candles for THIS symbol prevDayData[i].lowTime = GetM15PrevDayLowTime(symbol); if(prevDayData[i].lowTime > 0) { prevDayData[i].lowIndex = iBarShift(symbol, PERIOD_M15, prevDayData[i].lowTime, true); prevDayData[i].lowPrice = iLow(symbol, PERIOD_M15, prevDayData[i].lowIndex); } prevDayData[i].highTime = GetM15PrevDayHighTime(symbol); if(prevDayData[i].highTime > 0) { prevDayData[i].highIndex = iBarShift(symbol, PERIOD_M15, prevDayData[i].highTime, true); prevDayData[i].highPrice = iHigh(symbol, PERIOD_M15, prevDayData[i].highIndex); } // Debug print for previous day values if(EnableDebugPrints) { Print("DEBUG: Symbol=", symbol, " PrevDayHigh=", prevDayData[i].highPrice, " at ", TimeToString(prevDayData[i].highTime), " PrevDayLow=", prevDayData[i].lowPrice, " at ", TimeToString(prevDayData[i].lowTime)); } // Reset swing arrays for fresh analysis ArrayResize(swingData[i].swingLowPrices, 0); ArrayResize(swingData[i].swingLowTimes, 0); ArrayResize(swingData[i].swingHighPrices, 0); ArrayResize(swingData[i].swingHighTimes, 0); // Core analysis functions for THIS symbol FindSwingPoints(i); // Find swing highs and lows GetConsecutiveHighsAndLows(i); // Identify consecutive swings GetBreakPoint(i); // Determine breakout points // Validate patterns against previous day levels ValidatePatterns(i); } // Update market bias based on daily price action for THIS symbol FindBias(i); // Check for ORIGINAL alert conditions (UNCHANGED) CheckOriginalAlerts(i); // Check for retracement alerts (NEW - separate monitoring) CheckRetracementAlerts(i); } } // ============================================================ // TRADE EXECUTION FUNCTIONS // ============================================================ //+------------------------------------------------------------------+ //| Calculate position size based on risk percentage | //+------------------------------------------------------------------+ double CalculatePositionSize(string symbol, double entryPrice, double stopLossPrice, ENUM_ORDER_TYPE orderType) { double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE); double riskAmount = accountBalance * (MaxRiskPercent / 100.0); double pointValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE); double stopDistancePoints = MathAbs(entryPrice - stopLossPrice) / tickSize; double riskPerLot = stopDistancePoints * pointValue; if(riskPerLot <= 0) return LotSize; // Return default lot size if calculation fails double lotSize = riskAmount / riskPerLot; // Normalize lot size double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX); lotSize = MathFloor(lotSize / lotStep) * lotStep; lotSize = MathMax(minLot, MathMin(maxLot, lotSize)); if(EnableDebugPrints) { Print("DEBUG: Position size calculation for ", symbol); Print("DEBUG: Entry: ", entryPrice, " SL: ", stopLossPrice); Print("DEBUG: Stop distance points: ", stopDistancePoints); Print("DEBUG: Risk amount: ", riskAmount); Print("DEBUG: Calculated lot size: ", lotSize); } return lotSize; } //+------------------------------------------------------------------+ //| Execute bullish market buy trade | //+------------------------------------------------------------------+ bool ExecuteBullishTrade(int symbolIndex) { if(!EnableAutoTrading) { if(EnableDebugPrints) Print("DEBUG: Auto-trading disabled, skipping bullish trade for ", Symbols[symbolIndex]); return false; } string symbol = Symbols[symbolIndex]; double entryPrice = retraceTrackers[symbolIndex].bullishFortyPercentLevel; double stopLoss = retraceTrackers[symbolIndex].bullishBPrice; double takeProfit = retraceTrackers[symbolIndex].bullishSwingHigh; // Calculate position size double lotSize = CalculatePositionSize(symbol, entryPrice, stopLoss, ORDER_TYPE_BUY); // Ensure price is normalized int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); entryPrice = NormalizeDouble(entryPrice, digits); stopLoss = NormalizeDouble(stopLoss, digits); takeProfit = NormalizeDouble(takeProfit, digits); if(EnableDebugPrints) { Print("DEBUG: Executing BULLISH MARKET BUY for ", symbol); Print("DEBUG: Entry (40% level): ", entryPrice); Print("DEBUG: Stop Loss (B price): ", stopLoss); Print("DEBUG: Take Profit (Swing High): ", takeProfit); Print("DEBUG: Lot Size: ", lotSize); } // Set slippage trade.SetDeviationInPoints(10); // Open buy market order bool result = trade.Buy(lotSize, symbol, entryPrice, stopLoss, takeProfit, "TurtleSoup Bullish Setup"); if(result) { retraceTrackers[symbolIndex].bullishTradeTicket = trade.ResultDeal(); retraceTrackers[symbolIndex].bullishTradeState = TRADE_EXECUTED; if(EnableDebugPrints) { Print("DEBUG: SUCCESS - Bullish trade executed! Ticket: ", retraceTrackers[symbolIndex].bullishTradeTicket); } return true; } else { if(EnableDebugPrints) { Print("DEBUG: FAILED - Bullish trade execution failed. Error: ", GetLastError()); } return false; } } //+------------------------------------------------------------------+ //| Execute bearish market sell trade | //+------------------------------------------------------------------+ bool ExecuteBearishTrade(int symbolIndex) { if(!EnableAutoTrading) { if(EnableDebugPrints) Print("DEBUG: Auto-trading disabled, skipping bearish trade for ", Symbols[symbolIndex]); return false; } string symbol = Symbols[symbolIndex]; double entryPrice = retraceTrackers[symbolIndex].bearishFortyPercentLevel; double stopLoss = retraceTrackers[symbolIndex].bearishBPrice; double takeProfit = retraceTrackers[symbolIndex].bearishSwingLow; // Calculate position size double lotSize = CalculatePositionSize(symbol, entryPrice, stopLoss, ORDER_TYPE_SELL); // Ensure price is normalized int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); entryPrice = NormalizeDouble(entryPrice, digits); stopLoss = NormalizeDouble(stopLoss, digits); takeProfit = NormalizeDouble(takeProfit, digits); if(EnableDebugPrints) { Print("DEBUG: Executing BEARISH MARKET SELL for ", symbol); Print("DEBUG: Entry (40% level): ", entryPrice); Print("DEBUG: Stop Loss (B price): ", stopLoss); Print("DEBUG: Take Profit (Swing Low): ", takeProfit); Print("DEBUG: Lot Size: ", lotSize); } // Set slippage trade.SetDeviationInPoints(10); // Open sell market order bool result = trade.Sell(lotSize, symbol, entryPrice, stopLoss, takeProfit, "TurtleSoup Bearish Setup"); if(result) { retraceTrackers[symbolIndex].bearishTradeTicket = trade.ResultDeal(); retraceTrackers[symbolIndex].bearishTradeState = TRADE_EXECUTED; if(EnableDebugPrints) { Print("DEBUG: SUCCESS - Bearish trade executed! Ticket: ", retraceTrackers[symbolIndex].bearishTradeTicket); } return true; } else { if(EnableDebugPrints) { Print("DEBUG: FAILED - Bearish trade execution failed. Error: ", GetLastError()); } return false; } } // ============================================================ // ORIGINAL ALERT FUNCTION - COMPLETELY UNCHANGED // ============================================================ //+------------------------------------------------------------------+ //| Original alert monitoring (unchanged from version 1.3) | //+------------------------------------------------------------------+ void CheckOriginalAlerts(int symbolIndex) { string symbol = Symbols[symbolIndex]; // Get close price of previous candle for gap detection double closeLastCandle = iClose(symbol, PERIOD_M15, 1); // BULLISH ALERT CONDITION (ORIGINAL - UNCHANGED) if(biasData[symbolIndex].bias == BIAS_BULLISH && swingData[symbolIndex].bullishPatternValid && // Check PDL validation swingData[symbolIndex].currentBidPrice > swingData[symbolIndex].bullishSwingHigh && closeLastCandle < swingData[symbolIndex].bullishSwingHigh && swingData[symbolIndex].bullishSwingHigh != alertTrackers[symbolIndex].lastBullishSwingHigh && alertTrackers[symbolIndex].bullishAlertSent == false && alertTrackers[symbolIndex].bullishAlertCount < 1) { datetime timeNow = TimeCurrent(); // Perform enhanced validations swingData[symbolIndex].bullishBIsLowest = ValidateBullishBIsLowest(symbolIndex, timeNow); swingData[symbolIndex].bullishAIsHighest = ValidateBullishAIsHighest(symbolIndex, timeNow); // Validate price action: No trading above bullishSwingHigh before trigger int barsBetweenSwingHighAndTrigger = GetBarsBetweenTimes(swingData[symbolIndex].bullishSwingHighTime, timeNow, symbol); int barsBetweenFirstLowAndTrigger = GetBarsBetweenTimes(swingData[symbolIndex].Last_LL_Times[0], timeNow, symbol); bool allBarsValid = true; // Check all bars between swing high and trigger time for(int b = barsBetweenSwingHighAndTrigger - 1; b >= 1; b--) { if(iHigh(symbol, PERIOD_M15, b) >= swingData[symbolIndex].bullishSwingHigh) { allBarsValid = false; break; } } // Check all bars between first low and trigger time for(int b = barsBetweenFirstLowAndTrigger; b >= 0; b--) { if(iLow(symbol, PERIOD_M15, b) < swingData[symbolIndex].Last_LL_Prices[1]) { allBarsValid = false; break; } } // If all validations pass, trigger bullish alert if(allBarsValid && swingData[symbolIndex].bullishBIsLowest && swingData[symbolIndex].bullishAIsHighest) { // Update alert state for THIS SYMBOL (ORIGINAL) alertTrackers[symbolIndex].bullishAlertSent = true; alertTrackers[symbolIndex].bullishAlertCount++; alertTrackers[symbolIndex].lastBullishSwingHigh = swingData[symbolIndex].bullishSwingHigh; alertTrackers[symbolIndex].bullishAlertTime = TimeCurrent(); // STORE C TIME - NEVER CHANGES // SAVE DATA FOR RETRACEMENT ALERT (NEW) SaveBullishRetracementData(symbolIndex); if(EnableDebugPrints) { Print("DEBUG: BULLISH BREAKOUT ALERT for ", symbol, " at ", TimeToString(timeNow)); Print("DEBUG: Pattern validation: PASSED (B <= PDL)"); Print("DEBUG: Enhanced validations: B is lowest ✓, A is highest ✓"); Print("DEBUG: Swing High Price (A): ", swingData[symbolIndex].bullishSwingHigh, " Time: ", TimeToString(swingData[symbolIndex].bullishSwingHighTime)); Print("DEBUG: B Point Price: ", swingData[symbolIndex].Last_LL_Prices[1]); Print("DEBUG: Market Bid Price: ", swingData[symbolIndex].currentBidPrice); Print("DEBUG: Alert #", alertTrackers[symbolIndex].bullishAlertCount, " for today"); Print("DEBUG: C time FIXED at: ", TimeToString(alertTrackers[symbolIndex].bullishAlertTime)); Print("DEBUG: Now monitoring for retracement to 40% level..."); } // Capture chart state using BREAKOUT annotations string screenshotFilename = TakeScreenshot("Bullish_Breakout", symbol, symbolIndex); // Generate unique identifier string uniqueId = GenerateUniqueId(); // Send alert data (ORIGINAL - UNCHANGED) SendAlertData("bullish", swingData[symbolIndex].bullishSwingHigh, swingData[symbolIndex].bullishSwingHighTime, alertTrackers[symbolIndex].bullishAlertTime, screenshotFilename, uniqueId, symbol, symbolIndex); // Send screenshot if(screenshotFilename != "") SendScreenshotBinary(screenshotFilename, "bullish", uniqueId, symbol, symbolIndex); } else { if(EnableDebugPrints) { Print("DEBUG: Bullish alert conditions met but enhanced validations failed for ", symbol); Print("DEBUG: B is lowest: ", swingData[symbolIndex].bullishBIsLowest ? "✓" : "✗"); Print("DEBUG: A is highest: ", swingData[symbolIndex].bullishAIsHighest ? "✓" : "✗"); Print("DEBUG: Price action valid: ", allBarsValid ? "✓" : "✗"); } } } // BEARISH ALERT CONDITION (ORIGINAL - UNCHANGED) if(biasData[symbolIndex].bias == BIAS_BEARISH && swingData[symbolIndex].bearishPatternValid && // Check PDH validation swingData[symbolIndex].currentAskPrice < swingData[symbolIndex].bearishSwingLow && closeLastCandle > swingData[symbolIndex].bearishSwingLow && swingData[symbolIndex].bearishSwingLow != alertTrackers[symbolIndex].lastBearishSwingLow && alertTrackers[symbolIndex].bearishAlertSent == false && alertTrackers[symbolIndex].bearishAlertCount < 1) { datetime timeNow = TimeCurrent(); // Perform enhanced validations swingData[symbolIndex].bearishBIsHighest = ValidateBearishBIsHighest(symbolIndex, timeNow); swingData[symbolIndex].bearishAIsLowest = ValidateBearishAIsLowest(symbolIndex, timeNow); // Validate price action: No trading below bearishSwingLow before trigger int barsBetweenSwingLowAndTrigger = GetBarsBetweenTimes(swingData[symbolIndex].bearishSwingLowTime, timeNow, symbol); int barsBetweenFirstHighAndTrigger = GetBarsBetweenTimes(swingData[symbolIndex].Last_HH_Times[0], timeNow, symbol); bool allBarsValid = true; // Check all bars between swing low and trigger time for(int b = barsBetweenSwingLowAndTrigger - 1; b >= 1; b--) { if(iLow(symbol, PERIOD_M15, b) <= swingData[symbolIndex].bearishSwingLow) { allBarsValid = false; break; } } // Check all bars between first high and trigger time for(int b = barsBetweenFirstHighAndTrigger; b >= 0; b--) { if(iHigh(symbol, PERIOD_M15, b) > swingData[symbolIndex].Last_HH_Prices[1]) { allBarsValid = false; break; } } // If all validations pass, trigger bearish alert if(allBarsValid && swingData[symbolIndex].bearishBIsHighest && swingData[symbolIndex].bearishAIsLowest) { // Update alert state for THIS SYMBOL (ORIGINAL) alertTrackers[symbolIndex].bearishAlertSent = true; alertTrackers[symbolIndex].bearishAlertCount++; alertTrackers[symbolIndex].lastBearishSwingLow = swingData[symbolIndex].bearishSwingLow; alertTrackers[symbolIndex].bearishAlertTime = TimeCurrent(); // STORE C TIME - NEVER CHANGES // SAVE DATA FOR RETRACEMENT ALERT (NEW) SaveBearishRetracementData(symbolIndex); if(EnableDebugPrints) { Print("DEBUG: BEARISH BREAKOUT ALERT for ", symbol, " at ", TimeToString(timeNow)); Print("DEBUG: Pattern validation: PASSED (B >= PDH)"); Print("DEBUG: Enhanced validations: B is highest ✓, A is lowest ✓"); Print("DEBUG: Swing Low Price (A): ", swingData[symbolIndex].bearishSwingLow, " Time: ", TimeToString(swingData[symbolIndex].bearishSwingLowTime)); Print("DEBUG: B Point Price: ", swingData[symbolIndex].Last_HH_Prices[1]); Print("DEBUG: Market Ask Price: ", swingData[symbolIndex].currentAskPrice); Print("DEBUG: Alert #", alertTrackers[symbolIndex].bearishAlertCount, " for today"); Print("DEBUG: C time FIXED at: ", TimeToString(alertTrackers[symbolIndex].bearishAlertTime)); Print("DEBUG: Now monitoring for retracement to 40% level..."); } // Capture chart state using BREAKOUT annotations string screenshotFilename = TakeScreenshot("Bearish_Breakout", symbol, symbolIndex); // Generate unique identifier string uniqueId = GenerateUniqueId(); // Send alert data (ORIGINAL - UNCHANGED) SendAlertData("bearish", swingData[symbolIndex].bearishSwingLow, swingData[symbolIndex].bearishSwingLowTime, alertTrackers[symbolIndex].bearishAlertTime, screenshotFilename, uniqueId, symbol, symbolIndex); // Send screenshot if(screenshotFilename != "") SendScreenshotBinary(screenshotFilename, "bearish", uniqueId, symbol, symbolIndex); } else { if(EnableDebugPrints) { Print("DEBUG: Bearish alert conditions met but enhanced validations failed for ", symbol); Print("DEBUG: B is highest: ", swingData[symbolIndex].bearishBIsHighest ? "✓" : "✗"); Print("DEBUG: A is lowest: ", swingData[symbolIndex].bearishAIsLowest ? "✓" : "✗"); Print("DEBUG: Price action valid: ", allBarsValid ? "✓" : "✗"); } } } } // ============================================================ // NEW RETRACEMENT DATA SAVING FUNCTIONS // ============================================================ //+------------------------------------------------------------------+ //| Save bullish data for retracement alert | //+------------------------------------------------------------------+ void SaveBullishRetracementData(int symbolIndex) { retraceTrackers[symbolIndex].bullishBreakoutOccurred = true; retraceTrackers[symbolIndex].bullishBreakoutTime = alertTrackers[symbolIndex].bullishAlertTime; retraceTrackers[symbolIndex].bullishBreakoutPrice = swingData[symbolIndex].bullishSwingHigh; retraceTrackers[symbolIndex].bullishBPrice = swingData[symbolIndex].Last_LL_Prices[1]; retraceTrackers[symbolIndex].bullishSwingHigh = swingData[symbolIndex].bullishSwingHigh; retraceTrackers[symbolIndex].bullishSwingHighTime = swingData[symbolIndex].bullishSwingHighTime; retraceTrackers[symbolIndex].bullishTradeState = TRADE_PENDING; // Calculate 40% level double moveDistance = swingData[symbolIndex].bullishSwingHigh - swingData[symbolIndex].Last_LL_Prices[1]; retraceTrackers[symbolIndex].bullishFortyPercentLevel = swingData[symbolIndex].Last_LL_Prices[1] + (moveDistance * 0.40); // Save swing point data retraceTrackers[symbolIndex].bullishLast_LL_Prices[0] = swingData[symbolIndex].Last_LL_Prices[0]; retraceTrackers[symbolIndex].bullishLast_LL_Prices[1] = swingData[symbolIndex].Last_LL_Prices[1]; retraceTrackers[symbolIndex].bullishLast_LL_Times[0] = swingData[symbolIndex].Last_LL_Times[0]; retraceTrackers[symbolIndex].bullishLast_LL_Times[1] = swingData[symbolIndex].Last_LL_Times[1]; retraceTrackers[symbolIndex].bullishLast_HH_Prices[0] = swingData[symbolIndex].Last_HH_Prices[0]; retraceTrackers[symbolIndex].bullishLast_HH_Prices[1] = swingData[symbolIndex].Last_HH_Prices[1]; retraceTrackers[symbolIndex].bullishLast_HH_Times[0] = swingData[symbolIndex].Last_HH_Times[0]; retraceTrackers[symbolIndex].bullishLast_HH_Times[1] = swingData[symbolIndex].Last_HH_Times[1]; // Save previous day data retraceTrackers[symbolIndex].bullishPrevDayLow = prevDayData[symbolIndex].lowPrice; retraceTrackers[symbolIndex].bullishPrevDayLowTime = prevDayData[symbolIndex].lowTime; } //+------------------------------------------------------------------+ //| Save bearish data for retracement alert | //+------------------------------------------------------------------+ void SaveBearishRetracementData(int symbolIndex) { retraceTrackers[symbolIndex].bearishBreakoutOccurred = true; retraceTrackers[symbolIndex].bearishBreakoutTime = alertTrackers[symbolIndex].bearishAlertTime; retraceTrackers[symbolIndex].bearishBreakoutPrice = swingData[symbolIndex].bearishSwingLow; retraceTrackers[symbolIndex].bearishBPrice = swingData[symbolIndex].Last_HH_Prices[1]; retraceTrackers[symbolIndex].bearishSwingLow = swingData[symbolIndex].bearishSwingLow; retraceTrackers[symbolIndex].bearishSwingLowTime = swingData[symbolIndex].bearishSwingLowTime; retraceTrackers[symbolIndex].bearishTradeState = TRADE_PENDING; // Calculate 40% level double moveDistance = swingData[symbolIndex].Last_HH_Prices[1] - swingData[symbolIndex].bearishSwingLow; retraceTrackers[symbolIndex].bearishFortyPercentLevel = swingData[symbolIndex].Last_HH_Prices[1] - (moveDistance * 0.40); // Save swing point data retraceTrackers[symbolIndex].bearishLast_HH_Prices[0] = swingData[symbolIndex].Last_HH_Prices[0]; retraceTrackers[symbolIndex].bearishLast_HH_Prices[1] = swingData[symbolIndex].Last_HH_Prices[1]; retraceTrackers[symbolIndex].bearishLast_HH_Times[0] = swingData[symbolIndex].Last_HH_Times[0]; retraceTrackers[symbolIndex].bearishLast_HH_Times[1] = swingData[symbolIndex].Last_HH_Times[1]; retraceTrackers[symbolIndex].bearishLast_LL_Prices[0] = swingData[symbolIndex].Last_LL_Prices[0]; retraceTrackers[symbolIndex].bearishLast_LL_Prices[1] = swingData[symbolIndex].Last_LL_Prices[1]; retraceTrackers[symbolIndex].bearishLast_LL_Times[0] = swingData[symbolIndex].Last_LL_Times[0]; retraceTrackers[symbolIndex].bearishLast_LL_Times[1] = swingData[symbolIndex].Last_LL_Times[1]; // Save previous day data retraceTrackers[symbolIndex].bearishPrevDayHigh = prevDayData[symbolIndex].highPrice; retraceTrackers[symbolIndex].bearishPrevDayHighTime = prevDayData[symbolIndex].highTime; } // ============================================================ // NEW RETRACEMENT ALERT MONITORING WITH TRADE EXECUTION // ============================================================ //+------------------------------------------------------------------+ //| Check for retracement alerts (separate monitoring) | //+------------------------------------------------------------------+ void CheckRetracementAlerts(int symbolIndex) { string symbol = Symbols[symbolIndex]; // Check for bullish retracement if(retraceTrackers[symbolIndex].bullishBreakoutOccurred && retraceTrackers[symbolIndex].bullishTradeState == TRADE_PENDING) { // Monitor for retracement to 40% level if(swingData[symbolIndex].currentBidPrice <= retraceTrackers[symbolIndex].bullishFortyPercentLevel) { datetime timeNow = TimeCurrent(); if(EnableDebugPrints) { Print("DEBUG: BULLISH RETRACEMENT ALERT for ", symbol, " at ", TimeToString(timeNow)); Print("DEBUG: Price retraced to 40% level: ", retraceTrackers[symbolIndex].bullishFortyPercentLevel); Print("DEBUG: Original breakout at: ", TimeToString(retraceTrackers[symbolIndex].bullishBreakoutTime)); } // EXECUTE TRADE AT MARKET ON RETRACEMENT ExecuteBullishTrade(symbolIndex); // Capture chart state using RETRACEMENT annotations string screenshotFilename = TakeScreenshot("Bullish_Retracement", symbol, symbolIndex); // Generate unique identifier string uniqueId = GenerateUniqueId(); // Send alert data (same payload structure) SendAlertData("bullish_retracement", retraceTrackers[symbolIndex].bullishSwingHigh, retraceTrackers[symbolIndex].bullishSwingHighTime, timeNow, screenshotFilename, uniqueId, symbol, symbolIndex); // Send screenshot if(screenshotFilename != "") SendScreenshotBinary(screenshotFilename, "bullish_retracement", uniqueId, symbol, symbolIndex); // Don't reset flag - keep as executed to prevent re-triggering } } // Check for bearish retracement if(retraceTrackers[symbolIndex].bearishBreakoutOccurred && retraceTrackers[symbolIndex].bearishTradeState == TRADE_PENDING) { // Monitor for retracement to 40% level if(swingData[symbolIndex].currentAskPrice >= retraceTrackers[symbolIndex].bearishFortyPercentLevel) { datetime timeNow = TimeCurrent(); if(EnableDebugPrints) { Print("DEBUG: BEARISH RETRACEMENT ALERT for ", symbol, " at ", TimeToString(timeNow)); Print("DEBUG: Price retraced to 40% level: ", retraceTrackers[symbolIndex].bearishFortyPercentLevel); Print("DEBUG: Original breakout at: ", TimeToString(retraceTrackers[symbolIndex].bearishBreakoutTime)); } // EXECUTE TRADE AT MARKET ON RETRACEMENT ExecuteBearishTrade(symbolIndex); // Capture chart state using RETRACEMENT annotations string screenshotFilename = TakeScreenshot("Bearish_Retracement", symbol, symbolIndex); // Generate unique identifier string uniqueId = GenerateUniqueId(); // Send alert data (same payload structure) SendAlertData("bearish_retracement", retraceTrackers[symbolIndex].bearishSwingLow, retraceTrackers[symbolIndex].bearishSwingLowTime, timeNow, screenshotFilename, uniqueId, symbol, symbolIndex); // Send screenshot if(screenshotFilename != "") SendScreenshotBinary(screenshotFilename, "bearish_retracement", uniqueId, symbol, symbolIndex); // Don't reset flag - keep as executed to prevent re-triggering } } } // ============================================================ // FUNCTION: Validate patterns against previous day levels // ============================================================ //+------------------------------------------------------------------+ //| Validate bullish and bearish patterns against PDH/PDL | //+------------------------------------------------------------------+ void ValidatePatterns(int symbolIndex) { string symbol = Symbols[symbolIndex]; // Reset validity flags swingData[symbolIndex].bullishPatternValid = false; swingData[symbolIndex].bearishPatternValid = false; // Check if we have valid swing points and previous day data bool hasBullishSwings = (swingData[symbolIndex].Last_LL_Prices[0] > 0 && swingData[symbolIndex].Last_LL_Prices[1] > 0); bool hasBearishSwings = (swingData[symbolIndex].Last_HH_Prices[0] > 0 && swingData[symbolIndex].Last_HH_Prices[1] > 0); bool hasPrevDayData = (prevDayData[symbolIndex].lowPrice > 0 && prevDayData[symbolIndex].highPrice > 0); if(!hasPrevDayData) { if(EnableDebugPrints) { Print("DEBUG: Cannot validate patterns for ", symbol, " - Previous day data missing"); } return; } // Validate BULLISH pattern if(hasBullishSwings) { // Bullish pattern is VALID only if Last_LL_Prices[1] (B point) is LESS THAN or equal to previous day low if(swingData[symbolIndex].Last_LL_Prices[1] <= prevDayData[symbolIndex].lowPrice) { swingData[symbolIndex].bullishPatternValid = true; if(EnableDebugPrints) { Print("DEBUG: Bullish pattern VALID for ", symbol); Print("DEBUG: B point (LL2): ", swingData[symbolIndex].Last_LL_Prices[1]); Print("DEBUG: PDL: ", prevDayData[symbolIndex].lowPrice); Print("DEBUG: Condition: B <= PDL ✓"); } } else { if(EnableDebugPrints) { Print("DEBUG: Bullish pattern INVALID for ", symbol); Print("DEBUG: B point (LL2): ", swingData[symbolIndex].Last_LL_Prices[1]); Print("DEBUG: PDL: ", prevDayData[symbolIndex].lowPrice); Print("DEBUG: Condition failed: B > PDL"); } } } // Validate BEARISH pattern if(hasBearishSwings) { // Bearish pattern is VALID only if Last_HH_Prices[1] (B point) is GREATER THAN or equal to previous day high if(swingData[symbolIndex].Last_HH_Prices[1] >= prevDayData[symbolIndex].highPrice) { swingData[symbolIndex].bearishPatternValid = true; if(EnableDebugPrints) { Print("DEBUG: Bearish pattern VALID for ", symbol); Print("DEBUG: B point (HH2): ", swingData[symbolIndex].Last_HH_Prices[1]); Print("DEBUG: PDH: ", prevDayData[symbolIndex].highPrice); Print("DEBUG: Condition: B >= PDH ✓"); } } else { if(EnableDebugPrints) { Print("DEBUG: Bearish pattern INVALID for ", symbol); Print("DEBUG: B point (HH2): ", swingData[symbolIndex].Last_HH_Prices[1]); Print("DEBUG: PDH: ", prevDayData[symbolIndex].highPrice); Print("DEBUG: Condition failed: B < PDH"); } } } } // ============================================================ // ENHANCED VALIDATION FUNCTIONS // ============================================================ //+------------------------------------------------------------------+ //| Validate Bullish B is Lowest Low between SSL and C | //+------------------------------------------------------------------+ bool ValidateBullishBIsLowest(int symbolIndex, datetime triggerTime) { string symbol = Symbols[symbolIndex]; double bPrice = swingData[symbolIndex].Last_LL_Prices[1]; datetime sslTime = swingData[symbolIndex].Last_LL_Times[0]; // Get bar indices int sslBar = iBarShift(symbol, PERIOD_M15, sslTime, true); int triggerBar = iBarShift(symbol, PERIOD_M15, triggerTime, true); if(sslBar < 0 || triggerBar < 0 || sslBar <= triggerBar) { if(EnableDebugPrints) Print("DEBUG: Cannot validate bullish B - invalid bar indices"); return false; } // Check all bars between SSL and C (inclusive) for(int bar = sslBar; bar >= triggerBar; bar--) { double barLow = iLow(symbol, PERIOD_M15, bar); if(barLow < bPrice - 0.00001) // Allow small floating point tolerance { if(EnableDebugPrints) { Print("DEBUG: Bullish B validation FAILED - found lower low at bar ", bar); Print("DEBUG: B price: ", bPrice, " Lower low: ", barLow); } return false; } } if(EnableDebugPrints) { Print("DEBUG: Bullish B validation PASSED - B is the lowest low between SSL and C"); } return true; } //+------------------------------------------------------------------+ //| Validate Bullish A is Highest High between A and C+1 | //+------------------------------------------------------------------+ bool ValidateBullishAIsHighest(int symbolIndex, datetime triggerTime) { string symbol = Symbols[symbolIndex]; double aPrice = swingData[symbolIndex].bullishSwingHigh; datetime aTime = swingData[symbolIndex].bullishSwingHighTime; // C+1 is the candle before trigger (higher bar index) int triggerBar = iBarShift(symbol, PERIOD_M15, triggerTime, true); int aBar = iBarShift(symbol, PERIOD_M15, aTime, true); int cPlus1Bar = triggerBar + 1; // C+1 is one bar earlier if(aBar < 0 || triggerBar < 0 || aBar <= cPlus1Bar) { if(EnableDebugPrints) Print("DEBUG: Cannot validate bullish A - invalid bar indices"); return false; } // Check all bars between A and C+1 (inclusive) for(int bar = aBar; bar >= cPlus1Bar; bar--) { double barHigh = iHigh(symbol, PERIOD_M15, bar); if(barHigh > aPrice + 0.00001) // Allow small floating point tolerance { if(EnableDebugPrints) { Print("DEBUG: Bullish A validation FAILED - found higher high at bar ", bar); Print("DEBUG: A price: ", aPrice, " Higher high: ", barHigh); } return false; } } if(EnableDebugPrints) { Print("DEBUG: Bullish A validation PASSED - A is the highest high between A and C+1"); } return true; } //+------------------------------------------------------------------+ //| Validate Bearish B is Highest High between BSL and C | //+------------------------------------------------------------------+ bool ValidateBearishBIsHighest(int symbolIndex, datetime triggerTime) { string symbol = Symbols[symbolIndex]; double bPrice = swingData[symbolIndex].Last_HH_Prices[1]; datetime bslTime = swingData[symbolIndex].Last_HH_Times[0]; // Get bar indices int bslBar = iBarShift(symbol, PERIOD_M15, bslTime, true); int triggerBar = iBarShift(symbol, PERIOD_M15, triggerTime, true); if(bslBar < 0 || triggerBar < 0 || bslBar <= triggerBar) { if(EnableDebugPrints) Print("DEBUG: Cannot validate bearish B - invalid bar indices"); return false; } // Check all bars between BSL and C (inclusive) for(int bar = bslBar; bar >= triggerBar; bar--) { double barHigh = iHigh(symbol, PERIOD_M15, bar); if(barHigh > bPrice + 0.00001) // Allow small floating point tolerance { if(EnableDebugPrints) { Print("DEBUG: Bearish B validation FAILED - found higher high at bar ", bar); Print("DEBUG: B price: ", bPrice, " Higher high: ", barHigh); } return false; } } if(EnableDebugPrints) { Print("DEBUG: Bearish B validation PASSED - B is the highest high between BSL and C"); } return true; } //+------------------------------------------------------------------+ //| Validate Bearish A is Lowest Low between A and C+1 | //+------------------------------------------------------------------+ bool ValidateBearishAIsLowest(int symbolIndex, datetime triggerTime) { string symbol = Symbols[symbolIndex]; double aPrice = swingData[symbolIndex].bearishSwingLow; datetime aTime = swingData[symbolIndex].bearishSwingLowTime; // C+1 is the candle before trigger (higher bar index) int triggerBar = iBarShift(symbol, PERIOD_M15, triggerTime, true); int aBar = iBarShift(symbol, PERIOD_M15, aTime, true); int cPlus1Bar = triggerBar + 1; // C+1 is one bar earlier if(aBar < 0 || triggerBar < 0 || aBar <= cPlus1Bar) { if(EnableDebugPrints) Print("DEBUG: Cannot validate bearish A - invalid bar indices"); return false; } // Check all bars between A and C+1 (inclusive) for(int bar = aBar; bar >= cPlus1Bar; bar--) { double barLow = iLow(symbol, PERIOD_M15, bar); if(barLow < aPrice - 0.00001) // Allow small floating point tolerance { if(EnableDebugPrints) { Print("DEBUG: Bearish A validation FAILED - found lower low at bar ", bar); Print("DEBUG: A price: ", aPrice, " Lower low: ", barLow); } return false; } } if(EnableDebugPrints) { Print("DEBUG: Bearish A validation PASSED - A is the lowest low between A and C+1"); } return true; } // ============================================================ // ALERT MANAGEMENT FUNCTIONS // ============================================================ //+------------------------------------------------------------------+ //| Generate a unique identifier | //+------------------------------------------------------------------+ string GenerateUniqueId() { int randomNum = (int)MathRand(); string uniqueId = IntegerToString(TimeCurrent()) + "_" + IntegerToString(randomNum) + "_" + IntegerToString(GetTickCount()); return uniqueId; } // ============================================================ // CHART OBJECT MANAGEMENT FUNCTIONS // ============================================================ //+------------------------------------------------------------------+ //| Delete all chart objects | //+------------------------------------------------------------------+ void DeleteAllObjects() { int totalObjects = ObjectsTotal(0, 0, -1); bool objectsDeleted = false; for(int i = totalObjects - 1; i >= 0; i--) { string objName = ObjectName(0, i, 0, -1); if(StringFind(objName, "BullishTrend_") == 0 || StringFind(objName, "BearishTrend_") == 0 || StringFind(objName, "BSL_") == 0 || StringFind(objName, "SSL_") == 0 || StringFind(objName, "PDL_") == 0 || StringFind(objName, "PDH_") == 0 || StringFind(objName, "PDH") == 0 || StringFind(objName, "PDL") == 0 || StringFind(objName, "bearishSwingLow") == 0 || StringFind(objName, "bullishSwingHigh") == 0 || StringFind(objName, "H2") == 0 || StringFind(objName, "L2") == 0 || StringFind(objName, "triggerPoint") == 0) { ObjectDelete(0, objName); objectsDeleted = true; } } if(EnableDebugPrints && objectsDeleted) { Print("DEBUG: Deleted all chart objects. Total objects removed: ", totalObjects); } } //+------------------------------------------------------------------+ //| Draw bullish BREAKOUT annotations (identical to original) | //+------------------------------------------------------------------+ void DrawBullishBreakoutAnnotations(long chart_id, string symbol, int symbolIndex) { if(EnableDebugPrints) Print("DEBUG: Drawing bullish BREAKOUT annotations on chart ID: ", chart_id, " for ", symbol); // USE STORED ALERT TIME - THIS NEVER CHANGES! datetime timeNow = alertTrackers[symbolIndex].bullishAlertTime; if(timeNow == 0) { Print("ERROR: Bullish alert time not set for ", symbol); return; } // 1. Draw Previous Day Low (PDL) Trendline string PDL_Linename = "PDL_Bullish_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime); ObjectCreate(chart_id, PDL_Linename, OBJ_TREND, 0, prevDayData[symbolIndex].lowTime, prevDayData[symbolIndex].lowPrice, swingData[symbolIndex].Last_LL_Times[0], prevDayData[symbolIndex].lowPrice); ObjectSetInteger(chart_id, PDL_Linename, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, PDL_Linename, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(chart_id, PDL_Linename, OBJPROP_WIDTH, 3); ObjectSetInteger(chart_id, PDL_Linename, OBJPROP_RAY_RIGHT, false); ObjectSetString(chart_id, PDL_Linename, OBJPROP_TEXT, "PDL Trend"); // 2. Draw SSL (Swing Structure Low) Trendline string SSL_Linename = "SSL_Bullish_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime); ObjectCreate(chart_id, SSL_Linename, OBJ_TREND, 0, swingData[symbolIndex].Last_LL_Times[0], swingData[symbolIndex].Last_LL_Prices[0], swingData[symbolIndex].Last_LL_Times[1], swingData[symbolIndex].Last_LL_Prices[0]); ObjectSetInteger(chart_id, SSL_Linename, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, SSL_Linename, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(chart_id, SSL_Linename, OBJPROP_WIDTH, 1); ObjectSetInteger(chart_id, SSL_Linename, OBJPROP_RAY_RIGHT, false); ObjectSetString(chart_id, SSL_Linename, OBJPROP_TEXT, "SSL Trend"); // 3. Draw Bullish Swing High Trendline string bullishSwingHigh_Linename = "BullishTrend_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime); ObjectCreate(chart_id, bullishSwingHigh_Linename, OBJ_TREND, 0, swingData[symbolIndex].bullishSwingHighTime, swingData[symbolIndex].bullishSwingHigh, alertTrackers[symbolIndex].bullishAlertTime, swingData[symbolIndex].bullishSwingHigh); ObjectSetInteger(chart_id, bullishSwingHigh_Linename, OBJPROP_COLOR, clrGreen); ObjectSetInteger(chart_id, bullishSwingHigh_Linename, OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(chart_id, bullishSwingHigh_Linename, OBJPROP_WIDTH, 2); ObjectSetInteger(chart_id, bullishSwingHigh_Linename, OBJPROP_RAY_RIGHT, false); ObjectSetString(chart_id, bullishSwingHigh_Linename, OBJPROP_TEXT, "Bullish Swing High"); // 4. Text Labels string pdlTextName = "PDL_Text_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime); ObjectCreate(chart_id, pdlTextName, OBJ_TEXT, 0, prevDayData[symbolIndex].lowTime, prevDayData[symbolIndex].lowPrice); ObjectSetString(chart_id, pdlTextName, OBJPROP_TEXT, "PDL"); ObjectSetInteger(chart_id, pdlTextName, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, pdlTextName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chart_id, pdlTextName, OBJPROP_ANCHOR, ANCHOR_UPPER); string swingHighTextName = "SwingHigh_Text_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime); ObjectCreate(chart_id, swingHighTextName, OBJ_TEXT, 0, swingData[symbolIndex].bullishSwingHighTime, swingData[symbolIndex].bullishSwingHigh); ObjectSetString(chart_id, swingHighTextName, OBJPROP_TEXT, "A"); ObjectSetInteger(chart_id, swingHighTextName, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, swingHighTextName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chart_id, swingHighTextName, OBJPROP_ANCHOR, ANCHOR_LOWER); string l2TextName = "L2_Text_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime); ObjectCreate(chart_id, l2TextName, OBJ_TEXT, 0, swingData[symbolIndex].Last_LL_Times[1], swingData[symbolIndex].Last_LL_Prices[1]); ObjectSetString(chart_id, l2TextName, OBJPROP_TEXT, "B"); ObjectSetInteger(chart_id, l2TextName, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, l2TextName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chart_id, l2TextName, OBJPROP_ANCHOR, ANCHOR_UPPER); string triggerTextName = "Trigger_Text_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime); ObjectCreate(chart_id, triggerTextName, OBJ_TEXT, 0, timeNow, swingData[symbolIndex].bullishSwingHigh); ObjectSetString(chart_id, triggerTextName, OBJPROP_TEXT, "C"); ObjectSetInteger(chart_id, triggerTextName, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, triggerTextName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chart_id, triggerTextName, OBJPROP_ANCHOR, ANCHOR_LOWER); //=================================================================================== // MODIFIED RECTANGLE - Bullish (40% from B to C, time fixed) //=================================================================================== // Calculate the move from B price to C price double bPrice = swingData[symbolIndex].Last_LL_Prices[1]; double cPrice = swingData[symbolIndex].bullishSwingHigh; // C is the swing high (breakout level) // For bullish: B is lower (support), C is higher (resistance) double moveDistance = cPrice - bPrice; // Calculate 40% level (measured FROM B upward) double rectangleTopPrice = bPrice + (moveDistance * 0.40); if(EnableDebugPrints) { Print("DEBUG: Bullish Rectangle Calculation:"); Print("DEBUG: B Price: ", bPrice); Print("DEBUG: C Price: ", cPrice); Print("DEBUG: Move Distance: ", moveDistance); Print("DEBUG: 40% Level: ", rectangleTopPrice, " (40% from B)"); Print("DEBUG: Rectangle from B (", swingData[symbolIndex].Last_LL_Prices[1], ") to ", rectangleTopPrice); Print("DEBUG: C Time FIXED at: ", TimeToString(timeNow)); } // Rectangle from B (bottom) to 40% level (top) // Time coordinates are FIXED: from C time to C time + 5 periods (never changes) string rectangleName = "Bullish_Rect_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime); ObjectCreate(chart_id, rectangleName, OBJ_RECTANGLE, 0, timeNow, // Left time (C time - FIXED) swingData[symbolIndex].Last_LL_Prices[1], // Bottom price (B) timeNow + (5 * GetTimeframeSeconds()), // Right time (C time + 5 periods - FIXED) rectangleTopPrice); // Top price (40% level) ObjectSetInteger(chart_id, rectangleName, OBJPROP_COLOR, clrPaleGreen); ObjectSetInteger(chart_id, rectangleName, OBJPROP_BGCOLOR, clrPaleGreen); ObjectSetInteger(chart_id, rectangleName, OBJPROP_WIDTH, 1); ObjectSetInteger(chart_id, rectangleName, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(chart_id, rectangleName, OBJPROP_BACK, false); ObjectSetInteger(chart_id, rectangleName, OBJPROP_FILL, true); ObjectSetDouble(chart_id, rectangleName, OBJPROP_LEVELVALUE, 0.3); //Arrowed line - using original coordinates with FIXED time string arrowLineName = "Bullish_Arrow_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime); ObjectCreate(chart_id, arrowLineName, OBJ_ARROWED_LINE, 0, timeNow, bPrice, timeNow + (5 * GetTimeframeSeconds()), cPrice); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_COLOR, clrBlue); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_WIDTH, 1); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_RAY_RIGHT, false); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_STYLE, STYLE_DOT); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_BACK, false); if(EnableDebugPrints) Print("DEBUG: Completed drawing bullish BREAKOUT annotations with FIXED C time: ", TimeToString(timeNow)); } //+------------------------------------------------------------------+ //| Draw bullish RETRACEMENT annotations (IDENTICAL code, different name) | //+------------------------------------------------------------------+ void DrawBullishRetracementAnnotations(long chart_id, string symbol, int symbolIndex) { if(EnableDebugPrints) Print("DEBUG: Drawing bullish RETRACEMENT annotations on chart ID: ", chart_id, " for ", symbol); // USE STORED RETRACEMENT DATA datetime timeNow = retraceTrackers[symbolIndex].bullishBreakoutTime; // Use breakout time for consistency if(timeNow == 0) { Print("ERROR: Bullish retracement data not available for ", symbol); return; } // 1. Draw Previous Day Low (PDL) Trendline string PDL_Linename = "PDL_Bullish_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, PDL_Linename, OBJ_TREND, 0, retraceTrackers[symbolIndex].bullishPrevDayLowTime, retraceTrackers[symbolIndex].bullishPrevDayLow, retraceTrackers[symbolIndex].bullishLast_LL_Times[0], retraceTrackers[symbolIndex].bullishPrevDayLow); ObjectSetInteger(chart_id, PDL_Linename, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, PDL_Linename, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(chart_id, PDL_Linename, OBJPROP_WIDTH, 3); ObjectSetInteger(chart_id, PDL_Linename, OBJPROP_RAY_RIGHT, false); ObjectSetString(chart_id, PDL_Linename, OBJPROP_TEXT, "PDL Trend"); // 2. Draw SSL (Swing Structure Low) Trendline string SSL_Linename = "SSL_Bullish_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, SSL_Linename, OBJ_TREND, 0, retraceTrackers[symbolIndex].bullishLast_LL_Times[0], retraceTrackers[symbolIndex].bullishLast_LL_Prices[0], retraceTrackers[symbolIndex].bullishLast_LL_Times[1], retraceTrackers[symbolIndex].bullishLast_LL_Prices[0]); ObjectSetInteger(chart_id, SSL_Linename, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, SSL_Linename, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(chart_id, SSL_Linename, OBJPROP_WIDTH, 1); ObjectSetInteger(chart_id, SSL_Linename, OBJPROP_RAY_RIGHT, false); ObjectSetString(chart_id, SSL_Linename, OBJPROP_TEXT, "SSL Trend"); // 3. Draw Bullish Swing High Trendline string bullishSwingHigh_Linename = "BullishTrend_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, bullishSwingHigh_Linename, OBJ_TREND, 0, retraceTrackers[symbolIndex].bullishSwingHighTime, retraceTrackers[symbolIndex].bullishSwingHigh, timeNow, retraceTrackers[symbolIndex].bullishSwingHigh); ObjectSetInteger(chart_id, bullishSwingHigh_Linename, OBJPROP_COLOR, clrGreen); ObjectSetInteger(chart_id, bullishSwingHigh_Linename, OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(chart_id, bullishSwingHigh_Linename, OBJPROP_WIDTH, 2); ObjectSetInteger(chart_id, bullishSwingHigh_Linename, OBJPROP_RAY_RIGHT, false); ObjectSetString(chart_id, bullishSwingHigh_Linename, OBJPROP_TEXT, "Bullish Swing High"); // 4. Text Labels string pdlTextName = "PDL_Text_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, pdlTextName, OBJ_TEXT, 0, retraceTrackers[symbolIndex].bullishPrevDayLowTime, retraceTrackers[symbolIndex].bullishPrevDayLow); ObjectSetString(chart_id, pdlTextName, OBJPROP_TEXT, "PDL"); ObjectSetInteger(chart_id, pdlTextName, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, pdlTextName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chart_id, pdlTextName, OBJPROP_ANCHOR, ANCHOR_UPPER); string swingHighTextName = "SwingHigh_Text_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, swingHighTextName, OBJ_TEXT, 0, retraceTrackers[symbolIndex].bullishSwingHighTime, retraceTrackers[symbolIndex].bullishSwingHigh); ObjectSetString(chart_id, swingHighTextName, OBJPROP_TEXT, "A"); ObjectSetInteger(chart_id, swingHighTextName, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, swingHighTextName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chart_id, swingHighTextName, OBJPROP_ANCHOR, ANCHOR_LOWER); string l2TextName = "L2_Text_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, l2TextName, OBJ_TEXT, 0, retraceTrackers[symbolIndex].bullishLast_LL_Times[1], retraceTrackers[symbolIndex].bullishLast_LL_Prices[1]); ObjectSetString(chart_id, l2TextName, OBJPROP_TEXT, "B"); ObjectSetInteger(chart_id, l2TextName, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, l2TextName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chart_id, l2TextName, OBJPROP_ANCHOR, ANCHOR_UPPER); string triggerTextName = "Trigger_Text_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, triggerTextName, OBJ_TEXT, 0, timeNow, retraceTrackers[symbolIndex].bullishSwingHigh); ObjectSetString(chart_id, triggerTextName, OBJPROP_TEXT, "C"); ObjectSetInteger(chart_id, triggerTextName, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, triggerTextName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chart_id, triggerTextName, OBJPROP_ANCHOR, ANCHOR_LOWER); //=================================================================================== // MODIFIED RECTANGLE - Bullish (40% from B to C, time fixed) //=================================================================================== // Calculate the move from B price to C price double bPrice = retraceTrackers[symbolIndex].bullishLast_LL_Prices[1]; double cPrice = retraceTrackers[symbolIndex].bullishSwingHigh; double moveDistance = cPrice - bPrice; double rectangleTopPrice = bPrice + (moveDistance * 0.40); if(EnableDebugPrints) { Print("DEBUG: Bullish Retracement Rectangle Calculation:"); Print("DEBUG: B Price: ", bPrice); Print("DEBUG: C Price: ", cPrice); Print("DEBUG: Move Distance: ", moveDistance); Print("DEBUG: 40% Level: ", rectangleTopPrice, " (40% from B)"); Print("DEBUG: Rectangle from B (", bPrice, ") to ", rectangleTopPrice); Print("DEBUG: C Time FIXED at: ", TimeToString(timeNow)); } // Rectangle from B (bottom) to 40% level (top) string rectangleName = "Bullish_Rect_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, rectangleName, OBJ_RECTANGLE, 0, timeNow, // Left time (C time - FIXED) bPrice, // Bottom price (B) timeNow + (10 * GetTimeframeSeconds()), // Right time (C time + 10 periods - FIXED) rectangleTopPrice); // Top price (40% level) ObjectSetInteger(chart_id, rectangleName, OBJPROP_COLOR, clrPaleGreen); ObjectSetInteger(chart_id, rectangleName, OBJPROP_BGCOLOR, clrPaleGreen); ObjectSetInteger(chart_id, rectangleName, OBJPROP_WIDTH, 1); ObjectSetInteger(chart_id, rectangleName, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(chart_id, rectangleName, OBJPROP_BACK, false); ObjectSetInteger(chart_id, rectangleName, OBJPROP_FILL, true); ObjectSetDouble(chart_id, rectangleName, OBJPROP_LEVELVALUE, 0.3); //Arrowed line string arrowLineName = "Bullish_Arrow_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, arrowLineName, OBJ_ARROWED_LINE, 0, timeNow, bPrice, timeNow + (10 * GetTimeframeSeconds()), cPrice); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_COLOR, clrBlue); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_WIDTH, 1); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_RAY_RIGHT, false); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_STYLE, STYLE_DOT); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_BACK, false); if(EnableDebugPrints) Print("DEBUG: Completed drawing bullish RETRACEMENT annotations with FIXED C time: ", TimeToString(timeNow)); } //+------------------------------------------------------------------+ //| Draw bearish BREAKOUT annotations (identical to original) | //+------------------------------------------------------------------+ void DrawBearishBreakoutAnnotations(long chart_id, string symbol, int symbolIndex) { if(EnableDebugPrints) Print("DEBUG: Drawing bearish BREAKOUT annotations on chart ID: ", chart_id, " for ", symbol); // USE STORED ALERT TIME - THIS NEVER CHANGES! datetime timeNow = alertTrackers[symbolIndex].bearishAlertTime; if(timeNow == 0) { Print("ERROR: Bearish alert time not set for ", symbol); return; } // 1. Draw Previous Day High (PDH) Trendline string PDH_Linename = "PDH_Bearish_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime); ObjectCreate(chart_id, PDH_Linename, OBJ_TREND, 0, prevDayData[symbolIndex].highTime, prevDayData[symbolIndex].highPrice, swingData[symbolIndex].Last_HH_Times[0], prevDayData[symbolIndex].highPrice); ObjectSetInteger(chart_id, PDH_Linename, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, PDH_Linename, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(chart_id, PDH_Linename, OBJPROP_WIDTH, 3); ObjectSetInteger(chart_id, PDH_Linename, OBJPROP_RAY_RIGHT, false); ObjectSetString(chart_id, PDH_Linename, OBJPROP_TEXT, "PDH Trend"); // 2. Draw BSL (Bearish Structure Level) Trendline string BSL_Linename = "BSL_Bearish_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime); ObjectCreate(chart_id, BSL_Linename, OBJ_TREND, 0, swingData[symbolIndex].Last_HH_Times[0], swingData[symbolIndex].Last_HH_Prices[0], swingData[symbolIndex].Last_HH_Times[1], swingData[symbolIndex].Last_HH_Prices[0]); ObjectSetInteger(chart_id, BSL_Linename, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, BSL_Linename, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(chart_id, BSL_Linename, OBJPROP_WIDTH, 1); ObjectSetInteger(chart_id, BSL_Linename, OBJPROP_RAY_RIGHT, false); ObjectSetString(chart_id, BSL_Linename, OBJPROP_TEXT, "BSL Trend"); // 3. Draw Bearish Swing Low Trendline string bearishSwingLow_Linename = "BearishTrend_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime); ObjectCreate(chart_id, bearishSwingLow_Linename, OBJ_TREND, 0, swingData[symbolIndex].bearishSwingLowTime, swingData[symbolIndex].bearishSwingLow, alertTrackers[symbolIndex].bearishAlertTime, swingData[symbolIndex].bearishSwingLow); ObjectSetInteger(chart_id, bearishSwingLow_Linename, OBJPROP_COLOR, clrRed); ObjectSetInteger(chart_id, bearishSwingLow_Linename, OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(chart_id, bearishSwingLow_Linename, OBJPROP_WIDTH, 2); ObjectSetInteger(chart_id, bearishSwingLow_Linename, OBJPROP_RAY_RIGHT, false); ObjectSetString(chart_id, bearishSwingLow_Linename, OBJPROP_TEXT, "Bearish Swing Low"); // 4. Text Labels string pdhTextName = "PDH_Text_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime); ObjectCreate(chart_id, pdhTextName, OBJ_TEXT, 0, prevDayData[symbolIndex].highTime, prevDayData[symbolIndex].highPrice); ObjectSetString(chart_id, pdhTextName, OBJPROP_TEXT, "PDH"); ObjectSetInteger(chart_id, pdhTextName, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, pdhTextName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chart_id, pdhTextName, OBJPROP_ANCHOR, ANCHOR_LOWER); string swingLowTextName = "SwingLow_Text_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime); ObjectCreate(chart_id, swingLowTextName, OBJ_TEXT, 0, swingData[symbolIndex].bearishSwingLowTime, swingData[symbolIndex].bearishSwingLow); ObjectSetString(chart_id, swingLowTextName, OBJPROP_TEXT, "A"); ObjectSetInteger(chart_id, swingLowTextName, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, swingLowTextName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chart_id, swingLowTextName, OBJPROP_ANCHOR, ANCHOR_UPPER); string h2TextName = "H2_Text_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime); ObjectCreate(chart_id, h2TextName, OBJ_TEXT, 0, swingData[symbolIndex].Last_HH_Times[1], swingData[symbolIndex].Last_HH_Prices[1]); ObjectSetString(chart_id, h2TextName, OBJPROP_TEXT, "B"); ObjectSetInteger(chart_id, h2TextName, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, h2TextName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chart_id, h2TextName, OBJPROP_ANCHOR, ANCHOR_LOWER); string triggerTextName = "Trigger_Text_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime); ObjectCreate(chart_id, triggerTextName, OBJ_TEXT, 0, timeNow, swingData[symbolIndex].bearishSwingLow); ObjectSetString(chart_id, triggerTextName, OBJPROP_TEXT, "C"); ObjectSetInteger(chart_id, triggerTextName, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, triggerTextName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chart_id, triggerTextName, OBJPROP_ANCHOR, ANCHOR_UPPER); //=================================================================================== // MODIFIED RECTANGLE - Bearish (40% from B to C, time fixed) //=================================================================================== // Calculate the move from B price to C price double bPrice = swingData[symbolIndex].Last_HH_Prices[1]; double cPrice = swingData[symbolIndex].bearishSwingLow; // C is the swing low (breakout level) // For bearish: B is higher (resistance), C is lower (support) double moveDistance = bPrice - cPrice; // Calculate 40% level (measured FROM B downward) double rectangleBottomPrice = bPrice - (moveDistance * 0.40); if(EnableDebugPrints) { Print("DEBUG: Bearish Rectangle Calculation:"); Print("DEBUG: B Price: ", bPrice); Print("DEBUG: C Price: ", cPrice); Print("DEBUG: Move Distance: ", moveDistance); Print("DEBUG: 40% Level: ", rectangleBottomPrice, " (40% from B)"); Print("DEBUG: Rectangle from ", rectangleBottomPrice, " to B (", swingData[symbolIndex].Last_HH_Prices[1], ")"); Print("DEBUG: C Time FIXED at: ", TimeToString(timeNow)); } // Rectangle from 40% level (bottom) to B (top) string rectangleName = "Bearish_Rect_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime); ObjectCreate(chart_id, rectangleName, OBJ_RECTANGLE, 0, timeNow, // Left time (C time - FIXED) rectangleBottomPrice, // Bottom price (40% level) timeNow + (5 * GetTimeframeSeconds()), // Right time (C time + 5 periods - FIXED) swingData[symbolIndex].Last_HH_Prices[1]); // Top price (B) ObjectSetInteger(chart_id, rectangleName, OBJPROP_COLOR, clrLightPink); ObjectSetInteger(chart_id, rectangleName, OBJPROP_BGCOLOR, clrLightPink); ObjectSetInteger(chart_id, rectangleName, OBJPROP_WIDTH, 1); ObjectSetInteger(chart_id, rectangleName, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(chart_id, rectangleName, OBJPROP_BACK, false); ObjectSetInteger(chart_id, rectangleName, OBJPROP_FILL, true); ObjectSetDouble(chart_id, rectangleName, OBJPROP_LEVELVALUE, 0.3); //Arrowed line string arrowLineName = "Bearish_Arrow_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime); ObjectCreate(chart_id, arrowLineName, OBJ_ARROWED_LINE, 0, timeNow, bPrice, timeNow + (5 * GetTimeframeSeconds()), cPrice); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_COLOR, clrBlue); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_WIDTH, 1); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_RAY_RIGHT, false); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_STYLE, STYLE_DOT); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_BACK, false); if(EnableDebugPrints) Print("DEBUG: Completed drawing bearish BREAKOUT annotations with FIXED C time: ", TimeToString(timeNow)); } //+------------------------------------------------------------------+ //| Draw bearish RETRACEMENT annotations (IDENTICAL code, different name) | //+------------------------------------------------------------------+ void DrawBearishRetracementAnnotations(long chart_id, string symbol, int symbolIndex) { if(EnableDebugPrints) Print("DEBUG: Drawing bearish RETRACEMENT annotations on chart ID: ", chart_id, " for ", symbol); // USE STORED RETRACEMENT DATA datetime timeNow = retraceTrackers[symbolIndex].bearishBreakoutTime; // Use breakout time for consistency if(timeNow == 0) { Print("ERROR: Bearish retracement data not available for ", symbol); return; } // 1. Draw Previous Day High (PDH) Trendline string PDH_Linename = "PDH_Bearish_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, PDH_Linename, OBJ_TREND, 0, retraceTrackers[symbolIndex].bearishPrevDayHighTime, retraceTrackers[symbolIndex].bearishPrevDayHigh, retraceTrackers[symbolIndex].bearishLast_HH_Times[0], retraceTrackers[symbolIndex].bearishPrevDayHigh); ObjectSetInteger(chart_id, PDH_Linename, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, PDH_Linename, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(chart_id, PDH_Linename, OBJPROP_WIDTH, 3); ObjectSetInteger(chart_id, PDH_Linename, OBJPROP_RAY_RIGHT, false); ObjectSetString(chart_id, PDH_Linename, OBJPROP_TEXT, "PDH Trend"); // 2. Draw BSL (Bearish Structure Level) Trendline string BSL_Linename = "BSL_Bearish_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, BSL_Linename, OBJ_TREND, 0, retraceTrackers[symbolIndex].bearishLast_HH_Times[0], retraceTrackers[symbolIndex].bearishLast_HH_Prices[0], retraceTrackers[symbolIndex].bearishLast_HH_Times[1], retraceTrackers[symbolIndex].bearishLast_HH_Prices[0]); ObjectSetInteger(chart_id, BSL_Linename, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, BSL_Linename, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(chart_id, BSL_Linename, OBJPROP_WIDTH, 1); ObjectSetInteger(chart_id, BSL_Linename, OBJPROP_RAY_RIGHT, false); ObjectSetString(chart_id, BSL_Linename, OBJPROP_TEXT, "BSL Trend"); // 3. Draw Bearish Swing Low Trendline string bearishSwingLow_Linename = "BearishTrend_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, bearishSwingLow_Linename, OBJ_TREND, 0, retraceTrackers[symbolIndex].bearishSwingLowTime, retraceTrackers[symbolIndex].bearishSwingLow, timeNow, retraceTrackers[symbolIndex].bearishSwingLow); ObjectSetInteger(chart_id, bearishSwingLow_Linename, OBJPROP_COLOR, clrRed); ObjectSetInteger(chart_id, bearishSwingLow_Linename, OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(chart_id, bearishSwingLow_Linename, OBJPROP_WIDTH, 2); ObjectSetInteger(chart_id, bearishSwingLow_Linename, OBJPROP_RAY_RIGHT, false); ObjectSetString(chart_id, bearishSwingLow_Linename, OBJPROP_TEXT, "Bearish Swing Low"); // 4. Text Labels string pdhTextName = "PDH_Text_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, pdhTextName, OBJ_TEXT, 0, retraceTrackers[symbolIndex].bearishPrevDayHighTime, retraceTrackers[symbolIndex].bearishPrevDayHigh); ObjectSetString(chart_id, pdhTextName, OBJPROP_TEXT, "PDH"); ObjectSetInteger(chart_id, pdhTextName, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, pdhTextName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chart_id, pdhTextName, OBJPROP_ANCHOR, ANCHOR_LOWER); string swingLowTextName = "SwingLow_Text_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, swingLowTextName, OBJ_TEXT, 0, retraceTrackers[symbolIndex].bearishSwingLowTime, retraceTrackers[symbolIndex].bearishSwingLow); ObjectSetString(chart_id, swingLowTextName, OBJPROP_TEXT, "A"); ObjectSetInteger(chart_id, swingLowTextName, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, swingLowTextName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chart_id, swingLowTextName, OBJPROP_ANCHOR, ANCHOR_UPPER); string h2TextName = "H2_Text_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, h2TextName, OBJ_TEXT, 0, retraceTrackers[symbolIndex].bearishLast_HH_Times[1], retraceTrackers[symbolIndex].bearishLast_HH_Prices[1]); ObjectSetString(chart_id, h2TextName, OBJPROP_TEXT, "B"); ObjectSetInteger(chart_id, h2TextName, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, h2TextName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chart_id, h2TextName, OBJPROP_ANCHOR, ANCHOR_LOWER); string triggerTextName = "Trigger_Text_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, triggerTextName, OBJ_TEXT, 0, timeNow, retraceTrackers[symbolIndex].bearishSwingLow); ObjectSetString(chart_id, triggerTextName, OBJPROP_TEXT, "C"); ObjectSetInteger(chart_id, triggerTextName, OBJPROP_COLOR, clrBlack); ObjectSetInteger(chart_id, triggerTextName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chart_id, triggerTextName, OBJPROP_ANCHOR, ANCHOR_UPPER); //=================================================================================== // MODIFIED RECTANGLE - Bearish (40% from B to C, time fixed) //=================================================================================== // Calculate the move from B price to C price double bPrice = retraceTrackers[symbolIndex].bearishLast_HH_Prices[1]; double cPrice = retraceTrackers[symbolIndex].bearishSwingLow; double moveDistance = bPrice - cPrice; double rectangleBottomPrice = bPrice - (moveDistance * 0.40); if(EnableDebugPrints) { Print("DEBUG: Bearish Retracement Rectangle Calculation:"); Print("DEBUG: B Price: ", bPrice); Print("DEBUG: C Price: ", cPrice); Print("DEBUG: Move Distance: ", moveDistance); Print("DEBUG: 40% Level: ", rectangleBottomPrice, " (40% from B)"); Print("DEBUG: Rectangle from ", rectangleBottomPrice, " to B (", bPrice, ")"); Print("DEBUG: C Time FIXED at: ", TimeToString(timeNow)); } // Rectangle from 40% level (bottom) to B (top) string rectangleName = "Bearish_Rect_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, rectangleName, OBJ_RECTANGLE, 0, timeNow, // Left time (C time - FIXED) rectangleBottomPrice, // Bottom price (40% level) timeNow + (10 * GetTimeframeSeconds()), // Right time (C time + 10 periods - FIXED) bPrice); // Top price (B) ObjectSetInteger(chart_id, rectangleName, OBJPROP_COLOR, clrLightPink); ObjectSetInteger(chart_id, rectangleName, OBJPROP_BGCOLOR, clrLightPink); ObjectSetInteger(chart_id, rectangleName, OBJPROP_WIDTH, 1); ObjectSetInteger(chart_id, rectangleName, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(chart_id, rectangleName, OBJPROP_BACK, false); ObjectSetInteger(chart_id, rectangleName, OBJPROP_FILL, true); ObjectSetDouble(chart_id, rectangleName, OBJPROP_LEVELVALUE, 0.3); //Arrowed line string arrowLineName = "Bearish_Arrow_Retrace_" + IntegerToString(timeNow); ObjectCreate(chart_id, arrowLineName, OBJ_ARROWED_LINE, 0, timeNow, bPrice, timeNow + (10 * GetTimeframeSeconds()), cPrice); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_COLOR, clrBlue); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_WIDTH, 1); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_RAY_RIGHT, false); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_STYLE, STYLE_DOT); ObjectSetInteger(chart_id, arrowLineName, OBJPROP_BACK, false); if(EnableDebugPrints) Print("DEBUG: Completed drawing bearish RETRACEMENT annotations with FIXED C time: ", TimeToString(timeNow)); } //+------------------------------------------------------------------+ //| Take screenshot | //+------------------------------------------------------------------+ string TakeScreenshot(string alertType, string symbol, int symbolIndex) { string filename = alertType + "_" + symbol + "_" + IntegerToString(TimeCurrent()) + ".png"; long chart_id = 0; /* ChartOpen(symbol, _Period); if(chart_id <= 0) { Print("ERROR: Failed to open chart for ", symbol); return ""; } */ Sleep(500); tradeviewtpl(chart_id); Sleep(200); // Call the appropriate annotation function based on alert type if(alertType == "Bullish_Breakout") { DrawBullishBreakoutAnnotations(chart_id, symbol, symbolIndex); } else if(alertType == "Bullish_Retracement") { DrawBullishRetracementAnnotations(chart_id, symbol, symbolIndex); } else if(alertType == "Bearish_Breakout") { DrawBearishBreakoutAnnotations(chart_id, symbol, symbolIndex); } else if(alertType == "Bearish_Retracement") { DrawBearishRetracementAnnotations(chart_id, symbol, symbolIndex); } DrawWatermark(chart_id); Sleep(300); if(EnableDebugPrints) { Print("DEBUG: Attempting to capture screenshot for ", alertType, " alert on chart ID: ", chart_id); Print("DEBUG: Screenshot filename: ", filename); } if(ChartScreenShot(chart_id, filename, 1350, 1080, ALIGN_RIGHT)) { if(CloseChartAfterScreenshot) { ChartClose(chart_id); } if(EnableDebugPrints) { Print("DEBUG: Screenshot captured successfully: ", filename); Print("DEBUG: Saved to: ", TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Files\\" + filename); if(CloseChartAfterScreenshot) Print("DEBUG: Temporary chart closed"); } return filename; } else { if(EnableDebugPrints) { Print("DEBUG: ERROR - Screenshot capture failed. Last error: ", GetLastError()); } ChartClose(chart_id); return ""; } } //+------------------------------------------------------------------+ //| Draw watermark | //+------------------------------------------------------------------+ string WM_NAME = "TV_STYLE_WATERMARK"; bool DrawWatermark(long chartid) { string tf = GetTimeframeString(); string symbol = ChartSymbol(chartid); string text = symbol + " " + tf; if(ObjectFind(chartid, WM_NAME) == -1) { ObjectCreate(chartid, WM_NAME, OBJ_LABEL, 0, 0, 0); ObjectSetString(chartid, WM_NAME, OBJPROP_TEXT, text); ObjectSetInteger(chartid, WM_NAME, OBJPROP_COLOR, clrSilver); ObjectSetInteger(chartid, WM_NAME, OBJPROP_FONTSIZE, 50); ObjectSetString(chartid, WM_NAME, OBJPROP_FONT, "Arial Black"); ObjectSetInteger(chartid, WM_NAME, OBJPROP_BACK, true); ObjectSetInteger(chartid, WM_NAME, OBJPROP_SELECTABLE, false); ObjectSetInteger(chartid, WM_NAME, OBJPROP_HIDDEN, true); // Calculate center position int center_x = 1350 / 2; int center_y = 1080 / 2; ObjectSetInteger(chartid, WM_NAME, OBJPROP_XDISTANCE, center_x); ObjectSetInteger(chartid, WM_NAME, OBJPROP_YDISTANCE, center_y); ObjectSetInteger(chartid, WM_NAME, OBJPROP_ANCHOR, ANCHOR_CENTER); return true; } return false; } //+------------------------------------------------------------------+ //| Apply TradingView style template | //+------------------------------------------------------------------+ void tradeviewtpl(long chartid) { ChartSetInteger(chartid, CHART_COLOR_BACKGROUND, C'242,242,242'); ChartSetInteger(chartid, CHART_COLOR_FOREGROUND, C'19,23,34'); ChartSetInteger(chartid, CHART_COLOR_GRID, clrNONE); ChartSetInteger(chartid, CHART_COLOR_CHART_UP, C'38,166,154'); ChartSetInteger(chartid, CHART_COLOR_CHART_DOWN, C'239,83,80'); ChartSetInteger(chartid, CHART_COLOR_CANDLE_BULL, C'38,166,154'); ChartSetInteger(chartid, CHART_COLOR_CANDLE_BEAR, C'239,83,80'); ChartSetInteger(chartid, CHART_COLOR_CHART_LINE, C'38,166,154'); ChartSetInteger(chartid, CHART_SHOW_VOLUMES, false); ChartSetInteger(chartid, CHART_SHIFT, true); ChartSetInteger(chartid, CHART_SCALE, 3); } //+------------------------------------------------------------------+ //| Send alert data to server | //+------------------------------------------------------------------+ void SendAlertData(string alertType, double priceLevel, datetime swingTime, datetime alertTime, string screenshotFilename, string uniqueId, string symbol, int symbolIndex) { //Ensures screenshot if fully saved Sleep(5000); if(EnableDebugPrints) { Print("DEBUG: Preparing to send alert data to API for ", symbol); Print("DEBUG: Alert Type: ", alertType); Print("DEBUG: Price Level: ", priceLevel); Print("DEBUG: Swing Time: ", TimeToString(swingTime)); Print("DEBUG: Alert Time: ", TimeToString(alertTime)); Print("DEBUG: Unique ID: ", uniqueId); } string symbolSector = GetCustomSector(symbol); int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); string jsonPayload = "{"; jsonPayload += "\"symbol\":\"" + symbol + "\","; jsonPayload += "\"sector\":\"" + symbolSector + "\","; jsonPayload += "\"timeframe\":\"" + GetTimeframeString() + "\","; jsonPayload += "\"alert_type\":\"" + alertType + "\","; jsonPayload += "\"price_level\":" + DoubleToString(priceLevel, digits) + ","; jsonPayload += "\"swing_time\":" + IntegerToString(swingTime) + ","; jsonPayload += "\"alert_time\":" + IntegerToString(alertTime) + ","; jsonPayload += "\"bias\":\"" + BiasEnumToString(biasData[symbolIndex].bias) + "\","; jsonPayload += "\"session\":\"" + currentSession + "\","; jsonPayload += "\"strategy_name\":\"" + strategyName + "\","; jsonPayload += "\"strategy_id\":\"" + strategyId + "\","; jsonPayload += "\"unique_id\":\"" + uniqueId + "\","; jsonPayload += "\"screenshot_filename\":\"" + screenshotFilename + "\","; jsonPayload += "\"allowed_timeframes\":["; int tfCount = ArraySize(allowedTimeframe); for(int i = 0; i < tfCount; i++) { jsonPayload += "\"" + allowedTimeframe[i] + "\""; if(i < tfCount - 1) jsonPayload += ","; } jsonPayload += "],"; jsonPayload += "\"current_bid\":" + DoubleToString(swingData[symbolIndex].currentBidPrice, digits) + ","; jsonPayload += "\"current_ask\":" + DoubleToString(swingData[symbolIndex].currentAskPrice, digits); jsonPayload += "}"; if(EnableDebugPrints) { Print("DEBUG: JSON Payload:"); Print(jsonPayload); } string headers = "Content-Type: application/json\r\n"; uchar dataArray[]; StringToCharArray(jsonPayload, dataArray, 0, StringLen(jsonPayload)); uchar result[]; string resultHeaders; int response = WebRequest("POST", alertServerURL, headers, 10000, dataArray, result, resultHeaders); if(EnableDebugPrints) { Print("DEBUG: Alert data sent. Response: ", response); if(response == 200) { Print("DEBUG: SUCCESS - Alert data sent | Unique ID: ", uniqueId); } else if(response == -1) { Print("DEBUG: ERROR - WebRequest failed. Error: ", GetLastError()); } else { Print("DEBUG: Server response: ", response); } } } //+------------------------------------------------------------------+ //| Convert bias enum to string | //+------------------------------------------------------------------+ string BiasEnumToString(ENUM_BIAS bias) { switch(bias) { case BIAS_NEUTRAL: return "NEUTRAL"; case BIAS_BULLISH: return "BULLISH"; case BIAS_BEARISH: return "BEARISH"; default: return "UNKNOWN"; } } //+------------------------------------------------------------------+ //| Send screenshot binary data | //+------------------------------------------------------------------+ void SendScreenshotBinary(string filename, string alertType, string uniqueId, string symbol, int symbolIndex) { if(EnableDebugPrints) { Print("DEBUG: Preparing to send screenshot for ", symbol); Print("DEBUG: File: ", filename); Print("DEBUG: Unique ID: ", uniqueId); } int filehandle = FileOpen(filename, FILE_READ | FILE_BIN); if(filehandle == INVALID_HANDLE) { if(EnableDebugPrints) { Print("DEBUG: ERROR - Could not open file: ", filename); Print("DEBUG: Last error: ", GetLastError()); } return; } int fileSize = (int)FileSize(filehandle); uchar byteArray[]; ArrayResize(byteArray, fileSize); FileReadArray(filehandle, byteArray, 0, fileSize); FileClose(filehandle); string boundary = "----WebKitFormBoundary" + IntegerToString(TimeCurrent()); string headers = "Content-Type: multipart/form-data; boundary=" + boundary + "\r\n"; string formData = "--" + boundary + "\r\n"; formData += "Content-Disposition: form-data; name=\"alert_type\"\r\n\r\n"; formData += alertType + "\r\n"; formData += "--" + boundary + "\r\n"; formData += "Content-Disposition: form-data; name=\"symbol\"\r\n\r\n"; formData += symbol + "\r\n"; formData += "--" + boundary + "\r\n"; formData += "Content-Disposition: form-data; name=\"timestamp\"\r\n\r\n"; formData += IntegerToString(TimeCurrent()) + "\r\n"; formData += "--" + boundary + "\r\n"; formData += "Content-Disposition: form-data; name=\"strategy_name\"\r\n\r\n"; formData += strategyName + "\r\n"; formData += "--" + boundary + "\r\n"; formData += "Content-Disposition: form-data; name=\"strategy_id\"\r\n\r\n"; formData += strategyId + "\r\n"; formData += "--" + boundary + "\r\n"; formData += "Content-Disposition: form-data; name=\"sector\"\r\n\r\n"; formData += GetCustomSector(symbol) + "\r\n"; formData += "--" + boundary + "\r\n"; formData += "Content-Disposition: form-data; name=\"timeframe\"\r\n\r\n"; formData += GetTimeframeString() + "\r\n"; formData += "--" + boundary + "\r\n"; formData += "Content-Disposition: form-data; name=\"session\"\r\n\r\n"; formData += currentSession + "\r\n"; formData += "--" + boundary + "\r\n"; formData += "Content-Disposition: form-data; name=\"bias\"\r\n\r\n"; formData += BiasEnumToString(biasData[symbolIndex].bias) + "\r\n"; formData += "--" + boundary + "\r\n"; formData += "Content-Disposition: form-data; name=\"unique_id\"\r\n\r\n"; formData += uniqueId + "\r\n"; formData += "--" + boundary + "\r\n"; formData += "Content-Disposition: form-data; name=\"screenshot_filename\"\r\n\r\n"; formData += filename + "\r\n"; formData += "--" + boundary + "\r\n"; formData += "Content-Disposition: form-data; name=\"allowed_timeframes\"\r\n\r\n"; string allowedTFsStr = ""; int tfCount = ArraySize(allowedTimeframe); for(int i = 0; i < tfCount; i++) { allowedTFsStr += allowedTimeframe[i]; if(i < tfCount - 1) allowedTFsStr += ","; } formData += allowedTFsStr + "\r\n"; formData += "--" + boundary + "\r\n"; formData += "Content-Disposition: form-data; name=\"screenshot\"; filename=\"" + filename + "\"\r\n"; formData += "Content-Type: image/png\r\n\r\n"; uchar headerArray[]; StringToCharArray(formData, headerArray, 0, StringLen(formData)); string footer = "\r\n--" + boundary + "--\r\n"; uchar footerArray[]; StringToCharArray(footer, footerArray, 0, StringLen(footer)); uchar postData[]; int totalSize = ArraySize(headerArray) + ArraySize(byteArray) + ArraySize(footerArray); ArrayResize(postData, totalSize); for(int i = 0; i < ArraySize(headerArray); i++) postData[i] = headerArray[i]; int offset = ArraySize(headerArray); for(int i = 0; i < ArraySize(byteArray); i++) postData[offset + i] = byteArray[i]; offset += ArraySize(byteArray); for(int i = 0; i < ArraySize(footerArray); i++) postData[offset + i] = footerArray[i]; if(EnableDebugPrints) { Print("DEBUG: Sending multipart data, size: ", totalSize, " bytes"); } uchar result[]; string resultHeaders; int response = WebRequest("POST", screenshotServerURL, headers, 15000, postData, result, resultHeaders); if(EnableDebugPrints) { Print("DEBUG: Screenshot sent. Response: ", response); if(response == 200) { Print("DEBUG: SUCCESS - Screenshot sent | Unique ID: ", uniqueId); } else if(response == 400) { Print("DEBUG: ERROR - Bad Request (400)"); } else if(response == -1) { Print("DEBUG: ERROR - WebRequest failed. Error: ", GetLastError()); } } } // ============================================================ // SWING ANALYSIS FUNCTIONS // ============================================================ //+------------------------------------------------------------------+ //| Find swing points | //+------------------------------------------------------------------+ void FindSwingPoints(int symbolIndex) { string symbol = Symbols[symbolIndex]; int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); int startBar = SwingLookbackBars + SwingLeftBars; for(int i = startBar; i >= SwingRightBars; i--) { double currentLow = iLow(symbol, PERIOD_CURRENT, i); double currentHigh = iHigh(symbol, PERIOD_CURRENT, i); bool isSwingLow = true; bool isSwingHigh = true; // Check swing low left side for(int left = 1; left <= SwingLeftBars; left++) { double leftLow = iLow(symbol, PERIOD_CURRENT, i + left); if(currentLow >= leftLow) { isSwingLow = false; break; } } // Check swing low right side if(isSwingLow) { for(int right = 1; right <= SwingRightBars; right++) { double rightLow = iLow(symbol, PERIOD_CURRENT, i - right); if(currentLow >= rightLow) { isSwingLow = false; break; } } } // Check swing high left side for(int left = 1; left <= SwingLeftBars; left++) { double leftHigh = iHigh(symbol, PERIOD_CURRENT, i + left); if(currentHigh <= leftHigh) { isSwingHigh = false; break; } } // Check swing high right side if(isSwingHigh) { for(int right = 1; right <= SwingRightBars; right++) { double rightHigh = iHigh(symbol, PERIOD_CURRENT, i - right); if(currentHigh <= rightHigh) { isSwingHigh = false; break; } } } // Add swing low if(isSwingLow) { double swingLowPrice = NormalizeDouble(currentLow, symbol_digits); datetime swingLowTime = iTime(symbol, PERIOD_CURRENT, i); int size = ArraySize(swingData[symbolIndex].swingLowPrices); ArrayResize(swingData[symbolIndex].swingLowPrices, size + 1); ArrayResize(swingData[symbolIndex].swingLowTimes, size + 1); swingData[symbolIndex].swingLowPrices[size] = swingLowPrice; swingData[symbolIndex].swingLowTimes[size] = swingLowTime; } // Add swing high if(isSwingHigh) { double swingHighPrice = NormalizeDouble(currentHigh, symbol_digits); datetime swingHighTime = iTime(symbol, PERIOD_CURRENT, i); int size = ArraySize(swingData[symbolIndex].swingHighPrices); ArrayResize(swingData[symbolIndex].swingHighPrices, size + 1); ArrayResize(swingData[symbolIndex].swingHighTimes, size + 1); swingData[symbolIndex].swingHighPrices[size] = swingHighPrice; swingData[symbolIndex].swingHighTimes[size] = swingHighTime; } } } //+------------------------------------------------------------------+ //| Get consecutive highs and lows | //+------------------------------------------------------------------+ void GetConsecutiveHighsAndLows(int symbolIndex) { ArrayInitialize(swingData[symbolIndex].HH_Prices, 0); ArrayInitialize(swingData[symbolIndex].HH_Times, 0); ArrayInitialize(swingData[symbolIndex].LL_Prices, 0); ArrayInitialize(swingData[symbolIndex].LL_Times, 0); int swingHighCount = ArraySize(swingData[symbolIndex].swingHighPrices); int swingLowCount = ArraySize(swingData[symbolIndex].swingLowPrices); // Find last 2 consecutive higher highs for(int i = swingHighCount - 1; i >= 1; i--) { if(swingData[symbolIndex].swingHighPrices[i] > swingData[symbolIndex].swingHighPrices[i - 1]) { swingData[symbolIndex].HH_Prices[1] = swingData[symbolIndex].swingHighPrices[i]; swingData[symbolIndex].HH_Times[1] = swingData[symbolIndex].swingHighTimes[i]; swingData[symbolIndex].HH_Prices[0] = swingData[symbolIndex].swingHighPrices[i - 1]; swingData[symbolIndex].HH_Times[0] = swingData[symbolIndex].swingHighTimes[i - 1]; break; } } // Find last 2 consecutive lower lows for(int i = swingLowCount - 1; i >= 1; i--) { if(swingData[symbolIndex].swingLowPrices[i] < swingData[symbolIndex].swingLowPrices[i - 1]) { swingData[symbolIndex].LL_Prices[1] = swingData[symbolIndex].swingLowPrices[i]; swingData[symbolIndex].LL_Times[1] = swingData[symbolIndex].swingLowTimes[i]; swingData[symbolIndex].LL_Prices[0] = swingData[symbolIndex].swingLowPrices[i - 1]; swingData[symbolIndex].LL_Times[0] = swingData[symbolIndex].swingLowTimes[i - 1]; break; } } } //+------------------------------------------------------------------+ //| Determine breakout points | //+------------------------------------------------------------------+ void GetBreakPoint(int symbolIndex) { string symbol = Symbols[symbolIndex]; if(biasData[symbolIndex].bias == BIAS_BULLISH) { int Bullish_BarsInBetween = GetBarsBetweenTimes(swingData[symbolIndex].LL_Times[0], swingData[symbolIndex].LL_Times[1], symbol); int L2_Index = iBarShift(symbol, PERIOD_CURRENT, swingData[symbolIndex].LL_Times[1], true); int highestHigh_Index = iHighest(symbol, PERIOD_CURRENT, MODE_HIGH, Bullish_BarsInBetween - 1, L2_Index + 1); if(Is3BarSwingHigh(highestHigh_Index, symbol)) { swingData[symbolIndex].bullishSwingHigh = iHigh(symbol, PERIOD_CURRENT, highestHigh_Index); swingData[symbolIndex].bullishSwingHighTime = iTime(symbol, PERIOD_M15, highestHigh_Index); swingData[symbolIndex].Last_LL_Times[0] = swingData[symbolIndex].LL_Times[0]; swingData[symbolIndex].Last_LL_Times[1] = swingData[symbolIndex].LL_Times[1]; swingData[symbolIndex].Last_LL_Prices[0] = swingData[symbolIndex].LL_Prices[0]; swingData[symbolIndex].Last_LL_Prices[1] = swingData[symbolIndex].LL_Prices[1]; } } else if(biasData[symbolIndex].bias == BIAS_BEARISH) { int Bearish_BarsInBetween = GetBarsBetweenTimes(swingData[symbolIndex].HH_Times[0], swingData[symbolIndex].HH_Times[1], symbol); int H2_Index = iBarShift(symbol, PERIOD_CURRENT, swingData[symbolIndex].HH_Times[1], true); int lowestLow_Index = iLowest(symbol, PERIOD_CURRENT, MODE_LOW, Bearish_BarsInBetween - 1, H2_Index + 1); if(Is3BarSwingLow(lowestLow_Index, symbol)) { swingData[symbolIndex].bearishSwingLow = iLow(symbol, PERIOD_CURRENT, lowestLow_Index); swingData[symbolIndex].bearishSwingLowTime = iTime(symbol, PERIOD_M15, lowestLow_Index); swingData[symbolIndex].Last_HH_Times[0] = swingData[symbolIndex].HH_Times[0]; swingData[symbolIndex].Last_HH_Times[1] = swingData[symbolIndex].HH_Times[1]; swingData[symbolIndex].Last_HH_Prices[0] = swingData[symbolIndex].HH_Prices[0]; swingData[symbolIndex].Last_HH_Prices[1] = swingData[symbolIndex].HH_Prices[1]; } } } // ============================================================ // SWING PATTERN VALIDATION FUNCTIONS // ============================================================ //+------------------------------------------------------------------+ //| Check if 3-bar swing low | //+------------------------------------------------------------------+ bool Is3BarSwingLow(int swingLowBarIndex, string symbol) { if(swingLowBarIndex < 1) return false; return (iLow(symbol, PERIOD_CURRENT, swingLowBarIndex) < iLow(symbol, PERIOD_CURRENT, swingLowBarIndex + 1) && iLow(symbol, PERIOD_CURRENT, swingLowBarIndex) < iLow(symbol, PERIOD_CURRENT, swingLowBarIndex - 1)); } //+------------------------------------------------------------------+ //| Check if 3-bar swing high | //+------------------------------------------------------------------+ bool Is3BarSwingHigh(int swingHighBarIndex, string symbol) { if(swingHighBarIndex < 1) return false; return (iHigh(symbol, PERIOD_CURRENT, swingHighBarIndex) > iHigh(symbol, PERIOD_CURRENT, swingHighBarIndex + 1) && iHigh(symbol, PERIOD_CURRENT, swingHighBarIndex) > iHigh(symbol, PERIOD_CURRENT, swingHighBarIndex - 1)); } // ============================================================ // TIME AND BAR CALCULATION FUNCTIONS // ============================================================ //+------------------------------------------------------------------+ //| Calculate bars between times | //+------------------------------------------------------------------+ int GetBarsBetweenTimes(datetime startTime, datetime endTime, string symbol) { if(startTime > endTime) { datetime temp = startTime; startTime = endTime; endTime = temp; } int startBar = iBarShift(symbol, PERIOD_CURRENT, startTime, true); int endBar = iBarShift(symbol, PERIOD_CURRENT, endTime, true); if(startBar != -1 && endBar != -1) { return MathAbs(startBar - endBar); } return 0; } //+------------------------------------------------------------------+ //| Determine market bias | //+------------------------------------------------------------------+ void FindBias(int symbolIndex) { string symbol = Symbols[symbolIndex]; double dailyHigh0 = iHigh(symbol, PERIOD_D1, 0); double dailyHigh1 = prevDayData[symbolIndex].highPrice; double dailyLow0 = iLow(symbol, PERIOD_D1, 0); double dailyLow1 = prevDayData[symbolIndex].lowPrice; if(dailyHigh0 > dailyHigh1) { biasData[symbolIndex].bias = BIAS_BEARISH; } if(dailyLow0 < dailyLow1) { biasData[symbolIndex].bias = BIAS_BULLISH; } } //+------------------------------------------------------------------+ //| Check for new day | //+------------------------------------------------------------------+ bool IsNewDay(int index) { datetime t = iTime(Symbols[index], PERIOD_D1, 0); if(t != last_bar_time_d[index]) { last_bar_time_d[index] = t; return true; } return false; } //+------------------------------------------------------------------+ //| Check for new 15-min bar | //+------------------------------------------------------------------+ bool IsNew15MinBar(int index) { datetime t = iTime(Symbols[index], PERIOD_M15, 0); if(t != last_bar_time[index]) { last_bar_time[index] = t; return true; } return false; } // ============================================================ // PREVIOUS DAY ANALYSIS FUNCTIONS // ============================================================ //+------------------------------------------------------------------+ //| Get last tradeable day start | //+------------------------------------------------------------------+ datetime GetLastTradeableDayStart(string symbol) { int maxDaysToCheck = 10; datetime currentTime = TimeCurrent(); for(int daysBack = 1; daysBack <= maxDaysToCheck; daysBack++) { datetime dayToCheck = currentTime - (daysBack * 86400); MqlDateTime dt; TimeToStruct(dayToCheck, dt); dt.hour = 0; dt.min = 0; dt.sec = 0; dayToCheck = StructToTime(dt); datetime nextDay = dayToCheck + 86400; MqlRates rates[]; int copied = CopyRates(symbol, PERIOD_M15, dayToCheck, nextDay, rates); if(copied >= MinCandlesForTradeableDay) { return dayToCheck; } } datetime yesterday = currentTime - 86400; MqlDateTime dtYest; TimeToStruct(yesterday, dtYest); dtYest.hour = 0; dtYest.min = 0; dtYest.sec = 0; yesterday = StructToTime(dtYest); if(EnableDebugPrints) { Print("DEBUG: Using fallback - yesterday: ", TimeToString(yesterday, TIME_DATE)); } return yesterday; } //+------------------------------------------------------------------+ //| Get previous day high time | //+------------------------------------------------------------------+ datetime GetM15PrevDayHighTime(string symbol) { MqlRates rates[]; datetime highTime = 0; double highest = 0; datetime prevTradeableDay = GetLastTradeableDayStart(symbol); datetime currentDayOpen = iTime(symbol, PERIOD_D1, 0); int copied = CopyRates(symbol, PERIOD_M15, prevTradeableDay, currentDayOpen, rates); if(copied <= 0) { return 0; } for(int i = 0; i < copied; i++) { if(rates[i].high > highest) { highest = rates[i].high; highTime = rates[i].time; } } return highTime; } //+------------------------------------------------------------------+ //| Get previous day low time | //+------------------------------------------------------------------+ datetime GetM15PrevDayLowTime(string symbol) { MqlRates rates[]; datetime lowTime = 0; double lowest = DBL_MAX; datetime prevTradeableDay = GetLastTradeableDayStart(symbol); datetime currentDayOpen = iTime(symbol, PERIOD_D1, 0); int copied = CopyRates(symbol, PERIOD_M15, prevTradeableDay, currentDayOpen, rates); if(copied <= 0) { return 0; } for(int i = 0; i < copied; i++) { if(rates[i].low < lowest) { lowest = rates[i].low; lowTime = rates[i].time; } } return lowTime; } // ============================================================ // SESSION MANAGEMENT FUNCTIONS // ============================================================ //+------------------------------------------------------------------+ //| Check if in session | //+------------------------------------------------------------------+ bool IsInSession(string sessionStart, string sessionEnd) { datetime currentTime = TimeCurrent(); MqlDateTime timeStruct; TimeToStruct(currentTime, timeStruct); string startTimeStr = TimeToString(currentTime, TIME_DATE) + " " + sessionStart; string endTimeStr = TimeToString(currentTime, TIME_DATE) + " " + sessionEnd; datetime sessionStartTime = StringToTime(startTimeStr); datetime sessionEndTime = StringToTime(endTimeStr); if(sessionEndTime <= sessionStartTime) { sessionEndTime += 24 * 60 * 60; } return (currentTime >= sessionStartTime && currentTime < sessionEndTime); } //+------------------------------------------------------------------+ //| Get current session | //+------------------------------------------------------------------+ string GetCurrentSession() { if(IsInSession(SydneySessionStart, SydneySessionEnd)) return "Sydney"; if(IsInSession(AsiaSessionStart, AsiaSessionEnd)) return "Asian"; else if(IsInSession(LondonSessionStart, LondonSessionEnd)) return "London"; else if(IsInSession(NewYorkSessionStart, NewYorkSessionEnd)) return "NewYork"; else return "OutsideSession"; } //+------------------------------------------------------------------+ //| Get timeframe string | //+------------------------------------------------------------------+ string GetTimeframeString() { switch(_Period) { case PERIOD_M1: return "M1"; case PERIOD_M5: return "M5"; case PERIOD_M15: return "M15"; case PERIOD_M30: return "M30"; case PERIOD_H1: return "H1"; case PERIOD_H4: return "H4"; case PERIOD_H12: return "H12"; case PERIOD_D1: return "D1"; case PERIOD_W1: return "W1"; case PERIOD_MN1: return "MN1"; default: return "Unknown"; } } //+------------------------------------------------------------------+ //| Get timeframe seconds | //+------------------------------------------------------------------+ int GetTimeframeSeconds() { switch(_Period) { case PERIOD_M1: return 60; case PERIOD_M2: return 120; case PERIOD_M3: return 180; case PERIOD_M4: return 240; case PERIOD_M5: return 300; case PERIOD_M6: return 360; case PERIOD_M10: return 600; case PERIOD_M12: return 720; case PERIOD_M15: return 900; case PERIOD_M20: return 1200; case PERIOD_M30: return 1800; case PERIOD_H1: return 3600; case PERIOD_H2: return 7200; case PERIOD_H3: return 10800; case PERIOD_H4: return 14400; case PERIOD_H6: return 21600; case PERIOD_H8: return 28800; case PERIOD_H12: return 43200; case PERIOD_D1: return 86400; case PERIOD_W1: return 604800; default: return 3600; } } //+------------------------------------------------------------------+