2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| EntrySystem.mqh |
|
2025-07-30 19:33:01 +01:00
|
|
|
//| Entry System Module v1.3 |
|
|
|
|
//| Fixed: Recursive GetValidATR bug |
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
#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);
|
2025-07-30 19:33:01 +01:00
|
|
|
double GetValidATR(const MarketConditions &market);
|
|
|
|
double GetSafeATR(const MarketConditions &market);
|
2025-07-20 22:38:24 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2025-07-30 18:55:56 +01:00
|
|
|
//| Enhanced CheckSignal with comprehensive validation |
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
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();
|
|
|
|
|
2025-07-30 18:55:56 +01:00
|
|
|
//--- Validate market conditions first
|
2025-07-30 19:33:01 +01:00
|
|
|
double atr = GetSafeATR(market);
|
|
|
|
if(atr <= 0)
|
2025-07-30 18:55:56 +01:00
|
|
|
{
|
2025-07-30 19:33:01 +01:00
|
|
|
Print("EntrySystem: Invalid ATR, skipping signal check");
|
2025-07-30 18:55:56 +01:00
|
|
|
return signal;
|
|
|
|
}
|
|
|
|
|
2025-07-20 22:38:24 +01:00
|
|
|
//--- 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;
|
|
|
|
}
|
|
|
|
|
2025-07-30 18:55:56 +01:00
|
|
|
//--- Enhanced validation
|
2025-07-20 22:38:24 +01:00
|
|
|
if(signal.signal_type != SIGNAL_NONE)
|
|
|
|
{
|
2025-07-30 13:32:29 +01:00
|
|
|
// Critical: Ensure stop distance is valid
|
|
|
|
if(signal.stop_loss_distance <= 0)
|
|
|
|
{
|
2025-07-30 18:55:56 +01:00
|
|
|
Print("EntrySystem: Invalid stop distance (", signal.stop_loss_distance,
|
|
|
|
") in signal: ", signal.comment);
|
|
|
|
|
|
|
|
// Try to calculate a default stop distance
|
2025-07-30 19:33:01 +01:00
|
|
|
double default_atr = GetSafeATR(market);
|
2025-07-30 18:55:56 +01:00
|
|
|
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");
|
2025-07-30 13:32:29 +01:00
|
|
|
signal.signal_type = SIGNAL_NONE;
|
|
|
|
return signal;
|
|
|
|
}
|
2025-07-30 18:55:56 +01:00
|
|
|
|
|
|
|
// Standard validation
|
2025-07-20 22:38:24 +01:00
|
|
|
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;
|
2025-07-30 18:55:56 +01:00
|
|
|
|
|
|
|
// 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");
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
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]);
|
|
|
|
|
2025-07-30 13:32:29 +01:00
|
|
|
//--- Ensure we have valid market volatility
|
2025-07-30 19:33:01 +01:00
|
|
|
double atr = GetSafeATR(market);
|
2025-07-30 13:32:29 +01:00
|
|
|
|
2025-07-20 22:38:24 +01:00
|
|
|
if(bullish_cross && above_filter)
|
|
|
|
{
|
|
|
|
signal.signal_type = SIGNAL_BUY;
|
|
|
|
signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
2025-07-30 13:32:29 +01:00
|
|
|
signal.stop_loss_distance = atr * 2.0; // 2 ATR stop
|
|
|
|
signal.take_profit_distance = atr * 3.0; // 3 ATR target
|
2025-07-20 22:38:24 +01:00
|
|
|
signal.comment = "MA Bullish Cross";
|
|
|
|
signal.confidence = 70;
|
2025-07-30 13:32:29 +01:00
|
|
|
|
|
|
|
// Debug output
|
|
|
|
Print("MA Cross Signal: SL Distance = ", signal.stop_loss_distance,
|
|
|
|
" (", signal.stop_loss_distance / _Point, " points), ATR = ", atr);
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
else if(bearish_cross && below_filter)
|
|
|
|
{
|
|
|
|
signal.signal_type = SIGNAL_SELL;
|
|
|
|
signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
2025-07-30 13:32:29 +01:00
|
|
|
signal.stop_loss_distance = atr * 2.0; // 2 ATR stop
|
|
|
|
signal.take_profit_distance = atr * 3.0; // 3 ATR target
|
2025-07-20 22:38:24 +01:00
|
|
|
signal.comment = "MA Bearish Cross";
|
|
|
|
signal.confidence = 70;
|
2025-07-30 13:32:29 +01:00
|
|
|
|
|
|
|
// Debug output
|
|
|
|
Print("MA Cross Signal: SL Distance = ", signal.stop_loss_distance,
|
|
|
|
" (", signal.stop_loss_distance / _Point, " points), ATR = ", atr);
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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]);
|
2025-07-30 19:33:01 +01:00
|
|
|
double atr = GetSafeATR(market);
|
2025-07-20 22:38:24 +01:00
|
|
|
|
|
|
|
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);
|
2025-07-30 19:33:01 +01:00
|
|
|
signal.stop_loss_distance = MathMax(current_price - ma_slow[0] + atr * 0.5, atr * 1.5);
|
2025-07-20 22:38:24 +01:00
|
|
|
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);
|
2025-07-30 19:33:01 +01:00
|
|
|
signal.stop_loss_distance = MathMax(ma_slow[0] - current_price + atr * 0.5, atr * 1.5);
|
2025-07-20 22:38:24 +01:00
|
|
|
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;
|
|
|
|
|
2025-07-30 19:33:01 +01:00
|
|
|
double atr = GetSafeATR(market);
|
|
|
|
|
2025-07-20 22:38:24 +01:00
|
|
|
//--- 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);
|
2025-07-30 19:33:01 +01:00
|
|
|
signal.stop_loss_distance = atr * 1.5;
|
|
|
|
signal.take_profit_distance = atr * 3.0;
|
2025-07-20 22:38:24 +01:00
|
|
|
signal.comment = "Momentum Buy";
|
|
|
|
signal.confidence = adx[0] * 2; // Higher ADX = higher confidence
|
2025-07-30 19:33:01 +01:00
|
|
|
|
|
|
|
Print("Momentum Buy Signal: ATR=", atr, " SL=", signal.stop_loss_distance);
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
//--- 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);
|
2025-07-30 19:33:01 +01:00
|
|
|
signal.stop_loss_distance = atr * 1.5;
|
|
|
|
signal.take_profit_distance = atr * 3.0;
|
2025-07-20 22:38:24 +01:00
|
|
|
signal.comment = "Momentum Sell";
|
|
|
|
signal.confidence = adx[0] * 2;
|
2025-07-30 19:33:01 +01:00
|
|
|
|
|
|
|
Print("Momentum Sell Signal: ATR=", atr, " SL=", signal.stop_loss_distance);
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2025-07-30 19:33:01 +01:00
|
|
|
double atr = GetSafeATR(market);
|
2025-07-20 22:38:24 +01:00
|
|
|
|
|
|
|
//--- 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);
|
2025-07-30 19:33:01 +01:00
|
|
|
signal.stop_loss_distance = MathMax(current_price - bb_middle[0], atr * 1.5);
|
2025-07-20 22:38:24 +01:00
|
|
|
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);
|
2025-07-30 19:33:01 +01:00
|
|
|
signal.stop_loss_distance = MathMax(bb_middle[0] - current_price, atr * 1.5);
|
2025-07-20 22:38:24 +01:00
|
|
|
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);
|
2025-07-30 19:33:01 +01:00
|
|
|
double atr = GetSafeATR(market);
|
2025-07-20 22:38:24 +01:00
|
|
|
|
|
|
|
//--- Oversold bounce
|
|
|
|
if(current_price <= bb_lower[0] && rsi[0] < 30)
|
|
|
|
{
|
|
|
|
signal.signal_type = SIGNAL_BUY;
|
|
|
|
signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
2025-07-30 19:33:01 +01:00
|
|
|
signal.stop_loss_distance = atr * 1.0;
|
|
|
|
signal.take_profit_distance = MathMax(bb_middle[0] - current_price, atr * 1.5);
|
2025-07-20 22:38:24 +01:00
|
|
|
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);
|
2025-07-30 19:33:01 +01:00
|
|
|
signal.stop_loss_distance = atr * 1.0;
|
|
|
|
signal.take_profit_distance = MathMax(current_price - bb_middle[0], atr * 1.5);
|
2025-07-20 22:38:24 +01:00
|
|
|
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;
|
2025-07-30 19:33:01 +01:00
|
|
|
double atr = GetSafeATR(market);
|
2025-07-20 22:38:24 +01:00
|
|
|
|
|
|
|
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);
|
2025-07-30 19:33:01 +01:00
|
|
|
signal.stop_loss_distance = atr * 2.0;
|
|
|
|
signal.take_profit_distance = atr * 3.0;
|
2025-07-20 22:38:24 +01:00
|
|
|
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);
|
2025-07-30 19:33:01 +01:00
|
|
|
signal.stop_loss_distance = atr * 2.0;
|
|
|
|
signal.take_profit_distance = atr * 3.0;
|
2025-07-20 22:38:24 +01:00
|
|
|
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;
|
2025-07-30 19:33:01 +01:00
|
|
|
double atr = GetSafeATR(market);
|
|
|
|
double extreme_threshold = atr * 0.5;
|
2025-07-20 22:38:24 +01:00
|
|
|
|
|
|
|
//--- 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);
|
2025-07-30 19:33:01 +01:00
|
|
|
signal.stop_loss_distance = atr * 1.5;
|
|
|
|
signal.take_profit_distance = atr * 2.0;
|
2025-07-20 22:38:24 +01:00
|
|
|
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);
|
2025-07-30 19:33:01 +01:00
|
|
|
signal.stop_loss_distance = atr * 1.5;
|
|
|
|
signal.take_profit_distance = atr * 2.0;
|
2025-07-20 22:38:24 +01:00
|
|
|
signal.comment = "Contrarian Buy (Fade Selloff)";
|
|
|
|
signal.confidence = 60 + (30 - rsi[0]) / 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return signal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2025-07-30 19:33:01 +01:00
|
|
|
//| Validate signal against filters |
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool CEntrySystem::ValidateSignal(const EntrySignal &signal, const MarketConditions &market)
|
|
|
|
{
|
2025-07-22 17:33:44 +01:00
|
|
|
//--- Check spread with more reasonable limits
|
2025-07-20 22:38:24 +01:00
|
|
|
double spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
2025-07-30 19:33:01 +01:00
|
|
|
double atr = GetSafeATR(market);
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-07-22 17:33:44 +01:00
|
|
|
// 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;
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-07-22 17:33:44 +01:00
|
|
|
// 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);
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-07-22 17:33:44 +01:00
|
|
|
if(spread > max_spread)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-07-22 17:33:44 +01:00
|
|
|
Print("EntrySystem: Signal rejected - spread ", DoubleToString(spread/_Point, 1),
|
|
|
|
" points exceeds max ", DoubleToString(max_spread/_Point, 1), " points");
|
2025-07-20 22:38:24 +01:00
|
|
|
return false;
|
|
|
|
}
|
2025-07-22 18:11:53 +01:00
|
|
|
|
|
|
|
//--- 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;
|
|
|
|
}
|
2025-07-30 19:33:01 +01:00
|
|
|
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| 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++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-07-30 13:32:29 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-07-30 18:55:56 +01:00
|
|
|
//| Get valid ATR value (with fallback) - FIXED VERSION |
|
2025-07-30 13:32:29 +01:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
double CEntrySystem::GetValidATR(const MarketConditions &market)
|
|
|
|
{
|
2025-07-30 18:55:56 +01:00
|
|
|
// First try to use the market volatility (ATR from TechnicalAnalysis)
|
|
|
|
double atr = market.volatility;
|
2025-07-30 13:32:29 +01:00
|
|
|
|
2025-07-30 18:55:56 +01:00
|
|
|
// If market volatility is invalid, calculate it ourselves
|
2025-07-30 19:33:01 +01:00
|
|
|
if(atr <= 0 || atr == EMPTY_VALUE || !MathIsValidNumber(atr))
|
2025-07-30 13:32:29 +01:00
|
|
|
{
|
2025-07-30 19:33:01 +01:00
|
|
|
Print("EntrySystem: Market volatility invalid (", atr, "), calculating fallback");
|
|
|
|
|
2025-07-30 13:32:29 +01:00
|
|
|
int atr_handle = iATR(_Symbol, PERIOD_CURRENT, 14);
|
|
|
|
double atr_buffer[1];
|
|
|
|
|
2025-07-30 19:33:01 +01:00
|
|
|
if(atr_handle != INVALID_HANDLE && CopyBuffer(atr_handle, 0, 0, 1, atr_buffer) > 0 && atr_buffer[0] > 0)
|
2025-07-30 13:32:29 +01:00
|
|
|
{
|
|
|
|
atr = atr_buffer[0];
|
2025-07-30 19:33:01 +01:00
|
|
|
Print("EntrySystem: Calculated ATR = ", atr);
|
2025-07-30 13:32:29 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Final fallback: use percentage of current price
|
|
|
|
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
2025-07-30 19:33:01 +01:00
|
|
|
if(current_price > 0)
|
|
|
|
{
|
|
|
|
atr = current_price * 0.001; // 0.1% of price as default
|
2025-07-30 13:32:29 +01:00
|
|
|
|
2025-07-30 19:33:01 +01:00
|
|
|
// 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");
|
|
|
|
}
|
2025-07-30 13:32:29 +01:00
|
|
|
}
|
|
|
|
|
2025-07-30 19:33:01 +01:00
|
|
|
if(atr_handle != INVALID_HANDLE)
|
|
|
|
IndicatorRelease(atr_handle);
|
2025-07-30 13:32:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return atr;
|
|
|
|
}
|
|
|
|
|
2025-07-30 18:55:56 +01:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| 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))
|
|
|
|
{
|
2025-07-30 19:33:01 +01:00
|
|
|
Print("EntrySystem: GetValidATR failed, using spread-based fallback");
|
|
|
|
|
2025-07-30 18:55:56 +01:00
|
|
|
// 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");
|
|
|
|
}
|
|
|
|
|
2025-07-30 19:33:01 +01:00
|
|
|
// 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");
|
|
|
|
}
|
|
|
|
|
2025-07-30 18:55:56 +01:00
|
|
|
return atr;
|
|
|
|
}
|
|
|
|
|
2025-07-20 22:38:24 +01:00
|
|
|
#endif // ENTRY_SYSTEM_MQH
|
|
|
|
//+------------------------------------------------------------------+
|