//+------------------------------------------------------------------+ //| EntrySystem.mqh | //| Entry System Module v1.3 | //| Fixed: Recursive GetValidATR bug | //+------------------------------------------------------------------+ #ifndef ENTRY_SYSTEM_MQH #define ENTRY_SYSTEM_MQH #include "DataTypes.mqh" #include //+------------------------------------------------------------------+ //| Entry System Class | //+------------------------------------------------------------------+ class CEntrySystem { private: //--- Configuration EntrySystemConfig m_config; //--- Indicator handles int m_ma_fast_handle; int m_ma_slow_handle; int m_ma_filter_handle; int m_rsi_handle; int m_adx_handle; int m_bb_handle; int m_macd_handle; int m_stoch_handle; //--- Signal tracking datetime m_last_signal_time; ENUM_SIGNAL_TYPE m_last_signal_type; int m_consecutive_signals; //--- Entry methods EntrySignal CheckMACrossover(const MarketConditions &market); EntrySignal CheckMAPullback(const MarketConditions &market); EntrySignal CheckMomentum(const MarketConditions &market); EntrySignal CheckContrarian(const MarketConditions &market); EntrySignal CheckBreakout(const MarketConditions &market); EntrySignal CheckMeanReversion(const MarketConditions &market); EntrySignal CheckMultiStrategy(const MarketConditions &market); //--- Helper methods bool IsNewBar(); bool CheckFilter(ENUM_SIGNAL_TYPE signal_type); double CalculateSignalStrength(const EntrySignal &signal); void AddTechnicalLevels(EntrySignal &signal, const MarketConditions &market); double GetValidATR(const MarketConditions &market); double GetSafeATR(const MarketConditions &market); public: CEntrySystem(); ~CEntrySystem(); //--- Initialization bool Initialize(const EntrySystemConfig &config); //--- Main method EntrySignal CheckSignal(const MarketConditions &market); //--- Signal validation bool ValidateSignal(const EntrySignal &signal, const MarketConditions &market); //--- Getters int GetConsecutiveSignals() { return m_consecutive_signals; } datetime GetLastSignalTime() { return m_last_signal_time; } }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CEntrySystem::CEntrySystem() { m_ma_fast_handle = INVALID_HANDLE; m_ma_slow_handle = INVALID_HANDLE; m_ma_filter_handle = INVALID_HANDLE; m_rsi_handle = INVALID_HANDLE; m_adx_handle = INVALID_HANDLE; m_bb_handle = INVALID_HANDLE; m_macd_handle = INVALID_HANDLE; m_stoch_handle = INVALID_HANDLE; m_last_signal_time = 0; m_last_signal_type = SIGNAL_NONE; m_consecutive_signals = 0; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CEntrySystem::~CEntrySystem() { //--- Release indicator handles if(m_ma_fast_handle != INVALID_HANDLE) IndicatorRelease(m_ma_fast_handle); if(m_ma_slow_handle != INVALID_HANDLE) IndicatorRelease(m_ma_slow_handle); if(m_ma_filter_handle != INVALID_HANDLE) IndicatorRelease(m_ma_filter_handle); if(m_rsi_handle != INVALID_HANDLE) IndicatorRelease(m_rsi_handle); if(m_adx_handle != INVALID_HANDLE) IndicatorRelease(m_adx_handle); if(m_bb_handle != INVALID_HANDLE) IndicatorRelease(m_bb_handle); if(m_macd_handle != INVALID_HANDLE) IndicatorRelease(m_macd_handle); if(m_stoch_handle != INVALID_HANDLE) IndicatorRelease(m_stoch_handle); } //+------------------------------------------------------------------+ //| Initialize entry system | //+------------------------------------------------------------------+ bool CEntrySystem::Initialize(const EntrySystemConfig &config) { m_config = config; //--- Create necessary indicators based on entry mode switch(m_config.entry_mode) { case ENTRY_MA_CROSS: case ENTRY_MA_PULLBACK: m_ma_fast_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_EMA, PRICE_CLOSE); m_ma_slow_handle = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE); m_ma_filter_handle = iMA(_Symbol, PERIOD_CURRENT, 200, 0, MODE_SMA, PRICE_CLOSE); break; case ENTRY_MOMENTUM: m_rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); m_adx_handle = iADX(_Symbol, PERIOD_CURRENT, 14); m_macd_handle = iMACD(_Symbol, PERIOD_CURRENT, 12, 26, 9, PRICE_CLOSE); break; case ENTRY_MEAN_REVERSION: m_bb_handle = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE); m_rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); break; case ENTRY_BREAKOUT: m_adx_handle = iADX(_Symbol, PERIOD_CURRENT, 14); m_bb_handle = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE); break; case ENTRY_MULTI_STRATEGY: //--- Create all indicators for multi-strategy m_ma_fast_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_EMA, PRICE_CLOSE); m_ma_slow_handle = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE); m_rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); m_adx_handle = iADX(_Symbol, PERIOD_CURRENT, 14); m_bb_handle = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE); m_macd_handle = iMACD(_Symbol, PERIOD_CURRENT, 12, 26, 9, PRICE_CLOSE); m_stoch_handle = iStochastic(_Symbol, PERIOD_CURRENT, 5, 3, 3, MODE_SMA, STO_LOWHIGH); break; } Print("EntrySystem initialized: Mode=", EnumToString(m_config.entry_mode)); return true; } //+------------------------------------------------------------------+ //| Enhanced CheckSignal with comprehensive validation | //+------------------------------------------------------------------+ EntrySignal CEntrySystem::CheckSignal(const MarketConditions &market) { EntrySignal signal; signal.signal_type = SIGNAL_NONE; signal.entry_price = 0; signal.stop_loss_distance = 0; signal.take_profit_distance = 0; signal.confidence = 0; signal.comment = ""; signal.signal_time = TimeCurrent(); signal.timeframe = Period(); //--- Validate market conditions first double atr = GetSafeATR(market); if(atr <= 0) { Print("EntrySystem: Invalid ATR, skipping signal check"); return signal; } //--- Check if enough time passed since last signal if(TimeCurrent() - m_last_signal_time < m_config.min_time_between * 60) return signal; //--- Only check on new bar for most strategies if(!IsNewBar() && m_config.entry_mode != ENTRY_BREAKOUT) return signal; //--- Check signals based on entry mode switch(m_config.entry_mode) { case ENTRY_MA_CROSS: signal = CheckMACrossover(market); break; case ENTRY_MA_PULLBACK: signal = CheckMAPullback(market); break; case ENTRY_MOMENTUM: signal = CheckMomentum(market); break; case ENTRY_CONTRARIAN: signal = CheckContrarian(market); break; case ENTRY_BREAKOUT: signal = CheckBreakout(market); break; case ENTRY_MEAN_REVERSION: signal = CheckMeanReversion(market); break; case ENTRY_MULTI_STRATEGY: signal = CheckMultiStrategy(market); break; } //--- Enhanced validation if(signal.signal_type != SIGNAL_NONE) { // Critical: Ensure stop distance is valid if(signal.stop_loss_distance <= 0) { Print("EntrySystem: Invalid stop distance (", signal.stop_loss_distance, ") in signal: ", signal.comment); // Try to calculate a default stop distance double default_atr = GetSafeATR(market); if(default_atr > 0) { signal.stop_loss_distance = default_atr * 2.0; // 2 ATR default Print("EntrySystem: Applied default stop distance: ", signal.stop_loss_distance); } else { // Final fallback double min_stop = 30 * SymbolInfoDouble(_Symbol, SYMBOL_POINT); // 30 points minimum signal.stop_loss_distance = min_stop; Print("EntrySystem: Applied minimum stop distance: ", signal.stop_loss_distance); } } // Ensure take profit is also valid if(signal.take_profit_distance <= 0) { signal.take_profit_distance = signal.stop_loss_distance * 1.5; // Default 1.5:1 RR Print("EntrySystem: Applied default TP distance: ", signal.take_profit_distance); } // Validate minimum stop distance against broker requirements double min_stop_level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * SymbolInfoDouble(_Symbol, SYMBOL_POINT); if(signal.stop_loss_distance < min_stop_level * 1.1) // 10% buffer { signal.stop_loss_distance = min_stop_level * 1.1; Print("EntrySystem: Adjusted stop distance to meet broker minimum: ", signal.stop_loss_distance); } // Final validation if(signal.stop_loss_distance <= 0 || signal.take_profit_distance <= 0) { Print("EntrySystem: Final validation failed - rejecting signal"); signal.signal_type = SIGNAL_NONE; return signal; } // Standard validation if(ValidateSignal(signal, market)) { //--- Add technical levels to signal AddTechnicalLevels(signal, market); //--- Calculate signal strength signal.confidence = CalculateSignalStrength(signal); //--- Update tracking m_last_signal_time = TimeCurrent(); m_last_signal_type = signal.signal_type; if(signal.signal_type == m_last_signal_type) m_consecutive_signals++; else m_consecutive_signals = 1; // Log successful signal Print("EntrySystem: Valid signal generated - Type: ", (signal.signal_type == SIGNAL_BUY ? "BUY" : "SELL"), ", SL: ", signal.stop_loss_distance / _Point, " points", ", TP: ", signal.take_profit_distance / _Point, " points"); } else { signal.signal_type = SIGNAL_NONE; } } return signal; } //+------------------------------------------------------------------+ //| Check MA crossover signal | //+------------------------------------------------------------------+ EntrySignal CEntrySystem::CheckMACrossover(const MarketConditions &market) { EntrySignal signal; signal.signal_type = SIGNAL_NONE; //--- Get MA values double ma_fast[3], ma_slow[3], ma_filter[1]; if(CopyBuffer(m_ma_fast_handle, 0, 0, 3, ma_fast) < 3) return signal; if(CopyBuffer(m_ma_slow_handle, 0, 0, 3, ma_slow) < 3) return signal; if(m_ma_filter_handle != INVALID_HANDLE) { if(CopyBuffer(m_ma_filter_handle, 0, 0, 1, ma_filter) < 1) return signal; } //--- Check for crossover bool bullish_cross = (ma_fast[2] <= ma_slow[2] && ma_fast[1] > ma_slow[1]); bool bearish_cross = (ma_fast[2] >= ma_slow[2] && ma_fast[1] < ma_slow[1]); //--- Apply filter double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); bool above_filter = (m_ma_filter_handle == INVALID_HANDLE) || (current_price > ma_filter[0]); bool below_filter = (m_ma_filter_handle == INVALID_HANDLE) || (current_price < ma_filter[0]); //--- Ensure we have valid market volatility double atr = GetSafeATR(market); if(bullish_cross && above_filter) { signal.signal_type = SIGNAL_BUY; signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); signal.stop_loss_distance = atr * 2.0; // 2 ATR stop signal.take_profit_distance = atr * 3.0; // 3 ATR target signal.comment = "MA Bullish Cross"; signal.confidence = 70; // Debug output Print("MA Cross Signal: SL Distance = ", signal.stop_loss_distance, " (", signal.stop_loss_distance / _Point, " points), ATR = ", atr); } else if(bearish_cross && below_filter) { signal.signal_type = SIGNAL_SELL; signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); signal.stop_loss_distance = atr * 2.0; // 2 ATR stop signal.take_profit_distance = atr * 3.0; // 3 ATR target signal.comment = "MA Bearish Cross"; signal.confidence = 70; // Debug output Print("MA Cross Signal: SL Distance = ", signal.stop_loss_distance, " (", signal.stop_loss_distance / _Point, " points), ATR = ", atr); } return signal; } //+------------------------------------------------------------------+ //| Check MA pullback signal | //+------------------------------------------------------------------+ EntrySignal CEntrySystem::CheckMAPullback(const MarketConditions &market) { EntrySignal signal; signal.signal_type = SIGNAL_NONE; //--- Get MA values double ma_fast[2], ma_slow[2]; if(CopyBuffer(m_ma_fast_handle, 0, 0, 2, ma_fast) < 2) return signal; if(CopyBuffer(m_ma_slow_handle, 0, 0, 2, ma_slow) < 2) return signal; double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); //--- Check trend direction bool uptrend = ma_fast[0] > ma_slow[0] && ma_fast[1] > ma_slow[1]; bool downtrend = ma_fast[0] < ma_slow[0] && ma_fast[1] < ma_slow[1]; //--- Check for pullback to fast MA double distance_to_ma = MathAbs(current_price - ma_fast[0]); double atr = GetSafeATR(market); if(uptrend && current_price > ma_slow[0] && current_price <= ma_fast[0] + atr * 0.1 && market.momentum > 40) // Not oversold { signal.signal_type = SIGNAL_BUY; signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); signal.stop_loss_distance = MathMax(current_price - ma_slow[0] + atr * 0.5, atr * 1.5); signal.take_profit_distance = atr * 3.0; signal.comment = "MA Pullback Buy"; signal.confidence = 75; } else if(downtrend && current_price < ma_slow[0] && current_price >= ma_fast[0] - atr * 0.1 && market.momentum < 60) // Not overbought { signal.signal_type = SIGNAL_SELL; signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); signal.stop_loss_distance = MathMax(ma_slow[0] - current_price + atr * 0.5, atr * 1.5); signal.take_profit_distance = atr * 3.0; signal.comment = "MA Pullback Sell"; signal.confidence = 75; } return signal; } //+------------------------------------------------------------------+ //| Check momentum signal | //+------------------------------------------------------------------+ EntrySignal CEntrySystem::CheckMomentum(const MarketConditions &market) { EntrySignal signal; signal.signal_type = SIGNAL_NONE; //--- Get indicator values double rsi[2], adx[1], adx_di_plus[1], adx_di_minus[1]; double macd_main[2], macd_signal[2]; if(CopyBuffer(m_rsi_handle, 0, 0, 2, rsi) < 2) return signal; if(CopyBuffer(m_adx_handle, 0, 0, 1, adx) < 1) return signal; if(CopyBuffer(m_adx_handle, 1, 0, 1, adx_di_plus) < 1) return signal; if(CopyBuffer(m_adx_handle, 2, 0, 1, adx_di_minus) < 1) return signal; if(CopyBuffer(m_macd_handle, 0, 0, 2, macd_main) < 2) return signal; if(CopyBuffer(m_macd_handle, 1, 0, 2, macd_signal) < 2) return signal; //--- Strong trend requirement if(adx[0] < 25) return signal; double atr = GetSafeATR(market); //--- Bullish momentum if(rsi[0] > 50 && rsi[0] < 70 && // Not overbought rsi[0] > rsi[1] && // Rising RSI adx_di_plus[0] > adx_di_minus[0] && macd_main[0] > macd_signal[0] && macd_main[0] > macd_main[1]) // MACD rising { signal.signal_type = SIGNAL_BUY; signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); signal.stop_loss_distance = atr * 1.5; signal.take_profit_distance = atr * 3.0; signal.comment = "Momentum Buy"; signal.confidence = adx[0] * 2; // Higher ADX = higher confidence Print("Momentum Buy Signal: ATR=", atr, " SL=", signal.stop_loss_distance); } //--- Bearish momentum else if(rsi[0] < 50 && rsi[0] > 30 && // Not oversold rsi[0] < rsi[1] && // Falling RSI adx_di_minus[0] > adx_di_plus[0] && macd_main[0] < macd_signal[0] && macd_main[0] < macd_main[1]) // MACD falling { signal.signal_type = SIGNAL_SELL; signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); signal.stop_loss_distance = atr * 1.5; signal.take_profit_distance = atr * 3.0; signal.comment = "Momentum Sell"; signal.confidence = adx[0] * 2; Print("Momentum Sell Signal: ATR=", atr, " SL=", signal.stop_loss_distance); } return signal; } //+------------------------------------------------------------------+ //| Check breakout signal | //+------------------------------------------------------------------+ EntrySignal CEntrySystem::CheckBreakout(const MarketConditions &market) { EntrySignal signal; signal.signal_type = SIGNAL_NONE; //--- Get Bollinger Bands double bb_upper[2], bb_lower[2], bb_middle[1]; if(CopyBuffer(m_bb_handle, 1, 0, 2, bb_upper) < 2) return signal; if(CopyBuffer(m_bb_handle, 2, 0, 2, bb_lower) < 2) return signal; if(CopyBuffer(m_bb_handle, 0, 0, 1, bb_middle) < 1) return signal; double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); double prev_close = iClose(_Symbol, PERIOD_CURRENT, 1); double atr = GetSafeATR(market); //--- Check for breakout with volume confirmation long volume = iVolume(_Symbol, PERIOD_CURRENT, 0); long avg_volume = 0; for(int i = 1; i <= 20; i++) avg_volume += iVolume(_Symbol, PERIOD_CURRENT, i); avg_volume /= 20; bool volume_surge = volume > avg_volume * 1.5; //--- Upper band breakout if(current_price > bb_upper[0] && prev_close <= bb_upper[1] && volume_surge) { signal.signal_type = SIGNAL_BUY; signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); signal.stop_loss_distance = MathMax(current_price - bb_middle[0], atr * 1.5); signal.take_profit_distance = (bb_upper[0] - bb_middle[0]) * 2; signal.comment = "BB Upper Breakout"; signal.confidence = 65 + (volume_surge ? 15 : 0); } //--- Lower band breakout else if(current_price < bb_lower[0] && prev_close >= bb_lower[1] && volume_surge) { signal.signal_type = SIGNAL_SELL; signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); signal.stop_loss_distance = MathMax(bb_middle[0] - current_price, atr * 1.5); signal.take_profit_distance = (bb_middle[0] - bb_lower[0]) * 2; signal.comment = "BB Lower Breakout"; signal.confidence = 65 + (volume_surge ? 15 : 0); } return signal; } //+------------------------------------------------------------------+ //| Check mean reversion signal | //+------------------------------------------------------------------+ EntrySignal CEntrySystem::CheckMeanReversion(const MarketConditions &market) { EntrySignal signal; signal.signal_type = SIGNAL_NONE; //--- Best in ranging markets if(market.condition != MARKET_RANGING) return signal; //--- Get indicators double bb_upper[1], bb_lower[1], bb_middle[1]; double rsi[1]; if(CopyBuffer(m_bb_handle, 1, 0, 1, bb_upper) < 1) return signal; if(CopyBuffer(m_bb_handle, 2, 0, 1, bb_lower) < 1) return signal; if(CopyBuffer(m_bb_handle, 0, 0, 1, bb_middle) < 1) return signal; if(CopyBuffer(m_rsi_handle, 0, 0, 1, rsi) < 1) return signal; double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); double atr = GetSafeATR(market); //--- Oversold bounce if(current_price <= bb_lower[0] && rsi[0] < 30) { signal.signal_type = SIGNAL_BUY; signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); signal.stop_loss_distance = atr * 1.0; signal.take_profit_distance = MathMax(bb_middle[0] - current_price, atr * 1.5); signal.comment = "Mean Reversion Buy"; signal.confidence = 70 + (30 - rsi[0]); // More oversold = higher confidence } //--- Overbought reversal else if(current_price >= bb_upper[0] && rsi[0] > 70) { signal.signal_type = SIGNAL_SELL; signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); signal.stop_loss_distance = atr * 1.0; signal.take_profit_distance = MathMax(current_price - bb_middle[0], atr * 1.5); signal.comment = "Mean Reversion Sell"; signal.confidence = 70 + (rsi[0] - 70); // More overbought = higher confidence } return signal; } //+------------------------------------------------------------------+ //| Check multi-strategy signal | //+------------------------------------------------------------------+ EntrySignal CEntrySystem::CheckMultiStrategy(const MarketConditions &market) { EntrySignal signal; signal.signal_type = SIGNAL_NONE; //--- Collect signals from all strategies EntrySignal signals[5]; signals[0] = CheckMACrossover(market); signals[1] = CheckMomentum(market); signals[2] = CheckBreakout(market); signals[3] = CheckMeanReversion(market); signals[4] = CheckMAPullback(market); //--- Count buy/sell signals int buy_count = 0, sell_count = 0; double buy_confidence = 0, sell_confidence = 0; double atr = GetSafeATR(market); for(int i = 0; i < 5; i++) { if(signals[i].signal_type == SIGNAL_BUY) { buy_count++; buy_confidence += signals[i].confidence; } else if(signals[i].signal_type == SIGNAL_SELL) { sell_count++; sell_confidence += signals[i].confidence; } } //--- Need at least 3 agreeing signals if(buy_count >= 3 && buy_count > sell_count) { signal.signal_type = SIGNAL_BUY; signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); signal.stop_loss_distance = atr * 2.0; signal.take_profit_distance = atr * 3.0; signal.comment = StringFormat("Multi-Strategy Buy (%d/5)", buy_count); signal.confidence = buy_confidence / buy_count; } else if(sell_count >= 3 && sell_count > buy_count) { signal.signal_type = SIGNAL_SELL; signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); signal.stop_loss_distance = atr * 2.0; signal.take_profit_distance = atr * 3.0; signal.comment = StringFormat("Multi-Strategy Sell (%d/5)", sell_count); signal.confidence = sell_confidence / sell_count; } return signal; } //+------------------------------------------------------------------+ //| Check contrarian signal | //+------------------------------------------------------------------+ EntrySignal CEntrySystem::CheckContrarian(const MarketConditions &market) { EntrySignal signal; signal.signal_type = SIGNAL_NONE; //--- Look for extreme moves to fade int lookback = 5; double total_move = 0; bool all_bullish = true, all_bearish = true; for(int i = 1; i <= lookback; i++) { double close = iClose(_Symbol, PERIOD_CURRENT, i); double open = iOpen(_Symbol, PERIOD_CURRENT, i); double move = close - open; total_move += move; if(move <= 0) all_bullish = false; if(move >= 0) all_bearish = false; } double avg_move = MathAbs(total_move) / lookback; double atr = GetSafeATR(market); double extreme_threshold = atr * 0.5; //--- Fade extreme bullish move if(all_bullish && avg_move > extreme_threshold) { double rsi[1]; if(m_rsi_handle != INVALID_HANDLE) CopyBuffer(m_rsi_handle, 0, 0, 1, rsi); else rsi[0] = 70; // Default assumption if(rsi[0] > 70) // Overbought { signal.signal_type = SIGNAL_SELL; signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); signal.stop_loss_distance = atr * 1.5; signal.take_profit_distance = atr * 2.0; signal.comment = "Contrarian Sell (Fade Rally)"; signal.confidence = 60 + (rsi[0] - 70) / 3; } } //--- Fade extreme bearish move else if(all_bearish && avg_move > extreme_threshold) { double rsi[1]; if(m_rsi_handle != INVALID_HANDLE) CopyBuffer(m_rsi_handle, 0, 0, 1, rsi); else rsi[0] = 30; // Default assumption if(rsi[0] < 30) // Oversold { signal.signal_type = SIGNAL_BUY; signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); signal.stop_loss_distance = atr * 1.5; signal.take_profit_distance = atr * 2.0; signal.comment = "Contrarian Buy (Fade Selloff)"; signal.confidence = 60 + (30 - rsi[0]) / 3; } } return signal; } //+------------------------------------------------------------------+ //| Validate signal against filters | //+------------------------------------------------------------------+ bool CEntrySystem::ValidateSignal(const EntrySignal &signal, const MarketConditions &market) { //--- Check spread with more reasonable limits double spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * SymbolInfoDouble(_Symbol, SYMBOL_POINT); double atr = GetSafeATR(market); // Dynamic spread limit based on market conditions double spread_multiplier = 0.3; // Default 30% of ATR if(market.condition == MARKET_VOLATILE) spread_multiplier = 0.5; // Allow 50% in volatile markets else if(market.condition == MARKET_QUIET) spread_multiplier = 0.2; // Tighter in quiet markets double max_spread = atr * spread_multiplier; // Also set absolute maximum spread (e.g., 5 pips for majors) double absolute_max = 50 * SymbolInfoDouble(_Symbol, SYMBOL_POINT); // 5 pips max_spread = MathMin(max_spread, absolute_max); if(spread > max_spread) { Print("EntrySystem: Signal rejected - spread ", DoubleToString(spread/_Point, 1), " points exceeds max ", DoubleToString(max_spread/_Point, 1), " points"); return false; } //--- Check news filter if(market.news_event) { Print("EntrySystem: Signal rejected - news event"); return false; } //--- Check market hours (example: avoid Asian session for certain strategies) MqlDateTime time; TimeCurrent(time); if(m_config.entry_mode == ENTRY_MOMENTUM && (time.hour >= 0 && time.hour < 8)) { Print("EntrySystem: Momentum signal rejected - Asian session"); return false; } //--- Check minimum confidence if(signal.confidence < 50) { Print("EntrySystem: Signal rejected - low confidence"); return false; } // All checks passed return true; } //+------------------------------------------------------------------+ //| Check if new bar formed | //+------------------------------------------------------------------+ bool CEntrySystem::IsNewBar() { static datetime last_bar_time = 0; datetime current_bar_time = iTime(_Symbol, PERIOD_CURRENT, 0); if(current_bar_time != last_bar_time) { last_bar_time = current_bar_time; return true; } return false; } //+------------------------------------------------------------------+ //| Calculate signal strength | //+------------------------------------------------------------------+ double CEntrySystem::CalculateSignalStrength(const EntrySignal &signal) { double strength = signal.confidence; //--- Adjust for market conditions if(signal.signal_type == SIGNAL_BUY && signal.comment.Find("Momentum") >= 0 && signal.comment.Find("Buy") >= 0) { // Momentum strategies stronger in trends if(signal.comment.Find("TRENDING_UP") >= 0) strength *= 1.2; } else if(signal.comment.Find("Mean Reversion") >= 0) { // Mean reversion stronger in ranges if(signal.comment.Find("RANGING") >= 0) strength *= 1.2; } //--- Cap at 100 return MathMin(strength, 100); } //+------------------------------------------------------------------+ //| Add technical levels to signal | //+------------------------------------------------------------------+ void CEntrySystem::AddTechnicalLevels(EntrySignal &signal, const MarketConditions &market) { //--- Add nearest support/resistance levels int added = 0; double entry_price = signal.entry_price; for(int i = 0; i < market.sr_count && added < 5; i++) { //--- For buy signals, interested in nearby resistance (targets) and support (stops) if(signal.signal_type == SIGNAL_BUY) { if(market.sr_levels[i].type == "resistance" && market.sr_levels[i].price > entry_price && market.sr_levels[i].price < entry_price + signal.take_profit_distance) { signal.key_levels[added] = market.sr_levels[i]; added++; } else if(market.sr_levels[i].type == "support" && market.sr_levels[i].price < entry_price && market.sr_levels[i].price > entry_price - signal.stop_loss_distance) { signal.key_levels[added] = market.sr_levels[i]; added++; } } //--- For sell signals, opposite else if(signal.signal_type == SIGNAL_SELL) { if(market.sr_levels[i].type == "support" && market.sr_levels[i].price < entry_price && market.sr_levels[i].price > entry_price - signal.take_profit_distance) { signal.key_levels[added] = market.sr_levels[i]; added++; } else if(market.sr_levels[i].type == "resistance" && market.sr_levels[i].price > entry_price && market.sr_levels[i].price < entry_price + signal.stop_loss_distance) { signal.key_levels[added] = market.sr_levels[i]; added++; } } } } //+------------------------------------------------------------------+ //| Get valid ATR value (with fallback) - FIXED VERSION | //+------------------------------------------------------------------+ double CEntrySystem::GetValidATR(const MarketConditions &market) { // First try to use the market volatility (ATR from TechnicalAnalysis) double atr = market.volatility; // If market volatility is invalid, calculate it ourselves if(atr <= 0 || atr == EMPTY_VALUE || !MathIsValidNumber(atr)) { Print("EntrySystem: Market volatility invalid (", atr, "), calculating fallback"); int atr_handle = iATR(_Symbol, PERIOD_CURRENT, 14); double atr_buffer[1]; if(atr_handle != INVALID_HANDLE && CopyBuffer(atr_handle, 0, 0, 1, atr_buffer) > 0 && atr_buffer[0] > 0) { atr = atr_buffer[0]; Print("EntrySystem: Calculated ATR = ", atr); } else { // Final fallback: use percentage of current price double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); if(current_price > 0) { atr = current_price * 0.001; // 0.1% of price as default // For forex, ensure minimum of 20 pips double min_atr = 20 * SymbolInfoDouble(_Symbol, SYMBOL_POINT); if(atr < min_atr) atr = min_atr; Print("EntrySystem: Using price-based fallback ATR = ", atr, " (", atr / _Point, " points)"); } else { // Absolute last resort atr = 50 * SymbolInfoDouble(_Symbol, SYMBOL_POINT); // 50 points Print("EntrySystem: Using absolute fallback ATR = 50 points"); } } if(atr_handle != INVALID_HANDLE) IndicatorRelease(atr_handle); } return atr; } //+------------------------------------------------------------------+ //| Safe ATR calculation with multiple fallbacks | //+------------------------------------------------------------------+ double CEntrySystem::GetSafeATR(const MarketConditions &market) { double atr = GetValidATR(market); // Additional validation if(atr <= 0 || atr == EMPTY_VALUE || !MathIsValidNumber(atr)) { Print("EntrySystem: GetValidATR failed, using spread-based fallback"); // Calculate spread-based fallback double spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * SymbolInfoDouble(_Symbol, SYMBOL_POINT); // Use 5x spread as minimum volatility estimate atr = MathMax(spread * 5, 20 * SymbolInfoDouble(_Symbol, SYMBOL_POINT)); Print("EntrySystem: Using spread-based ATR fallback: ", atr / _Point, " points"); } // Final sanity check if(atr <= 0) { atr = 50 * SymbolInfoDouble(_Symbol, SYMBOL_POINT); // 50 points absolute minimum Print("EntrySystem: Critical error - using 50 points as absolute fallback"); } return atr; } #endif // ENTRY_SYSTEM_MQH //+------------------------------------------------------------------+