295 lines
11 KiB
MQL5
295 lines
11 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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<rates_total; i++)
|
|
{
|
|
//--- 1. Detect context
|
|
session = DetectSession(time[i]);
|
|
isNews = (Use_News) ? IsEconomicNewsEvent(time[i]) : false;
|
|
highVol = (Use_Volume && tick_volume[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<count; i++)
|
|
{
|
|
if(news[i].impact>=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(i<period) return 0;
|
|
double sum = 0;
|
|
for(int j=0; j<period; j++)
|
|
{
|
|
double tr1 = high[i-j] - low[i-j];
|
|
double tr2 = MathAbs(high[i-j] - close[i-j-1]);
|
|
double tr3 = MathAbs(low[i-j] - close[i-j-1]);
|
|
double tr = MathMax(tr1, MathMax(tr2,tr3));
|
|
sum += tr;
|
|
}
|
|
return sum/period;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Adaptive EMA (volume-weighted, context-sensitive) |
|
|
//+------------------------------------------------------------------+
|
|
double AdaptiveEMA(int i, int period, const double &close[], const long &volume[], bool useVolume, double &buffer[])
|
|
{
|
|
if(i==0) return close[i];
|
|
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];
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 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
|
|
|
|
*/
|
|
|