Page 2 of 5
Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView
Posted: Tue Sep 01, 2026 11:41 pm
by PTScalper
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.
The MQL4 Implementation
Code: Select all
// 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());
}
}
}
}
}
}
Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView
Posted: Tue Sep 01, 2026 11:41 pm
by PTScalper
Architectural Breakdown
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.
Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView
Posted: Tue Sep 01, 2026 11:42 pm
by PTScalper
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).
Code: Select all
#property copyright "Trading Architecture"
#property link ""
#property version "1.00"
#include <Trade\Trade.mqh>
//--- Inputs
input group "=== Risk & Sizing ==="
input double InpRiskPercent = 1.0; // Risk per trade (% of Balance)
input int InpMaxSpreadPoints = 40; // Max allowed spread (in points)
input int InpMaxSlippagePips = 2; // Slippage tolerance (in pips)
input group "=== Trade Management ==="
input double InpRR_Target = 1.0; // R:R Multiple for Breakeven & Partial Close
input double InpPartialClosePct = 50.0; // Percentage of position to close at target
input int InpBreakevenOffset = 1; // Pips above/below entry to lock in
input int InpTrailingDistPips = 15; // Trailing stop distance (in pips)
input int InpTrailingStepPips = 2; // Minimum step to update trailing stop
input group "=== System ==="
input ulong InpMagicNumber = 101102; // EA Magic Number
//--- Global Objects
CTrade trade;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
trade.SetExpertMagicNumber(InpMagicNumber);
// Normalize slippage deviation points
int deviation = InpMaxSlippagePips;
if (_Digits == 3 || _Digits == 5) deviation *= 10;
trade.SetDeviationInPoints(deviation);
// Auto-detect broker execution filling type
trade.SetTypeFillingBySymbol(_Symbol);
return(INIT_SUCCEEDED);
}
Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView
Posted: Tue Sep 01, 2026 11:42 pm
by PTScalper
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.
Code: Select all
//+------------------------------------------------------------------+
//| Calculates dynamic lot size based on account risk percentage |
//+------------------------------------------------------------------+
double CalculateLotSize(double entryPrice, double stopLossPrice, double riskPercent) {
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = balance * (riskPercent / 100.0);
double slDistancePoints = MathAbs(entryPrice - stopLossPrice) / _Point;
if (slDistancePoints == 0) return 0.0;
// MT5 Tick value and Tick size normalization
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
if (tickSize == 0) return 0.0;
double pointValue = tickValue * (_Point / tickSize);
double riskPerLot = slDistancePoints * pointValue;
if (riskPerLot == 0) return 0.0;
double rawLotSize = riskAmount / riskPerLot;
// Broker constraints
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
int steps = (int)MathFloor(rawLotSize / lotStep);
double finalLotSize = steps * lotStep;
if (finalLotSize < minLot) finalLotSize = minLot;
if (finalLotSize > maxLot) finalLotSize = maxLot;
return finalLotSize;
}
Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView
Posted: Tue Sep 01, 2026 11:43 pm
by PTScalper
3. Spread Gatekeeper
In MQL5, current market ticks are read via MqlTick.
Code: Select all
//+------------------------------------------------------------------+
//| Verifies real-time spread before order submission |
//+------------------------------------------------------------------+
bool IsExecutionSafe(int maxSpreadPoints) {
MqlTick currentTick;
if (!SymbolInfoTick(_Symbol, currentTick)) return false;
int currentSpread = (int)MathRound((currentTick.ask - currentTick.bid) / _Point);
if (currentSpread > maxSpreadPoints) {
PrintFormat("Execution Blocked: Spread (%d points) exceeds limit (%d points)", currentSpread, maxSpreadPoints);
return false;
}
return true;
}
Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView
Posted: Tue Sep 01, 2026 11:43 pm
by PTScalper
4. Trade Management: Breakeven & Native Partial Closes
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.
Code: Select all
//+------------------------------------------------------------------+
//| Manages 1R Breakeven and Partial Volume Liquidation |
//+------------------------------------------------------------------+
void ManageOpenPositions() {
MqlTick tick;
if (!SymbolInfoTick(_Symbol, tick)) return;
double pip = (_Digits == 3 || _Digits == 5) ? _Point * 10 : _Point;
double offsetPoints = InpBreakevenOffset * pip;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (ticket == 0) continue;
if (PositionGetString(POSITION_SYMBOL) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == InpMagicNumber) {
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
double currentTP = PositionGetDouble(POSITION_TP);
double volume = PositionGetDouble(POSITION_VOLUME);
double bePrice = (type == POSITION_TYPE_BUY) ? openPrice + offsetPoints : openPrice - offsetPoints;
// State Check: Skip if SL is already moved to/past BE
if ((type == POSITION_TYPE_BUY && currentSL >= bePrice) ||
(type == POSITION_TYPE_SELL && currentSL <= bePrice && currentSL != 0)) {
continue;
}
double riskDist = MathAbs(openPrice - currentSL);
if (riskDist == 0) continue;
bool targetHit = false;
if (type == POSITION_TYPE_BUY && tick.bid >= openPrice + (riskDist * InpRR_Target)) targetHit = true;
if (type == POSITION_TYPE_SELL && tick.ask <= openPrice - (riskDist * InpRR_Target)) targetHit = true;
if (targetHit) {
// 1. Move SL to Breakeven
if (trade.PositionModify(ticket, bePrice, currentTP)) {
// 2. Partial Volume Close
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double rawLots = volume * (InpPartialClosePct / 100.0);
double lotsToClose = MathFloor(rawLots / lotStep) * lotStep;
if (lotsToClose >= minLot && lotsToClose < volume) {
trade.PositionClosePartial(ticket, lotsToClose);
}
}
}
}
}
}
Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView
Posted: Tue Sep 01, 2026 11:44 pm
by PTScalper
5. Trailing Stop Engine
The trailing stop checks that the position is already protected at breakeven before taking over the runner, using InpTrailingStepPips as an anti-spam governor.
Code: Select all
//+------------------------------------------------------------------+
//| Dynamic trailing stop with Step filter for the remaining runner |
//+------------------------------------------------------------------+
void ApplyTrailingStop() {
MqlTick tick;
if (!SymbolInfoTick(_Symbol, tick)) return;
double pip = (_Digits == 3 || _Digits == 5) ? _Point * 10 : _Point;
double trailPoints = InpTrailingDistPips * pip;
double stepPoints = InpTrailingStepPips * pip;
double beOffset = InpBreakevenOffset * pip;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (ticket == 0) continue;
if (PositionGetString(POSITION_SYMBOL) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == InpMagicNumber) {
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
double currentTP = PositionGetDouble(POSITION_TP);
// Verify position is at least at Breakeven
bool isPastBE = false;
if (type == POSITION_TYPE_BUY && currentSL >= openPrice + beOffset) isPastBE = true;
if (type == POSITION_TYPE_SELL && currentSL <= openPrice - beOffset && currentSL != 0) isPastBE = true;
if (!isPastBE) continue;
// Execute step-filtered trailing modification
if (type == POSITION_TYPE_BUY) {
double newSL = tick.bid - trailPoints;
if (newSL > currentSL + stepPoints) {
trade.PositionModify(ticket, newSL, currentTP);
}
}
else if (type == POSITION_TYPE_SELL) {
double newSL = tick.ask + trailPoints;
if (newSL < currentSL - stepPoints || currentSL == 0) {
trade.PositionModify(ticket, newSL, currentTP);
}
}
}
}
}
Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView
Posted: Tue Sep 01, 2026 11:44 pm
by PTScalper
6. Main Event Loop
Code: Select all
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
// 1. Defend open positions first
ManageOpenPositions();
ApplyTrailingStop();
// 2. Scan & execute new entries
// Example placement using CTrade:
/*
if (IsSilverBulletWindow() && IsExecutionSafe(InpMaxSpreadPoints)) {
double lots = CalculateLotSize(EntryPrice, StopLoss, InpRiskPercent);
if (lots > 0) {
trade.BuyLimit(lots, EntryPrice, _Symbol, StopLoss, TakeProfit,
ORDER_TIME_DAY, 0, "Silver Bullet Buy");
}
}
*/
}
Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView
Posted: Tue Sep 01, 2026 11:45 pm
by PTScalper
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.
Code: Select all
#property copyright "Trading Architecture"
#property version "2.00"
#property strict
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\AccountInfo.mqh>
//--- Inputs
input group "=== Risk & Sizing ==="
input double InpRiskPercent = 1.0;
input int InpMaxSpreadPoints = 40;
input int InpMaxSlippagePips = 2;
input group "=== Trade Management ==="
input double InpRR_Target = 1.0;
input double InpPartialClosePct = 50.0;
input int InpBreakevenOffset = 1;
input int InpTrailingDistPips = 15;
input int InpTrailingStepPips = 2;
input group "=== System ==="
input ulong InpMagicNumber = 101102;
Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView
Posted: Tue Sep 01, 2026 11:45 pm
by PTScalper
2. The Trade Manager Class
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.
Code: Select all
//+------------------------------------------------------------------+
//| Class: CSilverManager |
//| Purpose: Encapsulates execution, sizing, and position management |
//+------------------------------------------------------------------+
class CSilverManager {
private:
CTrade m_trade;
CSymbolInfo m_symbol;
CPositionInfo m_position;
CAccountInfo m_account;
double m_pip; // Normalized pip value
int m_beOffsetPts; // Breakeven offset in points
public:
// Constructor & Initialization
CSilverManager();
bool Init(string symbol, ulong magic);
// Core Functions
double CalculateLotSize(double entryPrice, double stopLossPrice);
bool IsExecutionSafe();
void ManageOpenPositions();
void ApplyTrailingStop();
};
//--- Constructor
CSilverManager::CSilverManager() {}
//--- Initialization
bool CSilverManager::Init(string symbol, ulong magic) {
if(!m_symbol.Name(symbol)) return false;
m_symbol.RefreshRates();
m_trade.SetExpertMagicNumber(magic);
// Auto-detect fractional pricing for pip normalization
m_pip = (m_symbol.Digits() == 3 || m_symbol.Digits() == 5) ? m_symbol.Point() * 10 : m_symbol.Point();
m_beOffsetPts = (int)(InpBreakevenOffset * (m_pip / m_symbol.Point()));
// Setup execution parameters
int deviation = InpMaxSlippagePips * (int)(m_pip / m_symbol.Point());
m_trade.SetDeviationInPoints(deviation);
m_trade.SetTypeFillingBySymbol(m_symbol.Name());
return true;
}