This repository has been archived on 2026-01-12. You can view files and clone it, but cannot push or open issues or pull requests.
Files
MAEA/MAEA.mq4
Kunthawat Greethong cd0b2e35a2 Fix compilation errors - convert to MQL4 syntax
- Remove MQL5-specific indicator properties
- Replace iMA handles with direct iMA function calls
- Replace PlotIndexSetString with SetIndexLabel
- Replace IndicatorRelease with MQL4 equivalent
- Replace CopyBuffer with direct iMA calls
- Replace MqlDateTime with TimeHour and TimeDayOfWeek
- Fix all MQL4/MQL5 compatibility issues
2026-01-02 13:14:34 +07:00

698 lines
22 KiB
Plaintext

//+------------------------------------------------------------------+
//| MAEA.mq4 |
//| Moving Average Expert Advisor |
//| Advanced Trend-Following Strategy |
//+------------------------------------------------------------------+
#property copyright "MAEA"
#property link "https://git.moreminimore.com/kunthawat/MAEA"
#property version "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 5
//+------------------------------------------------------------------+
//| Input Parameters |
//+------------------------------------------------------------------+
input string Section1 = "=== EMA Settings ===";
input int EMAPeriod = 30; // EMA Period for all lines
input string Section2 = "=== Lot Settings ===";
input double LotSizeNormal = 0.01; // Lot size without pullback
input double LotSizePullback = 0.02; // Lot size with pullback
input string Section3 = "=== Risk Management ===";
input double TakeProfitUSD = 5.0; // Take Profit target in USD
input int BreakevenPoints = 100; // Points to trigger breakeven
input int TrailingStopPoints = 100; // Trailing stop distance
input double MaxDrawdownPercent = 10.0;// Max drawdown percentage
input string Section4 = "=== Filters ===";
input int MaxSpread = 30; // Maximum allowed spread in points
input int VolumePeriod = 20; // Period for volume average
input bool UseMTFFilter = true; // Enable MTF filter
input bool UseNewsFilter = true; // Enable news filter
input string NewsAvoidHours = "14,15,20,21"; // Hours to avoid (Thailand time)
input string NewsAvoidDays = "1,5"; // Days to avoid (Monday=1, Friday=5)
input string Section5 = "=== General ===";
input int MagicNumber = 12345; // EA unique identifier
//+------------------------------------------------------------------+
//| Global Variables |
//+------------------------------------------------------------------+
// Indicator buffers
double EMAHighBuffer[];
double EMAMediumBuffer[];
double EMALowBuffer[];
double HighBorderBuffer[];
double LowBorderBuffer[];
// State tracking
bool PullbackBuy = false;
bool PullbackSell = false;
bool BreakevenTriggered = false;
double OrderOpenPrice = 0;
double OrderStopLoss = 0;
double OrderTakeProfit = 0;
int OrderTicket = 0;
// Drawdown tracking
double AccountEquityPeak = 0;
// Previous bar values for signal detection
double PrevClose = 0;
double PrevEMAHigh = 0;
double PrevEMAMedium = 0;
double PrevEMALow = 0;
// News filter arrays
int AvoidHoursArray[];
int AvoidDaysArray[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Set indicator buffers
SetIndexBuffer(0, EMAHighBuffer);
SetIndexBuffer(1, EMAMediumBuffer);
SetIndexBuffer(2, EMALowBuffer);
SetIndexBuffer(3, HighBorderBuffer);
SetIndexBuffer(4, LowBorderBuffer);
// Set indicator labels
SetIndexLabel(0, "EMA High");
SetIndexLabel(1, "EMA Medium");
SetIndexLabel(2, "EMA Low");
SetIndexLabel(3, "High Border");
SetIndexLabel(4, "Low Border");
// Set indicator styles
SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 2, clrLightBlue);
SetIndexStyle(1, DRAW_LINE, STYLE_SOLID, 2, clrYellow);
SetIndexStyle(2, DRAW_LINE, STYLE_SOLID, 2, clrOrange);
SetIndexStyle(3, DRAW_LINE, STYLE_DASH, 1, clrPurple);
SetIndexStyle(4, DRAW_LINE, STYLE_DASH, 1, clrPurple);
// Parse news filter arrays
ParseNewsFilterArrays();
// Initialize account equity peak
AccountEquityPeak = AccountEquity();
Print("MAEA initialized successfully");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("MAEA deinitialized");
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
// Check for minimum bars
if(rates_total < EMAPeriod + 1) return(0);
// Set arrays as series
ArraySetAsSeries(EMAHighBuffer, false);
ArraySetAsSeries(EMAMediumBuffer, false);
ArraySetAsSeries(EMALowBuffer, false);
ArraySetAsSeries(HighBorderBuffer, false);
ArraySetAsSeries(LowBorderBuffer, false);
// Calculate start position
int start;
if(prev_calculated == 0)
{
start = EMAPeriod;
// Initialize buffers
for(int i = 0; i < start; i++)
{
EMAHighBuffer[i] = 0;
EMAMediumBuffer[i] = 0;
EMALowBuffer[i] = 0;
HighBorderBuffer[i] = 0;
LowBorderBuffer[i] = 0;
}
}
else
{
start = prev_calculated - 1;
}
// Calculate EMA values and border lines
for(int i = start; i < rates_total; i++)
{
// Get EMA values using iMA function (MQL4 style)
double emaHigh = iMA(Symbol(), PERIOD_CURRENT, EMAPeriod, 0, MODE_EMA, PRICE_HIGH, i);
double emaMedium = iMA(Symbol(), PERIOD_CURRENT, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE, i);
double emaLow = iMA(Symbol(), PERIOD_CURRENT, EMAPeriod, 0, MODE_EMA, PRICE_LOW, i);
// Calculate range
double range = emaMedium - emaLow;
// Calculate border lines
double highBorder = emaHigh + range;
double lowBorder = emaLow - range;
// Store values in buffers
EMAHighBuffer[i] = emaHigh;
EMAMediumBuffer[i] = emaMedium;
EMALowBuffer[i] = emaLow;
HighBorderBuffer[i] = highBorder;
LowBorderBuffer[i] = lowBorder;
}
// Only process trading logic on new bar
static datetime lastBarTime = 0;
if(time[rates_total - 1] != lastBarTime)
{
lastBarTime = time[rates_total - 1];
// Update previous bar values
int lastBar = rates_total - 1;
int prevBar = rates_total - 2;
if(prevBar >= EMAPeriod)
{
PrevClose = close[prevBar];
PrevEMAHigh = EMAHighBuffer[prevBar];
PrevEMAMedium = EMAMediumBuffer[prevBar];
PrevEMALow = EMALowBuffer[prevBar];
// Process trading logic
ProcessTradingLogic(rates_total, time, open, high, low, close, tick_volume, spread);
}
}
// Manage open orders on every tick
ManageOpenOrders();
return(rates_total);
}
//+------------------------------------------------------------------+
//| Get EMA value from handle |
//+------------------------------------------------------------------+
double GetEMAValue(int timeframe, int applied_price, int shift)
{
return iMA(Symbol(), timeframe, EMAPeriod, 0, MODE_EMA, applied_price, shift);
}
//+------------------------------------------------------------------+
//| Parse news filter arrays |
//+------------------------------------------------------------------+
void ParseNewsFilterArrays()
{
// Parse hours
string hoursStr = NewsAvoidHours;
ArrayResize(AvoidHoursArray, 0);
while(hoursStr != "")
{
int pos = StringFind(hoursStr, ",");
string hourStr;
if(pos >= 0)
{
hourStr = StringSubstr(hoursStr, 0, pos);
hoursStr = StringSubstr(hoursStr, pos + 1);
}
else
{
hourStr = hoursStr;
hoursStr = "";
}
StringTrimLeft(hourStr);
StringTrimRight(hourStr);
if(hourStr != "")
{
int hour = (int)StringToInteger(hourStr);
int size = ArraySize(AvoidHoursArray);
ArrayResize(AvoidHoursArray, size + 1);
AvoidHoursArray[size] = hour;
}
}
// Parse days
string daysStr = NewsAvoidDays;
ArrayResize(AvoidDaysArray, 0);
while(daysStr != "")
{
int pos = StringFind(daysStr, ",");
string dayStr;
if(pos >= 0)
{
dayStr = StringSubstr(daysStr, 0, pos);
daysStr = StringSubstr(daysStr, pos + 1);
}
else
{
dayStr = daysStr;
daysStr = "";
}
StringTrimLeft(dayStr);
StringTrimRight(dayStr);
if(dayStr != "")
{
int day = (int)StringToInteger(dayStr);
int size = ArraySize(AvoidDaysArray);
ArrayResize(AvoidDaysArray, size + 1);
AvoidDaysArray[size] = day;
}
}
}
//+------------------------------------------------------------------+
//| Process trading logic |
//+------------------------------------------------------------------+
void ProcessTradingLogic(const int rates_total,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const int &spread[])
{
// Check if we have an open order
if(HasOpenOrder()) return;
// Check drawdown
if(!CheckDrawdown()) return;
// Check news filter
if(UseNewsFilter && !CheckNewsFilter(time[rates_total - 1])) return;
// Check spread filter
if(spread[rates_total - 1] > MaxSpread) return;
// Check volume filter
if(!CheckVolumeFilter(tick_volume, rates_total)) return;
// Check MTF filter
if(UseMTFFilter && !CheckMTFFilter()) return;
// Get current values
int lastBar = rates_total - 1;
double currentClose = close[lastBar];
double currentEMAHigh = EMAHighBuffer[lastBar];
double currentEMALow = EMALowBuffer[lastBar];
// Detect pullback signals
DetectPullbackSignals(currentClose, currentEMAHigh, PrevEMAHigh, currentEMALow, PrevEMALow, PrevEMAMedium);
// Detect breakthrough signals
if(DetectBreakthroughBuy(currentClose, currentEMAHigh, PrevClose, PrevEMAHigh))
{
OpenBuyOrder();
}
else if(DetectBreakthroughSell(currentClose, currentEMALow, PrevClose, PrevEMALow))
{
OpenSellOrder();
}
}
//+------------------------------------------------------------------+
//| Detect pullback signals |
//+------------------------------------------------------------------+
void DetectPullbackSignals(double currentClose, double currentEMAHigh, double prevEMAHigh,
double currentEMALow, double prevEMALow, double prevEMAMedium)
{
// Buy pullback: price moved down to hit a line, then closed above it
if(PrevClose < prevEMAMedium && currentClose > currentEMALow)
{
PullbackBuy = true;
}
else if(PrevClose < prevEMALow && currentClose > currentEMALow)
{
PullbackBuy = true;
}
else if(PrevClose < prevEMAHigh && currentClose > currentEMAHigh)
{
PullbackBuy = true;
}
// Sell pullback: price moved up to hit a line, then closed below it
if(PrevClose > prevEMAMedium && currentClose < currentEMAHigh)
{
PullbackSell = true;
}
else if(PrevClose > prevEMAHigh && currentClose < currentEMAHigh)
{
PullbackSell = true;
}
else if(PrevClose > prevEMALow && currentClose < currentEMALow)
{
PullbackSell = true;
}
}
//+------------------------------------------------------------------+
//| Detect breakthrough buy signal |
//+------------------------------------------------------------------+
bool DetectBreakthroughBuy(double currentClose, double currentEMAHigh, double prevClose, double prevEMAHigh)
{
return (prevClose <= prevEMAHigh && currentClose > currentEMAHigh);
}
//+------------------------------------------------------------------+
//| Detect breakthrough sell signal |
//+------------------------------------------------------------------+
bool DetectBreakthroughSell(double currentClose, double currentEMALow, double prevClose, double prevEMALow)
{
return (prevClose >= prevEMALow && currentClose < currentEMALow);
}
//+------------------------------------------------------------------+
//| Check volume filter |
//+------------------------------------------------------------------+
bool CheckVolumeFilter(const long &tick_volume[], int rates_total)
{
if(rates_total < VolumePeriod + 1) return false;
// Calculate average volume
double avgVolume = 0;
for(int i = rates_total - VolumePeriod - 1; i < rates_total - 1; i++)
{
avgVolume += tick_volume[i];
}
avgVolume /= VolumePeriod;
// Check if current volume is above average
return (tick_volume[rates_total - 1] > avgVolume);
}
//+------------------------------------------------------------------+
//| Check MTF filter |
//+------------------------------------------------------------------+
bool CheckMTFFilter()
{
double d1Close = iClose(Symbol(), PERIOD_D1, 0);
double d1EMAHigh = GetEMAValue(PERIOD_D1, PRICE_HIGH, 0);
double d1EMALow = GetEMAValue(PERIOD_D1, PRICE_LOW, 0);
if(d1Close > d1EMAHigh)
{
// Only buy orders allowed
return true;
}
else if(d1Close < d1EMALow)
{
// Only sell orders allowed
return true;
}
else
{
// Price between lines - no orders allowed
return false;
}
}
//+------------------------------------------------------------------+
//| Check news filter |
//+------------------------------------------------------------------+
bool CheckNewsFilter(datetime currentTime)
{
// Convert to Thailand timezone (UTC+7)
// MT4 server time is usually UTC, so add 7 hours
datetime thailandTime = currentTime + 7 * 3600; // 7 hours in seconds
int thailandHour = TimeHour(thailandTime);
int thailandDay = TimeDayOfWeek(thailandTime);
// Check if current hour is in avoid list
for(int i = 0; i < ArraySize(AvoidHoursArray); i++)
{
if(thailandHour == AvoidHoursArray[i])
{
return false;
}
}
// Check if current day is in avoid list
for(int i = 0; i < ArraySize(AvoidDaysArray); i++)
{
if(thailandDay == AvoidDaysArray[i])
{
return false;
}
}
return true;
}
//+------------------------------------------------------------------+
//| Check drawdown |
//+------------------------------------------------------------------+
bool CheckDrawdown()
{
double currentEquity = AccountEquity();
// Update peak equity
if(currentEquity > AccountEquityPeak)
{
AccountEquityPeak = currentEquity;
}
// Calculate drawdown percentage
double drawdownPercent = ((AccountEquityPeak - currentEquity) / AccountEquityPeak) * 100;
// Check if drawdown exceeds limit
if(drawdownPercent >= MaxDrawdownPercent)
{
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Open buy order |
//+------------------------------------------------------------------+
void OpenBuyOrder()
{
double lotSize = PullbackBuy ? LotSizePullback : LotSizeNormal;
double stopLoss = LowBorderBuffer[ArraySize(LowBorderBuffer) - 1];
double takeProfit = CalculateTakeProfit(lotSize, OP_BUY);
OrderTicket = OrderSend(Symbol(), OP_BUY, lotSize, Ask, 3, stopLoss, takeProfit, "MAEA Buy", MagicNumber, 0, clrBlue);
if(OrderTicket > 0)
{
OrderOpenPrice = Ask;
OrderStopLoss = stopLoss;
OrderTakeProfit = takeProfit;
BreakevenTriggered = false;
// Reset pullback flags
PullbackBuy = false;
PullbackSell = false;
Print("Buy order opened: Ticket=", OrderTicket, " Price=", Ask, " SL=", stopLoss, " TP=", takeProfit);
}
else
{
Print("Error opening buy order: ", GetLastError());
}
}
//+------------------------------------------------------------------+
//| Open sell order |
//+------------------------------------------------------------------+
void OpenSellOrder()
{
double lotSize = PullbackSell ? LotSizePullback : LotSizeNormal;
double stopLoss = HighBorderBuffer[ArraySize(HighBorderBuffer) - 1];
double takeProfit = CalculateTakeProfit(lotSize, OP_SELL);
OrderTicket = OrderSend(Symbol(), OP_SELL, lotSize, Bid, 3, stopLoss, takeProfit, "MAEA Sell", MagicNumber, 0, clrRed);
if(OrderTicket > 0)
{
OrderOpenPrice = Bid;
OrderStopLoss = stopLoss;
OrderTakeProfit = takeProfit;
BreakevenTriggered = false;
// Reset pullback flags
PullbackBuy = false;
PullbackSell = false;
Print("Sell order opened: Ticket=", OrderTicket, " Price=", Bid, " SL=", stopLoss, " TP=", takeProfit);
}
else
{
Print("Error opening sell order: ", GetLastError());
}
}
//+------------------------------------------------------------------+
//| Calculate take profit in pips from USD target |
//+------------------------------------------------------------------+
double CalculateTakeProfit(double lotSize, int orderType)
{
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
double pipValue = tickValue / tickSize * Point;
double profitInPips = TakeProfitUSD / (lotSize * pipValue);
double profitInPrice = profitInPips * Point;
if(orderType == OP_BUY)
{
return Ask + profitInPrice;
}
else
{
return Bid - profitInPrice;
}
}
//+------------------------------------------------------------------+
//| Check if we have an open order |
//+------------------------------------------------------------------+
bool HasOpenOrder()
{
if(OrderTicket == 0) return false;
if(OrderSelect(OrderTicket, SELECT_BY_TICKET))
{
if(OrderCloseTime() == 0)
{
return true;
}
else
{
OrderTicket = 0;
return false;
}
}
else
{
OrderTicket = 0;
return false;
}
}
//+------------------------------------------------------------------+
//| Manage open orders |
//+------------------------------------------------------------------+
void ManageOpenOrders()
{
if(!HasOpenOrder()) return;
if(!OrderSelect(OrderTicket, SELECT_BY_TICKET)) return;
double currentPrice = OrderType() == OP_BUY ? Bid : Ask;
double currentProfit = OrderProfit();
double openPrice = OrderOpenPrice();
// Check take profit
if(currentProfit >= TakeProfitUSD)
{
CloseOrder();
return;
}
// Check stop loss
if(OrderType() == OP_BUY && currentPrice <= OrderStopLoss())
{
CloseOrder();
return;
}
else if(OrderType() == OP_SELL && currentPrice >= OrderStopLoss())
{
CloseOrder();
return;
}
// Calculate profit in points
double profitPoints = 0;
if(OrderType() == OP_BUY)
{
profitPoints = (currentPrice - openPrice) / Point;
}
else
{
profitPoints = (openPrice - currentPrice) / Point;
}
// Check breakeven
if(!BreakevenTriggered && profitPoints >= BreakevenPoints)
{
double newSL = openPrice;
if(OrderModify(OrderTicket, openPrice, newSL, OrderTakeProfit(), 0, clrNONE))
{
BreakevenTriggered = true;
OrderStopLoss = newSL;
Print("Breakeven triggered: SL moved to ", newSL);
}
}
// Check trailing stop
if(BreakevenTriggered && profitPoints >= BreakevenPoints + TrailingStopPoints)
{
double newSL;
if(OrderType() == OP_BUY)
{
newSL = currentPrice - TrailingStopPoints * Point;
if(newSL > OrderStopLoss())
{
if(OrderModify(OrderTicket, openPrice, newSL, OrderTakeProfit(), 0, clrNONE))
{
OrderStopLoss = newSL;
Print("Trailing stop: SL moved to ", newSL);
}
}
}
else
{
newSL = currentPrice + TrailingStopPoints * Point;
if(newSL < OrderStopLoss())
{
if(OrderModify(OrderTicket, openPrice, newSL, OrderTakeProfit(), 0, clrNONE))
{
OrderStopLoss = newSL;
Print("Trailing stop: SL moved to ", newSL);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Close order |
//+------------------------------------------------------------------+
void CloseOrder()
{
if(OrderSelect(OrderTicket, SELECT_BY_TICKET))
{
double closePrice = OrderType() == OP_BUY ? Bid : Ask;
if(OrderClose(OrderTicket, OrderLots(), closePrice, 3, clrNONE))
{
Print("Order closed: Ticket=", OrderTicket, " Profit=", OrderProfit());
OrderTicket = 0;
BreakevenTriggered = false;
}
else
{
Print("Error closing order: ", GetLastError());
}
}
}
//+------------------------------------------------------------------+