mql5/Experts/Advisors/Modules/EntrySystem.mqh

932 lines
35 KiB
MQL5
Raw Permalink Normal View History

//+------------------------------------------------------------------+
//| 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
//+------------------------------------------------------------------+