mql5/Experts/Previous/Modules_6.8/ExternalTradeManager.mqh

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