//+------------------------------------------------------------------+ //| SmartMA MT5 - The Self-Explaining Context MA | //| Author: Steve Rosenstock | //| Repository: https://forge.mql5.io/steverosenstock/SmartMA | //| Git: https://forge.mql5.io/steverosenstock/SmartMA.git | //| Version: 1.0 (2025-07-20) | //+------------------------------------------------------------------+ #property copyright "Steve Rosenstock" #property link "https://forge.mql5.io/steverosenstock/SmartMA" #property version "1.0" #property indicator_chart_window #property indicator_buffers 4 #property indicator_plots 3 #property indicator_label1 "SmartMA" #property indicator_type1 DRAW_LINE #property indicator_color1 clrDodgerBlue #property indicator_width1 2 #property indicator_label2 "GhostMA" #property indicator_type2 DRAW_LINE #property indicator_color2 clrGray #property indicator_width2 1 #property indicator_label3 "RiskBand" #property indicator_type3 DRAW_FILLING #property indicator_color3 clrLightYellow, clrLightYellow #property indicator_width3 2 //--- INPUT PARAMETERS input int MA_Period = 20; // Base MA period (auto-adapting) input int ATR_Period = 14; // ATR for risk band input bool Use_Volume = true; // Volume-weighted MA input bool Use_News = true; // Integrate MT5 news events input bool Show_Risk_Band = true; // Show SmartATR band (+/-) input bool Show_Ghost = true; // Show "Ghost" MA (w/o news) input bool Enable_Alerts = true; // Alert on regime/news change //--- BUFFERS double SmartMABuffer[]; // Main SmartMA output double GhostMABuffer[]; // "Ghost" MA (no news) double RiskBandTop[]; // Top of ATR band double RiskBandBottom[]; // Bottom of ATR band //--- GLOBALS int lastRegime = -1; string lastSession = ""; datetime lastExplained = 0; double lastATR = 0; datetime lastNewsAlert = 0; //+------------------------------------------------------------------+ //| Initialization | //+------------------------------------------------------------------+ int OnInit() { SetIndexBuffer(0, SmartMABuffer, INDICATOR_DATA); SetIndexBuffer(1, GhostMABuffer, INDICATOR_DATA); SetIndexBuffer(2, RiskBandTop, INDICATOR_DATA); SetIndexBuffer(3, RiskBandBottom, INDICATOR_DATA); PlotIndexSetString(0, PLOT_LABEL, "SmartMA"); PlotIndexSetString(1, PLOT_LABEL, "GhostMA"); PlotIndexSetString(2, PLOT_LABEL, "RiskBand"); IndicatorSetString(INDICATOR_SHORTNAME, "SmartMA MT5"); IndicatorSetInteger(INDICATOR_DIGITS, 5); ArrayInitialize(SmartMABuffer, 0.0); ArrayInitialize(GhostMABuffer, 0.0); ArrayInitialize(RiskBandTop, 0.0); ArrayInitialize(RiskBandBottom, 0.0); Print("SmartMA MT5 initialized!"); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Main Calculation | //+------------------------------------------------------------------+ 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[]) { if(rates_total < MathMax(MA_Period, ATR_Period) + 2) return(0); int currentRegime = -1; // 0=range, 1=trend, 2=neutral string regimeText = ""; string session = ""; int dynamic_period; double atr, vAvg; bool isNews, highVol; for(int i=0; i GetAvgVolume(tick_volume, i, 20) * 1.5); atr = CalculateATR(i, ATR_Period, high, low, close); vAvg = GetAvgVolume(tick_volume, i, MA_Period); // Regime detection (Trend/Range) double rollingATR = CalculateATR(i, 30, high, low, close); if(atr > rollingATR*1.3) { regimeText="Trend"; currentRegime=1; } else if(atr < rollingATR*0.7){ regimeText="Range"; currentRegime=0; } else { regimeText="Neutral"; currentRegime=2; } //--- 2. Dynamic MA period dynamic_period = MA_Period; if(currentRegime==1) dynamic_period = MathMax(7, MA_Period-6); if(currentRegime==0) dynamic_period = MathMin(50, MA_Period+8); if(session=="London") dynamic_period = MathMax(7, dynamic_period-2); if(session=="Asia") dynamic_period = MathMin(50, dynamic_period+4); //--- 3. MA calculation (EMA, volume-weighted if enabled) if(isNews) { SmartMABuffer[i] = (i>0) ? SmartMABuffer[i-1] : close[i]; if(i>0) GhostMABuffer[i] = GhostMABuffer[i-1]; else GhostMABuffer[i] = close[i]; // Alert for news bar (once per bar) if(Enable_Alerts && time[i]!=lastNewsAlert) { Alert("SmartMA: News event detected (", TimeToString(time[i]), ") - MA frozen"); lastNewsAlert = time[i]; } } else { SmartMABuffer[i] = AdaptiveEMA(i, dynamic_period, close, tick_volume, Use_Volume, SmartMABuffer); GhostMABuffer[i] = AdaptiveEMA_Ghost(i, dynamic_period, close, tick_volume, Use_Volume, time, GhostMABuffer); } //--- 4. Risk Band (±ATR) if(Show_Risk_Band) { RiskBandTop[i] = SmartMABuffer[i] + atr; RiskBandBottom[i] = SmartMABuffer[i] - atr; } else { RiskBandTop[i] = 0.0; RiskBandBottom[i] = 0.0; } //--- 5. Self-Explaining Comment (latest bar only) if(i == rates_total-1 && (time[i] != lastExplained)) { string info = "SmartMA: " + regimeText + ", " + session; info += ", Period=" + IntegerToString(dynamic_period); if(isNews) info += ", NEWS EVENT (MA frozen)"; else if(highVol) info += ", High Volume"; Comment(info); // Regime/Session change Alert if(Enable_Alerts && (currentRegime!=lastRegime || session!=lastSession)) { Alert("SmartMA: Context changed to " + regimeText + " / " + session + " ("+TimeToString(time[i])+")"); lastRegime = currentRegime; lastSession = session; } lastExplained = time[i]; } } return(rates_total); } //+------------------------------------------------------------------+ //| Helper: Session detection | //+------------------------------------------------------------------+ string DetectSession(datetime t) { int hour = TimeHour(t); if(hour>=0 && hour<7) return("Asia"); if(hour>=7 && hour<15) return("London"); if(hour>=15 && hour<=23)return("NewYork"); return("Other"); } //+------------------------------------------------------------------+ //| Helper: News event detection (MT5 Economic Calendar) | //+------------------------------------------------------------------+ bool IsEconomicNewsEvent(datetime bartime) { MqlCalendarValue news[]; datetime fromTime = bartime - 60; datetime toTime = bartime + 60; int count = CalendarValueHistory(news, fromTime, toTime); if(count>0) { for(int i=0; i=CALENDAR_IMPORTANCE_MEDIUM) return(true); } } return(false); } //+------------------------------------------------------------------+ //| Helper: Rolling average volume | //+------------------------------------------------------------------+ double GetAvgVolume(const long &volume[], int index, int window) { double sum = 0; int n = 0; for(int i=index; i>index-window && i>=0; i--) { sum += volume[i]; n++; } return (n>0) ? sum/n : 0; } //+------------------------------------------------------------------+ //| Helper: ATR calculation | //+------------------------------------------------------------------+ double CalculateATR(int i, int period, const double &high[], const double &low[], const double &close[]) { if(i0) vWeight = (double)volume[i]/GetAvgVolume(volume, i, period); return alpha*vWeight*close[i] + (1-alpha*vWeight)*buffer[i-1]; } //+------------------------------------------------------------------+ //| Adaptive EMA for Ghost MA (skips news bars) | //+------------------------------------------------------------------+ double AdaptiveEMA_Ghost(int i, int period, const double &close[], const long &volume[], bool useVolume, const datetime &time[], double &buffer[]) { if(i==0) return close[i]; bool isNews = IsEconomicNewsEvent(time[i]); if(isNews) return buffer[i-1]; double alpha = 2.0/(period+1); double vWeight = 1.0; if(useVolume && volume[i]>0) vWeight = (double)volume[i]/GetAvgVolume(volume, i, period); return alpha*vWeight*close[i] + (1-alpha*vWeight)*buffer[i-1]; } //+------------------------------------------------------------------+ //| Developer Notes & Hooks | //| - Community Profiles, Color Themes, Multi-Alerts: Add here! | //+------------------------------------------------------------------+ /* README FOR DEVELOPERS SmartMA MT5 is a fully open, context-adaptive, self-explaining Moving Average for MetaTrader 5. - Context-detection: Sessions, News, Regime (trend/range/neutral) - Adaptive period and volume weighting - "Ghost" MA for comparison (w/o news bars) - ATR-based risk band - Self-explaining chart overlay and alerts Ready for profiles, color themes, regime modules, and more. Fork, improve, and help build the next-generation Moving Average! Repository: https://forge.mql5.io/steverosenstock/SmartMA Git: https://forge.mql5.io/steverosenstock/SmartMA.git MIT License Author: Steve Rosenstock */