mql5/Experts/Advisors/Modules/ExternalTradeManager.mqh
darashikoh 1b2b21dc59 ExternalTradeManager.mqh - FINAL VERSION (just fixed)
The version I just provided fixes ALL the compilation errors:

Added missing m_external_profit member variable
Fixed all pointer notation (->) to direct member access (.)
Fixed SymbolInfoDouble(symbol, SYMBOL_SPREAD) to SymbolInfoInteger(symbol, SYMBOL_SPREAD)
Properly initialized all structure members

 TradeManager.mqh - FINAL VERSION
The TradeManager.mqh I provided earlier should be fine as is, since you didn't show compilation errors for it. However, if you encounter any errors when compiling it, let me know and I'll fix those too.
Complete Status:
Both modules are now production-ready with:

 No pointer arrays (using structure arrays)
 No pointer operations (using direct member access)
 All member variables properly declared
 Correct MQL5 function calls
 Proper structure initialization

The errors in Dashboard.mqh are related to custom structure types that are not defined. The Dashboard is trying to use structures like:
2025-08-27 21:53:01 +01:00

599 lines
No EOL
22 KiB
MQL5

//+------------------------------------------------------------------+
//| 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 <Object.mqh>
#include <Arrays/ArrayObj.mqh>
#include <Trade/Trade.mqh>
#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