# Moving Average Crossover EA – Complete MQL4 Source Code
This article provides a fully functional MT4 Expert Advisor that implements a classic dual Moving Average crossover strategy. The code is written in MQL4, ready to compile, and includes configurable parameters for fast/slow periods, risk management, and trade filters. Perfect for traders who want to understand how trend-following logic is automated.
Strategy Logic
The EA generates signals based on two Simple Moving Averages:
Each signal is filtered by a configurable minimum bar count to ensure sufficient data before trading. The EA supports:
Complete Source Code
Save the code below as `MACrossover_EA.mq4` in your `MQL4/Experts/` folder, then compile in MetaEditor.
```mql4
//+------------------------------------------------------------------+
//| MACrossover_EA.mq4 |
//| |
//| Based on dual MA crossover logic |
//+------------------------------------------------------------------+
#property copyright "ForexEA Strategy"
#property version "1.00"
#property strict
//--- Input parameters
input double InpLotSize = 0.1; // Fixed lot size (if RiskPercent=0)
input double InpRiskPercent = 2.0; // Risk % per trade (0 = use fixed lot)
input int InpFastMAPeriod = 9; // Fast MA period
input int InpSlowMAPeriod = 21; // Slow MA period
input int InpStopLoss = 300; // Stop Loss (points)
input int InpTakeProfit = 600; // Take Profit (points)
input int InpMagicNumber = 202606; // EA unique ID
input int InpMaxSpread = 30; // Max allowed spread (points)
input int InpMinBars = 50; // Minimum bars before trading
input int InpTradeDirection = 2; // 0=Both, 1=Buy only, 2=Sell only
//--- Global variables
double fastMA_prev, fastMA_curr;
double slowMA_prev, slowMA_curr;
int slip = 30; // Slippage in points
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
if(InpFastMAPeriod >= InpSlowMAPeriod)
{
Print("Error: Fast MA period must be less than Slow MA period");
return(INIT_PARAMETERS_INCORRECT);
}
if(InpStopLoss < 0 || InpTakeProfit < 0)
{
Print("Stop Loss and Take Profit cannot be negative");
return(INIT_PARAMETERS_INCORRECT);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment("");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Check minimum bars condition
if(Bars(_Symbol, PERIOD_CURRENT) < InpMinBars)
{
Comment("Not enough bars: ", Bars(_Symbol, PERIOD_CURRENT));
return;
}
//--- Check spread filter
if((MarketInfo(_Symbol, MODE_SPREAD) * Point) * 10000 > InpMaxSpread)
{
Comment("Spread too high: ", MarketInfo(_Symbol, MODE_SPREAD));
return;
}
//--- Calculate moving averages on current and previous bars
fastMA_curr = iMA(_Symbol, PERIOD_CURRENT, InpFastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
fastMA_prev = iMA(_Symbol, PERIOD_CURRENT, InpFastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 2);
slowMA_curr = iMA(_Symbol, PERIOD_CURRENT, InpSlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
slowMA_prev = iMA(_Symbol, PERIOD_CURRENT, InpSlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 2);
//--- Check if already has a position
if(CountPositions() > 0)
return;
//--- Signal detection: MA cross
bool buySignal = (fastMA_prev <= slowMA_prev && fastMA_curr > slowMA_curr);
bool sellSignal = (fastMA_prev >= slowMA_prev && fastMA_curr < slowMA_curr);
//--- Apply trade direction filter
if(InpTradeDirection == 1) sellSignal = false; // Buy only
if(InpTradeDirection == 2) buySignal = false; // Sell only
//--- Calculate lot size
double lot = CalculateLotSize();
//--- Execute trades
if(buySignal)
{
OpenOrder(OP_BUY, lot);
}
else if(sellSignal)
{
OpenOrder(OP_SELL, lot);
}
//--- Display current MA values on chart
Comment("Fast MA: ", DoubleToStr(fastMA_curr, _Digits),
"\nSlow MA: ", DoubleToStr(slowMA_curr, _Digits),
"\nLot size: ", lot);
}
//+------------------------------------------------------------------+
//| Count open positions |
//+------------------------------------------------------------------+
int CountPositions()
{
int count = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == _Symbol && OrderMagicNumber() == InpMagicNumber)
count++;
}
}
return(count);
}
//+------------------------------------------------------------------+
//| Calculate lot size based on risk or fixed |
//+------------------------------------------------------------------+
double CalculateLotSize()
{
if(InpRiskPercent <= 0)
return(InpLotSize);
double riskMoney = AccountBalance() * InpRiskPercent / 100.0;
double stopPoints = InpStopLoss;
if(stopPoints <= 0)
return(InpLotSize);
double tickValue = MarketInfo(_Symbol, MODE_TICKVALUE);
double lotStep = MarketInfo(_Symbol, MODE_LOTSTEP);
double minLot = MarketInfo(_Symbol, MODE_MINLOT);
double maxLot = MarketInfo(_Symbol, MODE_MAXLOT);
double calculatedLot = riskMoney / (stopPoints * tickValue);
calculatedLot = MathFloor(calculatedLot / lotStep) * lotStep;
calculatedLot = MathMax(minLot, MathMin(maxLot, calculatedLot));
return(calculatedLot);
}
//+------------------------------------------------------------------+
//| Open market order |
//+------------------------------------------------------------------+
void OpenOrder(int cmd, double lot)
{
double price, slPrice = 0, tpPrice = 0;
int slippage = slip;
if(cmd == OP_BUY)
{
price = Ask;
if(InpStopLoss > 0) slPrice = price - InpStopLoss * Point;
if(InpTakeProfit > 0) tpPrice = price + InpTakeProfit * Point;
}
else if(cmd == OP_SELL)
{
price = Bid;
if(InpStopLoss > 0) slPrice = price + InpStopLoss * Point;
if(InpTakeProfit > 0) tpPrice = price - InpTakeProfit * Point;
}
else return;
int ticket = OrderSend(_Symbol, cmd, lot, price, slippage, slPrice, tpPrice, "MA Crossover EA", InpMagicNumber, 0, clrNONE);
if(ticket < 0)
Print("OrderSend failed: ", GetLastError());
else
Print("Order opened: ", ticket);
}
//+------------------------------------------------------------------+
```
Parameter Explanation
| Parameter | Description |
|-----------|-------------|
| InpLotSize | Fixed lot size when RiskPercent = 0 |
| InpRiskPercent | Percentage of account balance to risk per trade (auto-calculates lot size based on stop loss) |
| InpFastMAPeriod | Period of the fast Moving Average |
| InpSlowMAPeriod | Period of the slow Moving Average |
| InpStopLoss | Stop loss distance in points |
| InpTakeProfit | Take profit distance in points |
| InpMagicNumber | Unique identifier for the EA |
| InpMaxSpread | Maximum allowed spread in points |
| InpMinBars | Minimum bars loaded before trading |
| InpTradeDirection | Restrict trades: 0=Both, 1=Buy only, 2=Sell only |
How to Compile and Install
1. Copy the code into MetaEditor (open from MT4: Tools → MetaQuotes Language Editor)
2. Save as `MACrossover_EA.mq4` in the `MQL4/Experts/` folder
3. Press F7 or click the Compile button
4. Fix any errors if present (the code above compiles cleanly)
5. Attach the EA to a chart in MT4 and adjust parameters
Suggested Optimization
For better performance on specific symbols and timeframes:
Always backtest using 90% modeling quality before live deployment.
Debugging Tip
If the EA does not trade:
Ready for More?
This basic crossover EA is a great starting point. Our premium EA packages include advanced features like:
Visit our website for professional automated trading solutions.
---
References:
1. MQL4 Documentation – iMA() function
2. MetaQuotes – OrderSend() parameters
3. Original source code compiled and tested on MT4 build 1420+