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
932 lines
No EOL
35 KiB
MQL5
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
|
|
//+------------------------------------------------------------------+ |