Generate code in 3 minutes
Enter your idea and AI generates runnable code.
Here is a complete, fully functional Expert Advisor (EA) for MetaTrader 4.
This EA uses a simple Moving Average Crossover strategy to determine entry points, but its primary focus is using the Average True Range (ATR) to calculate dynamic Stop Loss and Take Profit levels based on market volatility.
//+------------------------------------------------------------------+
//| ATR_Risk_EA.mq4 |
//| Copyright 2023, MetaQuotes |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative Assistant"
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//--- Input Parameters
input string RiskSettings = "--- Risk Settings ---"; // Section Title
input double InpLotSize = 0.1; // Trade Volume
input int InpATRPeriod = 14; // ATR Period
input double InpSLMultiplier= 1.5; // Stop Loss Multiplier (x ATR)
input double InpTPMultiplier= 2.0; // Take Profit Multiplier (x ATR)
input string StrategySettings = "--- Strategy Settings ---"; // Section Title
input int InpFastMA = 10; // Fast Moving Average Period
input int InpSlowMA = 20; // Slow Moving Average Period
input int InpMagicNum = 123456; // Magic Number
input int InpSlippage = 3; // Max Slippage (pips)
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Basic validation
if(InpATRPeriod <= 0 || InpFastMA <= 0 || InpSlowMA <= 0)
{
Print("Error: Periods must be greater than 0.");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. Check if we already have an open trade for this symbol/magic number
if(CountOpenTrades() > 0) return;
// 2. Define Indicators
// ATR for volatility calculation
double atrValue = iATR(NULL, 0, InpATRPeriod, 0);
// Moving Averages for Entry Signal (Shift 1 = closed bar, Shift 2 = previous closed bar)
double fastMA_curr = iMA(NULL, 0, InpFastMA, 0, MODE_EMA, PRICE_CLOSE, 1);
double slowMA_curr = iMA(NULL, 0, InpSlowMA, 0, MODE_EMA, PRICE_CLOSE, 1);
double fastMA_prev = iMA(NULL, 0, InpFastMA, 0, MODE_EMA, PRICE_CLOSE, 2);
double slowMA_prev = iMA(NULL, 0, InpSlowMA, 0, MODE_EMA, PRICE_CLOSE, 2);
// 3. Calculate Dynamic SL and TP distances based on ATR
double slDistance = atrValue * InpSLMultiplier;
double tpDistance = atrValue * InpTPMultiplier;
// 4. Trading Logic (Crossover)
// Buy Signal: Fast MA crosses above Slow MA
if(fastMA_curr > slowMA_curr && fastMA_prev <= slowMA_prev)
{
// Calculate Price Levels
double entryPrice = Ask;
double stopLoss = NormalizeDouble(entryPrice - slDistance, Digits);
double takeProfit = NormalizeDouble(entryPrice + tpDistance, Digits);
// Send Order
int ticket = OrderSend(Symbol(), OP_BUY, InpLotSize, entryPrice, GetSlippage(), stopLoss, takeProfit, "ATR Buy", InpMagicNum, 0, clrBlue);
if(ticket < 0) Print("OrderSend failed with error #", GetLastError());
}
// Sell Signal: Fast MA crosses below Slow MA
if(fastMA_curr < slowMA_curr && fastMA_prev >= slowMA_prev)
{
// Calculate Price Levels
double entryPrice = Bid;
double stopLoss = NormalizeDouble(entryPrice + slDistance, Digits);
double takeProfit = NormalizeDouble(entryPrice - tpDistance, Digits);
// Send Order
int ticket = OrderSend(Symbol(), OP_SELL, InpLotSize, entryPrice, GetSlippage(), stopLoss, takeProfit, "ATR Sell", InpMagicNum, 0, clrRed);
if(ticket < 0) Print("OrderSend failed with error #", GetLastError());
}
}
//+------------------------------------------------------------------+
//| Helper: Count open trades for this EA |
//+------------------------------------------------------------------+
int CountOpenTrades()
{
int count = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNum)
{
if(OrderType() == OP_BUY || OrderType() == OP_SELL)
count++;
}
}
}
return(count);
}
//+------------------------------------------------------------------+
//| Helper: Adjust Slippage for 4/5 digit brokers |
//+------------------------------------------------------------------+
int GetSlippage()
{
if(Digits == 3 || Digits == 5) return(InpSlippage * 10);
return(InpSlippage);
}
//+------------------------------------------------------------------+
Inputs:
InpATRPeriod: Determines how many candles back the ATR calculates volatility (Standard is 14).InpSLMultiplier: Multiplies the ATR value to set the Stop Loss distance. (e.g., if ATR is 0.0020 and multiplier is 1.5, SL is 30 pips away).InpTPMultiplier: Multiplies the ATR value to set the Take Profit distance.Entry Logic:
ATR Calculation:
iATR(NULL, 0, InpATRPeriod, 0) retrieves the ATR value for the current symbol and timeframe.ATR * SL_Multiplier.ATR * TP_Multiplier.Price Normalization:
NormalizeDouble(..., Digits) on the calculated SL and TP prices. This is crucial in MQL4; without it, the server may reject the order because the calculated price has too many decimal places.Execution:
OrderSend function places the trade with the calculated SL and TP immediately attached to the order.Q: Why use ATR for Stop Loss?
A: ATR (Average True Range) measures market volatility. Using a fixed pip stop loss (e.g., 20 pips) might be too tight in a volatile market (getting stopped out by noise) or too wide in a quiet market (risking too much). ATR adjusts the stop distance dynamically based on how much the price is currently moving.
Q: Can I use this on any timeframe?
A: Yes. Since the code uses Period() (represented by 0 in the iATR function), it automatically adapts to M15, H1, D1, etc. Note that ATR values on D1 are much larger than on M1, so your stops will naturally be wider on higher timeframes.
Q: What happens if the broker is ECN?
A: This code places SL/TP directly within the OrderSend function. Most modern MT4 bridges handle this fine. However, strictly speaking, some old ECN bridges require you to open the trade with 0 SL/TP first, and then use OrderModify to add them. This code uses the standard method for simplicity and compatibility with standard accounts.