Overview To fully integrate the enhanced external trade management system, updates are required to 5 out of 7 existing modules. The updates maintain backward compatibility while adding new functionality for external trade handling. Module Update Requirements 🟢 No Updates Required (2 modules) TechnicalAnalysis.mqh - Already provides necessary calculations EntrySystem.mqh - Only handles EA's own entry signals 🟡 Minor Updates (2 modules) DataTypes.mqh - Add external trade structures and fields Utilities.mqh - Enhanced logging for external trades 🟠 Moderate Updates (3 modules) RiskManager.mqh - Enhanced risk enforcement methods TradeManager.mqh - Improved stop management for externals Dashboard.mqh - Display external trade information Integration Steps Phase 1: Data Structures (DataTypes.mqh) Add ENUM_EXTERNAL_STATUS enumeration Extend ManagedTrade structure with external-specific fields Add ExternalTradeStats structure for metrics Update DashboardConfig with show_external flag Key additions: external_status - Track state of external trade source_name - Identify where trade came from stops_modified - Track if we modified the trade original_sl/tp - Store original values for comparison Phase 2: Risk Management (RiskManager.mqh) Add EnforceRiskRulesEnhanced() method Implement GetExternalExposure() for risk aggregation Add UpdateExternalStats() for tracking Enhance ValidateAndAdjustRiskExternal() method Key features: Separate risk calculation for external trades Cache mechanism for performance Statistical tracking of external positions Smart risk adjustment without closing trades Phase 3: Trade Management (TradeManager.mqh) Add ApplyDefaultStopsEnhanced() with better logic Implement OverrideExternalStops() with smart override Create ManageExternalTrade() with different rules Add ApplyBreakevenExternal() with wider triggers Key features: Smart stop override (only improve, never worsen) Different management rules for external trades Respect minimum broker distances Track modification success/failure rates Phase 4: User Interface (Dashboard.mqh) Add CreateExternalSection() for display area Implement UpdateExternalSection() for real-time updates Add SetCustomText() for flexible display Create ShowExternalTrades() toggle method Key features: Real-time external trade count and risk Color-coded risk warnings List of active external positions Modification statistics display Phase 5: Logging (Utilities.mqh) Add LogExternalTrade() for detailed event logging Create separate CSV log for external trades Enhance GenerateReportEnhanced() with external section Add IdentifyTradeSource() for magic number interpretation Key features: Separate CSV log for external trade events Detailed tracking of all modifications Source identification from magic numbers Enhanced reporting with external statistics
1054 lines
No EOL
37 KiB
MQL5
1054 lines
No EOL
37 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
|
|
//+------------------------------------------------------------------+ |