Public/Indicators/MA + ATR Bands.mq5

347 lines
No EOL
13 KiB
MQL5

//+------------------------------------------------------------------+
//| MA + ATR Bands.mq5 |
//| Copyright 2026, HectorandAlgos |
//| https://www.mql5.com/en/users/hector001 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, HectorandAlgos"
#property link "https://www.mql5.com/en/users/hector001"
#property version "1.00"
#property indicator_chart_window
#property indicator_buffers 4
#property indicator_plots 3
//--- Plot SMA
#property indicator_label1 "SMA"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
//--- Plot Upper Band
#property indicator_label2 "Upper Band"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrOrangeRed
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
//--- Plot Lower Band
#property indicator_label3 "Lower Band"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrLimeGreen
#property indicator_style3 STYLE_SOLID
#property indicator_width3 1
enum ENUM_MODE
{
MODE_SYMMETRIC,
MODE_ASYMMETRIC
};
enum ENUM_REFERENCE_PRICE
{
PRICE_CURRENT_BAR,
PRICE_PREVIOUS_BAR
};
//--- Input parameters
input int MAPeriod = 20; // MA Period
input int ATRPeriod = 14; // ATR Period
input double Multiplier = 2.0; // Mode 1 Multiplier
input double BigMultiplier = 2.5; // Mode 2 Big Multiplier
input double SmallMultiplier= 1.5; // Mode 2 Small Multiplier
input ENUM_MODE Mode = MODE_SYMMETRIC; // Mode 1 - Symmetric OR Mode 2 - Asymmetric
input ENUM_REFERENCE_PRICE RefPrice = PRICE_CURRENT_BAR; // Use Current Bar Close OR Use Previous Bar Close
input bool UsePriceCenter = false; // Use Current Price as Center?
input bool DebugPrint = true; // Enable Debug Printing
//--- Indicator buffers
double SMABuffer[];
double UpperBuffer[];
double LowerBuffer[];
double PriceCenterBuffer[];
//--- Handles
int maHandle;
int atrHandle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Set buffers
SetIndexBuffer(0, SMABuffer, INDICATOR_DATA);
SetIndexBuffer(1, UpperBuffer, INDICATOR_DATA);
SetIndexBuffer(2, LowerBuffer, INDICATOR_DATA);
SetIndexBuffer(3, PriceCenterBuffer, INDICATOR_CALCULATIONS);
//--- Set plot labels
PlotIndexSetString(0, PLOT_LABEL, "SMA");
PlotIndexSetString(1, PLOT_LABEL, "Upper Band");
PlotIndexSetString(2, PLOT_LABEL, "Lower Band");
//--- Create handles
maHandle = iMA(_Symbol, _Period, MAPeriod, 0, MODE_SMA, PRICE_CLOSE);
atrHandle = iATR(_Symbol, _Period, ATRPeriod);
if(maHandle == INVALID_HANDLE || atrHandle == INVALID_HANDLE)
{
Print("Error creating indicator handles");
return(INIT_FAILED);
}
if(DebugPrint)
{
Print("========================================");
Print("MA + ATR Bands Indicator Initialized");
Print("Symbol: ", _Symbol, " | Period: ", EnumToString(_Period));
Print("MA Period: ", MAPeriod, " | ATR Period: ", ATRPeriod);
Print("Mode: ", Mode == MODE_SYMMETRIC ? "Symmetric" : "Asymmetric");
Print("Reference Price: ", RefPrice == PRICE_CURRENT_BAR ? "Current Bar" : "Previous Bar");
Print("Use Price Center: ", UsePriceCenter ? "YES" : "NO");
Print("Multiplier (Mode 1): ", Multiplier);
Print("Big Multiplier (Mode 2): ", BigMultiplier);
Print("Small Multiplier (Mode 2): ", SmallMultiplier);
Print("Debug Print: ", DebugPrint ? "ENABLED" : "DISABLED");
Print("========================================");
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(maHandle != INVALID_HANDLE) IndicatorRelease(maHandle);
if(atrHandle != INVALID_HANDLE) IndicatorRelease(atrHandle);
if(DebugPrint)
{
Print("========================================");
Print("MA + ATR Bands Indicator Deinitialized");
Print("Reason: ", reason);
Print("========================================");
}
}
//+------------------------------------------------------------------+
//| 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[])
{
if(rates_total < MAPeriod || rates_total < ATRPeriod)
{
if(DebugPrint)
Print("Not enough bars: rates_total=", rates_total, " MAPeriod=", MAPeriod, " ATRPeriod=", ATRPeriod);
return(0);
}
//--- Determine start point
int start = 0;
if(prev_calculated == 0)
{
start = 0;
if(DebugPrint)
Print("First run: Starting from bar 0");
}
else
{
start = prev_calculated - 1;
if(start < 1) start = 1;
if(DebugPrint)
Print("Update run: Starting from bar ", start, " (prev_calculated=", prev_calculated, ")");
}
//--- FIX: Copy data WITHOUT setting as series
//--- We'll use normal array indexing (0 = oldest, rates_total-1 = newest)
double maBuffer[];
double atrBuffer[];
//--- Copy ALL bars from the beginning
int maCopied = CopyBuffer(maHandle, 0, 0, rates_total, maBuffer);
int atrCopied = CopyBuffer(atrHandle, 0, 0, rates_total, atrBuffer);
if(DebugPrint)
{
Print("Data Copy Status:");
Print(" maCopied: ", maCopied, " / ", rates_total, " bars");
Print(" atrCopied: ", atrCopied, " / ", rates_total, " bars");
//--- Check first few values to verify data is valid
if(maCopied > 0 && atrCopied > 0)
{
Print(" Sample SMA[0]: ", DoubleToString(maBuffer[0], _Digits));
Print(" Sample SMA[", rates_total-1, "]: ", DoubleToString(maBuffer[rates_total-1], _Digits));
Print(" Sample ATR[0]: ", DoubleToString(atrBuffer[0], _Digits));
Print(" Sample ATR[", rates_total-1, "]: ", DoubleToString(atrBuffer[rates_total-1], _Digits));
}
}
if(maCopied <= 0 || atrCopied <= 0)
{
if(DebugPrint)
{
Print("ERROR: Failed to copy indicator data!");
Print(" maCopied=", maCopied, " atrCopied=", atrCopied);
}
return(0);
}
//--- Check for invalid data (DBL_MAX or DBL_MIN)
bool validData = true;
if(maCopied > 0)
{
double lastSMA = maBuffer[rates_total-1];
if(!MathIsValidNumber(lastSMA) || lastSMA > 1e308 || lastSMA < 1e-308)
{
if(DebugPrint)
Print("ERROR: Invalid SMA data! lastSMA=", DoubleToString(lastSMA, _Digits));
validData = false;
}
}
if(atrCopied > 0)
{
double lastATR = atrBuffer[rates_total-1];
if(!MathIsValidNumber(lastATR) || lastATR > 1e308 || lastATR < 1e-308)
{
if(DebugPrint)
Print("ERROR: Invalid ATR data! lastATR=", DoubleToString(lastATR, _Digits));
validData = false;
}
}
if(!validData)
return(0);
//--- Main calculation loop - process from start to end
for(int i = start; i < rates_total; i++)
{
double sma = maBuffer[i];
double atr = atrBuffer[i];
double currentPrice = close[i];
//--- Determine which price to use for comparison
double refPrice;
if(RefPrice == PRICE_CURRENT_BAR)
refPrice = close[i];
else // PRICE_PREVIOUS_BAR
refPrice = (i > 0) ? close[i-1] : close[i];
//--- Center for bands
double center = UsePriceCenter ? currentPrice : sma;
PriceCenterBuffer[i] = center;
//--- Calculate bands based on mode
if(Mode == MODE_SYMMETRIC)
{
UpperBuffer[i] = center + (Multiplier * atr);
LowerBuffer[i] = center - (Multiplier * atr);
}
else // MODE_ASYMMETRIC
{
if(refPrice > sma)
{
UpperBuffer[i] = center + (BigMultiplier * atr);
LowerBuffer[i] = center - (SmallMultiplier * atr);
}
else if(refPrice < sma)
{
UpperBuffer[i] = center + (SmallMultiplier * atr);
LowerBuffer[i] = center - (BigMultiplier * atr);
}
else // refPrice == sma
{
UpperBuffer[i] = center + (BigMultiplier * atr);
LowerBuffer[i] = center - (BigMultiplier * atr);
}
}
SMABuffer[i] = sma;
}
//--- DEBUG: Print last candle figures
if(DebugPrint && rates_total > 0)
{
int lastIdx = rates_total - 1;
Print("========================================");
Print("LAST CANDLE FIGURES (Index: ", lastIdx, ")");
Print("----------------------------------------");
Print("Time: ", TimeToString(time[lastIdx]));
Print("Price Data:");
Print(" Open: ", DoubleToString(open[lastIdx], _Digits));
Print(" High: ", DoubleToString(high[lastIdx], _Digits));
Print(" Low: ", DoubleToString(low[lastIdx], _Digits));
Print(" Close: ", DoubleToString(close[lastIdx], _Digits));
Print("----------------------------------------");
Print("Indicator Values:");
Print(" SMA: ", DoubleToString(SMABuffer[lastIdx], _Digits));
Print(" ATR: ", DoubleToString(atrBuffer[lastIdx], _Digits));
Print(" Center: ", DoubleToString(PriceCenterBuffer[lastIdx], _Digits));
Print(" Upper Band: ", DoubleToString(UpperBuffer[lastIdx], _Digits));
Print(" Lower Band: ", DoubleToString(LowerBuffer[lastIdx], _Digits));
Print("----------------------------------------");
Print("Calculation Details:");
Print(" Ref Price: ", DoubleToString(refPriceFromClose(close, RefPrice, lastIdx), _Digits));
Print(" Center Source: ", UsePriceCenter ? "Current Close" : "SMA");
if(Mode == MODE_SYMMETRIC)
{
Print(" Mode: Symmetric");
Print(" Formula: Upper = Center + (", Multiplier, " × ATR)");
Print(" Lower = Center - (", Multiplier, " × ATR)");
}
else
{
Print(" Mode: Asymmetric");
double refPriceVal = refPriceFromClose(close, RefPrice, lastIdx);
string position = refPriceVal > SMABuffer[lastIdx] ? "ABOVE SMA" :
(refPriceVal < SMABuffer[lastIdx] ? "BELOW SMA" : "EQUAL TO SMA");
Print(" Price Position: ", position);
if(refPriceVal > SMABuffer[lastIdx])
{
Print(" Formula: Upper = Center + (", BigMultiplier, " × ATR)");
Print(" Lower = Center - (", SmallMultiplier, " × ATR)");
}
else if(refPriceVal < SMABuffer[lastIdx])
{
Print(" Formula: Upper = Center + (", SmallMultiplier, " × ATR)");
Print(" Lower = Center - (", BigMultiplier, " × ATR)");
}
else
{
Print(" Formula: Upper = Center + (", BigMultiplier, " × ATR)");
Print(" Lower = Center - (", BigMultiplier, " × ATR)");
}
}
Print(" Spread: ", spread[lastIdx]);
Print(" Tick Volume: ", tick_volume[lastIdx]);
Print("========================================");
}
return(rates_total);
}
//+------------------------------------------------------------------+
//| Helper function to get reference price |
//+------------------------------------------------------------------+
double refPriceFromClose(const double &close[], ENUM_REFERENCE_PRICE refType, int index)
{
if(refType == PRICE_CURRENT_BAR)
return close[index];
else // PRICE_PREVIOUS_BAR
return (index > 0) ? close[index-1] : close[index];
}
//+------------------------------------------------------------------+