//+------------------------------------------------------------------+ //| ExternalTradeManager.mqh v1.3 | //| Optimized Trade Management Module | //| Fixed: Compatibility with ERMT v6.8, pointers to structures | //+------------------------------------------------------------------+ #ifndef EXTERNAL_TRADE_MANAGER_MQH #define EXTERNAL_TRADE_MANAGER_MQH #include #include #include #include "DataTypes.mqh" #include "Utilities.mqh" //+------------------------------------------------------------------+ //| External Trade Manager Class | //+------------------------------------------------------------------+ class CExternalTradeManager : public CObject { private: // Dependencies CUtilities* m_utils; CTrade m_trade; // External trade tracking - using array of structures ExternalTradeRecord m_external_trades[]; int m_external_count; // Settings int m_magic_number; bool m_manage_external; double m_external_risk_limit; double m_external_profit; // Added missing member ENUM_MANAGEMENT_MODE m_default_mode; // Statistics int m_total_detected; int m_total_managed; // Helper methods bool IsExternalTrade(ulong ticket); bool UpdateExternalRecord(int index); int FindTradeIndex(ulong ticket); public: // Constructor/Destructor CExternalTradeManager(); ~CExternalTradeManager(); // Initialization bool Initialize(CUtilities* utils, int magic); void SetManagementMode(ENUM_MANAGEMENT_MODE mode) { m_default_mode = mode; } void SetExternalRiskLimit(double limit) { m_external_risk_limit = limit; } // Core functionality bool DetectExternalTrades(); bool ManageExternalTrade(ulong ticket); bool ApplyRiskRulesToExternal(ulong ticket); bool CloseExternalTrade(ulong ticket, string reason); bool AddExternalTrade(const ExternalTradeRecord &record); // Stop management bool UpdateExternalStops(ulong ticket, double sl, double tp); bool ApplyTrailingToExternal(ulong ticket, double trail_points); bool MoveExternalToBreakeven(ulong ticket); // Information methods int GetExternalTradeCount() const { return m_external_count; } bool GetExternalTradeInfo(ulong ticket, ExternalTradeRecord &record); double GetTotalExternalExposure(); double GetExternalProfit(); // Monitoring void MonitorExternalTrades(); bool IsTicketExternal(ulong ticket); ENUM_EXTERNAL_STATUS GetExternalStatus(ulong ticket); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CExternalTradeManager::CExternalTradeManager() { m_utils = NULL; m_magic_number = 0; m_manage_external = true; m_external_risk_limit = 5.0; // 5% default m_external_profit = 0; m_default_mode = MODE_STOPS_ONLY; m_external_count = 0; ArrayResize(m_external_trades, 10); // Initial size m_total_detected = 0; m_total_managed = 0; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CExternalTradeManager::~CExternalTradeManager() { ArrayFree(m_external_trades); } //+------------------------------------------------------------------+ //| Initialize | //+------------------------------------------------------------------+ bool CExternalTradeManager::Initialize(CUtilities* utils, int magic) { if(utils == NULL) return false; m_utils = utils; m_magic_number = magic; m_utils.Log("External Trade Manager initialized", LOG_INFO); return true; } //+------------------------------------------------------------------+ //| Detect External Trades | //+------------------------------------------------------------------+ bool CExternalTradeManager::DetectExternalTrades() { if(!m_manage_external) return true; int total_positions = PositionsTotal(); for(int i = 0; i < total_positions; i++) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; // Check if this is an external trade (not our magic number) if(PositionGetInteger(POSITION_MAGIC) == m_magic_number) continue; // Check if we're already tracking this trade if(IsTicketExternal(ticket)) continue; // Create new external trade record ExternalTradeRecord new_record; new_record.ticket = ticket; new_record.symbol = PositionGetString(POSITION_SYMBOL); new_record.type = (ENUM_ORDER_TYPE)PositionGetInteger(POSITION_TYPE); new_record.volume = PositionGetDouble(POSITION_VOLUME); new_record.entry_price = PositionGetDouble(POSITION_PRICE_OPEN); new_record.original_sl = PositionGetDouble(POSITION_SL); new_record.original_tp = PositionGetDouble(POSITION_TP); new_record.managed_sl = 0; new_record.managed_tp = 0; new_record.detection_time = TimeCurrent(); new_record.last_update = TimeCurrent(); new_record.status = EXTERNAL_DETECTED; new_record.management_mode = m_default_mode; new_record.max_profit = 0; new_record.max_loss = 0; new_record.notes = ""; // Add to tracking array if(AddExternalTrade(new_record)) { m_total_detected++; m_utils.Log(StringFormat("Detected external trade: Ticket %d, Symbol %s", ticket, new_record.symbol), LOG_INFO); } } return true; } //+------------------------------------------------------------------+ //| Add External Trade to Array | //+------------------------------------------------------------------+ bool CExternalTradeManager::AddExternalTrade(const ExternalTradeRecord &record) { // Check if we need to resize the array if(m_external_count >= ArraySize(m_external_trades)) { ArrayResize(m_external_trades, m_external_count + 10); } // Add the record m_external_trades[m_external_count] = record; m_external_count++; return true; } //+------------------------------------------------------------------+ //| Find Trade Index | //+------------------------------------------------------------------+ int CExternalTradeManager::FindTradeIndex(ulong ticket) { for(int i = 0; i < m_external_count; i++) { if(m_external_trades[i].ticket == ticket) return i; } return -1; } //+------------------------------------------------------------------+ //| Check if Trade is External | //+------------------------------------------------------------------+ bool CExternalTradeManager::IsExternalTrade(ulong ticket) { if(!PositionSelectByTicket(ticket)) return false; return (PositionGetInteger(POSITION_MAGIC) != m_magic_number); } //+------------------------------------------------------------------+ //| Check if Ticket is Being Tracked | //+------------------------------------------------------------------+ bool CExternalTradeManager::IsTicketExternal(ulong ticket) { return (FindTradeIndex(ticket) >= 0); } //+------------------------------------------------------------------+ //| Get External Trade Status | //+------------------------------------------------------------------+ ENUM_EXTERNAL_STATUS CExternalTradeManager::GetExternalStatus(ulong ticket) { int index = FindTradeIndex(ticket); if(index >= 0) return m_external_trades[index].status; return EXTERNAL_PENDING; } //+------------------------------------------------------------------+ //| Manage External Trade | //+------------------------------------------------------------------+ bool CExternalTradeManager::ManageExternalTrade(ulong ticket) { if(!m_manage_external) return false; // Find the external trade record int index = FindTradeIndex(ticket); if(index == -1) return false; // Select the position if(!PositionSelectByTicket(ticket)) { // Position closed m_external_trades[index].status = EXTERNAL_CLOSED; return false; } // Update current values m_external_trades[index].last_update = TimeCurrent(); double current_profit = PositionGetDouble(POSITION_PROFIT); // Track max profit/loss if(current_profit > m_external_trades[index].max_profit) m_external_trades[index].max_profit = current_profit; if(current_profit < m_external_trades[index].max_loss) m_external_trades[index].max_loss = current_profit; // Apply management based on mode switch(m_external_trades[index].management_mode) { case MODE_FULL_CONTROL: // Full management including entries/exits if(m_external_trades[index].status == EXTERNAL_DETECTED) { m_external_trades[index].status = EXTERNAL_MANAGED; m_total_managed++; } // Apply all management rules ApplyRiskRulesToExternal(ticket); break; case MODE_STOPS_ONLY: // Only manage stop losses if(m_external_trades[index].managed_sl == 0 && m_external_trades[index].original_sl == 0) { // Apply initial stop if none exists double atr = m_utils.GetATR(14); double stop_distance = atr * 2.0; double new_sl = (m_external_trades[index].type == ORDER_TYPE_BUY) ? m_external_trades[index].entry_price - stop_distance : m_external_trades[index].entry_price + stop_distance; UpdateExternalStops(ticket, new_sl, m_external_trades[index].original_tp); m_external_trades[index].managed_sl = new_sl; } break; case MODE_MONITOR_ONLY: // Just monitor, no modifications m_external_trades[index].status = EXTERNAL_MONITORED; break; case MODE_EMERGENCY_ONLY: // Only intervene in emergency situations if(current_profit < -(AccountInfoDouble(ACCOUNT_BALANCE) * m_external_risk_limit / 100)) { CloseExternalTrade(ticket, "Emergency: Risk limit exceeded"); } break; } return true; } //+------------------------------------------------------------------+ //| Apply Risk Rules to External Trade | //+------------------------------------------------------------------+ bool CExternalTradeManager::ApplyRiskRulesToExternal(ulong ticket) { if(!PositionSelectByTicket(ticket)) return false; double profit = PositionGetDouble(POSITION_PROFIT); double balance = AccountInfoDouble(ACCOUNT_BALANCE); double risk_percent = MathAbs(profit) / balance * 100; // Check if trade exceeds risk limit if(risk_percent > m_external_risk_limit) { m_utils.Log(StringFormat("External trade %d exceeds risk limit: %.2f%%", ticket, risk_percent), LOG_WARNING); // Consider closing or reducing position if(risk_percent > m_external_risk_limit * 1.5) { return CloseExternalTrade(ticket, "Risk limit exceeded"); } } return true; } //+------------------------------------------------------------------+ //| Update External Trade Stops | //+------------------------------------------------------------------+ bool CExternalTradeManager::UpdateExternalStops(ulong ticket, double sl, double tp) { if(!PositionSelectByTicket(ticket)) return false; // Validate stops string symbol = PositionGetString(POSITION_SYMBOL); ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)PositionGetInteger(POSITION_TYPE); double point = SymbolInfoDouble(symbol, SYMBOL_POINT); double bid = SymbolInfoDouble(symbol, SYMBOL_BID); double ask = SymbolInfoDouble(symbol, SYMBOL_ASK); int stops_level = (int)SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL); double min_stop = stops_level * point; // Validate stop loss if(sl > 0) { if(type == ORDER_TYPE_BUY && sl > bid - min_stop) sl = bid - min_stop; else if(type == ORDER_TYPE_SELL && sl < ask + min_stop) sl = ask + min_stop; } // Validate take profit if(tp > 0) { if(type == ORDER_TYPE_BUY && tp < bid + min_stop) tp = bid + min_stop; else if(type == ORDER_TYPE_SELL && tp > ask - min_stop) tp = ask - min_stop; } // Modify position m_trade.PositionModify(ticket, sl, tp); // Update record int index = FindTradeIndex(ticket); if(index >= 0) { m_external_trades[index].managed_sl = sl; m_external_trades[index].managed_tp = tp; m_external_trades[index].status = EXTERNAL_MODIFIED; } return true; } //+------------------------------------------------------------------+ //| Apply Trailing Stop to External Trade | //+------------------------------------------------------------------+ bool CExternalTradeManager::ApplyTrailingToExternal(ulong ticket, double trail_points) { if(!PositionSelectByTicket(ticket)) return false; string symbol = PositionGetString(POSITION_SYMBOL); ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)PositionGetInteger(POSITION_TYPE); double current_sl = PositionGetDouble(POSITION_SL); double current_price = PositionGetDouble(POSITION_PRICE_CURRENT); double point = SymbolInfoDouble(symbol, SYMBOL_POINT); double trail_distance = trail_points * point; double new_sl = 0; if(type == ORDER_TYPE_BUY) { new_sl = current_price - trail_distance; if(current_sl == 0 || new_sl > current_sl) { return UpdateExternalStops(ticket, new_sl, PositionGetDouble(POSITION_TP)); } } else if(type == ORDER_TYPE_SELL) { new_sl = current_price + trail_distance; if(current_sl == 0 || new_sl < current_sl) { return UpdateExternalStops(ticket, new_sl, PositionGetDouble(POSITION_TP)); } } return false; } //+------------------------------------------------------------------+ //| Move External Trade to Breakeven | //+------------------------------------------------------------------+ bool CExternalTradeManager::MoveExternalToBreakeven(ulong ticket) { if(!PositionSelectByTicket(ticket)) return false; double entry_price = PositionGetDouble(POSITION_PRICE_OPEN); double current_sl = PositionGetDouble(POSITION_SL); ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)PositionGetInteger(POSITION_TYPE); string symbol = PositionGetString(POSITION_SYMBOL); double point = SymbolInfoDouble(symbol, SYMBOL_POINT); int spread = (int)SymbolInfoInteger(symbol, SYMBOL_SPREAD); double spread_points = spread * point; // Add small buffer for breakeven double buffer = spread_points * 2; double breakeven_price = 0; if(type == ORDER_TYPE_BUY) { breakeven_price = entry_price + buffer; if(current_sl < breakeven_price) { return UpdateExternalStops(ticket, breakeven_price, PositionGetDouble(POSITION_TP)); } } else if(type == ORDER_TYPE_SELL) { breakeven_price = entry_price - buffer; if(current_sl == 0 || current_sl > breakeven_price) { return UpdateExternalStops(ticket, breakeven_price, PositionGetDouble(POSITION_TP)); } } return false; } //+------------------------------------------------------------------+ //| Close External Trade | //+------------------------------------------------------------------+ bool CExternalTradeManager::CloseExternalTrade(ulong ticket, string reason) { if(!PositionSelectByTicket(ticket)) return false; // Update status int index = FindTradeIndex(ticket); if(index >= 0) { m_external_trades[index].status = EXTERNAL_CLOSING; m_external_trades[index].notes = reason; } // Close the position bool result = m_trade.PositionClose(ticket); if(result) { m_utils.Log(StringFormat("Closed external trade %d: %s", ticket, reason), LOG_INFO); // Update final status if(index >= 0) { m_external_trades[index].status = EXTERNAL_CLOSED; } } return result; } //+------------------------------------------------------------------+ //| Get External Trade Info | //+------------------------------------------------------------------+ bool CExternalTradeManager::GetExternalTradeInfo(ulong ticket, ExternalTradeRecord &record) { int index = FindTradeIndex(ticket); if(index >= 0) { record = m_external_trades[index]; return true; } return false; } //+------------------------------------------------------------------+ //| Get Total External Exposure | //+------------------------------------------------------------------+ double CExternalTradeManager::GetTotalExternalExposure() { double total_exposure = 0; for(int i = 0; i < m_external_count; i++) { if(m_external_trades[i].status == EXTERNAL_CLOSED) continue; if(PositionSelectByTicket(m_external_trades[i].ticket)) { total_exposure += PositionGetDouble(POSITION_VOLUME); } } return total_exposure; } //+------------------------------------------------------------------+ //| Get External Profit | //+------------------------------------------------------------------+ double CExternalTradeManager::GetExternalProfit() { double total_profit = 0; for(int i = 0; i < m_external_count; i++) { if(m_external_trades[i].status == EXTERNAL_CLOSED) continue; if(PositionSelectByTicket(m_external_trades[i].ticket)) { total_profit += PositionGetDouble(POSITION_PROFIT); } } m_external_profit = total_profit; return total_profit; } //+------------------------------------------------------------------+ //| Monitor External Trades | //+------------------------------------------------------------------+ void CExternalTradeManager::MonitorExternalTrades() { // Update status of all tracked trades for(int i = m_external_count - 1; i >= 0; i--) { if(!PositionSelectByTicket(m_external_trades[i].ticket)) { // Trade closed if(m_external_trades[i].status != EXTERNAL_CLOSED) { m_external_trades[i].status = EXTERNAL_CLOSED; m_external_trades[i].last_update = TimeCurrent(); m_utils.Log(StringFormat("External trade %d closed", m_external_trades[i].ticket), LOG_INFO); } // Remove from array after some time if(TimeCurrent() - m_external_trades[i].last_update > 3600) // Keep for 1 hour { // Shift array elements for(int j = i; j < m_external_count - 1; j++) { m_external_trades[j] = m_external_trades[j + 1]; } m_external_count--; } } } // Update statistics GetExternalProfit(); } //+------------------------------------------------------------------+ //| Update External Record | //+------------------------------------------------------------------+ bool CExternalTradeManager::UpdateExternalRecord(int index) { if(index < 0 || index >= m_external_count) return false; if(!PositionSelectByTicket(m_external_trades[index].ticket)) { m_external_trades[index].status = EXTERNAL_CLOSED; return false; } // Update current values m_external_trades[index].last_update = TimeCurrent(); return true; } #endif // EXTERNAL_TRADE_MANAGER_MQH