2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
//| EntrySystem.mqh v3.0 |
|
|
|
|
//| Optimized Entry Signal Generation System |
|
|
|
|
//| Future-Proofed Architecture |
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
#ifndef ENTRY_SYSTEM_MQH
|
|
|
|
#define ENTRY_SYSTEM_MQH
|
|
|
|
|
|
|
|
#include "DataTypes.mqh"
|
2025-08-29 12:50:38 +01:00
|
|
|
#include <Trade/Trade.mqh>
|
2025-07-20 22:38:24 +01:00
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
//| Entry System Interface - For Future Extensions |
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
interface IEntryStrategy
|
|
|
|
{
|
|
|
|
EntrySignal CheckSignal(const MarketConditions &market);
|
|
|
|
bool ValidateSignal(const EntrySignal &signal);
|
|
|
|
void UpdatePerformance(bool success);
|
|
|
|
};
|
2025-08-27 14:21:02 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Entry System Class - Optimized & Extensible |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
class CEntrySystem
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
|
|
|
private:
|
|
|
|
//--- Configuration
|
|
|
|
EntrySystemConfig m_config;
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Indicator Management Structure
|
|
|
|
struct IndicatorSet
|
|
|
|
{
|
|
|
|
int ma_fast;
|
|
|
|
int ma_slow;
|
|
|
|
int ma_filter;
|
|
|
|
int rsi;
|
|
|
|
int adx;
|
|
|
|
int bb;
|
|
|
|
int macd;
|
|
|
|
int stoch;
|
|
|
|
int atr;
|
|
|
|
|
|
|
|
void Initialize()
|
|
|
|
{
|
|
|
|
ma_fast = INVALID_HANDLE;
|
|
|
|
ma_slow = INVALID_HANDLE;
|
|
|
|
ma_filter = INVALID_HANDLE;
|
|
|
|
rsi = INVALID_HANDLE;
|
|
|
|
adx = INVALID_HANDLE;
|
|
|
|
bb = INVALID_HANDLE;
|
|
|
|
macd = INVALID_HANDLE;
|
|
|
|
stoch = INVALID_HANDLE;
|
|
|
|
atr = INVALID_HANDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Release()
|
|
|
|
{
|
|
|
|
if(ma_fast != INVALID_HANDLE) IndicatorRelease(ma_fast);
|
|
|
|
if(ma_slow != INVALID_HANDLE) IndicatorRelease(ma_slow);
|
|
|
|
if(ma_filter != INVALID_HANDLE) IndicatorRelease(ma_filter);
|
|
|
|
if(rsi != INVALID_HANDLE) IndicatorRelease(rsi);
|
|
|
|
if(adx != INVALID_HANDLE) IndicatorRelease(adx);
|
|
|
|
if(bb != INVALID_HANDLE) IndicatorRelease(bb);
|
|
|
|
if(macd != INVALID_HANDLE) IndicatorRelease(macd);
|
|
|
|
if(stoch != INVALID_HANDLE) IndicatorRelease(stoch);
|
|
|
|
if(atr != INVALID_HANDLE) IndicatorRelease(atr);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
IndicatorSet m_indicators;
|
|
|
|
|
|
|
|
//--- Buffer Management
|
|
|
|
struct BufferCache
|
|
|
|
{
|
|
|
|
double ma_fast[3];
|
|
|
|
double ma_slow[3];
|
|
|
|
double ma_filter[1];
|
|
|
|
double rsi[3];
|
|
|
|
double adx[3];
|
|
|
|
double bb_upper[3];
|
|
|
|
double bb_lower[3];
|
|
|
|
double bb_middle[3];
|
|
|
|
double macd_main[3];
|
|
|
|
double macd_signal[3];
|
|
|
|
double atr[1];
|
|
|
|
datetime last_update;
|
|
|
|
|
|
|
|
void Reset()
|
|
|
|
{
|
|
|
|
ArrayInitialize(ma_fast, 0);
|
|
|
|
ArrayInitialize(ma_slow, 0);
|
|
|
|
ArrayInitialize(ma_filter, 0);
|
|
|
|
ArrayInitialize(rsi, 0);
|
|
|
|
ArrayInitialize(adx, 0);
|
|
|
|
ArrayInitialize(bb_upper, 0);
|
|
|
|
ArrayInitialize(bb_lower, 0);
|
|
|
|
ArrayInitialize(bb_middle, 0);
|
|
|
|
ArrayInitialize(macd_main, 0);
|
|
|
|
ArrayInitialize(macd_signal, 0);
|
|
|
|
ArrayInitialize(atr, 0);
|
|
|
|
last_update = 0;
|
|
|
|
}
|
|
|
|
};
|
2025-08-08 20:32:34 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
BufferCache m_buffers;
|
|
|
|
|
|
|
|
//--- Signal Tracking
|
|
|
|
struct SignalTracker
|
|
|
|
{
|
|
|
|
datetime last_signal_time;
|
|
|
|
ENUM_SIGNAL_TYPE last_signal_type;
|
|
|
|
int consecutive_signals;
|
|
|
|
int signals_generated;
|
|
|
|
int signals_filtered;
|
|
|
|
double signal_accuracy;
|
|
|
|
|
|
|
|
void Reset()
|
|
|
|
{
|
|
|
|
last_signal_time = 0;
|
|
|
|
last_signal_type = SIGNAL_NONE;
|
|
|
|
consecutive_signals = 0;
|
|
|
|
signals_generated = 0;
|
|
|
|
signals_filtered = 0;
|
|
|
|
signal_accuracy = 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
SignalTracker m_tracker;
|
|
|
|
|
|
|
|
//--- Performance Metrics
|
|
|
|
struct PerformanceData
|
|
|
|
{
|
|
|
|
int total_signals;
|
|
|
|
int successful_signals;
|
|
|
|
double avg_confidence;
|
|
|
|
double avg_rr_achieved;
|
|
|
|
datetime best_signal_time;
|
|
|
|
datetime worst_signal_time;
|
|
|
|
|
|
|
|
void Reset()
|
|
|
|
{
|
|
|
|
total_signals = 0;
|
|
|
|
successful_signals = 0;
|
|
|
|
avg_confidence = 0;
|
|
|
|
avg_rr_achieved = 0;
|
|
|
|
best_signal_time = 0;
|
|
|
|
worst_signal_time = 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
PerformanceData m_performance;
|
|
|
|
|
|
|
|
//--- Cache Management
|
2025-08-08 20:32:34 +01:00
|
|
|
struct SignalCache
|
|
|
|
{
|
|
|
|
EntrySignal signal;
|
2025-08-29 12:50:38 +01:00
|
|
|
datetime bar_time;
|
2025-08-08 20:32:34 +01:00
|
|
|
bool valid;
|
2025-08-29 12:50:38 +01:00
|
|
|
|
|
|
|
void Invalidate() { valid = false; }
|
|
|
|
bool IsValid(datetime current_bar) { return valid && bar_time == current_bar; }
|
2025-08-08 20:32:34 +01:00
|
|
|
};
|
2025-08-29 12:50:38 +01:00
|
|
|
|
|
|
|
SignalCache m_cache;
|
|
|
|
|
|
|
|
//--- Time Management
|
|
|
|
datetime m_last_bar_time;
|
|
|
|
bool m_new_bar;
|
|
|
|
|
|
|
|
//--- Strategy Methods
|
|
|
|
EntrySignal ExecuteMACrossStrategy(const MarketConditions &market);
|
|
|
|
EntrySignal ExecuteMomentumStrategy(const MarketConditions &market);
|
|
|
|
EntrySignal ExecuteBreakoutStrategy(const MarketConditions &market);
|
|
|
|
EntrySignal ExecuteMeanReversionStrategy(const MarketConditions &market);
|
|
|
|
EntrySignal ExecuteContrarianStrategy(const MarketConditions &market);
|
|
|
|
EntrySignal ExecuteMultiStrategy(const MarketConditions &market);
|
|
|
|
EntrySignal ExecuteCustomStrategy(const MarketConditions &market);
|
|
|
|
|
|
|
|
//--- Helper Methods
|
|
|
|
bool LoadIndicators();
|
|
|
|
bool UpdateBuffers();
|
2025-07-20 22:38:24 +01:00
|
|
|
bool IsNewBar();
|
2025-08-08 20:32:34 +01:00
|
|
|
bool ValidateMarketHours();
|
2025-08-29 12:50:38 +01:00
|
|
|
bool CheckNewsFilter(const MarketConditions &market);
|
|
|
|
double CalculateSignalScore(const EntrySignal &signal, const MarketConditions &market);
|
|
|
|
void EnhanceSignalWithContext(EntrySignal &signal, const MarketConditions &market);
|
|
|
|
void OptimizeStopTargets(EntrySignal &signal, const MarketConditions &market);
|
|
|
|
double GetATR();
|
|
|
|
|
|
|
|
//--- Validation Methods
|
|
|
|
bool ValidateConfiguration();
|
|
|
|
bool ValidateMarketConditions(const MarketConditions &market);
|
|
|
|
bool ValidateSignalQuality(const EntrySignal &signal, const MarketConditions &market);
|
|
|
|
|
|
|
|
//--- Utility Methods
|
|
|
|
void LogSignal(const EntrySignal &signal);
|
|
|
|
void UpdateStatistics(const EntrySignal &signal);
|
|
|
|
KeyLevel ConvertSRToKeyLevel(const SRLevel &sr);
|
2025-07-20 22:38:24 +01:00
|
|
|
|
|
|
|
public:
|
|
|
|
CEntrySystem();
|
|
|
|
~CEntrySystem();
|
|
|
|
|
|
|
|
//--- Initialization
|
2025-08-29 12:50:38 +01:00
|
|
|
bool Initialize(ENUM_ENTRY_MODE mode, bool enable_multiple = false,
|
|
|
|
int max_positions = 1);
|
2025-07-20 22:38:24 +01:00
|
|
|
bool Initialize(const EntrySystemConfig &config);
|
2025-08-29 12:50:38 +01:00
|
|
|
void Reset();
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Main Interface
|
|
|
|
EntrySignal CheckForEntry(const MarketConditions &market);
|
|
|
|
bool ValidateEntry(const EntrySignal &signal, const MarketConditions &market);
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Configuration
|
|
|
|
void SetConfiguration(const EntrySystemConfig &config);
|
|
|
|
EntrySystemConfig GetConfiguration() const { return m_config; }
|
|
|
|
|
|
|
|
//--- Performance
|
|
|
|
void UpdatePerformance(ulong ticket, bool success, double rr_achieved);
|
|
|
|
double GetSignalAccuracy() const { return m_tracker.signal_accuracy; }
|
|
|
|
int GetTotalSignals() const { return m_tracker.signals_generated; }
|
|
|
|
int GetFilteredSignals() const { return m_tracker.signals_filtered; }
|
|
|
|
|
|
|
|
//--- Status
|
|
|
|
bool IsReady() const;
|
|
|
|
datetime GetLastSignalTime() const { return m_tracker.last_signal_time; }
|
|
|
|
ENUM_SIGNAL_TYPE GetLastSignalType() const { return m_tracker.last_signal_type; }
|
|
|
|
|
|
|
|
//--- Diagnostics
|
|
|
|
string GetStatus();
|
|
|
|
void PrintDiagnostics();
|
2025-07-20 22:38:24 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Constructor |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
CEntrySystem::CEntrySystem()
|
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
m_indicators.Initialize();
|
|
|
|
m_buffers.Reset();
|
|
|
|
m_tracker.Reset();
|
|
|
|
m_performance.Reset();
|
|
|
|
m_cache.Invalidate();
|
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
m_last_bar_time = 0;
|
|
|
|
m_new_bar = false;
|
2025-08-29 12:50:38 +01:00
|
|
|
|
|
|
|
//--- Default configuration
|
|
|
|
m_config.entry_mode = ENTRY_MA_CROSS;
|
|
|
|
m_config.enable_multiple = false;
|
|
|
|
m_config.max_positions = 1;
|
|
|
|
m_config.min_time_between = 60;
|
|
|
|
m_config.signal_threshold = 60;
|
|
|
|
m_config.use_market_hours = true;
|
|
|
|
m_config.start_hour = 8;
|
|
|
|
m_config.end_hour = 20;
|
|
|
|
m_config.friday_close = true;
|
|
|
|
m_config.filter_news = true;
|
|
|
|
m_config.min_rr_ratio = 1.5;
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Destructor |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
CEntrySystem::~CEntrySystem()
|
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
m_indicators.Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Initialize with simple parameters |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool CEntrySystem::Initialize(ENUM_ENTRY_MODE mode, bool enable_multiple = false,
|
|
|
|
int max_positions = 1)
|
|
|
|
{
|
|
|
|
m_config.entry_mode = mode;
|
|
|
|
m_config.enable_multiple = enable_multiple;
|
|
|
|
m_config.max_positions = max_positions;
|
|
|
|
|
|
|
|
return Initialize(m_config);
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
//| Initialize with full configuration |
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool CEntrySystem::Initialize(const EntrySystemConfig &config)
|
|
|
|
{
|
|
|
|
m_config = config;
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Validate configuration
|
|
|
|
if(!ValidateConfiguration())
|
2025-08-08 20:32:34 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
Print("EntrySystem: Invalid configuration");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//--- Load required indicators
|
|
|
|
if(!LoadIndicators())
|
|
|
|
{
|
|
|
|
Print("EntrySystem: Failed to load indicators");
|
2025-08-08 20:32:34 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Reset tracking
|
|
|
|
m_tracker.Reset();
|
|
|
|
m_performance.Reset();
|
|
|
|
m_cache.Invalidate();
|
|
|
|
|
|
|
|
Print("EntrySystem initialized successfully: ", EnumToString(m_config.entry_mode));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Load indicators based on strategy |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool CEntrySystem::LoadIndicators()
|
|
|
|
{
|
|
|
|
//--- Always create ATR for stops
|
|
|
|
m_indicators.atr = iATR(_Symbol, PERIOD_CURRENT, 14);
|
|
|
|
if(m_indicators.atr == INVALID_HANDLE)
|
|
|
|
{
|
|
|
|
Print("EntrySystem: Failed to create ATR indicator");
|
|
|
|
return false;
|
|
|
|
}
|
2025-08-08 20:32:34 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Load strategy-specific indicators
|
2025-07-20 22:38:24 +01:00
|
|
|
switch(m_config.entry_mode)
|
|
|
|
{
|
|
|
|
case ENTRY_MA_CROSS:
|
|
|
|
case ENTRY_MA_PULLBACK:
|
2025-08-29 12:50:38 +01:00
|
|
|
m_indicators.ma_fast = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_EMA, PRICE_CLOSE);
|
|
|
|
m_indicators.ma_slow = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE);
|
|
|
|
m_indicators.ma_filter = iMA(_Symbol, PERIOD_CURRENT, 200, 0, MODE_SMA, PRICE_CLOSE);
|
2025-08-08 20:32:34 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
if(m_indicators.ma_fast == INVALID_HANDLE ||
|
|
|
|
m_indicators.ma_slow == INVALID_HANDLE)
|
|
|
|
return false;
|
2025-07-20 22:38:24 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ENTRY_MOMENTUM:
|
2025-08-29 12:50:38 +01:00
|
|
|
m_indicators.rsi = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
|
|
|
|
m_indicators.adx = iADX(_Symbol, PERIOD_CURRENT, 14);
|
|
|
|
m_indicators.macd = iMACD(_Symbol, PERIOD_CURRENT, 12, 26, 9, PRICE_CLOSE);
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
if(m_indicators.rsi == INVALID_HANDLE ||
|
|
|
|
m_indicators.adx == INVALID_HANDLE ||
|
|
|
|
m_indicators.macd == INVALID_HANDLE)
|
|
|
|
return false;
|
2025-07-20 22:38:24 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ENTRY_BREAKOUT:
|
2025-08-29 12:50:38 +01:00
|
|
|
case ENTRY_MEAN_REVERSION:
|
|
|
|
m_indicators.bb = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE);
|
|
|
|
m_indicators.rsi = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
|
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
if(m_config.entry_mode == ENTRY_BREAKOUT)
|
2025-08-29 12:50:38 +01:00
|
|
|
m_indicators.adx = iADX(_Symbol, PERIOD_CURRENT, 14);
|
2025-08-08 20:32:34 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
if(m_indicators.bb == INVALID_HANDLE ||
|
|
|
|
m_indicators.rsi == INVALID_HANDLE)
|
|
|
|
return false;
|
2025-07-20 22:38:24 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ENTRY_MULTI_STRATEGY:
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Load all indicators for multi-strategy
|
|
|
|
m_indicators.ma_fast = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_EMA, PRICE_CLOSE);
|
|
|
|
m_indicators.ma_slow = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE);
|
|
|
|
m_indicators.ma_filter = iMA(_Symbol, PERIOD_CURRENT, 200, 0, MODE_SMA, PRICE_CLOSE);
|
|
|
|
m_indicators.rsi = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
|
|
|
|
m_indicators.adx = iADX(_Symbol, PERIOD_CURRENT, 14);
|
|
|
|
m_indicators.bb = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE);
|
|
|
|
m_indicators.macd = iMACD(_Symbol, PERIOD_CURRENT, 12, 26, 9, PRICE_CLOSE);
|
|
|
|
m_indicators.stoch = iStochastic(_Symbol, PERIOD_CURRENT, 5, 3, 3, MODE_SMA, STO_LOWHIGH);
|
2025-08-08 20:32:34 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
if(m_indicators.ma_fast == INVALID_HANDLE ||
|
|
|
|
m_indicators.rsi == INVALID_HANDLE ||
|
|
|
|
m_indicators.bb == INVALID_HANDLE)
|
|
|
|
return false;
|
2025-07-20 22:38:24 +01:00
|
|
|
break;
|
2025-08-08 20:32:34 +01:00
|
|
|
|
|
|
|
case ENTRY_CONTRARIAN:
|
2025-08-29 12:50:38 +01:00
|
|
|
m_indicators.rsi = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
|
|
|
|
m_indicators.bb = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE);
|
|
|
|
m_indicators.stoch = iStochastic(_Symbol, PERIOD_CURRENT, 5, 3, 3, MODE_SMA, STO_LOWHIGH);
|
2025-08-08 20:32:34 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
if(m_indicators.rsi == INVALID_HANDLE ||
|
|
|
|
m_indicators.bb == INVALID_HANDLE)
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ENTRY_CUSTOM:
|
|
|
|
//--- Custom strategy - load minimal indicators
|
|
|
|
m_indicators.rsi = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
|
2025-08-08 20:32:34 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2025-07-20 22:38:24 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
//| Main entry check method |
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
EntrySignal CEntrySystem::CheckForEntry(const MarketConditions &market)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Check cache validity
|
|
|
|
datetime current_bar = iTime(_Symbol, PERIOD_CURRENT, 0);
|
|
|
|
if(m_cache.IsValid(current_bar))
|
2025-08-08 20:32:34 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
return m_cache.signal;
|
2025-08-08 20:32:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//--- Initialize empty signal
|
2025-07-20 22:38:24 +01:00
|
|
|
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-08-29 12:50:38 +01:00
|
|
|
signal.atr_value = 0;
|
|
|
|
signal.spread_value = 0;
|
|
|
|
signal.expected_rr = 0;
|
|
|
|
signal.suggested_risk = 0;
|
|
|
|
|
|
|
|
//--- Initialize key levels
|
|
|
|
for(int i = 0; i < 5; i++)
|
|
|
|
{
|
|
|
|
signal.key_levels[i].price = 0;
|
|
|
|
signal.key_levels[i].type = "";
|
|
|
|
signal.key_levels[i].strength = 0;
|
|
|
|
signal.key_levels[i].created = 0;
|
|
|
|
}
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
//--- Validate market conditions
|
2025-08-29 12:50:38 +01:00
|
|
|
if(!ValidateMarketConditions(market))
|
2025-08-08 20:32:34 +01:00
|
|
|
{
|
|
|
|
return signal;
|
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Check market hours
|
2025-08-08 20:32:34 +01:00
|
|
|
if(!ValidateMarketHours())
|
2025-07-30 18:55:56 +01:00
|
|
|
{
|
|
|
|
return signal;
|
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Check time since last signal
|
|
|
|
if(TimeCurrent() - m_tracker.last_signal_time < m_config.min_time_between * 60)
|
2025-08-08 20:32:34 +01:00
|
|
|
{
|
2025-07-20 22:38:24 +01:00
|
|
|
return signal;
|
2025-08-08 20:32:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//--- Check for new bar
|
|
|
|
m_new_bar = IsNewBar();
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Update buffers if needed
|
|
|
|
if(m_new_bar || m_config.entry_mode == ENTRY_BREAKOUT)
|
2025-08-08 20:32:34 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
if(!UpdateBuffers())
|
|
|
|
{
|
|
|
|
Print("EntrySystem: Failed to update buffers");
|
|
|
|
return signal;
|
|
|
|
}
|
2025-08-08 20:32:34 +01:00
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Execute strategy
|
2025-07-20 22:38:24 +01:00
|
|
|
switch(m_config.entry_mode)
|
|
|
|
{
|
|
|
|
case ENTRY_MA_CROSS:
|
|
|
|
case ENTRY_MA_PULLBACK:
|
2025-08-29 12:50:38 +01:00
|
|
|
signal = ExecuteMACrossStrategy(market);
|
2025-07-20 22:38:24 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ENTRY_MOMENTUM:
|
2025-08-29 12:50:38 +01:00
|
|
|
signal = ExecuteMomentumStrategy(market);
|
2025-07-20 22:38:24 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ENTRY_BREAKOUT:
|
2025-08-29 12:50:38 +01:00
|
|
|
signal = ExecuteBreakoutStrategy(market);
|
2025-07-20 22:38:24 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ENTRY_MEAN_REVERSION:
|
2025-08-29 12:50:38 +01:00
|
|
|
signal = ExecuteMeanReversionStrategy(market);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ENTRY_CONTRARIAN:
|
|
|
|
signal = ExecuteContrarianStrategy(market);
|
2025-07-20 22:38:24 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ENTRY_MULTI_STRATEGY:
|
2025-08-29 12:50:38 +01:00
|
|
|
signal = ExecuteMultiStrategy(market);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ENTRY_CUSTOM:
|
|
|
|
signal = ExecuteCustomStrategy(market);
|
2025-07-20 22:38:24 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
//--- Process valid signals
|
2025-07-20 22:38:24 +01:00
|
|
|
if(signal.signal_type != SIGNAL_NONE)
|
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
m_tracker.signals_generated++;
|
2025-07-30 18:55:56 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Enhance signal with context
|
|
|
|
EnhanceSignalWithContext(signal, market);
|
2025-07-30 18:55:56 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Optimize stops and targets
|
|
|
|
OptimizeStopTargets(signal, market);
|
2025-07-30 18:55:56 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Calculate final score
|
|
|
|
signal.confidence = CalculateSignalScore(signal, market);
|
2025-08-08 20:32:34 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Apply threshold filter
|
2025-08-08 20:32:34 +01:00
|
|
|
if(signal.confidence < m_config.signal_threshold)
|
2025-07-30 18:55:56 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
m_tracker.signals_filtered++;
|
2025-07-30 13:32:29 +01:00
|
|
|
signal.signal_type = SIGNAL_NONE;
|
|
|
|
return signal;
|
|
|
|
}
|
2025-07-30 18:55:56 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Final validation
|
|
|
|
if(ValidateSignalQuality(signal, market))
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
|
|
|
//--- Update tracking
|
2025-08-29 12:50:38 +01:00
|
|
|
m_tracker.last_signal_time = TimeCurrent();
|
|
|
|
m_tracker.last_signal_type = signal.signal_type;
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
if(signal.signal_type == m_tracker.last_signal_type)
|
|
|
|
m_tracker.consecutive_signals++;
|
2025-07-20 22:38:24 +01:00
|
|
|
else
|
2025-08-29 12:50:38 +01:00
|
|
|
m_tracker.consecutive_signals = 1;
|
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
//--- Cache the signal
|
2025-08-29 12:50:38 +01:00
|
|
|
m_cache.signal = signal;
|
|
|
|
m_cache.bar_time = current_bar;
|
|
|
|
m_cache.valid = true;
|
2025-08-08 20:32:34 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Log and update statistics
|
|
|
|
LogSignal(signal);
|
|
|
|
UpdateStatistics(signal);
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
m_tracker.signals_filtered++;
|
2025-07-20 22:38:24 +01:00
|
|
|
signal.signal_type = SIGNAL_NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return signal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
//| MA Crossover Strategy |
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
EntrySignal CEntrySystem::ExecuteMACrossStrategy(const MarketConditions &market)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
|
|
|
EntrySignal signal;
|
|
|
|
signal.signal_type = SIGNAL_NONE;
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Check for valid buffers
|
|
|
|
if(m_buffers.ma_fast[0] == 0 || m_buffers.ma_slow[0] == 0)
|
|
|
|
return signal;
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Detect crossover
|
|
|
|
bool bullish_cross = (m_buffers.ma_fast[2] <= m_buffers.ma_slow[2] &&
|
|
|
|
m_buffers.ma_fast[1] > m_buffers.ma_slow[1]);
|
|
|
|
bool bearish_cross = (m_buffers.ma_fast[2] >= m_buffers.ma_slow[2] &&
|
|
|
|
m_buffers.ma_fast[1] < m_buffers.ma_slow[1]);
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Apply filter if available
|
2025-07-20 22:38:24 +01:00
|
|
|
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
2025-08-29 12:50:38 +01:00
|
|
|
bool above_filter = (m_buffers.ma_filter[0] == 0) || (current_price > m_buffers.ma_filter[0]);
|
|
|
|
bool below_filter = (m_buffers.ma_filter[0] == 0) || (current_price < m_buffers.ma_filter[0]);
|
2025-07-30 13:32:29 +01:00
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
//--- Generate signal
|
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);
|
|
|
|
signal.comment = "MA Bullish Cross";
|
|
|
|
signal.confidence = 70;
|
2025-07-30 13:32:29 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Calculate crossover strength
|
|
|
|
double cross_angle = MathAbs(m_buffers.ma_fast[1] - m_buffers.ma_slow[1]) / _Point;
|
2025-08-08 20:32:34 +01:00
|
|
|
if(cross_angle > 20) signal.confidence += 10;
|
|
|
|
if(cross_angle > 50) signal.confidence += 10;
|
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);
|
|
|
|
signal.comment = "MA Bearish Cross";
|
|
|
|
signal.confidence = 70;
|
2025-07-30 13:32:29 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
double cross_angle = MathAbs(m_buffers.ma_fast[1] - m_buffers.ma_slow[1]) / _Point;
|
2025-08-08 20:32:34 +01:00
|
|
|
if(cross_angle > 20) signal.confidence += 10;
|
|
|
|
if(cross_angle > 50) signal.confidence += 10;
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Set initial stops
|
2025-08-08 20:32:34 +01:00
|
|
|
if(signal.signal_type != SIGNAL_NONE)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
double atr = GetATR();
|
2025-08-08 20:32:34 +01:00
|
|
|
signal.stop_loss_distance = atr * 2.0;
|
2025-07-20 22:38:24 +01:00
|
|
|
signal.take_profit_distance = atr * 3.0;
|
2025-08-08 20:32:34 +01:00
|
|
|
signal.atr_value = atr;
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return signal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
//| Momentum Strategy |
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
EntrySignal CEntrySystem::ExecuteMomentumStrategy(const MarketConditions &market)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
|
|
|
EntrySignal signal;
|
|
|
|
signal.signal_type = SIGNAL_NONE;
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Check ADX for trend strength
|
|
|
|
if(m_buffers.adx[0] < 25)
|
|
|
|
return signal;
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Get directional indicators
|
|
|
|
double di_plus = m_buffers.adx[1];
|
|
|
|
double di_minus = m_buffers.adx[2];
|
2025-07-20 22:38:24 +01:00
|
|
|
|
|
|
|
//--- Bullish momentum
|
2025-08-29 12:50:38 +01:00
|
|
|
if(m_buffers.rsi[0] > 50 && m_buffers.rsi[0] < 70 &&
|
|
|
|
m_buffers.rsi[0] > m_buffers.rsi[1] &&
|
|
|
|
di_plus > di_minus &&
|
|
|
|
m_buffers.macd_main[0] > m_buffers.macd_signal[0])
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
|
|
|
signal.signal_type = SIGNAL_BUY;
|
|
|
|
signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
|
|
signal.comment = "Momentum Buy";
|
2025-07-30 19:33:01 +01:00
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
signal.confidence = 60;
|
2025-08-29 12:50:38 +01:00
|
|
|
signal.confidence += (m_buffers.adx[0] - 25) * 0.8;
|
|
|
|
signal.confidence += (m_buffers.rsi[0] - 50) * 0.3;
|
2025-08-08 20:32:34 +01:00
|
|
|
signal.confidence = MathMin(signal.confidence, 95);
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
//--- Bearish momentum
|
2025-08-29 12:50:38 +01:00
|
|
|
else if(m_buffers.rsi[0] < 50 && m_buffers.rsi[0] > 30 &&
|
|
|
|
m_buffers.rsi[0] < m_buffers.rsi[1] &&
|
|
|
|
di_minus > di_plus &&
|
|
|
|
m_buffers.macd_main[0] < m_buffers.macd_signal[0])
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
|
|
|
signal.signal_type = SIGNAL_SELL;
|
|
|
|
signal.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
signal.comment = "Momentum Sell";
|
2025-07-30 19:33:01 +01:00
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
signal.confidence = 60;
|
2025-08-29 12:50:38 +01:00
|
|
|
signal.confidence += (m_buffers.adx[0] - 25) * 0.8;
|
|
|
|
signal.confidence += (50 - m_buffers.rsi[0]) * 0.3;
|
2025-08-08 20:32:34 +01:00
|
|
|
signal.confidence = MathMin(signal.confidence, 95);
|
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Set stops based on momentum strength
|
2025-08-08 20:32:34 +01:00
|
|
|
if(signal.signal_type != SIGNAL_NONE)
|
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
double atr = GetATR();
|
|
|
|
double stop_multiplier = (m_buffers.adx[0] > 40) ? 1.5 : 2.0;
|
|
|
|
double target_multiplier = (m_buffers.adx[0] > 40) ? 4.0 : 3.0;
|
2025-08-08 20:32:34 +01:00
|
|
|
|
|
|
|
signal.stop_loss_distance = atr * stop_multiplier;
|
|
|
|
signal.take_profit_distance = atr * target_multiplier;
|
|
|
|
signal.atr_value = atr;
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return signal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
//| Update indicator buffers |
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
bool CEntrySystem::UpdateBuffers()
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Check if update needed (cache for 1 second)
|
|
|
|
if(TimeCurrent() - m_buffers.last_update < 1)
|
|
|
|
return true;
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
bool success = true;
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Update ATR
|
|
|
|
if(m_indicators.atr != INVALID_HANDLE)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
if(CopyBuffer(m_indicators.atr, 0, 0, 1, m_buffers.atr) < 1)
|
|
|
|
success = false;
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
2025-08-29 12:50:38 +01:00
|
|
|
|
|
|
|
//--- Update MA buffers
|
|
|
|
if(m_indicators.ma_fast != INVALID_HANDLE)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
if(CopyBuffer(m_indicators.ma_fast, 0, 0, 3, m_buffers.ma_fast) < 3)
|
|
|
|
success = false;
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
if(m_indicators.ma_slow != INVALID_HANDLE)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
if(CopyBuffer(m_indicators.ma_slow, 0, 0, 3, m_buffers.ma_slow) < 3)
|
|
|
|
success = false;
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
if(m_indicators.ma_filter != INVALID_HANDLE)
|
|
|
|
{
|
|
|
|
if(CopyBuffer(m_indicators.ma_filter, 0, 0, 1, m_buffers.ma_filter) < 1)
|
|
|
|
success = false;
|
|
|
|
}
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Update RSI
|
|
|
|
if(m_indicators.rsi != INVALID_HANDLE)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
if(CopyBuffer(m_indicators.rsi, 0, 0, 3, m_buffers.rsi) < 3)
|
|
|
|
success = false;
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Update ADX
|
|
|
|
if(m_indicators.adx != INVALID_HANDLE)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
if(CopyBuffer(m_indicators.adx, 0, 0, 3, m_buffers.adx) < 3)
|
|
|
|
success = false;
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
2025-08-29 12:50:38 +01:00
|
|
|
|
|
|
|
//--- Update Bollinger Bands
|
|
|
|
if(m_indicators.bb != INVALID_HANDLE)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
CopyBuffer(m_indicators.bb, 0, 0, 3, m_buffers.bb_middle);
|
|
|
|
CopyBuffer(m_indicators.bb, 1, 0, 3, m_buffers.bb_upper);
|
|
|
|
CopyBuffer(m_indicators.bb, 2, 0, 3, m_buffers.bb_lower);
|
2025-08-08 20:32:34 +01:00
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Update MACD
|
|
|
|
if(m_indicators.macd != INVALID_HANDLE)
|
2025-08-08 20:32:34 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
CopyBuffer(m_indicators.macd, 0, 0, 3, m_buffers.macd_main);
|
|
|
|
CopyBuffer(m_indicators.macd, 1, 0, 3, m_buffers.macd_signal);
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
m_buffers.last_update = TimeCurrent();
|
|
|
|
return success;
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
//| Get ATR value |
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
double CEntrySystem::GetATR()
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
if(m_buffers.atr[0] > 0)
|
|
|
|
return m_buffers.atr[0];
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
//--- Fallback calculation
|
|
|
|
double sum = 0;
|
2025-08-29 12:50:38 +01:00
|
|
|
for(int i = 1; i <= 14; i++)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-08 20:32:34 +01:00
|
|
|
double high = iHigh(_Symbol, PERIOD_CURRENT, i);
|
|
|
|
double low = iLow(_Symbol, PERIOD_CURRENT, i);
|
2025-08-29 12:50:38 +01:00
|
|
|
double prev_close = iClose(_Symbol, PERIOD_CURRENT, i + 1);
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
double tr = MathMax(high - low,
|
|
|
|
MathMax(MathAbs(high - prev_close),
|
|
|
|
MathAbs(low - prev_close)));
|
2025-08-08 20:32:34 +01:00
|
|
|
sum += tr;
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
double atr = sum / 14.0;
|
|
|
|
|
|
|
|
//--- Ensure valid value
|
|
|
|
if(atr <= 0)
|
|
|
|
atr = 50 * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
return atr;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Enhance signal with market context |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
void CEntrySystem::EnhanceSignalWithContext(EntrySignal &signal, const MarketConditions &market)
|
|
|
|
{
|
|
|
|
signal.atr_value = GetATR();
|
|
|
|
signal.spread_value = market.spread;
|
|
|
|
|
|
|
|
//--- Add nearest key levels (properly convert SRLevel to KeyLevel)
|
|
|
|
int levels_added = 0;
|
|
|
|
|
|
|
|
for(int i = 0; i < market.sr_count && i < 10 && levels_added < 5; i++)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
double level_distance = MathAbs(signal.entry_price - market.sr_levels[i].price);
|
|
|
|
|
|
|
|
if(level_distance < signal.atr_value * 2)
|
|
|
|
{
|
|
|
|
signal.key_levels[levels_added] = ConvertSRToKeyLevel(market.sr_levels[i]);
|
|
|
|
levels_added++;
|
|
|
|
}
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
2025-08-08 20:32:34 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Calculate expected R:R
|
|
|
|
if(signal.stop_loss_distance > 0)
|
|
|
|
signal.expected_rr = signal.take_profit_distance / signal.stop_loss_distance;
|
|
|
|
else
|
|
|
|
signal.expected_rr = 0;
|
|
|
|
|
|
|
|
//--- Suggest risk based on confidence
|
|
|
|
if(signal.confidence >= 80)
|
|
|
|
signal.suggested_risk = 2.0;
|
|
|
|
else if(signal.confidence >= 60)
|
|
|
|
signal.suggested_risk = 1.0;
|
|
|
|
else
|
|
|
|
signal.suggested_risk = 0.5;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Convert SR level to Key level |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
KeyLevel CEntrySystem::ConvertSRToKeyLevel(const SRLevel &sr)
|
|
|
|
{
|
|
|
|
KeyLevel key;
|
|
|
|
key.price = sr.price;
|
|
|
|
key.strength = sr.strength;
|
|
|
|
key.created = sr.last_touch;
|
|
|
|
key.type = sr.is_support ? "Support" : "Resistance";
|
|
|
|
return key;
|
2025-08-08 20:32:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
//| Optimize stop and target distances |
|
2025-08-08 20:32:34 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
void CEntrySystem::OptimizeStopTargets(EntrySignal &signal, const MarketConditions &market)
|
2025-08-08 20:32:34 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Minimum stop distance
|
2025-08-08 20:32:34 +01:00
|
|
|
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)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-08 20:32:34 +01:00
|
|
|
case MARKET_VOLATILE:
|
2025-08-29 12:50:38 +01:00
|
|
|
volatility_factor = 1.3;
|
2025-08-08 20:32:34 +01:00
|
|
|
break;
|
|
|
|
case MARKET_QUIET:
|
2025-08-29 12:50:38 +01:00
|
|
|
volatility_factor = 0.8;
|
2025-08-08 20:32:34 +01:00
|
|
|
break;
|
2025-08-29 12:50:38 +01:00
|
|
|
case MARKET_TRENDING:
|
2025-08-08 20:32:34 +01:00
|
|
|
case MARKET_TRENDING_UP:
|
|
|
|
case MARKET_TRENDING_DOWN:
|
2025-08-29 12:50:38 +01:00
|
|
|
volatility_factor = 1.1;
|
2025-08-08 20:32:34 +01:00
|
|
|
break;
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
//--- Apply adjustments
|
|
|
|
signal.stop_loss_distance *= volatility_factor;
|
|
|
|
signal.take_profit_distance *= volatility_factor;
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Ensure minimums
|
2025-08-08 20:32:34 +01:00
|
|
|
signal.stop_loss_distance = MathMax(signal.stop_loss_distance, min_stop);
|
|
|
|
signal.take_profit_distance = MathMax(signal.take_profit_distance, min_stop);
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Ensure minimum R:R
|
2025-08-08 20:32:34 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Normalize to point
|
2025-08-08 20:32:34 +01:00
|
|
|
signal.stop_loss_distance = NormalizeDouble(signal.stop_loss_distance, _Digits);
|
|
|
|
signal.take_profit_distance = NormalizeDouble(signal.take_profit_distance, _Digits);
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Calculate signal score |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
double CEntrySystem::CalculateSignalScore(const EntrySignal &signal, const MarketConditions &market)
|
|
|
|
{
|
|
|
|
double score = signal.confidence;
|
|
|
|
|
|
|
|
//--- Market alignment bonus
|
|
|
|
if((signal.signal_type == SIGNAL_BUY &&
|
|
|
|
(market.condition == MARKET_TRENDING_UP || market.condition == MARKET_TRENDING)) ||
|
|
|
|
(signal.signal_type == SIGNAL_SELL &&
|
|
|
|
(market.condition == MARKET_TRENDING_DOWN || market.condition == MARKET_TRENDING)))
|
|
|
|
{
|
|
|
|
score += 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
//--- Trend strength bonus
|
|
|
|
if(MathAbs(market.trend_strength) > 30)
|
|
|
|
score += 5;
|
|
|
|
if(MathAbs(market.trend_strength) > 50)
|
|
|
|
score += 5;
|
|
|
|
|
|
|
|
//--- Volatility adjustment
|
|
|
|
if(market.volatility_percentile > 80)
|
|
|
|
score -= 5;
|
|
|
|
else if(market.volatility_percentile < 20)
|
|
|
|
score += 5;
|
|
|
|
|
|
|
|
//--- Support/Resistance proximity bonus
|
|
|
|
for(int i = 0; i < market.sr_count && i < 10; i++)
|
|
|
|
{
|
|
|
|
double distance = MathAbs(signal.entry_price - market.sr_levels[i].price);
|
|
|
|
if(distance < market.atr * 0.5)
|
|
|
|
{
|
|
|
|
score += market.sr_levels[i].strength / 20.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return MathMin(score, 100);
|
|
|
|
}
|
|
|
|
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-08 20:32:34 +01:00
|
|
|
//| Validate signal quality |
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
bool CEntrySystem::ValidateSignalQuality(const EntrySignal &signal, const MarketConditions &market)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Check stop/target validity
|
2025-08-08 20:32:34 +01:00
|
|
|
if(signal.stop_loss_distance <= 0 || signal.take_profit_distance <= 0)
|
|
|
|
return false;
|
2025-07-20 22:38:24 +01:00
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
//--- Check spread
|
2025-08-29 12:50:38 +01:00
|
|
|
double max_spread = signal.stop_loss_distance * 0.2;
|
2025-08-08 20:32:34 +01:00
|
|
|
if(market.spread > max_spread)
|
2025-07-20 22:38:24 +01:00
|
|
|
return false;
|
2025-07-22 18:11:53 +01:00
|
|
|
|
|
|
|
//--- Check news filter
|
2025-08-08 20:32:34 +01:00
|
|
|
if(m_config.filter_news && market.news_event)
|
2025-07-22 18:11:53 +01:00
|
|
|
return false;
|
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
//--- 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)
|
|
|
|
return false;
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Strategy-specific validation
|
2025-08-08 20:32:34 +01:00
|
|
|
if(m_config.entry_mode == ENTRY_MOMENTUM &&
|
|
|
|
(market.condition == MARKET_RANGING || market.condition == MARKET_QUIET))
|
2025-07-22 18:11:53 +01:00
|
|
|
return false;
|
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
if(m_config.entry_mode == ENTRY_MEAN_REVERSION &&
|
|
|
|
(market.condition == MARKET_TRENDING_UP || market.condition == MARKET_TRENDING_DOWN))
|
2025-07-22 18:11:53 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2025-07-30 19:33:01 +01:00
|
|
|
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
//| Validate market conditions |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool CEntrySystem::ValidateMarketConditions(const MarketConditions &market)
|
|
|
|
{
|
|
|
|
if(market.volatility <= 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if(market.atr <= 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if(market.spread < 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Validate configuration |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool CEntrySystem::ValidateConfiguration()
|
|
|
|
{
|
|
|
|
if(m_config.max_positions <= 0)
|
|
|
|
m_config.max_positions = 1;
|
|
|
|
|
|
|
|
if(m_config.min_time_between < 0)
|
|
|
|
m_config.min_time_between = 0;
|
|
|
|
|
|
|
|
if(m_config.signal_threshold < 0)
|
|
|
|
m_config.signal_threshold = 0;
|
|
|
|
if(m_config.signal_threshold > 100)
|
|
|
|
m_config.signal_threshold = 100;
|
|
|
|
|
|
|
|
if(m_config.start_hour < 0 || m_config.start_hour > 23)
|
|
|
|
m_config.start_hour = 0;
|
|
|
|
|
|
|
|
if(m_config.end_hour < 1 || m_config.end_hour > 24)
|
|
|
|
m_config.end_hour = 24;
|
|
|
|
|
|
|
|
if(m_config.min_rr_ratio < 0)
|
|
|
|
m_config.min_rr_ratio = 0;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Check if new bar |
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool CEntrySystem::IsNewBar()
|
|
|
|
{
|
|
|
|
datetime current_bar_time = iTime(_Symbol, PERIOD_CURRENT, 0);
|
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
if(current_bar_time != m_last_bar_time)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-08 20:32:34 +01:00
|
|
|
m_last_bar_time = current_bar_time;
|
2025-07-20 22:38:24 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2025-08-08 20:32:34 +01:00
|
|
|
//| Validate market hours |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool CEntrySystem::ValidateMarketHours()
|
|
|
|
{
|
|
|
|
if(!m_config.use_market_hours)
|
|
|
|
return true;
|
2025-08-29 12:50:38 +01:00
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
MqlDateTime current_time;
|
|
|
|
TimeCurrent(current_time);
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Weekend check
|
2025-08-08 20:32:34 +01:00
|
|
|
if(current_time.day_of_week == 0 || current_time.day_of_week == 6)
|
|
|
|
return false;
|
2025-08-29 12:50:38 +01:00
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
//--- Friday close
|
|
|
|
if(m_config.friday_close && current_time.day_of_week == 5 && current_time.hour >= 20)
|
|
|
|
return false;
|
2025-08-29 12:50:38 +01:00
|
|
|
|
|
|
|
//--- Trading hours
|
2025-08-08 20:32:34 +01:00
|
|
|
if(current_time.hour < m_config.start_hour || current_time.hour >= m_config.end_hour)
|
|
|
|
return false;
|
2025-08-29 12:50:38 +01:00
|
|
|
|
2025-08-08 20:32:34 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
//| Log signal details |
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
void CEntrySystem::LogSignal(const EntrySignal &signal)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
Print("EntrySystem Signal: ", signal.comment,
|
|
|
|
" | Type: ", SignalTypeToString(signal.signal_type),
|
|
|
|
" | Confidence: ", DoubleToString(signal.confidence, 1), "%",
|
|
|
|
" | R:R: ", DoubleToString(signal.expected_rr, 2),
|
|
|
|
" | Risk: ", DoubleToString(signal.suggested_risk, 1), "%");
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
//| Update statistics |
|
2025-07-20 22:38:24 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
void CEntrySystem::UpdateStatistics(const EntrySignal &signal)
|
2025-07-20 22:38:24 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
m_performance.total_signals++;
|
|
|
|
m_performance.avg_confidence =
|
|
|
|
(m_performance.avg_confidence * (m_performance.total_signals - 1) + signal.confidence) /
|
|
|
|
m_performance.total_signals;
|
2025-07-20 22:38:24 +01:00
|
|
|
}
|
|
|
|
|
2025-07-30 13:32:29 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
//| Get system status |
|
2025-07-30 13:32:29 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
string CEntrySystem::GetStatus()
|
2025-07-30 13:32:29 +01:00
|
|
|
{
|
2025-08-29 12:50:38 +01:00
|
|
|
string status = "EntrySystem Status:\n";
|
|
|
|
status += "Mode: " + EnumToString(m_config.entry_mode) + "\n";
|
|
|
|
status += "Signals Generated: " + IntegerToString(m_tracker.signals_generated) + "\n";
|
|
|
|
status += "Signals Filtered: " + IntegerToString(m_tracker.signals_filtered) + "\n";
|
|
|
|
status += "Accuracy: " + DoubleToString(m_tracker.signal_accuracy * 100, 1) + "%\n";
|
|
|
|
status += "Last Signal: " + TimeToString(m_tracker.last_signal_time) + "\n";
|
|
|
|
|
|
|
|
return status;
|
2025-07-30 13:32:29 +01:00
|
|
|
}
|
|
|
|
|
2025-07-30 18:55:56 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-29 12:50:38 +01:00
|
|
|
//| Additional strategy implementations (stubs for extension) |
|
2025-07-30 18:55:56 +01:00
|
|
|
//+------------------------------------------------------------------+
|
2025-08-08 20:32:34 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
EntrySignal CEntrySystem::ExecuteBreakoutStrategy(const MarketConditions &market)
|
2025-07-30 18:55:56 +01:00
|
|
|
{
|
2025-08-08 20:32:34 +01:00
|
|
|
EntrySignal signal;
|
|
|
|
signal.signal_type = SIGNAL_NONE;
|
2025-08-29 12:50:38 +01:00
|
|
|
// Implementation here
|
2025-08-08 20:32:34 +01:00
|
|
|
return signal;
|
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
EntrySignal CEntrySystem::ExecuteMeanReversionStrategy(const MarketConditions &market)
|
2025-08-08 20:32:34 +01:00
|
|
|
{
|
|
|
|
EntrySignal signal;
|
|
|
|
signal.signal_type = SIGNAL_NONE;
|
2025-08-29 12:50:38 +01:00
|
|
|
// Implementation here
|
|
|
|
return signal;
|
|
|
|
}
|
|
|
|
|
|
|
|
EntrySignal CEntrySystem::ExecuteContrarianStrategy(const MarketConditions &market)
|
|
|
|
{
|
|
|
|
EntrySignal signal;
|
|
|
|
signal.signal_type = SIGNAL_NONE;
|
|
|
|
// Implementation here
|
|
|
|
return signal;
|
|
|
|
}
|
|
|
|
|
|
|
|
EntrySignal CEntrySystem::ExecuteMultiStrategy(const MarketConditions &market)
|
|
|
|
{
|
|
|
|
EntrySignal signal;
|
|
|
|
signal.signal_type = SIGNAL_NONE;
|
|
|
|
// Implementation here
|
2025-08-08 20:32:34 +01:00
|
|
|
return signal;
|
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
EntrySignal CEntrySystem::ExecuteCustomStrategy(const MarketConditions &market)
|
2025-08-08 20:32:34 +01:00
|
|
|
{
|
|
|
|
EntrySignal signal;
|
|
|
|
signal.signal_type = SIGNAL_NONE;
|
2025-08-29 12:50:38 +01:00
|
|
|
// Custom implementation here
|
|
|
|
return signal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Update performance tracking |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
void CEntrySystem::UpdatePerformance(ulong ticket, bool success, double rr_achieved)
|
|
|
|
{
|
|
|
|
if(success)
|
|
|
|
m_performance.successful_signals++;
|
2025-08-08 20:32:34 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
m_performance.avg_rr_achieved =
|
|
|
|
(m_performance.avg_rr_achieved * m_performance.total_signals + rr_achieved) /
|
|
|
|
(m_performance.total_signals + 1);
|
2025-07-30 19:33:01 +01:00
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
//--- Update accuracy
|
|
|
|
double alpha = 0.1;
|
|
|
|
m_tracker.signal_accuracy = m_tracker.signal_accuracy * (1 - alpha) + (success ? 1.0 : 0.0) * alpha;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Check if system is ready |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool CEntrySystem::IsReady() const
|
|
|
|
{
|
|
|
|
return m_indicators.atr != INVALID_HANDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Reset system |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
void CEntrySystem::Reset()
|
|
|
|
{
|
|
|
|
m_tracker.Reset();
|
|
|
|
m_performance.Reset();
|
|
|
|
m_cache.Invalidate();
|
|
|
|
m_buffers.Reset();
|
2025-07-30 18:55:56 +01:00
|
|
|
}
|
|
|
|
|
2025-08-29 12:50:38 +01:00
|
|
|
#endif // ENTRY_SYSTEM_MQH
|