//+------------------------------------------------------------------+ //| _Close_at_PRofit.mq5 | //| Copyright © 2025 sunokfx | //| https://www.mql5.com/en/users/sunokfx | //+------------------------------------------------------------------+ #property copyright "Copyright © 2025 sunokfx" #property link "https://www.mql5.com/en/users/sunokfx" #property version "1.10" #property strict #include //--- Input parameters input bool useProfitToClose = true; // Close at profit input double profitToClose = 200; // Profit threshold ($) input bool useLossToClose = false; // Close at loss input double lossToClose = 100; // Loss threshold ($) input bool AllSymbols = false; // All symbols input bool PendingOrders = true; // Include pending orders input double MaxSlippage = 3; // Max slippage (pips) input bool showMenu = true; // Show info panel input color menuColor = clrLightSteelBlue; // Header color input color valueColor = clrTeal; // Value color (neutral) input color profitColor = clrLimeGreen; // Value color (profit) input color lossColor = clrTomato; // Value color (loss) input int fontSize = 10; // Font size sinput int Magic = -1; // Magic number (-1 = all) input int PanelX = 10; // Panel X offset (px) input int PanelY = 20; // Panel Y offset (px) input ENUM_BASE_CORNER PanelCorner = CORNER_LEFT_UPPER; // Panel corner //--- Panel constants #define PANEL_PREFIX "CAP_" #define ROW_H 18 // Row height (px) #define COL_LABEL_X 0 // Label column X #define COL_VALUE_X 185 // Value column X (aligned) //--- Panel object names #define OBJ_TITLE PANEL_PREFIX"title" #define OBJ_SEP1 PANEL_PREFIX"sep1" #define OBJ_LBL_TRADES PANEL_PREFIX"lbl_trades" #define OBJ_VAL_TRADES PANEL_PREFIX"val_trades" #define OBJ_LBL_LOTS PANEL_PREFIX"lbl_lots" #define OBJ_VAL_LOTS PANEL_PREFIX"val_lots" #define OBJ_LBL_PROFIT PANEL_PREFIX"lbl_profit" #define OBJ_VAL_PROFIT PANEL_PREFIX"val_profit" #define OBJ_SEP2 PANEL_PREFIX"sep2" #define OBJ_LBL_TARGET PANEL_PREFIX"lbl_target" #define OBJ_VAL_TARGET PANEL_PREFIX"val_target" #define OBJ_LBL_STOP PANEL_PREFIX"lbl_stop" #define OBJ_VAL_STOP PANEL_PREFIX"val_stop" string g_panelObjects[] = { OBJ_TITLE, OBJ_SEP1, OBJ_LBL_TRADES, OBJ_VAL_TRADES, OBJ_LBL_LOTS, OBJ_VAL_LOTS, OBJ_LBL_PROFIT, OBJ_VAL_PROFIT, OBJ_SEP2, OBJ_LBL_TARGET, OBJ_VAL_TARGET, OBJ_LBL_STOP, OBJ_VAL_STOP }; //--- Working variables double g_profit = 0; bool g_clear = true; CTrade g_trade; //+------------------------------------------------------------------+ //| Helper: create or update an OBJ_LABEL object | //+------------------------------------------------------------------+ void SetLabel(const string name, const string text, const int x, const int y, const color clr, const int fsize = 0, const bool bold = false) { if(ObjectFind(0, name) < 0) { ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, name, OBJPROP_CORNER, PanelCorner); ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, name, OBJPROP_SELECTED, false); ObjectSetInteger(0, name, OBJPROP_HIDDEN, true); ObjectSetInteger(0, name, OBJPROP_BACK, false); ObjectSetInteger(0, name, OBJPROP_ZORDER, 10); } int fs = (fsize > 0) ? fsize : fontSize; ObjectSetString (0, name, OBJPROP_TEXT, text); ObjectSetString (0, name, OBJPROP_FONT, bold ? "Arial Bold" : "Arial"); ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fs); ObjectSetInteger(0, name, OBJPROP_COLOR, clr); ObjectSetInteger(0, name, OBJPROP_XDISTANCE, PanelX + x); ObjectSetInteger(0, name, OBJPROP_YDISTANCE, PanelY + y); } //+------------------------------------------------------------------+ //| Draw static panel skeleton | //+------------------------------------------------------------------+ void DrawPanel() { int row = 0; // Title SetLabel(OBJ_TITLE, "Close at Profit / Loss", COL_LABEL_X, row * ROW_H, menuColor, fontSize + 1, true); row++; // Separator 1 SetLabel(OBJ_SEP1, "--------------------------------", COL_LABEL_X, row * ROW_H, clrDimGray, fontSize - 2); row++; // Row: Positions SetLabel(OBJ_LBL_TRADES, "Positions (S / B):", COL_LABEL_X, row * ROW_H, menuColor); SetLabel(OBJ_VAL_TRADES, "0 / 0", COL_VALUE_X, row * ROW_H, valueColor); row++; // Row: Lots SetLabel(OBJ_LBL_LOTS, "Lots (S / B):", COL_LABEL_X, row * ROW_H, menuColor); SetLabel(OBJ_VAL_LOTS, "0.00 / 0.00", COL_VALUE_X, row * ROW_H, valueColor); row++; // Row: Profit SetLabel(OBJ_LBL_PROFIT, "Profit (S / B):", COL_LABEL_X, row * ROW_H, menuColor); SetLabel(OBJ_VAL_PROFIT, "0.00 / 0.00", COL_VALUE_X, row * ROW_H, valueColor); row++; // Separator 2 SetLabel(OBJ_SEP2, "--------------------------------", COL_LABEL_X, row * ROW_H, clrDimGray, fontSize - 2); row++; // Row: Take Profit threshold string tpText = useProfitToClose ? DoubleToString(profitToClose, 2) + " $" : "disabled"; color tpClr = useProfitToClose ? profitColor : clrGray; SetLabel(OBJ_LBL_TARGET, "Take Profit:", COL_LABEL_X, row * ROW_H, menuColor); SetLabel(OBJ_VAL_TARGET, tpText, COL_VALUE_X, row * ROW_H, tpClr); row++; // Row: Stop Loss threshold string slText = useLossToClose ? DoubleToString(lossToClose, 2) + " $" : "disabled"; color slClr = useLossToClose ? lossColor : clrGray; SetLabel(OBJ_LBL_STOP, "Stop Loss:", COL_LABEL_X, row * ROW_H, menuColor); SetLabel(OBJ_VAL_STOP, slText, COL_VALUE_X, row * ROW_H, slClr); } //+------------------------------------------------------------------+ //| Refresh dynamic panel values only | //+------------------------------------------------------------------+ void UpdatePanel() { int totalS = 0, totalB = 0; double lotsS = 0, lotsB = 0; double profS = 0, profB = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(Magic != -1 && (int)PositionGetInteger(POSITION_MAGIC) != Magic) continue; if(!AllSymbols && PositionGetString(POSITION_SYMBOL) != Symbol()) continue; double vol = PositionGetDouble(POSITION_VOLUME); double prof = PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP); if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { totalB++; lotsB += vol; profB += prof; } else { totalS++; lotsS += vol; profS += prof; } } // Positions count ObjectSetString(0, OBJ_VAL_TRADES, OBJPROP_TEXT, IntegerToString(totalS) + " / " + IntegerToString(totalB)); // Lots ObjectSetString(0, OBJ_VAL_LOTS, OBJPROP_TEXT, DoubleToString(lotsS, 2) + " / " + DoubleToString(lotsB, 2)); // Profit with dynamic color coding double totalProfit = profS + profB; color profClr = (totalProfit > 0.0) ? profitColor : (totalProfit < 0.0) ? lossColor : valueColor; ObjectSetString (0, OBJ_VAL_PROFIT, OBJPROP_TEXT, DoubleToString(profS, 2) + " / " + DoubleToString(profB, 2)); ObjectSetInteger(0, OBJ_VAL_PROFIT, OBJPROP_COLOR, profClr); ChartRedraw(0); } //+------------------------------------------------------------------+ //| Delete all panel objects | //+------------------------------------------------------------------+ void DeletePanel() { int n = ArraySize(g_panelObjects); for(int i = 0; i < n; i++) ObjectDelete(0, g_panelObjects[i]); } //+------------------------------------------------------------------+ //| Close positions and optionally delete pending orders | //+------------------------------------------------------------------+ bool CloseAll(const bool currentSymbolOnly, const bool includePending) { // Close open positions for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(Magic != -1 && (int)PositionGetInteger(POSITION_MAGIC) != Magic) continue; if(currentSymbolOnly && PositionGetString(POSITION_SYMBOL) != Symbol()) continue; if(!g_trade.PositionClose(ticket)) { Print("CloseAll: failed to close position #", ticket, " err=", GetLastError()); return false; } } // Delete pending orders if(includePending) { for(int i = OrdersTotal() - 1; i >= 0; i--) { ulong ticket = OrderGetTicket(i); if(ticket == 0) continue; if(Magic != -1 && (int)OrderGetInteger(ORDER_MAGIC) != Magic) continue; if(currentSymbolOnly && OrderGetString(ORDER_SYMBOL) != Symbol()) continue; if(!g_trade.OrderDelete(ticket)) { Print("CloseAll: failed to delete order #", ticket, " err=", GetLastError()); return false; } } } return true; } //+------------------------------------------------------------------+ //| Calculate total floating P&L of matching positions | //+------------------------------------------------------------------+ double ProfitCheck() { double total = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(Magic != -1 && (int)PositionGetInteger(POSITION_MAGIC) != Magic) continue; if(!AllSymbols && PositionGetString(POSITION_SYMBOL) != Symbol()) continue; total += PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP); } return total; } //+------------------------------------------------------------------+ //| OnInit | //+------------------------------------------------------------------+ int OnInit() { g_trade.SetDeviationInPoints((ulong)(MaxSlippage * 10)); if(Magic != -1) g_trade.SetExpertMagicNumber(Magic); g_clear = true; if(showMenu) { DrawPanel(); UpdatePanel(); } return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| OnDeinit | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { DeletePanel(); } //+------------------------------------------------------------------+ //| OnTick | //+------------------------------------------------------------------+ void OnTick() { if(showMenu) UpdatePanel(); // Retry if previous close attempt failed if(!g_clear) { if(CloseAll(!AllSymbols, PendingOrders)) g_clear = true; else return; } g_profit = ProfitCheck(); // Take Profit trigger if(useProfitToClose && g_profit > profitToClose) { if(!CloseAll(!AllSymbols, PendingOrders)) g_clear = false; } // Stop Loss trigger if(useLossToClose && g_profit < -lossToClose) { if(!CloseAll(!AllSymbols, PendingOrders)) g_clear = false; } } //+------------------------------------------------------------------+