mql5/Experts/Advisors/Modules/Dashboard.mqh

588 lines
23 KiB
MQL5
Raw Permalink Normal View History

//+------------------------------------------------------------------+
//| Dashboard.mqh |
//| Visual Dashboard Module |
//+------------------------------------------------------------------+
#ifndef DASHBOARD_MQH
#define DASHBOARD_MQH
#include "DataTypes.mqh"
//+------------------------------------------------------------------+
//| Dashboard Class |
//+------------------------------------------------------------------+
class CDashboard
{
private:
//--- Display settings
int m_x_pos;
int m_y_pos;
color m_text_color;
color m_profit_color;
color m_loss_color;
color m_header_color;
color m_bg_color;
int m_font_size;
string m_font_name;
//--- Object names
string m_prefix;
string m_bg_name;
//--- Layout settings
int m_line_height;
int m_panel_width;
int m_section_gap;
//--- Display sections
bool m_show_account;
bool m_show_performance;
bool m_show_trades;
bool m_show_risk;
bool m_show_market;
//--- Helper methods
void CreateBackground();
void CreateTextLabel(string name, int x, int y, string text,
color clr, int size = 0, string font = "");
void UpdateTextLabel(string name, string text, color clr = clrNONE);
string FormatCurrency(double value);
string FormatPercent(double value);
string FormatRMultiple(double value);
color GetColorForValue(double value, bool inverse = false);
void CreateSection(string title, int &y_offset);
public:
CDashboard();
~CDashboard();
//--- Main methods
bool Create(int x_pos, int y_pos, color text_color,
color profit_color, color loss_color);
void Destroy();
void Update(const PerformanceMetrics &performance,
const ManagedTrade &trades[],
const MarketConditions &market);
//--- Settings
void SetColors(color text, color profit, color loss,
color header, color bg);
void SetFont(string font_name, int font_size);
void SetSections(bool account, bool perf, bool trades,
bool risk, bool market);
void Show();
void Hide();
void Move(int x, int y);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CDashboard::CDashboard()
{
m_prefix = "RMEA_DASH_";
m_bg_name = m_prefix + "BG";
m_x_pos = 20;
m_y_pos = 30;
m_text_color = clrWhiteSmoke;
m_profit_color = clrLime;
m_loss_color = clrRed;
m_header_color = clrGold;
m_bg_color = C'20,20,20';
m_font_size = 9;
m_font_name = "Arial";
m_line_height = 16;
m_panel_width = 350;
m_section_gap = 20;
//--- Show all sections by default
m_show_account = true;
m_show_performance = true;
m_show_trades = true;
m_show_risk = true;
m_show_market = true;
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CDashboard::~CDashboard()
{
Destroy();
}
//+------------------------------------------------------------------+
//| Create dashboard |
//+------------------------------------------------------------------+
bool CDashboard::Create(int x_pos, int y_pos, color text_color,
color profit_color, color loss_color)
{
m_x_pos = x_pos;
m_y_pos = y_pos;
m_text_color = text_color;
m_profit_color = profit_color;
m_loss_color = loss_color;
//--- Create background
CreateBackground();
//--- Create initial labels
int y_offset = m_y_pos + 10;
//--- Header
CreateTextLabel(m_prefix + "HEADER", m_x_pos + 10, y_offset,
"RISK MANAGEMENT OVERLAY v6.6", m_header_color, 11, "Arial Bold");
y_offset += 25;
//--- Create sections
if(m_show_account)
{
CreateSection("ACCOUNT STATUS", y_offset);
CreateTextLabel(m_prefix + "ACC_BALANCE", m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_line_height;
CreateTextLabel(m_prefix + "ACC_EQUITY", m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_line_height;
CreateTextLabel(m_prefix + "ACC_MARGIN", m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_line_height;
CreateTextLabel(m_prefix + "ACC_FLOAT", m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_section_gap;
}
if(m_show_performance)
{
CreateSection("PERFORMANCE", y_offset);
CreateTextLabel(m_prefix + "PERF_PROFIT", m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_line_height;
CreateTextLabel(m_prefix + "PERF_TRADES", m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_line_height;
CreateTextLabel(m_prefix + "PERF_WINRATE", m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_line_height;
CreateTextLabel(m_prefix + "PERF_PF", m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_line_height;
CreateTextLabel(m_prefix + "PERF_EXPECT", m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_section_gap;
}
if(m_show_trades)
{
CreateSection("ACTIVE TRADES", y_offset);
for(int i = 0; i < 5; i++) // Show up to 5 trades
{
CreateTextLabel(m_prefix + "TRADE_" + IntegerToString(i),
m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_line_height;
}
y_offset += m_section_gap;
}
if(m_show_risk)
{
CreateSection("RISK METRICS", y_offset);
CreateTextLabel(m_prefix + "RISK_DD", m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_line_height;
CreateTextLabel(m_prefix + "RISK_EXPOSURE", m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_line_height;
CreateTextLabel(m_prefix + "RISK_DAILY", m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_line_height;
CreateTextLabel(m_prefix + "RISK_SHARPE", m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_section_gap;
}
if(m_show_market)
{
CreateSection("MARKET CONDITIONS", y_offset);
CreateTextLabel(m_prefix + "MKT_TREND", m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_line_height;
CreateTextLabel(m_prefix + "MKT_VOL", m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_line_height;
CreateTextLabel(m_prefix + "MKT_SPREAD", m_x_pos + 10, y_offset, "", m_text_color);
y_offset += m_line_height;
}
//--- Update background height
ObjectSetInteger(0, m_bg_name, OBJPROP_YSIZE, y_offset - m_y_pos + 10);
return true;
}
//+------------------------------------------------------------------+
//| Update dashboard |
//+------------------------------------------------------------------+
void CDashboard::Update(const PerformanceMetrics &performance,
const ManagedTrade &trades[],
const MarketConditions &market)
{
//--- Account section
if(m_show_account)
{
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double margin = AccountInfoDouble(ACCOUNT_MARGIN);
double free_margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
double floating = equity - balance;
UpdateTextLabel(m_prefix + "ACC_BALANCE",
"Balance: " + FormatCurrency(balance), m_text_color);
UpdateTextLabel(m_prefix + "ACC_EQUITY",
"Equity: " + FormatCurrency(equity),
GetColorForValue(equity - balance));
UpdateTextLabel(m_prefix + "ACC_MARGIN",
"Margin: " + FormatCurrency(margin) + " / Free: " +
FormatCurrency(free_margin), m_text_color);
UpdateTextLabel(m_prefix + "ACC_FLOAT",
"Floating P/L: " + FormatCurrency(floating),
GetColorForValue(floating));
}
//--- Performance section
if(m_show_performance)
{
UpdateTextLabel(m_prefix + "PERF_PROFIT",
"Net Profit: " + FormatCurrency(performance.net_profit) +
" (" + FormatPercent(performance.net_profit /
AccountInfoDouble(ACCOUNT_BALANCE) * 100) + ")",
GetColorForValue(performance.net_profit));
UpdateTextLabel(m_prefix + "PERF_TRADES",
StringFormat("Trades: %d (W:%d / L:%d)",
performance.total_trades, performance.winning_trades,
performance.losing_trades), m_text_color);
UpdateTextLabel(m_prefix + "PERF_WINRATE",
"Win Rate: " + FormatPercent(performance.win_rate) +
" | Consec: W" + IntegerToString(performance.consecutive_wins) +
" L" + IntegerToString(performance.consecutive_losses),
GetColorForValue(performance.win_rate - 50));
UpdateTextLabel(m_prefix + "PERF_PF",
"Profit Factor: " + DoubleToString(performance.profit_factor, 2) +
" | RRR: " + DoubleToString(performance.risk_reward_ratio, 2),
GetColorForValue(performance.profit_factor - 1));
UpdateTextLabel(m_prefix + "PERF_EXPECT",
"Expectancy: " + FormatCurrency(performance.expectancy) +
" | Sharpe: " + DoubleToString(performance.sharpe_ratio, 2),
GetColorForValue(performance.expectancy));
}
//--- Active trades section
if(m_show_trades)
{
int trade_count = ArraySize(trades);
int display_count = MathMin(trade_count, 5);
for(int i = 0; i < 5; i++)
{
if(i < display_count)
{
string trade_info = StringFormat("#%d %s %.2f | P/L: %s | R: %s",
trades[i].ticket,
(trades[i].type == POSITION_TYPE_BUY) ? "BUY" : "SELL",
trades[i].volume,
FormatCurrency(trades[i].profit),
FormatRMultiple(trades[i].r_multiple));
UpdateTextLabel(m_prefix + "TRADE_" + IntegerToString(i),
trade_info, GetColorForValue(trades[i].profit));
}
else
{
UpdateTextLabel(m_prefix + "TRADE_" + IntegerToString(i), "", m_text_color);
}
}
}
//--- Risk metrics section
if(m_show_risk)
{
double current_risk = 0;
for(int i = 0; i < ArraySize(trades); i++)
{
current_risk += trades[i].risk_percent;
}
UpdateTextLabel(m_prefix + "RISK_DD",
"Max DD: " + FormatPercent(performance.max_drawdown_percent) +
" | Current: " + FormatPercent(performance.current_drawdown),
GetColorForValue(-performance.current_drawdown));
UpdateTextLabel(m_prefix + "RISK_EXPOSURE",
"Risk Exposure: " + FormatPercent(current_risk) +
" | Trades: " + IntegerToString(ArraySize(trades)),
GetColorForValue(10 - current_risk)); // Inverse - less is better
UpdateTextLabel(m_prefix + "RISK_DAILY",
"Daily P/L: " + FormatCurrency(performance.daily_profit) +
" | Trades: " + IntegerToString(performance.daily_trades),
GetColorForValue(performance.daily_profit));
UpdateTextLabel(m_prefix + "RISK_SHARPE",
StringFormat("Sharpe: %.2f | Sortino: %.2f | Calmar: %.2f",
performance.sharpe_ratio, performance.sortino_ratio,
performance.calmar_ratio), m_text_color);
}
//--- Market conditions section
if(m_show_market)
{
string trend_text = "UNKNOWN";
color trend_color = m_text_color;
switch(market.condition)
{
case MARKET_RANGING:
trend_text = "RANGING";
trend_color = clrYellow;
break;
case MARKET_TRENDING_UP:
trend_text = "TRENDING UP";
trend_color = m_profit_color;
break;
case MARKET_TRENDING_DOWN:
trend_text = "TRENDING DOWN";
trend_color = m_loss_color;
break;
case MARKET_VOLATILE:
trend_text = "VOLATILE";
trend_color = clrOrange;
break;
case MARKET_QUIET:
trend_text = "QUIET";
trend_color = clrGray;
break;
}
UpdateTextLabel(m_prefix + "MKT_TREND",
"Market: " + trend_text + " | Strength: " +
DoubleToString(market.trend_strength, 1), trend_color);
UpdateTextLabel(m_prefix + "MKT_VOL",
"Volatility: " + DoubleToString(market.volatility, _Digits) +
" | Momentum: " + DoubleToString(market.momentum, 1),
m_text_color);
UpdateTextLabel(m_prefix + "MKT_SPREAD",
"Spread: " + DoubleToString(market.spread, 0) + " pts" +
(market.news_event ? " | NEWS EVENT" : ""),
market.news_event ? clrRed : m_text_color);
}
}
//+------------------------------------------------------------------+
//| Create background rectangle |
//+------------------------------------------------------------------+
void CDashboard::CreateBackground()
{
ObjectCreate(0, m_bg_name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
ObjectSetInteger(0, m_bg_name, OBJPROP_XDISTANCE, m_x_pos);
ObjectSetInteger(0, m_bg_name, OBJPROP_YDISTANCE, m_y_pos);
ObjectSetInteger(0, m_bg_name, OBJPROP_XSIZE, m_panel_width);
ObjectSetInteger(0, m_bg_name, OBJPROP_YSIZE, 600); // Initial height
ObjectSetInteger(0, m_bg_name, OBJPROP_BGCOLOR, m_bg_color);
ObjectSetInteger(0, m_bg_name, OBJPROP_BORDER_TYPE, BORDER_RAISED);
ObjectSetInteger(0, m_bg_name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, m_bg_name, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, m_bg_name, OBJPROP_WIDTH, 2);
ObjectSetInteger(0, m_bg_name, OBJPROP_BACK, false);
ObjectSetInteger(0, m_bg_name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, m_bg_name, OBJPROP_SELECTED, false);
ObjectSetInteger(0, m_bg_name, OBJPROP_HIDDEN, false);
ObjectSetInteger(0, m_bg_name, OBJPROP_ZORDER, 0);
}
//+------------------------------------------------------------------+
//| Create text label |
//+------------------------------------------------------------------+
void CDashboard::CreateTextLabel(string name, int x, int y, string text,
color clr, int size = 0, string font = "")
{
if(size == 0) size = m_font_size;
if(font == "") font = m_font_name;
ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetString(0, name, OBJPROP_FONT, font);
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, size);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_HIDDEN, false);
ObjectSetInteger(0, name, OBJPROP_ZORDER, 1);
}
//+------------------------------------------------------------------+
//| Update text label |
//+------------------------------------------------------------------+
void CDashboard::UpdateTextLabel(string name, string text, color clr = clrNONE)
{
if(!ObjectFind(0, name) == 0) return;
ObjectSetString(0, name, OBJPROP_TEXT, text);
if(clr != clrNONE)
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
}
//+------------------------------------------------------------------+
//| Create section header |
//+------------------------------------------------------------------+
void CDashboard::CreateSection(string title, int &y_offset)
{
CreateTextLabel(m_prefix + "SEC_" + title, m_x_pos + 10, y_offset,
"--- " + title + " ---", m_header_color, 10, "Arial Bold");
y_offset += m_line_height + 5;
}
//+------------------------------------------------------------------+
//| Format currency value |
//+------------------------------------------------------------------+
string CDashboard::FormatCurrency(double value)
{
string sign = value >= 0 ? "" : "-";
return sign + "$" + DoubleToString(MathAbs(value), 2);
}
//+------------------------------------------------------------------+
//| Format percentage value |
//+------------------------------------------------------------------+
string CDashboard::FormatPercent(double value)
{
return DoubleToString(value, 2) + "%";
}
//+------------------------------------------------------------------+
//| Format R-multiple value |
//+------------------------------------------------------------------+
string CDashboard::FormatRMultiple(double value)
{
string sign = value >= 0 ? "+" : "";
return sign + DoubleToString(value, 2) + "R";
}
//+------------------------------------------------------------------+
//| Get color based on value |
//+------------------------------------------------------------------+
color CDashboard::GetColorForValue(double value, bool inverse = false)
{
if(inverse)
value = -value;
if(value > 0)
return m_profit_color;
else if(value < 0)
return m_loss_color;
else
return m_text_color;
}
//+------------------------------------------------------------------+
//| Destroy dashboard |
//+------------------------------------------------------------------+
void CDashboard::Destroy()
{
//--- Delete all objects with prefix
int total = ObjectsTotal(0);
for(int i = total - 1; i >= 0; i--)
{
string name = ObjectName(0, i);
if(StringFind(name, m_prefix) == 0)
ObjectDelete(0, name);
}
}
//+------------------------------------------------------------------+
//| Show dashboard |
//+------------------------------------------------------------------+
void CDashboard::Show()
{
int total = ObjectsTotal(0);
for(int i = 0; i < total; i++)
{
string name = ObjectName(0, i);
if(StringFind(name, m_prefix) == 0)
ObjectSetInteger(0, name, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);
}
}
//+------------------------------------------------------------------+
//| Hide dashboard |
//+------------------------------------------------------------------+
void CDashboard::Hide()
{
int total = ObjectsTotal(0);
for(int i = 0; i < total; i++)
{
string name = ObjectName(0, i);
if(StringFind(name, m_prefix) == 0)
ObjectSetInteger(0, name, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);
}
}
//+------------------------------------------------------------------+
//| Move dashboard |
//+------------------------------------------------------------------+
void CDashboard::Move(int x, int y)
{
int dx = x - m_x_pos;
int dy = y - m_y_pos;
m_x_pos = x;
m_y_pos = y;
//--- Move all objects
int total = ObjectsTotal(0);
for(int i = 0; i < total; i++)
{
string name = ObjectName(0, i);
if(StringFind(name, m_prefix) == 0)
{
int curr_x = (int)ObjectGetInteger(0, name, OBJPROP_XDISTANCE);
int curr_y = (int)ObjectGetInteger(0, name, OBJPROP_YDISTANCE);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, curr_x + dx);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, curr_y + dy);
}
}
}
//+------------------------------------------------------------------+
//| Set display colors |
//+------------------------------------------------------------------+
void CDashboard::SetColors(color text, color profit, color loss,
color header, color bg)
{
m_text_color = text;
m_profit_color = profit;
m_loss_color = loss;
m_header_color = header;
m_bg_color = bg;
//--- Update background color
ObjectSetInteger(0, m_bg_name, OBJPROP_BGCOLOR, m_bg_color);
}
//+------------------------------------------------------------------+
//| Set font settings |
//+------------------------------------------------------------------+
void CDashboard::SetFont(string font_name, int font_size)
{
m_font_name = font_name;
m_font_size = font_size;
m_line_height = (int)(font_size * 1.8);
}
//+------------------------------------------------------------------+
//| Set which sections to display |
//+------------------------------------------------------------------+
void CDashboard::SetSections(bool account, bool perf, bool trades,
bool risk, bool market)
{
m_show_account = account;
m_show_performance = perf;
m_show_trades = trades;
m_show_risk = risk;
m_show_market = market;
}
#endif // DASHBOARD_MQH
//+------------------------------------------------------------------+