Problem Description
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).
- 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.
- Buy Entry: When the price closes below the Lower Channel Line (Oversold).
- Sell Entry: When the price closes above the Upper Channel Line (Overbought).
- 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
-
Installation:
- Open MetaTrader 4.
- Press
F4to open the MetaEditor. - Click
New->Expert Advisor (template)-> Name itStdDevChannel. - Paste the code above into the file, replacing everything else.
- Press
F7to Compile. Ensure there are no errors.
-
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.
-
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).
- Math: The EA calculates the Linear Regression slope and intercept mathematically within the code (function
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.