//+------------------------------------------------------------------+ //| 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()); } } } //+------------------------------------------------------------------+