MT5 uses deals and orders rather than MT4's flat ticket system. The metric engine reads DEAL_ENTRY_OUT and DEAL_ENTRY_OUT_BY events to capture true round-turn completed trades, fees, commissions, and swaps.
Code: Select all
//+------------------------------------------------------------------+
//| RealizedMetricsHUD.mq5 |
//| Copyright 2026, Quant Metrics |
//+------------------------------------------------------------------+
#property copyright "Quant Metrics"
#property link ""
#property version "1.00"
#property indicator_chart_window
#property indicator_plots 0
#define PREFIX_HUD "HUD_METRIC_"
//--- Inputs
input group "=== History & Filters ==="
input bool InpFilterSymbol = true; // Filter Current Symbol Only
input long InpMagicNumber = 0; // Magic Number (0 = All)
input datetime InpStartDate = D'2024.01.01 00:00'; // History Start Date
input datetime InpEndDate = D'2030.01.01 00:00'; // History End Date
input group "=== HUD Settings ==="
input ENUM_BASE_CORNER InpCorner = CORNER_RIGHT_UPPER; // Chart Corner
input int InpXOffset = 240; // X Offset (px)
input int InpYOffset = 30; // Y Offset (px)
input int InpRowHeight = 18; // Row Height (px)
input color InpTextColor = clrWhite; // Label Text Color
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
UpdateDashboard();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, PREFIX_HUD);
ChartRedraw(0);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
UpdateDashboard();
return(rates_total);
}
//+------------------------------------------------------------------+
//| Calculate performance metrics and redraw HUD |
//+------------------------------------------------------------------+
void UpdateDashboard()
{
int totalTrades = 0;
int wins = 0;
int losses = 0;
double grossProfit = 0.0;
double grossLoss = 0.0;
double netProfit = 0.0;
// Load deal history for the specified window
if(HistorySelect(InpStartDate, InpEndDate))
{
int totalDeals = HistoryDealsTotal();
for(int i = 0; i < totalDeals; i++)
{
ulong ticket = HistoryDealGetTicket(i);
if(ticket == 0) continue;
// Only evaluate exiting deals (completed trades)
long entry = HistoryDealGetInteger(ticket, DEAL_ENTRY);
if(entry != DEAL_ENTRY_OUT && entry != DEAL_ENTRY_OUT_BY) continue;
long dealType = HistoryDealGetInteger(ticket, DEAL_TYPE);
if(dealType != DEAL_TYPE_BUY && dealType != DEAL_TYPE_SELL) continue;
string symbol = HistoryDealGetString(ticket, DEAL_SYMBOL);
if(InpFilterSymbol && symbol != _Symbol) continue;
long magic = HistoryDealGetInteger(ticket, DEAL_MAGIC);
if(InpMagicNumber > 0 && magic != InpMagicNumber) continue;
// Full cost realization: Profit + Swap + Commission + Broker Fee
double pnl = HistoryDealGetDouble(ticket, DEAL_PROFIT)
+ HistoryDealGetDouble(ticket, DEAL_SWAP)
+ HistoryDealGetDouble(ticket, DEAL_COMMISSION)
+ HistoryDealGetDouble(ticket, DEAL_FEE);
netProfit += pnl;
totalTrades++;
if(pnl > 0.0)
{
wins++;
grossProfit += pnl;
}
else if(pnl < 0.0)
{
losses++;
grossLoss += MathAbs(pnl);
}
}
}
// Metric Derivations
double winRate = totalTrades > 0 ? ((double)wins / totalTrades) * 100.0 : 0.0;
double avgWin = wins > 0 ? (grossProfit / wins) : 0.0;
double avgLoss = losses > 0 ? (grossLoss / losses) : 0.0;
double rrRatio = avgLoss > 0.0 ? (avgWin / avgLoss) : 0.0;
double profitFactor = grossLoss > 0.0 ? (grossProfit / grossLoss) : (grossProfit > 0.0 ? 99.0 : 0.0);
double expectancy = (winRate / 100.0 * avgWin) - ((1.0 - winRate / 100.0) * avgLoss);
// Dynamic Color Coding
color wrColor = (winRate >= 50.0) ? clrMediumSeaGreen : clrCrimson;
color rrColor = (rrRatio >= 1.5) ? clrMediumSeaGreen : (rrRatio >= 1.0 ? clrGoldenrod : clrCrimson);
color pfColor = (profitFactor >= 1.5) ? clrMediumSeaGreen : (profitFactor >= 1.0 ? clrGoldenrod : clrCrimson);
color expColor = (expectancy > 0.0) ? clrMediumSeaGreen : clrCrimson;
color npColor = (netProfit >= 0.0) ? clrMediumSeaGreen : clrCrimson;
// Render Panel
RenderPanelBackground(230, 7 * InpRowHeight + 16);
RenderRow(0, "Total Trades:", IntegerToString(totalTrades), clrWhite);
RenderRow(1, "Win Rate:", DoubleToString(winRate, 2) + "%", wrColor);
RenderRow(2, "Realized R:R:", "1 : " + DoubleToString(rrRatio, 2), rrColor);
RenderRow(3, "Profit Factor:", DoubleToString(profitFactor, 2), pfColor);
RenderRow(4, "Expectancy / Trade:", DoubleToString(expectancy, 2), expColor);
RenderRow(5, "Avg Win / Loss:", DoubleToString(avgWin, 2) + " / " + DoubleToString(avgLoss, 2), clrSilver);
RenderRow(6, "Net Profit:", DoubleToString(netProfit, 2), npColor);
ChartRedraw(0);
}
//+------------------------------------------------------------------+
//| GUI Helper: Render Background Panel |
//+------------------------------------------------------------------+
void RenderPanelBackground(int width, int height)
{
string name = PREFIX_HUD + "BG";
if(ObjectFind(0, name) < 0)
{
ObjectCreate(0, name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_CORNER, InpCorner);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, InpXOffset + 10);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, InpYOffset - 8);
ObjectSetInteger(0, name, OBJPROP_XSIZE, width);
ObjectSetInteger(0, name, OBJPROP_YSIZE, height);
ObjectSetInteger(0, name, OBJPROP_BGCOLOR, C'20,24,35');
ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, C'60,65,80');
ObjectSetInteger(0, name, OBJPROP_BORDER_TYPE, BORDER_FLAT);
ObjectSetInteger(0, name, OBJPROP_BACK, false);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
}
}
//+------------------------------------------------------------------+
//| GUI Helper: Render Single Data Row |
//+------------------------------------------------------------------+
void RenderRow(int row, string title, string val, color valColor)
{
string titleName = PREFIX_HUD + "T_" + IntegerToString(row);
string valName = PREFIX_HUD + "V_" + IntegerToString(row);
int yPos = InpYOffset + (row * InpRowHeight);
// Title Label
if(ObjectFind(0, titleName) < 0)
{
ObjectCreate(0, titleName, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, titleName, OBJPROP_CORNER, InpCorner);
ObjectSetInteger(0, titleName, OBJPROP_XDISTANCE, InpXOffset);
ObjectSetString(0, titleName, OBJPROP_FONT, "Segoe UI");
ObjectSetInteger(0, titleName, OBJPROP_FONTSIZE, 9);
ObjectSetInteger(0, titleName, OBJPROP_COLOR, InpTextColor);
ObjectSetInteger(0, titleName, OBJPROP_SELECTABLE, false);
}
ObjectSetInteger(0, titleName, OBJPROP_YDISTANCE, yPos);
ObjectSetString(0, titleName, OBJPROP_TEXT, title);
// Value Label
if(ObjectFind(0, valName) < 0)
{
ObjectCreate(0, valName, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, valName, OBJPROP_CORNER, InpCorner);
ObjectSetInteger(0, valName, OBJPROP_XDISTANCE, InpXOffset - 120);
ObjectSetString(0, valName, OBJPROP_FONT, "Segoe UI Semibold");
ObjectSetInteger(0, valName, OBJPROP_FONTSIZE, 9);
ObjectSetInteger(0, valName, OBJPROP_SELECTABLE, false);
}
ObjectSetInteger(0, valName, OBJPROP_YDISTANCE, yPos);
ObjectSetString(0, valName, OBJPROP_TEXT, val);
ObjectSetInteger(0, valName, OBJPROP_COLOR, valColor);
}