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;
}