347 lines
13 KiB
MQL5
347 lines
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];
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|