MACD M15 Expert Advisor MQL5: Intraday Momentum EA
This educational EA uses completed M15 MACD main/signal crossovers, an optional completed H4 200-period SMA filter, and fixed-lot orders on the chart symbol. The source includes an entry spread limit, server-hour cutoff, fixed stop/target distances, and stop adjustment. No reproducible backtest accompanies this page: trade frequency, profitability, drawdown, and optimal settings are unknown. Compile with the standard Trade library in MetaEditor, inspect broker contract specifications, and test with costs before considering any deployment.
Strategy logic
Entry conditions
At each new M15 bar, the EA compares MACD main and signal values on completed bars 2 and 1. The difference main minus signal supplies the histogram value; iMACD has no third histogram buffer. A crossover already implies the matching difference sign, so that sign is not an independent confirmation. When enabled, the H4 filter compares the current bid with the last completed H4 SMA. Entry also requires no matching position and a spread at or below the configured point limit. There is no candle-direction, ATR, news, volume, or correlation filter. All distances are broker points, not universally equivalent pips.
Exit conditions
Fixed stops and targets are attached to the order request. On each new M15 bar with available MACD data, an opposing completed-bar main-minus-signal value requests a position close. Stop adjustment runs on ticks: after the configured favorable movement, it requests a stop two points beyond entry only when that tightens the existing stop. This is not a guarantee against loss after commission, slippage, gaps, or rejected modification. At or after the cutoff server hour, matching positions are requested closed on each tick and new entries stop until the next server day. Failed requests are logged; execution requires an open market and acceptable broker conditions. There is no partial profit-taking, daily loss cap, percentage-risk sizing, or portfolio coordinator.
MQL5 Expert Advisor Code
//+------------------------------------------------------------------+
//| MACD M15 Expert Advisor — Intraday Momentum Scalper |
//| Pineify.app |
//+------------------------------------------------------------------+
#property copyright "Pineify.app"
#property version "1.10"
#property description "MACD M15 EA — trades signal crossovers on 15-min chart"
#property description "H4 trend filter, breakeven, histogram exit"
#include <Trade/Trade.mqh>
CTrade Trade;
//--- input parameters
input int InpMagicNumber = 482910; // EA Magic Number
input double InpLots = 0.01; // Fixed lot size
input int InpMACDFast = 12; // MACD fast EMA
input int InpMACDSlow = 26; // MACD slow EMA
input int InpMACDSignal = 9; // MACD signal period
input int InpSLPoints = 150; // Stop loss in points
input int InpTPPoints = 200; // Take profit in points
input int InpSpreadLimit = 20; // Max spread in points
input int InpDailyCloseHour = 23; // Close all at this hour (server)
input bool InpH4Filter = true; // Apply H4 200-SMA filter
input double InpBreakEvenPoints = 150.0; // Trigger break-even at this profit
//--- global handles
int macdHandle = INVALID_HANDLE;
int h4SmaHandle = INVALID_HANDLE;
double macdMain[], macdSignal[];
datetime lastBarTime = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
if(InpMagicNumber < 0 || InpLots <= 0 || InpMACDFast <= 0 ||
InpMACDSlow <= InpMACDFast || InpMACDSignal <= 0 ||
InpSLPoints <= 0 || InpTPPoints <= 0 || InpSpreadLimit < 0 ||
InpDailyCloseHour < 0 || InpDailyCloseHour > 23 || InpBreakEvenPoints <= 2)
return INIT_PARAMETERS_INCORRECT;
Trade.SetTypeFillingBySymbol(_Symbol);
Trade.SetDeviationInPoints(10);
Trade.SetExpertMagicNumber(InpMagicNumber);
Trade.SetAsyncMode(false);
macdHandle = iMACD(_Symbol, PERIOD_M15,
InpMACDFast, InpMACDSlow, InpMACDSignal,
PRICE_CLOSE);
if(macdHandle == INVALID_HANDLE)
{
Print("Failed to create MACD handle. Error: ", GetLastError());
return INIT_FAILED;
}
if(InpH4Filter)
{
h4SmaHandle = iMA(_Symbol, PERIOD_H4, 200, 0, MODE_SMA, PRICE_CLOSE);
if(h4SmaHandle == INVALID_HANDLE)
{
Print("Failed to create H4 SMA handle. Error: ", GetLastError());
return INIT_FAILED;
}
}
ArraySetAsSeries(macdMain, true);
ArraySetAsSeries(macdSignal, true);
lastBarTime = 0;
Print("MACD M15 EA initialized. Magic: ", InpMagicNumber,
" | Pair: ", _Symbol,
" | TF: M15");
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(macdHandle != INVALID_HANDLE)
IndicatorRelease(macdHandle);
if(h4SmaHandle != INVALID_HANDLE)
IndicatorRelease(h4SmaHandle);
Comment("");
Print("MACD M15 EA deinitialized. Reason: ", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
MqlTick tick;
if(!SymbolInfoTick(_Symbol, tick) || tick.ask <= 0 || tick.bid <= 0) return;
MqlDateTime dt;
TimeToStruct(tick.time, dt);
if(dt.hour >= InpDailyCloseHour)
{
CloseAllPositions();
Comment("Cutoff active; matching positions remaining: ", CountPositions());
return;
}
// Tick-level stop adjustment does not depend on entry spread or indicator data.
ManagePositions(false, 0.0);
datetime currentBarTime = iTime(_Symbol, PERIOD_M15, 0);
if(currentBarTime <= 0 || currentBarTime == lastBarTime) return;
if(CopyBuffer(macdHandle, 0, 1, 2, macdMain) != 2 ||
CopyBuffer(macdHandle, 1, 1, 2, macdSignal) != 2) return;
// Dynamic series arrays: index 0 is completed bar 1, index 1 is bar 2.
double histogram = macdMain[0] - macdSignal[0];
ManagePositions(true, histogram);
if(CountPositions() > 0) { lastBarTime = currentBarTime; return; }
bool trendUp = true, trendDown = true;
if(InpH4Filter)
{
double completedSma[1];
if(CopyBuffer(h4SmaHandle, 0, 1, 1, completedSma) != 1) return;
trendUp = tick.bid > completedSma[0];
trendDown = tick.bid < completedSma[0];
}
lastBarTime = currentBarTime;
if((tick.ask - tick.bid) / _Point > InpSpreadLimit) return;
bool longCross = macdMain[1] <= macdSignal[1] && macdMain[0] > macdSignal[0];
bool shortCross = macdMain[1] >= macdSignal[1] && macdMain[0] < macdSignal[0];
if(longCross && trendUp)
OpenTrade(ORDER_TYPE_BUY, InpLots, tick.ask,
tick.ask - InpSLPoints * _Point, tick.ask + InpTPPoints * _Point);
else if(shortCross && trendDown)
OpenTrade(ORDER_TYPE_SELL, InpLots, tick.bid,
tick.bid + InpSLPoints * _Point, tick.bid - InpTPPoints * _Point);
}
bool CheckTradeResult(bool sent, string action)
{
uint code = Trade.ResultRetcode();
bool accepted = sent && (code == TRADE_RETCODE_DONE || code == TRADE_RETCODE_DONE_PARTIAL);
if(!accepted) Print(action, " not completed: ", code, " ", Trade.ResultRetcodeDescription());
return accepted;
}
//+------------------------------------------------------------------+
//| Open a market order with CTrade |
//+------------------------------------------------------------------+
void OpenTrade(ENUM_ORDER_TYPE type, double volume,
double price, double sl, double tp)
{
CheckTradeResult(Trade.PositionOpen(_Symbol, type, volume, price,
NormalizeDouble(sl, _Digits), NormalizeDouble(tp, _Digits),
"MACD-M15-Pineify"), "Open");
}
//+------------------------------------------------------------------+
//| Count open positions for this EA's symbol & magic |
//+------------------------------------------------------------------+
int CountPositions()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
(int)PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| Manage open positions — histogram exit + breakeven |
//+------------------------------------------------------------------+
void ManagePositions(bool checkHistogram, double histogram)
{
MqlTick tick;
if(!SymbolInfoTick(_Symbol, tick)) return;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0 || !PositionSelectByTicket(ticket)) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol ||
PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue;
bool isBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY;
if(checkHistogram && ((isBuy && histogram < 0) || (!isBuy && histogram > 0)))
{
CheckTradeResult(Trade.PositionClose(ticket), "Histogram close");
continue;
}
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
double sl = PositionGetDouble(POSITION_SL);
double tp = PositionGetDouble(POSITION_TP);
double movement = (isBuy ? tick.bid - entry : entry - tick.ask) / _Point;
double newSl = NormalizeDouble(entry + (isBuy ? 2 : -2) * _Point, _Digits);
if(movement >= InpBreakEvenPoints &&
(sl == 0 || (isBuy ? newSl > sl : newSl < sl)))
CheckTradeResult(Trade.PositionModify(ticket, newSl, tp), "Stop adjustment");
}
}
//+------------------------------------------------------------------+
//| Close all positions for this EA |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
continue;
if((int)PositionGetInteger(POSITION_MAGIC) != InpMagicNumber)
continue;
CheckTradeResult(Trade.PositionClose(ticket), "Cutoff close");
}
}
Copy this code into MetaEditor, save it in the MQL5/Experts folder, and compile with F7.
Generate a custom Multi-pair scalping EA
Describe the entry, exit, position, order, and risk rules you want. Pineify generates editable MQL5 source code that you can inspect and compile in MetaEditor.
Frequently Asked Questions
Related MQL5 pages
Risk and testing note
These educational examples require independent compilation and testing. A compile pass or an illustrative setting does not establish execution correctness or profitability. Any reported performance must be supported by reproducible data and test settings; past results do not predict future returns. Trading involves significant risk of loss. This content is not financial advice.