Advertisement IC Markets

Forex scalping technique inside Fibonaci zone

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

Forex scalping technique inside Fibonaci zone

Post by PTScalper »

Hi all forex scalpers :-)

Static Fibonacci levels are retail traps. To build a robust algorithm that will actually survive high-frequency backtesting, the retracement logic must be entirely dynamic, calculating swings mathematically in memory.

The "secret sauce" for scalping inside a Fibonacci zone is Tick Volume Exhaustion. When price hits the 61.8% to 78.6% Optimal Trade Entry (OTE) zone, limit orders absorb the liquidity. We can track this invisible hand using the Money Flow Index (MFI) — effectively an RSI injected with tick volume.

Here is the Volume-Weighted OTE setup.

The Algorithmic Mechanics
Macro Trend: 200 EMA to filter out counter-trend noise.

Dynamic Anchors: The EA scans the last 40 bars to find the highest high and lowest low, ensuring the impulse leg is in the direction of the 200 EMA.

The Kill Zone: Memory calculates the 61.8% and 78.6% levels of that dynamic swing. The EA arms itself only when the price enters this zone.

The Secret Sauce (MFI): Inside the Kill Zone, the MFI (Period 3) must drop below 20 (severe tick volume exhaustion/oversold) and then cross back above it to trigger the execution.

The MQL4 Implementation
Because calculating object-based Fibonacci levels visually will crush your CPU during optimization, this script relies strictly on invisible, in-memory state architecture.

Code: Select all

//+------------------------------------------------------------------+
//|                                     VolumeWeightedOTE.mq4        |
//|                                      Strictly for M1/M5 Scalping |
//+------------------------------------------------------------------+
#property strict

//--- Inputs
input double LotSize       = 0.1;
input int    Slippage      = 3;
input int    MagicNumber   = 888888;

//--- Indicator Parameters
input int    SwingLookback = 40;   // Bars to scan for the impulse leg
input int    EmaPeriod     = 200;  // Macro trend filter
input int    MfiPeriod     = 3;    // Hyper-fast tick volume tracking
input double StopLossBuffer= 2.0;  // Pips beyond swing low/high

//--- State Variables
datetime lastBarTime = 0;
double   Pips;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    Pips = Point;
    if(Digits == 3 || Digits == 5) Pips = Point * 10;
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // Execute strictly on the open of a new candle
    if(Time[0] == lastBarTime) return; 

    // 1. Fetch Indicators
    double ema200_1 = iMA(NULL, 0, EmaPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
    double mfi1     = iMFI(NULL, 0, MfiPeriod, 1);
    double mfi2     = iMFI(NULL, 0, MfiPeriod, 2);

    // 2. Locate Dynamic Swing High / Low over the Lookback Period
    int highestIndex = iHighest(NULL, 0, MODE_HIGH, SwingLookback, 1);
    int lowestIndex  = iLowest(NULL, 0, MODE_LOW, SwingLookback, 1);
    
    double swingHigh = High[highestIndex];
    double swingLow  = Low[lowestIndex];
    double swingRange= swingHigh - swingLow;

    // Abort if there is no measurable swing
    if(swingRange == 0) return;

    // 3. Setup Logic Variables
    bool isBuySetup = false;
    bool isSellSetup = false;
    
    double sl = 0;
    double tp = 0;

    // --- LONG LOGIC ---
    // Rule 1: We are in a macro uptrend (Price > 200 EMA)
    // Rule 2: The Lowest point happened BEFORE the Highest point (valid upward impulse leg)
    if(Close[1] > ema200_1 && lowestIndex > highestIndex)
    {
        double fib618 = swingHigh - (swingRange * 0.618);
        double fib786 = swingHigh - (swingRange * 0.786);
        
        // Rule 3: Price closed inside the OTE Zone
        bool inKillZone = (Close[1] <= fib618) && (Close[1] >= fib786);
        
        // Rule 4: Volume Exhaustion (MFI dipped below 20, now crossing up)
        bool mfiTrigger = (mfi2 <= 20) && (mfi1 > 20);

        if(inKillZone && mfiTrigger)
        {
            isBuySetup = true;
            sl = swingLow - (StopLossBuffer * Pips); 
            tp = swingHigh; // Target the top of the impulse leg
        }
    }

    // --- SHORT LOGIC ---
    // Rule 1: We are in a macro downtrend (Price < 200 EMA)
    // Rule 2: The Highest point happened BEFORE the Lowest point (valid downward impulse leg)
    if(Close[1] < ema200_1 && highestIndex > lowestIndex)
    {
        double fib618 = swingLow + (swingRange * 0.618);
        double fib786 = swingLow + (swingRange * 0.786);
        
        // Rule 3: Price closed inside the OTE Zone
        bool inKillZone = (Close[1] >= fib618) && (Close[1] <= fib786);
        
        // Rule 4: Volume Exhaustion (MFI spiked above 80, now crossing down)
        bool mfiTrigger = (mfi2 >= 80) && (mfi1 < 80);

        if(inKillZone && mfiTrigger)
        {
            isSellSetup = true;
            sl = swingHigh + (StopLossBuffer * Pips);
            tp = swingLow; // Target the bottom of the impulse leg
        }
    }

    // 4. Execution Engine
    if(CountOpenPositions() == 0) 
    {
        if(isBuySetup)
        {
            int ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, Slippage, sl, tp, "FibMFI-Buy", MagicNumber, 0, clrDodgerBlue);
            if(ticket > 0) lastBarTime = Time[0];
        }
        else if(isSellSetup)
        {
            int ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, Slippage, sl, tp, "FibMFI-Sell", MagicNumber, 0, clrCrimson);
            if(ticket > 0) lastBarTime = Time[0];
        }
    }
}

//+------------------------------------------------------------------+
//| Helper: Count open positions for this EA                         |
//+------------------------------------------------------------------+
int CountOpenPositions()
{
    int count = 0;
    for(int i = 0; i < OrdersTotal(); i++)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) count++;
        }
    }
    return count;
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping technique inside Fibonaci zone

Post by PTScalper »

I decided to extend this EA.

To survive live market conditions—especially on M1/M5 timeframes—an EA needs strict safety mechanisms. I have refactored your code to make it production-ready.

Key Upgrades Implemented
Price Normalization: Added NormalizeDouble() to SL and TP calculations. MQL4 will often reject OrderSend with Error 130 (Invalid Stops) if prices aren't rounded to the broker's exact digit requirements.

Spread Filter: M1/M5 scalping is heavily dependent on tight spreads. Added a MaxSpread input to block trades if the spread widens, protecting your Risk-to-Reward ratio.

Dynamic Risk Management: Added an AutoLot feature that calculates position size based on a percentage of your account balance and the exact pip distance to your Stop Loss.

Trade Management: Implemented an optional Trailing Stop to lock in profits if price spikes in your favor but fails to hit the full TP.

Error Logging: Replaced silent failures with GetLastError() print statements so you can troubleshoot rejected trades in the Experts tab.

The Refactored MQL4 Code

Code: Select all

//+------------------------------------------------------------------+
//|                                     VolumeWeightedOTE_Pro.mq4    |
//|                                      Strictly for M1/M5 Scalping |
//+------------------------------------------------------------------+
#property strict

//--- Risk & Execution Inputs
input double FixedLotSize  = 0.1;
input bool   UseAutoLot    = false; 
input double RiskPercent   = 1.0;  // % of Balance to risk per trade
input int    Slippage      = 3;
input int    MaxSpreadPips = 2;    // Maximum allowed spread in pips
input int    MagicNumber   = 888888;

//--- Indicator Parameters
input int    SwingLookback = 40;   // Bars to scan for the impulse leg
input int    EmaPeriod     = 200;  // Macro trend filter
input int    MfiPeriod     = 3;    // Hyper-fast tick volume tracking
input double StopLossBuffer= 2.0;  // Pips beyond swing low/high

//--- Trade Management Inputs
input bool   UseTrailing   = true;
input double TrailStartPips= 5.0;  // Pips in profit to start trailing
input double TrailStepPips = 2.0;  // Distance to trail

//--- State Variables
datetime lastBarTime = 0;
double   Pips;
double   TickSize;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    Pips = Point;
    if(Digits == 3 || Digits == 5) Pips = Point * 10;
    TickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
    
    Print("Volume Weighted OTE Initialized. Pips value: ", DoubleToStr(Pips, Digits));
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // 1. Trade Management (Runs every tick)
    if(UseTrailing) ManageTrailingStops();

    // Execute strictly on the open of a new candle for signals
    if(Time[0] == lastBarTime) return; 

    // 2. Spread Check
    double currentSpread = (Ask - Bid) / Pips;
    if(currentSpread > MaxSpreadPips) return; // Spread too high, abort

    // 3. Fetch Indicators
    double ema200_1 = iMA(NULL, 0, EmaPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
    double mfi1     = iMFI(NULL, 0, MfiPeriod, 1);
    double mfi2     = iMFI(NULL, 0, MfiPeriod, 2);

    // 4. Locate Dynamic Swing High / Low over the Lookback Period
    int highestIndex = iHighest(NULL, 0, MODE_HIGH, SwingLookback, 1);
    int lowestIndex  = iLowest(NULL, 0, MODE_LOW, SwingLookback, 1);
    
    double swingHigh = High[highestIndex];
    double swingLow  = Low[lowestIndex];
    double swingRange= swingHigh - swingLow;

    if(swingRange == 0) return;

    // 5. Setup Logic Variables
    bool isBuySetup = false, isSellSetup = false;
    double sl = 0, tp = 0, slDistPips = 0;

    // --- LONG LOGIC ---
    if(Close[1] > ema200_1 && lowestIndex > highestIndex)
    {
        double fib618 = swingHigh - (swingRange * 0.618);
        double fib786 = swingHigh - (swingRange * 0.786);
        
        bool inKillZone = (Close[1] <= fib618) && (Close[1] >= fib786);
        bool mfiTrigger = (mfi2 <= 20) && (mfi1 > 20);

        if(inKillZone && mfiTrigger)
        {
            isBuySetup = true;
            sl = swingLow - (StopLossBuffer * Pips); 
            tp = swingHigh;
            slDistPips = (Ask - sl) / Pips;
        }
    }

    // --- SHORT LOGIC ---
    if(Close[1] < ema200_1 && highestIndex > lowestIndex)
    {
        double fib618 = swingLow + (swingRange * 0.618);
        double fib786 = swingLow + (swingRange * 0.786);
        
        bool inKillZone = (Close[1] >= fib618) && (Close[1] <= fib786);
        bool mfiTrigger = (mfi2 >= 80) && (mfi1 < 80);

        if(inKillZone && mfiTrigger)
        {
            isSellSetup = true;
            sl = swingHigh + (StopLossBuffer * Pips);
            tp = swingLow;
            slDistPips = (sl - Bid) / Pips;
        }
    }

    // 6. Execution Engine
    if(CountOpenPositions() == 0) 
    {
        sl = NormalizeDouble(sl, Digits);
        tp = NormalizeDouble(tp, Digits);
        
        double lotSizeToUse = FixedLotSize;
        if(UseAutoLot && slDistPips > 0) lotSizeToUse = CalculateLotSize(slDistPips);

        if(isBuySetup)
        {
            int ticket = OrderSend(Symbol(), OP_BUY, lotSizeToUse, Ask, Slippage, sl, tp, "FibMFI-Buy", MagicNumber, 0, clrDodgerBlue);
            if(ticket > 0) lastBarTime = Time[0];
            else Print("Buy Order Failed! Error: ", GetLastError());
        }
        else if(isSellSetup)
        {
            int ticket = OrderSend(Symbol(), OP_SELL, lotSizeToUse, Bid, Slippage, sl, tp, "FibMFI-Sell", MagicNumber, 0, clrCrimson);
            if(ticket > 0) lastBarTime = Time[0];
            else Print("Sell Order Failed! Error: ", GetLastError());
        }
    }
}

//+------------------------------------------------------------------+
//| Helper: Calculate Dynamic Lot Size                               |
//+------------------------------------------------------------------+
double CalculateLotSize(double slPips)
{
    double riskAmount = AccountBalance() * (RiskPercent / 100.0);
    double tickValue  = MarketInfo(Symbol(), MODE_TICKVALUE);
    if(tickValue == 0) return FixedLotSize;
    
    double pipValue = tickValue;
    if(Digits == 3 || Digits == 5) pipValue = tickValue * 10;
    
    double calculatedLot = riskAmount / (slPips * pipValue);
    
    double minLot = MarketInfo(Symbol(), MODE_MINLOT);
    double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
    double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
    
    calculatedLot = MathRound(calculatedLot / lotStep) * lotStep;
    if(calculatedLot < minLot) calculatedLot = minLot;
    if(calculatedLot > maxLot) calculatedLot = maxLot;
    
    return calculatedLot;
}

//+------------------------------------------------------------------+
//| Helper: Manage Trailing Stops                                    |
//+------------------------------------------------------------------+
void ManageTrailingStops()
{
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
            {
                if(OrderType() == OP_BUY)
                {
                    if(Bid - OrderOpenPrice() > TrailStartPips * Pips)
                    {
                        double newSL = NormalizeDouble(Bid - (TrailStepPips * Pips), Digits);
                        if(newSL > OrderStopLoss() && newSL < Bid)
                        {
                            bool mod = OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrDodgerBlue);
                        }
                    }
                }
                else if(OrderType() == OP_SELL)
                {
                    if(OrderOpenPrice() - Ask > TrailStartPips * Pips)
                    {
                        double newSL = NormalizeDouble(Ask + (TrailStepPips * Pips), Digits);
                        if(newSL < OrderStopLoss() || OrderStopLoss() == 0)
                        {
                            bool mod = OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrCrimson);
                        }
                    }
                }
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Helper: Count open positions for this EA                         |
//+------------------------------------------------------------------+
int CountOpenPositions()
{
    int count = 0;
    for(int i = 0; i < OrdersTotal(); i++)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) count++;
        }
    }
    return count;
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping technique inside Fibonaci zone

Post by PTScalper »

To take this Expert Advisor from a "good script" to an Institutional/Pro-Grade Algorithmic System, we need to address the realities of live market trading.

Professional EAs do not rely solely on static numbers (like fixed pip buffers). They adapt to volatility, manage executions carefully to avoid broker rejection (ECN/STP compatibility), protect capital instantly when in profit, and only trade when liquidity is highest.

Here is the Pro Version.

The "Pro" Upgrades Added:

1.) ECN/STP Safe Execution: Many A-Book (Pro) brokers reject orders that are sent with a Stop Loss and Take Profit in the initial request. This EA now opens the trade with 0 SL/TP and instantly modifies it.

2.) ATR-Based Dynamic Stop Loss: Instead of a fixed 2-pip buffer, it uses the Average True Range (ATR). If the market is volatile, your stop loss widens automatically to prevent getting wick-hunted.

3.) Session Time Filters: Scalping M1/M5 during the Asian session or roll-over is a death sentence due to spreads. You can now restrict trading strictly to the London/New York overlaps.

4.) Auto Break-Even: Before trailing starts, the EA will instantly move the stop to Entry + Profit Pips as soon as the price moves in your favor, securing a risk-free trade.

5.) On-Chart HUD (Dashboard): A professional on-chart display that reads out real-time spread, trend status, and lot size calculations.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping technique inside Fibonaci zone

Post by PTScalper »

The Refactored Pro-Grade Code:

Code: Select all

//+------------------------------------------------------------------+
//|                                VolumeWeightedOTE_Institutional.mq4 |
//|                                      Strictly for M1/M5 Scalping |
//+------------------------------------------------------------------+
#property strict
#property description "Professional OTE Scalping System"
#property description "Features ECN Execution, ATR Stops & Auto BE"

enum ENUM_LOT_TYPE {
    FIXED_LOT = 0,    // Fixed Lot Size
    AUTO_LOT = 1      // Dynamic Risk %
};

//--- 1. RISK & BROKER SETTINGS
input string               __1__ = "--- Risk & Broker ---";
input ENUM_LOT_TYPE        LotType       = AUTO_LOT;
input double               RiskPercent   = 1.0;      // Risk per trade (%)
input double               FixedLot      = 0.1;
input int                  MaxSpreadPips = 2;        // Max Spread (Pips)
input int                  Slippage      = 3;
input int                  MagicNumber   = 888888;

//--- 2. TRADE LOGIC & ADAPTIVE VOLATILITY
input string               __2__ = "--- Entry & Volatility ---";
input int                  SwingLookback = 40;       // Bars for Impulse Leg
input int                  EmaPeriod     = 200;      // Macro Trend Filter
input int                  MfiPeriod     = 3;        // Tick Volume Tracking
input double               AtrMultiplier = 1.0;      // ATR Multiplier for Stop Buffer

//--- 3. TRADE MANAGEMENT (BE & TRAIL)
input string               __3__ = "--- Management ---";
input bool                 UseBreakEven  = true;
input double               BeTriggerPips = 5.0;      // Pips in profit to trigger BE
input double               BeLockInPips  = 0.5;      // Profit pips to lock in
input bool                 UseTrailing   = true;
input double               TrailStepPips = 2.0;      // Distance to trail behind price

//--- 4. SESSION FILTERS
input string               __4__ = "--- Session Filters ---";
input bool                 UseTimeFilter = true;
input int                  StartHour     = 8;        // Start (Broker Server Time)
input int                  EndHour       = 17;       // End (Broker Server Time)

//--- Global Variables
datetime lastBarTime = 0;
double   Pips;
double   TickSize;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    Pips = Point;
    if(Digits == 3 || Digits == 5) Pips = Point * 10;
    TickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
    
    DrawDashboard();
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    ObjectsDeleteAll(0, "HUD_");
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    UpdateDashboard(); // Refresh on-chart data
    ManageOpenTrades(); // Handle BE and Trailing

    // Strictly execute on a new candle for signals
    if(Time[0] == lastBarTime) return; 

    // Spread and Time Check
    double currentSpread = (Ask - Bid) / Pips;
    if(currentSpread > MaxSpreadPips) return; 
    if(UseTimeFilter && !IsTradingSession()) return;

    // Indicators
    double ema200 = iMA(NULL, 0, EmaPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
    double mfi1   = iMFI(NULL, 0, MfiPeriod, 1);
    double mfi2   = iMFI(NULL, 0, MfiPeriod, 2);
    double atr    = iATR(NULL, 0, 14, 1); // 14-period ATR for volatility buffer

    // Price Action Setup
    int highestIndex = iHighest(NULL, 0, MODE_HIGH, SwingLookback, 1);
    int lowestIndex  = iLowest(NULL, 0, MODE_LOW, SwingLookback, 1);
    
    double swingHigh = High[highestIndex];
    double swingLow  = Low[lowestIndex];
    double swingRange = swingHigh - swingLow;
    if(swingRange == 0) return;

    bool isBuy = false, isSell = false;
    double sl = 0, tp = 0;
    double atrBuffer = atr * AtrMultiplier;

    // --- LONG LOGIC ---
    if(Close[1] > ema200 && lowestIndex > highestIndex)
    {
        double fib618 = swingHigh - (swingRange * 0.618);
        double fib786 = swingHigh - (swingRange * 0.786);
        
        if(Close[1] <= fib618 && Close[1] >= fib786 && mfi2 <= 20 && mfi1 > 20)
        {
            isBuy = true;
            sl = swingLow - atrBuffer; // Volatility-adapted SL
            tp = swingHigh;
        }
    }

    // --- SHORT LOGIC ---
    if(Close[1] < ema200 && highestIndex > lowestIndex)
    {
        double fib618 = swingLow + (swingRange * 0.618);
        double fib786 = swingLow + (swingRange * 0.786);
        
        if(Close[1] >= fib618 && Close[1] <= fib786 && mfi2 >= 80 && mfi1 < 80)
        {
            isSell = true;
            sl = swingHigh + atrBuffer; // Volatility-adapted SL
            tp = swingLow;
        }
    }

    // --- ECN/STP EXECUTION ENGINE ---
    if(CountOpenPositions() == 0) 
    {
        if(isBuy || isSell)
        {
            sl = NormalizeDouble(sl, Digits);
            tp = NormalizeDouble(tp, Digits);
            
            double slDistPips = isBuy ? (Ask - sl) / Pips : (sl - Bid) / Pips;
            double lotSize = (LotType == AUTO_LOT) ? CalculateLotSize(slDistPips) : FixedLot;

            // ECN Brokers require opening with 0 SL/TP first
            int cmd = isBuy ? OP_BUY : OP_SELL;
            double openPrice = isBuy ? Ask : Bid;
            color arrColor = isBuy ? clrDodgerBlue : clrCrimson;

            int ticket = OrderSend(Symbol(), cmd, lotSize, openPrice, Slippage, 0, 0, "Pro-OTE", MagicNumber, 0, arrColor);
            
            if(ticket > 0) 
            {
                lastBarTime = Time[0];
                if(OrderSelect(ticket, SELECT_BY_TICKET))
                {
                    // Modify SL and TP immediately after successful open
                    bool mod = OrderModify(ticket, OrderOpenPrice(), sl, tp, 0, arrColor);
                    if(!mod) Print("Failed to modify SL/TP. Error: ", GetLastError());
                }
            }
            else Print("OrderSend Error: ", GetLastError());
        }
    }
}

//+------------------------------------------------------------------+
//| Helper: ECN Safe Trailing & Break Even Management                |
//+------------------------------------------------------------------+
void ManageOpenTrades()
{
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
            {
                double openPrice = OrderOpenPrice();
                double currentSL = OrderStopLoss();
                
                // Break Even Logic
                if(UseBreakEven)
                {
                    if(OrderType() == OP_BUY && Bid - openPrice >= BeTriggerPips * Pips)
                    {
                        double targetBE = NormalizeDouble(openPrice + (BeLockInPips * Pips), Digits);
                        if(currentSL < targetBE) 
                            OrderModify(OrderTicket(), openPrice, targetBE, OrderTakeProfit(), 0, clrGreen);
                    }
                    else if(OrderType() == OP_SELL && openPrice - Ask >= BeTriggerPips * Pips)
                    {
                        double targetBE = NormalizeDouble(openPrice - (BeLockInPips * Pips), Digits);
                        if(currentSL > targetBE || currentSL == 0) 
                            OrderModify(OrderTicket(), openPrice, targetBE, OrderTakeProfit(), 0, clrGreen);
                    }
                }
                
                // Trailing Stop Logic (Only applies AFTER Break Even)
                if(UseTrailing)
                {
                    if(OrderType() == OP_BUY && currentSL >= openPrice) // Ensure trade is risk-free first
                    {
                        double newSL = NormalizeDouble(Bid - (TrailStepPips * Pips), Digits);
                        if(newSL > currentSL)
                            OrderModify(OrderTicket(), openPrice, newSL, OrderTakeProfit(), 0, clrBlue);
                    }
                    else if(OrderType() == OP_SELL && (currentSL <= openPrice && currentSL != 0))
                    {
                        double newSL = NormalizeDouble(Ask + (TrailStepPips * Pips), Digits);
                        if(newSL < currentSL)
                            OrderModify(OrderTicket(), openPrice, newSL, OrderTakeProfit(), 0, clrRed);
                    }
                }
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Helper: Calculate Dynamic Lot Size                               |
//+------------------------------------------------------------------+
double CalculateLotSize(double slPips)
{
    if(slPips <= 0) return MarketInfo(Symbol(), MODE_MINLOT);
    
    double riskAmount = AccountBalance() * (RiskPercent / 100.0);
    double tickValue  = MarketInfo(Symbol(), MODE_TICKVALUE);
    if(tickValue == 0) return FixedLot;
    
    double pipValue = (Digits == 3 || Digits == 5) ? tickValue * 10 : tickValue;
    double calculatedLot = riskAmount / (slPips * pipValue);
    
    double minLot = MarketInfo(Symbol(), MODE_MINLOT);
    double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
    double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
    
    calculatedLot = MathRound(calculatedLot / lotStep) * lotStep;
    return MathMax(minLot, MathMin(maxLot, calculatedLot));
}

//+------------------------------------------------------------------+
//| Helper: Session Filter                                           |
//+------------------------------------------------------------------+
bool IsTradingSession()
{
    int currentHour = TimeHour(TimeCurrent());
    if(StartHour < EndHour) return (currentHour >= StartHour && currentHour < EndHour);
    else return (currentHour >= StartHour || currentHour < EndHour); // Overnight session handler
}

int CountOpenPositions()
{
    int count = 0;
    for(int i = 0; i < OrdersTotal(); i++)
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) count++;
    return count;
}

//+------------------------------------------------------------------+
//| Pro-Grade UI Dashboard                                           |
//+------------------------------------------------------------------+
void DrawDashboard()
{
    ObjectCreate(0, "HUD_Bg", OBJ_LABEL, 0, 0, 0);
    ObjectSetInteger(0, "HUD_Bg", OBJPROP_CORNER, CORNER_LEFT_UPPER);
    ObjectSetInteger(0, "HUD_Bg", OBJPROP_XDISTANCE, 10);
    ObjectSetInteger(0, "HUD_Bg", OBJPROP_YDISTANCE, 20);
    
    for(int i = 0; i < 4; i++) {
        string name = "HUD_L" + IntegerToString(i);
        ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
        ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
        ObjectSetInteger(0, name, OBJPROP_XDISTANCE, 15);
        ObjectSetInteger(0, name, OBJPROP_YDISTANCE, 20 + (i * 15));
        ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 8);
        ObjectSetString(0, name, OBJPROP_FONT, "Trebuchet MS");
    }
}

void UpdateDashboard()
{
    double spread = (Ask - Bid) / Pips;
    bool sessionActive = (!UseTimeFilter || IsTradingSession());
    
    ObjectSetString(0, "HUD_L0", OBJPROP_TEXT, "■ INSTITUTIONAL OTE SCALPER ■");
    ObjectSetString(0, "HUD_L0", OBJPROP_COLOR, clrSilver);
    
    ObjectSetString(0, "HUD_L1", OBJPROP_TEXT, "Spread: " + DoubleToStr(spread, 1) + " Pips | Max: " + IntegerToString(MaxSpreadPips));
    ObjectSetString(0, "HUD_L1", OBJPROP_COLOR, (spread <= MaxSpreadPips) ? clrLimeGreen : clrRed);
    
    ObjectSetString(0, "HUD_L2", OBJPROP_TEXT, "Trading Session: " + (sessionActive ? "ACTIVE" : "CLOSED"));
    ObjectSetString(0, "HUD_L2", OBJPROP_COLOR, sessionActive ? clrLimeGreen : clrOrange);
    
    ObjectSetString(0, "HUD_L3", OBJPROP_TEXT, "Trend (200 EMA): " + (Close[0] > iMA(NULL,0,200,0,MODE_EMA,PRICE_CLOSE,0) ? "BULLISH" : "BEARISH"));
    ObjectSetString(0, "HUD_L3", OBJPROP_COLOR, clrWhite);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping technique inside Fibonaci zone

Post by PTScalper »

How to optimize this for the best results:

1.) Never test M1 scalping on standard broker data. You must use a tool like Tick Data Suite (TDS) or QuantDataManager to download 99.9% real tick data with variable spreads. M1/M5 strategies backtested on MT4's default 90% modeling quality will lie to you.

2.) Session Timings: Check your broker's server time (look at Market Watch). Set the StartHour to the London Open and EndHour to the New York close.

3.) ATR Stop tuning: Start by testing the AtrMultiplier at 1.0 or 1.5. This prevents you from getting tagged out by normal M1 noise before the price runs in your direction.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping technique inside Fibonaci zone

Post by PTScalper »

Here is the MetaTrader 5 (MQL5) version of the Institutional OTE Scalper.

1.) Moving from MT4 to MT5 requires a complete architectural shift. MT5 uses Object-Oriented Programming for trading (CTrade), requires Indicator Handles initialized in OnInit(), and copies price/indicator data into arrays during OnTick().

2.) Key Upgrades for MT5:
Natively ECN/STP Compliant: Unlike MT4, MT5's trade server architecture natively handles SL/TP splitting for ECN brokers. We can safely send the Stop Loss and Take Profit directly in the opening order without needing the 2-step "0 SL/TP" workaround.

3.) Handle-Based Indicators: Optimized memory usage using iMA, iMFI, and iATR handles combined with CopyBuffer().

4.) Array Series Logic: Recreated MT4's reverse-time indexing (where bar 1 is the previous closed candle) using ArraySetAsSeries() to perfectly preserve your swing high/low logic.

4.) Standard Library Utilization: Uses the built-in <Trade\Trade.mqh> class for institutional-grade execution speed.

The MQL5 Code

Code: Select all

//+------------------------------------------------------------------+
//|                                VolumeWeightedOTE_Institutional.mq5 |
//|                                      Strictly for M1/M5 Scalping |
//+------------------------------------------------------------------+
#property description "Professional OTE Scalping System for MT5"
#property description "Features ATR Stops, Auto BE, and CTrade Execution"
#property version   "2.0"

#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\PositionInfo.mqh>

enum ENUM_LOT_TYPE {
    FIXED_LOT = 0,    // Fixed Lot Size
    AUTO_LOT = 1      // Dynamic Risk %
};

//--- 1. RISK & BROKER SETTINGS
input group "--- Risk & Broker ---"
input ENUM_LOT_TYPE        LotType       = AUTO_LOT;
input double               RiskPercent   = 1.0;      // Risk per trade (%)
input double               FixedLot      = 0.1;
input int                  MaxSpreadPips = 2;        // Max Spread (Pips)
input ulong                Slippage      = 3;
input ulong                MagicNumber   = 888888;

//--- 2. TRADE LOGIC & ADAPTIVE VOLATILITY
input group "--- Entry & Volatility ---"
input int                  SwingLookback = 40;       // Bars for Impulse Leg
input int                  EmaPeriod     = 200;      // Macro Trend Filter
input int                  MfiPeriod     = 3;        // Tick Volume Tracking
input double               AtrMultiplier = 1.0;      // ATR Multiplier for Stop Buffer

//--- 3. TRADE MANAGEMENT (BE & TRAIL)
input group "--- Management ---"
input bool                 UseBreakEven  = true;
input double               BeTriggerPips = 5.0;      // Pips in profit to trigger BE
input double               BeLockInPips  = 0.5;      // Profit pips to lock in
input bool                 UseTrailing   = true;
input double               TrailStepPips = 2.0;      // Distance to trail behind price

//--- 4. SESSION FILTERS
input group "--- Session Filters ---"
input bool                 UseTimeFilter = true;
input int                  StartHour     = 8;        // Start (Broker Server Time)
input int                  EndHour       = 17;       // End (Broker Server Time)

//--- Global Variables & Objects
CTrade         trade;
CSymbolInfo    symInfo;
CPositionInfo  posInfo;

datetime       lastBarTime = 0;
double         Pips;

// Indicator Handles
int            emaHandle;
int            mfiHandle;
int            atrHandle;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    symInfo.Name(_Symbol);
    symInfo.Refresh();
    
    // Define Pip value (10 points for 5/3 digit brokers, 1 for 4/2 digit)
    Pips = symInfo.Point();
    if(symInfo.Digits() == 3 || symInfo.Digits() == 5) Pips = symInfo.Point() * 10;
    
    // Setup Trade Class
    trade.SetExpertMagicNumber(MagicNumber);
    trade.SetDeviationInPoints(Slippage);
    trade.SetTypeFilling(ORDER_FILLING_FOK); // Fill or Kill (Best for Scalping)

    // Initialize Indicator Handles
    emaHandle = iMA(_Symbol, _Period, EmaPeriod, 0, MODE_EMA, PRICE_CLOSE);
    mfiHandle = iMFI(_Symbol, _Period, MfiPeriod, VOLUME_TICK);
    atrHandle = iATR(_Symbol, _Period, 14);

    if(emaHandle == INVALID_HANDLE || mfiHandle == INVALID_HANDLE || atrHandle == INVALID_HANDLE)
    {
        Print("Failed to load indicators!");
        return(INIT_FAILED);
    }

    DrawDashboard();
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    IndicatorRelease(emaHandle);
    IndicatorRelease(mfiHandle);
    IndicatorRelease(atrHandle);
    ObjectsDeleteAll(0, "HUD_");
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    symInfo.RefreshRates();
    UpdateDashboard();
    ManageOpenTrades();

    // Execute strictly on a new candle for signals
    datetime timeArray[];
    if(CopyTime(_Symbol, _Period, 0, 1, timeArray) <= 0) return;
    if(timeArray[0] == lastBarTime) return; 

    // Spread and Time Check
    double currentSpread = (symInfo.Ask() - symInfo.Bid()) / Pips;
    if(currentSpread > MaxSpreadPips) return; 
    if(UseTimeFilter && !IsTradingSession()) return;

    // --- Fetch Indicator Data ---
    double ema[], mfi[], atr[], close[];
    if(CopyBuffer(emaHandle, 0, 1, 1, ema) <= 0) return;
    if(CopyBuffer(atrHandle, 0, 1, 1, atr) <= 0) return;
    if(CopyBuffer(mfiHandle, 0, 1, 2, mfi) <= 0) return; // Gets shift 1 & 2
    if(CopyClose(_Symbol, _Period, 1, 1, close) <= 0) return;
    
    ArraySetAsSeries(mfi, true); // mfi[0] becomes shift 1, mfi[1] is shift 2

    // --- Fetch Price Action (Highs/Lows) ---
    double high[], low[];
    if(CopyHigh(_Symbol, _Period, 1, SwingLookback, high) <= 0) return;
    if(CopyLow(_Symbol, _Period, 1, SwingLookback, low) <= 0) return;
    
    ArraySetAsSeries(high, true);
    ArraySetAsSeries(low, true);
    
    int highestIndex = ArrayMaximum(high, 0, SwingLookback);
    int lowestIndex  = ArrayMinimum(low, 0, SwingLookback);
    
    double swingHigh = high[highestIndex];
    double swingLow  = low[lowestIndex];
    double swingRange = swingHigh - swingLow;
    
    if(swingRange == 0) return;

    bool isBuy = false, isSell = false;
    double sl = 0, tp = 0;
    double atrBuffer = atr[0] * AtrMultiplier;

    // --- LONG LOGIC ---
    if(close[0] > ema[0] && lowestIndex > highestIndex)
    {
        double fib618 = swingHigh - (swingRange * 0.618);
        double fib786 = swingHigh - (swingRange * 0.786);
        
        if(close[0] <= fib618 && close[0] >= fib786 && mfi[1] <= 20 && mfi[0] > 20)
        {
            isBuy = true;
            sl = swingLow - atrBuffer;
            tp = swingHigh;
        }
    }

    // --- SHORT LOGIC ---
    if(close[0] < ema[0] && highestIndex > lowestIndex)
    {
        double fib618 = swingLow + (swingRange * 0.618);
        double fib786 = swingLow + (swingRange * 0.786);
        
        if(close[0] >= fib618 && close[0] <= fib786 && mfi[1] >= 80 && mfi[0] < 80)
        {
            isSell = true;
            sl = swingHigh + atrBuffer;
            tp = swingLow;
        }
    }

    // --- EXECUTION ENGINE ---
    if(CountOpenPositions() == 0) 
    {
        if(isBuy || isSell)
        {
            sl = NormalizeDouble(sl, symInfo.Digits());
            tp = NormalizeDouble(tp, symInfo.Digits());
            
            double slDistPips = isBuy ? (symInfo.Ask() - sl) / Pips : (sl - symInfo.Bid()) / Pips;
            double lotSize = (LotType == AUTO_LOT) ? CalculateLotSize(slDistPips) : FixedLot;

            bool orderResult = false;
            
            if(isBuy)
                orderResult = trade.Buy(lotSize, _Symbol, symInfo.Ask(), sl, tp, "Pro-OTE Buy");
            else
                orderResult = trade.Sell(lotSize, _Symbol, symInfo.Bid(), sl, tp, "Pro-OTE Sell");
            
            if(orderResult) 
            {
                lastBarTime = timeArray[0];
                Print("Position Opened Successfully: ", trade.ResultDeal());
            }
            else 
            {
                Print("Order Failed! Result Retcode: ", trade.ResultRetcode(), " | Description: ", trade.ResultRetcodeDescription());
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Helper: Trailing & Break Even Management                         |
//+------------------------------------------------------------------+
void ManageOpenTrades()
{
    for(int i = PositionsTotal() - 1; i >= 0; i--)
    {
        if(posInfo.SelectByIndex(i))
        {
            if(posInfo.Symbol() == _Symbol && posInfo.Magic() == MagicNumber)
            {
                double openPrice = posInfo.PriceOpen();
                double currentSL = posInfo.StopLoss();
                double currentTP = posInfo.TakeProfit();
                
                // Break Even Logic
                if(UseBreakEven)
                {
                    if(posInfo.PositionType() == POSITION_TYPE_BUY && symInfo.Bid() - openPrice >= BeTriggerPips * Pips)
                    {
                        double targetBE = NormalizeDouble(openPrice + (BeLockInPips * Pips), symInfo.Digits());
                        if(currentSL < targetBE) 
                            trade.PositionModify(posInfo.Ticket(), targetBE, currentTP);
                    }
                    else if(posInfo.PositionType() == POSITION_TYPE_SELL && openPrice - symInfo.Ask() >= BeTriggerPips * Pips)
                    {
                        double targetBE = NormalizeDouble(openPrice - (BeLockInPips * Pips), symInfo.Digits());
                        if(currentSL > targetBE || currentSL == 0) 
                            trade.PositionModify(posInfo.Ticket(), targetBE, currentTP);
                    }
                }
                
                // Trailing Stop Logic (Applies AFTER Break Even)
                if(UseTrailing)
                {
                    if(posInfo.PositionType() == POSITION_TYPE_BUY && currentSL >= openPrice) 
                    {
                        double newSL = NormalizeDouble(symInfo.Bid() - (TrailStepPips * Pips), symInfo.Digits());
                        if(newSL > currentSL)
                            trade.PositionModify(posInfo.Ticket(), newSL, currentTP);
                    }
                    else if(posInfo.PositionType() == POSITION_TYPE_SELL && (currentSL <= openPrice && currentSL != 0))
                    {
                        double newSL = NormalizeDouble(symInfo.Ask() + (TrailStepPips * Pips), symInfo.Digits());
                        if(newSL < currentSL)
                            trade.PositionModify(posInfo.Ticket(), newSL, currentTP);
                    }
                }
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Helper: Calculate Dynamic Lot Size for MT5                       |
//+------------------------------------------------------------------+
double CalculateLotSize(double slPips)
{
    if(slPips <= 0) return symInfo.LotMin();
    
    double riskAmount = AccountInfoDouble(ACCOUNT_BALANCE) * (RiskPercent / 100.0);
    double tickValue  = symInfo.TickValue();
    double tickSize   = symInfo.TickSize();
    
    if(tickValue == 0 || tickSize == 0) return FixedLot;
    
    double slPoints = slPips * (Pips / symInfo.Point());
    double lossPerLot = (slPoints * symInfo.Point() / tickSize) * tickValue;
    
    if(lossPerLot == 0) return FixedLot;
    
    double calculatedLot = riskAmount / lossPerLot;
    
    double minLot  = symInfo.LotMin();
    double maxLot  = symInfo.LotMax();
    double lotStep = symInfo.LotStep();
    
    calculatedLot = MathRound(calculatedLot / lotStep) * lotStep;
    return MathMax(minLot, MathMin(maxLot, calculatedLot));
}

//+------------------------------------------------------------------+
//| Helper: Session Filter                                           |
//+------------------------------------------------------------------+
bool IsTradingSession()
{
    MqlDateTime dt;
    TimeCurrent(dt);
    int currentHour = dt.hour;
    if(StartHour < EndHour) return (currentHour >= StartHour && currentHour < EndHour);
    else return (currentHour >= StartHour || currentHour < EndHour); 
}

int CountOpenPositions()
{
    int count = 0;
    for(int i = 0; i < PositionsTotal(); i++)
        if(posInfo.SelectByIndex(i))
            if(posInfo.Symbol() == _Symbol && posInfo.Magic() == MagicNumber) count++;
    return count;
}

//+------------------------------------------------------------------+
//| Pro-Grade UI Dashboard                                           |
//+------------------------------------------------------------------+
void DrawDashboard()
{
    ObjectCreate(0, "HUD_Bg", OBJ_LABEL, 0, 0, 0);
    ObjectSetInteger(0, "HUD_Bg", OBJPROP_CORNER, CORNER_LEFT_UPPER);
    ObjectSetInteger(0, "HUD_Bg", OBJPROP_XDISTANCE, 10);
    ObjectSetInteger(0, "HUD_Bg", OBJPROP_YDISTANCE, 20);
    
    for(int i = 0; i < 4; i++) {
        string name = "HUD_L" + IntegerToString(i);
        ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
        ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
        ObjectSetInteger(0, name, OBJPROP_XDISTANCE, 15);
        ObjectSetInteger(0, name, OBJPROP_YDISTANCE, 20 + (i * 15));
        ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 9);
        ObjectSetString(0, name, OBJPROP_FONT, "Trebuchet MS");
    }
}

void UpdateDashboard()
{
    double spread = (symInfo.Ask() - symInfo.Bid()) / Pips;
    bool sessionActive = (!UseTimeFilter || IsTradingSession());
    
    double ema[];
    string trend = "UNKNOWN";
    if(CopyBuffer(emaHandle, 0, 0, 1, ema) > 0)
        trend = (symInfo.Bid() > ema[0]) ? "BULLISH" : "BEARISH";
    
    ObjectSetString(0, "HUD_L0", OBJPROP_TEXT, "■ MT5 INSTITUTIONAL OTE SCALPER ■");
    ObjectSetInteger(0, "HUD_L0", OBJPROP_COLOR, clrSilver);
    
    ObjectSetString(0, "HUD_L1", OBJPROP_TEXT, "Spread: " + DoubleToString(spread, 1) + " Pips | Max: " + IntegerToString(MaxSpreadPips));
    ObjectSetInteger(0, "HUD_L1", OBJPROP_COLOR, (spread <= MaxSpreadPips) ? clrLimeGreen : clrRed);
    
    ObjectSetString(0, "HUD_L2", OBJPROP_TEXT, "Trading Session: " + (string)(sessionActive ? "ACTIVE" : "CLOSED"));
    ObjectSetInteger(0, "HUD_L2", OBJPROP_COLOR, sessionActive ? clrLimeGreen : clrOrange);
    
    ObjectSetString(0, "HUD_L3", OBJPROP_TEXT, "Trend (200 EMA): " + trend);
    ObjectSetInteger(0, "HUD_L3", OBJPROP_COLOR, clrWhite);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping technique inside Fibonaci zone

Post by PTScalper »

Critical MT5 Details to Note:

ORDER_FILLING_FOK: In the OnInit() function, the trade type is set to Fill Or Kill (ORDER_FILLING_FOK), which is mandatory for scalping algorithms to avoid partial fills at worse prices.

ResultRetcode: If an order fails, MT5 doesn't just give you a generic error number; it will print trade.ResultRetcodeDescription() directly to your Experts tab, telling you exactly why the server rejected the trade (e.g., "Not enough money", "Invalid Stops", or "Market Closed").

ArraySetAsSeries: MT5 arrays fill from oldest to newest by default. I have explicitly applied ArraySetAsSeries() to the MFI, High, and Low arrays to perfectly mirror how your MQL4 loop assessed older vs. newer bars to track that impulse swing leg successfully.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping technique inside Fibonaci zone

Post by PTScalper »

To transition this algorithm to cTrader (using C# and the cAlgo API), we move to a deeply object-oriented, event-driven environment. cTrader is highly regarded by algorithmic traders for its modern API, natively built-in ECN handling, and granular volume calculations (Units vs. Lots).

Key Upgrades for cTrader (C#):

1.) OnBar() vs OnTick(): cTrader natively splits tick logic and bar logic. The trade entry signals are strictly placed inside OnBar() (which runs only once per candle open), while dynamic trade management (Break-Even and Trailing Stop) is safely placed in OnTick().

2.) Volume In Units: Unlike MetaTrader, which uses "Lots," cTrader handles order size in absolute Units (e.g., 100,000 = 1 Standard Lot). The Auto-Lot algorithm here calculates the exact raw units based on risk percentage, then utilizes Symbol.NormalizeVolumeInUnits() to snap it perfectly to the broker’s allowed minimums and steps.

3.) Labels instead of Magic Numbers: cTrader tracks algorithmic positions through string Labels rather than integers. This acts exactly like a Magic Number but is more human-readable.

4.) C# LINQ Integration: Loop hunting for old trades is replaced by elegant, lightning-fast C# LINQ queries Positions.Where(p => p.Label == TradeLabel).

The cTrader cBot Code (C#)

Open cTrader Automate, create a new cBot named VolumeWeightedOTE_Institutional, and replace the default code with this:

Code: Select all

using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class VolumeWeightedOTE_Institutional : Robot
    {
        public enum LotTypeEnum { FixedVolume, AutoRisk }

        // --- 1. RISK & BROKER SETTINGS ---
        [Parameter("Lot Type", Group = "Risk & Broker", DefaultValue = LotTypeEnum.AutoRisk)]
        public LotTypeEnum LotType { get; set; }

        [Parameter("Risk Percent (%)", Group = "Risk & Broker", DefaultValue = 1.0, MinValue = 0.1)]
        public double RiskPercent { get; set; }

        [Parameter("Fixed Volume (Units)", Group = "Risk & Broker", DefaultValue = 1000)]
        public double FixedVolume { get; set; }

        [Parameter("Max Spread (Pips)", Group = "Risk & Broker", DefaultValue = 2.0)]
        public double MaxSpreadPips { get; set; }
        
        [Parameter("Trade Label", Group = "Risk & Broker", DefaultValue = "Pro-OTE")]
        public string TradeLabel { get; set; }

        // --- 2. TRADE LOGIC & ADAPTIVE VOLATILITY ---
        [Parameter("Swing Lookback", Group = "Entry & Volatility", DefaultValue = 40)]
        public int SwingLookback { get; set; }

        [Parameter("EMA Period", Group = "Entry & Volatility", DefaultValue = 200)]
        public int EmaPeriod { get; set; }

        [Parameter("MFI Period", Group = "Entry & Volatility", DefaultValue = 3)]
        public int MfiPeriod { get; set; }

        [Parameter("ATR Multiplier", Group = "Entry & Volatility", DefaultValue = 1.0)]
        public double AtrMultiplier { get; set; }

        // --- 3. TRADE MANAGEMENT (BE & TRAIL) ---
        [Parameter("Use Break Even", Group = "Management", DefaultValue = true)]
        public bool UseBreakEven { get; set; }

        [Parameter("BE Trigger (Pips)", Group = "Management", DefaultValue = 5.0)]
        public double BeTriggerPips { get; set; }

        [Parameter("BE Lock-In (Pips)", Group = "Management", DefaultValue = 0.5)]
        public double BeLockInPips { get; set; }

        [Parameter("Use Trailing Stop", Group = "Management", DefaultValue = true)]
        public bool UseTrailing { get; set; }

        [Parameter("Trail Step (Pips)", Group = "Management", DefaultValue = 2.0)]
        public double TrailStepPips { get; set; }

        // --- 4. SESSION FILTERS ---
        [Parameter("Use Time Filter", Group = "Session Filters", DefaultValue = true)]
        public bool UseTimeFilter { get; set; }

        [Parameter("Start Hour (Server Time)", Group = "Session Filters", DefaultValue = 8)]
        public int StartHour { get; set; }

        [Parameter("End Hour (Server Time)", Group = "Session Filters", DefaultValue = 17)]
        public int EndHour { get; set; }

        // --- Indicators ---
        private ExponentialMovingAverage _ema200;
        private MoneyFlowIndex _mfi;
        private AverageTrueRange _atr;

        protected override void OnStart()
        {
            _ema200 = Indicators.ExponentialMovingAverage(Bars.ClosePrices, EmaPeriod);
            _mfi = Indicators.MoneyFlowIndex(MfiPeriod);
            _atr = Indicators.AverageTrueRange(14, MovingAverageType.Simple);
            
            DrawDashboard();
        }

        protected override void OnTick()
        {
            UpdateDashboard();
            ManageOpenTrades();
        }

        protected override void OnBar()
        {
            // Spread and Time Check
            double currentSpread = Symbol.Spread / Symbol.PipSize;
            if (currentSpread > MaxSpreadPips) return;
            if (UseTimeFilter && !IsTradingSession()) return;

            // Enforce max 1 position per symbol for this bot
            if (Positions.Count(p => p.Label == TradeLabel && p.SymbolName == SymbolName) > 0) return;

            // Price Action Loop: Find dynamic swing Highs & Lows
            int highestIndex = -1, lowestIndex = -1;
            double swingHigh = double.MinValue, swingLow = double.MaxValue;

            for (int i = 1; i <= SwingLookback; i++)
            {
                if (Bars.HighPrices.Last(i) > swingHigh)
                {
                    swingHigh = Bars.HighPrices.Last(i);
                    highestIndex = i; // Higher 'i' means older in time
                }
                if (Bars.LowPrices.Last(i) < swingLow)
                {
                    swingLow = Bars.LowPrices.Last(i);
                    lowestIndex = i;
                }
            }

            double swingRange = swingHigh - swingLow;
            if (swingRange == 0) return;

            // Fetch Indicator Data (Last(1) is the previous closed candle)
            double close1 = Bars.ClosePrices.Last(1);
            double ema1 = _ema200.Result.Last(1);
            double mfi1 = _mfi.Result.Last(1);
            double mfi2 = _mfi.Result.Last(2);
            double atrBuffer = _atr.Result.Last(1) * AtrMultiplier;

            bool isBuy = false, isSell = false;
            double sl = 0, tp = 0;

            // --- LONG LOGIC ---
            // Lowest index > highest index means the low happened *before* the high (Uptrend Impulse)
            if (close1 > ema1 && lowestIndex > highestIndex)
            {
                double fib618 = swingHigh - (swingRange * 0.618);
                double fib786 = swingHigh - (swingRange * 0.786);

                if (close1 <= fib618 && close1 >= fib786 && mfi2 <= 20 && mfi1 > 20)
                {
                    isBuy = true;
                    sl = swingLow - atrBuffer;
                    tp = swingHigh;
                }
            }

            // --- SHORT LOGIC ---
            // Highest index > lowest index means the high happened *before* the low (Downtrend Impulse)
            if (close1 < ema1 && highestIndex > lowestIndex)
            {
                double fib618 = swingLow + (swingRange * 0.618);
                double fib786 = swingLow + (swingRange * 0.786);

                if (close1 >= fib618 && close1 <= fib786 && mfi2 >= 80 && mfi1 < 80)
                {
                    isSell = true;
                    sl = swingHigh + atrBuffer;
                    tp = swingLow;
                }
            }

            // --- EXECUTION ENGINE ---
            if (isBuy || isSell)
            {
                double slPips = Math.Abs((isBuy ? Symbol.Ask : Symbol.Bid) - sl) / Symbol.PipSize;
                double volume = (LotType == LotTypeEnum.AutoRisk) ? CalculateVolume(slPips) : Symbol.NormalizeVolumeInUnits(FixedVolume, RoundingMode.Down);
                
                TradeType direction = isBuy ? TradeType.Buy : TradeType.Sell;
                double tpPips = Math.Abs((isBuy ? Symbol.Ask : Symbol.Bid) - tp) / Symbol.PipSize;

                ExecuteMarketOrder(direction, SymbolName, volume, TradeLabel, slPips, tpPips);
            }
        }

        //+------------------------------------------------------------------+
        //| Helper: ECN Safe Trailing & Break Even Management                |
        //+------------------------------------------------------------------+
        private void ManageOpenTrades()
        {
            var botPositions = Positions.Where(p => p.SymbolName == SymbolName && p.Label == TradeLabel);

            foreach (var pos in botPositions)
            {
                // Break Even Logic
                if (UseBreakEven)
                {
                    if (pos.TradeType == TradeType.Buy && Symbol.Bid - pos.EntryPrice >= BeTriggerPips * Symbol.PipSize)
                    {
                        double targetBE = Math.Round(pos.EntryPrice + (BeLockInPips * Symbol.PipSize), Symbol.Digits);
                        if (pos.StopLoss == null || pos.StopLoss < targetBE)
                            ModifyPosition(pos, targetBE, pos.TakeProfit);
                    }
                    else if (pos.TradeType == TradeType.Sell && pos.EntryPrice - Symbol.Ask >= BeTriggerPips * Symbol.PipSize)
                    {
                        double targetBE = Math.Round(pos.EntryPrice - (BeLockInPips * Symbol.PipSize), Symbol.Digits);
                        if (pos.StopLoss == null || pos.StopLoss > targetBE)
                            ModifyPosition(pos, targetBE, pos.TakeProfit);
                    }
                }

                // Trailing Stop Logic (Only applies AFTER Break Even)
                if (UseTrailing)
                {
                    if (pos.TradeType == TradeType.Buy && pos.StopLoss >= pos.EntryPrice)
                    {
                        double newSL = Math.Round(Symbol.Bid - (TrailStepPips * Symbol.PipSize), Symbol.Digits);
                        if (newSL > pos.StopLoss)
                            ModifyPosition(pos, newSL, pos.TakeProfit);
                    }
                    else if (pos.TradeType == TradeType.Sell && (pos.StopLoss <= pos.EntryPrice && pos.StopLoss != null))
                    {
                        double newSL = Math.Round(Symbol.Ask + (TrailStepPips * Symbol.PipSize), Symbol.Digits);
                        if (newSL < pos.StopLoss)
                            ModifyPosition(pos, newSL, pos.TakeProfit);
                    }
                }
            }
        }

        //+------------------------------------------------------------------+
        //| Helper: Calculate Dynamic Volume (Units)                         |
        //+------------------------------------------------------------------+
        private double CalculateVolume(double slPips)
        {
            if (slPips <= 0) return Symbol.VolumeInUnitsMin;

            double riskAmount = Account.Balance * (RiskPercent / 100.0);
            
            // Value of 1 pip for 1 unit of volume
            double pipValuePerUnit = Symbol.PipValue; 

            if (pipValuePerUnit == 0) return Symbol.NormalizeVolumeInUnits(FixedVolume, RoundingMode.Down);

            // Calculate exact volume
            double exactVolume = riskAmount / (slPips * pipValuePerUnit);

            // Return cleanly normalized volume conforming to broker steps
            return Symbol.NormalizeVolumeInUnits(exactVolume, RoundingMode.Down);
        }

        //+------------------------------------------------------------------+
        //| Helper: Session Filter                                           |
        //+------------------------------------------------------------------+
        private bool IsTradingSession()
        {
            int currentHour = Server.Time.Hour;
            if (StartHour < EndHour) 
                return (currentHour >= StartHour && currentHour < EndHour);
            else 
                return (currentHour >= StartHour || currentHour < EndHour); // Overnight session handler
        }

        //+------------------------------------------------------------------+
        //| Pro-Grade UI Dashboard                                           |
        //+------------------------------------------------------------------+
        private void DrawDashboard()
        {
            Chart.DrawStaticText("HUD_L0", "■ INSTITUTIONAL OTE SCALPER (cTrader) ■", VerticalAlignment.Top, HorizontalAlignment.Left, Color.Silver);
            Chart.DrawStaticText("HUD_L1", "\n\nLoading spread...", VerticalAlignment.Top, HorizontalAlignment.Left, Color.Gray);
            Chart.DrawStaticText("HUD_L2", "\n\n\nLoading session...", VerticalAlignment.Top, HorizontalAlignment.Left, Color.Gray);
            Chart.DrawStaticText("HUD_L3", "\n\n\n\nLoading trend...", VerticalAlignment.Top, HorizontalAlignment.Left, Color.Gray);
        }

        private void UpdateDashboard()
        {
            double spread = Math.Round(Symbol.Spread / Symbol.PipSize, 1);
            bool sessionActive = (!UseTimeFilter || IsTradingSession());
            string trend = (Symbol.Bid > _ema200.Result.Last(0)) ? "BULLISH" : "BEARISH";

            Color spreadCol = (spread <= MaxSpreadPips) ? Color.LimeGreen : Color.Red;
            Color sessionCol = sessionActive ? Color.LimeGreen : Color.Orange;

            Chart.DrawStaticText("HUD_L1", $"\n\nSpread: {spread} Pips | Max: {MaxSpreadPips}", VerticalAlignment.Top, HorizontalAlignment.Left, spreadCol);
            Chart.DrawStaticText("HUD_L2", $"\n\n\nTrading Session: {(sessionActive ? "ACTIVE" : "CLOSED")}", VerticalAlignment.Top, HorizontalAlignment.Left, sessionCol);
            Chart.DrawStaticText("HUD_L3", $"\n\n\n\nTrend (200 EMA): {trend}", VerticalAlignment.Top, HorizontalAlignment.Left, Color.White);
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping technique inside Fibonaci zone

Post by PTScalper »

Important Execution Details for cTrader:

1.) Targeting Pips directly: cTrader calculates Stops automatically when entering ExecuteMarketOrder. Instead of feeding the API absolute price levels for SL/TP (which causes errors on ECNs), you feed it distance in pips. slPips = Math.Abs(Symbol.Ask - sl) / Symbol.PipSize.

2.) OnBar Method Setup: By calling the core logic purely inside OnBar(), this guarantees it runs precisely at 00:00.00 on the new candle when prices open, which perfectly emulates your old MQL4 if(Time[0] == lastBarTime) return; block, but does so with vastly better CPU efficiency.

3.) Array Loop Iteration: In cTrader, Bars.HighPrices.Last(1) is the previous closed candle. Last(40) is 40 bars ago. The loop correctly handles finding the swing range by iterating backwards chronologically.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping technique inside Fibonaci zone

Post by PTScalper »

Translating this algorithm to TradingView’s Pine Script v5 requires adapting to Pine's strategy tester engine. Pine natively manages orders, risk sizing, and backtesting history in a slightly different manner than MT4/MT5 or cTrader.

Here are the key Pine Script adaptations:

Strategy Sizing vs Absolute Lots: We use Pine Script’s native equity math to calculate the exact fractional contract/lot sizing dynamically based on your account equity and pip distance to the Stop Loss.

calc_on_every_tick: This is set to true to ensure the Break-Even and Trailing Stop logic evaluates in real-time as the candle is forming, rather than waiting for the close.

Data-Series Offsets: Instead of array iteration, Pine uses built-in time-series functions (ta.highestbars and ta.lowestbars) to perfectly locate the chronological sequence of the impulse leg.

On-Chart HUD: Rebuilt using Pine Script’s table.new() capabilities to anchor the dashboard to the top-left of the chart.

The Pine Script v5 Code

Open TradingView, go to the Pine Editor, create a new Strategy, and paste this code:

Code: Select all

//@version=5
strategy("Institutional OTE Scalper [Pro]", overlay=true, calc_on_every_tick=true, initial_capital=10000, default_qty_type=strategy.cash, commission_type=strategy.commission.cash_per_order, commission_value=3)

// --- 1. RISK & BROKER SETTINGS ---
G_RISK = "--- Risk & Broker ---"
lot_type = input.string("Auto Risk %", options=["Auto Risk %", "Fixed Volume"], group=G_RISK)
risk_pct = input.float(1.0, "Risk Percent (%)", step=0.1, group=G_RISK)
fixed_qty = input.float(1.0, "Fixed Volume (Contracts/Lots)", group=G_RISK)
max_spread = input.float(2.0, "Max Spread (Pips)", group=G_RISK, tooltip="Live spread filter (will not affect historical backtesting)")

// --- 2. TRADE LOGIC & ADAPTIVE VOLATILITY ---
G_LOGIC = "--- Entry & Volatility ---"
lookback = input.int(40, "Swing Lookback", group=G_LOGIC)
ema_len = input.int(200, "EMA Period", group=G_LOGIC)
mfi_len = input.int(3, "MFI Period", group=G_LOGIC)
atr_mult = input.float(1.0, "ATR Multiplier (Stop Buffer)", group=G_LOGIC)

// --- 3. TRADE MANAGEMENT (BE & TRAIL) ---
G_MGMT = "--- Management ---"
use_be = input.bool(true, "Use Break Even", group=G_MGMT)
be_trigger = input.float(5.0, "BE Trigger (Pips)", group=G_MGMT)
be_lock = input.float(0.5, "BE Lock-In (Pips)", group=G_MGMT)
use_trail = input.bool(true, "Use Trailing Stop", group=G_MGMT)
trail_step = input.float(2.0, "Trail Step (Pips)", group=G_MGMT)

// --- 4. SESSION FILTERS ---
G_SESS = "--- Session Filters ---"
use_session = input.bool(true, "Use Time Filter", group=G_SESS)
session_time = input.session("0800-1700", "Trading Session (Exchange Time)", group=G_SESS)

// --- System Variables ---
// Determine Pip Size (Forex usually uses 10 minticks per pip, Crypto/Stocks use 1)
pip_size = syminfo.type == "forex" ? syminfo.mintick * 10 : syminfo.mintick

// Indicators
ema200 = ta.ema(close, ema_len)
mfi = ta.mfi(hlc3, mfi_len)
atr = ta.atr(14)

plot(ema200, "200 EMA", color=color.new(color.white, 30), linewidth=2)

// --- Price Action Swing Logic ---
// ta.highestbars returns a negative offset (e.g., -5 means 5 bars ago)
high_idx = ta.highestbars(high, lookback)
low_idx  = ta.lowestbars(low, lookback)

swing_high = ta.highest(high, lookback)
swing_low  = ta.lowest(low, lookback)
swing_range = swing_high - swing_low

// If the lowest point has a smaller (more negative) index than the highest point, 
// the low happened BEFORE the high -> Valid Upward Impulse Leg
impulse_up = (low_idx < high_idx) 
impulse_dn = (high_idx < low_idx)

// Fib Zones
fib618_L = swing_high - (swing_range * 0.618)
fib786_L = swing_high - (swing_range * 0.786)
fib618_S = swing_low + (swing_range * 0.618)
fib786_S = swing_low + (swing_range * 0.786)

// Conditions
in_session = use_session ? not na(time(timeframe.period, session_time)) : true

in_zone_L = (close <= fib618_L) and (close >= fib786_L)
mfi_trigger_L = (mfi[1] <= 20) and (mfi > 20)
long_cond = (close > ema200) and impulse_up and in_zone_L and mfi_trigger_L and in_session and strategy.opentrades == 0

in_zone_S = (close >= fib618_S) and (close <= fib786_S)
mfi_trigger_S = (mfi[1] >= 80) and (mfi < 80)
short_cond = (close < ema200) and impulse_dn and in_zone_S and mfi_trigger_S and in_session and strategy.opentrades == 0

// --- Trade State Variables ---
var float sl_level = na
var float tp_level = na

// --- Entry Engine ---
if long_cond
    sl_level := swing_low - (atr * atr_mult)
    tp_level := swing_high
    
    // Dynamic Risk Sizing Math
    sl_dist = math.abs(close - sl_level)
    risk_cash = strategy.equity * (risk_pct / 100)
    qty = lot_type == "Auto Risk %" ? (risk_cash / (sl_dist * syminfo.pointvalue)) : fixed_qty
    
    strategy.entry("OTE Long", strategy.long, qty=qty)
    strategy.exit("Exit Long", "OTE Long", stop=sl_level, limit=tp_level)

if short_cond
    sl_level := swing_high + (atr * atr_mult)
    tp_level := swing_low
    
    sl_dist = math.abs(sl_level - close)
    risk_cash = strategy.equity * (risk_pct / 100)
    qty = lot_type == "Auto Risk %" ? (risk_cash / (sl_dist * syminfo.pointvalue)) : fixed_qty
    
    strategy.entry("OTE Short", strategy.short, qty=qty)
    strategy.exit("Exit Short", "OTE Short", stop=sl_level, limit=tp_level)

// --- Break Even & Trailing Stop Engine ---
if strategy.position_size > 0 // We are in a Long
    curr_profit_pips = (high - strategy.position_avg_price) / pip_size
    
    if use_be and (curr_profit_pips >= be_trigger)
        be_price = strategy.position_avg_price + (be_lock * pip_size)
        if sl_level < be_price or na(sl_level)
            sl_level := be_price
            
    if use_trail and (sl_level >= strategy.position_avg_price) // Only trail after BE is secured
        trail_price = close - (trail_step * pip_size)
        if trail_price > sl_level
            sl_level := trail_price
            
    strategy.exit("Exit Long", "OTE Long", stop=sl_level, limit=tp_level)

else if strategy.position_size < 0 // We are in a Short
    curr_profit_pips = (strategy.position_avg_price - low) / pip_size
    
    if use_be and (curr_profit_pips >= be_trigger)
        be_price = strategy.position_avg_price - (be_lock * pip_size)
        if sl_level > be_price or na(sl_level)
            sl_level := be_price
            
    if use_trail and (sl_level <= strategy.position_avg_price) // Only trail after BE is secured
        trail_price = close + (trail_step * pip_size)
        if trail_price < sl_level
            sl_level := trail_price
            
    strategy.exit("Exit Short", "OTE Short", stop=sl_level, limit=tp_level)

// --- Pro-Grade UI Dashboard ---
var table hud = table.new(position.top_left, 1, 4, bgcolor=color.new(color.black, 70), frame_color=color.gray, frame_width=1)

if barstate.islast
    table.cell(hud, 0, 0, "■ INSTITUTIONAL OTE SCALPER ■", text_color=color.silver, text_size=size.small, text_halign=text.align_left)
    
    // Spread calculation (Only functional in live-market realtime ticks)
    live_spread = syminfo.ask and syminfo.bid ? (syminfo.ask - syminfo.bid) / pip_size : 0.0
    spread_color = live_spread <= max_spread ? color.lime : color.red
    table.cell(hud, 0, 1, "Spread: " + str.tostring(live_spread, "#.#") + " Pips", text_color=spread_color, text_size=size.small, text_halign=text.align_left)
    
    session_color = in_session ? color.lime : color.orange
    table.cell(hud, 0, 2, "Trading Session: " + (in_session ? "ACTIVE" : "CLOSED"), text_color=session_color, text_size=size.small, text_halign=text.align_left)
    
    trend_color = close > ema200 ? color.lime : color.red
    table.cell(hud, 0, 3, "Trend (200 EMA): " + (close > ema200 ? "BULLISH" : "BEARISH"), text_color=trend_color, text_size=size.small, text_halign=text.align_left)
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply