mql5/Experts/Advisors/Modules/TradeManager.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

1316 lines
No EOL
46 KiB
MQL5

//+------------------------------------------------------------------+
//| TradeManager.mqh v2.2 |
//| Optimized Trade Management Module |
//| Fixed: Compatibility with ERMT 6.8 |
//+------------------------------------------------------------------+
#ifndef TRADE_MANAGER_MQH
#define TRADE_MANAGER_MQH
#include <Object.mqh>
#include <Trade/Trade.mqh>
#include <Arrays/ArrayObj.mqh>
#include "DataTypes.mqh"
#include "Utilities.mqh"
#include "RiskManager.mqh"
#include "TechnicalAnalysis.mqh"
#include "ExternalTradeManager.mqh"
//+------------------------------------------------------------------+
//| Trade Manager Class |
//+------------------------------------------------------------------+
class CTradeManager : public CObject
{
private:
// Dependencies
CUtilities* m_utils;
CRiskManager* m_risk;
CTechnicalAnalysis* m_tech;
CExternalTradeManager* m_external;
// Trade execution
CTrade m_trade;
// Settings
int m_magic_number;
string m_comment;
double m_slippage;
ENUM_POSITION_SIZING m_sizing_method;
double m_fixed_lot_size;
double m_atr_multiplier_sl;
double m_atr_multiplier_tp;
// Trailing stop settings
ENUM_TRAILING_METHOD m_trailing_method;
double m_trail_start;
double m_trail_step;
double m_trail_distance;
bool m_breakeven_enabled;
double m_breakeven_trigger;
double m_breakeven_offset;
// Trade tracking - using arrays of structures
TradeInfo m_managed_trades[];
int m_trade_count;
// Performance tracking
PerformanceMetrics m_performance;
double m_peak_balance;
datetime m_last_update;
// Trade history
TradeInfo m_history[];
int m_history_count;
// Helper methods
bool UpdateTradeInfo(int index);
void RecordTrade(const TradeInfo &trade);
void UpdatePerformanceMetrics();
bool ExecuteMarketOrder(MqlTradeRequest &request, MqlTradeResult &result);
double CalculateStopLoss(ENUM_ORDER_TYPE type, double entry, double atr);
double CalculateTakeProfit(ENUM_ORDER_TYPE type, double entry, double sl);
int FindTradeIndex(ulong ticket);
public:
// Constructor/Destructor
CTradeManager();
~CTradeManager();
// Initialization
bool Initialize(CUtilities* utils, CRiskManager* risk,
CTechnicalAnalysis* tech,
CExternalTradeManager* external,
int magic);
// Configuration
void SetSizingMethod(ENUM_POSITION_SIZING method) { m_sizing_method = method; }
void SetFixedLotSize(double size) { m_fixed_lot_size = size; }
void SetATRMultipliers(double sl_mult, double tp_mult);
void SetTrailingMethod(ENUM_TRAILING_METHOD method) { m_trailing_method = method; }
void SetTrailingParams(double start, double step, double distance);
void SetBreakevenEnabled(bool enabled) { m_breakeven_enabled = enabled; }
void SetBreakevenParams(double trigger, double offset);
void SetSlippage(double slippage) { m_slippage = slippage; }
void SetComment(string comment) { m_comment = comment; }
// Trade execution
ulong OpenTrade(MqlTradeRequest &request);
bool CloseTrade(ulong ticket, string reason = "");
bool PartialClose(ulong ticket, double volume, string reason = "");
bool ModifyTrade(ulong ticket, double sl, double tp);
// Position management
bool ApplyTrailingStop(ulong ticket);
bool CheckBreakeven(ulong ticket);
bool MoveToBreakeven(ulong ticket);
bool ScaleOut(ulong ticket, double percent, string reason);
bool ScaleIn(ulong ticket, double additional_lots);
// Trade information
int GetManagedTrades(TradeInfo &trades[]);
int GetOpenTradeCount() const { return m_trade_count; }
bool GetTradeInfo(ulong ticket, TradeInfo &trade);
double GetTotalExposure();
double GetTotalProfit();
// Performance metrics
void GetPerformanceMetrics(PerformanceMetrics &metrics);
double GetWinRate() const { return m_performance.win_rate; }
double GetProfitFactor() const { return m_performance.profit_factor; }
double GetAverageWin() const { return m_performance.average_win; }
double GetAverageLoss() const { return m_performance.average_loss; }
double GetMaxDrawdown() const { return m_performance.max_drawdown; }
// Trade monitoring
void UpdateAllTrades();
void CheckStopLevels();
void CheckTakeProfitLevels();
// Utility
void CleanupHistory();
void ExportHistory(string filename);
void ResetPerformance();
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CTradeManager::CTradeManager()
{
m_utils = NULL;
m_risk = NULL;
m_tech = NULL;
m_external = NULL;
m_magic_number = 0;
m_comment = "";
m_slippage = 3;
m_sizing_method = SIZING_FIXED_PERCENT;
m_fixed_lot_size = 0.1;
m_atr_multiplier_sl = 2.0;
m_atr_multiplier_tp = 3.0;
m_trailing_method = TRAIL_NONE;
m_trail_start = 30;
m_trail_step = 10;
m_trail_distance = 20;
m_breakeven_enabled = false;
m_breakeven_trigger = 20;
m_breakeven_offset = 2;
m_trade_count = 0;
ArrayResize(m_managed_trades, 10);
m_history_count = 0;
ArrayResize(m_history, 100);
m_peak_balance = 0;
m_last_update = 0;
// Initialize performance metrics
ZeroMemory(m_performance);
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTradeManager::~CTradeManager()
{
ArrayFree(m_managed_trades);
ArrayFree(m_history);
}
//+------------------------------------------------------------------+
//| Initialize |
//+------------------------------------------------------------------+
bool CTradeManager::Initialize(CUtilities* utils, CRiskManager* risk,
CTechnicalAnalysis* tech,
CExternalTradeManager* external, int magic)
{
if(utils == NULL || risk == NULL || tech == NULL) return false;
m_utils = utils;
m_risk = risk;
m_tech = tech;
m_external = external;
m_magic_number = magic;
// Set trade parameters
m_trade.SetExpertMagicNumber(m_magic_number);
m_trade.SetDeviationInPoints((ulong)m_slippage);
m_trade.SetTypeFilling(ORDER_FILLING_RETURN);
m_trade.SetAsyncMode(false);
// Initialize performance tracking
m_peak_balance = AccountInfoDouble(ACCOUNT_BALANCE);
m_last_update = TimeCurrent();
// Load existing positions
UpdateAllTrades();
m_utils.Log("Trade Manager initialized successfully", LOG_INFO);
m_utils.Log(StringFormat("Managing positions with magic: %d", m_magic_number), LOG_INFO);
return true;
}
//+------------------------------------------------------------------+
//| Set ATR Multipliers |
//+------------------------------------------------------------------+
void CTradeManager::SetATRMultipliers(double sl_mult, double tp_mult)
{
m_atr_multiplier_sl = sl_mult;
m_atr_multiplier_tp = tp_mult;
m_utils.Log(StringFormat("ATR multipliers set: SL=%.1f, TP=%.1f",
sl_mult, tp_mult), LOG_DEBUG);
}
//+------------------------------------------------------------------+
//| Set Trailing Parameters |
//+------------------------------------------------------------------+
void CTradeManager::SetTrailingParams(double start, double step, double distance)
{
m_trail_start = start;
m_trail_step = step;
m_trail_distance = distance;
m_utils.Log(StringFormat("Trailing params: Start=%.0f, Step=%.0f, Distance=%.0f",
start, step, distance), LOG_DEBUG);
}
//+------------------------------------------------------------------+
//| Set Breakeven Parameters |
//+------------------------------------------------------------------+
void CTradeManager::SetBreakevenParams(double trigger, double offset)
{
m_breakeven_trigger = trigger;
m_breakeven_offset = offset;
}
//+------------------------------------------------------------------+
//| Open Trade |
//+------------------------------------------------------------------+
ulong CTradeManager::OpenTrade(MqlTradeRequest &request)
{
MqlTradeResult result;
MqlTradeCheckResult check_result;
ZeroMemory(result);
ZeroMemory(check_result);
// Ensure proper filling type
request.type_filling = ORDER_FILLING_RETURN;
request.deviation = (ulong)m_slippage;
// Set magic and comment if not already set
if(request.magic == 0) request.magic = m_magic_number;
if(request.comment == "") request.comment = m_comment;
// Validate stops
if(!m_trade.OrderCheck(request, check_result))
{
m_utils.Log(StringFormat("Order check failed: %s", check_result.comment), LOG_ERROR);
return 0;
}
// Execute order
if(!ExecuteMarketOrder(request, result))
{
return 0;
}
// Create trade info record
if(result.order > 0)
{
// Wait for position to appear
Sleep(100);
// Find the position
for(int i = 0; i < PositionsTotal(); i++)
{
ulong ticket = PositionGetTicket(i);
if(PositionGetInteger(POSITION_MAGIC) == m_magic_number &&
PositionGetInteger(POSITION_IDENTIFIER) == result.order)
{
// Create new trade record
if(m_trade_count >= ArraySize(m_managed_trades))
{
ArrayResize(m_managed_trades, m_trade_count + 10);
}
TradeInfo trade;
trade.ticket = ticket;
trade.symbol = request.symbol;
trade.type = request.type;
trade.volume = request.volume;
trade.entry_price = result.price;
trade.current_price = result.price;
trade.sl = request.sl;
trade.tp = request.tp;
trade.open_time = TimeCurrent();
trade.last_update = TimeCurrent();
trade.status = TRADE_STATUS_ACTIVE;
trade.comment = request.comment;
trade.magic = m_magic_number;
trade.is_external = false;
trade.management_mode = MODE_FULL_CONTROL;
trade.profit = 0;
trade.swap = 0;
trade.commission = 0;
trade.close_requested = false;
trade.close_reason = "";
trade.partial_close_volume = 0;
trade.partial_close_reason = "";
// Calculate risk
double stop_distance = MathAbs(result.price - request.sl);
trade.risk_amount = m_risk.CalculateTradeRisk(request.volume,
stop_distance,
request.symbol);
trade.risk_percent = (trade.risk_amount / AccountInfoDouble(ACCOUNT_BALANCE)) * 100;
trade.current_rr = 0;
m_managed_trades[m_trade_count] = trade;
m_trade_count++;
m_utils.LogTrade(trade);
return ticket;
}
}
}
return result.order;
}
//+------------------------------------------------------------------+
//| Execute Market Order |
//+------------------------------------------------------------------+
bool CTradeManager::ExecuteMarketOrder(MqlTradeRequest &request, MqlTradeResult &result)
{
// Try to execute order
if(!m_trade.OrderSend(request, result))
{
m_utils.Log(StringFormat("OrderSend failed: %d - %s",
m_trade.ResultRetcode(),
m_trade.ResultComment()), LOG_ERROR);
return false;
}
// Check result
if(result.retcode != TRADE_RETCODE_DONE)
{
m_utils.Log(StringFormat("Order failed: Retcode %d - %s",
result.retcode, result.comment), LOG_ERROR);
return false;
}
m_utils.Log(StringFormat("Order executed: #%d at %.5f",
result.order, result.price), LOG_INFO);
return true;
}
//+------------------------------------------------------------------+
//| Find Trade Index |
//+------------------------------------------------------------------+
int CTradeManager::FindTradeIndex(ulong ticket)
{
for(int i = 0; i < m_trade_count; i++)
{
if(m_managed_trades[i].ticket == ticket)
return i;
}
return -1;
}
//+------------------------------------------------------------------+
//| Close Trade |
//+------------------------------------------------------------------+
bool CTradeManager::CloseTrade(ulong ticket, string reason)
{
if(!PositionSelectByTicket(ticket))
{
m_utils.Log(StringFormat("Position %d not found", ticket), LOG_WARNING);
return false;
}
// Close position
if(!m_trade.PositionClose(ticket, (ulong)m_slippage))
{
m_utils.Log(StringFormat("Failed to close position %d: %s",
ticket, m_trade.ResultComment()), LOG_ERROR);
return false;
}
// Update trade record
int index = FindTradeIndex(ticket);
if(index >= 0)
{
m_managed_trades[index].status = TRADE_STATUS_CLOSED;
m_managed_trades[index].close_reason = reason;
m_managed_trades[index].last_update = TimeCurrent();
// Record in history
RecordTrade(m_managed_trades[index]);
// Remove from active trades by shifting array
for(int j = index; j < m_trade_count - 1; j++)
{
m_managed_trades[j] = m_managed_trades[j + 1];
}
m_trade_count--;
}
m_utils.Log(StringFormat("Position %d closed: %s", ticket, reason), LOG_INFO);
// Update performance
UpdatePerformanceMetrics();
return true;
}
//+------------------------------------------------------------------+
//| Partial Close |
//+------------------------------------------------------------------+
bool CTradeManager::PartialClose(ulong ticket, double volume, string reason)
{
if(!PositionSelectByTicket(ticket))
{
m_utils.Log(StringFormat("Position %d not found", ticket), LOG_WARNING);
return false;
}
double current_volume = PositionGetDouble(POSITION_VOLUME);
if(volume >= current_volume)
{
// Close entire position
return CloseTrade(ticket, reason);
}
// Partial close
if(!m_trade.PositionClosePartial(ticket, volume, (ulong)m_slippage))
{
m_utils.Log(StringFormat("Failed to partial close %d: %s",
ticket, m_trade.ResultComment()), LOG_ERROR);
return false;
}
// Update trade record
int index = FindTradeIndex(ticket);
if(index >= 0)
{
m_managed_trades[index].volume = current_volume - volume;
m_managed_trades[index].partial_close_volume = 0;
m_managed_trades[index].partial_close_reason = "";
m_managed_trades[index].last_update = TimeCurrent();
}
m_utils.Log(StringFormat("Partial close %d: %.2f lots (%s)",
ticket, volume, reason), LOG_INFO);
return true;
}
//+------------------------------------------------------------------+
//| Modify Trade |
//+------------------------------------------------------------------+
bool CTradeManager::ModifyTrade(ulong ticket, double sl, double tp)
{
if(!PositionSelectByTicket(ticket))
{
m_utils.Log(StringFormat("Position %d not found", ticket), LOG_WARNING);
return false;
}
// Validate stops
string symbol = PositionGetString(POSITION_SYMBOL);
ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)PositionGetInteger(POSITION_TYPE);
double current_price = PositionGetDouble(POSITION_PRICE_CURRENT);
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
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)
{
if(sl > current_price - min_stop)
{
sl = current_price - min_stop;
}
}
else
{
if(sl < current_price + min_stop)
{
sl = current_price + min_stop;
}
}
}
// Validate take profit
if(tp > 0)
{
if(type == ORDER_TYPE_BUY)
{
if(tp < current_price + min_stop)
{
tp = current_price + min_stop;
}
}
else
{
if(tp > current_price - min_stop)
{
tp = current_price - min_stop;
}
}
}
// Modify position
if(!m_trade.PositionModify(ticket, sl, tp))
{
m_utils.Log(StringFormat("Failed to modify position %d: %s",
ticket, m_trade.ResultComment()), LOG_ERROR);
return false;
}
// Update trade record
int index = FindTradeIndex(ticket);
if(index >= 0)
{
m_managed_trades[index].sl = sl;
m_managed_trades[index].tp = tp;
m_managed_trades[index].last_update = TimeCurrent();
// Recalculate risk
double stop_distance = MathAbs(m_managed_trades[index].entry_price - sl);
m_managed_trades[index].risk_amount = m_risk.CalculateTradeRisk(
m_managed_trades[index].volume, stop_distance, symbol);
m_managed_trades[index].risk_percent =
(m_managed_trades[index].risk_amount / AccountInfoDouble(ACCOUNT_BALANCE)) * 100;
}
m_utils.Log(StringFormat("Position %d modified: SL=%.5f, TP=%.5f",
ticket, sl, tp), LOG_DEBUG);
return true;
}
//+------------------------------------------------------------------+
//| Apply Trailing Stop |
//+------------------------------------------------------------------+
bool CTradeManager::ApplyTrailingStop(ulong ticket)
{
if(m_trailing_method == TRAIL_NONE) return false;
if(!PositionSelectByTicket(ticket))
return false;
string symbol = PositionGetString(POSITION_SYMBOL);
ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)PositionGetInteger(POSITION_TYPE);
double current_price = PositionGetDouble(POSITION_PRICE_CURRENT);
double current_sl = PositionGetDouble(POSITION_SL);
double entry_price = PositionGetDouble(POSITION_PRICE_OPEN);
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
double new_sl = 0;
switch(m_trailing_method)
{
case TRAIL_FIXED_POINTS:
{
double trail_distance = m_trail_distance * point;
if(type == ORDER_TYPE_BUY)
{
new_sl = current_price - trail_distance;
if(current_sl == 0 || new_sl > current_sl)
{
return ModifyTrade(ticket, new_sl, PositionGetDouble(POSITION_TP));
}
}
else
{
new_sl = current_price + trail_distance;
if(current_sl == 0 || new_sl < current_sl)
{
return ModifyTrade(ticket, new_sl, PositionGetDouble(POSITION_TP));
}
}
}
break;
case TRAIL_PERCENT:
{
double trail_distance = current_price * m_trail_distance / 100;
if(type == ORDER_TYPE_BUY)
{
new_sl = current_price - trail_distance;
if(current_sl == 0 || new_sl > current_sl)
{
return ModifyTrade(ticket, new_sl, PositionGetDouble(POSITION_TP));
}
}
else
{
new_sl = current_price + trail_distance;
if(current_sl == 0 || new_sl < current_sl)
{
return ModifyTrade(ticket, new_sl, PositionGetDouble(POSITION_TP));
}
}
}
break;
case TRAIL_ATR:
{
double atr = m_tech.GetATR();
double trail_distance = atr * (m_trail_distance / 10); // Normalize
if(type == ORDER_TYPE_BUY)
{
new_sl = current_price - trail_distance;
if(current_sl == 0 || new_sl > current_sl)
{
return ModifyTrade(ticket, new_sl, PositionGetDouble(POSITION_TP));
}
}
else
{
new_sl = current_price + trail_distance;
if(current_sl == 0 || new_sl < current_sl)
{
return ModifyTrade(ticket, new_sl, PositionGetDouble(POSITION_TP));
}
}
}
break;
case TRAIL_PARABOLIC_SAR:
{
int sar_handle = iSAR(symbol, PERIOD_CURRENT, 0.02, 0.2);
if(sar_handle != INVALID_HANDLE)
{
double sar_buffer[1];
if(CopyBuffer(sar_handle, 0, 0, 1, sar_buffer) > 0)
{
new_sl = sar_buffer[0];
IndicatorRelease(sar_handle);
if(type == ORDER_TYPE_BUY && (current_sl == 0 || new_sl > current_sl))
{
return ModifyTrade(ticket, new_sl, PositionGetDouble(POSITION_TP));
}
else if(type == ORDER_TYPE_SELL && (current_sl == 0 || new_sl < current_sl))
{
return ModifyTrade(ticket, new_sl, PositionGetDouble(POSITION_TP));
}
}
else
{
IndicatorRelease(sar_handle);
}
}
}
break;
case TRAIL_MOVING_AVERAGE:
{
double ma = m_tech.GetMA();
if(type == ORDER_TYPE_BUY && (current_sl == 0 || ma > current_sl))
{
return ModifyTrade(ticket, ma, PositionGetDouble(POSITION_TP));
}
else if(type == ORDER_TYPE_SELL && (current_sl == 0 || ma < current_sl))
{
return ModifyTrade(ticket, ma, PositionGetDouble(POSITION_TP));
}
}
break;
case TRAIL_STEPPED:
{
double profit_points = (current_price - entry_price) / point;
if(type == ORDER_TYPE_SELL)
profit_points = (entry_price - current_price) / point;
if(profit_points >= m_trail_start)
{
int steps = (int)(profit_points / m_trail_step);
double new_trail = steps * m_trail_step * point;
if(type == ORDER_TYPE_BUY)
{
new_sl = entry_price + new_trail - m_trail_distance * point;
if(current_sl == 0 || new_sl > current_sl)
{
return ModifyTrade(ticket, new_sl, PositionGetDouble(POSITION_TP));
}
}
else
{
new_sl = entry_price - new_trail + m_trail_distance * point;
if(current_sl == 0 || new_sl < current_sl)
{
return ModifyTrade(ticket, new_sl, PositionGetDouble(POSITION_TP));
}
}
}
}
break;
}
return false;
}
//+------------------------------------------------------------------+
//| Check Breakeven |
//+------------------------------------------------------------------+
bool CTradeManager::CheckBreakeven(ulong ticket)
{
if(!m_breakeven_enabled) return false;
if(!PositionSelectByTicket(ticket))
return false;
double entry_price = PositionGetDouble(POSITION_PRICE_OPEN);
double current_price = PositionGetDouble(POSITION_PRICE_CURRENT);
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);
double profit_points = 0;
if(type == ORDER_TYPE_BUY)
{
profit_points = (current_price - entry_price) / point;
if(profit_points >= m_breakeven_trigger)
{
double new_sl = entry_price + m_breakeven_offset * point;
if(current_sl < new_sl)
{
return MoveToBreakeven(ticket);
}
}
}
else
{
profit_points = (entry_price - current_price) / point;
if(profit_points >= m_breakeven_trigger)
{
double new_sl = entry_price - m_breakeven_offset * point;
if(current_sl == 0 || current_sl > new_sl)
{
return MoveToBreakeven(ticket);
}
}
}
return false;
}
//+------------------------------------------------------------------+
//| Move to Breakeven |
//+------------------------------------------------------------------+
bool CTradeManager::MoveToBreakeven(ulong ticket)
{
if(!PositionSelectByTicket(ticket))
return false;
double entry_price = PositionGetDouble(POSITION_PRICE_OPEN);
ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)PositionGetInteger(POSITION_TYPE);
string symbol = PositionGetString(POSITION_SYMBOL);
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
double new_sl = 0;
if(type == ORDER_TYPE_BUY)
{
new_sl = entry_price + m_breakeven_offset * point;
}
else
{
new_sl = entry_price - m_breakeven_offset * point;
}
bool result = ModifyTrade(ticket, new_sl, PositionGetDouble(POSITION_TP));
if(result)
{
m_utils.Log(StringFormat("Position %d moved to breakeven at %.5f",
ticket, new_sl), LOG_INFO);
}
return result;
}
//+------------------------------------------------------------------+
//| Get Managed Trades |
//+------------------------------------------------------------------+
int CTradeManager::GetManagedTrades(TradeInfo &trades[])
{
UpdateAllTrades();
ArrayResize(trades, m_trade_count);
for(int i = 0; i < m_trade_count; i++)
{
trades[i] = m_managed_trades[i];
}
return m_trade_count;
}
//+------------------------------------------------------------------+
//| Update All Trades |
//+------------------------------------------------------------------+
void CTradeManager::UpdateAllTrades()
{
// Update existing trades
for(int i = m_trade_count - 1; i >= 0; i--)
{
if(!UpdateTradeInfo(i))
{
// Trade closed
RecordTrade(m_managed_trades[i]);
// Shift array
for(int j = i; j < m_trade_count - 1; j++)
{
m_managed_trades[j] = m_managed_trades[j + 1];
}
m_trade_count--;
}
}
// Check for new positions
for(int i = 0; i < PositionsTotal(); i++)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(PositionGetInteger(POSITION_MAGIC) != m_magic_number)
continue;
// Check if already tracked
if(FindTradeIndex(ticket) >= 0) continue;
// Add new trade
if(m_trade_count >= ArraySize(m_managed_trades))
{
ArrayResize(m_managed_trades, m_trade_count + 10);
}
TradeInfo trade;
trade.ticket = ticket;
trade.symbol = PositionGetString(POSITION_SYMBOL);
trade.type = (ENUM_ORDER_TYPE)PositionGetInteger(POSITION_TYPE);
trade.volume = PositionGetDouble(POSITION_VOLUME);
trade.entry_price = PositionGetDouble(POSITION_PRICE_OPEN);
trade.current_price = PositionGetDouble(POSITION_PRICE_CURRENT);
trade.sl = PositionGetDouble(POSITION_SL);
trade.tp = PositionGetDouble(POSITION_TP);
trade.profit = PositionGetDouble(POSITION_PROFIT);
trade.swap = PositionGetDouble(POSITION_SWAP);
trade.commission = 0; // Not available directly
trade.open_time = (datetime)PositionGetInteger(POSITION_TIME);
trade.last_update = TimeCurrent();
trade.status = TRADE_STATUS_ACTIVE;
trade.comment = PositionGetString(POSITION_COMMENT);
trade.magic = m_magic_number;
trade.is_external = false;
trade.management_mode = MODE_FULL_CONTROL;
trade.close_requested = false;
trade.close_reason = "";
trade.partial_close_volume = 0;
trade.partial_close_reason = "";
trade.risk_amount = 0;
trade.risk_percent = 0;
trade.current_rr = 0;
m_managed_trades[m_trade_count] = trade;
m_trade_count++;
m_utils.Log(StringFormat("New position detected: %d", ticket), LOG_INFO);
}
}
//+------------------------------------------------------------------+
//| Update Trade Info |
//+------------------------------------------------------------------+
bool CTradeManager::UpdateTradeInfo(int index)
{
if(index < 0 || index >= m_trade_count) return false;
ulong ticket = m_managed_trades[index].ticket;
if(!PositionSelectByTicket(ticket))
{
// Position closed
m_managed_trades[index].status = TRADE_STATUS_CLOSED;
m_managed_trades[index].last_update = TimeCurrent();
return false;
}
// Update current values
m_managed_trades[index].current_price = PositionGetDouble(POSITION_PRICE_CURRENT);
m_managed_trades[index].profit = PositionGetDouble(POSITION_PROFIT);
m_managed_trades[index].swap = PositionGetDouble(POSITION_SWAP);
m_managed_trades[index].sl = PositionGetDouble(POSITION_SL);
m_managed_trades[index].tp = PositionGetDouble(POSITION_TP);
m_managed_trades[index].volume = PositionGetDouble(POSITION_VOLUME);
m_managed_trades[index].last_update = TimeCurrent();
// Update risk metrics
if(m_managed_trades[index].sl > 0)
{
double stop_distance = MathAbs(m_managed_trades[index].entry_price -
m_managed_trades[index].sl);
m_managed_trades[index].risk_amount = m_risk.CalculateTradeRisk(
m_managed_trades[index].volume, stop_distance, m_managed_trades[index].symbol);
m_managed_trades[index].risk_percent =
(m_managed_trades[index].risk_amount / AccountInfoDouble(ACCOUNT_BALANCE)) * 100;
if(m_managed_trades[index].tp > 0)
{
m_managed_trades[index].current_rr = m_utils.CalculateRiskReward(
m_managed_trades[index].entry_price,
m_managed_trades[index].sl,
m_managed_trades[index].tp,
m_managed_trades[index].type);
}
}
return true;
}
//+------------------------------------------------------------------+
//| Record Trade in History |
//+------------------------------------------------------------------+
void CTradeManager::RecordTrade(const TradeInfo &trade)
{
if(m_history_count >= ArraySize(m_history))
{
ArrayResize(m_history, m_history_count + 100);
}
m_history[m_history_count] = trade;
m_history_count++;
// Update performance metrics
UpdatePerformanceMetrics();
}
//+------------------------------------------------------------------+
//| Update Performance Metrics |
//+------------------------------------------------------------------+
void CTradeManager::UpdatePerformanceMetrics()
{
// Reset metrics
ZeroMemory(m_performance);
double total_wins = 0;
double total_losses = 0;
int win_count = 0;
int loss_count = 0;
// Analyze history
for(int i = 0; i < m_history_count; i++)
{
m_performance.total_trades++;
if(m_history[i].profit > 0)
{
m_performance.winning_trades++;
total_wins += m_history[i].profit;
win_count++;
}
else if(m_history[i].profit < 0)
{
m_performance.losing_trades++;
total_losses += MathAbs(m_history[i].profit);
loss_count++;
}
m_performance.total_profit += m_history[i].profit;
}
// Calculate metrics
if(m_performance.total_trades > 0)
{
m_performance.win_rate = (double)m_performance.winning_trades /
m_performance.total_trades * 100;
}
if(win_count > 0)
{
m_performance.average_win = total_wins / win_count;
}
if(loss_count > 0)
{
m_performance.average_loss = total_losses / loss_count;
}
if(total_losses > 0)
{
m_performance.profit_factor = total_wins / total_losses;
}
// Calculate drawdown
double current_balance = AccountInfoDouble(ACCOUNT_BALANCE);
if(current_balance > m_peak_balance)
{
m_peak_balance = current_balance;
}
m_performance.current_drawdown = ((m_peak_balance - current_balance) / m_peak_balance) * 100;
m_performance.max_drawdown = MathMax(m_performance.max_drawdown,
m_performance.current_drawdown);
// Expected value
if(m_performance.total_trades > 0)
{
m_performance.expected_value = m_performance.total_profit /
m_performance.total_trades;
}
// Sharpe ratio (simplified)
if(m_performance.average_loss > 0)
{
double avg_return = m_performance.expected_value;
double std_dev = MathSqrt(MathPow(m_performance.average_win - avg_return, 2) *
m_performance.win_rate / 100 +
MathPow(m_performance.average_loss + avg_return, 2) *
(1 - m_performance.win_rate / 100));
if(std_dev > 0)
{
m_performance.sharpe_ratio = avg_return / std_dev;
}
}
// Recovery factor
if(m_performance.max_drawdown > 0)
{
m_performance.recovery_factor = m_performance.total_profit /
m_performance.max_drawdown;
}
m_performance.last_update = TimeCurrent();
// Update risk manager with performance data
m_risk.UpdatePerformanceMetrics(m_performance.win_rate,
m_performance.average_win,
m_performance.average_loss);
}
//+------------------------------------------------------------------+
//| Get Performance Metrics |
//+------------------------------------------------------------------+
void CTradeManager::GetPerformanceMetrics(PerformanceMetrics &metrics)
{
UpdatePerformanceMetrics();
metrics = m_performance;
}
//+------------------------------------------------------------------+
//| Get Total Exposure |
//+------------------------------------------------------------------+
double CTradeManager::GetTotalExposure()
{
double total = 0;
for(int i = 0; i < m_trade_count; i++)
{
total += m_managed_trades[i].volume;
}
return total;
}
//+------------------------------------------------------------------+
//| Get Total Profit |
//+------------------------------------------------------------------+
double CTradeManager::GetTotalProfit()
{
double total = 0;
for(int i = 0; i < m_trade_count; i++)
{
total += m_managed_trades[i].profit;
}
return total;
}
//+------------------------------------------------------------------+
//| Get Trade Info |
//+------------------------------------------------------------------+
bool CTradeManager::GetTradeInfo(ulong ticket, TradeInfo &trade)
{
int index = FindTradeIndex(ticket);
if(index >= 0)
{
trade = m_managed_trades[index];
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Cleanup History |
//+------------------------------------------------------------------+
void CTradeManager::CleanupHistory()
{
// Keep only last 1000 trades in memory
if(m_history_count > 1000)
{
int to_remove = m_history_count - 1000;
// Shift remaining records
for(int i = 0; i < 1000; i++)
{
m_history[i] = m_history[i + to_remove];
}
m_history_count = 1000;
m_utils.Log("Trade history cleaned up", LOG_DEBUG);
}
}
//+------------------------------------------------------------------+
//| Export History |
//+------------------------------------------------------------------+
void CTradeManager::ExportHistory(string filename)
{
if(filename == "")
{
filename = StringFormat("TradeHistory_%s.csv",
TimeToString(TimeCurrent(), TIME_DATE));
}
TradeInfo history_array[];
ArrayResize(history_array, m_history_count);
for(int i = 0; i < m_history_count; i++)
{
history_array[i] = m_history[i];
}
m_utils.SaveTradeHistory(history_array, m_history_count);
}
//+------------------------------------------------------------------+
//| Reset Performance |
//+------------------------------------------------------------------+
void CTradeManager::ResetPerformance()
{
ZeroMemory(m_performance);
m_peak_balance = AccountInfoDouble(ACCOUNT_BALANCE);
m_last_update = TimeCurrent();
// Clear history
m_history_count = 0;
m_utils.Log("Performance metrics reset", LOG_INFO);
}
//+------------------------------------------------------------------+
//| Calculate Stop Loss |
//+------------------------------------------------------------------+
double CTradeManager::CalculateStopLoss(ENUM_ORDER_TYPE type, double entry, double atr)
{
double sl = 0;
double stop_distance = atr * m_atr_multiplier_sl;
if(type == ORDER_TYPE_BUY)
{
sl = entry - stop_distance;
}
else
{
sl = entry + stop_distance;
}
return m_utils.NormalizePrice(sl, _Symbol);
}
//+------------------------------------------------------------------+
//| Calculate Take Profit |
//+------------------------------------------------------------------+
double CTradeManager::CalculateTakeProfit(ENUM_ORDER_TYPE type, double entry, double sl)
{
if(sl == 0) return 0;
double risk = MathAbs(entry - sl);
double reward = risk * m_atr_multiplier_tp / m_atr_multiplier_sl;
double tp = 0;
if(type == ORDER_TYPE_BUY)
{
tp = entry + reward;
}
else
{
tp = entry - reward;
}
return m_utils.NormalizePrice(tp, _Symbol);
}
//+------------------------------------------------------------------+
//| Scale Out Position |
//+------------------------------------------------------------------+
bool CTradeManager::ScaleOut(ulong ticket, double percent, string reason)
{
if(!PositionSelectByTicket(ticket))
return false;
double current_volume = PositionGetDouble(POSITION_VOLUME);
double close_volume = current_volume * percent / 100;
return PartialClose(ticket, close_volume, reason);
}
//+------------------------------------------------------------------+
//| Scale In Position |
//+------------------------------------------------------------------+
bool CTradeManager::ScaleIn(ulong ticket, double additional_lots)
{
if(!PositionSelectByTicket(ticket))
return false;
// Create new position in same direction
MqlTradeRequest request = {};
request.symbol = PositionGetString(POSITION_SYMBOL);
request.type = (ENUM_ORDER_TYPE)PositionGetInteger(POSITION_TYPE);
request.volume = additional_lots;
request.magic = m_magic_number;
request.comment = "Scale In";
// Use same stops as original
request.sl = PositionGetDouble(POSITION_SL);
request.tp = PositionGetDouble(POSITION_TP);
ulong new_ticket = OpenTrade(request);
if(new_ticket > 0)
{
m_utils.Log(StringFormat("Scaled in position %d with %.2f lots",
ticket, additional_lots), LOG_INFO);
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Check Stop Levels |
//+------------------------------------------------------------------+
void CTradeManager::CheckStopLevels()
{
for(int i = 0; i < m_trade_count; i++)
{
// Check if stop loss needs adjustment
if(m_managed_trades[i].sl == 0)
{
// Apply default stop
double atr = m_tech.GetATR();
double sl = CalculateStopLoss(m_managed_trades[i].type,
m_managed_trades[i].entry_price, atr);
ModifyTrade(m_managed_trades[i].ticket, sl, m_managed_trades[i].tp);
}
}
}
//+------------------------------------------------------------------+
//| Check Take Profit Levels |
//+------------------------------------------------------------------+
void CTradeManager::CheckTakeProfitLevels()
{
for(int i = 0; i < m_trade_count; i++)
{
// Check if take profit needs adjustment
if(m_managed_trades[i].tp == 0 && m_managed_trades[i].sl != 0)
{
// Apply default TP based on SL
double tp = CalculateTakeProfit(m_managed_trades[i].type,
m_managed_trades[i].entry_price,
m_managed_trades[i].sl);
ModifyTrade(m_managed_trades[i].ticket, m_managed_trades[i].sl, tp);
}
}
}
#endif // TRADE_MANAGER_MQH