MQL5 Fibonacci Library: Code Retracement Levels for EAs

Build a reusable MQL5 Fibonacci library for EA retracement and extension levels. Includes full code, ZigZag swing detection, and real trading logic.

mql5-fibonacci-library-code-retracement-levels

Why Every MQL5 Dev Needs a Fibonacci Library

I've lost count of how many EAs I've reviewed where the developer hard-coded Fibonacci levels like 0.382, 0.5, and 0.618 directly into entry logic. It works—until you want to change the level set, switch timeframes, or recalculate after a new swing high forms. Then you're digging through spaghetti code, updating magic numbers in three different places. That's where a dedicated MQL5 Fibonacci library saves your week.

This isn't about drawing Fibonacci lines on a chart—MetaTrader 5 already does that beautifully with the built-in Fibonacci object. This is about pulling those same levels into your EA's logic so you can trigger entries at 0.618, set stop-losses beyond 1.272, or trail profits between extensions. A custom MQL5 function library makes this reusable across every EA you write, and it's about 150 lines of clean code.

I'll walk you through building an MQL5 include file that calculates Fibonacci retracement levels from ZigZag-identified swing points, then show you how to plug it into an EA for actual signals. By the end, you'll have a drop-in library you can #include in any project. No fluff, no fake promises—just code that works.

What a Fibonacci Retracement Library Actually Needs to Do

Before we write a single line of MQL5, let's define the requirements. A usable Fibonacci retracement MQL5 library should:

  • Accept a swing high and swing low price (double values).
  • Return an array of retracement levels (0.0, 0.236, 0.382, 0.5, 0.618, 0.764, 1.0).
  • Optionally return extension levels (1.272, 1.414, 1.618, 2.0, 2.618).
  • Work in both uptrend (retracement from high down to low) and downtrend (retracement from low up to high).
  • Be stateless—no global variables, no chart objects—so you can call it from any EA without side effects.

Most traders overlook the direction handling. If you just subtract a fixed percentage from the high, you get the same numbers whether the market is rising or falling. That's wrong for Fibonacci EA signals because a bullish retracement should project upward from the low, and a bearish one downward from the high. Our library will handle both.

Building the MQL5 Fibonacci Include File

Create a new file in MetaEditor called FibLib.mqh and save it in MQL5\Include\. This is our MQL5 include file that any EA can reference. Open MetaEditor from Tools > MetaQuotes Language Editor, or hit F4 in MT5. Right-click the Include folder in the Navigator, select "Create New File", choose "Include file (.mqh)", and name it FibLib.mqh.

Core Function: CalculateFibLevels

Here's the heart of the library—a function that takes two prices and a direction flag, then fills a dynamic array with levels. I've added an enum for readability, so you can reference levels by name instead of remembering array indices. This is a small touch that pays off when you're debugging at 2 AM.

//+------------------------------------------------------------------+
//| FibLib.mqh - Fibonacci retracement & extension library          |
//+------------------------------------------------------------------+
#property copyright "Your Name"
#property version   "1.00"
#property strict

// Enumerate level types for readability
enum ENUM_FIB_LEVEL
  {
   FIB_0     = 0,
   FIB_236   = 1,
   FIB_382   = 2,
   FIB_50    = 3,
   FIB_618   = 4,
   FIB_764   = 5,
   FIB_100   = 6,
   FIB_1272  = 7,
   FIB_1414  = 8,
   FIB_1618  = 9,
   FIB_2000  = 10,
   FIB_2618  = 11
  };

//+------------------------------------------------------------------+
//| Calculate Fibonacci levels from swing high and low               |
//| direction: true = bullish (low to high), false = bearish (high to low) |
//| Returns number of levels calculated                              |
//+------------------------------------------------------------------+
int CalculateFibLevels(double high, double low, bool bullish, double &levels[])
  {
   double range = high - low;
   if(range <= 0.0) return(0);  // invalid range

   // Define retracement ratios (0.0 to 1.0) and extensions (>1.0)
   double ratios[] = {0.0, 0.236, 0.382, 0.5, 0.618, 0.764, 1.0,
                      1.272, 1.414, 1.618, 2.0, 2.618};

   int count = ArraySize(ratios);
   ArrayResize(levels, count);

   for(int i = 0; i < count; i++)
     {
      if(bullish)
         // Uptrend: start from low and project upward
         levels[i] = low + range * ratios[i];
      else
         // Downtrend: start from high and project downward
         levels[i] = high - range * ratios[i];
     }

   return(count);
  }
//+------------------------------------------------------------------+

Notice the bullish flag. When true, the lowest level (ratio 0.0) equals the swing low, and 1.0 equals the swing high. When false, it's reversed—0.0 is the swing high, 1.0 is the swing low. This means levels[FIB_618] always gives you the 0.618 retracement relative to the trend direction, without you having to remember which price is higher. I've seen too many EAs fail because the developer assumed the high was always the first argument. This design removes that assumption.

Helper: Get Nearest Swing Points via ZigZag

You won't always pass prices manually. Most EAs need to find the last swing high and low automatically. I use the built-in ZigZag indicator for this because it's already in MetaTrader 5 and doesn't require external DLLs. Add this to FibLib.mqh:

//+------------------------------------------------------------------+
//| Get last two ZigZag swing points                                 |
//| Returns true on success, false if not enough swings found        |
//+------------------------------------------------------------------+
bool GetZigZagSwings(int depth=12, int deviation=5, int backstep=3,
                     double &swingHigh, double &swingLow)
  {
   // Handle ZigZag handle
   int zzHandle = iCustom(_Symbol, _Period, "ZigZag", depth, deviation, backstep);
   if(zzHandle == INVALID_HANDLE)
     {
      Print("FibLib: Failed to create ZigZag indicator handle");
      return(false);
     }

   // Copy last 100 values to find non-empty swings
   double zzBuffer[];
   ArraySetAsSeries(zzBuffer, true);
   if(CopyBuffer(zzHandle, 0, 0, 100, zzBuffer) < 3)
     {
      Print("FibLib: Not enough ZigZag data");
      return(false);
     }

   // Find the last two swing points (non-zero values)
   double swings[];
   ArrayResize(swings, 100);
   int swingCount = 0;

   for(int i = 0; i < 100 && swingCount < 2; i++)
     {
      if(zzBuffer[i] != 0.0 && zzBuffer[i] != EMPTY_VALUE)
        {
         swings[swingCount] = zzBuffer[i];
         swingCount++;
        }
     }

   if(swingCount < 2)
     {
      Print("FibLib: Only found ", swingCount, " swing point(s)");
      return(false);
     }

   // Determine which is high and which is low
   if(swings[0] > swings[1])
     {
      swingHigh = swings[0];
      swingLow  = swings[1];
     }
   else
     {
      swingHigh = swings[1];
      swingLow  = swings[0];
     }

   return(true);
  }
//+------------------------------------------------------------------+

One gotcha: ZigZag repaints. On the current bar, the last swing might change as new data arrives. I only use this function on closed bars—check Volume[0] > 1 or shift the lookback by one bar. You can also cache the swing points and only update when a new bar starts. In my own EAs, I store the swing high/low in static variables and recalculate only on new bar events. This prevents the EA from jumping in and out of positions based on repainted data.

Integrating the Fibonacci Library into an EA

Now let's put this to work. I'll show you a minimal EA that opens a buy order when price touches the 0.618 retracement level from the last swing. Create a new EA in MetaEditor and add this:

//+------------------------------------------------------------------+
//| FibTestEA.mq5 - Demo EA using FibLib                             |
//+------------------------------------------------------------------+
#include <FibLib.mqh>

input double LotSize = 0.1;
input int    ZigZagDepth = 12;
input int    ZigZagDeviation = 5;
input int    ZigZagBackstep = 3;

double fibLevels[];

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   // Only trade on new bar to avoid repainting issues
   static datetime lastBar = 0;
   if(Time[0] == lastBar) return;
   lastBar = Time[0];

   double swingHigh, swingLow;
   if(!GetZigZagSwings(ZigZagDepth, ZigZagDeviation, ZigZagBackstep,
                        swingHigh, swingLow))
     {
      Print("Could not get swing points");
      return;
     }

   // Determine trend: if swing high is more recent than swing low, we're in uptrend
   bool bullish = (swingHigh > swingLow);  // simplified; use bar index for precision

   int levelsCount = CalculateFibLevels(swingHigh, swingLow, bullish, fibLevels);
   if(levelsCount == 0) return;

   double fib618 = fibLevels[FIB_618];
   double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

   // Bullish entry: price retraced to 0.618 from high, bounce expected
   if(bullish && currentBid <= fib618 + _Point * 10 && currentBid >= fib618 - _Point * 10)
     {
      // Place buy stop at 0.618 with SL at swing low
      double sl = swingLow - 10 * _Point;
      double tp = swingHigh + (swingHigh - swingLow) * 0.5;  // 1:1.5 risk-reward
      OpenBuy(LotSize, sl, tp);
     }

   // Bearish entry: price retraced to 0.618 from low, rejection expected
   if(!bullish && currentAsk >= fib618 - _Point * 10 && currentAsk <= fib618 + _Point * 10)
     {
      double sl = swingHigh + 10 * _Point;
      double tp = swingLow - (swingHigh - swingLow) * 0.5;
      OpenSell(LotSize, sl, tp);
     }
  }

//+------------------------------------------------------------------+
//| Simplified order send function (use CTrade for production)       |
//+------------------------------------------------------------------+
void OpenBuy(double lot, double sl, double tp)
  {
   MqlTradeRequest request = {0};
   MqlTradeResult  result = {0};
   request.action   = TRADE_ACTION_DEAL;
   request.symbol   = _Symbol;
   request.volume   = lot;
   request.type     = ORDER_TYPE_BUY;
   request.price    = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   request.sl       = sl;
   request.tp       = tp;
   request.deviation= 10;
   request.magic    = 123456;
   OrderSend(request, result);
  }

void OpenSell(double lot, double sl, double tp)
  {
   // Mirror of OpenBuy with ORDER_TYPE_SELL
   MqlTradeRequest request = {0};
   MqlTradeResult  result = {0};
   request.action   = TRADE_ACTION_DEAL;
   request.symbol   = _Symbol;
   request.volume   = lot;
   request.type     = ORDER_TYPE_SELL;
   request.price    = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   request.sl       = sl;
   request.tp       = tp;
   request.deviation= 10;
   request.magic    = 123456;
   OrderSend(request, result);
  }
//+------------------------------------------------------------------+

I used a 10-point proximity zone to avoid missing the level on the next tick. In production, you'd want to use pending orders or a more sophisticated entry filter—maybe a candlestick pattern confirmation or RSI divergence. The point is the Fibonacci EA signals are now a single function call away.

Handling Edge Cases and Troubleshooting

Let's talk about what can go wrong. First, the ZigZag indicator might return EMPTY_VALUE for the first few bars of a new chart. Your EA should handle this gracefully—check for INVALID_HANDLE and print a warning. Second, if the market is ranging tightly with a range less than 10 points, the Fibonacci levels become meaningless. I add a minimum range check: if(range < 50 * _Point) return(0); in the CalculateFibLevels function.

Another edge case: when the swing high and low are the same price (a doji bar or flat market). The range becomes zero, and we return 0 levels. Your EA should skip trading in that scenario. The bullish flag logic I used is simplified—it assumes the higher price is the more recent swing. For production, you'd want to compare bar indices to determine the actual sequence. I've seen EAs open trades in the wrong direction because a lower swing formed after a higher one. Always check the bar order.

Parameter Tables for Your EA

When you expose Fibonacci levels as inputs, keep them clean. Here's the table I use in my own EAs. These parameters go in the EA's input section, and the user can tweak them in the Strategy Tester or live trading:

ParameterTypeDefaultDescription
FibRetracementLevel

Want to build your own version?

Recreate similar entry logic, risk rules, and filters in TradingBotMaker—no MQL coding. Start free.

Community

Clap for the article and open comments only when you want to read them.

0 claps0 comments

Related articles