| //+------------------------------------------------------------------+
|
| //| TradeExecutor_Impl.mqh |
|
| //| Implementation of CTradeExecutor methods |
|
| //+------------------------------------------------------------------+
|
| #property strict
|
|
|
| #include "TradeExecutor.mqh" // include header for class definition
|
|
|
| // NOTE: This file contains implementations of CTradeExecutor methods
|
| // Only method implementations should be in this file - no class definitions.
|
|
|
| //+------------------------------------------------------------------+
|
| //| Check if trading is allowed |
|
| //+------------------------------------------------------------------+
|
| bool CTradeExecutor::IsTradingAllowed()
|
| {
|
| if(!m_isInitialized)
|
| {
|
| m_lastError = "Trade executor not initialized";
|
| return false;
|
| }
|
| if(!m_isTradingAllowed)
|
| {
|
| m_lastError = "Trading is disabled by configuration";
|
| return false;
|
| }
|
| if(m_circuitBroken)
|
| {
|
| if(TimeCurrent() - m_circuitBreakTime < m_circuitBreakPeriod)
|
| {
|
| m_lastError = "Circuit breaker active";
|
| return false;
|
| }
|
| else
|
| {
|
| m_circuitBroken = false;
|
| m_consecutiveErrors = 0;
|
| }
|
| }
|
|
|
| // Rate limit check
|
| datetime currentTime = TimeCurrent();
|
| if(TimeMinute(currentTime) != TimeMinute(m_currentMinute))
|
| {
|
| m_currentMinute = currentTime - (currentTime % 60);
|
| m_ordersThisMinute = 0;
|
| }
|
|
|
| if(m_ordersThisMinute >= m_maxOrdersPerMinute)
|
| {
|
| m_lastError = "Rate limit exceeded";
|
| return false;
|
| }
|
|
|
| return true;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Constructor |
|
| //+------------------------------------------------------------------+
|
| CTradeExecutor::CTradeExecutor() :
|
| m_totalTrades(0),
|
| m_successfulTrades(0),
|
| m_failedTrades(0),
|
| m_totalSlippage(0.0),
|
| m_avgExecutionTime(0.0),
|
| m_isInitialized(false),
|
| m_isTradingAllowed(false),
|
| m_riskManager(NULL),
|
| m_retryDelay(1000), // 1 second default retry delay
|
| m_maxSlippage(10), // 10 points default slippage
|
| m_useCircuitBreaker(true),
|
| m_checkMarketConditions(true)
|
| {
|
| // Initialize trade object with default settings
|
| m_trade.SetDeviationInPoints(m_maxSlippage);
|
| m_trade.SetExpertMagicNumber(0);
|
| m_trade.SetMarginMode();
|
| m_trade.SetTypeFilling(ORDER_FILLING_FOK);
|
|
|
| // Initialize arrays
|
| m_tradeHistory = new CArrayObj();
|
| m_pendingOrders = new CArrayObj();
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Destructor |
|
| //+------------------------------------------------------------------+
|
| CTradeExecutor::~CTradeExecutor()
|
| {
|
| // Clean up trade history
|
| if(CheckPointer(m_tradeHistory) == POINTER_DYNAMIC)
|
| delete m_tradeHistory;
|
|
|
| // Clean up pending orders
|
| if(CheckPointer(m_pendingOrders) == POINTER_DYNAMIC)
|
| delete m_pendingOrders;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Reset circuit breaker |
|
| //+------------------------------------------------------------------+
|
| void CTradeExecutor::ResetCircuitBreaker()
|
| {
|
| m_circuitBreakTime = 0;
|
| m_consecutiveErrors = 0;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Clean up old trade records |
|
| //+------------------------------------------------------------------+
|
| void CTradeExecutor::CleanupOldRecords()
|
| {
|
| datetime cutoff = TimeCurrent() - 7 * 24 * 60 * 60; // 1 week
|
|
|
| for(int i = m_tradeHistory.Total() - 1; i >= 0; i--)
|
| {
|
| CTradeRecord* record = m_tradeHistory.At(i);
|
| if(record != NULL && record.time < cutoff)
|
| {
|
| m_tradeHistory.Delete(i);
|
| }
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Update circuit breaker state |
|
| //+------------------------------------------------------------------+
|
| void CTradeExecutor::UpdateCircuitBreaker(bool errorOccurred)
|
| {
|
| if(errorOccurred)
|
| {
|
| m_consecutiveErrors++;
|
|
|
| // If we've hit the maximum consecutive errors, activate circuit breaker
|
| if(m_consecutiveErrors >= m_maxConsecutiveErrors)
|
| {
|
| m_circuitBreakTime = TimeCurrent() + 300; // 5 minutes cooldown
|
| m_isTradingAllowed = false;
|
| Print("Circuit breaker activated! Trading paused for 5 minutes.");
|
| }
|
| }
|
| else
|
| {
|
| // Reset error counter on successful operation
|
| m_consecutiveErrors = 0;
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Initialize |
|
| //+------------------------------------------------------------------+
|
| bool CTradeExecutor::Initialize(CRiskManager *riskManager)
|
| {
|
| if(m_isInitialized)
|
| return true;
|
| m_trade.SetExpertMagicNumber(0);
|
| m_trade.SetDeviationInPoints(m_maxSlippage);
|
| m_trade.SetAsyncMode(true);
|
| m_riskManager = riskManager;
|
| if(!m_security.Initialize())
|
| {
|
| m_lastError = "Failed to initialize security enhancements: "+m_security.GetLastError();
|
| return false;
|
| }
|
| ResetCircuitBreaker();
|
| m_totalTrades = m_successfulTrades = m_failedTrades = 0;
|
| m_totalSlippage = m_avgExecutionTime = 0.0;
|
| m_trade.SetDeviationInPoints(InpMaxSlippage);
|
| CleanupOldRecords();
|
| m_isInitialized = true;
|
| m_isTradingAllowed = true;
|
| Print("TradeExecutor: Initialized successfully");
|
| return true;
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Deinitialize |
|
| //+------------------------------------------------------------------+
|
| void CTradeExecutor::Deinitialize(void)
|
| {
|
| if(!m_isInitialized)
|
| return;
|
| m_tradeHistory.Clear();
|
| m_activeTrades.Clear();
|
| m_hasPendingOrder = false;
|
| ZeroMemory(m_pendingOrder);
|
| m_circuitBroken = false;
|
| m_consecutiveErrors = 0;
|
| m_circuitBreakTime = 0;
|
| s_ordersThisMinute = 0;
|
| s_currentMinute = 0;
|
| m_totalTrades = m_successfulTrades = m_failedTrades = 0;
|
| m_totalSlippage = m_avgExecutionTime = 0.0;
|
| m_lastError = "";
|
| m_isInitialized = false;
|
| m_isTradingAllowed = false;
|
| Print("TradeExecutor: Deinitialized");
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Open a new market position with comprehensive security checks |
|
| //+------------------------------------------------------------------+
|
| bool CTradeExecutor::OpenPosition(string symbol, ENUM_ORDER_TYPE type, double volume,
|
| double price, double sl, double tp, string comment,
|
| int magic, datetime expiration, double maxSlippage)
|
| {
|
| // Check if trading is allowed
|
| if(!m_isInitialized || !m_isTradingAllowed)
|
| {
|
| m_lastError = "Trading is not allowed. Check initialization.";
|
| Print("TradeExecutor Error: ", m_lastError);
|
| return false;
|
| }
|
|
|
| // Check circuit breaker
|
| if(m_circuitBroken)
|
| {
|
| if(TimeCurrent() >= m_circuitBreakTime + m_circuitBreakTime)
|
| {
|
| Print("Circuit breaker cooldown period ended. Resuming normal operations.");
|
| ResetCircuitBreaker();
|
| }
|
| else
|
| {
|
| int remaining = (int)(m_circuitBreakTime + m_circuitBreakTime - TimeCurrent());
|
| m_lastError = "Circuit breaker active. Trading suspended for " + IntegerToString(remaining) + " seconds";
|
| Print("TradeExecutor Error: ", m_lastError);
|
| return false;
|
| }
|
| }
|
|
|
| // Check rate limiting
|
| if(!CheckRateLimit())
|
| {
|
| m_lastError = "Rate limit exceeded";
|
| return false;
|
| }
|
|
|
| // Check market conditions if enabled
|
| if(m_checkMarketConditions && !CheckMarketConditions(symbol))
|
| {
|
| m_lastError = "Market conditions check failed for " + symbol;
|
| Print("TradeExecutor Error: ", m_lastError);
|
| return false;
|
| }
|
|
|
| // Prepare order request with additional security parameters
|
| SOrderRequest request = {};
|
| request.symbol = symbol;
|
| request.type = type;
|
| request.volume = volume;
|
| request.price = price;
|
| request.sl = sl;
|
| request.tp = tp;
|
| request.comment = comment;
|
| request.magic = (magic > 0) ? magic : m_trade.RequestMagic();
|
| request.expiration = expiration;
|
| request.attempts = 0;
|
| request.reqId = IntegerToString(GetTickCount()) + "_" + IntegerToString(OrdersTotal());
|
| request.maxSlippage = (maxSlippage > 0) ? maxSlippage : InpMaxSlippage;
|
| request.requireVerification = InpVerifyOrderFills;
|
|
|
| // Log the trade attempt
|
| if(InpLogAllTrades)
|
| {
|
| string orderType = (type == ORDER_TYPE_BUY) ? "BUY" : "SELL";
|
| PrintFormat("TradeExecutor: Attempting %s %s %.2f lots @ %s (SL: %s, TP: %s)",
|
| orderType, symbol, volume, DoubleToString(price, _Digits),
|
| DoubleToString(sl, _Digits), DoubleToString(tp, _Digits));
|
| }
|
|
|
| // Execute the order with retry logic
|
| bool result = false;
|
| MqlTradeRequest tradeRequest = {};
|
| MqlTradeResult tradeResult = {};
|
|
|
| // Prepare trade request
|
| tradeRequest.action = TRADE_ACTION_DEAL;
|
| tradeRequest.symbol = symbol;
|
| tradeRequest.volume = volume;
|
| tradeRequest.type = type;
|
| tradeRequest.price = price;
|
| tradeRequest.sl = sl;
|
| tradeRequest.tp = tp;
|
| tradeRequest.deviation = (ushort)request.maxSlippage;
|
| tradeRequest.magic = request.magic;
|
| tradeRequest.comment = comment;
|
| tradeRequest.type_filling = ORDER_FILLING_FOK;
|
|
|
| // Validate the request using the unified validator
|
| if(!ValidateOrder(symbol, type, volume, price, sl, tp, request.magic, request.maxSlippage, comment))
|
| {
|
| m_lastError = "Trade request validation failed: " + m_lastError;
|
| Print("TradeExecutor Error: ", m_lastError);
|
| UpdateCircuitBreaker(true);
|
| return false;
|
| }
|
|
|
| // Execute the trade with retry logic
|
| ulong startTime = GetTickCount();
|
| for(int attempt = 0; attempt <= InpMaxRetries; attempt++)
|
| {
|
| // Check for user break
|
| if(IsStopped())
|
| {
|
| m_lastError = "Operation canceled by user";
|
| Print("TradeExecutor: ", m_lastError);
|
| return false;
|
| }
|
|
|
| // Add delay between retries (except first attempt)
|
| if(attempt > 0)
|
| {
|
| int delay = MathMin(InpRetryDelay * (int)MathPow(2, attempt-1), 5000); // Exponential backoff, max 5s
|
| PrintFormat("TradeExecutor: Retry attempt %d of %d after %d ms",
|
| attempt, InpMaxRetries, delay);
|
| Sleep(delay);
|
| }
|
|
|
| // Execute the trade
|
| ZeroMemory(tradeResult);
|
| bool success = OrderSend(tradeRequest, tradeResult);
|
|
|
| // Log the execution
|
| ulong duration = GetTickCount() - startTime;
|
| LogTradeExecution(tradeRequest, tradeResult, duration);
|
|
|
| // Check the result
|
| if(success && tradeResult.retcode == TRADE_RETCODE_DONE)
|
| {
|
| // Verify the order fill if required
|
| if(!request.requireVerification || VerifyOrderFill(tradeRequest, tradeResult))
|
| {
|
| // Create trade record
|
| CTradeRecord *record = new CTradeRecord();
|
| record.Set(tradeResult.order, symbol, type, volume, tradeResult.price,
|
| sl, tp, tradeResult.deviation, comment);
|
| AddToHistory(record);
|
|
|
| // Update metrics
|
| m_successfulTrades++;
|
| m_totalSlippage += tradeResult.deviation;
|
| m_avgExecutionTime = (m_avgExecutionTime * (m_successfulTrades - 1) + duration) / m_successfulTrades;
|
|
|
| if(InpLogAllTrades)
|
| {
|
| PrintFormat("TradeExecutor: Order #%d executed successfully in %d ms. Slippage: %.1f pips",
|
| tradeResult.order, duration, tradeResult.deviation);
|
| }
|
|
|
| result = true;
|
| break;
|
| }
|
| else
|
| {
|
| // Verification failed
|
| m_lastError = "Order fill verification failed";
|
| Print("TradeExecutor Error: ", m_lastError);
|
| UpdateCircuitBreaker(true);
|
| return false;
|
| }
|
| }
|
| else
|
| {
|
| // Log the error
|
| string errorDesc = GetRetcodeDescription(tradeResult.retcode);
|
| m_lastError = StringFormat("OrderSend failed: %s (code: %d)", errorDesc, tradeResult.retcode);
|
| Print("TradeExecutor Error: ", m_lastError);
|
|
|
| // Update error tracking
|
| m_failedTrades++;
|
| m_consecutiveErrors++;
|
|
|
| // Check if we should continue retrying
|
| if(attempt >= InpMaxRetries - 1)
|
| {
|
| UpdateCircuitBreaker(true);
|
| return false;
|
| }
|
| }
|
| }
|
|
|
| // Update metrics
|
| m_totalTrades++;
|
|
|
| // Update last order time for rate limiting
|
| m_lastOrderTime = TimeCurrent();
|
|
|
| // Check if we need to trigger circuit breaker
|
| if(!result)
|
| {
|
| UpdateCircuitBreaker(true);
|
| }
|
|
|
| return result;
|
| }
|
|
|
| // Static member definitions --------------------------------------------------
|
| int CTradeExecutor::s_ordersThisMinute = 0;
|
| datetime CTradeExecutor::s_currentMinute = 0;
|
|
|
| //---------------------------------------------------------------------------
|
| // END MOVED IMPLEMENTATIONS
|
| //---------------------------------------------------------------------------
|