//+------------------------------------------------------------------+ //| 50 Pips Push or Pull to EMA 25.mq5 | //| Copyright 2025,Setup Radar | //| https://www.Setup-Radar.com | //| Author: X.com/Hectorand | //| Production Ready - Cleaned & Commented | //+------------------------------------------------------------------+ #property copyright "Copyright 2025,Setup Radar" #property link "https://www.Setup-Radar.com" #property version "1.00" // Include sector mapping functionality #include #include //--- Input parameters input int EMA_Period = 25; // EMA Period input double Min_Pips_Distance = 50.0; // Minimum Pips Distance input int Min_Bars_Between_Touches = 3; // Min bars between touches input bool Enable_Alerts = false; // Enable alerts input bool Enable_Print = true; // Enable Print to Journal input bool Show_Visual_Markers = true; // Show visual markers on chart input bool Enable_Debug_Prints = true; // Enable debug prints for API communication input bool Enable_Data_Send = true; // Enable sending data to API input bool Enable_Screenshot_Send = true; // Enable sending screenshots to API //--- Server Configuration string alertServerURL = "https://api.setupradar.app/api/v1/strategy/webhook"; string screenshotServerURL = "https://api.setupradar.app/api/v1/strategy/webhook"; string strategyName = "50 Pips Push or Pull to EMA 25"; string strategyId = "0000003"; string allowedTimeframe[]; // Empty array, no elements //--- Session Configuration 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 string currentSession = "OutsideSession"; // ---- GLOBALS ---- string Symbols[]; int SymbolsCount; int EmaHandles[]; double ema_buffer[]; MqlRates price_data[]; datetime last_bar_time[]; datetime last_bar_time_15[]; int min_bars; datetime last_alert_time[]; bool touch_alerted_current_bar[]; bool alert_sent[]; // ---- INIT ---- int OnInit() { min_bars = EMA_Period + 100; SymbolsCount = SymbolsTotal(true); ArrayResize(Symbols, SymbolsCount); ArrayResize(EmaHandles, SymbolsCount); ArrayResize(last_bar_time, SymbolsCount); ArrayResize(last_bar_time_15, SymbolsCount); ArrayResize(alert_sent, SymbolsCount); ArrayResize(touch_alerted_current_bar, SymbolsCount); for(int i = 0; i < SymbolsCount; i++) { Symbols[i] = SymbolName(i, true); EmaHandles[i] = iMA( Symbols[i], _Period, EMA_Period, 0, MODE_EMA, PRICE_CLOSE ); alert_sent[i] = false; if(EmaHandles[i] == INVALID_HANDLE) Print("EMA handle failed for ", Symbols[i]); last_bar_time[i] = 0; last_bar_time_15[i] = 0; } ArraySetAsSeries(ema_buffer, true); ArraySetAsSeries(price_data, true); EventSetTimer(2); Print("Minimal multisymbol base initialized"); currentSession = GetCurrentSession(); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CheckForEMATouch(string symb, int index) { if(!IsCandleTouchingEMA(0)) return; if(IsPriceAboveEMA(1)) CheckBullishSetup(0, symb, index); else if(IsPriceBelowEMA(1)) { CheckBearishSetup(0, symb, index); } } // ---- TIMER LOOP ---- void OnTimer() { for(int i = 0; i < SymbolsCount; i++) { string symbol_name = Symbols[i]; if(!RefreshData(i)) continue; if(IsNewBar(i)) { alert_sent[i] = false; } CheckForEMATouch(Symbols[i], i); } // Update session every 15 minutes if(IsNew15MinBar(0)) currentSession = GetCurrentSession(); } // ---- DATA REFRESH ---- bool RefreshData(int index) { string sym = Symbols[index]; int bars_needed = Bars(sym, _Period); if(bars_needed < min_bars) return false; if(Bars(sym, _Period) < EMA_Period + 5) return false; if(CopyRates(sym, _Period, 0, min_bars, price_data) < min_bars) return false; if(CopyBuffer(EmaHandles[index], 0, 0, min_bars, ema_buffer) < min_bars) return false; ArraySetAsSeries(ema_buffer, true); return true; } // ---- NEW BAR CHECK ---- bool IsNewBar(int index) { datetime t = iTime(Symbols[index], _Period, 0); if(t != last_bar_time[index]) { last_bar_time[index] = t; return true; } return false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool IsNew15MinBar(int index) { datetime t = iTime(Symbols[index], PERIOD_M15, 0); if(t != last_bar_time_15[index]) { last_bar_time_15[index] = t; return true; } return false; } // ---- DEINIT ---- void OnDeinit(const int reason) { for(int i = 0; i < SymbolsCount; i++) { if(EmaHandles[i] != INVALID_HANDLE) IndicatorRelease(EmaHandles[i]); } EventKillTimer(); Print("Minimal multisymbol base stopped"); Comment(""); ObjectDelete(0, WM_NAME); } //+------------------------------------------------------------------+ //| Check if EMA is between candle high and low (touch detection) | //+------------------------------------------------------------------+ bool IsCandleTouchingEMA(int candle_idx) { return (ema_buffer[candle_idx + 1] >= price_data[candle_idx].low && ema_buffer[candle_idx + 1] <= price_data[candle_idx].high); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool IsPriceAboveEMA(int candle_idx) { return price_data[candle_idx].open > ema_buffer[candle_idx]; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool IsPriceBelowEMA(int candle_idx) { return price_data[candle_idx].open < ema_buffer[candle_idx]; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CheckBullishSetup(int current_candle_idx, string symbol, int i) { int ema_size = ArraySize(ema_buffer); if(current_candle_idx + 1 >= ema_size) return; int prev_touch_idx = FindPreviousTouch(current_candle_idx); if(prev_touch_idx == -1) return; if(MathAbs(current_candle_idx - prev_touch_idx) <= Min_Bars_Between_Touches) return; double highest_high = FindHighestHigh(prev_touch_idx, current_candle_idx, symbol); // Print(current_candle_idx, " " , prev_touch_idx, " ", highest_high); if(highest_high == 0) return; double ema_val = ema_buffer[current_candle_idx + 1]; double distance_pips = PriceToPips(highest_high - ema_val, symbol); if(distance_pips >= Min_Pips_Distance && bearish(symbol, current_candle_idx)) { string alert_msg = StringFormat( "BULLISH Setup | %s %s | Distance: %.1f pips | HH: %.5f | EMA: %.5f", symbol, GetTimeframeString(), distance_pips, highest_high, ema_val ); if(!alert_sent[i]) { if(Enable_Print) Print(alert_msg); if(Enable_Alerts) { Alert(alert_msg); } string screenshotFilename = TakeScreenshot("Bullish", highest_high, ema_val, distance_pips, symbol, current_candle_idx, i); SendAlert("Bullish", highest_high, ema_val, distance_pips, screenshotFilename, symbol); alert_sent[i] = true; } } } //+------------------------------------------------------------------+ //| Validate bearish EMA bounce setup | //+------------------------------------------------------------------+ void CheckBearishSetup(int current_candle_idx, string symbol, int i) { // Find previous EMA touch int prev_touch_idx = FindPreviousTouch(current_candle_idx); if(prev_touch_idx == -1) return; // Ensure minimum separation between touches if(MathAbs(current_candle_idx - prev_touch_idx) <= Min_Bars_Between_Touches) return; // Find lowest low between the two touches double lowest_low = FindLowestLow(prev_touch_idx, current_candle_idx, symbol); // Print(current_candle_idx, prev_touch_idx, lowest_low); if(lowest_low == 0) return; // Calculate distance from EMA to lowest low in pips double distance_pips = PriceToPips(ema_buffer[current_candle_idx] - lowest_low, symbol); // Validate minimum distance requirement (50 pips) if(distance_pips >= Min_Pips_Distance && bullish(symbol, current_candle_idx)) { // Format alert message with setup details string alert_msg = StringFormat("BEARISH Setup | %s %s | Distance: %.1f pips | LL: %.5f | EMA: %.5f", symbol, GetTimeframeString(), distance_pips, lowest_low, ema_buffer[current_candle_idx]); // Print to journal if enabled if(!alert_sent[i]) { if(Enable_Print) Print(alert_msg); // Show pop-up alert only on that candle if(Enable_Alerts) { Alert(alert_msg); } // Take screenshot and send to API string screenshotFilename = TakeScreenshot("Bearish", lowest_low, ema_buffer[current_candle_idx], distance_pips, symbol, current_candle_idx, i); SendAlert("Bearish", lowest_low, ema_buffer[current_candle_idx], distance_pips, screenshotFilename, symbol); alert_sent[i] = true; } } } //+------------------------------------------------------------------+ //| Find previous EMA touch candle (search forward in time) | //+------------------------------------------------------------------+ int FindPreviousTouch(int start_candle_idx) { for(int i = start_candle_idx + 1; i < MathMin(min_bars, ArraySize(price_data)); i++) { if(i >= ArraySize(price_data) || i >= ArraySize(ema_buffer)) break; if(IsCandleTouchingEMA(i)) return i; } return -1; } //+------------------------------------------------------------------+ //| Find highest high between two touch candles | //+------------------------------------------------------------------+ double FindHighestHigh(int start_idx, int end_idx, string symb) { double highest = 0; int highint = iHighest(symb, _Period, MODE_HIGH, MathAbs(start_idx - end_idx), end_idx); return iHigh(symb, _Period, highint); } //+------------------------------------------------------------------+ //| Find lowest low between two touch candles | //+------------------------------------------------------------------+ double FindLowestLow(int start_idx, int end_idx, string symb) { double lowest = 0; int lowint = iLowest(symb, _Period, MODE_LOW, MathAbs(start_idx - end_idx), end_idx); return iLow(symb, _Period, lowint); } //+------------------------------------------------------------------+ //| Convert price difference to pip units | //+------------------------------------------------------------------+ double PriceToPips(double price_diff, string symbol) { return MathAbs(price_diff / GetPipDivider(symbol)); } //+------------------------------------------------------------------+ //| Convert period to readable timeframe string | //+------------------------------------------------------------------+ string GetTimeframeString() { switch(_Period) { case PERIOD_M1: return "M1"; case PERIOD_M2: return "M2"; case PERIOD_M3: return "M3"; case PERIOD_M4: return "M4"; case PERIOD_M5: return "M5"; case PERIOD_M6: return "M6"; case PERIOD_M10: return "M10"; case PERIOD_M15: return "M15"; case PERIOD_M20: return "M20"; case PERIOD_M30: return "M30"; case PERIOD_H1: return "H1"; case PERIOD_H2: return "H2"; case PERIOD_H3: return "H3"; case PERIOD_H4: return "H4"; case PERIOD_H6: return "H6"; case PERIOD_H8: return "H8"; case PERIOD_H12: return "H12"; case PERIOD_D1: return "D1"; case PERIOD_W1: return "W1"; case PERIOD_MN1: return "MN1"; default: return "Unknown"; } } //+------------------------------------------------------------------+ //| Determine current trading session based on time | //+------------------------------------------------------------------+ string GetCurrentSession() { datetime currentTime = TimeCurrent(); MqlDateTime timeStruct; TimeToStruct(currentTime, timeStruct); // Check Sydney session string startTimeStr = TimeToString(currentTime, TIME_DATE) + " " + SydneySessionStart; string endTimeStr = TimeToString(currentTime, TIME_DATE) + " " + SydneySessionEnd; datetime sessionStartTime = StringToTime(startTimeStr); datetime sessionEndTime = StringToTime(endTimeStr); if(sessionEndTime <= sessionStartTime) sessionEndTime += 24 * 60 * 60; if(currentTime >= sessionStartTime && currentTime < sessionEndTime) return "Sydney"; // Check Asian session startTimeStr = TimeToString(currentTime, TIME_DATE) + " " + AsiaSessionStart; endTimeStr = TimeToString(currentTime, TIME_DATE) + " " + AsiaSessionEnd; sessionStartTime = StringToTime(startTimeStr); sessionEndTime = StringToTime(endTimeStr); if(sessionEndTime <= sessionStartTime) sessionEndTime += 24 * 60 * 60; if(currentTime >= sessionStartTime && currentTime < sessionEndTime) return "Asian"; // Check London session startTimeStr = TimeToString(currentTime, TIME_DATE) + " " + LondonSessionStart; endTimeStr = TimeToString(currentTime, TIME_DATE) + " " + LondonSessionEnd; sessionStartTime = StringToTime(startTimeStr); sessionEndTime = StringToTime(endTimeStr); if(sessionEndTime <= sessionStartTime) sessionEndTime += 24 * 60 * 60; if(currentTime >= sessionStartTime && currentTime < sessionEndTime) return "London"; // Check New York session startTimeStr = TimeToString(currentTime, TIME_DATE) + " " + NewYorkSessionStart; endTimeStr = TimeToString(currentTime, TIME_DATE) + " " + NewYorkSessionEnd; sessionStartTime = StringToTime(startTimeStr); sessionEndTime = StringToTime(endTimeStr); if(sessionEndTime <= sessionStartTime) sessionEndTime += 24 * 60 * 60; if(currentTime >= sessionStartTime && currentTime < sessionEndTime) return "NewYork"; return "OutsideSession"; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool WaitForChart(long chart_id, int timeout_ms = 5000) { int waited = 0; while(waited < timeout_ms) { if(ChartSymbol(chart_id) != "") return true; Sleep(100); waited += 100; } return false; } string WM_NAME = "TV_STYLE_WATERMARK"; //+------------------------------------------------------------------+ //| Capture chart screenshot when setup is detected | //+------------------------------------------------------------------+ string TakeScreenshot(string setupType, double extremePrice, double emaPrice, double distancePips, string symbol, int current_candle_idx, int index) { string filename = setupType + "_EMA_Bounce_" + symbol + "_" + IntegerToString(TimeCurrent()) + ".png"; long chart_id = ChartOpen(symbol, _Period); if(chart_id <= 0) return "failed to open chart"; tradeviewtpl(chart_id, index); Sleep(200); DrawWatermark(chart_id); if(Show_Visual_Markers) AddVisualMarker(current_candle_idx, setupType, distancePips, extremePrice, symbol, chart_id); Sleep(200); if(ChartScreenShot(chart_id, filename, 1350, 1080, ALIGN_RIGHT)) { if(Enable_Debug_Prints) Print("[DEBUG] Screenshot taken: ", filename); Sleep(50); } else { if(Enable_Debug_Prints) Print("[DEBUG] Screenshot failed"); filename = ""; } ChartClose(chart_id); ObjectDelete(0, WM_NAME); return filename; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void tradeviewtpl(long chartid, int index) { 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, false); ChartSetInteger(chartid, CHART_SCALE, 5); Print("Indiciator added for screenshot : ", ChartIndicatorAdd(chartid, 0, EmaHandles[index])); } //trying to commit //+------------------------------------------------------------------+ //| Generate a unique identifier using timestamp and random number | //+------------------------------------------------------------------+ string GenerateUniqueId() { // Create a unique ID using timestamp and random number int randomNum = (int)MathRand(); string uniqueId = IntegerToString(TimeCurrent()) + "_" + IntegerToString(randomNum) + "_" + IntegerToString(GetTickCount()); return uniqueId; } //+------------------------------------------------------------------+ //| Send alert data and screenshot to API | //+------------------------------------------------------------------+ void SendAlert(string setupType, double extremePrice, double emaPrice, double distancePips, string screenshotFilename, string symbol) { // Generate unique identifier for this alert string uniqueId = GenerateUniqueId(); // Send setup data to API if enabled if(Enable_Data_Send) { if(Enable_Debug_Prints) Print("[DEBUG] Sending alert data to API... | Unique ID: ", uniqueId); SendalertData(setupType, extremePrice, emaPrice, distancePips, screenshotFilename, symbol, uniqueId); } else { if(Enable_Debug_Prints) Print("[DEBUG] Data send disabled - skipping alert data"); } // Send screenshot to API if enabled and file exists if(Enable_Screenshot_Send && screenshotFilename != "") { if(Enable_Debug_Prints) Print("[DEBUG] Sending screenshot to API... | Unique ID: ", uniqueId); SendScreenshotBinary(screenshotFilename, setupType, extremePrice, emaPrice, distancePips, symbol, uniqueId); } else if(screenshotFilename == "") { if(Enable_Debug_Prints) Print("[DEBUG] No screenshot to send (filename empty)"); } else { if(Enable_Debug_Prints) Print("[DEBUG] Screenshot send disabled - skipping"); } } //+------------------------------------------------------------------+ //| Send setup data as JSON to API server | //+------------------------------------------------------------------+ void SendalertData(string setupType, double extremePrice, double emaPrice, double distancePips, string screenshotFilename, string symbol, string uniqueId) { // Get symbol sector and current market prices string symbolSector = GetCustomSector(symbol); double currentBid = SymbolInfoDouble(symbol, SYMBOL_BID); double currentAsk = SymbolInfoDouble(symbol, SYMBOL_ASK); // Debug output if enabled if(Enable_Debug_Prints) { Print("[DEBUG] Preparing JSON payload..."); Print("[DEBUG] Symbol: ", symbol, " | Sector: ", symbolSector); Print("[DEBUG] Session: ", currentSession, " | Strategy: ", strategyName); Print("[DEBUG] Setup Type: ", setupType, " | Distance: ", distancePips, " pips"); Print("[DEBUG] Screenshot Filename: ", screenshotFilename); Print("[DEBUG] Unique ID: ", uniqueId); } // Build JSON payload with all setup information string jsonPayload = "{"; jsonPayload = jsonPayload + "\"symbol\":\"" + symbol + "\","; jsonPayload = jsonPayload + "\"sector\":\"" + symbolSector + "\","; jsonPayload = jsonPayload + "\"timeframe\":\"" + GetTimeframeString() + "\","; jsonPayload = jsonPayload + "\"strategy_name\":\"" + strategyName + "\","; jsonPayload = jsonPayload + "\"strategy_id\":\"" + strategyId + "\","; // Add unique identifier and screenshot filename jsonPayload = jsonPayload + "\"unique_id\":\"" + uniqueId + "\","; jsonPayload = jsonPayload + "\"screenshot_filename\":\"" + screenshotFilename + "\","; // Handle allowed_timeframes (empty or populated) jsonPayload += "\"allowed_timeframes\":["; int tfCount = ArraySize(allowedTimeframe); for(int i = 0; i < tfCount; i++) { jsonPayload = jsonPayload + "\"" + allowedTimeframe[i] + "\""; if(i < tfCount - 1) jsonPayload = jsonPayload + ","; } jsonPayload = jsonPayload + "],"; jsonPayload = jsonPayload + "\"session\":\"" + currentSession + "\","; jsonPayload = jsonPayload + "\"setup_type\":\"" + setupType + "\","; jsonPayload = jsonPayload + "\"formation_time\":" + IntegerToString(TimeCurrent()) + ","; int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); // Add setup-specific price data if(setupType == "Bullish") { jsonPayload = jsonPayload + "\"highest_high\":" + DoubleToString(extremePrice, digits) + ","; jsonPayload = jsonPayload + "\"ema_price\":" + DoubleToString(emaPrice, digits) + ","; } else { jsonPayload = jsonPayload + "\"lowest_low\":" + DoubleToString(extremePrice, digits) + ","; jsonPayload = jsonPayload + "\"ema_price\":" + DoubleToString(emaPrice, digits) + ","; } // Add distance and current market data jsonPayload = jsonPayload + "\"distance_pips\":" + DoubleToString(distancePips, 1) + ","; jsonPayload = jsonPayload + "\"current_bid\":" + DoubleToString(currentBid, digits) + ","; jsonPayload = jsonPayload + "\"current_ask\":" + DoubleToString(currentAsk, digits); jsonPayload = jsonPayload + "}"; // Debug output of full JSON payload if(Enable_Debug_Prints) Print("[DEBUG] JSON Payload: ", jsonPayload); // Send HTTP POST request to API server 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); // Debug response status if(Enable_Debug_Prints) { if(response == 200) Print("[DEBUG] Alert data sent successfully. Response: 200 OK | Unique ID: ", uniqueId); else Print("[DEBUG] Failed to send alert data. Response: ", response, " | Error: ", GetLastError(), " | Unique ID: ", uniqueId); } } //+------------------------------------------------------------------+ //| Send screenshot as binary multipart form data | //+------------------------------------------------------------------+ void SendScreenshotBinary(string filename, string setupType, double extremePrice, double emaPrice, double distancePips, string symbol, string uniqueId) { if(Enable_Debug_Prints) Print("[DEBUG] Opening screenshot file: ", filename, " | Unique ID: ", uniqueId); // Open screenshot file for reading int filehandle = FileOpen(filename, FILE_READ | FILE_BIN); if(filehandle == INVALID_HANDLE) { if(Enable_Debug_Prints) Print("[DEBUG] Failed to open screenshot file"); return; } // Get file size and read contents int fileSize = (int)FileSize(filehandle); if(Enable_Debug_Prints) Print("[DEBUG] Screenshot file size: ", fileSize, " bytes | Unique ID: ", uniqueId); uchar byteArray[]; ArrayResize(byteArray, fileSize); FileReadArray(filehandle, byteArray, 0, fileSize); FileClose(filehandle); // Create unique boundary for multipart form data string boundary = "----WebKitFormBoundary" + IntegerToString(TimeCurrent()); string headers = "Content-Type: multipart/form-data; boundary=" + boundary + "\r\n"; // Build form data with ALL REQUIRED setup information including unique identifier and filename string formData = "--" + boundary + "\r\n"; formData = formData + "Content-Disposition: form-data; name=\"alert_type\"\r\n\r\n"; formData = formData + setupType + "\r\n"; formData = formData + "--" + boundary + "\r\n"; formData = formData + "Content-Disposition: form-data; name=\"symbol\"\r\n\r\n"; formData = formData + symbol + "\r\n"; formData = formData + "--" + boundary + "\r\n"; formData = formData + "Content-Disposition: form-data; name=\"timestamp\"\r\n\r\n"; formData = formData + IntegerToString(TimeCurrent()) + "\r\n"; formData = formData + "--" + boundary + "\r\n"; formData = formData + "Content-Disposition: form-data; name=\"strategy_name\"\r\n\r\n"; formData = formData + strategyName + "\r\n"; formData = formData + "--" + boundary + "\r\n"; formData = formData + "Content-Disposition: form-data; name=\"strategy_id\"\r\n\r\n"; formData = formData + strategyId + "\r\n"; formData = formData + "--" + boundary + "\r\n"; formData = formData + "Content-Disposition: form-data; name=\"sector\"\r\n\r\n"; formData = formData + GetCustomSector(symbol) + "\r\n"; formData = formData + "--" + boundary + "\r\n"; formData = formData + "Content-Disposition: form-data; name=\"session\"\r\n\r\n"; formData = formData + currentSession + "\r\n"; formData = formData + "--" + boundary + "\r\n"; formData = formData + "Content-Disposition: form-data; name=\"distance_pips\"\r\n\r\n"; formData = formData + DoubleToString(distancePips, 1) + "\r\n"; //this is another one // Add unique identifier field (same as in JSON payload) formData = formData + "--" + boundary + "\r\n"; formData = formData + "Content-Disposition: form-data; name=\"unique_id\"\r\n\r\n"; formData = formData + uniqueId + "\r\n"; // Add screenshot filename field (same as in JSON payload) formData = formData + "--" + boundary + "\r\n"; formData = formData + "Content-Disposition: form-data; name=\"screenshot_filename\"\r\n\r\n"; formData = formData + filename + "\r\n"; // Add allowed_timeframes field (can be empty) formData = formData + "--" + boundary + "\r\n"; formData = 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 = formData + allowedTFsStr + "\r\n"; // Add current timeframe formData = formData + "--" + boundary + "\r\n"; formData = formData + "Content-Disposition: form-data; name=\"timeframe\"\r\n\r\n"; formData = formData + GetTimeframeString() + "\r\n"; // Add screenshot file section with original filename formData = formData + "--" + boundary + "\r\n"; formData = formData + "Content-Disposition: form-data; name=\"screenshot\"; filename=\"" + filename + "\"\r\n"; formData = formData + "Content-Type: image/png\r\n\r\n"; // Convert form data to byte array uchar headerArray[]; StringToCharArray(formData, headerArray, 0, StringLen(formData)); // Create form data footer string footer = "\r\n--" + boundary + "--\r\n"; uchar footerArray[]; StringToCharArray(footer, footerArray, 0, StringLen(footer)); // Combine all parts: headers + image data + footer uchar postData[]; int totalSize = ArraySize(headerArray) + ArraySize(byteArray) + ArraySize(footerArray); ArrayResize(postData, totalSize); // Copy header bytes for(int i = 0; i < ArraySize(headerArray); i++) postData[i] = headerArray[i]; // Copy image bytes int offset = ArraySize(headerArray); for(int i = 0; i < ArraySize(byteArray); i++) postData[offset + i] = byteArray[i]; // Copy footer bytes offset += ArraySize(byteArray); for(int i = 0; i < ArraySize(footerArray); i++) postData[offset + i] = footerArray[i]; // Debug output before sending if(Enable_Debug_Prints) Print("[DEBUG] Sending multipart form data, total size: ", totalSize, " bytes | Unique ID: ", uniqueId); // Send HTTP POST request with multipart data uchar result[]; string resultHeaders; int response = WebRequest("POST", screenshotServerURL, headers, 15000, postData, result, resultHeaders); // Debug response status if(Enable_Debug_Prints) { if(response == 200) Print("[DEBUG] Screenshot sent successfully. Response: 200 OK | Unique ID: ", uniqueId); else if(response == 400) Print("[DEBUG] Bad Request (400). Server rejected the form data. Check required fields. | Unique ID: ", uniqueId); else Print("[DEBUG] Failed to send screenshot. Response: ", response, " | Error: ", GetLastError(), " | Unique ID: ", uniqueId); } } //+------------------------------------------------------------------+ //| Add visual marker (arrow) to chart for setup visualization | //+------------------------------------------------------------------+ void AddVisualMarker(int candle_idx, string type, double distance_pips, double extreme_price, string symbol, long chartid) { ObjectsDeleteAll(0, "EMA_Bounce_"); string obj_name = "EMA_Bounce_" + IntegerToString(TimeCurrent()); // Calculate arrow position: below candle for bullish, above for bearish double arrow_price = type == "Bullish" ? price_data[candle_idx].low - GetPipDivider(symbol) : price_data[candle_idx].high + GetPipDivider(symbol); // Create bullish arrow (green up arrow) if(type == "Bullish") { if(ObjectCreate(chartid, obj_name, OBJ_ARROW_BUY, 0, price_data[candle_idx].time, arrow_price)) { ObjectSetInteger(chartid, obj_name, OBJPROP_ARROWCODE, 241); ObjectSetInteger(chartid, obj_name, OBJPROP_COLOR, clrLime); } } // Create bearish arrow (red down arrow) else { if(ObjectCreate(chartid, obj_name, OBJ_ARROW_SELL, 0, price_data[candle_idx].time, arrow_price)) { ObjectSetInteger(chartid, obj_name, OBJPROP_ARROWCODE, 242); ObjectSetInteger(chartid, obj_name, OBJPROP_COLOR, clrRed); } } } //+------------------------------------------------------------------+ 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); // Get chart dimensions int chart_width = (int)ChartGetInteger(chartid, CHART_WIDTH_IN_PIXELS); int chart_height = (int)ChartGetInteger(chartid, CHART_HEIGHT_IN_PIXELS); // Calculate center position int center_x = 1350 / 2; //chart_width / 2; int center_y = 1080 / 2; //chart_height / 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; } //+------------------------------------------------------------------+ int EstimateTextWidth(string text, int fontSize) { return (int)(StringLen(text) * fontSize * 0.65); } //+------------------------------------------------------------------+ bool bullish(string symbol, int shift) { return iClose(symbol, PERIOD_CURRENT, shift) >= iOpen(symbol, PERIOD_CURRENT, shift); } //+------------------------------------------------------------------+ bool bearish(string symbol, int shift) { return iClose(symbol, PERIOD_CURRENT, shift) < iOpen(symbol, PERIOD_CURRENT, shift); } //+------------------------------------------------------------------+