IC Markets

ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView

Share, develop, and backtest custom MQL4/MQL5 Expert Advisors, Python data-scraping scripts, trading bots, and automated market alert systems.
PTScalper
Site Admin
Posts: 771
Joined: Mon Jul 20, 2026 1:28 pm

Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView

Post 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());
                    }
                }
            }
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 771
Joined: Mon Jul 20, 2026 1:28 pm

Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView

Post 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.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 771
Joined: Mon Jul 20, 2026 1:28 pm

Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView

Post 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);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 771
Joined: Mon Jul 20, 2026 1:28 pm

Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView

Post 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;
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 771
Joined: Mon Jul 20, 2026 1:28 pm

Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView

Post 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;
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 771
Joined: Mon Jul 20, 2026 1:28 pm

Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView

Post 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);
                    }
                }
            }
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 771
Joined: Mon Jul 20, 2026 1:28 pm

Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView

Post 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);
                }
            }
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 771
Joined: Mon Jul 20, 2026 1:28 pm

Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView

Post 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");
        }
    }
    */
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 771
Joined: Mon Jul 20, 2026 1:28 pm

Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView

Post 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;
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 771
Joined: Mon Jul 20, 2026 1:28 pm

Re: ICT Silver Bullet EA MT4, MT5, Ctrader and TradingView

Post 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;
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply