CloseProfit/CloseProfit.mq5

340 lines
12 KiB
MQL5
Raw Permalink Normal View History

//+------------------------------------------------------------------+
//| _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 <Trade\Trade.mqh>
//--- 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;
}
}
//+------------------------------------------------------------------+