Partial exits on M5 gold scalps without turning winners into scratches
Partial exits on M5 gold scalps without turning winners into scratches
Partial exits on M5 gold are where many scalpers quietly turn a clean winner into a scratch — or worse, a late loser. I still scale out. I just refuse to do it by feel mid-candle.
The problem is not taking profit. The problem is random first targets that leave the remainder oversized into noise, then moving the stop to entry before the structure that justified the trade has done anything. That is how +1.5R ideas become +0.2R with stress.
My fixed partial framework for XAUUSD M5:
1. Entry only with a pre-written invalidation. Stop is beyond that micro structure, sized so full risk is known before the first click.
2. First partial is mechanical: 40–50% at +1R measured from entry to stop. No “it looks extended.” R is the unit.
3. After partial one, stop moves to breakeven only if M5 has printed a clear higher low (long) or lower high (short) that protects the idea — not because I am nervous.
4. Second partial (optional): another 25–30% at a pre-marked M15 level or liquidity pool. Remainder trails behind structure, not behind an arbitrary tick trail.
5. If price never reaches +1R and invalidates, full stop. I do not save” the trade by cutting early into noise unless a hard session rule forces flat (news, daily stop).
What I refuse:
- Scaling out 80% at +0.4R because the candle paused. That is fear dressed as management.
- Leaving full size to “let it run with no partial plan when my edge is mean-reversion-ish reclaim, not trend riding.
- Moving stop to entry immediately after partial one on a still-developing impulse. You convert a valid stop into a free scratch lottery.
A practical example: long gold after a London sweep-and-reclaim. Stop under the sweep low. At +1R I take half. I do not move stop to entry until M5 holds a higher low above the reclaim. If gold stalls and revisits the reclaim zone, the remainder can still be wrong — that is allowed. Partial one paid for process; remainder is still risked on structure.
Journal tag I use: partial_plan_followed (Y/N). Wins that ignore the plan score lower on process than losses that followed it. Over a month, expectancy improves when partials are boring and repeatable.
If your gold winners keep dying as scratches, write the partial percentages and the breakeven rule before the session. Execute the plan. Do not renegotiate R while the candle is open.
The problem is not taking profit. The problem is random first targets that leave the remainder oversized into noise, then moving the stop to entry before the structure that justified the trade has done anything. That is how +1.5R ideas become +0.2R with stress.
My fixed partial framework for XAUUSD M5:
1. Entry only with a pre-written invalidation. Stop is beyond that micro structure, sized so full risk is known before the first click.
2. First partial is mechanical: 40–50% at +1R measured from entry to stop. No “it looks extended.” R is the unit.
3. After partial one, stop moves to breakeven only if M5 has printed a clear higher low (long) or lower high (short) that protects the idea — not because I am nervous.
4. Second partial (optional): another 25–30% at a pre-marked M15 level or liquidity pool. Remainder trails behind structure, not behind an arbitrary tick trail.
5. If price never reaches +1R and invalidates, full stop. I do not save” the trade by cutting early into noise unless a hard session rule forces flat (news, daily stop).
What I refuse:
- Scaling out 80% at +0.4R because the candle paused. That is fear dressed as management.
- Leaving full size to “let it run with no partial plan when my edge is mean-reversion-ish reclaim, not trend riding.
- Moving stop to entry immediately after partial one on a still-developing impulse. You convert a valid stop into a free scratch lottery.
A practical example: long gold after a London sweep-and-reclaim. Stop under the sweep low. At +1R I take half. I do not move stop to entry until M5 holds a higher low above the reclaim. If gold stalls and revisits the reclaim zone, the remainder can still be wrong — that is allowed. Partial one paid for process; remainder is still risked on structure.
Journal tag I use: partial_plan_followed (Y/N). Wins that ignore the plan score lower on process than losses that followed it. Over a month, expectancy improves when partials are boring and repeatable.
If your gold winners keep dying as scratches, write the partial percentages and the breakeven rule before the session. Execute the plan. Do not renegotiate R while the candle is open.
- Attachments
-
- 24-partial-exits-plain.png (27.59 KiB) Viewed 6 times
It’s Fairman 
Re: Partial exits on M5 gold scalps without turning winners into scratches
Hi Fairman,Fairman wrote: Sat Sep 05, 2026 4:28 pm Partial exits on M5 gold are where many scalpers quietly turn a clean winner into a scratch — or worse, a late loser. I still scale out. I just refuse to do it by feel mid-candle.
The problem is not taking profit. The problem is random first targets that leave the remainder oversized into noise, then moving the stop to entry before the structure that justified the trade has done anything. That is how +1.5R ideas become +0.2R with stress.
My fixed partial framework for XAUUSD M5:
1. Entry only with a pre-written invalidation. Stop is beyond that micro structure, sized so full risk is known before the first click.
2. First partial is mechanical: 40–50% at +1R measured from entry to stop. No “it looks extended.” R is the unit.
3. After partial one, stop moves to breakeven only if M5 has printed a clear higher low (long) or lower high (short) that protects the idea — not because I am nervous.
4. Second partial (optional): another 25–30% at a pre-marked M15 level or liquidity pool. Remainder trails behind structure, not behind an arbitrary tick trail.
5. If price never reaches +1R and invalidates, full stop. I do not save” the trade by cutting early into noise unless a hard session rule forces flat (news, daily stop).
What I refuse:
- Scaling out 80% at +0.4R because the candle paused. That is fear dressed as management.
- Leaving full size to “let it run with no partial plan when my edge is mean-reversion-ish reclaim, not trend riding.
- Moving stop to entry immediately after partial one on a still-developing impulse. You convert a valid stop into a free scratch lottery.
A practical example: long gold after a London sweep-and-reclaim. Stop under the sweep low. At +1R I take half. I do not move stop to entry until M5 holds a higher low above the reclaim. If gold stalls and revisits the reclaim zone, the remainder can still be wrong — that is allowed. Partial one paid for process; remainder is still risked on structure.
Journal tag I use: partial_plan_followed (Y/N). Wins that ignore the plan score lower on process than losses that followed it. Over a month, expectancy improves when partials are boring and repeatable.
If your gold winners keep dying as scratches, write the partial percentages and the breakeven rule before the session. Execute the plan. Do not renegotiate R while the candle is open.
"Fear dressed as management" is exactly what happens when traders chop a winner at +0.4R because they are staring at a 1-minute candle pause instead of their actual framework. Your point about not moving the stop to breakeven immediately after partial one is also critical. Moving to breakeven prematurely turns a carefully calculated structural stop into a free scratch lottery ticket, usually resulting in getting wicked out right before the real expansion.
Since you need this executed mechanically without mid-candle hesitation, I have written the MQL4 code for you below.
Note on MT4 mechanics: I built this as a lightweight Expert Advisor (EA) rather than a Script. A Script only executes once and stops; to automatically catch that exact +1R target mid-candle, you need an EA looping on every tick to monitor the price.
The M5 Fixed Partial Manager (MQL4)
This EA runs quietly on your XAUUSD chart. It monitors any manually opened trades. Once a trade has a Stop Loss, it calculates the exact 1R distance, waits for price to hit +1R, and automatically closes your specified percentage (default 50%). It leaves your Stop Loss exactly where it is so you can manage the remainder structurally.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Partial exits on M5 gold scalps without turning winners into scratches
Code: Select all
//+------------------------------------------------------------------+
//| XAU_FixedPartial_Manager.mq4 |
//| For XAUUSD M5 Scalers |
//+------------------------------------------------------------------+
#property strict
//--- Inputs
input double PartialPercent = 50.0; // Percentage of position to close at Target 1
input double R_Multiplier = 1.0; // R-Multiple for First Partial (e.g., 1.0 = +1R)
input int Slippage = 3; // Allowed slippage in points
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Loop through all open orders
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
// Only manage trades for the current chart symbol (XAUUSD)
if(OrderSymbol() == Symbol())
{
double openPrice = OrderOpenPrice();
double stopLoss = OrderStopLoss();
// 1. Must have a Stop Loss to calculate R
if(stopLoss == 0) continue;
// 2. Prevent infinite partial loops.
// When MT4 partially closes a trade, it creates a new ticket but keeps the original OpenTime and OpenPrice.
// We use a Global Variable based on OpenTime to ensure we only partial this trade structure once.
string gvName = "PartialTaken_" + IntegerToString(OrderOpenTime());
if(GlobalVariableCheck(gvName)) continue;
// 3. Calculate 1R Distance
double riskDistance = MathAbs(openPrice - stopLoss);
if(riskDistance == 0) continue;
double r_TargetDist = riskDistance * R_Multiplier;
// 4. Calculate Lot Size to Close
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
double lotsToClose = OrderLots() * (PartialPercent / 100.0);
// Round to nearest broker lot step
lotsToClose = MathFloor(lotsToClose / lotStep) * lotStep;
if(lotsToClose < minLot) continue; // Position too small to partial
// 5. Check targets and execute
if(OrderType() == OP_BUY)
{
double targetPrice = openPrice + r_TargetDist;
if(Bid >= targetPrice)
{
bool success = OrderClose(OrderTicket(), lotsToClose, Bid, Slippage, clrGreen);
if(success)
{
GlobalVariableSet(gvName, 1); // Mark as partialed
Print("Mechanical Partial Executed: ", lotsToClose, " lots closed at +1R.");
}
}
}
else if(OrderType() == OP_SELL)
{
double targetPrice = openPrice - r_TargetDist;
if(Ask <= targetPrice)
{
bool success = OrderClose(OrderTicket(), lotsToClose, Ask, Slippage, clrRed);
if(success)
{
GlobalVariableSet(gvName, 1); // Mark as partialed
Print("Mechanical Partial Executed: ", lotsToClose, " lots closed at +1R.");
}
}
}
}
}
}
}Compile: Open MetaEditor in MT4, create a new Expert Advisor, paste this code, and click Compile.
Global Variables: The script uses MT4's GlobalVariables to tag a trade the millisecond it takes a partial. Because MT4 creates a new ticket for the remaining 50%, this prevents the EA from accidentally taking another 50% of the remainder if price hovers around the 1R line.
Lot Math: If you open 0.50 lots, it will close 0.25 at exactly 1R. If you open 0.15 lots, MT4 math will round down to your broker's lot step (closing 0.07), leaving 0.08 to trail.
Execution: It ignores trades without a Stop Loss, because as you noted, invalidation must be pre-written before the first click to know what 1R actually is.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Partial exits on M5 gold scalps without turning winners into scratches
A standard fractal requires five candles to complete: a high/low surrounded by two lower highs/higher lows on each side. This means the EA will wait for a structure to be fully confirmed (two candles after the pivot) before moving your stop. It guarantees the structure is locked in, entirely removing the urge to trail aggressively during a live, unconfirmed candle.
Here is the updated EA. It now includes a trailing mechanism that only activates after your mechanical +1R partial has been secured.
The Structural Fractal Trailing Manager (MQL4)
Here is the updated EA. It now includes a trailing mechanism that only activates after your mechanical +1R partial has been secured.
The Structural Fractal Trailing Manager (MQL4)
Code: Select all
//+------------------------------------------------------------------+
//| XAU_FixedPartial_Manager.mq4 |
//| With Structural Fractal Trailing |
//+------------------------------------------------------------------+
#property strict
//--- Partial Settings
input double PartialPercent = 50.0; // Percentage of position to close at Target 1
input double R_Multiplier = 1.0; // R-Multiple for First Partial (e.g., 1.0 = +1R)
input int Slippage = 3; // Allowed slippage in points
//--- Trailing Settings
input bool EnableFractalTrail = true; // Trail remainder behind fractals?
input ENUM_TIMEFRAMES TrailTF = PERIOD_M5; // Timeframe for structural fractals (M5, M15)
input int FractalPadding = 15; // Points padding behind the fractal to avoid wicks
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol())
{
double openPrice = OrderOpenPrice();
double stopLoss = OrderStopLoss();
int ticket = OrderTicket();
if(stopLoss == 0) continue;
string gvName = "PartialTaken_" + IntegerToString(OrderOpenTime());
bool partialTaken = GlobalVariableCheck(gvName);
// ==========================================
// 1. MECHANICAL PARTIAL LOGIC
// ==========================================
if(!partialTaken)
{
double riskDistance = MathAbs(openPrice - stopLoss);
if(riskDistance == 0) continue;
double r_TargetDist = riskDistance * R_Multiplier;
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
double lotsToClose = OrderLots() * (PartialPercent / 100.0);
lotsToClose = MathFloor(lotsToClose / lotStep) * lotStep;
if(lotsToClose >= minLot)
{
if(OrderType() == OP_BUY && Bid >= (openPrice + r_TargetDist))
{
if(OrderClose(ticket, lotsToClose, Bid, Slippage, clrGreen))
{
GlobalVariableSet(gvName, 1);
Print("Mechanical Partial: ", lotsToClose, " lots closed at +1R.");
continue; // Skip trailing until the next tick
}
}
else if(OrderType() == OP_SELL && Ask <= (openPrice - r_TargetDist))
{
if(OrderClose(ticket, lotsToClose, Ask, Slippage, clrRed))
{
GlobalVariableSet(gvName, 1);
Print("Mechanical Partial: ", lotsToClose, " lots closed at +1R.");
continue; // Skip trailing until the next tick
}
}
}
}
// ==========================================
// 2. FRACTAL TRAILING LOGIC (REMAINDER ONLY)
// ==========================================
if(partialTaken && EnableFractalTrail)
{
double point = MarketInfo(Symbol(), MODE_POINT);
if(OrderType() == OP_BUY)
{
double lastFractalLow = GetLastFractalLow(TrailTF);
if(lastFractalLow > 0)
{
double newSL = NormalizeDouble(lastFractalLow - (FractalPadding * point), Digits);
// Only move stop UP, and ensure it's below current price
if(newSL > stopLoss && newSL < Bid)
{
bool mod = OrderModify(ticket, openPrice, newSL, OrderTakeProfit(), 0, clrBlue);
if(mod) Print("Structural Trail: Buy SL moved up behind higher low to ", newSL);
}
}
}
else if(OrderType() == OP_SELL)
{
double lastFractalHigh = GetLastFractalHigh(TrailTF);
if(lastFractalHigh > 0)
{
double newSL = NormalizeDouble(lastFractalHigh + (FractalPadding * point), Digits);
// Only move stop DOWN, and ensure it's above current price
if(newSL < stopLoss && newSL > Ask)
{
bool mod = OrderModify(ticket, openPrice, newSL, OrderTakeProfit(), 0, clrMagenta);
if(mod) Print("Structural Trail: Sell SL moved down behind lower high to ", newSL);
}
}
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Helper: Find most recent confirmed Down Fractal (Higher Low) |
//+------------------------------------------------------------------+
double GetLastFractalLow(ENUM_TIMEFRAMES tf)
{
// Start at shift 3 (requires 2 closed candles after the low to confirm structure)
for(int i = 3; i < 100; i++)
{
double frac = iFractals(Symbol(), tf, MODE_LOWER, i);
if(frac != 0 && frac != EMPTY_VALUE) return frac;
}
return 0.0;
}
//+------------------------------------------------------------------+
//| Helper: Find most recent confirmed Up Fractal (Lower High) |
//+------------------------------------------------------------------+
double GetLastFractalHigh(ENUM_TIMEFRAMES tf)
{
for(int i = 3; i < 100; i++)
{
double frac = iFractals(Symbol(), tf, MODE_UPPER, i);
if(frac != 0 && frac != EMPTY_VALUE) return frac;
}
return 0.0;
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Partial exits on M5 gold scalps without turning winners into scratches
How the trailing mechanism works:
The Checkpoint: It specifically checks if(partialTaken && EnableFractalTrail). The trailing engine remains completely dormant until your +1R target has been banked.
The Validation Delay: The helper functions start searching for fractals at i = 3 (3 candles ago). Because a valid structural pivot requires two lower highs/lows after the extreme, it will ignore live candles and only anchor to completely formed, historic structure.
The Padding: The FractalPadding input solves the gold liquidity sweep problem. If a fractal low forms at $2505.50, you don't want your stop right at .50 to get tagged by noise. Setting this to 15-20 points ensures your stop sits just below the structure.
One-Way Ratchet: The if(newSL > stopLoss) check ensures your stop can only move forward, locking in structural gains without ever loosening the risk.
The Checkpoint: It specifically checks if(partialTaken && EnableFractalTrail). The trailing engine remains completely dormant until your +1R target has been banked.
The Validation Delay: The helper functions start searching for fractals at i = 3 (3 candles ago). Because a valid structural pivot requires two lower highs/lows after the extreme, it will ignore live candles and only anchor to completely formed, historic structure.
The Padding: The FractalPadding input solves the gold liquidity sweep problem. If a fractal low forms at $2505.50, you don't want your stop right at .50 to get tagged by noise. Setting this to 15-20 points ensures your stop sits just below the structure.
One-Way Ratchet: The if(newSL > stopLoss) check ensures your stop can only move forward, locking in structural gains without ever loosening the risk.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Partial exits on M5 gold scalps without turning winners into scratches
I have updated the EA to draw two dynamic, dashed horizontal lines on your chart. These lines constantly track the most recent confirmed upper and lower fractals, including your padding. This means the lines show the exact price level your stop will jump to once the +1R partial is secured.
I also added an OnInit() function to ensure object descriptions are visible on your chart, and an OnDeinit() function so the EA cleans up its drawing lines if you remove it.
The Visual Structural Trailing Manager (MQL4)
I also added an OnInit() function to ensure object descriptions are visible on your chart, and an OnDeinit() function so the EA cleans up its drawing lines if you remove it.
The Visual Structural Trailing Manager (MQL4)
Code: Select all
//+------------------------------------------------------------------+
//| XAU_FixedPartial_Manager.mq4 |
//| With Structural Trailing & Visual SL Targets |
//+------------------------------------------------------------------+
#property strict
//--- Partial Settings
input double PartialPercent = 50.0; // Percentage of position to close at Target 1
input double R_Multiplier = 1.0; // R-Multiple for First Partial (e.g., 1.0 = +1R)
input int Slippage = 3; // Allowed slippage in points
//--- Trailing Settings
input bool EnableFractalTrail = true; // Trail remainder behind fractals?
input ENUM_TIMEFRAMES TrailTF = PERIOD_M5; // Timeframe for structural fractals (M5, M15)
input int FractalPadding = 15; // Points padding behind the fractal to avoid wicks
//--- Visual Settings
input bool ShowVisualLines = true; // Draw dynamic SL target lines on chart?
input color ColorLongSL = clrDodgerBlue;
input color ColorShortSL = clrOrangeRed;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Ensure object descriptions (labels) are enabled on the chart
if(ShowVisualLines)
{
ChartSetInteger(0, CHART_SHOW_OBJECT_DESCR, true);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Clean up visual lines when EA is removed
ObjectDelete(0, "Struct_SL_Long");
ObjectDelete(0, "Struct_SL_Short");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
double point = MarketInfo(Symbol(), MODE_POINT);
// ==========================================
// VISUAL TARGET UPDATES (Runs constantly)
// ==========================================
double lastFractalLow = GetLastFractalLow(TrailTF);
double lastFractalHigh = GetLastFractalHigh(TrailTF);
double targetLongSL = 0;
double targetShortSL = 0;
if(lastFractalLow > 0) targetLongSL = NormalizeDouble(lastFractalLow - (FractalPadding * point), Digits);
if(lastFractalHigh > 0) targetShortSL = NormalizeDouble(lastFractalHigh + (FractalPadding * point), Digits);
if(ShowVisualLines)
{
DrawTrailingLine("Struct_SL_Long", targetLongSL, ColorLongSL, " Target Long SL (Padded)");
DrawTrailingLine("Struct_SL_Short", targetShortSL, ColorShortSL, " Target Short SL (Padded)");
}
// ==========================================
// TRADE MANAGEMENT LOOP
// ==========================================
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol())
{
double openPrice = OrderOpenPrice();
double stopLoss = OrderStopLoss();
int ticket = OrderTicket();
if(stopLoss == 0) continue;
string gvName = "PartialTaken_" + IntegerToString(OrderOpenTime());
bool partialTaken = GlobalVariableCheck(gvName);
// 1. MECHANICAL PARTIAL LOGIC
if(!partialTaken)
{
double riskDistance = MathAbs(openPrice - stopLoss);
if(riskDistance == 0) continue;
double r_TargetDist = riskDistance * R_Multiplier;
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
double lotsToClose = OrderLots() * (PartialPercent / 100.0);
lotsToClose = MathFloor(lotsToClose / lotStep) * lotStep;
if(lotsToClose >= minLot)
{
if(OrderType() == OP_BUY && Bid >= (openPrice + r_TargetDist))
{
if(OrderClose(ticket, lotsToClose, Bid, Slippage, clrGreen))
{
GlobalVariableSet(gvName, 1);
Print("Mechanical Partial: ", lotsToClose, " lots closed at +1R.");
continue;
}
}
else if(OrderType() == OP_SELL && Ask <= (openPrice - r_TargetDist))
{
if(OrderClose(ticket, lotsToClose, Ask, Slippage, clrRed))
{
GlobalVariableSet(gvName, 1);
Print("Mechanical Partial: ", lotsToClose, " lots closed at +1R.");
continue;
}
}
}
}
// 2. FRACTAL TRAILING LOGIC (REMAINDER ONLY)
if(partialTaken && EnableFractalTrail)
{
if(OrderType() == OP_BUY && targetLongSL > 0)
{
// Only move stop UP, and ensure it's below current price
if(targetLongSL > stopLoss && targetLongSL < Bid)
{
bool mod = OrderModify(ticket, openPrice, targetLongSL, OrderTakeProfit(), 0, clrBlue);
if(mod) Print("Structural Trail: Buy SL moved up to ", targetLongSL);
}
}
else if(OrderType() == OP_SELL && targetShortSL > 0)
{
// Only move stop DOWN, and ensure it's above current price
if(targetShortSL < stopLoss && targetShortSL > Ask)
{
bool mod = OrderModify(ticket, openPrice, targetShortSL, OrderTakeProfit(), 0, clrMagenta);
if(mod) Print("Structural Trail: Sell SL moved down to ", targetShortSL);
}
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Helper: Find most recent confirmed Down Fractal (Higher Low) |
//+------------------------------------------------------------------+
double GetLastFractalLow(ENUM_TIMEFRAMES tf)
{
for(int i = 3; i < 100; i++)
{
double frac = iFractals(Symbol(), tf, MODE_LOWER, i);
if(frac != 0 && frac != EMPTY_VALUE) return frac;
}
return 0.0;
}
//+------------------------------------------------------------------+
//| Helper: Find most recent confirmed Up Fractal (Lower High) |
//+------------------------------------------------------------------+
double GetLastFractalHigh(ENUM_TIMEFRAMES tf)
{
for(int i = 3; i < 100; i++)
{
double frac = iFractals(Symbol(), tf, MODE_UPPER, i);
if(frac != 0 && frac != EMPTY_VALUE) return frac;
}
return 0.0;
}
//+------------------------------------------------------------------+
//| Helper: Draw or move horizontal target lines |
//+------------------------------------------------------------------+
void DrawTrailingLine(string name, double price, color clr, string text)
{
if(price <= 0) return;
if(ObjectFind(0, name) < 0)
{
ObjectCreate(0, name, OBJ_HLINE, 0, 0, price);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DASHDOT); // Dashed line
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true); // Hide from object list so it's not accidentally deleted
}
else
{
ObjectMove(0, name, 0, 0, price);
}
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Partial exits on M5 gold scalps without turning winners into scratches
Visual Enhancements Added:
Always-On Chart Awareness: The lines calculate and display even when you have no open positions. When you are stalking an M5 setup, you will see exactly where the trailing stop would go before you even click buy or sell.
Built-in Padding Visibility: The line doesn't just rest on the raw wick; it includes your FractalPadding input. What you see is exactly where MT4 will place the stop order.
Clean Styling: The levels render as non-intrusive dashed lines (STYLE_DASHDOT) with clear text labels ("Target Long SL / Target Short SL") positioned dynamically at the price level.
Always-On Chart Awareness: The lines calculate and display even when you have no open positions. When you are stalking an M5 setup, you will see exactly where the trailing stop would go before you even click buy or sell.
Built-in Padding Visibility: The line doesn't just rest on the raw wick; it includes your FractalPadding input. What you see is exactly where MT4 will place the stop order.
Clean Styling: The levels render as non-intrusive dashed lines (STYLE_DASHDOT) with clear text labels ("Target Long SL / Target Short SL") positioned dynamically at the price level.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Partial exits on M5 gold scalps without turning winners into scratches
Here is the exact structural logic translated for both MT5 and cTrader.
1. MT5 / MQL5 Version
MT5 requires the CTrade library to execute partial closures cleanly. It also handles indicator buffers differently; the EA initializes a Fractal "handle" on startup and copies the buffer data on every tick to find the structure.
How to use: Open MetaEditor 5, create a new Expert Advisor, paste, and compile.
1. MT5 / MQL5 Version
MT5 requires the CTrade library to execute partial closures cleanly. It also handles indicator buffers differently; the EA initializes a Fractal "handle" on startup and copies the buffer data on every tick to find the structure.
How to use: Open MetaEditor 5, create a new Expert Advisor, paste, and compile.
Code: Select all
//+------------------------------------------------------------------+
//| XAU_FixedPartial_Manager.mq5 |
//+------------------------------------------------------------------+
#property strict
#include <Trade\Trade.mqh>
//--- Inputs
input double PartialPercent = 50.0; // Percentage of position to close at Target 1
input double R_Multiplier = 1.0; // R-Multiple for First Partial (e.g., 1.0 = +1R)
input ulong Slippage = 30; // Allowed slippage in points
input bool EnableFractalTrail = true; // Trail remainder behind fractals?
input ENUM_TIMEFRAMES TrailTF = PERIOD_M5; // Timeframe for structural fractals
input int FractalPadding = 15; // Points padding behind the fractal
input bool ShowVisualLines = true; // Draw dynamic SL target lines on chart?
input color ColorLongSL = clrDodgerBlue;
input color ColorShortSL = clrOrangeRed;
CTrade trade;
int fractal_handle;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
trade.SetExpertMagicNumber(123456);
trade.SetDeviationInPoints(Slippage);
// Get handle for Fractals indicator
fractal_handle = iFractals(_Symbol, TrailTF);
if(fractal_handle == INVALID_HANDLE) return(INIT_FAILED);
if(ShowVisualLines) ChartSetInteger(0, CHART_SHOW_OBJECT_DESCR, true);
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
ObjectDelete(0, "Struct_SL_Long");
ObjectDelete(0, "Struct_SL_Short");
IndicatorRelease(fractal_handle);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
// --- Visual Target Updates ---
double lastFractalLow = GetLastFractal(1); // 1 = Lower buffer
double lastFractalHigh = GetLastFractal(0); // 0 = Upper buffer
double targetLongSL = 0, targetShortSL = 0;
if(lastFractalLow > 0) targetLongSL = NormalizeDouble(lastFractalLow - (FractalPadding * point), _Digits);
if(lastFractalHigh > 0) targetShortSL = NormalizeDouble(lastFractalHigh + (FractalPadding * point), _Digits);
if(ShowVisualLines)
{
DrawTrailingLine("Struct_SL_Long", targetLongSL, ColorLongSL, " Target Long SL (Padded)");
DrawTrailingLine("Struct_SL_Short", targetShortSL, ColorShortSL, " Target Short SL (Padded)");
}
// --- Trade Management Loop ---
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double stopLoss = PositionGetDouble(POSITION_SL);
double volume = PositionGetDouble(POSITION_VOLUME);
long type = PositionGetInteger(POSITION_TYPE);
long posId = PositionGetInteger(POSITION_IDENTIFIER);
if(stopLoss == 0) continue;
string gvName = "PartialTaken_" + IntegerToString(posId);
bool partialTaken = GlobalVariableCheck(gvName);
// 1. MECHANICAL PARTIAL
if(!partialTaken)
{
double riskDistance = MathAbs(openPrice - stopLoss);
if(riskDistance == 0) continue;
double r_TargetDist = riskDistance * R_Multiplier;
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double lotsToClose = volume * (PartialPercent / 100.0);
lotsToClose = MathFloor(lotsToClose / lotStep) * lotStep;
if(lotsToClose >= minLot)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(type == POSITION_TYPE_BUY && bid >= (openPrice + r_TargetDist))
{
if(trade.PositionClosePartial(ticket, lotsToClose))
{
GlobalVariableSet(gvName, 1);
Print("Mechanical Partial Executed");
}
}
else if(type == POSITION_TYPE_SELL && ask <= (openPrice - r_TargetDist))
{
if(trade.PositionClosePartial(ticket, lotsToClose))
{
GlobalVariableSet(gvName, 1);
Print("Mechanical Partial Executed");
}
}
}
}
// 2. STRUCTURAL TRAILING
if(partialTaken && EnableFractalTrail)
{
double currentTP = PositionGetDouble(POSITION_TP);
if(type == POSITION_TYPE_BUY && targetLongSL > 0)
{
if(targetLongSL > stopLoss && targetLongSL < SymbolInfoDouble(_Symbol, SYMBOL_BID))
{
trade.PositionModify(ticket, targetLongSL, currentTP);
}
}
else if(type == POSITION_TYPE_SELL && targetShortSL > 0)
{
if(targetShortSL < stopLoss && targetShortSL > SymbolInfoDouble(_Symbol, SYMBOL_ASK))
{
trade.PositionModify(ticket, targetShortSL, currentTP);
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Helpers |
//+------------------------------------------------------------------+
double GetLastFractal(int bufferIndex)
{
double fractalArray[];
ArraySetAsSeries(fractalArray, true);
// Start at index 3 (2 closed bars after the fractal pin)
if(CopyBuffer(fractal_handle, bufferIndex, 3, 100, fractalArray) > 0)
{
for(int i = 0; i < ArraySize(fractalArray); i++)
{
if(fractalArray[i] != EMPTY_VALUE && fractalArray[i] != DBL_MAX)
return fractalArray[i];
}
}
return 0.0;
}
void DrawTrailingLine(string name, double price, color clr, string text)
{
if(price <= 0) return;
if(ObjectFind(0, name) < 0)
{
ObjectCreate(0, name, OBJ_HLINE, 0, 0, price);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DASHDOT);
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
}
else
{
ObjectMove(0, name, 0, 0, price);
}
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Partial exits on M5 gold scalps without turning winners into scratches
2. cTrader / cAlgo (C#) Version
Because cBots run actively in memory, we do not need Global Variables to track state. We simply store the Position.Id in a HashSet once a partial is taken.
How to use: Open cTrader Automate, click "New cBot," paste the code, and click Build.
Because cBots run actively in memory, we do not need Global Variables to track state. We simply store the Position.Id in a HashSet once a partial is taken.
How to use: Open cTrader Automate, click "New cBot," paste the code, and click Build.
Code: Select all
using System;
using System.Linq;
using System.Collections.Generic;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class XAUFixedPartialManager : Robot
{
[Parameter("Partial Percent", DefaultValue = 50.0)]
public double PartialPercent { get; set; }
[Parameter("R-Multiplier (1.0 = 1R)", DefaultValue = 1.0)]
public double R_Multiplier { get; set; }
[Parameter("Enable Fractal Trail", DefaultValue = true)]
public bool EnableFractalTrail { get; set; }
[Parameter("Fractal Timeframe", DefaultValue = "Minute5")]
public TimeFrame TrailTF { get; set; }
[Parameter("Fractal Padding (Points)", DefaultValue = 15)]
public int FractalPadding { get; set; }
[Parameter("Show Visual Lines", DefaultValue = true)]
public bool ShowVisualLines { get; set; }
private Fractals _fractals;
private HashSet<int> _partialTakenIds = new HashSet<int>();
protected override void OnStart()
{
// Initialize Bill Williams Fractals indicator (requires 5 bars total)
_fractals = Indicators.Fractals(MarketData.GetSeries(TrailTF), 5);
}
protected override void OnTick()
{
// --- Visual Target Updates ---
double point = Symbol.TickSize / 10; // Convert pip/tick sizing to raw points
double targetLongSL = 0;
double targetShortSL = 0;
double lastFractalLow = GetLastFractalLow();
double lastFractalHigh = GetLastFractalHigh();
if (lastFractalLow > 0) targetLongSL = Math.Round(lastFractalLow - (FractalPadding * point), Symbol.Digits);
if (lastFractalHigh > 0) targetShortSL = Math.Round(lastFractalHigh + (FractalPadding * point), Symbol.Digits);
if (ShowVisualLines)
{
if (targetLongSL > 0)
Chart.DrawHorizontalLine("Struct_SL_Long", targetLongSL, Color.DodgerBlue, 1, LineStyle.Lines).Text = " Target Long SL";
if (targetShortSL > 0)
Chart.DrawHorizontalLine("Struct_SL_Short", targetShortSL, Color.OrangeRed, 1, LineStyle.Lines).Text = " Target Short SL";
}
// --- Trade Management Loop ---
foreach (var position in Positions.Where(p => p.SymbolName == SymbolName))
{
if (!position.StopLoss.HasValue) continue;
bool partialTaken = _partialTakenIds.Contains(position.Id);
// 1. MECHANICAL PARTIAL
if (!partialTaken)
{
double riskDistance = Math.Abs(position.EntryPrice - position.StopLoss.Value);
if (riskDistance == 0) continue;
double r_TargetDist = riskDistance * R_Multiplier;
double targetPrice = position.TradeType == TradeType.Buy
? position.EntryPrice + r_TargetDist
: position.EntryPrice - r_TargetDist;
bool targetHit = position.TradeType == TradeType.Buy
? Symbol.Bid >= targetPrice
: Symbol.Ask <= targetPrice;
if (targetHit)
{
double volumeToClose = position.VolumeInUnits * (PartialPercent / 100.0);
volumeToClose = Symbol.NormalizeVolumeInUnits(volumeToClose, RoundingMode.Down);
if (volumeToClose >= Symbol.VolumeInUnitsMin)
{
ClosePosition(position, volumeToClose);
_partialTakenIds.Add(position.Id);
Print("Mechanical Partial Executed at +1R");
continue;
}
}
}
// 2. STRUCTURAL TRAILING
if (partialTaken && EnableFractalTrail)
{
if (position.TradeType == TradeType.Buy && targetLongSL > 0)
{
if (targetLongSL > position.StopLoss.Value && targetLongSL < Symbol.Bid)
{
ModifyPositionAsync(position, targetLongSL, position.TakeProfit);
}
}
else if (position.TradeType == TradeType.Sell && targetShortSL > 0)
{
if (targetShortSL < position.StopLoss.Value && targetShortSL > Symbol.Ask)
{
ModifyPositionAsync(position, targetShortSL, position.TakeProfit);
}
}
}
}
}
// --- Helpers ---
private double GetLastFractalLow()
{
// Start looking 3 candles back to ensure the fractal is locked/confirmed
for (int i = _fractals.DownFractal.Count - 4; i >= 0; i--)
{
if (!double.IsNaN(_fractals.DownFractal[i]))
return _fractals.DownFractal[i];
}
return 0.0;
}
private double GetLastFractalHigh()
{
for (int i = _fractals.UpFractal.Count - 4; i >= 0; i--)
{
if (!double.IsNaN(_fractals.UpFractal[i]))
return _fractals.UpFractal[i];
}
return 0.0;
}
}
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Partial exits on M5 gold scalps without turning winners into scratches
Moving this logic to TradingView requires an important paradigm shift. Unlike MT4 or cTrader, Pine Script cannot "see" or manage trades you place manually on a third-party broker terminal. It exists entirely inside the TradingView sandbox.
To solve this, I have written this as a Strategy and Visual Assistant.
The Visuals: It calculates and plots the padded fractal target lines on your chart in real-time, so you can manually manage your MT4/cTrader positions visually.
The Backtester: It includes a complete trading engine. You can plug your own entry edge into the code (I included a placeholder moving average crossover so it runs out of the box), and TradingView will backtest exactly how your fixed partials and structural trailing rules perform over thousands of historical trades.
Here is the complete Pine Script (v5) translation of your framework.
The Structural Trailing Manager (Pine Script v5)
To solve this, I have written this as a Strategy and Visual Assistant.
The Visuals: It calculates and plots the padded fractal target lines on your chart in real-time, so you can manually manage your MT4/cTrader positions visually.
The Backtester: It includes a complete trading engine. You can plug your own entry edge into the code (I included a placeholder moving average crossover so it runs out of the box), and TradingView will backtest exactly how your fixed partials and structural trailing rules perform over thousands of historical trades.
Here is the complete Pine Script (v5) translation of your framework.
The Structural Trailing Manager (Pine Script v5)
Code: Select all
//@version=5
strategy("XAU Fixed Partial & Fractal Trail", overlay=true, initial_capital=10000, default_qty_type=strategy.percent_of_equity, default_qty_value=2)
// ==========================================
// 1. INPUTS
// ==========================================
grp_partial = "Partial & Target Settings"
r_mult = input.float(1.0, title="R-Multiplier for Target 1", group=grp_partial)
partial_pct = input.float(50.0, title="Partial Close %", group=grp_partial)
grp_trail = "Trailing Settings"
trail_tf = input.timeframe("5", title="Fractal Timeframe (e.g., '5' or '15')", group=grp_trail)
padding_pts = input.int(15, title="Fractal Padding (Ticks/Points)", group=grp_trail)
grp_visuals = "Visuals"
show_lines = input.bool(true, title="Show Trailing SL Targets", group=grp_visuals)
// ==========================================
// 2. FRACTAL LOGIC (Multi-Timeframe)
// ==========================================
// A valid Bill Williams Fractal requires 5 bars. The pivot is bar [2], confirmed when bar [0] closes.
get_fractals() =>
upF = high[4] < high[2] and high[3] < high[2] and high[2] > high[1] and high[2] > high[0]
dnF = low[4] > low[2] and low[3] > low[2] and low[2] < low[1] and low[2] < low[0]
// Store the most recent confirmed fractals
fHigh = ta.valuewhen(upF, high[2], 0)
fLow = ta.valuewhen(dnF, low[2], 0)
[fHigh, fLow]
// Request fractals from the chosen timeframe
[mtf_high, mtf_low] = request.security(syminfo.tickerid, trail_tf, get_fractals(), lookahead=barmerge.lookahead_off)
// Calculate padded target levels
pad_price = padding_pts * syminfo.mintick
target_long_sl = mtf_low - pad_price
target_short_sl = mtf_high + pad_price
// Draw visual targets on chart
plot(show_lines and not na(target_long_sl) ? target_long_sl : na, color=color.new(color.blue, 30), style=plot.style_circles, linewidth=1, title="Long SL Target")
plot(show_lines and not na(target_short_sl) ? target_short_sl : na, color=color.new(color.red, 30), style=plot.style_circles, linewidth=1, title="Short SL Target")
// ==========================================
// 3. TRADE STATE TRACKING
// ==========================================
var float initial_sl = na
var float initial_ep = na
var float target_price = na
var float current_sl = na
var bool is_partial = false
// Dummy entry condition for backtesting (Replace with your actual edge)
fast_ma = ta.sma(close, 9)
slow_ma = ta.sma(close, 21)
long_cond = ta.crossover(fast_ma, slow_ma)
short_cond = ta.crossunder(fast_ma, slow_ma)
// Calculate an example structural stop for the initial entry (e.g., recent swing low)
swing_low = ta.lowest(low, 10)
swing_high = ta.highest(high, 10)
// ==========================================
// 4. EXECUTION & MANAGEMENT
// ==========================================
// Entries
if strategy.position_size == 0
if long_cond
initial_sl := swing_low - pad_price
initial_ep := close
risk = initial_ep - initial_sl
target_price := initial_ep + (risk * r_mult)
current_sl := initial_sl
is_partial := false
strategy.entry("Long", strategy.long)
if short_cond
initial_sl := swing_high + pad_price
initial_ep := close
risk = initial_sl - initial_ep
target_price := initial_ep - (risk * r_mult)
current_sl := initial_sl
is_partial := false
strategy.entry("Short", strategy.short)
// State Detection: Did we hit the partial?
// If position size shrinks but remains open, the limit order was hit.
if strategy.position_size != 0 and math.abs(strategy.position_size) < math.abs(strategy.position_size[1])
is_partial := true
// Update trailing stop only AFTER partial is taken
if is_partial
if strategy.position_size > 0 // Long Trailing
if target_long_sl > current_sl and target_long_sl < close
current_sl := target_long_sl
if strategy.position_size < 0 // Short Trailing
if target_short_sl < current_sl and target_short_sl > close
current_sl := target_short_sl
// Exit Orders Placement
if strategy.position_size > 0
// Places a limit order for the 1R partial, and a baseline stop for the whole position
strategy.exit("Partial", from_entry="Long", qty_percent=partial_pct, limit=target_price, stop=initial_sl)
// Manages the remainder with the dynamic trailing stop
strategy.exit("Trail", from_entry="Long", stop=current_sl)
if strategy.position_size < 0
strategy.exit("Partial", from_entry="Short", qty_percent=partial_pct, limit=target_price, stop=initial_sl)
strategy.exit("Trail", from_entry="Short", stop=current_sl)Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.