//+------------------------------------------------------------------+ //| SignalDetector.mqh | //| Universal Buffer Reader EA v2.0 | //+------------------------------------------------------------------+ #property copyright "Copyright 2025" #property link "" #property version "1.00" #property strict //+------------------------------------------------------------------+ //| CSignalDetector - Reads indicator buffers and detects signals | //+------------------------------------------------------------------+ class CSignalDetector { private: string m_indicator_name; int m_indicator_handle; // Signal buffers int m_buy_signal_buffer; int m_sell_signal_buffer; // SL/TP buffers (separate for BUY/SELL) int m_buy_sl_buffer; int m_buy_tp_buffers[]; int m_sell_sl_buffer; int m_sell_tp_buffers[]; // ATR settings int m_sltp_mode; int m_atr_period; int m_atr_handle; double m_sl_atr_multiplier; double m_tp_atr_multiplier; int m_min_tp_pips; // Symbol info string m_symbol; double m_pip_value; int m_digits; // Logging bool m_enable_debug; public: //+------------------------------------------------------------------+ //| Signal data structure with multiple TPs | //+------------------------------------------------------------------+ struct SignalData { bool has_signal; bool is_buy; double signal_price; double sl_price; double tp_prices[]; int tp_count; bool used_atr_fallback; }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CSignalDetector() { m_indicator_name = ""; m_indicator_handle = INVALID_HANDLE; m_buy_signal_buffer = 0; m_sell_signal_buffer = 1; m_buy_sl_buffer = 2; m_sell_sl_buffer = 6; m_sltp_mode = 0; m_atr_period = 14; m_atr_handle = INVALID_HANDLE; m_sl_atr_multiplier = 1.5; m_tp_atr_multiplier = 3.0; m_min_tp_pips = 300; m_symbol = ""; m_pip_value = 0; m_digits = 0; m_enable_debug = false; ArrayResize(m_buy_tp_buffers, 0); ArrayResize(m_sell_tp_buffers, 0); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ ~CSignalDetector() { Deinitialize(); } //+------------------------------------------------------------------+ //| Initialize indicator handles | //+------------------------------------------------------------------+ bool Initialize() { Print("[SignalDetector] Initializing..."); // Initialize custom indicator handle if(m_indicator_name != "" && m_indicator_name != "My_Indicator") { m_indicator_handle = iCustom(m_symbol, PERIOD_CURRENT, m_indicator_name); if(m_indicator_handle == INVALID_HANDLE) { int error = GetLastError(); Print("[ERROR] Failed to create indicator handle for: ", m_indicator_name, " Error code: ", error); return false; } Print("[SignalDetector] Custom indicator handle created: ", m_indicator_name); } else { Print("[SignalDetector] Using ATR mode only (no custom indicator)"); } // Initialize ATR handle m_atr_handle = iATR(m_symbol, PERIOD_CURRENT, m_atr_period); if(m_atr_handle == INVALID_HANDLE) { int error = GetLastError(); Print("[ERROR] Failed to create ATR handle. Error code: ", error); return false; } Print("[SignalDetector] ATR handle created (Period: ", m_atr_period, ")"); Print("[SignalDetector] Initialization complete"); return true; } //+------------------------------------------------------------------+ //| Deinitialize indicator handles | //+------------------------------------------------------------------+ void Deinitialize() { Print("[SignalDetector] Deinitializing..."); if(m_indicator_handle != INVALID_HANDLE) { IndicatorRelease(m_indicator_handle); m_indicator_handle = INVALID_HANDLE; Print("[SignalDetector] Custom indicator handle released"); } if(m_atr_handle != INVALID_HANDLE) { IndicatorRelease(m_atr_handle); m_atr_handle = INVALID_HANDLE; Print("[SignalDetector] ATR handle released"); } ArrayResize(m_buy_tp_buffers, 0); ArrayResize(m_sell_tp_buffers, 0); Print("[SignalDetector] Deinitialization complete"); } //+------------------------------------------------------------------+ //| Set parameters | //+------------------------------------------------------------------+ void SetParameters( string indicator_name, int buy_signal_buffer, int sell_signal_buffer, int buy_sl_buffer, int sell_sl_buffer, int sltp_mode, int atr_period, double sl_atr_multiplier, double tp_atr_multiplier, int min_tp_pips, double pip_value, int digits, string symbol, bool enable_debug = false ) { m_indicator_name = indicator_name; m_buy_signal_buffer = buy_signal_buffer; m_sell_signal_buffer = sell_signal_buffer; m_buy_sl_buffer = buy_sl_buffer; m_sell_sl_buffer = sell_sl_buffer; m_sltp_mode = sltp_mode; m_atr_period = atr_period; m_sl_atr_multiplier = sl_atr_multiplier; m_tp_atr_multiplier = tp_atr_multiplier; m_min_tp_pips = min_tp_pips; m_pip_value = pip_value; m_digits = digits; m_symbol = symbol; m_enable_debug = enable_debug; } //+------------------------------------------------------------------+ //| Set debug mode | //+------------------------------------------------------------------+ void SetDebugMode(bool enable_debug) { m_enable_debug = enable_debug; } //+------------------------------------------------------------------+ //| Set TP buffers (can have multiple) | //+------------------------------------------------------------------+ void SetBuyTPBuffers(int &buffers[]) { ArrayCopy(m_buy_tp_buffers, buffers); } //+------------------------------------------------------------------+ //| Set TP buffers (can have multiple) | //+------------------------------------------------------------------+ void SetSellTPBuffers(int &buffers[]) { ArrayCopy(m_sell_tp_buffers, buffers); } //+------------------------------------------------------------------+ //| Detect signal from indicator buffers | //+------------------------------------------------------------------+ SignalData DetectSignal(int bar_shift = 1) { SignalData result; result.has_signal = false; result.is_buy = false; result.signal_price = 0; result.sl_price = 0; result.tp_count = 0; result.used_atr_fallback = false; ArrayResize(result.tp_prices, 0); if(m_enable_debug) { Print("[SignalDetector] Detecting signal at bar shift: ", bar_shift); } // Read buy/sell signal buffers double buy_signal = GetIndicatorBufferValue(m_buy_signal_buffer, bar_shift); double sell_signal = GetIndicatorBufferValue(m_sell_signal_buffer, bar_shift); // Determine signal type bool has_buy = (buy_signal != EMPTY_VALUE && buy_signal != 0); bool has_sell = (sell_signal != EMPTY_VALUE && sell_signal != 0); if(!has_buy && !has_sell) { if(m_enable_debug) { Print("[SignalDetector] No signal detected"); } return result; } // Validate: Both buy and sell signals should not be present if(has_buy && has_sell) { Print("[WARNING] Both BUY and SELL signals detected. Using BUY signal."); has_sell = false; } result.has_signal = true; result.is_buy = has_buy; result.signal_price = has_buy ? buy_signal : sell_signal; if(m_enable_debug) { Print("[SignalDetector] ", (has_buy ? "BUY" : "SELL"), " signal detected at: ", DoubleToString(result.signal_price, m_digits)); } // Calculate SL/TP based on mode if(m_sltp_mode == 1) { if(m_enable_debug) { Print("[SignalDetector] Using Mode 1: Indicator SL/TP buffers"); } // Try indicator buffers first double sl_buffer = has_buy ? GetIndicatorBufferValue(m_buy_sl_buffer, bar_shift) : GetIndicatorBufferValue(m_sell_sl_buffer, bar_shift); if(IsValidPrice(sl_buffer)) { result.sl_price = sl_buffer; if(m_enable_debug) { Print("[SignalDetector] SL from indicator: ", DoubleToString(result.sl_price, m_digits)); } } else { if(m_enable_debug) { Print("[SignalDetector] SL buffer empty, will use ATR fallback"); } } // Read TP buffers int tp_count; if(has_buy) tp_count = ArraySize(m_buy_tp_buffers); else tp_count = ArraySize(m_sell_tp_buffers); if(tp_count > 0) { ArrayResize(result.tp_prices, tp_count); result.tp_count = 0; for(int i = 0; i < tp_count; i++) { int buffer_index; if(has_buy) buffer_index = m_buy_tp_buffers[i]; else buffer_index = m_sell_tp_buffers[i]; double tp_buffer = GetIndicatorBufferValue(buffer_index, bar_shift); if(IsValidPrice(tp_buffer)) { result.tp_prices[result.tp_count] = tp_buffer; result.tp_count++; if(m_enable_debug) { Print("[SignalDetector] TP", (i + 1), " from indicator:", DoubleToString(tp_buffer, m_digits)); } } } } } else { if(m_enable_debug) { Print("[SignalDetector] Using Mode 0: ATR-based SL/TP"); } } // Fall back to ATR if SL/TP not set if(result.sl_price == 0 || result.tp_count == 0) { if(m_enable_debug) { Print("[SignalDetector] SL or TP not set from indicator, using ATR fallback"); } double atr = GetATRValue(bar_shift); if(atr > 0) { double open_price = SymbolInfoDouble(m_symbol, SYMBOL_BID); result.sl_price = CalculateATRSL(has_buy, atr, open_price); CalculateATRTPs(has_buy, atr, open_price, result.tp_prices, result.tp_count); result.used_atr_fallback = true; if(m_enable_debug) { Print("[SignalDetector] ATR value: ", DoubleToString(atr, m_digits)); Print("[SignalDetector] SL from ATR: ", DoubleToString(result.sl_price, m_digits)); for(int i = 0; i < result.tp_count; i++) { Print("[SignalDetector] TP", (i + 1), " from ATR: ", DoubleToString(result.tp_prices[i], m_digits)); } } } else { Print("[ERROR] ATR value is 0, cannot calculate SL/TP"); } } // Apply minimum TP ApplyMinimumTP(has_buy, result.tp_prices, result.tp_count, result.signal_price); if(m_enable_debug) { Print("[SignalDetector] Signal detection complete. SL: ", DoubleToString(result.sl_price, m_digits), ", TPs: ", result.tp_count, ", ATR fallback: ", (result.used_atr_fallback ? "Yes" : "No")); } return result; } //+------------------------------------------------------------------+ //| Get ATR value | //+------------------------------------------------------------------+ double GetATRValue(int bar_shift = 1) { if(m_atr_handle == INVALID_HANDLE) { Print("[ERROR] ATR handle is invalid"); return 0; } double atr[]; ArraySetAsSeries(atr, true); int copied = CopyBuffer(m_atr_handle, 0, bar_shift, 1, atr); if(copied <= 0) { int error = GetLastError(); Print("[ERROR] Failed to copy ATR buffer. Error code: ", error); return 0; } if(atr[0] <= 0) { if(m_enable_debug) { Print("[SignalDetector] ATR value is 0 or negative: ", atr[0]); } return 0; } return atr[0]; } private: //+------------------------------------------------------------------+ //| Get indicator buffer value | //+------------------------------------------------------------------+ double GetIndicatorBufferValue(int buffer_index, int bar_shift) { if(m_indicator_handle == INVALID_HANDLE) { if(m_enable_debug) { Print("[SignalDetector] Indicator handle is invalid"); } return EMPTY_VALUE; } if(buffer_index < 0) { Print("[ERROR] Invalid buffer index: ", buffer_index); return EMPTY_VALUE; } double buffer[]; ArraySetAsSeries(buffer, true); int copied = CopyBuffer(m_indicator_handle, buffer_index, bar_shift, 1, buffer); if(copied <= 0) { if(m_enable_debug) { int error = GetLastError(); Print("[SignalDetector] Failed to copy buffer ", buffer_index, ". Error code: ", error); } return EMPTY_VALUE; } return buffer[0]; } //+------------------------------------------------------------------+ //| Calculate ATR-based SL | //+------------------------------------------------------------------+ double CalculateATRSL(bool is_buy, double atr_value, double open_price) { if(atr_value <= 0) return 0; if(is_buy) { return NormalizeDouble(open_price - atr_value * m_sl_atr_multiplier, m_digits); } else { return NormalizeDouble(open_price + atr_value * m_sl_atr_multiplier, m_digits); } } //+------------------------------------------------------------------+ //| Calculate ATR-based TPs | //+------------------------------------------------------------------+ void CalculateATRTPs(bool is_buy, double atr_value, double open_price, double &tp_prices[], int &tp_count) { if(atr_value <= 0) return; // Default to 3 TPs if using ATR int num_tps = 3; ArrayResize(tp_prices, num_tps); tp_count = num_tps; for(int i = 0; i < num_tps; i++) { double multiplier = m_tp_atr_multiplier * (i + 1); if(is_buy) { tp_prices[i] = NormalizeDouble(open_price + atr_value * multiplier, m_digits); } else { tp_prices[i] = NormalizeDouble(open_price - atr_value * multiplier, m_digits); } } } //+------------------------------------------------------------------+ //| Apply minimum TP to all TPs | //+------------------------------------------------------------------+ void ApplyMinimumTP(bool is_buy, double &tp_prices[], int tp_count, double open_price) { if(m_min_tp_pips <= 0) { if(m_enable_debug) { Print("[SignalDetector] Minimum TP disabled (0 pips)"); } return; } if(tp_count == 0) { if(m_enable_debug) { Print("[SignalDetector] No TPs to apply minimum TP"); } return; } int adjusted_count = 0; for(int i = 0; i < tp_count; i++) { if(is_buy) { double min_tp = NormalizeDouble(open_price + m_min_tp_pips * m_pip_value, m_digits); if(tp_prices[i] < min_tp) { if(m_enable_debug) { Print("[SignalDetector] TP", (i + 1), " adjusted to minimum: ", DoubleToString(tp_prices[i], m_digits), " -> ", DoubleToString(min_tp, m_digits)); } tp_prices[i] = min_tp; adjusted_count++; } } else { double min_tp = NormalizeDouble(open_price - m_min_tp_pips * m_pip_value, m_digits); if(tp_prices[i] > min_tp) { if(m_enable_debug) { Print("[SignalDetector] TP", (i + 1), " adjusted to minimum: ", DoubleToString(tp_prices[i], m_digits), " -> ", DoubleToString(min_tp, m_digits)); } tp_prices[i] = min_tp; adjusted_count++; } } } if(m_enable_debug && adjusted_count > 0) { Print("[SignalDetector] Minimum TP applied to ", adjusted_count, " TP(s)"); } } //+------------------------------------------------------------------+ //| Check if price is valid | //+------------------------------------------------------------------+ bool IsValidPrice(double price) { return (price != EMPTY_VALUE && price != 0 && price > 0); } }; //+------------------------------------------------------------------+