You are currently viewing the resource titled "Coding Custom Fibonacci Retracement Strategy in MQL5 with Custom Levels". This page provides detailed information about the resource, including its content, attached files, and recent discussions. Feel free to explore, download available files if logged in, and join the community conversation below!
Banner Image

Introduction


Let's pivot to a fresh adventure: whipping up a Fibonacci Retracement trading system with your own custom levels. 😎 This clever bot pulls retracement levels from either the daily candle's rollercoaster ride or a historical price array, sniffs out bullish vibes (close > open) or bearish grumps (close < open), and leaps into action when prices cross your handpicked spots—like the classic 50% or golden 61.8%. We've got caps on trades per level to avoid overzealous betting, an option to slam the door on old trades when fresh Fibs recalculate, trailing stops that activate once you've pocketed enough pips (points-based, baby!), and buffers on stops and profits tied to the range's percentage for that extra cushion. Think of it as your market's elastic band—snap back to profits! 📏💥

We'll unpack this gem step by step: First, demystifying the Fibonacci Retracement strategy (spoiler: it's math magic from an Italian genius meets modern trading). Then, rolling up our sleeves for the MQL5 implementation—code breakdowns, tips, and tricks. Next, backtesting basics to see if it holds water (or pips). And finally, a wrap-up with customization ideas to make it yours. By the time we're done, you'll have a battle-ready MQL5 EA that's flexible, fun, and fibonacci-fied. Grab your coffee—let's code some market mischief! ☕🛠️


Understanding the Fibonacci Retracement Strategy


Let's geek out on the Fibonacci retracement strategy—it's like the market's secret geometry lesson, where we slap those golden ratios from the famous sequence (you know, the one that pops up in nature, art, and now your trades) onto a recent price swing to spot sneaky support and resistance spots. This helps traders play detective in trending markets, predicting where prices might hit the brakes during pullbacks, either reversing like a plot twist or powering through after catching their breath. 📐✨

In a bullish romp, picture prices rocketing from low to high—then they dip back to Fib levels like 50% or the mystical 61.8%, turning into prime "buy the dip" zones where you bet on a bouncy comeback upward. Flip the script for bearish drama: after a nosedive from high to low, those same levels become "sell the rally" traps during brief upticks, wagering on the downtrend's ruthless return. It's all about riding the wave without getting wiped out! 🐂🐻

We supercharge this with smart tweaks: entries only fire on confirmed price crossings (no false alarms!), buffers padded onto levels based on the swing's size for that risk-management hug, trade limits per level to keep your portfolio from turning into a mosh pit, trailing stops that lock in gains as prices strut your way, and an optional "new Fib, who dis?" closure to reset for fresh opportunities. Mix it all, and boom—you're laser-focused on those trend-reversal sweet spots. Peek at this bearish retracement tease below; it's like the market whispering, "Sell me if you dare!" 😏

Our master plan? Whip up Fib levels from daily candle escapades or a historical array stash (toggle at will for your strategy flavor), gauge the mood—bullish if close beats open, bearish otherwise—then pounce on crossings of your custom ratios (50% and 61.8% are our go-to rockstars, but swap in whatever floats your boat; they're popular for a reason!). Throw in trade caps for sanity, SL/TP with range-percentage buffers for wiggle room, points-based trailing that kicks in post-profit milestone, optional closes on recalcs, and eye-candy visuals via colored chart bling and info labels. Voilà—a bendy, beastly system for snagging retracements like a pro! 🛡️🚀


Implementation in MQL5


Alright, let's kick off this MQL5 coding fiesta! 🚀 First things first: Launch MetaEditor (your trusty sidekick for MetaTrader magic), zip over to the Navigator panel on the left, hunt down the Experts folder like a treasure seeker, smack that "New" button with gusto, and waltz through the simple prompts to spawn your fresh Expert Advisor file. Boom—now it's staring back at you in the editor, ready for action! Next up, we'll sprinkle in some input parameters (those user-tweakable knobs) and global variables (the behind-the-scenes heroes that keep everything humming across the program). These bad boys will be our go-to tools for making the EA flexible and smart. Think of it as outfitting your bot with a utility belt—practical, powerful, and oh-so-professional. 😎🛠️

//+------------------------------------------------------------------+
//|                                 Fibonacci Retracement Ratios.mq5 |
//|                           Copyright 2025, Allan Munene Mutiiria. |
//|                                   https://t.me/Forex_Algo_Trader |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Allan Munene Mutiiria."
#property link      "https://t.me/Forex_Algo_Trader"
#property version   "1.00"
#property strict

#include <Trade\Trade.mqh>                                        // For trade execution

//+------------------------------------------------------------------+
//| Enums                                                            |
//+------------------------------------------------------------------+
enum CloseOnNewEnum {                                             // Define enum for closing on new Fibonacci
   CloseOnNew_No  = 0,                                            // No
   CloseOnNew_Yes = 1                                             // Yes
};
enum TrailingTypeEnum {                                           // Define enum for trailing stop types
   Trailing_None   = 0,                                           // None
   Trailing_Points = 2                                            // By Points
};

//+------------------------------------------------------------------+
//| Input Parameters                                                 |
//+------------------------------------------------------------------+
input bool   UseDailyApproach     = true;                         // Use daily candle (true) or array (false)
input string fibLevelsStr         = "50,61.8";                    // Comma-separated Fib levels for entry (e.g., 50,61.8)
input int    maxTradesPerLevel    = 1;                            // Max trades per level per Fib period (0=unlimited)
input CloseOnNewEnum CloseOnNewFib = CloseOnNew_No;               // Close trades on new Fib calc
input TrailingTypeEnum TrailingType = Trailing_None;              // Trailing Stop Type
input double Trailing_Stop_Pips   = 30.0;                         // Trailing Stop in Pips (for Points type)
input double Min_Profit_To_Trail_Pips = 50.0;                     // Min Profit to Start Trailing in Pips
input int    LookbackSize         = 100;                          // Number of candles for array approach
input double LotSize              = 0.1;                          // Trade lot size
input int    MagicNumber          = 12345;                        // Magic number for trades
input bool   IncludeCurrentBar    = false;                        // Include current bar in array calcs for updates
input double SlBufferPercent      = 0.0;                          // SL buffer percent of range (0=no buffer)
input double TpBufferPercent      = 0.0;                          // TP buffer percent of range (0=no buffer)

Let's jump right into the code foundations—like laying the bricks for our Fibonacci fortress! 🏰 We start by pulling in the "Trade" library via #include <Trade\Trade.mqh> to unlock all those nifty functions for firing off orders and wrangling positions. Then, we craft two enums for user-friendly choices: CloseOnNewEnum lets you pick "No" to keep trades humming through new Fib calcs or "Yes" to wipe the slate clean, while TrailingTypeEnum offers "None" for no trailing drama or "Points" to slide stops based on pip distances.

Shifting gears to inputs—these are your customization playground! UseDailyApproach (true by default) toggles between daily candle magic or array lookbacks for Fib levels; fibLevelsStr ("50,61.8") takes comma-separated ratios for entry hotspots; maxTradesPerLevel (1) caps trades per level to dodge overload (0 means party on!); CloseOnNewFib taps the enum for recalc closures; and TrailingType picks your trailing adventure.

For the trailing nitty-gritty: Trailing_Stop_Pips (30.0) dials the stop distance, Min_Profit_To_Trail_Pips (50.0) sets the "I'm winning!" threshold to activate it. LookbackSize (100) scopes the candle history for array mode; LotSize (0.1) sizes your bets; MagicNumber (12345) tags trades as yours; IncludeCurrentBar (false) optionally sneaks in the current bar for live updates; and SlBufferPercent/TpBufferPercent (both 0.0) add percentage-based padding from the range to SL/TP for that safety squish—crank 'em up for more breathing room. Compile this baby, and voilà—a dashboard of inputs ready to tweak! 🎛️😄

With those shiny inputs locked and loaded, let's march forward and whip up some global variables that'll be our trusty sidekicks all through this programmatic adventure—keeping tabs on everything from trade tallies to Fib flags like a vigilant squirrel hoarding nuts! 🐿️🧠

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
CTrade obj_Trade;                                                 //--- Trade object
int    barsTotal;                                                 //--- For daily approach
#define FIB_OBJ "Fibonacci Retracement"                           //--- Define Fibonacci object name
// Persistent variables for both approaches
static double storedEntryLvls[];                                  //--- Array of entry levels
static int    storedTradesCount[];                                //--- Trades count per level
static double storedSl = 0.0;                                     //--- Stored stop loss
static double storedTp = 0.0;                                     //--- Stored take profit
static string storedInfo = "";                                    //--- Stored information string
static bool   storedIsBullish = false;                            //--- Stored bullish flag
static double fibLevels[];                                        //--- Parsed Fibonacci levels (original order)
static string lastShownInfo = "";                                 //--- To detect changes and avoid unnecessary updates
// For array approach
static bool   fibCalculated = false;                              //--- Fibonacci calculated flag
static double currentHigh = 0.0;                                  //--- Current high
static double currentLow = 0.0;                                   //--- Current low
static string fibName = "Fib_Array";                              //--- Fibonacci name for array

Next, we're rolling out the red carpet for our global variables—the unsung heroes that keep the EA's brain buzzing across ticks! We start with "obj_Trade" as a shiny new CTrade instance to boss around orders like a traffic cop on caffeine, "barsTotal" to tally up those daily bars in the daily mode (because who doesn't love a good count?), and define "FIB_OBJ" as "Fibonacci Retracement" for the name of our main Fib chart doodle. For the persistent crew that works in both daily and array approaches, we've got static arrays like "storedEntryLvls[]" to stash those calculated entry prices, "storedTradesCount[]" for keeping score on trades per level, doubles "storedSl" and "storedTp" kicking off at 0.0 for stop loss and take profit duties, "storedInfo" as an empty string ready for display banter, "storedIsBullish" set to false as our direction detective, "fibLevels[]" for the parsed-out ratios, and "lastShownInfo" blank to catch changes and skip pointless redraws (efficiency wins! 💡). Zooming in on array-mode exclusives: static "fibCalculated" starts false to flag if levels are computed, "currentHigh" and "currentLow" at 0.0 to monitor those extreme highs and lows for update triggers, and "fibName" as "Fib_Array" for its unique object label. With this powerhouse lineup in place, we're geared up to tackle the implementation logic—starting with the OnInit event handler to fire up the initialization party! 🎊🔥

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   obj_Trade.SetExpertMagicNumber(MagicNumber);                   //--- Set magic number for trade object
   // Force initial calculation for daily approach
   barsTotal = 0;                                                 //--- Ensure first tick updates
   // Parse fibLevelsStr into fibLevels array (from MQL5 docs: StringSplit)
   string tempLevels[];                                           //--- Temporary levels array
   ushort commaSep = StringGetCharacter(",", 0);                  //--- Get comma separator
   int numLevels = StringSplit(fibLevelsStr, commaSep, tempLevels); //--- Split string into levels
   ArrayResize(fibLevels, numLevels);                             //--- Resize fibLevels array
   for (int i = 0; i < numLevels; i++) {                          //--- Iterate through levels
      fibLevels[i] = StringToDouble(tempLevels[i]);               //--- Convert to double
   }
   ArrayResize(storedEntryLvls, numLevels);                       //--- Resize storedEntryLvls
   ArrayResize(storedTradesCount, numLevels);                     //--- Resize storedTradesCount
   // Clean up old labels
   ObjectsDeleteAll(0, "InfoLabel_", -1, OBJ_LABEL);              //--- Delete all info labels
   lastShownInfo = "";                                            //--- Reset last shown info
   // Clean up old Fib object for array
   ObjectDelete(0, fibName);                                      //--- Delete Fibonacci object
   fibCalculated = false;                                         //--- Reset calculated flag
   return(INIT_SUCCEEDED);                                        //--- Return success
}

In the OnInit event handler—think of it as the EA's grand entrance—we kick things off by tuning the trade object with "obj_Trade.SetExpertMagicNumber(MagicNumber)" to slap our unique ID on all orders, ensuring no mix-ups in the trading crowd. For the daily approach, we zero out "barsTotal" to force that fresh update on the very first tick, like hitting the reset button for a clean start. Next, we dissect "fibLevelsStr" into the "fibLevels[]" array by chopping the string at commas via StringSplit (grabbing the comma char with StringGetCharacter), dumping the pieces into a temp array "tempLevels[]", sizing "fibLevels[]" to fit the lot, and looping through to convert each string bit to a double using StringToDouble—voilà, usable ratios! We mirror that size to "storedEntryLvls[]" and "storedTradesCount[]" for seamless tracking of entries and tallies.

For a spotless stage, we nuke all lingering info labels with ObjectsDeleteAll (targeting "InfoLabel_" prefix and OBJ_LABEL type), blank out "lastShownInfo" for change detection, zap any stale Fib object called "fibName" via ObjectDelete, and flip "fibCalculated" back to false. Wrap it up by returning INIT_SUCCEEDED—high-five, setup nailed! Now we're primed to tackle the OnTick handler and roll out that daily signal logic to get this Fib party swinging. 🎉🔧

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   if (UseDailyApproach) {                                        //--- Check daily approach
      // Daily approach logic remains the same
      int bars = iBars(_Symbol, PERIOD_D1);                       //--- Get daily bars
      if (barsTotal != bars && TimeCurrent() > StringToTime("00:05")) { //--- Check new bar
         barsTotal = bars;                                        //--- Update bars total
         ObjectDelete(0, FIB_OBJ);                                //--- Delete Fib object
         double openPrice = iOpen(_Symbol, PERIOD_D1, 1);         //--- Get open price
         double closePrice = iClose(_Symbol, PERIOD_D1, 1);       //--- Get close price
         double high = iHigh(_Symbol, PERIOD_D1, 1);              //--- Get high
         double low = iLow(_Symbol, PERIOD_D1, 1);                //--- Get low
         datetime startingTime = iTime(_Symbol, PERIOD_D1, 1);    //--- Get start time
         datetime endingTime = iTime(_Symbol, PERIOD_D1, 0) - 1;  //--- Get end time
         double range = high - low;                               //--- Calc range
         storedIsBullish = (closePrice > openPrice);              //--- Set bullish flag
         string levelsList = "";                                  //--- Init levels list
         for (int i = 0; i < ArraySize(fibLevels); i++) {         //--- Iterate levels
            storedTradesCount[i] = 0;                             //--- Reset count
            if (storedIsBullish) {                                //--- Check bullish
               storedEntryLvls[i] = NormalizeDouble(high - range * fibLevels[i] / 100, _Digits); //--- Calc entry
            } else {                                              //--- Handle bearish
               storedEntryLvls[i] = NormalizeDouble(low + range * fibLevels[i] / 100, _Digits); //--- Calc entry
            }
            levelsList += DoubleToString(fibLevels[i], 1) + ": " + DoubleToString(storedEntryLvls[i], _Digits) + "\n"; //--- Add to list
         }
         if (storedIsBullish) {                                   //--- Check bullish
            // Bullish: Fibo from low to high for correct 0% at high, 100% at low, green
            ObjectCreate(0, FIB_OBJ, OBJ_FIBO, 0, startingTime, low, endingTime, high); //--- Create Fib
            ObjectSetInteger(0, FIB_OBJ, OBJPROP_COLOR, clrGreen); //--- Set color
            for (int i = 0; i < ObjectGetInteger(0, FIB_OBJ, OBJPROP_LEVELS); i++) { //--- Iterate levels
               ObjectSetInteger(0, FIB_OBJ, OBJPROP_LEVELCOLOR, i, clrGreen); //--- Set level color
            }
            storedSl = NormalizeDouble(low - range * (SlBufferPercent / 100), _Digits); //--- Calc SL
            storedTp = NormalizeDouble(high + range * (TpBufferPercent / 100), _Digits); //--- Calc TP
            storedInfo = "Daily Approach - Bullish\n" +           //--- Set info
                         "Open: " + DoubleToString(openPrice, _Digits) + "\n" +
                         "Close: " + DoubleToString(closePrice, _Digits) + "\n" +
                         "Buy Entries:\n" + levelsList +
                         "SL: " + DoubleToString(storedSl, _Digits) + "\n" +
                         "TP: " + DoubleToString(storedTp, _Digits);
            Print("New daily bar: Bullish Fibonacci levels calculated. Entries: ", levelsList); //--- Log
         } else {                                                 //--- Handle bearish
            // Bearish: Fibo from high to low for correct 0% at low, 100% at high, red
            ObjectCreate(0, FIB_OBJ, OBJ_FIBO, 0, startingTime, high, endingTime, low); //--- Create Fib
            ObjectSetInteger(0, FIB_OBJ, OBJPROP_COLOR, clrRed);  //--- Set color
            for (int i = 0; i < ObjectGetInteger(0, FIB_OBJ, OBJPROP_LEVELS); i++) { //--- Iterate levels
               ObjectSetInteger(0, FIB_OBJ, OBJPROP_LEVELCOLOR, i, clrRed); //--- Set level color
            }
            storedSl = NormalizeDouble(high + range * (SlBufferPercent / 100), _Digits); //--- Calc SL
            storedTp = NormalizeDouble(low - range * (TpBufferPercent / 100), _Digits); //--- Calc TP
            storedInfo = "Daily Approach - Bearish\n" +           //--- Set info
                         "Open: " + DoubleToString(openPrice, _Digits) + "\n" +
                         "Close: " + DoubleToString(closePrice, _Digits) + "\n" +
                         "Sell Entries:\n" + levelsList +
                         "SL: " + DoubleToString(storedSl, _Digits) + "\n" +
                         "TP: " + DoubleToString(storedTp, _Digits);
            Print("New daily bar: Bearish Fibonacci levels calculated. Entries: ", levelsList); //--- Log
         }
      }
   }
   // Redraw chart objects
   ChartRedraw();                                                 //--- Redraw chart
}

In the OnTick event handler—our EA's heartbeat that pulses with every market tick—if "UseDailyApproach" is flipped on, we snag the daily bar count using iBars with _Symbol and PERIOD_D1 (because daily drama is where it's at!). When a fresh daily bar pops up and the clock ticks past 00:05 (checked via TimeCurrent > StringToTime), we refresh "barsTotal", banish any old Fib object with ObjectDelete on "FIB_OBJ" (out with the old!), and grab yesterday's candle stats: open from iOpen, close via iClose, high with iHigh, low using iLow, plus start/end times (nudge end by -1 second for spot-on drawing). We crunch the range as high - low, flag "storedIsBullish" true if close > open (party time!), then loop through ArraySize of "fibLevels" to zero "storedTradesCount[i]", compute those entry levels with NormalizeDouble (high minus range * percent for bullish buys, low plus for bearish sells—precision is key!), and whip up a "levelsList" string for show-and-tell.

For bullish vibes, we spawn a Fib object "FIB_OBJ" as OBJ_FIBO, anchoring from low at start to high at end, splash it green with ObjectSetInteger on OBJPROP_COLOR, and loop over ObjectGetInteger(OBJPROP_LEVELS) to green-ify each level's OBJPROP_LEVELCOLOR (go team green! 🟢). Calc "storedSl" as low minus buffer % of range, "storedTp" as high plus, format "storedInfo" with setup type, open/close deets, entries list, and levels—then log the whole shebang for posterity. Bearish? Mirror the magic: anchor high to low, go red for colors (🔴 alert!), SL above high, TP below low, update info, and log away. Cap it off with ChartRedraw to freshen the visuals—like giving your chart a quick spa day. Pro tip: Compile and test at every step—catch those bugs early, or they'll crash your trading party! 🐛🚫 When you hit compile, here's what bubbles up. 😄

From the cheeky snapshot in the image, it's crystal clear we're whipping up the range calculation, playing detective on market direction (bullish or bearish, anyone?), and slapping that Fibonacci object onto the chart like a digital graffiti artist. But wait, there's more—the excitement ramps up as we stalk those sneaky retracements and fire off positions the moment prices boogie back to our handpicked levels. Here's the sneaky-smart logic we cooked up to nail that down! 📸🔍🚀

//+------------------------------------------------------------------+
//| Display info using labels without flicker                        |
//+------------------------------------------------------------------+
void ShowLabels(string info) {
   if (info == lastShownInfo) return;                             //--- Skip if no change
   lastShownInfo = info;                                          //--- Update last info
   // Split info into lines
   string lines[];                                                //--- Lines array
   ushort nlSep = StringGetCharacter("\n", 0);                    //--- Get newline sep
   int numLines = StringSplit(info, nlSep, lines);                //--- Split into lines
   int y = 10;                                                    //--- Starting Y
   for (int i = 0; i < numLines; i++) {                           //--- Iterate lines
      string name = "InfoLabel_" + IntegerToString(i);            //--- Label name
      if (ObjectFind(0, name) < 0) {                              //--- Check exists
         ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);               //--- Create label
         ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER); //--- Set corner
         ObjectSetInteger(0, name, OBJPROP_XDISTANCE, 10);        //--- Set X distance
         ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 8);          //--- Set font size
      }
      ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);            //--- Set Y distance
      ObjectSetString(0, name, OBJPROP_TEXT, lines[i]);           //--- Set text
      y += 15;                                                    //--- Increment Y
   }
   // Delete extra labels if numLines decreased
   for (int i = numLines; ; i++) {                                //--- Iterate extras
      string name = "InfoLabel_" + IntegerToString(i);            //--- Label name
      if (ObjectFind(0, name) < 0) break;                         //--- Break if none
      ObjectDelete(0, name);                                      //--- Delete label
   }
}

// Display info every tick using labels (but only update if changed)
ShowLabels(storedInfo);                                           //--- Show labels
// Entry logic: Checked every tick using stored levels (no existing position)
if (PositionsTotal() == 0) {                                      //--- Check no positions
   double close1 = iClose(_Symbol, _Period, 1);                   //--- Get close 1
   double close2 = iClose(_Symbol, _Period, 2);                   //--- Get close 2
   for (int i = 0; i < ArraySize(storedEntryLvls); i++) {         //--- Iterate levels
      // Only enter on levels 0 < fib <=100 (retracements), ignore 0/100/extensions for entry
      if (fibLevels[i] <= 0 || fibLevels[i] > 100.0) continue;    //--- Skip invalid
      if ((maxTradesPerLevel == 0 || storedTradesCount[i] < maxTradesPerLevel) && //--- Check count
          ((storedIsBullish && close1 > storedEntryLvls[i] && close2 <= storedEntryLvls[i]) || //--- Buy cross
           (!storedIsBullish && close1 < storedEntryLvls[i] && close2 >= storedEntryLvls[i]))) { //--- Sell cross
         string levelStr = DoubleToString(fibLevels[i], 1);       //--- Level string
         ulong ticket = 0;                                        //--- Init ticket
         if (storedIsBullish) {                                   //--- Check buy
            Print("Buy signal triggered at ", close1, " crossing level ", levelStr, " (", storedEntryLvls[i], ")"); //--- Log
            obj_Trade.Buy(LotSize, _Symbol, 0, storedSl, storedTp, "Fibo Buy at " + levelStr); //--- Open buy
            ticket = obj_Trade.ResultDeal();                      //--- Get deal
         } else {                                                 //--- Handle sell
            Print("Sell signal triggered at ", close1, " crossing level ", levelStr, " (", storedEntryLvls[i], ")"); //--- Log
            obj_Trade.Sell(LotSize, _Symbol, 0, storedSl, storedTp, "Fibo Sell at " + levelStr); //--- Open sell
            ticket = obj_Trade.ResultDeal();                      //--- Get deal
         }
         storedTradesCount[i]++;                                  //--- Increment count
         break;                                                   //--- Break loop
      }
   }
}

First off, let's craft the "ShowLabels" function—your chart's chatty billboard for strategy deets, updating labels smartly without that annoying flicker-fest redraw! It takes a string "info" as input and bails early if it's the same as "lastShownInfo" (no need to repaint the town), but if fresh, it updates "lastShownInfo" to match. We chop "info" into a "lines[]" array using StringSplit on newlines (grabbed via StringGetCharacter), then loop over "numLines" to spawn or tweak labels named "InfoLabel_" + index—check if they exist with ObjectFind; if not, whip 'em up via ObjectCreate as OBJ_LABEL, parked in the upper-left corner with X at 10 and font size 8. Stack Y distances starting at 10, bumping by 15 per line for neat spacing, and slap in the text from "lines[i]" using ObjectSetString on OBJPROP_TEXT. For cleanup if your info shrinks, loop from "numLines" up, zapping extra labels with ObjectDelete until ObjectFind says "nope, gone!"

Then, in the OnTick function right after our earlier logic, we ping "ShowLabels" with "storedInfo" every tick to refresh the display only when stuff changes—like a efficient news ticker. For the entry excitement, if PositionsTotal hits zero (no open trades, fresh canvas!), we grab prior closes with iClose at shifts 1 and 2, then cruise through ArraySize of "storedEntryLvls", nixing any levels outside 0-100% (gotta keep it real retracement-style). If trades are greenlit (0 for unlimited or below "maxTradesPerLevel") and a crossover magic happens—close1 > level with close2 ≤ for bullish buys, or close1 < with close2 ≥ for bearish sells—we format "levelStr" via DoubleToString to one decimal (fancy!), set up a ticket, log the signal for the books, fire a buy or sell through "obj_Trade.Buy" or "Sell" with "LotSize", symbol, 0 for market entry, "storedSl", "storedTp", and a comment tagging the level, snag the deal result, bump "storedTradesCount[i]", and break to prevent tick-overload entries. Compile time? Check out the sweet results below—your EA's evolving like a boss! 😄📊🚀

Now that we've nailed those entry confirmations in the daily approach logic (high-fives all around! 🙌), let's shimmy over to the alternative method. We tossed in two logics for this project purely to flex how you can flip-flop between them or remix everything to fit your wildest trading dreams—like a choose-your-own-adventure book, but with pips instead of plot twists. 📖💡 Since this array approach is super dynamic and churns out more signals (signal party, anyone?), we run the analysis just once, chilling as long as prices stay cozy within the prior setup. We only rev the engines for a new crunch after a breach of that setup—usually when prices bust out of the 0 to 100% levels like a rebel escaping curfew. Pro tip: There are extension levels lurking beyond 100%, so keep an eye out! To catch those sneaky breaches, we'll craft a handy function to sound the alarm. 🚨😉

//+------------------------------------------------------------------+
//| Check if price breaches the current Fib extremes                 |
//+------------------------------------------------------------------+
bool IsBreach() {
   if (!fibCalculated) return false;                              //--- Return false if not calculated
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);            //--- Get bid price
   if (storedIsBullish) {                                         //--- Check bullish
      // For bullish, 0% is high, 100% is low
      return (bid > currentHigh || bid < currentLow);             //--- Check breach
   } else {                                                       //--- Handle bearish
      // For bearish, 0% is low, 100% is high
      return (bid > currentLow || bid < currentHigh);             //--- Check breach
   }
}

Here we go, rolling out the "IsBreach" function—like a watchful bouncer at the Fib club, spotting if the current bid price (snagged via SymbolInfoDouble with SYMBOL_BID) has snuck past those stored extremes! It plays it safe by returning false right away if "fibCalculated" isn't true (no false alarms on uninitialized turf). For bullish setups where 0% chills at the high and 100% at the low, it checks if bid blasts above "currentHigh" or dips under "currentLow"—escape artist detected! For bearish flips with anchors swapped, it verifies if bid climbs over "currentLow" or plummets below "currentHigh", cueing recalcs in array mode when the breach is real. Nailed it! Now we can weave in that array approach, which is basically a twin to the daily one—same vibes, different data dance. 😜🕵️‍♂️📈

else {                                                          //--- Array approach
   // Array approach: Calculate only when not calculated or breached
   if (!fibCalculated || IsBreach()) {                          //--- Check recalc
      if (fibCalculated) {                                      //--- Check calculated
         // Invalidate and forget previous
         ObjectDelete(0, fibName);                              //--- Delete Fib
         fibCalculated = false;                                 //--- Reset flag
      }
      int startShift = IncludeCurrentBar ? 0 : 1;               //--- Set start shift
      int copyCount = IncludeCurrentBar ? LookbackSize : LookbackSize; //--- Set copy count
      double high[], low[];                                     //--- High and low arrays
      ArraySetAsSeries(high, true);                             //--- Set as series
      ArraySetAsSeries(low, true);                              //--- Set as series
      if (CopyHigh(_Symbol, _Period, startShift, copyCount, high) <= 0) return; //--- Copy high
      if (CopyLow(_Symbol, _Period, startShift, copyCount, low) <= 0) return; //--- Copy low
      int highestCandle = ArrayMaximum(high, 0, copyCount);     //--- Get highest
      int lowestCandle = ArrayMinimum(low, 0, copyCount);       //--- Get lowest
      MqlRates pArray[];                                        //--- Rates array
      ArraySetAsSeries(pArray, true);                           //--- Set as series
      int pData = CopyRates(_Symbol, _Period, startShift, copyCount, pArray); //--- Copy rates
      if (pData <= 0) return;                                   //--- Check data
      double highVal = pArray[highestCandle].high;              //--- Get high val
      double lowVal = pArray[lowestCandle].low;                 //--- Get low val
      double range = highVal - lowVal;                          //--- Calc range
      int oldestShift = IncludeCurrentBar ? (LookbackSize - 1) : LookbackSize; //--- Oldest shift
      double openCandle = iOpen(_Symbol, _Period, oldestShift); //--- Get open
      double closeCandle = iClose(_Symbol, _Period, IncludeCurrentBar ? 0 : 1); //--- Get close
      storedIsBullish = (closeCandle > openCandle);             //--- Set bullish
      string levelsList = "";                                   //--- Init list
      for (int i = 0; i < ArraySize(fibLevels); i++) {          //--- Iterate levels
         storedTradesCount[i] = 0;                              //--- Reset count
         if (storedIsBullish) {                                 //--- Check bullish
            storedEntryLvls[i] = NormalizeDouble(highVal - range * fibLevels[i] / 100, _Digits); //--- Calc entry
         } else {                                               //--- Handle bearish
            storedEntryLvls[i] = NormalizeDouble(lowVal + range * fibLevels[i] / 100, _Digits); //--- Calc entry
         }
         levelsList += DoubleToString(fibLevels[i], 1) + ": " + DoubleToString(storedEntryLvls[i], _Digits) + "\n"; //--- Add to list
      }
      if (storedIsBullish) {                                    //--- Check bullish
         // Bullish: Anchor from low to high
         datetime time1 = pArray[lowestCandle].time;            //--- Time1
         double price1 = lowVal;                                //--- Price1
         datetime time2 = pArray[highestCandle].time;           //--- Time2
         double price2 = highVal;                               //--- Price2
         ObjectCreate(0, fibName, OBJ_FIBO, 0, time1, price1, time2, price2); //--- Create Fib
         ObjectSetInteger(0, fibName, OBJPROP_COLOR, clrGreen); //--- Set color
         for (int i = 0; i < ObjectGetInteger(0, fibName, OBJPROP_LEVELS); i++) { //--- Iterate levels
            ObjectSetInteger(0, fibName, OBJPROP_LEVELCOLOR, i, clrGreen); //--- Set level color
         }
         storedSl = NormalizeDouble(lowVal - range * (SlBufferPercent / 100), _Digits); //--- Calc SL
         storedTp = NormalizeDouble(highVal + range * (TpBufferPercent / 100), _Digits); //--- Calc TP
         storedInfo = "Array Approach - Bullish\n" +            //--- Set info
                      "Array Open: " + DoubleToString(openCandle, _Digits) + "\n" +
                      "Array Close: " + DoubleToString(closeCandle, _Digits) + "\n" +
                      "Buy Entries:\n" + levelsList +
                      "SL: " + DoubleToString(storedSl, _Digits) + "\n" +
                      "TP: " + DoubleToString(storedTp, _Digits);
      } else {                                                  //--- Handle bearish
         // Bearish: Anchor from high to low
         datetime time1 = pArray[highestCandle].time;           //--- Time1
         double price1 = highVal;                               //--- Price1
         datetime time2 = pArray[lowestCandle].time;            //--- Time2
         double price2 = lowVal;                                //--- Price2
         ObjectCreate(0, fibName, OBJ_FIBO, 0, time1, price1, time2, price2); //--- Create Fib
         ObjectSetInteger(0, fibName, OBJPROP_COLOR, clrRed);   //--- Set color
         for (int i = 0; i < ObjectGetInteger(0, fibName, OBJPROP_LEVELS); i++) { //--- Iterate levels
            ObjectSetInteger(0, fibName, OBJPROP_LEVELCOLOR, i, clrRed); //--- Set level color
         }
         storedSl = NormalizeDouble(highVal + range * (SlBufferPercent / 100), _Digits); //--- Calc SL
         storedTp = NormalizeDouble(lowVal - range * (TpBufferPercent / 100), _Digits); //--- Calc TP
         storedInfo = "Array Approach - Bearish\n" +            //--- Set info
                      "Array Open: " + DoubleToString(openCandle, _Digits) + "\n" +
                      "Array Close: " + DoubleToString(closeCandle, _Digits) + "\n" +
                      "Sell Entries:\n" + levelsList +
                      "SL: " + DoubleToString(storedSl, _Digits) + "\n" +
                      "TP: " + DoubleToString(storedTp, _Digits);
      }
      currentHigh = storedIsBullish ? highVal : lowVal;         //--- Set current high
      currentLow = storedIsBullish ? lowVal : highVal;          //--- Set current low
      fibCalculated = true;                                     //--- Set calculated
   }
   // Display info using labels (but only update if changed)
   ShowLabels(storedInfo);                                        //--- Show labels
   // Entry logic: Checked every tick using stored levels (no existing position)
   if (PositionsTotal() == 0) {                                   //--- Check no positions
      double close1 = iClose(_Symbol, _Period, 1);                //--- Get close 1
      double close2 = iClose(_Symbol, _Period, 2);                //--- Get close 2
      for (int i = 0; i < ArraySize(storedEntryLvls); i++) {      //--- Iterate levels
         if (fibLevels[i] <= 0 || fibLevels[i] > 100.0) continue; //--- Skip invalid
         if ((maxTradesPerLevel == 0 || storedTradesCount[i] < maxTradesPerLevel) && //--- Check count
             ((storedIsBullish && close1 > storedEntryLvls[i] && close2 <= storedEntryLvls[i]) || //--- Buy cross
              (!storedIsBullish && close1 < storedEntryLvls[i] && close2 >= storedEntryLvls[i]))) { //--- Sell cross
            string levelStr = DoubleToString(fibLevels[i], 1);     //--- Level string
            ulong ticket = 0;                                      //--- Init ticket
            if (storedIsBullish) {                                 //--- Check buy
               Print("Buy signal triggered (Array) at ", close1, " crossing level ", levelStr, " (", storedEntryLvls[i], ")"); //--- Log
               obj_Trade.Buy(LotSize, _Symbol, 0, storedSl, storedTp, "Fibo Buy Array at " + levelStr); //--- Open buy
               ticket = obj_Trade.ResultDeal();                        //--- Get deal
            } else {                                               //--- Handle sell
               Print("Sell signal triggered (Array) at ", close1, " crossing level ", levelStr, " (", storedEntryLvls[i], ")"); //--- Log
               obj_Trade.Sell(LotSize, _Symbol, 0, storedSl, storedTp, "Fibo Sell Array at " + levelStr); //--- Open sell
               ticket = obj_Trade.ResultDeal();                        //--- Get deal
            }
            storedTradesCount[i]++;                                //--- Increment count
            break;                                                 //--- Break loop
         }
      }
   }
}

Switching to array mode when daily's not your jam? No problem—Fib levels get a refresh only if they're virgin territory or if "IsBreach" spots a price jailbreak (escape from those extremes!). If they're already cozy, we evict the old Fib object with ObjectDelete on "fibName" and flip "fibCalculated" back to false like resetting a quirky alarm clock. The start shift and copy count play tag with "IncludeCurrentBar": if true, go full throttle with 0 shift and the whole lookback squad; if false, play safe with shift 1 and just the finished bars (no peeking at the unfinished drama!). High and low arrays get the series treatment via ArraySetAsSeries, then stuffed with data using CopyHigh and CopyLow—bail if that flops, nobody likes a failed buffet.

For pinpoint accuracy on values and times, we haul in rates to "pArray" with CopyRates (wave goodbye if data's skimpy), snag "highVal" from the peak candle's high and "lowVal" from the valley's low, then crunch range as their gap (simple math, big impact!). Direction detection? Grab open at the ancient shift with iOpen and close at the fresh one via iClose, tagging "storedIsBullish" if close struts above open—optimism detected! 🐂 Loop over ArraySize of "fibLevels" to zero counts, whip up normalized entries (highVal minus range % for bullish bounces, lowVal plus for bearish dives), and stitch "levelsList" for the info parade. Drawing and trading? Echo the daily mode's swagger—familiar moves, fresh data. Compile, and behold the glorious results below—your EA's getting smarter by the second! 😎📈🚀

We can see that we use the array approach and initiate positions. What now remains is managing the positions by closing them when we have new signals and trailing the ones that move in our favour.

//+------------------------------------------------------------------+
//| Close all positions with matching magic and symbol               |
//+------------------------------------------------------------------+
void CloseAllPositions() {
   for (int i = PositionsTotal() - 1; i >= 0; i--) {              //--- Iterate positions reverse
      if (PositionGetTicket(i) > 0 && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol) { //--- Check position
         obj_Trade.PositionClose(PositionGetTicket(i));                //--- Close position
      }
   }
}

//+------------------------------------------------------------------+
//| Apply Points Trailing Stop (from reference)                      |
//+------------------------------------------------------------------+
void ApplyPointsTrailing() {
   double point = _Point;                                         //--- Get point value
   for (int i = PositionsTotal() - 1; i >= 0; i--) {              //--- Iterate positions reverse
      if (PositionGetTicket(i) > 0) {                             //--- Check valid ticket
         if (PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber) { //--- Check symbol and magic
            double sl = PositionGetDouble(POSITION_SL);              //--- Get SL
            double tp = PositionGetDouble(POSITION_TP);              //--- Get TP
            double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Get open price
            ulong ticket = PositionGetInteger(POSITION_TICKET);      //--- Get ticket
            if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy
               double newSL = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) - Trailing_Stop_Pips * point, _Digits); //--- Calc new SL
               if (newSL > sl && SymbolInfoDouble(_Symbol, SYMBOL_BID) - openPrice > Min_Profit_To_Trail_Pips * point) { //--- Check conditions
                  obj_Trade.PositionModify(ticket, newSL, tp);           //--- Modify position
               }
            } else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell
               double newSL = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) + Trailing_Stop_Pips * point, _Digits); //--- Calc new SL
               if (newSL < sl && openPrice - SymbolInfoDouble(_Symbol, SYMBOL_ASK) > Min_Profit_To_Trail_Pips * point) { //--- Check conditions
                  obj_Trade.PositionModify(ticket, newSL, tp);           //--- Modify position
               }
            }
         }
      }
   }
}
These are the functions that we need to achieve the management logic. We just need to call them respectively where needed.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   // Points trailing can run anytime
   if (TrailingType == Trailing_Points && PositionsTotal() > 0) { //--- Check trailing
      ApplyPointsTrailing();                                      //--- Apply trailing
   }
   //--- call where necessary
         if (CloseOnNewFib == CloseOnNew_Yes) {                   //--- Check close on new
            CloseAllPositions();                                  //--- Close positions
         }
}

We sprinkle those function calls into the OnTick handler wherever they fit like puzzle pieces—seamless and spot-on! Now, the next adventure? Tidying up our mess by nuking those created objects when the chart says "peace out," ensuring a graceful exit without leftover clutter. Here's how we pull it off: 🧹✨

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   ObjectsDeleteAll(0, "InfoLabel_", -1, OBJ_LABEL);              //--- Delete all info labels
   ObjectDelete(0, FIB_OBJ);                                      //--- Delete daily Fibonacci
   ObjectDelete(0, fibName);                                      //--- Delete array Fibonacci
}

In the OnDeinit function—your EA's graceful curtain call—we blitz all those pesky info labels with ObjectsDeleteAll, honing in on the "InfoLabel_" prefix and OBJ_LABEL type across every subwindow (because leftover clutter is so last season!), then nuke the daily Fib object via ObjectDelete on "FIB_OBJ" and its array sibling on "fibName" for a spotless visual farewell. Hit compile, and feast your eyes on the shiny outcome below—code cleanup at its finest! 😎🧼🚀

Voila! We can spot how our EA handles positions like a seasoned babysitter, automatically slapping on those trailing stops right when the profits start strutting—keeping gains locked in and risks at bay, totally nailing our trading goals with style. The last puzzle piece? Backtesting this bad boy to see if it struts or stumbles in historical data—and guess what, that's the star of the show in the upcoming section. Stay tuned, folks! 😄📊🔍


Backtesting


After thorough backtesting, we have the following results.

Backtest graph:

GRAPH

Backtest report:

REPORT


Conclusion


In wrapping up this epic coding quest, we've conjured a Fibonacci retracement trading wizard in MQL5 that's as clever as it is crafty—pulling levels from daily candle escapades or historical array treasure troves, sniffing out bullish cheers (close > open) or bearish growls (close < open), and pouncing on buys or sells when prices cross your bespoke ratios with per-level trade caps to keep things from turning into a chaotic mosh pit. Add in optional "fresh Fib, fresh start" closures on recalcs, points-based trailing stops that activate once profits hit that sweet threshold (locking in gains like a vault!), range-buffered entries for risk-resilient padding, and snazzy on-chart visuals plus info labels to make monitoring a breeze. It's like giving your trades a superhero cape—spotting those pullback gems in trends with style! 🦸‍♂️📈

Disclaimer: Hey, this is purely educational fun—think of it as a trading sandbox, not a golden ticket. Markets are wild beasts with teeth; volatility can chomp your capital, leading to real losses. Always backtest like your portfolio depends on it (spoiler: it does) and layer on solid risk management before unleashing this in live action. No guarantees, just smart prep! ⚠️💡

Armed with this Fib powerhouse, you're all set to chase those sneaky pullbacks like a market ninja, tweaking and optimizing along the way. Here's to pips in your pocket and grins on your face—happy trading, adventurers! 🎉🚀

Disclaimer: The ideas and strategies presented in this resource are solely those of the author and are intended for informational and educational purposes only. They do not constitute financial advice, and past performance is not indicative of future results. All materials, including but not limited to text, images, files, and any downloadable content, are protected by copyright and intellectual property laws and are the exclusive property of Forex Algo-Trader or its licensors. Reproduction, distribution, modification, or commercial use of these materials without prior written consent from Forex Algo-Trader is strictly prohibited and may result in legal action. Users are advised to exercise extreme caution, perform thorough independent research, and consult with qualified financial professionals before implementing any trading strategies or decisions based on this resource, as trading in financial markets involves significant risk of loss.

Recent Comments

Go to discussion to Comment or View other Comments

No comments yet. Be the first to comment!

Banner
🤖 Building a ChatGPT AI Trade Brain in MQL5
What’s the point of an AI trading assistant that talks but never acts? In this article, we transfo...
2026-05-15 16:50:14
Banner
Grid Scalper MA MT5 EA Review (2026): The Forever-Free MetaTrader 5 Robot
Grid Scalper MA MT5 EA Review (2026): The forever-free MetaTrader 5 robot that's quietly earning a 4...
2026-05-14 19:35:18
Banner
Supercharging Your TPO Market Profile With Volume Intelligence
You've got a TPO market profile that tracks where price spent its time — now imagine giving it a f...
2026-04-03 16:05:42