mql5/Experts/Advisors/Modules/EntrySystem.mqh
darashikoh 56209c4d63 Summary of Fixes
The "Stop distance must be greater than 0" error is caused by a chain of issues in the ATR calculation. Here's what you need to do:
1. Replace the GetValidATR function in EntrySystem.mqh
The original code had a recursive call bug. Replace the entire EntrySystem.mqh file with the fixed version I provided above.
2. Update the GetATR method in TechnicalAnalysis.mqh
Replace the GetATR method with the fixed version that includes fallback calculations.
3. Update the AnalyzeMarket method in TechnicalAnalysis.mqh
Replace the AnalyzeMarket method with the enhanced version that ensures volatility is always valid.
4. Update the main EA file (ERMT_6.0.mq5)
Replace the CheckEntrySignals() and UpdateMarketConditions() functions with the enhanced versions that include debugging and validation.
Key Changes Made:

Fixed Recursive Bug: The GetValidATR function was calling itself instead of using market.volatility
Added Multiple Fallbacks:

Primary: Use market.volatility from TechnicalAnalysis
2025-07-30 19:33:01 +01:00

932 lines
No EOL
35 KiB
MQL5

//+------------------------------------------------------------------+
//| EntrySystem.mqh |
//| Entry System Module v1.3 |
//| Fixed: Recursive GetValidATR bug |
//+------------------------------------------------------------------+
#ifndef ENTRY_SYSTEM_MQH
#define ENTRY_SYSTEM_MQH
#include "DataTypes.mqh"
#include <Indicators/Indicators.mqh>
//+------------------------------------------------------------------+
//| 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
//+------------------------------------------------------------------+