Русский
preview
Strategy Optimization and Forward Analysis (Part 1): The Pardo Method — A Basic Model

Strategy Optimization and Forward Analysis (Part 1): The Pardo Method — A Basic Model

MetaTrader 5 — Tester |
386 4
Roman Shiredchenko
Roman Shiredchenko

Introduction

    You have written a strategy, built an indicator and an Expert Advisor (EA), and gotten a nice curve in the backtest — but the main question remains: can you trust it in real trading? This article focuses not merely on implementing ideas in MQL5 code, but on building a reproducible workflow pipeline: from formalizing a trading idea to objective post-optimization validation. We describe the specific artifacts and steps that every practicing algorithm developer should end up with:

    • clear formalization of entry/exit rules and risk management (an indicator plus one or two Expert Advisors (EAs));
    • selection of relevant parameters and determination of scan ranges;
    • a scheme for dividing the history into in-sample and out-of-sample (forward) periods, with criteria for window sizes;
    • a method for finding “plateau” (stable) settings instead of fragile best runs;
    • a protocol for forward testing, multi-environment testing (across multiple markets and timeframes), and decision-making thresholds.

    The purpose of this article is not to provide a set of magic filters, but rather a practical test plan and procedure that will help you understand which parameters are truly important, how to assess the overall reliability of a model, and how to determine its “shelf life” and the rules for re-optimization. The examples show the PardoSystem indicator and the PardoEA / Breakout_Bounce Expert Advisors (EAs) as illustrative implementations of this methodology.


    Development and Structure of a Trading System

    Developing a trading system is a complex process consisting of several interrelated steps. This entire procedure is fairly simple, provided that the trader follows each step carefully and meticulously, giving due consideration to its importance.

    There are two approaches to creating a trading system. The first approach relies on logic and systematic empirical verification: that is, each step must be thoroughly considered before beginning rigorous testing. This approach, which is presented and developed in this article, is the path of knowledge. The profitable trading models that result from this approach provide an invaluable advantage. A trader who uses this system has a thorough understanding of why and how this trading model works and succeeds. A simple example of this approach is presented in the form of an indicator and two Expert Advisors (EAs).

    To understand the results of a trading model, you need to follow the seven steps for building and testing it:

    1. Formulating a trading strategy
    2. Documenting its rules in a specific format
    3. Testing the trading strategy
    4. Optimizing the trading strategy
    5. Trading using this strategy
    6. Tracking live trading performance and comparing it with test performance
    7. Improving and refining this trading strategy

    Each of these steps depends on the successful completion of the previous step. Constant feedback, and using information from subsequent steps to revisit and adjust earlier steps, is a fundamental component of this approach and one of its greatest strengths. If the system is sound, this approach leads to the continuous evolution, refinement, and improvement of the chosen trading strategy.

    Developing a trading system starts with an idea. Without it, everything is just guesswork. The rules that make up the strategy must be laid out in a logical sequence. A strategy can be simple or complex — whatever you prefer; ultimately, it doesn't matter. Coherence and consistency are important for a strategy. In this article, we will analyze and optimize several trading systems based on trading range breakouts.

    Failing to fully define all the rules of a strategy is one of the most common mistakes made when developing systems. This is especially true for novice traders. The two basic requirements for defining a trading strategy are an entry rule and an exit rule.

    Typically, a strategy consists of buy and sell conditions that are “mirror images” of each other. For example, a buy signal occurs when the price rises above the 3-day high, and a sell signal occurs when the price breaks below the 3-day low. This is a symmetrical (mirror) system: the conditions for BUY and SELL are exactly the same, but they point in different directions (a breakout above the high/below the low over N bars).

    A strategy may also consist of completely different conditions for entering a buy or sell position. For example, a buy signal is generated when the price breaks above the 5-day high, and a sell signal is generated when the 5-day moving average falls below the 20-day moving average. This is an asymmetric trading system where BUY uses a breakout of the high, while SELL uses a moving-average crossover.

    • Preliminary testing: early detection of fundamental flaws, verification of basic functionality, and assessment of an idea’s initial viability.
    • Validation through multiple testing: confirming performance under various conditions, verifying the consistency of results, and assessing the robustness of the strategy.
    • Optimization results: verifying parameter stability, assessing the risk of over-optimization, and determining the reliability of the settings.
    • Forward analysis: testing on unseen data, assessing generalization capability, and out-of-sample testing beyond the optimization sample.
    • Real-world performance: final validation under real-world conditions, testing execution in live trading, and assessing practical applicability.

    This algorithm ensures that trading systems undergo rigorous validation before live deployment and have a clear path for continuous improvement based on real-world results.

    Key principles:

    • step-by-step development: from concept to live trading through validated stages
    • evidence-based approach: every decision is based on testing and analysis
    • risk management: minimizing losses at each stage
    • adaptability: the ability to improve based on experience
    • discipline: following a structured methodology

    Developing a trading system is a complex process consisting of several interrelated steps. In practice, this process becomes manageable if the trader carefully and consistently follows each step, giving due attention to its significance, because clear rules and discipline are the key to successful trading. We will try to translate this philosophy into actual MQL5 code and create not one, but two fully-fledged trading systems, each of which is a complete strategy with entry and exit rules.

    The first of these is a trend-following trading system based on the classic Envelopes indicator and modernized using filters developed by Robert Pardo, author of the renowned book The Evaluation and Optimization of Trading Strategies. The system is designed to trade at the opening of a bar and consists of the PardoSystem.mq5 indicator and the PardoEA.mq5 Expert Advisor (EA). The indicator analyzes the Open[0] price (of the current forming bar) and generates a signal, while the EA, synchronized with the indicator on bar 0, immediately opens a trade at the market price (Ask/Bid). In the Strategy Tester using the “OHLC” model, this perfectly simulates entry at the open price.


    Description and Operation of the PardoSystem.mq5 Indicator: Trading in the Direction of the Trend

    The system combines three powerful filters to weed out false entries:

    • Envelopes channel breakout: the open price must be above the upper band (for Buy) or below the lower band (for Sell).
    • Trend confirmation: the open price of the current bar must be higher (for Buy) or lower (for Sell) than the open price of the previous bar (a simple but effective trend filter).
    • Local extremum breakout (Pardo): the open price must break out above the high (for Buy) or below the low (for Sell) formed over the last N bars (the `InpLookbackBars` parameter), excluding the current and first bars.

    Indicator PardoSystem

    External variables (Input Parameters)

    //--- INPUT PARAMETERS
    //--- ENVELOPES CHANNEL PARAMETERS
    input group "========== ENVELOPES CHANNEL PARAMETERS =========="
    input int           InpMAPeriod       = 20;               // MA Period for Envelopes
    input int           InpMAShift        = 0;                // MA Shift
    input double        InpDeviation      = 0.25;             // Deviation (0.25 = 25%)
    input ENUM_MA_METHOD InpMAMethod      = MODE_SMA;         // MA Method
    input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE;   // Price for MA
    input color         InpUpperColor     = clrDodgerBlue;    // Upper band color
    input color         InpLowerColor     = clrOrangeRed;     // Lower band color
    input bool          InpShowEnvelopes  = true;             // Show bands

    Description of the Envelopes parameters:

    • InpMAPeriod — the moving average period used to calculate the channel: the higher the value, the slower the channel reacts
    • InpDeviation — channel deviation as a percentage: for example, 0.25 means 25% of the MA value
    • InpMAMethod — the method used to calculate the moving average (SMA, EMA, SMMA, etc.)
    • InpAppliedPrice — the price used to calculate the MA (CLOSE, OPEN, HIGH, LOW)
    • InpUpperColor/InpLowerColor — channel band colors for visual distinction
    • InpShowEnvelopes — you can disable the display of the bands, leaving only the arrows

      Group "Pardo System Parameters"

      //--- PARDO SYSTEM PARAMETERS
      input group "========== PARDO SYSTEM PARAMETERS =========="
      input int           InpLookbackBars = 5;                 // Bars for MAX/MIN search

      Description of Pardo parameters:

      • InpLookbackBars — the number of bars to search for the maximum and minimum (starting from bar 2): determines the system's sensitivity to breakouts

      Group "Arrow Display Settings"

      //--- SIGNAL ARROW SETTINGS
      input group "========== SIGNAL ARROW SETTINGS =========="
      input color         InpBuyArrowColor = clrBlue;          // BUY arrow color
      input color         InpSellArrowColor = clrRed;          // SELL arrow color
      input int           InpArrowSize = 2;                    // Arrow size
      input double        InpArrowOffset = 15;                 // Offset from the OPEN price

      Description of the arrow settings:

      • InpBuyArrowColor/InpSellArrowColor — arrow colors for different directions
      • InpArrowSize — arrow line thickness
      • InpArrowOffset — the arrow offset from the OPEN price in points (so that the arrow does not overlap the price)
      Performance Parameters

      //--- PERFORMANCE PARAMETERS
      input group "========== PERFORMANCE PARAMETERS =========="
      input int           InpMaxHistoryBars = 1000;             // Max bars to display

      This indicator:

      • displays trading conditions on a chart,
      • plots key levels and indicators,
      • displays signals as arrows on bar 0,
      • displays an instructional comment with calculations,
      • helps you understand how the system works.

        The OnInit() function — initializing the indicator


        //+------------------------------------------------------------------+
        //| Initialization function                                          |
        //+------------------------------------------------------------------+
        int OnInit()
          {
           Print("╔═══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗");
           Print("║        PardoSystem v8.2                                                                                       ║");
           Print("╠═══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣");
           Print("║   TRADING LOGIC:                                                                                              ║");
           Print("║    1. CHANNEL BREAKOUT: OPEN > UPPER (BUY) / OPEN < LOWER (SELL)                                              ║");
           Print("║    2. TREND FILTER: OPEN[0] > OPEN[1] (BUY) / < (SELL)                                                        ║");
           Print("║    3. EXTREMUM BREAKOUT: MAX(High[2..", InpLookbackBars, "]) / MIN(Low[2..", InpLookbackBars, "])             ║");
           Print("╠═══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣");
           Print("║ ARROWS ARE CALCULATED LIVE ON EACH TICK/NEW BAR                                                               ║");
           Print("║ ONLY ONE SIGNAL PER BAR!                                                                                      ║");
           Print("║ BUFFER 2 - BUY | BUFFER 3 - SELL                                                                              ║");
           Print("╚═══════════════════════════════════════════════════════════════════════════════════════════════════════════════╝");
           
           //--- Bind buffers
           SetIndexBuffer(0, UpperBuffer,    INDICATOR_DATA);
           SetIndexBuffer(1, LowerBuffer,    INDICATOR_DATA);
           SetIndexBuffer(2, BuyArrowBuffer, INDICATOR_DATA);
           SetIndexBuffer(3, SellArrowBuffer,INDICATOR_DATA);
           
           //--- Plotting Envelopes bands
           PlotIndexSetInteger(0, PLOT_DRAW_TYPE,   InpShowEnvelopes ? DRAW_LINE : DRAW_NONE);
           PlotIndexSetInteger(0, PLOT_LINE_COLOR,  InpUpperColor);
           PlotIndexSetInteger(0, PLOT_LINE_WIDTH,  2);
           
           PlotIndexSetInteger(1, PLOT_DRAW_TYPE,   InpShowEnvelopes ? DRAW_LINE : DRAW_NONE);
           PlotIndexSetInteger(1, PLOT_LINE_COLOR,  InpLowerColor);
           PlotIndexSetInteger(1, PLOT_LINE_WIDTH,  2);
           
           //--- BUY arrows (code 233 = up arrow)
           PlotIndexSetInteger(2, PLOT_DRAW_TYPE,   DRAW_ARROW);
           PlotIndexSetInteger(2, PLOT_ARROW,       233);
           PlotIndexSetInteger(2, PLOT_LINE_WIDTH,  InpArrowSize);
           PlotIndexSetInteger(2, PLOT_LINE_COLOR,  InpBuyArrowColor);
           
           //--- SELL arrows (code 234 = down arrow)
           PlotIndexSetInteger(3, PLOT_DRAW_TYPE,   DRAW_ARROW);
           PlotIndexSetInteger(3, PLOT_ARROW,       234);
           PlotIndexSetInteger(3, PLOT_LINE_WIDTH,  InpArrowSize);
           PlotIndexSetInteger(3, PLOT_LINE_COLOR,  InpSellArrowColor);
           
           //--- Create Envelopes indicator handle
           handleEnvelopes = iEnvelopes(_Symbol, _Period, InpMAPeriod, InpMAShift,
                                        InpMAMethod, InpAppliedPrice, InpDeviation);
           if(handleEnvelopes == INVALID_HANDLE)
             {
              Print("ERROR: Failed to create Envelopes handle! Error code: ", GetLastError());
              return(INIT_FAILED);
             }
           
           IndicatorSetString(INDICATOR_SHORTNAME, indicatorName + " v8.2 FIXED");
           lastBarTime = 0;
           return(INIT_SUCCEEDED);
          }
        This function:

        • displays a nice banner describing the logic,
        • binds buffers to the indicator,
        • configures the display (colors, line types, arrow symbols),
        • creates a handle for the built-in indicator iEnvelopes

        The OnCalculate() function — the indicator’s “brain”


        //+------------------------------------------------------------------+
        //| Main calculation function                                        |
        //+------------------------------------------------------------------+
        int OnCalculate(const int rates_total,
                        const int prev_calculated,
                        const datetime &time[],
                        const double &open[],
                        const double &high[],
                        const double &low[],
                        const double &close[],
                        const long &tick_volume[],
                        const long &volume[],
                        const int &spread[])
          {
           if(handleEnvelopes == INVALID_HANDLE)
              return(0);
           
           //--- Need at least InpLookbackBars+2 bars for extremum calculation
           if(rates_total < InpLookbackBars + 10)
              return(0);
           
           //--- Ensure Envelopes has enough data
           if(BarsCalculated(handleEnvelopes) < rates_total)
              return(0);
           
           //--- Copy the Envelopes bands (both upper and lower)
           if(CopyBuffer(handleEnvelopes, 0, 0, rates_total, UpperBuffer) <= 0)
              return(0);
           if(CopyBuffer(handleEnvelopes, 1, 0, rates_total, LowerBuffer) <= 0)
              return(0);
           
           //--- Set series orientation (0 = newest bar)
           ArraySetAsSeries(UpperBuffer,    true);
           ArraySetAsSeries(LowerBuffer,    true);
           ArraySetAsSeries(BuyArrowBuffer, true);
           ArraySetAsSeries(SellArrowBuffer,true);
           ArraySetAsSeries(open,           true);
           ArraySetAsSeries(high,           true);
           ArraySetAsSeries(low,            true);
           ArraySetAsSeries(time,           true);
           
           //--- Determine the calculation range
           // For performance reasons, we recalculate only the last InpMaxHistoryBars.
           // But we must clear any arrows outside this range.
           int maxBars = MathMin(rates_total, InpMaxHistoryBars);
           
           //--- Reset all arrow buffers before recalculating (to avoid ghost signals)
           for(int i = 0; i < rates_total; i++)
             {
              BuyArrowBuffer[i]  = EMPTY_VALUE;
              SellArrowBuffer[i] = EMPTY_VALUE;
             }
           
           //--- Main loop: calculate signals for all bars within the history limit
           for(int bar = 0; bar < maxBars; bar++)
             {
              //--- Need enough future bars for the extremum (bar+2 ... bar+InpLookbackBars)
              if(bar + InpLookbackBars + 2 >= rates_total)
                 continue;
              
              //--- Get current and previous open prices
              double currOpen = open[bar];
              double prevOpen = open[bar+1];
              
              //--- Get Envelopes bands
              double upperEnv = UpperBuffer[bar];
              double lowerEnv = LowerBuffer[bar];
              
              //--- Validate bands
              if(upperEnv <= 0 || lowerEnv <= 0 || upperEnv <= lowerEnv)
                 continue;
              
              //--- Calculate the Pardo extremum:
              //    MAX(High[bar+2] ... High[bar+InpLookbackBars])
              //    MIN(Low[bar+2]  ... Low[bar+InpLookbackBars])
              double maxHigh = high[bar+2];
              double minLow  = low[bar+2];
              for(int i = 2; i <= InpLookbackBars; i++)
                {
                 int idx = bar + i;
                 if(idx >= rates_total) break;
                 if(high[idx] > maxHigh) maxHigh = high[idx];
                 if(low[idx]  < minLow)  minLow  = low[idx];
                }
              
              //--- BUY signal conditions:
              //    1. OPEN price above the UPPER band
              //    2. Trend filter: current OPEN &gt; previous OPEN
              //    3. Pardo breakout: current OPEN &gt; maxHigh of lookback bars
              if(currOpen > upperEnv)
                {
                 if(currOpen > prevOpen && currOpen > maxHigh)
                   {
                    // Place the BUY arrow slightly below the OPEN price
                    double arrowPrice = currOpen - (InpArrowOffset * _Point);
                    BuyArrowBuffer[bar] = arrowPrice;
                   }
                }
              //--- SELL signal conditions:
              //    1. OPEN price below the LOWER band
              //    2. Trend filter: current OPEN &lt; previous OPEN
              //    3. Pardo breakout: current OPEN &lt; minLow of lookback bars
              else if(currOpen < lowerEnv)
                {
                 if(currOpen < prevOpen && currOpen < minLow)
                   {
                    // Place the SELL arrow slightly above the OPEN price
                    double arrowPrice = currOpen + (InpArrowOffset * _Point);
                    SellArrowBuffer[bar] = arrowPrice;
                   }
                }
              //--- If the price is inside the channel -&gt; no signal (already cleared by the reset)
             }
           
           //--- Optional: log on new bar (commented out to prevent spam)
           datetime currentBarTime = time[0];
           if(currentBarTime != lastBarTime)
             {
              lastBarTime = currentBarTime;
              // Uncomment for debugging:
              // Print("New bar: ", TimeToString(currentBarTime), " | Total bars: ", rates_total);
             }
           
           return(rates_total);
          }

        This function:

        • loads Envelopes data via CopyBuffer,
        • sets the arrays as time series for correct indexing (bar 0 is the current bar),
        • clears all buffers on the first run (to prevent artifacts),
        • iterates over the bars in the main loop from newest to oldest,
        • calculates new signals based on three conditions,
        • limits the display to the last 1,000 bars.

          The indicator displays three key elements on the chart:

          • Envelopes channel (blue upper band and red lower band) — normal volatility zone
          • blue up arrows — BUY signals
          • red downward arrows — SELL signals

            IMPORTANT: Each bar can have ONLY ONE arrow! The indicator does not generate both BUY and SELL signals on the same bar.

            BUY SIGNAL:
                Bar OPEN price > UPPER Envelopes band             // Upward breakout of the channel
                AND
                OPEN[0] > OPEN[1]                                 // Upward trend
                AND
                OPEN[0] > MAX(High[2..N])                         // Breakout of the N-bar high (Pardo)
            
            SELL SIGNAL:
                Bar OPEN price < LOWER Envelopes band             // Downward breakout of the channel
                AND
                OPEN[0] < OPEN[1]                                 // Downward trend
                AND
                OPEN[0] < MIN(Low[2..N])                          // Breakout of the N-bar low (Pardo)

              The indicator first checks the price's position relative to the channel:

              • if the price is ABOVE the upper band — we check the BUY conditions
              • if the price is BELOW the lower band — we check the SELL conditions
              • if the price is INSIDE the channel — there are NO signals

                It makes sense: a channel breakout is an excellent initial filter.

                /*
                  INDICATOR BUFFER STRUCTURE:
                  
                  Buffer 0 - Upper Envelopes band (UpperBuffer)    - blue line
                  Buffer 1 - Lower Envelopes band (LowerBuffer)    - red line
                  Buffer 2 - BUY arrows (BuyArrowBuffer)           - value != EMPTY_VALUE = signal
                  Buffer 3 - SELL arrows (SellArrowBuffer)         - value != EMPTY_VALUE = signal
                */

                A clear visualization of the indicator is available on the chart, and the arrow colors and sizes can be changed in the external variables.

                Trading signals for opening positions are indicated by arrows colored blue (buy) and red (sell):

                Here is how the indicator displays a bearish trend:

                downward trend


                PardoEA Expert Advisor

                This Expert Advisor (EA) analyzes the market based on the selected strategy, automatically opens and closes positions, manages risks (lot size calculation, Stop Loss, Take Profit), keeps a log of all actions, and works on ANY timeframe (from M1 to D1).

                    Mathematical formula for trading signals:

                    • buy

                    Open[0] > Open[1]                   // Upward trend
                    AND
                    Open[0] > MAX(High[2..N])           // Breakout of the N-bar high
                    AND  
                    Open[0] > UpperEnvelopes            // Breakout above the upper volatility band

                    • sell

                    Open[0] < Open[1]                   // Downward trend
                    AND
                    Open[0] < MIN(Low[2..N])            // Breakout below the N-bar low
                    AND
                    Open[0] < LowerEnvelopes            // Breakout below the lower volatility band

                    The Expert Advisor (EA) operates based on the iEnvelopes technical indicator and uses it to obtain raw data for processing and preparation for making trading decisions. It is also synchronized to trade at the open price of the current bar)with index "0".

                    How the Expert Advisor Works

                    External Variables (Input Parameters)

                    //--- INPUT PARAMETERS
                    //--- ENVELOPES CHANNEL PARAMETERS
                    input group "--- ENVELOPES CHANNEL PARAMETERS"
                    input int           InpMAPeriod          = 20;               // MA Period for Channel
                    input double        InpDeviation         = 0.25;             // Channel deviation (0.25 = 25%)
                    input ENUM_MA_METHOD InpMAMethod         = MODE_SMA;         // Moving Average Method
                    input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE;      // Price for MA Calculation

                    Description of the Envelopes group:

                    • InpMAPeriod — the moving average period used to calculate the channel: the higher the value, the slower the channel reacts
                    • InpDeviation — channel deviation as a percentage: for example, 0.25 means 25% of the MA value; creates the upper and lower bands
                    • InpMAMethod — the method used to calculate the moving average (SMA, EMA, SMMA, etc.)
                    • InpAppliedPrice — the price used to calculate the MA (CLOSE, OPEN, HIGH, LOW, etc.)

                      //--- PARDO SYSTEM PARAMETERS
                      input group "--- PARDO SYSTEM PARAMETERS"
                      input int           InpLookbackBars      = 5;                // Depth for high/low search (N)

                      Description of the Pardo group:

                      • InpLookbackBars — the number of bars used to find the high and low: determines the system's sensitivity to breakouts.

                      //--- TRADING PARAMETERS
                      input group "--- TRADING PARAMETERS"
                      input bool          buy                  = true;       // Allow BUY trades
                      input bool          sell                 = true;       // Allow SELL trades
                      input double        InpLotSize           = 0.01;       // Fixed position volume
                      input int           InpMagicNumber       = 0;          // Unique EA number
                      input bool          InpReverseClose      = true;       // Close on opposite signal

                      Description of trading parameters:

                      • buy/sell — flags indicating whether trading is allowed in each direction. Unwanted signals can be disabled.
                      • InpLotSize — a fixed lot size for all trades.
                      • InpMagicNumber — the unique identifier for the Expert Advisor (EA). This allows it to "see" only its own positions and not interfere with other Expert Advisors (EAs) or manual trading.
                      • InpReverseClose — if true, when an opposite signal is received, the current position is closed and a new one is opened (reversal).
                        //--- RISK MANAGEMENT
                        input group "--- RISK MANAGEMENT"
                        input double        InpStopLoss          = 100;              // Stop Loss (in points, 0 - off)
                        input double        InpTakeProfit        = 200;              // Take Profit (in points, 0 - off)

                        Description of risk management:

                        • InpStopLoss — protection level in points from the entry price: for example, 100 points = 100 * Point
                        • InpTakeProfit — the take-profit level in points relative to the entry price

                          The OnInit() function — initialization of the Expert Advisor (EA)


                          //+------------------------------------------------------------------+
                          //| Expert initialization function                                   |
                          //+------------------------------------------------------------------+
                          int OnInit()
                            {
                             Print("PardoEA v8.4 | ", _Symbol, " ", EnumToString(_Period),
                                   " | MA=", InpMAPeriod, " Dev=", InpDeviation,
                                   " Lookback=", InpLookbackBars, " Lot=", InpLotSize,
                                   " SL/TP=", InpStopLoss, "/", InpTakeProfit);
                          
                             if(InpLotSize <= 0)
                               {
                                Print("ERROR: Lot size must be > 0");
                                return(INIT_PARAMETERS_INCORRECT);
                               }
                          
                             trade.SetExpertMagicNumber(InpMagicNumber);
                             trade.SetDeviationInPoints(0);
                          
                             ResetLastError();
                             handleEnvelopes = iEnvelopes(_Symbol, _Period,
                                                           InpMAPeriod, 0,
                                                           InpMAMethod,
                                                           InpAppliedPrice,
                                                           InpDeviation);
                          
                             if(handleEnvelopes == INVALID_HANDLE)
                               {
                                Print("ERROR: iEnvelopes failed, error ", GetLastError());
                                return(INIT_FAILED);
                               }
                          
                             lastBarTime = iTime(_Symbol, _Period, 0);
                             return(INIT_SUCCEEDED);
                            }

                          Displays a detailed table with the Expert Advisor (EA) parameters:

                          • checks the validity of the input parameters
                          • creates a handle for the built-in iEnvelopes indicator with the specified parameters
                          • initializes the "last bar" variable to track new bars
                          • sets a magic number for trading operations

                            The OnTick() function — the main execution loop


                            //+------------------------------------------------------------------+
                            //| OnTick function                                                  |
                            //+------------------------------------------------------------------+
                            void OnTick()
                              {
                               if(!IsNewBar()) return;
                            
                               int signal = GetSignal();
                               if(signal == 0) return;   // no signal → silent
                            
                               string signalType = (signal == 1) ? "BUY" : "SELL";
                               Print("SIGNAL: ", signalType);
                            
                               //--- check permission
                               if((signal == 1 && !buy) || (signal == -1 && !sell))
                                  return;
                            
                               int currentPos = GetCurrentPositionType();
                            
                               if(currentPos != 0)
                                 {
                                  if(InpReverseClose && currentPos != signal)
                                    {
                                     Print("Opposite signal -> closing position");
                                     CloseAllPositions();
                                    }
                                  else
                                    {
                                     return;   // same direction or reverse disabled
                                    }
                                 }
                            
                               OpenPosition(signal);
                              }

                            Purpose: It is called on every tick, but performs actions only on a new bar. Gets a signal, checks permissions, manages existing positions, and opens new positions.

                            The GetSignal() function — the brain of the trading system


                            //+------------------------------------------------------------------+
                            //| Get trading signal (silent, no debug prints)                     |
                            //+------------------------------------------------------------------+
                            int GetSignal()
                              {
                               int bars = iBars(_Symbol, _Period);
                               if(bars < InpLookbackBars + 10) return(0);
                            
                               double open[], high[], low[];
                               ArraySetAsSeries(open, true);
                               ArraySetAsSeries(high, true);
                               ArraySetAsSeries(low, true);
                            
                               int needed = InpLookbackBars + 5;
                               if(CopyOpen(_Symbol, _Period, 0, needed, open) < needed) return(0);
                               if(CopyHigh(_Symbol, _Period, 0, needed, high) < needed) return(0);
                               if(CopyLow(_Symbol, _Period, 0, needed, low) < needed) return(0);
                            
                               double open0 = open[0];
                               double open1 = open[1];
                            
                               double upperEnv0, lowerEnv0;
                               if(!GetEnvelopesValues(0, upperEnv0, lowerEnv0)) return(0);
                            
                               double maxHigh = high[2];
                               double minLow  = low[2];
                               for(int i = 2; i < 2 + InpLookbackBars; i++)
                                 {
                                  if(i >= ArraySize(high)) break;
                                  if(high[i] > maxHigh) maxHigh = high[i];
                                  if(low[i]  < minLow)  minLow  = low[i];
                                 }
                            
                               if(open0 > open1 && open0 > maxHigh && open0 > upperEnv0)
                                  return(1);   // BUY
                            
                               if(open0 < open1 && open0 < minLow && open0 < lowerEnv0)
                                  return(-1);  // SELL
                            
                               return(0);
                              }
                            
                            This function:

                            • copies market data (OPEN, HIGH, LOW) for calculations,
                            • retrieves the Envelopes values for the current bar via GetEnvelopesValues(),
                            • calculates the maximum and minimum using the Pardo Method (bars from 2 to 2+N),
                            • checks ALL THREE CONDITIONS SIMULTANEOUSLY at OPEN[0],
                            • returns: 1 — BUY, -1 — SELL, 0 — no signal.

                              All conditions are checked on the current bar "0" (candlestick) — this ensures an immediate response to a trading signal.

                              The GetEnvelopesValues() Function — Working with the Indicator


                              //+------------------------------------------------------------------+
                              //| Get Envelopes values for the specified bar                       |
                              //+------------------------------------------------------------------+
                              bool GetEnvelopesValues(int barIndex, double &upperEnv, double &lowerEnv)
                                {
                                 double upperBuffer[1], lowerBuffer[1];
                              
                                 ResetLastError();
                                 int copiedUpper = CopyBuffer(handleEnvelopes, 0, barIndex, 1, upperBuffer);
                                 int copiedLower = CopyBuffer(handleEnvelopes, 1, barIndex, 1, lowerBuffer);
                              
                                 if(copiedUpper == 1 && copiedLower == 1)
                                   {
                                    if(upperBuffer[0] != EMPTY_VALUE && lowerBuffer[0] != EMPTY_VALUE &&
                                       upperBuffer[0] > 0 && lowerBuffer[0] > 0 &&
                                       MathIsValidNumber(upperBuffer[0]) && MathIsValidNumber(lowerBuffer[0]))
                                      {
                                       upperEnv = upperBuffer[0];
                                       lowerEnv = lowerBuffer[0];
                                       return(true);
                                      }
                                   }
                                 
                                 return(false);
                                }

                              Reads data from the iEnvelopes indicator buffers using CopyBuffer:

                              1. Buffer 0 — the upper Envelopes band
                              2. Buffer 1 — the lower Envelopes band
                              3. Checks data validity:
                                • The value is not equal to EMPTY_VALUE
                                • The value is greater than 0
                                • The value is a valid number (MathIsValidNumber)
                              4. Returns true if the read operation is successful

                                The OpenPosition() function — opening a position

                                //+------------------------------------------------------------------+
                                //| Open position                                                    |
                                //+------------------------------------------------------------------+
                                void OpenPosition(int signal)
                                  {
                                   double entryPrice = iOpen(_Symbol, _Period, 0); //--- OPEN[0]
                                   
                                   if(entryPrice <= 0)
                                     {
                                      Print("ERROR: Invalid open price");
                                      return;
                                     }
                                   
                                   double sl, tp;
                                   string typeStr = (signal == 1) ? "BUY" : "SELL";
                                
                                   CalculateSLTP(entryPrice, signal, sl, tp);
                                
                                   bool result = false;
                                   if(signal == 1)
                                      result = trade.Buy(InpLotSize, _Symbol, entryPrice, sl, tp, eaComment);
                                   else
                                      result = trade.Sell(InpLotSize, _Symbol, entryPrice, sl, tp, eaComment);
                                
                                   if(result)
                                     {
                                      Print("SUCCESS: OPEN ", typeStr, " @ ", DoubleToString(entryPrice, _Digits),
                                            " | SL: ", DoubleToString(sl, _Digits), " | TP: ", DoubleToString(tp, _Digits));
                                     }
                                   else
                                     {
                                      Print("ERROR: Opening ", typeStr, ": ", trade.ResultRetcodeDescription());
                                     }
                                   
                                   return;
                                  }
                                This function:
                                • uses the OPEN[0] price to enter the position,
                                • calculates SL and TP in points relative to the entry price,
                                • opens a position via CTrade::Buy() or CTrade::Sell(),
                                • logs the result of the trade.
                                  The Expert Advisor (EA) does not use an external indicator; instead, it calculates all conditions on its own based on the built-in iEnvelopes and market data.
                                  // ALL DATA IS SYNCHRONIZED AUTOMATICALLY:
                                  // 1. Market data (OPEN, HIGH, LOW) is retrieved directly from the trading terminal
                                  // 2. The Envelopes data is taken from the built-in iEnvelopes indicator
                                  // 3. All calculations are performed on the CURRENT BAR 0
                                  
                                  double buySignal[1], sellSignal[1];
                                  
                                  // Retrieving Envelopes values for the CURRENT bar (shift=0!)
                                  CopyBuffer(handleEnvelopes, 0, 0, 1, upperBuffer);  // Upper band
                                  CopyBuffer(handleEnvelopes, 1, 0, 1, lowerBuffer);  // Lower band
                                  
                                  // Check: value != EMPTY_VALUE
                                  if(upperBuffer[0] != EMPTY_VALUE && lowerBuffer[0] != EMPTY_VALUE)
                                  {
                                     // The signal is calculated based on OPEN[0] and the obtained values
                                  }

                                  Characteristics of the Expert Advisor's operation:

                                  Characteristic Description
                                  Immediate response The program reads signals on the current bar “0” and immediately opens a position
                                  Technical analysis Trades the trend + a breakout above the previous price high
                                  Proper risk management Customizable Stop Loss and Take Profit levels in points from the position entry price
                                  Using a magic number Manages only its own positions, without interfering with others
                                  Closing positions safely Iterating through a list of positions from the highest index to the lowest
                                  Detailed logging Complete information about each trade in the "Experts" journal
                                  Protection against repeated entries Trades only on new candlesticks (bars)
                                  Reversal mode Automatically closes opposite positions when a new trading signal is received

                                  Here is an example of how the PardoEA Expert Advisor works:

                                  PardoEA

                                  Detailed logging displays trading progress in the "Experts" tab of the MetaTrader 5 trading terminal.

                                  You choose the trading timeframe yourself and run the Expert Advisor (EA). The goal of trading is to make a profit. The main reasons why a trading system helps achieve this goal are its ability to present potential quantitative results, the verifiability of those results, the system’s objectivity, and its consistency.


                                    The Complete Guide to the Breakout_Bounce Universal Trading Strategy

                                    Description of the Trading Algorithm

                                    PHASE 1: Market Analysis and Level Calculation

                                    1. Start on a New Candlestick — the system is activated when each new bar opens
                                    2. Calculating Daily Levels — identifying the High/Low over the previous N days
                                    3. Retrieving Current Prices — current Ask and Bid values
                                      PHASE 2: Choosing a Strategy
                                      1. Analyzing Market Conditions — the user selects a strategy manually
                                      2. BREAKOUT MODE — entry in the direction of a level breakout
                                      3. BOUNCE MODE — entry in the opposite direction (bounce)
                                        PHASE 3: Checking Signals

                                        For the BREAKOUT strategy:

                                        • BUY signal: Ask &gt; Daily High Level (upward breakout of resistance)
                                        • SELL signal: Bid &lt; Daily Low Level (downward breakout of support)

                                          For the BOUNCE strategy:

                                          • BUY signal: Bid &lt; Daily Low Level (upward bounce from support)
                                          • SELL signal: Ask &gt; Daily High Level (downward bounce from resistance)
                                            PHASE 4: Position Management
                                            1. Closing Opposite Positions — automatic closing before opening a new position
                                            2. Checking for an Open Position — preventing duplication
                                            3. Opening a Position — BUY at Ask, SELL at Bid
                                            4. Setting SL/TP — calculating SL and TP levels
                                              PHASE 5: Visualization
                                              1. Drawing Arrows — blue for BUY, red for SELL
                                              2. Updating Information — displaying statistics on the chart
                                                PHASE 6: Monitoring and Exit
                                                1. Continuous Monitoring — tracking the open position
                                                2. Exit Conditions — TP or SL triggering, a new opposite signal
                                                3. Closing a Position — exiting the market
                                                  PHASE 7: Post-Trade Processing
                                                  1. Deleting Graphical Objects — clearing arrows for closed positions
                                                  2. Updating Statistics — calculating and displaying results
                                                  3. Preparing for the Next Candlestick — resetting states and waiting

                                                  KEY FEATURES OF THE ALGORITHM

                                                  1. DUAL STRATEGY LOGIC

                                                  • BREAKOUT (LEVEL BREAKOUT) — for trending markets; trading in the direction of the level breakout
                                                  • BOUNCE (BOUNCE OFF LEVELS) — for range-bound markets; trading on bounces from key levels
                                                  • Manual Selection by the user
                                                  2. RISK MANAGEMENT
                                                  • Automatic SL/TP Calculation
                                                  • Preventing Duplicate Positions
                                                  • Closing Opposite Positions
                                                    3. VISUAL GUIDANCE
                                                    • Colored entry arrows
                                                    • Automatic cleanup
                                                    • Detailed statistics on the chart
                                                      ADVANTAGES OF THE ALGORITHM:
                                                      1. Clear structure — logical division into phases
                                                      2. Strategy flexibility — two opposing strategies in one system
                                                      3. Full visibility — every step is clearly defined and visualized
                                                      4. Reliability — multiple checks before opening positions
                                                      5. Implementation of a systematic approach to management and monitoring

                                                        WE TRADE THE TWO "FACES" OF THE MARKET

                                                        Mode 1: breakout (BREAKOUT)
                                                        Mode 2: bounce (BOUNCE)
                                                        Trading the "Strength Continues the Move" market phase
                                                        Trading the market phase "Resistance is weakening"
                                                        Trading IN THE DIRECTION of the breakout
                                                        Trading AGAINST the breakout
                                                        Buy when the price is ABOVE
                                                        Buy when the price is BELOW
                                                        Sell when the price is BELOW
                                                        Sell when the price is ABOVE

                                                        Mathematical Model

                                                        PERIOD_D1 in CalculateDailyLevels():

                                                        datetime today = iTime(_Symbol, PERIOD_D1, 0);
                                                        CopyHigh(_Symbol, PERIOD_D1, 1, InpDayPeriod, highArray);

                                                        Levels are always calculated based on the daily timeframe.
                                                        InpDayPeriod = the number of days to search for an extreme (2–60 days).
                                                        The execution timeframe (M1–H4) determines only the frequency at which conditions are checked.

                                                        Trading Signal Logic:

                                                        Levels = MAX(High[D1...DN]) and MIN(Low[D1...DN]) for N days
                                                            Where N = InpDayPeriod (optimized parameter: 3, 5, 7, 10, 14)       
                                                            
                                                            

                                                        BREAKOUT (breakout_bounce = true): BUY = Ask > DayHighLevel // Price broke out above the daily high SELL = Bid < DayLowLevel // Price broke out below the daily low BOUNCE (breakout_bounce = false): BUY = Bid < DayLowLevel // Price moved below the low (waiting for an upward bounce) SELL = Ask > DayHighLevel // Price moved above the high (waiting for a downward bounce)

                                                        Note: In the Expert Advisor (EA), we read the trading signal for a breakout/bounce from the symbol’s quote value on the daily timeframe. Mode switching is designed for ease of use (one variable — two completely different strategies). The external variables speak for themselves (the code is thoroughly commented).

                                                        //══════════════════════════════════════════════════════════════════════════════════
                                                        // GROUP 1: BASIC TRADING SETTINGS
                                                        //══════════════════════════════════════════════════════════════════════════════════
                                                        input group "══════════════════ BASIC TRADING SETTINGS ═══════════════════"
                                                        input double   InpLotSize = 0.1;           // Lot size
                                                        input int      InpMagicNumber = 1;         // Magic number
                                                        input int      InpSlippage = 1000;         // Slippage (points)
                                                        
                                                        //══════════════════════════════════════════════════════════════════════════════════
                                                        // GROUP 2: RISK MANAGEMENT
                                                        //══════════════════════════════════════════════════════════════════════════════════
                                                        input group "══════════════════ RISK MANAGEMENT ═══════════════════"
                                                        input double   InpStopLoss = 200;          // Stop Loss (points), 0 = no stop loss
                                                        input double   InpTakeProfit = 200;        // Take Profit (points), 0 = no take profit
                                                        
                                                        //══════════════════════════════════════════════════════════════════════════════════
                                                        // GROUP 3: PARDO SYSTEM STRATEGY
                                                        //══════════════════════════════════════════════════════════════════════════════════
                                                        input group "══════════════════ PARDO SYSTEM STRATEGY ═══════════════════"
                                                        input bool     breakout_bounce = true;     // Strategy: true = Breakout, false = Bounce
                                                        input int      InpDayPeriod = 3;           // Number of days for finding extremes
                                                        
                                                        //══════════════════════════════════════════════════════════════════════════════════
                                                        // GROUP 4: VISUALIZATION AND LOGGING SETTINGS
                                                        //══════════════════════════════════════════════════════════════════════════════════
                                                        input group "══════════════════ VISUALIZATION SETTINGS ═══════════════════"
                                                        input bool     InpEnableLogging = true;    // Enable logging
                                                        input bool     InpEnableVisuals = true;    // Enable visualization
                                                        input color    InpHighLineColor = clrDodgerBlue;  // High line color
                                                        input color    InpLowLineColor = clrRed;   // Low line color
                                                        input color    InpBuyArrowColor = clrBlue; // BUY arrows color
                                                        input color    InpSellArrowColor = clrRed; // SELL arrow color
                                                        input bool breakout_bounce = true; // true = BREAKOUT, false = BOUNCE

                                                        Mathematical Basis of the System (Calculation of Key Levels)

                                                        DayHighLevel = MAX(High[1..N days])
                                                        DayLowLevel  = MIN(Low[1..N days])
                                                        
                                                        Where N = InpDayPeriod (default is 3 days)
                                                        

                                                        Let's take a look at the main functions.

                                                        //+------------------------------------------------------------------+
                                                        //| Expert initialization function                                   |
                                                        //+------------------------------------------------------------------+
                                                        int OnInit()
                                                          {
                                                        //--- Initialize trading objects
                                                           Trade.SetExpertMagicNumber(InpMagicNumber);
                                                           Trade.SetDeviationInPoints(InpSlippage);
                                                           Trade.SetTypeFilling(ORDER_FILLING_FOK);
                                                           
                                                        //--- Initialize symbol information
                                                           if(!SymbolInfo.Name(_Symbol))
                                                             {
                                                              Print("Error: Failed to initialize symbol " + _Symbol);
                                                              return(INIT_FAILED);
                                                             }
                                                           SymbolInfo.RefreshRates();
                                                           
                                                        //--- Initialize graphical objects (using “_” as the prefix)
                                                           highLineName = "_HIGH_" + _Symbol + "_" + IntegerToString(InpDayPeriod);
                                                           lowLineName  = "_LOW_"  + _Symbol + "_" + IntegerToString(InpDayPeriod);
                                                           
                                                        //--- Display startup information
                                                           Print("");
                                                           Print("--- BREAKOUT/BOUNCE SYSTEM v6.5 (PARDO METHODOLOGY)");
                                                           Print("");
                                                           Print("---   • Symbol: " + _Symbol);
                                                           Print("---   • Timeframe: " + EnumToString(_Period));
                                                           Print("---   • Strategy Mode: " + (breakout_bounce ? "BREAKOUT" : "BOUNCE"));
                                                           Print("---   • Day Period: " + IntegerToString(InpDayPeriod));
                                                           Print("---   • Lot Size: " + DoubleToString(InpLotSize, 2));
                                                           Print("---   • SL/TP: " + DoubleToString(InpStopLoss, 0) + "/" + DoubleToString(InpTakeProfit, 0) + " pts");
                                                           Print("---   • Magic Number: " + IntegerToString(InpMagicNumber));
                                                           Print("---   • Visualization: " + (InpEnableVisuals ? "ON" : "OFF"));
                                                           Print("---   • Arrows: BLUE for BUY, RED for SELL");
                                                           Print("");
                                                           
                                                           if(breakout_bounce)
                                                             {
                                                              Print("---   ENTRY CONDITIONS (BREAKOUT MODE):");
                                                              Print("---   • BUY: Ask > Daily High (Breakout upward)");
                                                              Print("---   • SELL: Bid < Daily Low (Breakout downward)");
                                                             }
                                                           else
                                                             {
                                                              Print("---   ENTRY CONDITIONS (BOUNCE MODE):");
                                                              Print("---   • BUY: Bid < Daily Low (Expect bounce up from level)");
                                                              Print("---   • SELL: Ask > Daily High (Expect bounce down from level)");
                                                             }
                                                           Print("");
                                                           
                                                           return(INIT_SUCCEEDED);
                                                          }
                                                        This function is responsible for preparing the system for operation and configuring all settings. It:
                                                        • configures the CTrade trading object with a magic number,
                                                        • creates unique names for graphical objects,
                                                        • displays a detailed banner with strategy settings,
                                                        • displays the entry conditions for the selected mode.
                                                        Function 2: OnTick() — main loop
                                                        //+------------------------------------------------------------------+
                                                        //| Expert tick function                                             |
                                                        //+------------------------------------------------------------------+
                                                        void OnTick()
                                                          {
                                                        //--- Check for a new bar (only one signal per bar)
                                                           if(!IsNewBar())
                                                              return;
                                                           
                                                        //--- Update status
                                                           currentStatus = "WAITING";
                                                           
                                                        //--- Check the trading context
                                                           if(!CheckTradeContext())
                                                              return;
                                                           
                                                        //--- Calculate daily levels
                                                           if(!CalculateDailyLevels())
                                                             {
                                                              currentStatus = "CALC_ERROR";
                                                              UpdateChartInfo();
                                                              return;
                                                             }
                                                           
                                                        //--- Visualize levels (only in live trading)
                                                           if(InpEnableVisuals && !MQLInfoInteger(MQL_TESTER))
                                                              UpdateVisualLevels();
                                                           
                                                        //--- Get current prices
                                                           SymbolInfo.RefreshRates();
                                                           double currentAsk = SymbolInfo.Ask();
                                                           double currentBid = SymbolInfo.Bid();
                                                           
                                                        //--- Get a trading signal
                                                           int signal = GetTradingSignal(currentAsk, currentBid);
                                                           
                                                        //--- Process signal
                                                           switch(signal)
                                                             {
                                                              case 1:  // BUY signal
                                                                 ProcessBuySignal(currentAsk);
                                                                 break;
                                                                 
                                                              case -1: // SELL signal
                                                                 ProcessSellSignal(currentBid);
                                                                 break;
                                                                 
                                                              default: // No signal
                                                                 lastBuySignal = false;
                                                                 lastSellSignal = false;
                                                                 currentStatus = "NO_SIGNAL";
                                                                 break;
                                                             }
                                                           
                                                        //--- Update chart information
                                                           UpdateChartInfo();
                                                           return;
                                                          }
                                                        

                                                        This function:

                                                        • Works ONLY on a new "bar" (candlestick) — protection against multiple entries,
                                                        • checks whether trading is possible,
                                                        • calculates current levels,
                                                        • displays the levels on the chart,
                                                        • receives a signal based on the selected strategy,
                                                        • executes a trade when a signal is received,
                                                        • updates the information panel.

                                                          The CalculateDailyLevels() function — calculation of key levels

                                                          //+------------------------------------------------------------------+
                                                          //| Daily level calculation (key function)                           |
                                                          //+------------------------------------------------------------------+
                                                          bool CalculateDailyLevels()
                                                            {
                                                          //--- Check: Have the levels already been calculated for today?
                                                             datetime today = iTime(_Symbol, PERIOD_D1, 0);
                                                             if(dayLevelsDate == today && dayHighLevel > 0 && dayLowLevel > 0)
                                                                return(true); // The levels are current
                                                             
                                                          //--- Copy high data for the specified number of days
                                                             double highArray[];
                                                             int copiedHigh = CopyHigh(_Symbol, PERIOD_D1, 1, InpDayPeriod, highArray);
                                                             
                                                          //--- Copy the low data for the specified number of days
                                                             double lowArray[];
                                                             int copiedLow = CopyLow(_Symbol, PERIOD_D1, 1, InpDayPeriod, lowArray);
                                                             
                                                          //--- Check whether the data was copied successfully
                                                             if(copiedHigh <= 0 || copiedLow <= 0)
                                                               {
                                                                Print("Error: Insufficient data for level calculation");
                                                                return(false);
                                                               }
                                                             
                                                          //--- Find the highest high and the lowest low over the period
                                                             dayHighLevel = highArray[0];
                                                             dayLowLevel  = lowArray[0];
                                                             
                                                             for(int i = 1; i < copiedHigh; i++)
                                                               {
                                                                if(highArray[i] > dayHighLevel)
                                                                   dayHighLevel = highArray[i];
                                                               }
                                                             
                                                             for(int i = 1; i < copiedLow; i++)
                                                               {
                                                                if(lowArray[i] < dayLowLevel)
                                                                   dayLowLevel = lowArray[i];
                                                               }
                                                             
                                                          //--- Normalize to the correct number of digits
                                                             dayHighLevel = NormalizeDouble(dayHighLevel, _Digits);
                                                             dayLowLevel  = NormalizeDouble(dayLowLevel,  _Digits);
                                                             dayLevelsDate = today;
                                                             
                                                          //--- Log if enabled
                                                             if(InpEnableLogging)
                                                               {
                                                                Print("--- Levels updated: High=" + DoubleToString(dayHighLevel, _Digits) + 
                                                                      " Low=" + DoubleToString(dayLowLevel, _Digits) + 
                                                                      " for " + IntegerToString(InpDayPeriod) + " days");
                                                               }
                                                             
                                                             return(true);
                                                            }
                                                          

                                                          Function:

                                                          • checks whether the levels for the current day have already been calculated,
                                                          • copies the HIGH and LOW data for the specified number of days,
                                                          • finds the MAXIMUM High and the MINIMUM Low for the period,
                                                          • saves the levels and the calculation date.

                                                            KEY POINT: levels are recalculated only once a day.

                                                            GetTradingSignal() — the system's intelligence: signal generation

                                                            int GetTradingSignal(double ask, double bid)
                                                            {
                                                               //--- BREAKOUT MODE
                                                               if(breakout_bounce)
                                                                 {
                                                                  // BREAKOUT: Entry in the direction of the breakout
                                                                  bool buySignal = (ask > dayHighLevel);   // Breakout above the high on the daily timeframe
                                                                  bool sellSignal = (bid < dayLowLevel);   // A breakout below the low based on data from the daily timeframe
                                                                  
                                                                  return ProcessSignal(buySignal, sellSignal, ask, bid, "BREAKOUT");
                                                                 }
                                                               else
                                                               //--- BOUNCE MODE
                                                                 {
                                                                  // BOUNCE: Entry in the opposite direction
                                                                  bool buySignal = (bid < dayLowLevel);    // The price has fallen below the low (we expect an upward bounce)
                                                                  bool sellSignal = (ask > dayHighLevel);  // The price has risen above the high (we expect a downward bounce)
                                                                  
                                                                  return ProcessSignal(buySignal, sellSignal, ask, bid, "BOUNCE");
                                                                 }
                                                            }

                                                            Generates signals based on the selected strategy.

                                                            1. Defines the selected strategy mode
                                                            2. For BREAKOUT:
                                                              • BUY on a breakout above the high (Ask above the level)
                                                              • SELL on a breakout below the low (Bid below the level)
                                                            3. For BOUNCE:
                                                              • BUY if the price falls below the low (expecting an upward bounce)
                                                              • SELL if the price rises above the high (expecting a downward bounce)
                                                            4. Calls ProcessSignal to process the conditions

                                                              The ProcessSignal() function — signal processing

                                                              //+------------------------------------------------------------------+
                                                              //| Process signal                                                   |
                                                              //+------------------------------------------------------------------+
                                                              int ProcessSignal(bool buyCondition, bool sellCondition, double ask, double bid, string strategy)
                                                                {
                                                                 if(buyCondition && sellCondition)
                                                                    return(0); // Signal conflict
                                                                 
                                                              //--- Process BUY signal
                                                                 if(buyCondition && !lastBuySignal)
                                                                   {
                                                                    totalSignals++;
                                                                    lastBuySignal  = true;
                                                                    lastSellSignal = false;
                                                                    
                                                                    if(InpEnableLogging)
                                                                       Print("--- SIGNAL: " + strategy + " BUY | Price: " + DoubleToString(ask, _Digits) + 
                                                                             " | Level: " + DoubleToString(dayHighLevel, _Digits));
                                                                    
                                                                    return(1);
                                                                   }
                                                                 
                                                              //--- Process SELL signal
                                                                 if(sellCondition && !lastSellSignal)
                                                                   {
                                                                    totalSignals++;
                                                                    lastSellSignal = true;
                                                                    lastBuySignal  = false;
                                                                    
                                                                    if(InpEnableLogging)
                                                                       Print("--- SIGNAL: " + strategy + " SELL | Price: " + DoubleToString(bid, _Digits) + 
                                                                             " | Level: " + DoubleToString(dayLowLevel, _Digits));
                                                                    
                                                                    return(-1);
                                                                   }
                                                                 
                                                              //--- Reset flags if the conditions no longer exist
                                                                 if(!buyCondition)
                                                                    lastBuySignal = false;
                                                                 if(!sellCondition)
                                                                    lastSellSignal = false;
                                                                 
                                                                 return(0);
                                                                }

                                                              This function:

                                                              • checks for conflicting signals (simultaneous BUY and SELL),
                                                              • prevents duplicate signals via the lastBuySignal and lastSellSignal flags,
                                                              • increases the signal counter,
                                                              • logs the signal when logging is enabled,
                                                              • returns the signal code: 1 = BUY, -1 = SELL, 0 = no signal.

                                                                The ProcessBuySignal() function — executing a BUY trade

                                                                //+------------------------------------------------------------------+
                                                                //| Process BUY signal                                               |
                                                                //+------------------------------------------------------------------+
                                                                void ProcessBuySignal(double entryPrice)
                                                                  {
                                                                //--- Close opposite positions (SELL)
                                                                   ClosePositionsByType(POSITION_TYPE_SELL);
                                                                   
                                                                //--- Check for an existing BUY position
                                                                   if(CountPositionsByType(POSITION_TYPE_BUY) > 0)
                                                                      return;
                                                                   
                                                                //--- Calculate Stop Loss and Take Profit
                                                                   double sl = CalculateStopLoss(entryPrice, POSITION_TYPE_BUY);
                                                                   double tp = CalculateTakeProfit(entryPrice, POSITION_TYPE_BUY);
                                                                   
                                                                //--- Create a trade comment
                                                                   string strategyTag = breakout_bounce ? "BREAKOUT" : "BOUNCE";
                                                                   string comment = StringFormat("%s_B|D%d|S#%d", strategyTag, InpDayPeriod, totalSignals);
                                                                   
                                                                //--- Open position
                                                                   currentStatus = strategyTag + "_BUY_PROCESSING";
                                                                   
                                                                   if(Trade.Buy(InpLotSize, _Symbol, entryPrice, sl, tp, comment))
                                                                     {
                                                                      ulong ticket = Trade.ResultOrder();
                                                                      totalTrades++;
                                                                      currentStatus = strategyTag + "_BUY_OPENED";
                                                                      
                                                                      //--- Draw a blue BUY arrow (using the "_" prefix)
                                                                      if(InpEnableVisuals && !MQLInfoInteger(MQL_TESTER))
                                                                        {
                                                                         string arrowName = "_BUY_" + IntegerToString(ticket);
                                                                         
                                                                         if(ObjectCreate(0, arrowName, OBJ_ARROW_BUY, 0, TimeCurrent(), entryPrice))
                                                                           {
                                                                            ObjectSetInteger(0, arrowName, OBJPROP_COLOR, InpBuyArrowColor);
                                                                            ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 2);
                                                                            ObjectSetInteger(0, arrowName, OBJPROP_BACK, false);
                                                                            ObjectSetInteger(0, arrowName, OBJPROP_SELECTABLE, false);
                                                                            
                                                                            Print("--- BLUE BUY arrow drawn: " + arrowName);
                                                                           }
                                                                        }
                                                                      
                                                                      if(InpEnableLogging)
                                                                         Print("--- SUCCESS: " + strategyTag + " BUY #" + IntegerToString(ticket) + 
                                                                               " | Price: " + DoubleToString(entryPrice, _Digits));
                                                                     }
                                                                   else
                                                                     {
                                                                      currentStatus = "BUY_ERROR";
                                                                      Print("--- Error opening BUY: " + Trade.ResultRetcodeDescription());
                                                                     }
                                                                   return;   
                                                                  }
                                                                
                                                                Function:
                                                                • closes any opposite positions (SELL), if present,
                                                                • checks whether a BUY position is already open,
                                                                • calculates the Stop Loss and Take Profit levels,
                                                                • generates an informative comment for the trade,
                                                                • opens a position using Trade.Buy(),
                                                                • draws a blue BUY arrow on the chart,
                                                                • logs a successful opening.

                                                                  UpdateVisualLevels() — visual representation

                                                                  //+------------------------------------------------------------------+
                                                                  //| Visualize levels on the chart                                    |
                                                                  //+------------------------------------------------------------------+
                                                                  void UpdateVisualLevels()
                                                                    {
                                                                     if(!InpEnableVisuals)
                                                                        return;
                                                                     
                                                                  //--- High line
                                                                     if(ObjectFind(0, highLineName) < 0 || dayHighLevel != ObjectGetDouble(0, highLineName, OBJPROP_PRICE))
                                                                       {
                                                                        ObjectDelete(0, highLineName);
                                                                        if(ObjectCreate(0, highLineName, OBJ_HLINE, 0, 0, dayHighLevel))
                                                                          {
                                                                           ObjectSetInteger(0, highLineName, OBJPROP_COLOR, InpHighLineColor);
                                                                           ObjectSetInteger(0, highLineName, OBJPROP_STYLE, STYLE_DASH);
                                                                           ObjectSetInteger(0, highLineName, OBJPROP_WIDTH, 2);
                                                                           ObjectSetString(0, highLineName, OBJPROP_TEXT, "Daily High (" + IntegerToString(InpDayPeriod) + "d)");
                                                                           ObjectSetInteger(0, highLineName, OBJPROP_BACK, true);
                                                                          }
                                                                       }
                                                                     
                                                                  //--- Low line
                                                                     if(ObjectFind(0, lowLineName) < 0 || dayLowLevel != ObjectGetDouble(0, lowLineName, OBJPROP_PRICE))
                                                                       {
                                                                        ObjectDelete(0, lowLineName);
                                                                        if(ObjectCreate(0, lowLineName, OBJ_HLINE, 0, 0, dayLowLevel))
                                                                          {
                                                                           ObjectSetInteger(0, lowLineName, OBJPROP_COLOR, InpLowLineColor);
                                                                           ObjectSetInteger(0, lowLineName, OBJPROP_STYLE, STYLE_DASH);
                                                                           ObjectSetInteger(0, lowLineName, OBJPROP_WIDTH, 2);
                                                                           ObjectSetString(0, lowLineName, OBJPROP_TEXT, "Daily Low (" + IntegerToString(InpDayPeriod) + "d)");
                                                                           ObjectSetInteger(0, lowLineName, OBJPROP_BACK, true);
                                                                          }
                                                                       }
                                                                     return;   
                                                                    }

                                                                  This function:

                                                                  • draws a horizontal line at the level of the daily high,
                                                                  • draws a horizontal line at the daily low,
                                                                  • automatically updates the lines when the levels change,
                                                                  • adds labels to the lines.

                                                                    Visual benefits:

                                                                    • automatic updates — levels change every day
                                                                    • good design — clear dotted lines
                                                                    • informative labels — easy to understand even for beginners
                                                                    • background rendering — does not interfere with chart analysis

                                                                    ProcessBuySignal() / ProcessSellSignal() — trading decisions

                                                                    //+------------------------------------------------------------------+
                                                                    //| Process SELL signal                                              |
                                                                    //+------------------------------------------------------------------+
                                                                    void ProcessSellSignal(double entryPrice)
                                                                      {
                                                                    //--- Close opposite positions (BUY)
                                                                       ClosePositionsByType(POSITION_TYPE_BUY);
                                                                       
                                                                    //--- Check for an existing SELL position
                                                                       if(CountPositionsByType(POSITION_TYPE_SELL) > 0)
                                                                          return;
                                                                       
                                                                    //--- Calculate SL and TP
                                                                       double sl = CalculateStopLoss(entryPrice, POSITION_TYPE_SELL);
                                                                       double tp = CalculateTakeProfit(entryPrice, POSITION_TYPE_SELL);
                                                                       
                                                                    //--- Create a trade comment
                                                                       string strategyTag = breakout_bounce ? "BREAKOUT" : "BOUNCE";
                                                                       string comment = StringFormat("%s_S|D%d|S#%d", strategyTag, InpDayPeriod, totalSignals);
                                                                       
                                                                    //--- Open position
                                                                       currentStatus = strategyTag + "_SELL_PROCESSING";
                                                                       
                                                                       if(Trade.Sell(InpLotSize, _Symbol, entryPrice, sl, tp, comment))
                                                                         {
                                                                          ulong ticket = Trade.ResultOrder();
                                                                          totalTrades++;
                                                                          currentStatus = strategyTag + "_SELL_OPENED";
                                                                          
                                                                          //--- Draw a red SELL arrow (using the "_" prefix)
                                                                          if(InpEnableVisuals && !MQLInfoInteger(MQL_TESTER))
                                                                            {
                                                                             string arrowName = "_SELL_" + IntegerToString(ticket);
                                                                             
                                                                             if(ObjectCreate(0, arrowName, OBJ_ARROW_SELL, 0, TimeCurrent(), entryPrice))
                                                                               {
                                                                                ObjectSetInteger(0, arrowName, OBJPROP_COLOR, InpSellArrowColor);
                                                                                ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 2);
                                                                                ObjectSetInteger(0, arrowName, OBJPROP_BACK, false);
                                                                                ObjectSetInteger(0, arrowName, OBJPROP_SELECTABLE, false);
                                                                                
                                                                                Print("--- RED SELL arrow drawn: " + arrowName);
                                                                               }
                                                                            }
                                                                          
                                                                          if(InpEnableLogging)
                                                                             Print("--- SUCCESS: " + strategyTag + " SELL #" + IntegerToString(ticket) + 
                                                                                   " | Price: " + DoubleToString(entryPrice, _Digits));
                                                                         }
                                                                       else
                                                                         {
                                                                          currentStatus = "SELL_ERROR";
                                                                          Print("--- Error opening SELL: " + Trade.ResultRetcodeDescription());
                                                                         }
                                                                       return;  
                                                                      }

                                                                    Smart features of the trading module:

                                                                    • Automatic closing of opposite positions — risk hedging
                                                                    • Protection against repeated entries — one position per signal
                                                                    • Dynamic SL/TP — calculated in points, not prices
                                                                    • Detailed comments — full history in one click
                                                                    • Visual markers — arrows for manual verification

                                                                    UpdateChartInfo() — visual display on the chart

                                                                    //+------------------------------------------------------------------+
                                                                    //| Update chart information                                         |
                                                                    //+------------------------------------------------------------------+
                                                                    void UpdateChartInfo()
                                                                      {
                                                                    //--- Check and delete arrows for closed positions
                                                                       CheckAndDeleteArrows();
                                                                       
                                                                    //--- Calculate current profit
                                                                       double currentProfit = CalculateCurrentProfit();
                                                                       
                                                                    //--- Create an information panel
                                                                       string modeText = breakout_bounce ? "BREAKOUT" : "BOUNCE";
                                                                       string profitText = (currentProfit >= 0) ? 
                                                                                           "Profit: +$" + DoubleToString(currentProfit, 2) : 
                                                                                           "Loss: -$"  + DoubleToString(MathAbs(currentProfit), 2);
                                                                       
                                                                       string info = 
                                                                          "╔═══════════ " + modeText + " v6.5 (PARDO) ═══════════╗\n" +
                                                                          "║ Status: " + currentStatus + "\n" +
                                                                          "║ Time: " + TimeToString(TimeCurrent(), TIME_SECONDS) + "\n" +
                                                                          "║ Magic: " + IntegerToString(InpMagicNumber) + "\n" +
                                                                          "╠═════════════════════════════════════════════════════╣\n" +
                                                                          "║ TRADING LEVELS (LAST " + IntegerToString(InpDayPeriod) + " DAYS):\n" +
                                                                          "║ • Daily High: " + DoubleToString(dayHighLevel, _Digits) + "\n" +
                                                                          "║ • Daily Low:  " + DoubleToString(dayLowLevel,  _Digits) + "\n" +
                                                                          "╠═════════════════════════════════════════════════════╣\n" +
                                                                          "║ STATISTICS:\n" +
                                                                          "║ • Signals: " + IntegerToString(totalSignals) + "\n" +
                                                                          "║ • Trades:  " + IntegerToString(totalTrades) + "\n" +
                                                                          "║ • Open:    " + IntegerToString(CountPositions()) + "\n" +
                                                                          "╠═════════════════════════════════════════════════════╣\n" +
                                                                          "║  " + profitText + "\n" +
                                                                          "╠═════════════════════════════════════════════════════╣\n" +
                                                                          "║ SIGNALS FOR " + modeText + " MODE:\n" +
                                                                          "║ • " + (breakout_bounce ? "BUY:  Ask > Daily High (BREAKOUT ↑)" : "BUY:  Bid < Daily Low (BOUNCE ↑)") + "\n" +
                                                                          "║ • " + (breakout_bounce ? "SELL: Bid < Daily Low (BREAKOUT ↓)" : "SELL: Ask > Daily High (BOUNCE ↓)") + "\n" +
                                                                          "║ • ARROWS: BLUE = BUY, RED = SELL\n" +
                                                                          "╚═════════════════════════════════════════════════════╝";
                                                                       
                                                                       Comment(info);
                                                                       return;
                                                                      }

                                                                    It:

                                                                    • calls CheckAndDeleteArrows() to clear the chart,
                                                                    • calculates the current profit/loss for open positions,
                                                                    • creates an attractive framed information panel,
                                                                    • displays status, time, levels, statistics, and profit,
                                                                    • displays the entry conditions for the current mode,
                                                                    • updates the information on each bar.

                                                                    Information panel:

                                                                    • System mode — BREAKOUT or BOUNCE is visible at a glance
                                                                    • Key Levels — Current DayHigh/DayLow
                                                                    • Statistics — Signals, Trades, Open Positions
                                                                    • Time and Magic Number — Full Control
                                                                    • Beautiful Design — Pseudographic Symbols

                                                                      The OnDeinit() function — complete cleanup

                                                                      //+------------------------------------------------------------------+
                                                                      //| Expert deinitialization function                                 |
                                                                      //+------------------------------------------------------------------+
                                                                      void OnDeinit(const int reason)
                                                                        {
                                                                      //--- Remove main graphical objects (level lines)
                                                                         if(ObjectFind(0, highLineName) >= 0)
                                                                            ObjectDelete(0, highLineName);
                                                                         if(ObjectFind(0, lowLineName) >= 0)
                                                                            ObjectDelete(0, lowLineName);
                                                                         
                                                                      //--- Remove ALL objects with the "_" prefix (arrows)
                                                                         DeleteAllObjectsWithUnderscore();
                                                                         
                                                                      //--- Display final statistics
                                                                         PrintFinalStats(reason);
                                                                         return;
                                                                        }
                                                                      

                                                                      Removes the horizontal level lines and also calls DeleteAllPardoObjects() to delete all arrows and displays detailed final performance statistics.

                                                                      The DeleteAllObjectsWithUnderscore() function — bulk deletion

                                                                      //+------------------------------------------------------------------+
                                                                      //| Delete all objects with the "_" prefix                           |
                                                                      //+------------------------------------------------------------------+
                                                                      void DeleteAllObjectsWithUnderscore()
                                                                        {
                                                                         int totalObjects = ObjectsTotal(0);
                                                                         int deleted = 0;
                                                                         
                                                                         for(int i = totalObjects - 1; i >= 0; i--)
                                                                           {
                                                                            string objName = ObjectName(0, i);
                                                                            
                                                                            //--- Look for objects containing "_"
                                                                            if(StringFind(objName, "_") >= 0)
                                                                              {
                                                                               ObjectDelete(0, objName);
                                                                               deleted++;
                                                                              }
                                                                           }
                                                                         
                                                                         if(InpEnableLogging && deleted > 0)
                                                                            Print("--- Objects with '_' deleted: " + IntegerToString(deleted));
                                                                         return;   
                                                                        }
                                                                      The function iterates through all objects on the chart, finds objects with the "_" prefix and deletes them (arrows and lines), and logs the number of deleted objects.

                                                                        Features and Benefits of the Expert Advisor (EA)

                                                                        Characteristic Description
                                                                        Two strategies in one
                                                                        Switching a single variable between breakout and bounce
                                                                        Level visualization
                                                                        Automatic drawing of key levels on the chart
                                                                        Colored arrows
                                                                        Blue = BUY, Red = SELL, linked to tickets
                                                                        Automatic arrow cleanup
                                                                        Arrows are removed when positions are closed
                                                                        Information panel
                                                                        All trading data on the chart
                                                                        Spam protection
                                                                        Only one signal per bar
                                                                        Works on any timeframe
                                                                        From M1 upward

                                                                        The operation of the Expert Advisor (EA), with detailed comments, is shown in the following figure (the blue and red horizontal levels are the price breakout levels for entering a position):

                                                                        Breakout_Bounce trade


                                                                        Testing and Optimizing a Trading System Using the Robert Pardo Method

                                                                        The optimization technique is relatively simple, but it requires care. It includes five elements: selecting parameters, setting their ranges, determining the sample size, choosing the model evaluation method, and defining the final testing criterion.

                                                                        Only relevant parameters should be used in optimization. If a parameter's effect is insignificant, it is best to fix it or exclude it. When the significance of a parameter is unknown, it is tested by scanning a range: significant changes in performance indicate importance, while minor changes indicate that the parameter is of secondary importance.

                                                                        Optimization begins with selecting key variables and defining appropriate ranges. It is also important to select a sufficient amount of data to cover a range of market conditions and to use an appropriate method for assessing the model's stability.

                                                                        To verify its generalizability, the model is tested across different markets: the broader the coverage, the greater its stability and statistical reliability. Models that operate in only one market raise doubts unless they were specifically designed for that market. Testing is also conducted across different timeframes.

                                                                        Market conditions are constantly changing — trends, volatility, and liquidity. If the model performs inconsistently across different periods, this may indicate either unfavorable conditions or shortcomings in the model.

                                                                        Forward Test

                                                                        The forward test consists of two stages. First, optimization is performed, and the best parameters are selected. They are then tested on a new data segment, which simulates real trading.

                                                                        Thus, the model is trained on one historical data segment and tested on another. This is out-of-sample testing that allows its effectiveness to be evaluated objectively.

                                                                        The size of the optimization window is determined empirically. The model's validity period is influenced by volatility, the structure of trends, and the model's completeness. Typically, a model trained on two years of data remains effective for 3–6 months; one trained on one year of data remains effective for 1–2 months; and one trained on six months of data remains effective for 2–4 weeks.

                                                                        The forward window is typically 10–20% of the optimization period.


                                                                        Optimization in the Strategy Tester — From Chaos to Order

                                                                        Next, a similarly rigorous test should be applied to the selected instruments from the test basket across various market sectors. After that, you can evaluate the overall results.

                                                                        If the forward analysis across the entire set of tests proves unprofitable, you should check the testing structure for errors. If errors are found, they should be corrected and the analysis repeated. If there are no errors, however, the model should be discarded— its previous results were due to optimization, and the model itself is unstable.

                                                                        If the forward analysis shows only barely acceptable profitability, you should also verify that the test was conducted correctly. If errors are found, they must be corrected and the analysis repeated. If there are no errors, poor results indicate that the model is of mediocre quality. Nevertheless, its characteristics can be useful as part of a diversified portfolio.

                                                                        If a forward analysis of the entire basket of markets or part of it shows solid profitability, the model can be considered viable. It has passed one of the most rigorous verification stages and can be used in trading, while still exercising the necessary caution.

                                                                        So, let's move on to the most interesting part of this process — optimizing the values of the trading parameters of external variables.

                                                                        Recommendations for Optimization

                                                                        Parameter Range Step Explanation
                                                                        InpDayPeriod
                                                                        2–60
                                                                        2 Number of days for searching for levels (step 2 to speed up optimization)
                                                                        InpStopLoss
                                                                        100–1,000
                                                                        100 Stop Loss in points (for 5-digit quotes = 10–100 pips)
                                                                        InpTakeProfit
                                                                        200–2,000
                                                                        200 Take Profit in points (1:2 ratio to SL)
                                                                        breakout_bounce
                                                                        true / false
                                                                        - We test the two modes separately

                                                                        Below are three testing periods with overlapping time windows. The goal is to practice optimization and forward validation, and to select the best optimization run. If necessary, the number of periods can be increased to 10. To work through this more thoroughly, this example uses 3 periods:

                                                                        Period Optimization (In-Sample) Forward (Out-of-Sample)
                                                                        1 January 1, 2020 – December 31, 2023 (48 months) January 1, 2024 – December 31, 2024 (25%)
                                                                        2 January 1, 2021 – December 31, 2024 (48 months)
                                                                        January 1, 2025 – December 31, 2025 (25%)
                                                                        3 January 1, 2022 – December 31, 2025 (48 months)
                                                                        January 1, 2026 – March 31, 2026 (through the current date, no more than 25%)

                                                                        Now that we have the variable map and the calendar, we will perform the actual optimization.


                                                                        STEP 1: Preparation

                                                                        1. Make sure the Expert Advisor (EA) is compiled and located in the MQL5/Experts/Advisors folder; select the Expert Advisor (EA), trading symbol,
                                                                        2. Open the Strategy Tester (Ctrl+R)
                                                                        3. Select a symbol, timeframe, and date range

                                                                          STEP 2: Configuring Testing

                                                                          Mode: “Open prices only.” Timeframe: M15/H1. Range: the past 1–2 years. Deposit: 10,000 USD. Optimization criteria: profitability (total profit), profit factor (Profit Factor &gt; 1.5), maximum drawdown (&lt; 20%), number of trades (at least 100 for statistical purposes), average profit per trade.

                                                                          Validation of Results. In-sample optimization: 70% of the data; out-of-sample test: up to 20–30% of recent data; forward testing: on current data; testing on different symbols: EURUSD, GBPUSD, XAUUSD; testing on different timeframes: M15, H1, H4.

                                                                          Selecting a “plateau” variant of the variable values is a key step after optimizing a trading approach and is the optimization goal for further use in trading, as shown in the figure:

                                                                          plateau variant of variable values

                                                                          The optimization is as follows:

                                                                          Breakout_Bounce opt

                                                                          The optimization and testing reports are included in the attached archives.

                                                                          The Strategy Tester in MetaTrader 5 provides a unique opportunity to perform forward testing using predefined portions of the total period. You can set asymmetric entry conditions. For example: for BUY, a breakout above the 25-day high; for SELL, a breakout below the 5-day low.

                                                                          I performed the optimization using the following step sizes and settings:

                                                                          Breakout_Bounce opt step
                                                                           

                                                                          Model shelf life

                                                                          The size of the test window has a significant impact on the “lifespan” of a trading system. Systems that use optimization require periodic re-optimization to adapt to current market conditions. At the same time, models optimized over longer timeframes generally remain functional for longer. By contrast, systems with a short test window require more frequent re-optimization and are less robust.

                                                                          Typically, the interval between re-optimizations is a fraction of the test window length. A rule of thumb is between 1/8 and 1/4. For example, with an optimization window of 24 months, the system can operate effectively for 3 to 6 months.

                                                                          This is because the market is constantly changing. The farther away from the optimization point, the less reliable the forecasts are. If conditions remain stable, re-optimization may not be necessary, but in practice this almost never happens.

                                                                          A short testing window covers a limited range of market conditions. This model works well only in a familiar environment and loses its effectiveness when market conditions change, so it requires frequent readjustment. A long window, on the other hand, incorporates a wider range of market conditions, allowing the model to adapt better to new conditions and remain relevant for longer.

                                                                          In summary: short windows result in more sensitive but less stable systems; long windows result in more stable but less responsive systems. Optimization is the process of improving efficiency, and it is precisely a sound approach to optimization that distinguishes a successful trader from a casino gambler.


                                                                          Conclusion

                                                                          The algorithm for development, testing, and optimization described here is a practical guide, not a theoretical concept. The result should not be some abstract “improvement,” but rather specific artifacts and decisions:

                                                                          • a set of working files (indicator + Expert Advisor(s)) with documented entry/exit and risk management rules;
                                                                          • a test plan for the MetaTrader 5 Strategy Tester: a list of variables to be optimized and their ranges, rules for splitting the historical data (IS/OOS), selection criteria (e.g., Profit Factor, maximum drawdown, minimum number of trades), and the method for selecting a plateau;
                                                                          • a forward validation procedure and decision table: when the model is considered valid, when it requires re-optimization, and how often to re-optimize (as a guideline — 1/8 to 1/4 of the optimization window length);
                                                                          • practical recommendations for multi-symbol and multi-timeframe testing to improve statistical validity.

                                                                          Key cautions: avoid over-optimization, do not rely on a single “best” run, test the model across different markets and in forward testing, and always test on demo or controlled accounts before moving to live trading. This methodology provides a structured path from an idea to a reproducible, measurable, and adaptable trading system.

                                                                          The material and code are provided for educational purposes. Be sure to perform testing before using them on live accounts. Trading involves risks.

                                                                          File name Description
                                                                          Templates_sets_reports.zip Strategy Tester reports and set files containing the Expert Advisor's parameter values
                                                                          PardoSystem.mq5 Indicator based on R. Pardo's trading method
                                                                          Breakout_Bounce.mq5 Expert Advisor (EA) for level breakout/bounce trading conditions
                                                                          PardoEA.mq5 Expert Advisor (EA) based on R. Pardo's Method


                                                                          Translated from Russian by MetaQuotes Ltd.
                                                                          Original article: https://www.mql5.com/ru/articles/19520

                                                                          Attached files |
                                                                          Breakout_Bounce.mq5 (30.59 KB)
                                                                          PardoSystem.mq5 (12.29 KB)
                                                                          PardoEA.mq5 (10.8 KB)
                                                                          Last comments | Go to discussion (4)
                                                                          Stanislav Korotky
                                                                          Stanislav Korotky | 31 Mar 2026 at 13:33

                                                                          It says:

                                                                          Существует два подхода к созданию торговой системы. Первый подход использует логику...

                                                                          Where is the second attempt? I haven’t even seen a mention of it.

                                                                          Roman Shiredchenko
                                                                          Roman Shiredchenko | 31 Mar 2026 at 14:27
                                                                          Stanislav Korotky #:

                                                                          Written:

                                                                          Where’s the second attempt? I haven’t even seen a mention of it.

                                                                          In the second part, I’ll explain: the second approach is an empirical search, without any theoretical basis or explanation – like looking for a needle in a haystack. Although the second approach can lead to profitable trading models, it is characterised by a poor understanding of why the model works...
                                                                          Dmitriy Skub
                                                                          Dmitriy Skub | 1 Apr 2026 at 04:49
                                                                          So where’s the nice picture showing the result of the test run/tuning?))
                                                                          Roman Shiredchenko
                                                                          Roman Shiredchenko | 1 Apr 2026 at 12:35
                                                                          Dmitriy Skub #:
                                                                          So where’s the nice picture showing the result of the test run/tuning))?

                                                                          ) (that’s the key bit)

                                                                          Let’s keep this in the discussions – it doesn’t fit the narrative, but these are positive findings for real trading!

                                                                          Practical Modules from Other Languages in MQL5 (Part 08): MATPLOTLIB Practical Modules from Other Languages in MQL5 (Part 08): MATPLOTLIB
                                                                          Learn how to bring advanced plotting and data visualization to the MetaTrader 5 terminal by leveraging Matplotlib, one of Python's most powerful and widely used visualization libraries.
                                                                          Master the Z-Score: Building Mean-Reverting MQL5 Trading Systems Master the Z-Score: Building Mean-Reverting MQL5 Trading Systems
                                                                          This article explains how to use the Z-Score to quantify price deviations in standard deviations and apply the concept in MQL5. We cover the calculation, interpretation across market regimes, and the implementation of a custom indicator and three trading strategies as Expert Advisors. Readers get complete code examples and a testing workflow, plus practical notes on lookback selection, fat tails, execution timing, and risk controls.
                                                                          Price Action Analysis Toolkit Development (Part 82): Annotating Historical Bookmarks with Reliable Timeframe Switching Price Action Analysis Toolkit Development (Part 82): Annotating Historical Bookmarks with Reliable Timeframe Switching
                                                                          We implement visible bookmark annotations and a hand-off mechanism for cross-symbol and cross-timeframe recall. The navigator stores the target in terminal Global Variables, waits for stable bars after reinitialization, and rebuilds only matching markers with safe cleanup. Traders can inspect saved events in context and navigate back to them without reconstructing chart settings.
                                                                          Larry Williams Market Secrets (Part 18): Automating the Greatest Swing Value Breakout Strategy in MQL5 Larry Williams Market Secrets (Part 18): Automating the Greatest Swing Value Breakout Strategy in MQL5
                                                                          This article converts the Greatest Swing Value breakout rules into a configurable MQL5 Expert Advisor. It implements measurable setup conditions, failure-swing calculations, one-bar setup validity, and M1 breakout confirmation. The EA also supports broker-compatible risk management, manual or percentage-risk sizing, position control, and trade-server validation, providing a reproducible framework for independent Strategy Tester research across different markets and historical periods.