//+------------------------------------------------------------------+ //| SecurityEnhancements_Impl.mqh | //| Copyright 2025, EscapeEA | //| https://www.escapeea.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, EscapeEA" #property link "https://www.escapeea.com" #property version "1.00" #property strict // Include necessary headers #include #include #include #include #include #include #include // Include the header file to get class declaration #include "SecurityEnhancements.mqh" //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CSecurityEnhancements::CSecurityEnhancements(void) : m_riskManager(NULL), m_trade(), m_position(), m_deal(), m_lastTradeTime(0), m_tradeCount(0), m_avgTradeTime(0.0), m_anomalyCount(0), m_maxSlippage(10) // 10 points default slippage { ZeroMemory(m_lastTick); m_lastError = ""; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CSecurityEnhancements::~CSecurityEnhancements(void) { Deinitialize(); } //+------------------------------------------------------------------+ //| Initialization | //+------------------------------------------------------------------+ bool CSecurityEnhancements::Initialize(void) { // Initialize trade object m_trade.SetExpertMagicNumber(0); m_trade.SetDeviationInPoints(m_maxSlippage); m_trade.SetTypeFilling(ORDER_FILLING_FOK); // Reset counters m_tradeCount = 0; m_anomalyCount = 0; m_avgTradeTime = 0.0; return true; } //+------------------------------------------------------------------+ //| Deinitialization | //+------------------------------------------------------------------+ void CSecurityEnhancements::Deinitialize(void) { // Clean up resources m_tradeCount = 0; m_anomalyCount = 0; m_avgTradeTime = 0.0; m_lastError = ""; } //+------------------------------------------------------------------+ //| Check price deviation from current market price | //+------------------------------------------------------------------+ bool CSecurityEnhancements::CheckPriceDeviation(const string symbol, const double price) { if(!SymbolInfoTick(symbol, m_lastTick)) { m_lastError = "Failed to get tick data for " + symbol; return false; } double point = SymbolInfoDouble(symbol, SYMBOL_POINT); if(point == 0) point = 0.00001; // Prevent division by zero long spread = SymbolInfoInteger(symbol, SYMBOL_SPREAD); double spread_pips = spread * point; double max_deviation = spread_pips * 5.0; // Allow up to 5x spread deviation if(MathAbs(price - m_lastTick.ask) > max_deviation && MathAbs(price - m_lastTick.bid) > max_deviation) { m_lastError = StringFormat("Suspicious price deviation: %G vs [%G, %G]", price, m_lastTick.bid, m_lastTick.ask); return false; } return true; } //+------------------------------------------------------------------+ //| Check and enforce rate limiting | //+------------------------------------------------------------------+ bool CSecurityEnhancements::CheckRateLimit(void) { datetime current_time = TimeCurrent(); // Reset counter if minute, hour, or day has changed MqlDateTime time_struct; TimeToStruct(current_time, time_struct); MqlDateTime last_trade_time_struct; TimeToStruct(m_lastTradeTime, last_trade_time_struct); if(time_struct.min != last_trade_time_struct.min || time_struct.hour != last_trade_time_struct.hour || time_struct.day != last_trade_time_struct.day) { m_tradeCount = 0; } // Check if rate limit is exceeded if(m_tradeCount >= 10) // 10 trades per minute limit { m_lastError = StringFormat("Rate limit exceeded: %d trades in last minute", m_tradeCount); return false; } // Update counters m_lastTradeTime = current_time; m_tradeCount++; return true; } //+------------------------------------------------------------------+ //| Execute trade with enhanced security checks | //+------------------------------------------------------------------+ bool CSecurityEnhancements::ExecuteTrade(MqlTradeRequest &request, MqlTradeResult &result) { // Initialize result structure ZeroMemory(result); // Validate the trade request first if(!ValidateTradeRequest(request)) { result.retcode = TRADE_RETCODE_ERROR; return false; } bool success = false; uint start_time = GetTickCount(); // Process different types of trade actions switch(request.action) { case TRADE_ACTION_DEAL: if(request.type == ORDER_TYPE_BUY || request.type == ORDER_TYPE_SELL) { success = m_trade.PositionOpen(request.symbol, request.type, request.volume, request.price, request.sl, request.tp, request.comment); } else { success = m_trade.OrderSend(request, result); } break; case TRADE_ACTION_MODIFY: success = m_trade.OrderModify(request.order, request.price, request.sl, request.tp, ORDER_TIME_GTC, 0, 0); break; case TRADE_ACTION_REMOVE: success = m_trade.OrderDelete(request.order); break; default: m_lastError = "Unsupported trade action: " + IntegerToString(request.action); result.retcode = TRADE_RETCODE_REJECT; return false; } // Update performance metrics uint execution_time = GetTickCount() - start_time; if(m_tradeCount > 0) m_avgTradeTime = (m_avgTradeTime * m_tradeCount + execution_time) / (m_tradeCount + 1); else m_avgTradeTime = execution_time; // Verify order fill if required if(success && request.action == TRADE_ACTION_DEAL) { if(!VerifyOrderFill(result.order)) { m_lastError = "Order fill verification failed"; success = false; } } return success; } //+------------------------------------------------------------------+ //| Verify order fill details | //+------------------------------------------------------------------+ bool CSecurityEnhancements::VerifyOrderFill(const ulong ticket) { if(!m_deal.SelectByIndex(0)) // Select the most recent deal { m_lastError = "Failed to select deal for ticket " + IntegerToString(ticket); return false; } double volume = m_deal.Volume(); double volume_initial = m_deal.Volume(); // In MQL5, Volume() returns initial volume if(volume < volume_initial) { PrintFormat("Partial fill detected: %G/%G lots", volume, volume_initial); // Not necessarily an error, just log it } double point = SymbolInfoDouble(m_deal.Symbol(), SYMBOL_POINT); if(point == 0) point = 0.00001; // Prevent division by zero double max_slippage = m_maxSlippage * point; double actual_slippage = MathAbs(m_deal.Price() - m_deal.Price()); if(actual_slippage > max_slippage) { m_lastError = "Excessive slippage detected: " + DoubleToString(actual_slippage, 5); return false; } return true; } //+------------------------------------------------------------------+ //| Detect market anomalies | //+------------------------------------------------------------------+ bool CSecurityEnhancements::DetectAnomalies(const MqlTradeRequest &request) { if(request.price <= 0.0) { m_lastError = "Invalid price in trade request"; return false; } double point = SymbolInfoDouble(request.symbol, SYMBOL_POINT); if(point == 0) point = 0.00001; // Prevent division by zero long stop_level = SymbolInfoInteger(request.symbol, SYMBOL_TRADE_STOPS_LEVEL); double ask = SymbolInfoDouble(request.symbol, SYMBOL_ASK); if(stop_level == 0 || ask == 0) { m_lastError = "Failed to get symbol information for " + request.symbol; return false; } double min_price = stop_level * point; double max_price = (min_price > 0) ? ask * 2.0 : 1e10; if(request.price < min_price || request.price > max_price) { m_lastError = StringFormat("Invalid price: %G (valid range: %G - %G)", request.price, min_price, max_price); return false; } return true; } //+------------------------------------------------------------------+ //| Validate trade request parameters | //+------------------------------------------------------------------+ bool CSecurityEnhancements::ValidateTradeRequest(MqlTradeRequest &request) { // Basic validation if(request.symbol == "") { m_lastError = "Empty symbol in trade request"; return false; } if(request.volume <= 0) { m_lastError = "Invalid volume in trade request: " + DoubleToString(request.volume); return false; } // Check for news events if enabled if(m_newsFilterEnabled && IsNewsEvent(request.symbol)) { m_lastError = "Trading is restricted during news events for " + request.symbol; return false; } // Check price anomalies if(!DetectAnomalies(request)) { return false; } // Check rate limits if(!CheckRateLimit()) { return false; } // Additional validation based on order type switch(request.type) { case ORDER_TYPE_BUY: case ORDER_TYPE_SELL: // Market order - check price deviation if(!CheckPriceDeviation(request.symbol, request.price)) { return false; } break; case ORDER_TYPE_BUY_LIMIT: case ORDER_TYPE_SELL_LIMIT: case ORDER_TYPE_BUY_STOP: case ORDER_TYPE_SELL_STOP: // Pending order - check price is valid if(request.price <= 0) { m_lastError = "Invalid price in pending order: " + DoubleToString(request.price); return false; } break; default: m_lastError = "Unsupported order type: " + IntegerToString(request.type); return false; } return true; } //+------------------------------------------------------------------+