To integrate a trailing stop that only activates after the partial close has executed, we rely on the state flag established in the previous step: the Breakeven Stop Loss.
By checking if the current Stop Loss is already at or better than the breakeven price, the EA inherently knows the partial close has occurred. This completely isolates the trailing logic from the initial risk parameters.
Additionally, because XAG/USD ticks violently, updating a trailing stop on every single tick will spam the broker's trade server with OrderModify() requests, resulting in an Error 1 (ERR_NO_RESULT) and potential throttling or banning of the EA. We mitigate this using a TrailingStep.
// External parameters for user optimization
extern int TrailingDistancePips = 15; // Distance to trail behind price
extern int TrailingStepPips = 2; // Minimum pip movement required before modifying SL again
//+------------------------------------------------------------------+
//| Trails the Stop Loss for the remaining position post-partial |
//+------------------------------------------------------------------+
void ApplyTrailingStop() {
for (int i = OrdersTotal() - 1; i >= 0; i--) {
if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if (OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) {
int type = OrderType();
double openPrice = OrderOpenPrice();
double currentSL = OrderStopLoss();
// 1. Establish the pip multiplier for fractional brokers
double pip = Point;
if (Digits == 3 || Digits == 5) pip = Point * 10;
// 2. Re-calculate the Breakeven threshold
double beOffset = BreakevenOffsetPips * pip;
double bePriceBuy = openPrice + beOffset;
double bePriceSell = openPrice - beOffset;
// 3. STATE CHECK: Only trail if the SL is already at or past Breakeven
bool isPastBE = false;
if (type == OP_BUY && currentSL >= bePriceBuy) isPastBE = true;
if (type == OP_SELL && (currentSL <= bePriceSell && currentSL != 0)) isPastBE = true;
if (!isPastBE) continue; // Skip if partial close/BE hasn't happened yet
// 4. Calculate distances in points
double trailPoints = TrailingDistancePips * pip;
double stepPoints = TrailingStepPips * pip;
// 5. EXECUTION: Trailing Logic with Step Filter
if (type == OP_BUY) {
double newSL = Bid - trailPoints;
// Only modify if the new SL is higher than the current SL by at least the Step
if (newSL > currentSL + stepPoints) {
bool modified = OrderModify(OrderTicket(), openPrice, newSL, OrderTakeProfit(), 0, Blue);
if (!modified) Print("Trailing Stop (Buy) Error: ", GetLastError());
}
}
else if (type == OP_SELL) {
double newSL = Ask + trailPoints;
// Only modify if the new SL is lower than the current SL by at least the Step
if (newSL < currentSL - stepPoints || currentSL == 0) {
bool modified = OrderModify(OrderTicket(), openPrice, newSL, OrderTakeProfit(), 0, Red);
if (!modified) Print("Trailing Stop (Sell) Error: ", GetLastError());
}
}
}
}
}
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
The State Hook (isPastBE): The logic recalculates the exact breakeven price from the previous function. If the current SL isn't at least equal to this price, the loop skips the order. This guarantees the trailing stop never interferes with the initial 1R partial-close target.
The Trailing Step Filter (stepPoints): Checking newSL > currentSL + stepPoints serves as an anti-spam governor. If the market ticks up by 0.1 pips, the EA does nothing. It waits for the market to move a full 2 pips (your defined step) before firing a new OrderModify() command. This drastically reduces CPU load and keeps your broker's connection stable.
Localized Pip Normalization: Instead of relying on a global function, defining pip locally ensures that the trailing math cleanly handles 3-digit silver pricing, converting the user-friendly pip inputs into raw server points instantly.
Place ApplyTrailingStop() immediately after ManageTrades() inside the OnTick() loop. The EA will now automatically split the ticket at the RR target, lock in breakeven on the remainder, and seamlessly hand off the new ticket to the trailing stop engine.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
And i prepared new version for MT5 traders as well.
Migrating this architecture to MetaTrader 5 (MQL5) cleans up the implementation significantly. MT5 separates pending Orders from open Positions, natively supports partial volume closes without destroying position tickets, and provides the standard CTrade class to handle order routing, execution types, and slippage deviations out of the box.
Here is the complete, modular MQL5 translation of the execution, sizing, and trade management stack.
1. Global Setup & Header Integration
In MQL5, always include Trade.mqh. We also set the execution policy automatically to handle broker filling mode restrictions (FOK, IOC, Return).
2. Dynamic Lot Sizing (Point vs TickSize Normalized)
MQL5 uses SymbolInfoDouble instead of MarketInfo. The normalization against the tick size/value ratio remains essential for commodities and metals like XAG/USD.
In MT5, iterating through open market trades is handled with PositionsTotal(). When trade.PositionClosePartial() is called, the position ticket ID remains identical in hedging mode—no ticket splitting workarounds are needed.
The trailing stop checks that the position is already protected at breakeven before taking over the runner, using InpTrailingStepPips as an anti-spam governor.
To elevate this script to a professional, enterprise-grade Expert Advisor, we must move away from procedural global functions and adopt Object-Oriented Programming (OOP).
Professional MQL5 architecture relies heavily on the Standard Library (CTrade, CSymbolInfo, CPositionInfo, CAccountInfo). This approach encapsulates trade logic, caches symbol data to reduce CPU load, and implements strict error-code checking.
Here is the refactored, OOP-based architecture for the Silver Bullet trade manager.
1. Global Setup & Standard Library Includes
We include the core trade classes. This allows us to interact with positions and symbols as objects rather than querying the trade server with raw functions on every tick.
By encapsulating the logic within a CSilverManager class, the variables and state are protected. The Init() method caches symbol parameters exactly once, preventing the EA from wasting CPU cycles calling SymbolInfoDouble() continuously.