fix: Partial close (once per position), Move SL to breakeven

This commit is contained in:
Naji El Chemaly
2026-06-09 14:34:33 +03:00
parent 80b1ce8743
commit 16df67c6fc
+58 -7
View File
@@ -5,7 +5,7 @@
//| Based on: ORB-All-Sessions.pine + LuxAlgo Order Block Detector |
//+------------------------------------------------------------------+
#property copyright "NANDR"
#property version "1.34"
#property version "1.35"
#property strict
#include <Trade\Trade.mqh>
@@ -115,6 +115,7 @@ input double InpRRRatio = 1.0; // Risk:Reward Ratio
input group "═══ Trade Management ═══"
input double InpBreakevenTrigPips = 50.0; // Breakeven Trigger (pips)
input double InpBreakevenOffPips = 2.0; // Breakeven Offset (pips)
input double InpPartialCloseRatio = 0.5; // Partial Close Ratio at BE (0=disabled, 0.5=half)
input double InpTrailingStopPips = 0.0; // Trailing Stop Pips (0=off)
input int InpMaxTradesPerDay = 8; // Max Trades per Day
input int InpMaxPosPerSession = 2; // Max Positions per Session
@@ -217,6 +218,27 @@ int g_BearOBCount = 0;
// contradict this bias are suppressed, preventing counter-trend trades.
int g_GlobalBiasDir = 0;
// Tracks tickets that have already had partial close executed so it only fires once per position
#define MAX_PARTIAL_TRACKED 20
ulong g_PartialClosedTickets[MAX_PARTIAL_TRACKED];
int g_PartialClosedCount = 0;
bool IsPartialClosed(ulong ticket)
{
for(int i = 0; i < g_PartialClosedCount; i++)
if(g_PartialClosedTickets[i] == ticket) return true;
return false;
}
void MarkPartialClosed(ulong ticket)
{
if(g_PartialClosedCount < MAX_PARTIAL_TRACKED)
{
g_PartialClosedTickets[g_PartialClosedCount] = ticket;
g_PartialClosedCount++;
}
}
// Daily stats
datetime g_LastDayReset = 0;
int g_TodayTrades = 0;
@@ -1257,17 +1279,45 @@ void ManageOpenTrades()
ulong ticket = g_Position.Ticket();
int posDir = (g_Position.PositionType() == POSITION_TYPE_BUY) ? 1 : -1;
// Breakeven
// Breakeven + Partial Close
if(InpBreakevenTrigPips > 0)
{
double trigDist = PipsToPrice(InpBreakevenTrigPips);
double beDist = PipsToPrice(InpBreakevenOffPips);
double beLevel = (posDir > 0) ? entry + beDist : entry - beDist;
bool triggered = (posDir > 0)
? (price >= entry + trigDist && sl < beLevel)
: (price <= entry - trigDist && (sl > beLevel || sl == 0));
if(posDir > 0 && price >= entry + trigDist && sl < beLevel)
currentSL = NormalizeDouble(beLevel, (int)SymbolInfoInteger(g_Symbol, SYMBOL_DIGITS));
else if(posDir < 0 && price <= entry - trigDist && (sl > beLevel || sl == 0))
if(triggered)
{
// Step 1: partial close — fire once per position
if(InpPartialCloseRatio > 0 && !IsPartialClosed(ticket))
{
double fullLots = g_Position.Volume();
double closeLots = NormalizeLot(fullLots * InpPartialCloseRatio);
double minLot = SymbolInfoDouble(g_Symbol, SYMBOL_VOLUME_MIN);
if(closeLots >= minLot)
{
bool partResult = (posDir > 0)
? g_Trade.Sell(closeLots, g_Symbol, 0, 0, 0,
StringFormat("NANDR|PartialClose|%.0fpips", InpBreakevenTrigPips))
: g_Trade.Buy(closeLots, g_Symbol, 0, 0, 0,
StringFormat("NANDR|PartialClose|%.0fpips", InpBreakevenTrigPips));
if(partResult)
{
MarkPartialClosed(ticket);
PrintFormat("NANDR EA: Partial close %.2f lots at %.2f (%.0f pips profit). Ticket=%llu",
closeLots, price, InpBreakevenTrigPips, ticket);
}
else
PrintFormat("NANDR EA: Partial close failed. Error=%d Ticket=%llu",
GetLastError(), ticket);
}
}
// Step 2: move SL to breakeven
currentSL = NormalizeDouble(beLevel, (int)SymbolInfoInteger(g_Symbol, SYMBOL_DIGITS));
}
}
// Trailing stop
@@ -1310,8 +1360,9 @@ void CheckDailyReset()
g_TodayLosses = 0;
g_TodayPnL = 0;
g_DayStartEquity = AccountInfoDouble(ACCOUNT_EQUITY);
g_TradingHalted = false;
g_GlobalBiasDir = 0; // clear day bias — first session breakout will re-establish it
g_TradingHalted = false;
g_GlobalBiasDir = 0; // clear day bias — first session breakout will re-establish it
g_PartialClosedCount = 0; // clear partial close tracker for the new day
// Reset all session ORB data for the new day
for(int s = 0; s < g_SessionCount; s++)