//+------------------------------------------------------------------+ //| Strategy.mqh - XAUUSD M15 EMA Pullback Continuation Strategy | //| Trend direction on H1, pullback on M15, RSI and ATR confirmation | //+------------------------------------------------------------------+ #ifndef __STRATEGY_MQH__ #define __STRATEGY_MQH__ #include "Signal.mqh" #include "MarketData.mqh" #include "Logger.mqh" #include "Config.mqh" #include "Utilities.mqh" class CStrategy { private: // Indicator handles int m_ema_h1_fast_handle; int m_ema_h1_slow_handle; int m_ema_m15_fast_handle; int m_rsi_m15_handle; int m_atr_m15_handle; // References CMarketData *mp_market_data; CLogger *mp_logger; // Timeframes ENUM_TIMEFRAMES m_entry_timeframe; ENUM_TIMEFRAMES m_trend_timeframe; // Parameters int m_trend_fast_ema_period; int m_trend_slow_ema_period; int m_entry_fast_ema_period; int m_entry_pullback_ema_period; int m_rsi_period; int m_rsi_buy_threshold; int m_rsi_sell_threshold; int m_atr_period; double m_last_atr_value; bool m_use_atr_stops; double m_atr_sl_multiplier; double m_atr_tp_multiplier; public: // Constructor CStrategy(CMarketData *market_data, CLogger *logger) { mp_market_data = market_data; mp_logger = logger; m_ema_h1_fast_handle = INVALID_HANDLE; m_ema_h1_slow_handle = INVALID_HANDLE; m_ema_m15_fast_handle = INVALID_HANDLE; m_rsi_m15_handle = INVALID_HANDLE; m_atr_m15_handle = INVALID_HANDLE; m_last_atr_value = 0.0; m_entry_timeframe = g_strategy_entry_timeframe; m_trend_timeframe = g_strategy_trend_timeframe; m_trend_fast_ema_period = g_trend_fast_ema_period; m_trend_slow_ema_period = g_trend_slow_ema_period; m_entry_fast_ema_period = g_entry_fast_ema_period; m_entry_pullback_ema_period = g_entry_pullback_ema_period; m_rsi_period = g_rsi_period; m_rsi_buy_threshold = g_rsi_buy_threshold; m_rsi_sell_threshold = g_rsi_sell_threshold; m_atr_period = g_atr_period; m_use_atr_stops = g_use_atr_stops; m_atr_sl_multiplier = g_atr_sl_multiplier; m_atr_tp_multiplier = g_atr_tp_multiplier; } // Destructor - clean up indicator handles ~CStrategy() { Cleanup(); } // Initialize strategy and create indicator handles bool Init() { const string symbol = mp_market_data.GetSymbol(); m_ema_h1_fast_handle = iMA(symbol, m_trend_timeframe, m_trend_fast_ema_period, 0, MODE_EMA, PRICE_CLOSE); if(m_ema_h1_fast_handle == INVALID_HANDLE) { if(mp_logger) mp_logger.Error("Failed to create H1 EMA fast indicator"); return false; } m_ema_h1_slow_handle = iMA(symbol, m_trend_timeframe, m_trend_slow_ema_period, 0, MODE_EMA, PRICE_CLOSE); if(m_ema_h1_slow_handle == INVALID_HANDLE) { if(mp_logger) mp_logger.Error("Failed to create H1 EMA slow indicator"); IndicatorRelease(m_ema_h1_fast_handle); m_ema_h1_fast_handle = INVALID_HANDLE; return false; } m_ema_m15_fast_handle = iMA(symbol, m_entry_timeframe, m_entry_fast_ema_period, 0, MODE_EMA, PRICE_CLOSE); if(m_ema_m15_fast_handle == INVALID_HANDLE) { if(mp_logger) mp_logger.Error("Failed to create M15 EMA fast indicator"); IndicatorRelease(m_ema_h1_fast_handle); m_ema_h1_fast_handle = INVALID_HANDLE; IndicatorRelease(m_ema_h1_slow_handle); m_ema_h1_slow_handle = INVALID_HANDLE; return false; } m_rsi_m15_handle = iRSI(symbol, m_entry_timeframe, m_rsi_period, PRICE_CLOSE); if(m_rsi_m15_handle == INVALID_HANDLE) { if(mp_logger) mp_logger.Error("Failed to create M15 RSI indicator"); ReleaseAllHandles(); return false; } m_atr_m15_handle = iATR(symbol, m_entry_timeframe, m_atr_period); if(m_atr_m15_handle == INVALID_HANDLE) { if(mp_logger) mp_logger.Error("Failed to create M15 ATR indicator"); ReleaseAllHandles(); return false; } if(mp_logger) mp_logger.Info("Strategy (M15 EMA Pullback Continuation) initialized successfully"); return true; } // Generate trading signal based on H1 trend and M15 pullback continuation E_SIGNAL GetSignal() { const string symbol = mp_market_data.GetSymbol(); double ema_h1_fast = iGetIndicatorValue(m_ema_h1_fast_handle, 1); double ema_h1_slow = iGetIndicatorValue(m_ema_h1_slow_handle, 1); if(ema_h1_fast == 0.0 || ema_h1_slow == 0.0) return SIGNAL_NONE; double ema_m15_fast_last = iGetIndicatorValue(m_ema_m15_fast_handle, 1); if(ema_m15_fast_last == 0.0) return SIGNAL_NONE; double close_last = iClose(symbol, m_entry_timeframe, 1); double open_last = iOpen(symbol, m_entry_timeframe, 1); if(close_last <= 0.0 || open_last <= 0.0) return SIGNAL_NONE; if(ema_h1_fast > ema_h1_slow) { bool entry_condition = (close_last > ema_m15_fast_last && close_last > open_last); if(entry_condition) { if(mp_logger && g_debug_mode) mp_logger.Info(StringFormat("BUY Signal: H1 EMA50=%.5f > EMA200=%.5f, M15 close=%.5f > EMA20=%.5f", ema_h1_fast, ema_h1_slow, close_last, ema_m15_fast_last)); return SIGNAL_BUY; } } else if(ema_h1_fast < ema_h1_slow) { bool entry_condition = (close_last < ema_m15_fast_last && close_last < open_last); if(entry_condition) { if(mp_logger && g_debug_mode) mp_logger.Info(StringFormat("SELL Signal: H1 EMA50=%.5f < EMA200=%.5f, M15 close=%.5f < EMA20=%.5f", ema_h1_fast, ema_h1_slow, close_last, ema_m15_fast_last)); return SIGNAL_SELL; } } return SIGNAL_NONE; } double GetLastAtrValue() const { return m_last_atr_value; } void Cleanup() { ReleaseAllHandles(); } private: void ReleaseAllHandles() { if(m_ema_h1_fast_handle != INVALID_HANDLE) { IndicatorRelease(m_ema_h1_fast_handle); m_ema_h1_fast_handle = INVALID_HANDLE; } if(m_ema_h1_slow_handle != INVALID_HANDLE) { IndicatorRelease(m_ema_h1_slow_handle); m_ema_h1_slow_handle = INVALID_HANDLE; } if(m_ema_m15_fast_handle != INVALID_HANDLE) { IndicatorRelease(m_ema_m15_fast_handle); m_ema_m15_fast_handle = INVALID_HANDLE; } if(m_rsi_m15_handle != INVALID_HANDLE) { IndicatorRelease(m_rsi_m15_handle); m_rsi_m15_handle = INVALID_HANDLE; } if(m_atr_m15_handle != INVALID_HANDLE) { IndicatorRelease(m_atr_m15_handle); m_atr_m15_handle = INVALID_HANDLE; } } double iGetIndicatorValue(int handle, int shift) { if(handle == INVALID_HANDLE) return 0.0; double value[1]; ArraySetAsSeries(value, true); if(CopyBuffer(handle, 0, shift, 1, value) <= 0) return 0.0; return value[0]; } }; #endif //__STRATEGY_MQH__