mql5/Experts/Advisors/ERMT_6.0.mq5
darashikoh 82c958b8a4
2025-07-30 20:50:27 +01:00

841 lines
No EOL
31 KiB
MQL5

//+------------------------------------------------------------------+
//| Risk Management Overlay EA v6.6 |
//| Modular Institutional Grade System |
//| Main Coordinator File |
//| Fixed: Zero stop distance + Lot calculation |
//+------------------------------------------------------------------+
#property copyright "Institutional Risk Management System v6.6"
#property version "6.51"
#property copyright "Institutional Risk Management System v6.6"
#property version "6.51"
#property strict
#property description "Modular risk management system with advanced monitoring"
//--- Include system modules
#include <Trade/Trade.mqh>
#include "Modules/RiskManager.mqh"
#include "Modules/TradeManager.mqh"
#include "Modules/EntrySystem.mqh"
#include "Modules/TechnicalAnalysis.mqh"
#include "Modules/Dashboard.mqh"
#include "Modules/DataTypes.mqh"
#include "Modules/Utilities.mqh"
//+------------------------------------------------------------------+
//| INPUT PARAMETERS - GROUPED BY MODULE |
//+------------------------------------------------------------------+
//--- System Configuration
input group "=== SYSTEM CONFIGURATION ==="
input bool SystemEnabled = true; // Enable System
input int TradingMagic = 12345; // EA Magic Number
input bool ManageExternalTrades = true; // Manage External Trades
input int MagicNumberFilter = 0; // External Magic Filter (0=all)
input ENUM_LOG_LEVEL LogLevel = LOG_INFO; // Logging Level
//--- Entry System Parameters
input group "=== ENTRY SYSTEM ==="
input ENUM_ENTRY_MODE EntryMode = ENTRY_MA_CROSS; // Entry System Type
input bool EnableMultipleEntries = false; // Allow Multiple Entries
input int MaxPositions = 3; // Maximum Positions
input double MinTimeBetweenTrades = 60; // Min Minutes Between Trades
//--- Risk Management Parameters
input group "=== RISK MANAGEMENT ==="
input ENUM_POSITION_SIZING SizingMode = PS_RISK_PERCENT; // Position Sizing Mode
input double RiskPercent = 2.5; // Risk Per Trade (%)
input double MaxRiskPercent = 5.0; // Maximum Risk (%)
input double MaxDailyDrawdown = 5.0; // Max Daily Drawdown (%)
input double MaxTotalExposure = 10.0; // Max Total Exposure (%)
input bool UseKellyCriterion = false; // Use Kelly Criterion
input double KellyFraction = 0.25; // Kelly Fraction
input double MinLotSize = 0.01; // Minimum Lot Size
input double MaxLotSize = 10.0; // Maximum Lot Size
//--- Trade Management Parameters
input group "=== TRADE MANAGEMENT ==="
input ENUM_TP_MODE TPMode = TP_ADAPTIVE; // Take Profit Mode
input ENUM_SL_MODE SLMode = SL_ATR; // Stop Loss Mode
input bool EnableBreakeven = true; // Enable Breakeven
input double BreakevenTrigger = 1.0; // Breakeven at X:1
input bool EnableTrailing = true; // Enable Trailing Stop
input double TrailingStart = 1.5; // Start Trailing at X:1
input bool EnablePartialClose = true; // Enable Partial Closes
input string PartialLevels = "1.0:33,2.0:33,3.0:20"; // Partial Close Levels
//--- External Trade Management
input group "=== EXTERNAL TRADE RULES ==="
input bool ForceRiskRules = true; // Force Risk Rules
input bool ForceStopLoss = true; // Force Stop Loss
input bool ForceTakeProfit = true; // Force Take Profit
input bool OverrideExternalSL = false; // Override Existing SL
input bool OverrideExternalTP = false; // Override Existing TP
input double DefaultExternalRisk = 1.0; // Default External Risk (%)
input bool CloseExcessiveRisk = true; // Close Excessive Risk
//--- Technical Analysis Parameters
input group "=== TECHNICAL ANALYSIS ==="
input bool UseSupportResistance = true; // Use S/R Levels
input bool UseFibonacci = true; // Use Fibonacci
input bool UsePivotPoints = true; // Use Pivot Points
input bool UseMarketStructure = true; // Use Market Structure
input bool EnableLogging = true; // Enable Detailed Logging
input int TechnicalLookback = 200; // Technical Lookback Bars
//--- Dashboard Parameters
input group "=== DASHBOARD SETTINGS ==="
input bool ShowDashboard = true; // Show Dashboard
input int DashboardX = 20; // Dashboard X Position
input int DashboardY = 30; // Dashboard Y Position
input color DashboardColor = clrWhiteSmoke; // Dashboard Text Color
input color ProfitColor = clrLime; // Profit Color
input color LossColor = clrRed; // Loss Color
input int UpdateFrequency = 1; // Update Frequency (seconds)
//+------------------------------------------------------------------+
//| GLOBAL OBJECTS |
//+------------------------------------------------------------------+
//--- Module instances
CRiskManager *RiskMgr;
CTradeManager *TradeMgr;
CEntrySystem *EntrySys;
CTechnicalAnalysis *TechAnalysis;
CDashboard *Dashboard;
CUtilities *Utils;
//--- System state
SystemState g_SystemState;
PerformanceMetrics g_Performance;
MarketConditions g_MarketConditions;
//--- Trade tracking
ManagedTrade g_ManagedTrades[];
int g_TotalTrades = 0;
//--- Timing variables
datetime g_LastUpdateTime = 0;
datetime g_LastTradeTime = 0;
datetime g_LastReportTime = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Initialize system state
g_SystemState.is_active = SystemEnabled;
g_SystemState.magic_number = TradingMagic;
g_SystemState.start_balance = AccountInfoDouble(ACCOUNT_BALANCE);
g_SystemState.peak_balance = g_SystemState.start_balance;
g_SystemState.session_start = TimeCurrent();
//--- Create module instances
RiskMgr = new CRiskManager();
TradeMgr = new CTradeManager();
EntrySys = new CEntrySystem();
TechAnalysis = new CTechnicalAnalysis();
Dashboard = new CDashboard();
Utils = new CUtilities();
//--- Initialize modules
if(!InitializeModules())
{
Print("Failed to initialize modules");
return(INIT_FAILED);
}
// Add this to OnInit() after module initialization:
//--- Wait for indicators to be ready
if(MQLInfoInteger(MQL_TESTER))
{
Print("Backtest mode: Waiting for indicators to initialize...");
// Wait for sufficient bars
int required_bars = MathMax(200, TechnicalLookback);
if(Bars(_Symbol, PERIOD_CURRENT) < required_bars)
{
Print("Insufficient bars for indicator calculation. Need ", required_bars,
", have ", Bars(_Symbol, PERIOD_CURRENT));
return(INIT_FAILED);
}
// Validate ATR is working
double test_atr = (*TechAnalysis).GetATR(14);
if(test_atr <= 0)
{
Print("ATR indicator not ready. Please ensure sufficient historical data.");
return(INIT_FAILED);
}
Print("Indicators initialized successfully. ATR: ", test_atr);
}
//--- Initialize performance metrics
ZeroMemory(g_Performance);
//--- Load existing positions
LoadExistingPositions();
//--- Create dashboard
if(ShowDashboard)
{
//Dashboard->Create(DashboardX, DashboardY, DashboardColor, ProfitColor, LossColor);
// Instead of copying, manually type:
(*Dashboard).Create(DashboardX, DashboardY, DashboardColor, ProfitColor, LossColor);
(*Dashboard).Update(g_Performance, g_ManagedTrades, g_MarketConditions);
}
//--- Set timer for periodic updates
EventSetTimer(UpdateFrequency);
//--- Log initialization
(*Utils).Log(LOG_INFO, StringFormat("Risk Management Overlay v6.6 initialized. Balance: %.2f",
g_SystemState.start_balance));
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- Kill timer
EventKillTimer();
//--- Save final report
if(Utils != NULL)
(*Utils).GenerateReport(g_Performance, g_ManagedTrades);
//--- Cleanup modules
if(Dashboard != NULL && ShowDashboard)
(*Dashboard).Destroy();
//--- Delete module instances
delete RiskMgr;
delete TradeMgr;
delete EntrySys;
delete TechAnalysis;
delete Dashboard;
delete Utils;
//--- Log shutdown
Print("Risk Management Overlay shutdown: ", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Check if system is enabled
if(!g_SystemState.is_active || !SystemEnabled)
return;
//--- Check terminal connection
if(!TerminalInfoInteger(TERMINAL_CONNECTED))
return;
//--- Check terminal connection and auto trading
if(Utils != NULL)
{
if(!(*Utils).CheckTerminalConnection())
return;
if(!(*Utils).CheckAutoTrading())
return;
}
//--- Update market conditions
UpdateMarketConditions();
//--- Process existing trades
ProcessManagedTrades();
//--- Check for new external trades
if(ManageExternalTrades)
CheckForNewExternalTrades();
//--- Check entry signals (if enabled)
if(EntryMode != ENTRY_DISABLED)
CheckEntrySignals();
//--- Update dashboard
if(ShowDashboard && TimeCurrent() - g_LastUpdateTime >= UpdateFrequency)
{
(*Dashboard).Update(g_Performance, g_ManagedTrades, g_MarketConditions);
g_LastUpdateTime = TimeCurrent();
}
//--- Periodic tasks
PerformPeriodicTasks();
}
//+------------------------------------------------------------------+
//| Initialize all modules |
//+------------------------------------------------------------------+
bool InitializeModules()
{
//--- Initialize Risk Manager
RiskManagerConfig riskConfig;
riskConfig.sizing_mode = SizingMode;
riskConfig.risk_percent = RiskPercent;
riskConfig.max_risk_percent = MaxRiskPercent;
riskConfig.max_daily_dd = MaxDailyDrawdown;
riskConfig.max_exposure = MaxTotalExposure;
riskConfig.use_kelly = UseKellyCriterion;
riskConfig.kelly_fraction = KellyFraction;
riskConfig.min_lot_size = MinLotSize;
riskConfig.max_lot_size = MaxLotSize;
if(!(*RiskMgr).Initialize(riskConfig))
return false;
//--- Initialize Trade Manager
TradeManagerConfig tradeConfig;
tradeConfig.tp_mode = TPMode;
tradeConfig.sl_mode = SLMode;
tradeConfig.enable_breakeven = EnableBreakeven;
tradeConfig.breakeven_trigger = BreakevenTrigger;
tradeConfig.enable_trailing = EnableTrailing;
tradeConfig.trailing_start = TrailingStart;
tradeConfig.enable_partial = EnablePartialClose;
tradeConfig.partial_levels = PartialLevels;
tradeConfig.force_sl = ForceStopLoss;
tradeConfig.force_tp = ForceTakeProfit;
tradeConfig.override_sl = OverrideExternalSL;
tradeConfig.override_tp = OverrideExternalTP;
if(!(*TradeMgr).Initialize(tradeConfig, TradingMagic))
return false;
//--- Initialize Entry System
EntrySystemConfig entryConfig;
entryConfig.entry_mode = EntryMode;
entryConfig.max_positions = MaxPositions;
entryConfig.min_time_between = MinTimeBetweenTrades;
entryConfig.allow_multiple = EnableMultipleEntries;
if(!(*EntrySys).Initialize(entryConfig))
return false;
//--- Initialize Technical Analysis
TechnicalConfig techConfig;
techConfig.use_sr = UseSupportResistance;
techConfig.use_fib = UseFibonacci;
techConfig.use_pivot = UsePivotPoints;
techConfig.use_structure = UseMarketStructure;
techConfig.lookback = TechnicalLookback;
if(!(*TechAnalysis).Initialize(techConfig))
return false;
//--- Initialize Utilities
if(!(*Utils).Initialize(LogLevel))
return false;
return true;
}
//+------------------------------------------------------------------+
//| Load existing positions into management system |
//+------------------------------------------------------------------+
void LoadExistingPositions()
{
int total = PositionsTotal();
for(int i = 0; i < total; i++)
{
string symbol = PositionGetSymbol(i);
if(symbol != _Symbol)
continue;
if(PositionSelect(symbol))
{
ulong magic = PositionGetInteger(POSITION_MAGIC);
bool should_manage = false;
//--- Check if we should manage this position
if(magic == TradingMagic)
should_manage = true;
else if(ManageExternalTrades && (MagicNumberFilter == 0 || magic == MagicNumberFilter))
should_manage = true;
if(should_manage)
{
ManagedTrade trade;
if(CreateManagedTrade(trade, PositionGetInteger(POSITION_TICKET)))
{
AddManagedTrade(trade);
(*Utils).Log(LOG_INFO, StringFormat("Loaded position #%d into management",
trade.ticket));
}
}
}
}
}
//+------------------------------------------------------------------+
//| Update market conditions - ENHANCED VERSION |
//+------------------------------------------------------------------+
void UpdateMarketConditions()
{
// Get fresh market analysis
g_MarketConditions = (*TechAnalysis).AnalyzeMarket();
// Additional validation and fallback
if(g_MarketConditions.volatility <= 0)
{
Print("UpdateMarketConditions: Invalid volatility from TechAnalysis, calculating fallback");
// Direct ATR calculation
double atr = (*TechAnalysis).GetATR(14);
if(atr > 0)
{
g_MarketConditions.volatility = atr;
Print("UpdateMarketConditions: Set volatility to ", atr);
}
else
{
// Emergency fallback
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
g_MarketConditions.volatility = price * 0.001; // 0.1% of price
Print("UpdateMarketConditions: Using emergency volatility: ", g_MarketConditions.volatility);
}
}
g_MarketConditions.spread = (double)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
//--- Update risk manager with market conditions
(*RiskMgr).UpdateMarketConditions(g_MarketConditions);
}
//+------------------------------------------------------------------+
//| Process all managed trades |
//+------------------------------------------------------------------+
void ProcessManagedTrades()
{
for(int i = ArraySize(g_ManagedTrades) - 1; i >= 0; i--)
{
//--- Check if position still exists
if(!PositionSelectByTicket(g_ManagedTrades[i].ticket))
{
//--- Position closed, update performance
UpdatePerformanceMetrics(g_ManagedTrades[i]);
RemoveManagedTrade(i);
continue;
}
//--- Update trade information
UpdateTradeInfo(g_ManagedTrades[i]);
//--- Apply risk management
(*RiskMgr).ValidateAndAdjustRisk(g_ManagedTrades[i]);
//--- Apply trade management
(*TradeMgr).ManageTrade(g_ManagedTrades[i], g_MarketConditions);
}
}
//+------------------------------------------------------------------+
//| Check for new external trades |
//+------------------------------------------------------------------+
void CheckForNewExternalTrades()
{
int total = PositionsTotal();
for(int i = 0; i < total; i++)
{
string symbol = PositionGetSymbol(i);
if(symbol != _Symbol)
continue;
if(PositionSelect(symbol))
{
ulong ticket = PositionGetInteger(POSITION_TICKET);
ulong magic = PositionGetInteger(POSITION_MAGIC);
//--- Skip if already managed or wrong magic
if(IsTradeManaged(ticket))
continue;
if(magic == TradingMagic)
continue; // Our own trade
if(MagicNumberFilter != 0 && magic != MagicNumberFilter)
continue;
//--- Create managed trade entry
ManagedTrade trade;
if(CreateManagedTrade(trade, ticket))
{
//--- Apply risk rules to external trade
if(ForceRiskRules)
{
(*RiskMgr).EnforceRiskRules(trade, DefaultExternalRisk, CloseExcessiveRisk);
}
AddManagedTrade(trade);
(*Utils).Log(LOG_INFO, StringFormat("Added external trade #%d to management", ticket));
}
}
}
}
//+------------------------------------------------------------------+
//| Check entry signals - ENHANCED VERSION WITH DEBUGGING |
//+------------------------------------------------------------------+
void CheckEntrySignals()
{
//--- Check position limits
if(ArraySize(g_ManagedTrades) >= MaxPositions)
{
if(EnableLogging)
(*Utils).Log(LOG_DEBUG, "Max positions reached");
return;
}
//--- Check time restriction
if(TimeCurrent() - g_LastTradeTime < MinTimeBetweenTrades * 60)
{
if(EnableLogging)
(*Utils).Log(LOG_DEBUG, "Time restriction active");
return;
}
//--- Debug market conditions BEFORE signal check
Print("=== Market Conditions Debug ===");
Print("Market Volatility (ATR): ", g_MarketConditions.volatility);
Print("Market Trend Strength: ", g_MarketConditions.trend_strength);
Print("Market Condition: ", EnumToString(g_MarketConditions.condition));
// Force update market conditions if ATR is invalid
if(g_MarketConditions.volatility <= 0)
{
Print("WARNING: Market volatility is 0, forcing update");
UpdateMarketConditions();
Print("After update - Market Volatility: ", g_MarketConditions.volatility);
}
//--- Get entry signal
EntrySignal signal = (*EntrySys).CheckSignal(g_MarketConditions);
if(EnableLogging && signal.signal_type != SIGNAL_NONE)
{
(*Utils).Log(LOG_DEBUG, StringFormat("Signal found: Type=%d, Confidence=%.1f, Comment=%s",
signal.signal_type, signal.confidence, signal.comment));
}
if(signal.signal_type != SIGNAL_NONE)
{
// Debug logging
Print("=== Signal Debug Info ===");
Print("Signal Type: ", signal.comment);
Print("Market Volatility (ATR): ", g_MarketConditions.volatility);
Print("Stop Distance: ", signal.stop_loss_distance, " (",
signal.stop_loss_distance / _Point, " points)");
Print("TP Distance: ", signal.take_profit_distance, " (",
signal.take_profit_distance / _Point, " points)");
Print("Current Spread: ", SymbolInfoInteger(_Symbol, SYMBOL_SPREAD), " points");
Print("Min Stop Level: ", SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL), " points");
Print("======================");
// Additional safety check
if(signal.stop_loss_distance <= 0)
{
(*Utils).Log(LOG_ERROR, StringFormat("Zero stop distance detected! Signal: %s, ATR: %.5f",
signal.comment, g_MarketConditions.volatility));
// Emergency fallback
double emergency_stop = 50 * SymbolInfoDouble(_Symbol, SYMBOL_POINT); // 50 points
signal.stop_loss_distance = emergency_stop;
signal.take_profit_distance = emergency_stop * 2; // 2:1 RR
(*Utils).Log(LOG_WARNING, StringFormat("Applied emergency stop: %.5f", emergency_stop));
}
//--- Log signal details
if(EnableLogging)
{
(*Utils).Log(LOG_INFO, StringFormat("Entry Signal: %s | SL Distance: %.5f (%.1f pips) | TP Distance: %.5f",
signal.comment, signal.stop_loss_distance,
signal.stop_loss_distance / _Point / 10, signal.take_profit_distance));
}
//--- Calculate position size
double lotSize = (*RiskMgr).CalculatePositionSize(signal.stop_loss_distance);
if(lotSize > 0 && (*RiskMgr).ValidateNewPosition(lotSize, signal.stop_loss_distance))
{
//--- Prepare trade request
TradeRequest request;
request.symbol = _Symbol;
request.volume = lotSize;
request.type = (signal.signal_type == SIGNAL_BUY) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
request.sl_distance = signal.stop_loss_distance;
request.tp_distance = signal.take_profit_distance;
request.comment = signal.comment;
//--- Execute trade
ulong ticket = (*TradeMgr).OpenPosition(request);
if(ticket > 0)
{
g_LastTradeTime = TimeCurrent();
//--- Add to managed trades
ManagedTrade trade;
if(CreateManagedTrade(trade, ticket))
{
trade.entry_reason = signal.comment;
trade.entry_signal = signal;
AddManagedTrade(trade);
(*Utils).Log(LOG_INFO, StringFormat("Opened %s position #%d, Lots: %.2f, Risk: %.2f%%",
(signal.signal_type == SIGNAL_BUY) ? "BUY" : "SELL",
ticket, lotSize, RiskPercent));
}
}
else if(EnableLogging)
{
(*Utils).Log(LOG_WARNING, "Failed to open position - check logs for details");
}
}
else if(EnableLogging && lotSize <= 0)
{
(*Utils).Log(LOG_WARNING, "Invalid lot size calculated - risk limits may be exceeded");
}
}
}
//+------------------------------------------------------------------+
//| Perform periodic maintenance tasks |
//+------------------------------------------------------------------+
void PerformPeriodicTasks()
{
//--- Daily reset check
MqlDateTime current_time;
TimeToStruct(TimeCurrent(), current_time);
static int last_day = -1;
if(current_time.day != last_day)
{
last_day = current_time.day;
ResetDailyMetrics();
}
//--- Generate periodic reports
if(TimeCurrent() - g_LastReportTime > 3600) // Hourly
{
(*Utils).GenerateReport(g_Performance, g_ManagedTrades);
g_LastReportTime = TimeCurrent();
}
//--- Check for news events
if((*Utils).IsNewsTime()) // Fixed: Added opening parenthesis
{
g_MarketConditions.news_event = true;
}
}
//+------------------------------------------------------------------+
//| Create managed trade structure |
//+------------------------------------------------------------------+
bool CreateManagedTrade(ManagedTrade &trade, ulong ticket)
{
if(!PositionSelectByTicket(ticket))
return false;
trade.ticket = ticket;
trade.symbol = PositionGetString(POSITION_SYMBOL);
trade.type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
trade.open_time = (datetime)PositionGetInteger(POSITION_TIME);
trade.open_price = PositionGetDouble(POSITION_PRICE_OPEN);
trade.volume = PositionGetDouble(POSITION_VOLUME);
trade.current_volume = trade.volume;
trade.sl = PositionGetDouble(POSITION_SL);
trade.tp = PositionGetDouble(POSITION_TP);
trade.magic = PositionGetInteger(POSITION_MAGIC);
trade.profit = PositionGetDouble(POSITION_PROFIT);
trade.commission = 0; // Will be calculated from deal history if needed
trade.swap = PositionGetDouble(POSITION_SWAP);
//--- Initialize management fields
trade.be_activated = false;
trade.trailing_activated = false;
trade.partial_count = 0;
trade.risk_amount = (*RiskMgr).CalculateTradeRisk(trade);
trade.max_profit = 0;
trade.max_drawdown = 0;
trade.management_start = TimeCurrent();
//--- Set entry reason
if(trade.magic == TradingMagic)
trade.entry_reason = "EA Signal";
else
trade.entry_reason = "External Trade";
return true;
}
//+------------------------------------------------------------------+
//| Add trade to managed array |
//+------------------------------------------------------------------+
void AddManagedTrade(ManagedTrade &trade)
{
int size = ArraySize(g_ManagedTrades);
ArrayResize(g_ManagedTrades, size + 1);
g_ManagedTrades[size] = trade;
g_TotalTrades++;
}
//+------------------------------------------------------------------+
//| Remove trade from managed array |
//+------------------------------------------------------------------+
void RemoveManagedTrade(int index)
{
int size = ArraySize(g_ManagedTrades);
if(index < 0 || index >= size)
return;
for(int i = index; i < size - 1; i++)
{
g_ManagedTrades[i] = g_ManagedTrades[i + 1];
}
ArrayResize(g_ManagedTrades, size - 1);
}
//+------------------------------------------------------------------+
//| Check if trade is already managed |
//+------------------------------------------------------------------+
bool IsTradeManaged(ulong ticket)
{
for(int i = 0; i < ArraySize(g_ManagedTrades); i++)
{
if(g_ManagedTrades[i].ticket == ticket)
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Update trade information |
//+------------------------------------------------------------------+
void UpdateTradeInfo(ManagedTrade &trade)
{
if(!PositionSelectByTicket(trade.ticket))
return;
trade.current_volume = PositionGetDouble(POSITION_VOLUME);
trade.sl = PositionGetDouble(POSITION_SL);
trade.tp = PositionGetDouble(POSITION_TP);
trade.profit = PositionGetDouble(POSITION_PROFIT);
trade.commission = (*Utils).CalculatePositionCommission(trade.ticket);
trade.swap = PositionGetDouble(POSITION_SWAP);
//--- Update max profit/drawdown
double total_profit = trade.profit + trade.commission + trade.swap;
if(total_profit > trade.max_profit)
trade.max_profit = total_profit;
if(total_profit < trade.max_drawdown)
trade.max_drawdown = total_profit;
}
//+------------------------------------------------------------------+
//| Update performance metrics |
//+------------------------------------------------------------------+
void UpdatePerformanceMetrics(ManagedTrade &trade)
{
double final_profit = trade.profit + trade.commission + trade.swap;
//--- Update counts
g_Performance.total_trades++;
if(final_profit > 0)
{
g_Performance.winning_trades++;
g_Performance.gross_profit += final_profit;
g_Performance.consecutive_wins++;
g_Performance.consecutive_losses = 0;
}
else
{
g_Performance.losing_trades++;
g_Performance.gross_loss += MathAbs(final_profit);
g_Performance.consecutive_losses++;
g_Performance.consecutive_wins = 0;
}
//--- Update max consecutive
if(g_Performance.consecutive_wins > g_Performance.max_consecutive_wins)
g_Performance.max_consecutive_wins = g_Performance.consecutive_wins;
if(g_Performance.consecutive_losses > g_Performance.max_consecutive_losses)
g_Performance.max_consecutive_losses = g_Performance.consecutive_losses;
//--- Update profit factor and win rate
if(g_Performance.gross_loss > 0)
g_Performance.profit_factor = g_Performance.gross_profit / g_Performance.gross_loss;
else
g_Performance.profit_factor = (g_Performance.gross_profit > 0) ? 999.99 : 0;
g_Performance.win_rate = (g_Performance.total_trades > 0) ?
(double)g_Performance.winning_trades / g_Performance.total_trades * 100 : 0;
//--- Update drawdown
double current_balance = AccountInfoDouble(ACCOUNT_BALANCE);
if(current_balance > g_SystemState.peak_balance)
g_SystemState.peak_balance = current_balance;
double drawdown = (g_SystemState.peak_balance - current_balance) / g_SystemState.peak_balance * 100;
if(drawdown > g_Performance.max_drawdown_percent)
g_Performance.max_drawdown_percent = drawdown;
}
//+------------------------------------------------------------------+
//| Reset daily metrics |
//+------------------------------------------------------------------+
void ResetDailyMetrics()
{
g_Performance.daily_profit = 0;
g_Performance.daily_trades = 0;
g_Performance.daily_volume = 0;
(*Utils).Log(LOG_INFO, "Daily metrics reset");
}
//+------------------------------------------------------------------+
//| Timer function for periodic updates |
//+------------------------------------------------------------------+
void OnTimer()
{
//--- Update dashboard if needed
if(ShowDashboard)
{
(*Dashboard).Update(g_Performance, g_ManagedTrades, g_MarketConditions);
}
}
//+------------------------------------------------------------------+
//| Trade transaction event |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
const MqlTradeRequest& request,
const MqlTradeResult& result)
{
//--- Handle trade events
if(trans.type == TRADE_TRANSACTION_DEAL_ADD)
{
if(trans.deal_type == DEAL_TYPE_BUY || trans.deal_type == DEAL_TYPE_SELL)
{
(*Utils).Log(LOG_DEBUG, StringFormat("New deal: #%d, Volume: %.2f",
trans.deal, trans.volume));
}
}
}
//+------------------------------------------------------------------+