🚀 AI One-Click Generation of mql4 Strategy Code

Building a Trading EA Based on the Standard Deviation Channel in Mql4

mql4 | Published on: 2025/12/9
The following content is generated by EasyQuant .

Problem Description

Please help me code a trading strategy that uses a standard deviation channel to enter and exit trades.

Solution

Here is a complete, fully functional Expert Advisor (EA) for MetaTrader 4 that implements a Standard Deviation Channel (Linear Regression Channel) strategy.

Strategy Logic

This is a Mean Reversion strategy. It assumes that price moves within a statistical channel and tends to return to the center (Linear Regression Line).

  1. Calculation: It calculates a Linear Regression Line over a specified period, then calculates the Standard Deviation of prices relative to that line to create Upper and Lower bands.
  2. Buy Entry: When the price closes below the Lower Channel Line (Oversold).
  3. Sell Entry: When the price closes above the Upper Channel Line (Overbought).
  4. Exit: Trades are closed when price returns to the Middle Line (Regression Line), or via hard Stop Loss/Take Profit.

MQL4 Code

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

//--- Input Parameters
input string   Strategy_Settings = "=== Strategy Settings ===";
input int      ChannelPeriod     = 50;       // Linear Regression Period
input double   ChannelDeviations = 2.0;      // Standard Deviations for Bands
input int      AppliedPrice      = PRICE_CLOSE; // Applied Price (0=Close)

input string   Risk_Settings     = "=== Risk Management ===";
input double   LotSize           = 0.1;      // Fixed Lot Size
input int      StopLoss          = 500;      // Stop Loss in points (0 = disabled)
input int      TakeProfit        = 0;        // Hard Take Profit in points (0 = disabled)
input int      Slippage          = 3;        // Max Slippage in pips
input int      MagicNumber       = 123456;   // Magic Number to identify trades

//--- Global Variables
double g_middleLine, g_upperLine, g_lowerLine;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   if(ChannelPeriod < 2)
   {
      Alert("Channel Period must be greater than 1");
      return(INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // Clean up if necessary
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // 1. Calculate Channel Data for the previous completed bar (Shift 1)
   // We use Shift 1 to ensure the bar is closed and values are fixed (no repainting)
   CalculateRegChannel(1);

   // 2. Check for Exits (Mean Reversion)
   ManageOpenTrades();

   // 3. Check for Entries
   // Only open if we have no open trades for this symbol/magic number
   if(CountOpenTrades() == 0)
   {
      CheckEntryConditions();
   }
}

//+------------------------------------------------------------------+
//| Calculate Linear Regression Channel Values                       |
//+------------------------------------------------------------------+
void CalculateRegChannel(int shift)
{
   double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
   
   // Linear Regression Calculation (Least Squares Method)
   // X = Time index (0 to Period-1), Y = Price
   for(int i = 0; i < ChannelPeriod; i++)
   {
      double price = iMA(NULL, 0, 1, 0, MODE_SMA, AppliedPrice, shift + i);
      double x = i; 
      
      sumX  += x;
      sumY  += price;
      sumXY += x * price;
      sumX2 += x * x;
   }
   
   double slope = (ChannelPeriod * sumXY - sumX * sumY) / (ChannelPeriod * sumX2 - sumX * sumX);
   double intercept = (sumY - slope * sumX) / ChannelPeriod;
   
   // The Regression value at the requested shift (x=0 in our local calculation loop relative to start)
   g_middleLine = intercept + slope * 0; 
   
   // Calculate Standard Deviation relative to the Regression Line
   double sumSqDiff = 0;
   for(int i = 0; i < ChannelPeriod; i++)
   {
      double price = iMA(NULL, 0, 1, 0, MODE_SMA, AppliedPrice, shift + i);
      double regValue = intercept + slope * i;
      sumSqDiff += MathPow(price - regValue, 2);
   }
   
   double stdDev = MathSqrt(sumSqDiff / ChannelPeriod);
   
   // Set Bands
   g_upperLine = g_middleLine + (stdDev * ChannelDeviations);
   g_lowerLine = g_middleLine - (stdDev * ChannelDeviations);
}

//+------------------------------------------------------------------+
//| Check Entry Conditions                                           |
//+------------------------------------------------------------------+
void CheckEntryConditions()
{
   double closePrice = iClose(NULL, 0, 1); // Close of previous bar
   
   // Buy Signal: Price closed below the Lower Channel Line
   if(closePrice < g_lowerLine)
   {
      double sl = (StopLoss > 0) ? Ask - StopLoss * Point : 0;
      double tp = (TakeProfit > 0) ? Ask + TakeProfit * Point : 0;
      
      int ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, Slippage, sl, tp, "StdDev Buy", MagicNumber, 0, clrBlue);
      if(ticket < 0) Print("OrderSend failed with error #", GetLastError());
   }
   
   // Sell Signal: Price closed above the Upper Channel Line
   if(closePrice > g_upperLine)
   {
      double sl = (StopLoss > 0) ? Bid + StopLoss * Point : 0;
      double tp = (TakeProfit > 0) ? Bid - TakeProfit * Point : 0;
      
      int ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, Slippage, sl, tp, "StdDev Sell", MagicNumber, 0, clrRed);
      if(ticket < 0) Print("OrderSend failed with error #", GetLastError());
   }
}

//+------------------------------------------------------------------+
//| Manage Open Trades (Exit Logic)                                  |
//+------------------------------------------------------------------+
void ManageOpenTrades()
{
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
         {
            double closePrice = iClose(NULL, 0, 1);
            bool closeOrder = false;
            
            // Logic: Close Buy if price crosses back above Middle Line
            if(OrderType() == OP_BUY)
            {
               if(closePrice >= g_middleLine) closeOrder = true;
            }
            
            // Logic: Close Sell if price crosses back below Middle Line
            if(OrderType() == OP_SELL)
            {
               if(closePrice <= g_middleLine) closeOrder = true;
            }
            
            if(closeOrder)
            {
               bool result = false;
               if(OrderType() == OP_BUY)
                  result = OrderClose(OrderTicket(), OrderLots(), Bid, Slippage, clrNONE);
               else
                  result = OrderClose(OrderTicket(), OrderLots(), Ask, Slippage, clrNONE);
                  
               if(!result) Print("OrderClose failed with error #", GetLastError());
            }
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Count Open Trades for this EA                                    |
//+------------------------------------------------------------------+
int CountOpenTrades()
{
   int count = 0;
   for(int i = 0; i < OrdersTotal(); i++)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
            count++;
      }
   }
   return count;
}
//+------------------------------------------------------------------+

How to Use This Strategy

  1. Installation:

    • Open MetaTrader 4.
    • Press F4 to open the MetaEditor.
    • Click New -> Expert Advisor (template) -> Name it StdDevChannel.
    • Paste the code above into the file, replacing everything else.
    • Press F7 to Compile. Ensure there are no errors.
  2. Configuration (Inputs):

    • ChannelPeriod: The number of bars used to calculate the regression line (default 50). Higher numbers make the channel smoother/slower.
    • ChannelDeviations: The width of the channel (default 2.0). Increasing this (e.g., to 2.5 or 3.0) will result in fewer, but statistically more extreme trades.
    • StopLoss/TakeProfit: Defined in "Points". If you are on a 5-digit broker (e.g., EURUSD is 1.12345), 500 points = 50 pips.
  3. Logic Explanation:

    • Math: The EA calculates the Linear Regression slope and intercept mathematically within the code (function CalculateRegChannel). It does not rely on drawing objects on the chart, making it faster and suitable for the Strategy Tester.
    • Entry: It looks at the previous closed bar (Shift 1). If the previous bar closed outside the bands, it enters immediately on the new tick.
    • Exit: It checks every tick. If a Buy position exists and the previous bar closed above the Middle Line, it takes profit (Mean Reversion).

Q&A: Standard Deviation Channel Strategy

Q: Why does the strategy use Shift 1 instead of Shift 0?
A: Shift 0 represents the current, forming bar. The price moves up and down constantly. If we use Shift 0, the signal might appear and disappear within the same candle (repainting), causing the EA to open trades that look invalid once the bar closes. Using Shift 1 ensures the price is locked in.

Q: Can I use this for Trend Following instead of Mean Reversion?
A: Yes, but you would need to invert the logic in the code. For trend following (breakouts), you would Buy when price > Upper Line and Sell when price < Lower Line. The current code is set up for Mean Reversion (Buy Low, Sell High).

Q: How is this different from Bollinger Bands?
A: Bollinger Bands use a Simple Moving Average (SMA) as the center line. This strategy uses a Linear Regression Line. Linear Regression attempts to fit a straight line through the data points to minimize error, often providing a better representation of the current "fair value" trend than a lagging SMA.