In MT5, we can pass the ATR handle directly into the Moving Average calculation as the applied_price, which keeps the engine incredibly efficient during high-tick data overlaps.
Code: Select all
//+------------------------------------------------------------------+
//| London_Liquidity_Zones_MT5.mq5 |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_label1 "Slippage Danger"
#property indicator_type1 DRAW_ARROW
#property indicator_color1 clrRed
#property indicator_width1 2
input string SessionStart = "10:00"; // London Start (Broker Server HH:MM)
input string SessionEnd = "18:30"; // London End (Broker Server HH:MM)
input color SessionColor = clrDarkSlateGray; // Session Background
input int AtrPeriod = 14; // ATR Length
input int AtrMaPeriod = 50; // Average ATR Length
input double AtrSpikeMult = 2.5; // Spike Multiplier (Slippage Warning)
double dangerBuffer[];
int atrHandle;
int maHandle;
int startHour, startMin, endHour, endMin;
int OnInit()
{
SetIndexBuffer(0, dangerBuffer, INDICATOR_DATA);
PlotIndexSetInteger(0, PLOT_ARROW, 251); // Wingdings Cross
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
string startArr[], endArr[];
StringSplit(SessionStart, ':', startArr);
StringSplit(SessionEnd, ':', endArr);
startHour = (int)StringToInteger(startArr[0]);
startMin = (int)StringToInteger(startArr[1]);
endHour = (int)StringToInteger(endArr[0]);
endMin = (int)StringToInteger(endArr[1]);
atrHandle = iATR(_Symbol, _Period, AtrPeriod);
if(atrHandle == INVALID_HANDLE) return INIT_FAILED;
// Natively apply the MA to the ATR handle in MT5
maHandle = iMA(_Symbol, _Period, AtrMaPeriod, 0, MODE_SMA, atrHandle);
if(maHandle == INVALID_HANDLE) return INIT_FAILED;
return(INIT_SUCCEEDED);
}
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[])
{
ArraySetAsSeries(time, true);
ArraySetAsSeries(high, true);
ArraySetAsSeries(low, true);
ArraySetAsSeries(dangerBuffer, true);
int limit = rates_total - prev_calculated;
if(limit == 0) limit = 1;
if(limit >= rates_total) limit = rates_total - AtrMaPeriod;
double atr[], ma[];
ArraySetAsSeries(atr, true);
ArraySetAsSeries(ma, true);
if(CopyBuffer(atrHandle, 0, 0, limit, atr) <= 0) return 0;
if(CopyBuffer(maHandle, 0, 0, limit, ma) <= 0) return 0;
for(int i = limit - 1; i >= 0; i--)
{
bool inSession = IsInSession(time[i]);
if(inSession && atr[i] > (ma[i] * AtrSpikeMult)) {
// Plot directly beneath the candle structure
dangerBuffer[i] = low[i] - (atr[i] * 0.5);
} else {
dangerBuffer[i] = EMPTY_VALUE;
}
if(inSession) {
DrawSessionBackground(time[i], high[i], low[i]);
}
}
return(rates_total);
}
bool IsInSession(datetime t)
{
MqlDateTime dt;
TimeToStruct(t, dt);
int currentMins = dt.hour * 60 + dt.min;
int sMins = startHour * 60 + startMin;
int eMins = endHour * 60 + endMin;
if(sMins <= eMins) return (currentMins >= sMins && currentMins < eMins);
else return (currentMins >= sMins || currentMins < eMins);
}
void DrawSessionBackground(datetime t, double h, double l)
{
MqlDateTime dt;
TimeToStruct(t, dt);
dt.hour = 0; dt.min = 0; dt.sec = 0;
datetime dayStart = StructToTime(dt);
string objName = "LondonZone_" + TimeToString(dayStart, TIME_DATE);
if(ObjectFind(0, objName) < 0) {
ObjectCreate(0, objName, OBJ_RECTANGLE, 0, t, h, t, l);
ObjectSetInteger(0, objName, OBJPROP_COLOR, SessionColor);
ObjectSetInteger(0, objName, OBJPROP_BACK, true);
ObjectSetInteger(0, objName, OBJPROP_FILL, true);
ObjectSetInteger(0, objName, OBJPROP_HIDDEN, true);
ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
} else {
double oldHigh = ObjectGetDouble(0, objName, OBJPROP_PRICE, 0);
double oldLow = ObjectGetDouble(0, objName, OBJPROP_PRICE, 1);
datetime oldTime1 = (datetime)ObjectGetInteger(0, objName, OBJPROP_TIME, 0);
datetime oldTime2 = (datetime)ObjectGetInteger(0, objName, OBJPROP_TIME, 1);
if(h > oldHigh) ObjectSetDouble(0, objName, OBJPROP_PRICE, 0, h);
if(l < oldLow) ObjectSetDouble(0, objName, OBJPROP_PRICE, 1, l);
if(t < oldTime1) ObjectSetInteger(0, objName, OBJPROP_TIME, 0, t);
if(t > oldTime2) ObjectSetInteger(0, objName, OBJPROP_TIME, 1, t);
}
}