403 líneas
16 KiB
MQL5
403 líneas
16 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| PME_Monitor_Dashboard.mq5 |
|
||
|
|
//| Real-time Position Monitoring Dashboard |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#property indicator_chart_window
|
||
|
|
#property indicator_plots 0
|
||
|
|
|
||
|
|
#include "Modules_PME/DataTypes_PME.mqh"
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Input Parameters |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
input int InpUpdateSeconds = 1; // Update Frequency (seconds)
|
||
|
|
input int InpDashboardX = 20; // Dashboard X Position
|
||
|
|
input int InpDashboardY = 100; // Dashboard Y Position
|
||
|
|
input color InpTextColor = clrWhite; // Text Color
|
||
|
|
input color InpHeaderColor = clrGold; // Header Color
|
||
|
|
input color InpWarningColor = clrOrange; // Warning Color
|
||
|
|
input color InpDangerColor = clrRed; // Danger Color
|
||
|
|
input color InpSuccessColor = clrLime; // Success Color
|
||
|
|
input bool InpShowAlerts = true; // Show Alert Warnings
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Global Variables |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
string g_prefix = "PME_MON_";
|
||
|
|
datetime g_last_update = 0;
|
||
|
|
int g_positions_count = 0;
|
||
|
|
double g_total_profit = 0;
|
||
|
|
double g_total_risk = 0;
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Custom indicator initialization function |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
int OnInit()
|
||
|
|
{
|
||
|
|
// Set timer
|
||
|
|
EventSetTimer(InpUpdateSeconds);
|
||
|
|
|
||
|
|
// Create dashboard
|
||
|
|
CreateDashboard();
|
||
|
|
|
||
|
|
return(INIT_SUCCEEDED);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Custom indicator deinitialization function |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void OnDeinit(const int reason)
|
||
|
|
{
|
||
|
|
EventKillTimer();
|
||
|
|
|
||
|
|
// Remove all dashboard objects
|
||
|
|
ObjectsDeleteAll(0, g_prefix);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Timer function |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void OnTimer()
|
||
|
|
{
|
||
|
|
UpdateDashboard();
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 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[])
|
||
|
|
{
|
||
|
|
// Update on new bar
|
||
|
|
if(prev_calculated != rates_total)
|
||
|
|
{
|
||
|
|
UpdateDashboard();
|
||
|
|
}
|
||
|
|
|
||
|
|
return(rates_total);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Create Dashboard |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void CreateDashboard()
|
||
|
|
{
|
||
|
|
int y = InpDashboardY;
|
||
|
|
|
||
|
|
// Background
|
||
|
|
CreateRectangle(g_prefix + "BG", InpDashboardX - 5, y - 5, 300, 450,
|
||
|
|
clrBlack, STYLE_SOLID, 1, 80);
|
||
|
|
|
||
|
|
// Header
|
||
|
|
CreateLabel(g_prefix + "HEADER", InpDashboardX, y,
|
||
|
|
"═══ PME POSITION MONITOR ═══", InpHeaderColor, 10, true);
|
||
|
|
y += 25;
|
||
|
|
|
||
|
|
// Time
|
||
|
|
CreateLabel(g_prefix + "TIME", InpDashboardX, y,
|
||
|
|
"Last Update: --:--:--", InpTextColor, 8);
|
||
|
|
y += 20;
|
||
|
|
|
||
|
|
// Separator
|
||
|
|
CreateLabel(g_prefix + "SEP1", InpDashboardX, y,
|
||
|
|
"────────────────────────", InpTextColor, 8);
|
||
|
|
y += 15;
|
||
|
|
|
||
|
|
// Position Summary
|
||
|
|
CreateLabel(g_prefix + "POS_HEADER", InpDashboardX, y,
|
||
|
|
"POSITION SUMMARY", InpHeaderColor, 9, true);
|
||
|
|
y += 18;
|
||
|
|
|
||
|
|
CreateLabel(g_prefix + "POS_COUNT", InpDashboardX, y,
|
||
|
|
"Active Positions: 0", InpTextColor, 9);
|
||
|
|
y += 15;
|
||
|
|
|
||
|
|
CreateLabel(g_prefix + "TOTAL_PROFIT", InpDashboardX, y,
|
||
|
|
"Total P/L: $0.00", InpTextColor, 9);
|
||
|
|
y += 15;
|
||
|
|
|
||
|
|
CreateLabel(g_prefix + "TOTAL_RISK", InpDashboardX, y,
|
||
|
|
"Total Risk: $0.00", InpTextColor, 9);
|
||
|
|
y += 20;
|
||
|
|
|
||
|
|
// Separator
|
||
|
|
CreateLabel(g_prefix + "SEP2", InpDashboardX, y,
|
||
|
|
"────────────────────────", InpTextColor, 8);
|
||
|
|
y += 15;
|
||
|
|
|
||
|
|
// Position Details
|
||
|
|
CreateLabel(g_prefix + "DETAIL_HEADER", InpDashboardX, y,
|
||
|
|
"POSITION DETAILS", InpHeaderColor, 9, true);
|
||
|
|
y += 18;
|
||
|
|
|
||
|
|
// Create slots for up to 5 positions
|
||
|
|
for(int i = 0; i < 5; i++)
|
||
|
|
{
|
||
|
|
CreateLabel(g_prefix + "POS_" + IntegerToString(i) + "_INFO",
|
||
|
|
InpDashboardX, y + (i * 45), "", InpTextColor, 8);
|
||
|
|
CreateLabel(g_prefix + "POS_" + IntegerToString(i) + "_STATUS",
|
||
|
|
InpDashboardX, y + (i * 45) + 12, "", InpTextColor, 8);
|
||
|
|
CreateLabel(g_prefix + "POS_" + IntegerToString(i) + "_RISK",
|
||
|
|
InpDashboardX, y + (i * 45) + 24, "", InpTextColor, 8);
|
||
|
|
CreateLabel(g_prefix + "POS_" + IntegerToString(i) + "_EXIT",
|
||
|
|
InpDashboardX, y + (i * 45) + 36, "", InpWarningColor, 8);
|
||
|
|
}
|
||
|
|
|
||
|
|
y += 230;
|
||
|
|
|
||
|
|
// Separator
|
||
|
|
CreateLabel(g_prefix + "SEP3", InpDashboardX, y,
|
||
|
|
"────────────────────────", InpTextColor, 8);
|
||
|
|
y += 15;
|
||
|
|
|
||
|
|
// Warnings
|
||
|
|
CreateLabel(g_prefix + "WARNING_HEADER", InpDashboardX, y,
|
||
|
|
"WARNINGS", InpHeaderColor, 9, true);
|
||
|
|
y += 18;
|
||
|
|
|
||
|
|
CreateLabel(g_prefix + "WARNING_1", InpDashboardX, y, "", InpWarningColor, 8);
|
||
|
|
y += 12;
|
||
|
|
CreateLabel(g_prefix + "WARNING_2", InpDashboardX, y + 12, "", InpWarningColor, 8);
|
||
|
|
y += 12;
|
||
|
|
CreateLabel(g_prefix + "WARNING_3", InpDashboardX, y + 24, "", InpDangerColor, 8);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Update Dashboard |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void UpdateDashboard()
|
||
|
|
{
|
||
|
|
// Update time
|
||
|
|
UpdateLabel(g_prefix + "TIME", "Last Update: " + TimeToString(TimeCurrent(), TIME_SECONDS));
|
||
|
|
|
||
|
|
// Get positions
|
||
|
|
int total = PositionsTotal();
|
||
|
|
double total_profit = 0;
|
||
|
|
double total_risk = 0;
|
||
|
|
|
||
|
|
// Clear warnings
|
||
|
|
UpdateLabel(g_prefix + "WARNING_1", "");
|
||
|
|
UpdateLabel(g_prefix + "WARNING_2", "");
|
||
|
|
UpdateLabel(g_prefix + "WARNING_3", "");
|
||
|
|
|
||
|
|
int warning_count = 0;
|
||
|
|
|
||
|
|
// Process positions
|
||
|
|
for(int i = 0; i < MathMin(total, 5); i++)
|
||
|
|
{
|
||
|
|
ulong ticket = PositionGetTicket(i);
|
||
|
|
if(PositionSelectByTicket(ticket))
|
||
|
|
{
|
||
|
|
string symbol = PositionGetString(POSITION_SYMBOL);
|
||
|
|
ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)PositionGetInteger(POSITION_TYPE);
|
||
|
|
double volume = PositionGetDouble(POSITION_VOLUME);
|
||
|
|
double profit = PositionGetDouble(POSITION_PROFIT);
|
||
|
|
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
||
|
|
double current = PositionGetDouble(POSITION_PRICE_CURRENT);
|
||
|
|
double sl = PositionGetDouble(POSITION_SL);
|
||
|
|
double tp = PositionGetDouble(POSITION_TP);
|
||
|
|
datetime open_time = (datetime)PositionGetInteger(POSITION_TIME);
|
||
|
|
|
||
|
|
total_profit += profit;
|
||
|
|
|
||
|
|
// Calculate risk
|
||
|
|
if(sl > 0)
|
||
|
|
{
|
||
|
|
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||
|
|
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
|
||
|
|
double sl_points = MathAbs(current - sl) / point;
|
||
|
|
double risk = sl_points * tick_value * volume;
|
||
|
|
total_risk += risk;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Calculate bars and points
|
||
|
|
int bars = Bars(symbol, PERIOD_CURRENT, open_time, TimeCurrent());
|
||
|
|
double points = 0;
|
||
|
|
if(type == ORDER_TYPE_BUY)
|
||
|
|
points = (current - entry) / SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||
|
|
else
|
||
|
|
points = (entry - current) / SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||
|
|
|
||
|
|
// Update position info
|
||
|
|
string pos_info = StringFormat("#%d %s %s %.2f",
|
||
|
|
ticket, symbol,
|
||
|
|
type == ORDER_TYPE_BUY ? "BUY" : "SELL",
|
||
|
|
volume);
|
||
|
|
|
||
|
|
string status = StringFormat("P/L: %s%.2f (%.0f pts)",
|
||
|
|
profit >= 0 ? "+" : "", profit, points);
|
||
|
|
|
||
|
|
string risk_info = StringFormat("Bars: %d | SL: %s | TP: %s",
|
||
|
|
bars,
|
||
|
|
sl > 0 ? "YES" : "NO",
|
||
|
|
tp > 0 ? "YES" : "NO");
|
||
|
|
|
||
|
|
// Check exit conditions
|
||
|
|
string exit_warning = "";
|
||
|
|
color status_color = InpTextColor;
|
||
|
|
|
||
|
|
// Check for potential exits
|
||
|
|
if(bars < 10)
|
||
|
|
{
|
||
|
|
exit_warning = "⏱ New position (< 10 bars)";
|
||
|
|
status_color = InpSuccessColor;
|
||
|
|
}
|
||
|
|
else if(points >= 30 && points < 50)
|
||
|
|
{
|
||
|
|
exit_warning = "📊 Approaching BE trigger";
|
||
|
|
status_color = InpWarningColor;
|
||
|
|
}
|
||
|
|
else if(points >= 50)
|
||
|
|
{
|
||
|
|
exit_warning = "📈 In trailing zone";
|
||
|
|
status_color = InpSuccessColor;
|
||
|
|
}
|
||
|
|
else if(profit < -50)
|
||
|
|
{
|
||
|
|
exit_warning = "⚠️ Significant loss";
|
||
|
|
status_color = InpDangerColor;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check RSI
|
||
|
|
int rsi_handle = iRSI(symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
|
||
|
|
double rsi_buffer[1];
|
||
|
|
if(CopyBuffer(rsi_handle, 0, 0, 1, rsi_buffer) > 0)
|
||
|
|
{
|
||
|
|
double rsi = rsi_buffer[0];
|
||
|
|
if(type == ORDER_TYPE_BUY && rsi > 75)
|
||
|
|
{
|
||
|
|
exit_warning += StringFormat(" RSI:%.0f", rsi);
|
||
|
|
status_color = InpWarningColor;
|
||
|
|
}
|
||
|
|
else if(type == ORDER_TYPE_SELL && rsi < 25)
|
||
|
|
{
|
||
|
|
exit_warning += StringFormat(" RSI:%.0f", rsi);
|
||
|
|
status_color = InpWarningColor;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
IndicatorRelease(rsi_handle);
|
||
|
|
|
||
|
|
// Update labels
|
||
|
|
UpdateLabel(g_prefix + "POS_" + IntegerToString(i) + "_INFO", pos_info);
|
||
|
|
UpdateLabel(g_prefix + "POS_" + IntegerToString(i) + "_STATUS", status,
|
||
|
|
profit >= 0 ? InpSuccessColor : InpDangerColor);
|
||
|
|
UpdateLabel(g_prefix + "POS_" + IntegerToString(i) + "_RISK", risk_info);
|
||
|
|
UpdateLabel(g_prefix + "POS_" + IntegerToString(i) + "_EXIT", exit_warning, status_color);
|
||
|
|
|
||
|
|
// Add warnings
|
||
|
|
if(sl == 0 && warning_count < 3)
|
||
|
|
{
|
||
|
|
UpdateLabel(g_prefix + "WARNING_" + IntegerToString(warning_count + 1),
|
||
|
|
StringFormat("⚠️ #%d has no stop loss!", ticket));
|
||
|
|
warning_count++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Clear unused position slots
|
||
|
|
for(int i = total; i < 5; i++)
|
||
|
|
{
|
||
|
|
UpdateLabel(g_prefix + "POS_" + IntegerToString(i) + "_INFO", "");
|
||
|
|
UpdateLabel(g_prefix + "POS_" + IntegerToString(i) + "_STATUS", "");
|
||
|
|
UpdateLabel(g_prefix + "POS_" + IntegerToString(i) + "_RISK", "");
|
||
|
|
UpdateLabel(g_prefix + "POS_" + IntegerToString(i) + "_EXIT", "");
|
||
|
|
}
|
||
|
|
|
||
|
|
// Update summary
|
||
|
|
UpdateLabel(g_prefix + "POS_COUNT", StringFormat("Active Positions: %d", total));
|
||
|
|
UpdateLabel(g_prefix + "TOTAL_PROFIT", StringFormat("Total P/L: %s%.2f",
|
||
|
|
total_profit >= 0 ? "$" : "-$",
|
||
|
|
MathAbs(total_profit)),
|
||
|
|
total_profit >= 0 ? InpSuccessColor : InpDangerColor);
|
||
|
|
UpdateLabel(g_prefix + "TOTAL_RISK", StringFormat("Total Risk: $%.2f", total_risk));
|
||
|
|
|
||
|
|
// Check for global warnings
|
||
|
|
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
||
|
|
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
|
||
|
|
double daily_loss = ((balance - equity) / balance) * 100;
|
||
|
|
|
||
|
|
if(daily_loss > 5 && warning_count < 3)
|
||
|
|
{
|
||
|
|
UpdateLabel(g_prefix + "WARNING_" + IntegerToString(warning_count + 1),
|
||
|
|
StringFormat("⚠️ Daily loss: %.1f%%", daily_loss), InpDangerColor);
|
||
|
|
warning_count++;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Alert on significant changes
|
||
|
|
if(InpShowAlerts)
|
||
|
|
{
|
||
|
|
static int last_count = 0;
|
||
|
|
if(total < last_count && last_count > 0)
|
||
|
|
{
|
||
|
|
Alert("PME Monitor: Position closed!");
|
||
|
|
}
|
||
|
|
last_count = total;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Create Label |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void CreateLabel(string name, int x, int y, string text, color clr,
|
||
|
|
int size, bool bold = false)
|
||
|
|
{
|
||
|
|
ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
||
|
|
ObjectSetString(0, name, OBJPROP_TEXT, text);
|
||
|
|
ObjectSetString(0, name, OBJPROP_FONT, bold ? "Arial Bold" : "Arial");
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, size);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Update Label |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void UpdateLabel(string name, string text, color clr = clrNONE)
|
||
|
|
{
|
||
|
|
if(ObjectFind(0, name) >= 0)
|
||
|
|
{
|
||
|
|
ObjectSetString(0, name, OBJPROP_TEXT, text);
|
||
|
|
if(clr != clrNONE)
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Create Rectangle |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void CreateRectangle(string name, int x, int y, int width, int height,
|
||
|
|
color clr, ENUM_LINE_STYLE style, int line_width, int transparency = 255)
|
||
|
|
{
|
||
|
|
ObjectCreate(0, name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_XSIZE, width);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_YSIZE, height);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_BGCOLOR, clr);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_BORDER_TYPE, BORDER_FLAT);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_WIDTH, line_width);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_STYLE, style);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_BACK, true);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
|
||
|
|
|
||
|
|
// Set transparency (0-255, where 255 is opaque)
|
||
|
|
long color_value = (long)clr;
|
||
|
|
long alpha = (255 - transparency) << 24;
|
||
|
|
color_value = (color_value & 0x00FFFFFF) | alpha;
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_BGCOLOR, (color)color_value);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|