Page 1 of 1

Forex scalping technique inside Fibonaci zone

Posted: Sat Jul 25, 2026 9:37 pm
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;
}