1053 lines
No EOL
36 KiB
MQL5
1053 lines
No EOL
36 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| EntrySystem.mqh v2.0 |
|
|
//| Optimized Entry Signal Generation |
|
|
//| Enhanced with signal caching |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef ENTRY_SYSTEM_MQH
|
|
#define ENTRY_SYSTEM_MQH
|
|
|
|
#include "DataTypes.mqh"
|
|
#include <Indicators/Indicators.mqh>
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Entry System Class - Optimized Version |
|
|
//+------------------------------------------------------------------+
|
|
class CEntrySystem
|
|
{
|
|
private:
|
|
//--- Configuration
|
|
EntrySystemConfig m_config;
|
|
|
|
//--- Indicator handles (persistent)
|
|
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;
|
|
int m_atr_handle; // Dedicated ATR handle
|
|
|
|
//--- Signal tracking
|
|
datetime m_last_signal_time;
|
|
ENUM_SIGNAL_TYPE m_last_signal_type;
|
|
int m_consecutive_signals;
|
|
|
|
//--- Performance metrics
|
|
int m_signals_generated;
|
|
int m_signals_filtered;
|
|
double m_signal_accuracy;
|
|
|
|
//--- Bar tracking
|
|
datetime m_last_bar_time;
|
|
bool m_new_bar;
|
|
|
|
//--- Signal cache
|
|
struct SignalCache
|
|
{
|
|
datetime time;
|
|
EntrySignal signal;
|
|
bool valid;
|
|
};
|
|
SignalCache m_signal_cache;
|
|
|
|
//--- ATR cache for efficiency
|
|
double m_cached_atr;
|
|
datetime m_atr_cache_time;
|
|
|
|
//--- 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, const MarketConditions &market);
|
|
double CalculateSignalStrength(const EntrySignal &signal, const MarketConditions &market);
|
|
void AddTechnicalContext(EntrySignal &signal, const MarketConditions &market);
|
|
double GetCachedATR();
|
|
bool ValidateMarketHours();
|
|
double ScoreSignalQuality(const EntrySignal &signal, const MarketConditions &market);
|
|
void OptimizeStopDistance(EntrySignal &signal, const MarketConditions &market);
|
|
|
|
//--- Indicator data buffers (reusable)
|
|
double m_ma_fast_buffer[3];
|
|
double m_ma_slow_buffer[3];
|
|
double m_rsi_buffer[3];
|
|
double m_adx_buffer[3];
|
|
double m_bb_upper_buffer[3];
|
|
double m_bb_lower_buffer[3];
|
|
double m_bb_middle_buffer[3];
|
|
|
|
public:
|
|
CEntrySystem();
|
|
~CEntrySystem();
|
|
|
|
//--- Initialization
|
|
bool Initialize(const EntrySystemConfig &config);
|
|
void UpdateConfiguration(const EntrySystemConfig &config);
|
|
|
|
//--- Main method
|
|
EntrySignal CheckSignal(const MarketConditions &market);
|
|
|
|
//--- Signal validation
|
|
bool ValidateSignal(const EntrySignal &signal, const MarketConditions &market);
|
|
|
|
//--- Performance tracking
|
|
void UpdateSignalPerformance(const EntrySignal &signal, bool success);
|
|
|
|
//--- Getters
|
|
int GetConsecutiveSignals() { return m_consecutive_signals; }
|
|
datetime GetLastSignalTime() { return m_last_signal_time; }
|
|
double GetSignalAccuracy() { return m_signal_accuracy; }
|
|
int GetSignalsGenerated() { return m_signals_generated; }
|
|
|
|
//--- Market timing
|
|
bool IsOptimalTradingTime();
|
|
bool ShouldAvoidNews(const MarketConditions &market);
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor |
|
|
//+------------------------------------------------------------------+
|
|
CEntrySystem::CEntrySystem()
|
|
{
|
|
//--- Initialize handles
|
|
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_atr_handle = INVALID_HANDLE;
|
|
|
|
//--- Initialize tracking
|
|
m_last_signal_time = 0;
|
|
m_last_signal_type = SIGNAL_NONE;
|
|
m_consecutive_signals = 0;
|
|
m_signals_generated = 0;
|
|
m_signals_filtered = 0;
|
|
m_signal_accuracy = 0;
|
|
|
|
//--- Initialize cache
|
|
m_signal_cache.valid = false;
|
|
m_cached_atr = 0;
|
|
m_atr_cache_time = 0;
|
|
m_last_bar_time = 0;
|
|
m_new_bar = false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CEntrySystem::~CEntrySystem()
|
|
{
|
|
//--- Release all 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);
|
|
if(m_atr_handle != INVALID_HANDLE) IndicatorRelease(m_atr_handle);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize entry system - Optimized |
|
|
//+------------------------------------------------------------------+
|
|
bool CEntrySystem::Initialize(const EntrySystemConfig &config)
|
|
{
|
|
m_config = config;
|
|
|
|
//--- Always create ATR for stop calculations
|
|
m_atr_handle = iATR(_Symbol, PERIOD_CURRENT, 14);
|
|
if(m_atr_handle == INVALID_HANDLE)
|
|
{
|
|
Print("EntrySystem: Failed to create ATR indicator");
|
|
return false;
|
|
}
|
|
|
|
//--- Create indicators based on entry mode
|
|
bool init_success = true;
|
|
|
|
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);
|
|
|
|
init_success = (m_ma_fast_handle != INVALID_HANDLE &&
|
|
m_ma_slow_handle != INVALID_HANDLE);
|
|
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);
|
|
|
|
init_success = (m_rsi_handle != INVALID_HANDLE &&
|
|
m_adx_handle != INVALID_HANDLE &&
|
|
m_macd_handle != INVALID_HANDLE);
|
|
break;
|
|
|
|
case ENTRY_MEAN_REVERSION:
|
|
case ENTRY_BREAKOUT:
|
|
m_bb_handle = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE);
|
|
m_rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
|
|
if(m_config.entry_mode == ENTRY_BREAKOUT)
|
|
m_adx_handle = iADX(_Symbol, PERIOD_CURRENT, 14);
|
|
|
|
init_success = (m_bb_handle != INVALID_HANDLE &&
|
|
m_rsi_handle != INVALID_HANDLE);
|
|
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_ma_filter_handle = iMA(_Symbol, PERIOD_CURRENT, 200, 0, MODE_SMA, 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);
|
|
|
|
init_success = (m_ma_fast_handle != INVALID_HANDLE &&
|
|
m_rsi_handle != INVALID_HANDLE &&
|
|
m_bb_handle != INVALID_HANDLE);
|
|
break;
|
|
|
|
case ENTRY_CONTRARIAN:
|
|
m_rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
|
|
m_bb_handle = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE);
|
|
m_stoch_handle = iStochastic(_Symbol, PERIOD_CURRENT, 5, 3, 3, MODE_SMA, STO_LOWHIGH);
|
|
|
|
init_success = (m_rsi_handle != INVALID_HANDLE &&
|
|
m_bb_handle != INVALID_HANDLE);
|
|
break;
|
|
}
|
|
|
|
if(!init_success)
|
|
{
|
|
Print("EntrySystem: Failed to create required indicators");
|
|
return false;
|
|
}
|
|
|
|
Print("EntrySystem initialized: Mode=", EnumToString(m_config.entry_mode));
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check signal - Optimized main function |
|
|
//+------------------------------------------------------------------+
|
|
EntrySignal CEntrySystem::CheckSignal(const MarketConditions &market)
|
|
{
|
|
//--- Check if cached signal is still valid (within same bar)
|
|
if(m_signal_cache.valid && m_signal_cache.time == iTime(_Symbol, PERIOD_CURRENT, 0))
|
|
{
|
|
return m_signal_cache.signal;
|
|
}
|
|
|
|
//--- Initialize empty signal
|
|
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
|
|
if(market.volatility <= 0)
|
|
{
|
|
Print("EntrySystem: Invalid market volatility");
|
|
return signal;
|
|
}
|
|
|
|
//--- Check time restrictions
|
|
if(!ValidateMarketHours())
|
|
{
|
|
return signal;
|
|
}
|
|
|
|
//--- Check if enough time passed since last signal
|
|
if(TimeCurrent() - m_last_signal_time < m_config.min_time_between * 60)
|
|
{
|
|
return signal;
|
|
}
|
|
|
|
//--- Check for new bar
|
|
m_new_bar = IsNewBar();
|
|
|
|
//--- Only check on new bar for most strategies (except breakout)
|
|
if(!m_new_bar && 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;
|
|
}
|
|
|
|
//--- Process valid signals
|
|
if(signal.signal_type != SIGNAL_NONE)
|
|
{
|
|
m_signals_generated++;
|
|
|
|
//--- Ensure valid stop distances
|
|
OptimizeStopDistance(signal, market);
|
|
|
|
//--- Add technical context
|
|
AddTechnicalContext(signal, market);
|
|
|
|
//--- Calculate final signal strength
|
|
signal.confidence = CalculateSignalStrength(signal, market);
|
|
|
|
//--- Apply signal threshold
|
|
if(signal.confidence < m_config.signal_threshold)
|
|
{
|
|
m_signals_filtered++;
|
|
signal.signal_type = SIGNAL_NONE;
|
|
return signal;
|
|
}
|
|
|
|
//--- Validate signal
|
|
if(ValidateSignal(signal, market))
|
|
{
|
|
//--- 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;
|
|
|
|
//--- Cache the signal
|
|
m_signal_cache.time = iTime(_Symbol, PERIOD_CURRENT, 0);
|
|
m_signal_cache.signal = signal;
|
|
m_signal_cache.valid = true;
|
|
|
|
//--- Log signal
|
|
Print("EntrySystem: ", signal.comment,
|
|
" | Confidence: ", DoubleToString(signal.confidence, 1), "%",
|
|
" | SL: ", DoubleToString(signal.stop_loss_distance / _Point, 1), " points",
|
|
" | TP: ", DoubleToString(signal.take_profit_distance / _Point, 1), " points");
|
|
}
|
|
else
|
|
{
|
|
m_signals_filtered++;
|
|
signal.signal_type = SIGNAL_NONE;
|
|
}
|
|
}
|
|
|
|
return signal;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| MA Crossover Strategy - Optimized |
|
|
//+------------------------------------------------------------------+
|
|
EntrySignal CEntrySystem::CheckMACrossover(const MarketConditions &market)
|
|
{
|
|
EntrySignal signal;
|
|
signal.signal_type = SIGNAL_NONE;
|
|
|
|
//--- Get MA values efficiently
|
|
if(CopyBuffer(m_ma_fast_handle, 0, 0, 3, m_ma_fast_buffer) < 3) return signal;
|
|
if(CopyBuffer(m_ma_slow_handle, 0, 0, 3, m_ma_slow_buffer) < 3) return signal;
|
|
|
|
//--- Optional filter
|
|
double ma_filter = 0;
|
|
if(m_ma_filter_handle != INVALID_HANDLE)
|
|
{
|
|
double filter_buffer[1];
|
|
if(CopyBuffer(m_ma_filter_handle, 0, 0, 1, filter_buffer) > 0)
|
|
ma_filter = filter_buffer[0];
|
|
}
|
|
|
|
//--- Check for crossover
|
|
bool bullish_cross = (m_ma_fast_buffer[2] <= m_ma_slow_buffer[2] &&
|
|
m_ma_fast_buffer[1] > m_ma_slow_buffer[1]);
|
|
bool bearish_cross = (m_ma_fast_buffer[2] >= m_ma_slow_buffer[2] &&
|
|
m_ma_fast_buffer[1] < m_ma_slow_buffer[1]);
|
|
|
|
//--- Apply filter
|
|
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
bool above_filter = (ma_filter == 0) || (current_price > ma_filter);
|
|
bool below_filter = (ma_filter == 0) || (current_price < ma_filter);
|
|
|
|
//--- Generate signal
|
|
if(bullish_cross && above_filter)
|
|
{
|
|
signal.signal_type = SIGNAL_BUY;
|
|
signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
signal.comment = "MA Bullish Cross";
|
|
signal.confidence = 70;
|
|
|
|
//--- Calculate crossover angle for confidence
|
|
double cross_angle = MathAbs(m_ma_fast_buffer[1] - m_ma_slow_buffer[1]) / _Point;
|
|
if(cross_angle > 20) signal.confidence += 10;
|
|
if(cross_angle > 50) signal.confidence += 10;
|
|
}
|
|
else if(bearish_cross && below_filter)
|
|
{
|
|
signal.signal_type = SIGNAL_SELL;
|
|
signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
signal.comment = "MA Bearish Cross";
|
|
signal.confidence = 70;
|
|
|
|
//--- Calculate crossover angle
|
|
double cross_angle = MathAbs(m_ma_fast_buffer[1] - m_ma_slow_buffer[1]) / _Point;
|
|
if(cross_angle > 20) signal.confidence += 10;
|
|
if(cross_angle > 50) signal.confidence += 10;
|
|
}
|
|
|
|
//--- Set stop/target distances based on ATR
|
|
if(signal.signal_type != SIGNAL_NONE)
|
|
{
|
|
double atr = GetCachedATR();
|
|
signal.stop_loss_distance = atr * 2.0;
|
|
signal.take_profit_distance = atr * 3.0;
|
|
signal.atr_value = atr;
|
|
}
|
|
|
|
return signal;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Momentum Strategy - Enhanced |
|
|
//+------------------------------------------------------------------+
|
|
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;
|
|
|
|
//--- 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] && // Bullish direction
|
|
macd_main[0] > macd_signal[0] && // MACD bullish
|
|
macd_main[0] > macd_main[1]) // MACD rising
|
|
{
|
|
signal.signal_type = SIGNAL_BUY;
|
|
signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
signal.comment = "Momentum Buy";
|
|
|
|
//--- Dynamic confidence based on indicators
|
|
signal.confidence = 60;
|
|
signal.confidence += (adx[0] - 25) * 0.8; // ADX strength
|
|
signal.confidence += (rsi[0] - 50) * 0.3; // RSI momentum
|
|
signal.confidence = MathMin(signal.confidence, 95);
|
|
}
|
|
//--- 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] && // Bearish direction
|
|
macd_main[0] < macd_signal[0] && // MACD bearish
|
|
macd_main[0] < macd_main[1]) // MACD falling
|
|
{
|
|
signal.signal_type = SIGNAL_SELL;
|
|
signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
signal.comment = "Momentum Sell";
|
|
|
|
//--- Dynamic confidence
|
|
signal.confidence = 60;
|
|
signal.confidence += (adx[0] - 25) * 0.8;
|
|
signal.confidence += (50 - rsi[0]) * 0.3;
|
|
signal.confidence = MathMin(signal.confidence, 95);
|
|
}
|
|
|
|
//--- Set stops based on volatility and momentum
|
|
if(signal.signal_type != SIGNAL_NONE)
|
|
{
|
|
double atr = GetCachedATR();
|
|
|
|
//--- Tighter stops in strong momentum
|
|
double stop_multiplier = (adx[0] > 40) ? 1.5 : 2.0;
|
|
signal.stop_loss_distance = atr * stop_multiplier;
|
|
|
|
//--- Wider targets in strong trends
|
|
double target_multiplier = (adx[0] > 40) ? 4.0 : 3.0;
|
|
signal.take_profit_distance = atr * target_multiplier;
|
|
|
|
signal.atr_value = atr;
|
|
}
|
|
|
|
return signal;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Breakout Strategy - Enhanced with volume |
|
|
//+------------------------------------------------------------------+
|
|
EntrySignal CEntrySystem::CheckBreakout(const MarketConditions &market)
|
|
{
|
|
EntrySignal signal;
|
|
signal.signal_type = SIGNAL_NONE;
|
|
|
|
//--- Get Bollinger Bands
|
|
if(CopyBuffer(m_bb_handle, 1, 0, 2, m_bb_upper_buffer) < 2) return signal;
|
|
if(CopyBuffer(m_bb_handle, 2, 0, 2, m_bb_lower_buffer) < 2) return signal;
|
|
if(CopyBuffer(m_bb_handle, 0, 0, 1, m_bb_middle_buffer) < 1) return signal;
|
|
|
|
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
double prev_close = iClose(_Symbol, PERIOD_CURRENT, 1);
|
|
|
|
//--- Volume analysis
|
|
long current_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;
|
|
|
|
double volume_ratio = (avg_volume > 0) ? (double)current_volume / avg_volume : 1.0;
|
|
bool volume_surge = volume_ratio > 1.5;
|
|
|
|
//--- Upper band breakout
|
|
if(current_price > m_bb_upper_buffer[0] &&
|
|
prev_close <= m_bb_upper_buffer[1])
|
|
{
|
|
signal.signal_type = SIGNAL_BUY;
|
|
signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
signal.comment = "Bollinger Upper Breakout";
|
|
|
|
//--- Base confidence
|
|
signal.confidence = 60;
|
|
|
|
//--- Volume confirmation
|
|
if(volume_surge)
|
|
{
|
|
signal.confidence += 20;
|
|
signal.comment += " (Volume Confirmed)";
|
|
}
|
|
|
|
//--- Band width analysis
|
|
double band_width = (m_bb_upper_buffer[0] - m_bb_lower_buffer[0]) / m_bb_middle_buffer[0];
|
|
if(band_width < 0.02) // Squeeze breakout
|
|
{
|
|
signal.confidence += 15;
|
|
signal.comment += " (Squeeze)";
|
|
}
|
|
}
|
|
//--- Lower band breakout
|
|
else if(current_price < m_bb_lower_buffer[0] &&
|
|
prev_close >= m_bb_lower_buffer[1])
|
|
{
|
|
signal.signal_type = SIGNAL_SELL;
|
|
signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
signal.comment = "Bollinger Lower Breakout";
|
|
|
|
signal.confidence = 60;
|
|
|
|
if(volume_surge)
|
|
{
|
|
signal.confidence += 20;
|
|
signal.comment += " (Volume Confirmed)";
|
|
}
|
|
|
|
double band_width = (m_bb_upper_buffer[0] - m_bb_lower_buffer[0]) / m_bb_middle_buffer[0];
|
|
if(band_width < 0.02)
|
|
{
|
|
signal.confidence += 15;
|
|
signal.comment += " (Squeeze)";
|
|
}
|
|
}
|
|
|
|
//--- Set stops for breakout
|
|
if(signal.signal_type != SIGNAL_NONE)
|
|
{
|
|
double atr = GetCachedATR();
|
|
|
|
//--- Use band structure for stops
|
|
if(signal.signal_type == SIGNAL_BUY)
|
|
{
|
|
signal.stop_loss_distance = MathMax(current_price - m_bb_middle_buffer[0], atr * 1.5);
|
|
signal.take_profit_distance = (m_bb_upper_buffer[0] - m_bb_middle_buffer[0]) * 2.5;
|
|
}
|
|
else
|
|
{
|
|
signal.stop_loss_distance = MathMax(m_bb_middle_buffer[0] - current_price, atr * 1.5);
|
|
signal.take_profit_distance = (m_bb_middle_buffer[0] - m_bb_lower_buffer[0]) * 2.5;
|
|
}
|
|
|
|
signal.atr_value = atr;
|
|
}
|
|
|
|
return signal;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Multi-Strategy Consensus - Optimized |
|
|
//+------------------------------------------------------------------+
|
|
EntrySignal CEntrySystem::CheckMultiStrategy(const MarketConditions &market)
|
|
{
|
|
EntrySignal signal;
|
|
signal.signal_type = SIGNAL_NONE;
|
|
|
|
//--- Collect signals from strategies
|
|
EntrySignal signals[5];
|
|
double weights[5] = {1.0, 1.2, 1.5, 0.8, 1.0}; // Strategy weights
|
|
|
|
signals[0] = CheckMACrossover(market);
|
|
signals[1] = CheckMomentum(market);
|
|
signals[2] = CheckBreakout(market);
|
|
signals[3] = CheckMeanReversion(market);
|
|
signals[4] = CheckMAPullback(market);
|
|
|
|
//--- Weighted voting
|
|
double buy_score = 0, sell_score = 0;
|
|
double total_buy_confidence = 0, total_sell_confidence = 0;
|
|
int buy_count = 0, sell_count = 0;
|
|
|
|
for(int i = 0; i < 5; i++)
|
|
{
|
|
if(signals[i].signal_type == SIGNAL_BUY)
|
|
{
|
|
buy_count++;
|
|
buy_score += weights[i];
|
|
total_buy_confidence += signals[i].confidence * weights[i];
|
|
}
|
|
else if(signals[i].signal_type == SIGNAL_SELL)
|
|
{
|
|
sell_count++;
|
|
sell_score += weights[i];
|
|
total_sell_confidence += signals[i].confidence * weights[i];
|
|
}
|
|
}
|
|
|
|
//--- Need weighted majority (>2.5 weight score)
|
|
if(buy_score >= 2.5 && buy_score > sell_score)
|
|
{
|
|
signal.signal_type = SIGNAL_BUY;
|
|
signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
signal.comment = StringFormat("Multi-Strategy Buy (%d/5 strategies)", buy_count);
|
|
signal.confidence = total_buy_confidence / buy_score;
|
|
|
|
//--- Bonus for unanimous decision
|
|
if(buy_count >= 4) signal.confidence += 10;
|
|
}
|
|
else if(sell_score >= 2.5 && sell_score > buy_score)
|
|
{
|
|
signal.signal_type = SIGNAL_SELL;
|
|
signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
signal.comment = StringFormat("Multi-Strategy Sell (%d/5 strategies)", sell_count);
|
|
signal.confidence = total_sell_confidence / sell_score;
|
|
|
|
if(sell_count >= 4) signal.confidence += 10;
|
|
}
|
|
|
|
//--- Average stops from agreeing strategies
|
|
if(signal.signal_type != SIGNAL_NONE)
|
|
{
|
|
double avg_sl = 0, avg_tp = 0;
|
|
int count = 0;
|
|
|
|
for(int i = 0; i < 5; i++)
|
|
{
|
|
if(signals[i].signal_type == signal.signal_type)
|
|
{
|
|
avg_sl += signals[i].stop_loss_distance;
|
|
avg_tp += signals[i].take_profit_distance;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if(count > 0)
|
|
{
|
|
signal.stop_loss_distance = avg_sl / count;
|
|
signal.take_profit_distance = avg_tp / count;
|
|
}
|
|
else
|
|
{
|
|
//--- Fallback
|
|
double atr = GetCachedATR();
|
|
signal.stop_loss_distance = atr * 2.0;
|
|
signal.take_profit_distance = atr * 3.0;
|
|
}
|
|
|
|
signal.atr_value = GetCachedATR();
|
|
}
|
|
|
|
return signal;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get cached ATR value |
|
|
//+------------------------------------------------------------------+
|
|
double CEntrySystem::GetCachedATR()
|
|
{
|
|
//--- Check cache validity (5 seconds)
|
|
if(TimeCurrent() - m_atr_cache_time < 5 && m_cached_atr > 0)
|
|
return m_cached_atr;
|
|
|
|
//--- Update cache
|
|
double atr_buffer[1];
|
|
if(m_atr_handle != INVALID_HANDLE &&
|
|
CopyBuffer(m_atr_handle, 0, 0, 1, atr_buffer) > 0 &&
|
|
atr_buffer[0] > 0)
|
|
{
|
|
m_cached_atr = atr_buffer[0];
|
|
m_atr_cache_time = TimeCurrent();
|
|
return m_cached_atr;
|
|
}
|
|
|
|
//--- Fallback calculation
|
|
double sum = 0;
|
|
int period = 14;
|
|
|
|
for(int i = 1; i <= period; i++)
|
|
{
|
|
double high = iHigh(_Symbol, PERIOD_CURRENT, i);
|
|
double low = iLow(_Symbol, PERIOD_CURRENT, i);
|
|
double close_prev = iClose(_Symbol, PERIOD_CURRENT, i + 1);
|
|
|
|
double tr = MathMax(high - low, MathMax(MathAbs(high - close_prev), MathAbs(low - close_prev)));
|
|
sum += tr;
|
|
}
|
|
|
|
m_cached_atr = sum / period;
|
|
m_atr_cache_time = TimeCurrent();
|
|
|
|
//--- Final validation
|
|
if(m_cached_atr <= 0)
|
|
{
|
|
m_cached_atr = 50 * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
Print("EntrySystem: Using fallback ATR");
|
|
}
|
|
|
|
return m_cached_atr;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Optimize stop distances based on market conditions |
|
|
//+------------------------------------------------------------------+
|
|
void CEntrySystem::OptimizeStopDistance(EntrySignal &signal, const MarketConditions &market)
|
|
{
|
|
//--- Ensure minimum stop distance
|
|
double min_stop = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) *
|
|
SymbolInfoDouble(_Symbol, SYMBOL_POINT) * 1.1;
|
|
|
|
//--- Adjust for market conditions
|
|
double volatility_factor = 1.0;
|
|
|
|
switch(market.condition)
|
|
{
|
|
case MARKET_VOLATILE:
|
|
volatility_factor = 1.3; // Wider stops in volatile markets
|
|
break;
|
|
|
|
case MARKET_QUIET:
|
|
volatility_factor = 0.8; // Tighter stops in quiet markets
|
|
break;
|
|
|
|
case MARKET_TRENDING_UP:
|
|
case MARKET_TRENDING_DOWN:
|
|
volatility_factor = 1.1; // Slightly wider in trends
|
|
break;
|
|
}
|
|
|
|
//--- Apply adjustments
|
|
signal.stop_loss_distance *= volatility_factor;
|
|
signal.take_profit_distance *= volatility_factor;
|
|
|
|
//--- Ensure minimum distances
|
|
signal.stop_loss_distance = MathMax(signal.stop_loss_distance, min_stop);
|
|
signal.take_profit_distance = MathMax(signal.take_profit_distance, min_stop);
|
|
|
|
//--- Ensure minimum R:R ratio
|
|
if(m_config.min_rr_ratio > 0)
|
|
{
|
|
double min_tp = signal.stop_loss_distance * m_config.min_rr_ratio;
|
|
signal.take_profit_distance = MathMax(signal.take_profit_distance, min_tp);
|
|
}
|
|
|
|
//--- Round to symbol point
|
|
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
signal.stop_loss_distance = NormalizeDouble(signal.stop_loss_distance, _Digits);
|
|
signal.take_profit_distance = NormalizeDouble(signal.take_profit_distance, _Digits);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Validate signal quality |
|
|
//+------------------------------------------------------------------+
|
|
bool CEntrySystem::ValidateSignal(const EntrySignal &signal, const MarketConditions &market)
|
|
{
|
|
//--- Basic validation
|
|
if(signal.stop_loss_distance <= 0 || signal.take_profit_distance <= 0)
|
|
{
|
|
Print("EntrySystem: Invalid stop/target distances");
|
|
return false;
|
|
}
|
|
|
|
//--- Check spread
|
|
double max_spread = signal.stop_loss_distance * 0.2; // Max 20% of stop
|
|
if(market.spread > max_spread)
|
|
{
|
|
Print("EntrySystem: Spread too high - ",
|
|
DoubleToString(market.spread/_Point, 1), " points");
|
|
return false;
|
|
}
|
|
|
|
//--- Check news filter
|
|
if(m_config.filter_news && market.news_event)
|
|
{
|
|
Print("EntrySystem: Signal rejected - news event");
|
|
return false;
|
|
}
|
|
|
|
//--- Check R:R ratio
|
|
double rr_ratio = signal.take_profit_distance / signal.stop_loss_distance;
|
|
if(m_config.min_rr_ratio > 0 && rr_ratio < m_config.min_rr_ratio)
|
|
{
|
|
Print("EntrySystem: R:R ratio too low - ", DoubleToString(rr_ratio, 2));
|
|
return false;
|
|
}
|
|
|
|
//--- Market condition filter
|
|
if(m_config.entry_mode == ENTRY_MOMENTUM &&
|
|
(market.condition == MARKET_RANGING || market.condition == MARKET_QUIET))
|
|
{
|
|
Print("EntrySystem: Momentum signal rejected in ranging market");
|
|
return false;
|
|
}
|
|
|
|
if(m_config.entry_mode == ENTRY_MEAN_REVERSION &&
|
|
(market.condition == MARKET_TRENDING_UP || market.condition == MARKET_TRENDING_DOWN))
|
|
{
|
|
Print("EntrySystem: Mean reversion rejected in trending market");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if new bar formed |
|
|
//+------------------------------------------------------------------+
|
|
bool CEntrySystem::IsNewBar()
|
|
{
|
|
datetime current_bar_time = iTime(_Symbol, PERIOD_CURRENT, 0);
|
|
|
|
if(current_bar_time != m_last_bar_time)
|
|
{
|
|
m_last_bar_time = current_bar_time;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Validate market hours |
|
|
//+------------------------------------------------------------------+
|
|
bool CEntrySystem::ValidateMarketHours()
|
|
{
|
|
if(!m_config.use_market_hours)
|
|
return true;
|
|
|
|
MqlDateTime current_time;
|
|
TimeCurrent(current_time);
|
|
|
|
//--- Check day of week
|
|
if(current_time.day_of_week == 0 || current_time.day_of_week == 6)
|
|
return false;
|
|
|
|
//--- Friday close
|
|
if(m_config.friday_close && current_time.day_of_week == 5 && current_time.hour >= 20)
|
|
return false;
|
|
|
|
//--- Check trading hours
|
|
if(current_time.hour < m_config.start_hour || current_time.hour >= m_config.end_hour)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate signal strength with market context |
|
|
//+------------------------------------------------------------------+
|
|
double CEntrySystem::CalculateSignalStrength(const EntrySignal &signal, const MarketConditions &market)
|
|
{
|
|
double strength = signal.confidence;
|
|
|
|
//--- Market alignment bonus
|
|
if((signal.signal_type == SIGNAL_BUY && market.condition == MARKET_TRENDING_UP) ||
|
|
(signal.signal_type == SIGNAL_SELL && market.condition == MARKET_TRENDING_DOWN))
|
|
{
|
|
strength += 10;
|
|
}
|
|
|
|
//--- Trend strength bonus
|
|
if(market.trend_strength > 30)
|
|
strength += 5;
|
|
if(market.trend_strength > 50)
|
|
strength += 5;
|
|
|
|
//--- Volatility adjustment
|
|
if(market.volatility_percentile > 80) // High volatility
|
|
strength -= 5;
|
|
else if(market.volatility_percentile < 20) // Low volatility
|
|
strength += 5;
|
|
|
|
//--- Support/Resistance proximity
|
|
for(int i = 0; i < market.sr_count && i < 5; i++)
|
|
{
|
|
double distance = MathAbs(signal.entry_price - market.sr_levels[i].price);
|
|
if(distance < market.volatility * 0.5)
|
|
{
|
|
strength += market.sr_levels[i].strength / 20; // Max +5 from S/R
|
|
}
|
|
}
|
|
|
|
return MathMin(strength, 100);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Add technical context to signal |
|
|
//+------------------------------------------------------------------+
|
|
void CEntrySystem::AddTechnicalContext(EntrySignal &signal, const MarketConditions &market)
|
|
{
|
|
signal.atr_value = GetCachedATR();
|
|
signal.spread_value = market.spread;
|
|
|
|
//--- Add nearest key levels
|
|
int levels_added = 0;
|
|
|
|
for(int i = 0; i < market.sr_count && levels_added < 5; i++)
|
|
{
|
|
double level_distance = MathAbs(signal.entry_price - market.sr_levels[i].price);
|
|
|
|
//--- Include levels within 2 ATR
|
|
if(level_distance < signal.atr_value * 2)
|
|
{
|
|
signal.key_levels[levels_added] = market.sr_levels[i];
|
|
levels_added++;
|
|
}
|
|
}
|
|
|
|
//--- Calculate expected R:R
|
|
signal.expected_rr = signal.take_profit_distance / signal.stop_loss_distance;
|
|
|
|
//--- Suggested risk based on signal quality
|
|
if(signal.confidence >= 80)
|
|
signal.suggested_risk = 2.0; // 2% for high confidence
|
|
else if(signal.confidence >= 60)
|
|
signal.suggested_risk = 1.0; // 1% for medium
|
|
else
|
|
signal.suggested_risk = 0.5; // 0.5% for low
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update signal performance tracking |
|
|
//+------------------------------------------------------------------+
|
|
void CEntrySystem::UpdateSignalPerformance(const EntrySignal &signal, bool success)
|
|
{
|
|
//--- Simple exponential average of success rate
|
|
double alpha = 0.1; // Learning rate
|
|
|
|
if(success)
|
|
m_signal_accuracy = m_signal_accuracy * (1 - alpha) + 1.0 * alpha;
|
|
else
|
|
m_signal_accuracy = m_signal_accuracy * (1 - alpha) + 0.0 * alpha;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Additional signal generation methods |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//--- MA Pullback implementation
|
|
EntrySignal CEntrySystem::CheckMAPullback(const MarketConditions &market)
|
|
{
|
|
EntrySignal signal;
|
|
signal.signal_type = SIGNAL_NONE;
|
|
|
|
//--- Similar structure to other methods but checking for pullbacks
|
|
//--- Implementation details...
|
|
|
|
return signal;
|
|
}
|
|
|
|
//--- Mean Reversion implementation
|
|
EntrySignal CEntrySystem::CheckMeanReversion(const MarketConditions &market)
|
|
{
|
|
EntrySignal signal;
|
|
signal.signal_type = SIGNAL_NONE;
|
|
|
|
//--- Check if market is ranging
|
|
if(market.condition != MARKET_RANGING && market.condition != MARKET_QUIET)
|
|
return signal;
|
|
|
|
//--- Implementation details...
|
|
|
|
return signal;
|
|
}
|
|
|
|
//--- Contrarian implementation
|
|
EntrySignal CEntrySystem::CheckContrarian(const MarketConditions &market)
|
|
{
|
|
EntrySignal signal;
|
|
signal.signal_type = SIGNAL_NONE;
|
|
|
|
//--- Look for extreme moves to fade
|
|
//--- Implementation details...
|
|
|
|
return signal;
|
|
}
|
|
|
|
#endif // ENTRY_SYSTEM_MQH
|
|
//+------------------------------------------------------------------+ |