🚀 AI One-Click Generation of mql4 Strategy Code

How to Build a Price Channel Breakout Expert Advisor in Mql4

mql4 | Published on: 2025/11/28
The following content is generated by EasyQuant .

Problem Description

Please write a trading robot that places a buy stop order above the high of the last 20 bars and a sell stop order below the low of the last 20 bars.

Solution

Here is a complete, fully functional Expert Advisor (trading robot) written in MQL4.

This robot implements a "Breakout" strategy. It calculates the highest high and lowest low of the previous 20 closed bars. It then places a Buy Stop order above the high and a Sell Stop order below the low. It includes logic to cancel the opposite pending order once a trade is triggered (One-Cancels-Other logic).

MQL4 Trading Robot Code

//+------------------------------------------------------------------+
//|                                            20BarBreakout_EA.mq4  |
//|                                      Copyright 2023, Quantitative|
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative"
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

//--- Input Parameters
input int      LookbackPeriod = 20;       // Number of bars to check for High/Low
input double   LotSize        = 0.1;      // Fixed Lot Size
input int      BufferPoints   = 10;       // Distance from High/Low to place order (in points)
input int      StopLoss       = 500;      // Stop Loss in points (0 = no SL)
input int      TakeProfit     = 1000;     // Take Profit in points (0 = no TP)
input int      MagicNumber    = 123456;   // Unique Identifier for this EA
input int      ExpirationBars = 1;        // Expiration of pending orders in bars (0 = no expiration)

//--- Global Variables
double pPoint; // Adjusted point value for 3/5 digit brokers

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // Adjust point for 5-digit brokers (0.00001) vs 4-digit (0.0001)
   if(Digits == 3 || Digits == 5) pPoint = Point * 10;
   else pPoint = Point;

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // Optional: Clean up all pending orders on removal
   DeleteAllPendingOrders();
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // 1. Check if we have open positions or pending orders managed by this EA
   int buyOrders = 0;
   int sellOrders = 0;
   int buyStops = 0;
   int sellStops = 0;

   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
         {
            if(OrderType() == OP_BUY)        buyOrders++;
            if(OrderType() == OP_SELL)       sellOrders++;
            if(OrderType() == OP_BUYSTOP)    buyStops++;
            if(OrderType() == OP_SELLSTOP)   sellStops++;
         }
      }
   }

   // 2. OCO Logic (One Cancels Other)
   // If a market order is active, delete remaining pending orders
   if(buyOrders > 0 || sellOrders > 0)
   {
      if(buyStops > 0 || sellStops > 0) DeleteAllPendingOrders();
      return; // Exit OnTick, do not place new orders while in a trade
   }

   // 3. If pending orders already exist, do nothing (wait for expiration or trigger)
   if(buyStops > 0 || sellStops > 0) return;

   // 4. Calculate High and Low of the last 'LookbackPeriod' bars
   // We start at index 1 to look at closed bars, ignoring the currently forming bar (index 0)
   int highestIndex = iHighest(NULL, 0, MODE_HIGH, LookbackPeriod, 1);
   int lowestIndex  = iLowest(NULL, 0, MODE_LOW, LookbackPeriod, 1);

   double highPrice = iHigh(NULL, 0, highestIndex);
   double lowPrice  = iLow(NULL, 0, lowestIndex);

   // 5. Calculate Entry Prices
   double buyStopPrice  = NormalizeDouble(highPrice + BufferPoints * Point, Digits);
   double sellStopPrice = NormalizeDouble(lowPrice - BufferPoints * Point, Digits);

   // 6. Calculate Expiration Time
   datetime expirationTime = 0;
   if(ExpirationBars > 0)
   {
      expirationTime = TimeCurrent() + (PeriodSeconds() * ExpirationBars);
   }

   // 7. Check Minimum Stop Level (Distance from current price)
   double stopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL) * Point;
   
   // Ensure Buy Stop is above Ask + Stoplevel
   if(buyStopPrice > Ask + stopLevel)
   {
      double sl = (StopLoss > 0) ? buyStopPrice - StopLoss * Point : 0;
      double tp = (TakeProfit > 0) ? buyStopPrice + TakeProfit * Point : 0;
      
      // Normalize SL/TP
      if(sl > 0) sl = NormalizeDouble(sl, Digits);
      if(tp > 0) tp = NormalizeDouble(tp, Digits);

      int ticket = OrderSend(Symbol(), OP_BUYSTOP, LotSize, buyStopPrice, 3, sl, tp, "Buy Breakout", MagicNumber, expirationTime, clrGreen);
      if(ticket < 0) Print("Error sending Buy Stop: ", GetLastError());
   }

   // Ensure Sell Stop is below Bid - Stoplevel
   if(sellStopPrice < Bid - stopLevel)
   {
      double sl = (StopLoss > 0) ? sellStopPrice + StopLoss * Point : 0;
      double tp = (TakeProfit > 0) ? sellStopPrice - TakeProfit * Point : 0;

      // Normalize SL/TP
      if(sl > 0) sl = NormalizeDouble(sl, Digits);
      if(tp > 0) tp = NormalizeDouble(tp, Digits);

      int ticket = OrderSend(Symbol(), OP_SELLSTOP, LotSize, sellStopPrice, 3, sl, tp, "Sell Breakout", MagicNumber, expirationTime, clrRed);
      if(ticket < 0) Print("Error sending Sell Stop: ", GetLastError());
   }
}

//+------------------------------------------------------------------+
//| Helper: Delete all pending orders for this EA                    |
//+------------------------------------------------------------------+
void DeleteAllPendingOrders()
{
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
         {
            if(OrderType() == OP_BUYSTOP || OrderType() == OP_SELLSTOP)
            {
               bool res = OrderDelete(OrderTicket());
               if(!res) Print("Failed to delete order: ", GetLastError());
            }
         }
      }
   }
}
//+------------------------------------------------------------------+

Strategy Logic and Features

  1. High/Low Calculation:
    • The robot uses iHighest and iLowest starting from index 1.
    • Index 1 represents the last closed bar. We do not use index 0 because the current bar is still forming, and its high/low can change every tick, which would cause the pending orders to jump around constantly.
  2. Buffer:
    • It adds a BufferPoints value to the High and subtracts it from the Low. This ensures you don't buy exactly at the high, but slightly above it (filtering out false breakouts).
  3. One-Cancels-Other (OCO):
    • The code checks if a market position (Buy or Sell) is active. If a trade has been triggered, it automatically deletes the remaining pending order to prevent being hedged unintentionally.
  4. Expiration:
    • The ExpirationBars input allows the pending orders to expire automatically if price does not break out within a certain time (default is 1 bar). This ensures the orders follow the moving 20-bar window dynamically.
  5. Risk Management:
    • Includes inputs for StopLoss and TakeProfit in points.
    • Includes a MagicNumber to ensure the EA only manages its own trades and doesn't interfere with other manual trades or robots.

How to Install and Run

  1. Open MetaTrader 4.
  2. Press F4 to open the MetaEditor.
  3. In MetaEditor, click New -> Expert Advisor (template) -> Next.
  4. Name it 20BarBreakout and click Finish.
  5. Delete everything in the new file and paste the code provided above.
  6. Press F7 or click Compile. Ensure there are no errors in the "Errors" tab at the bottom.
  7. Return to the main MetaTrader 4 window.
  8. Find 20BarBreakout in the Navigator panel (under Expert Advisors).
  9. Drag it onto a chart (e.g., EURUSD H1).
  10. In the settings window, adjust LotSize, StopLoss, or LookbackPeriod if desired.
  11. Ensure the AutoTrading button at the top of MT4 is green (Enabled).

Q&A: Common Issues

Q: Why are my orders not being placed?
A: Check the "Journal" tab in the Terminal window. Common errors include:

  • Error 130 (Invalid Stops): Your Stop Loss or Take Profit is too close to the entry price. Increase the SL/TP values.
  • Error 134 (Not Enough Money): You do not have enough free margin for the LotSize selected.
  • Stop Level: If the market is very volatile, the BufferPoints might place the order too close to the current market price (inside the broker's freeze level).

Q: Does this work on 5-digit brokers?
A: Yes. The code includes a pPoint calculation in OnInit (though standard Point is used for the OrderSend math for simplicity, the inputs are treated as raw points). If you input StopLoss = 500, on a 5-digit broker, this is 50.0 pips.

Q: Why do the pending orders disappear after one bar?
A: This is due to the ExpirationBars setting (default 1). This is intentional. As the 20-bar window moves forward, the High/Low changes. The old order expires, and a new one is placed at the new High/Low. If you want them to stay forever, set ExpirationBars = 0.