In MetaEditor 5, create a new Custom Indicator named NYOpenSweepDetector.mq5 and paste the code below. It includes native SendNotification() integration so any M15 sweep immediately pushes the Entry, SL, and TP coordinates to your MT5 mobile app.
Code: Select all
//+------------------------------------------------------------------+
//| NYOpenSweepDetector.mq5 |
//| M15 Pre-Market Sweep & Close Inside Detector |
//+------------------------------------------------------------------+
#property copyright "Algorithmic Index Scalping"
#property version "1.00"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2
// Plot 1: Bearish Sweep Arrow
#property indicator_label1 "Bearish Sweep"
#property indicator_type1 DRAW_ARROW
#property indicator_color1 clrCrimson
#property indicator_width1 2
// Plot 2: Bullish Sweep Arrow
#property indicator_label2 "Bullish Sweep"
#property indicator_type2 DRAW_ARROW
#property indicator_color2 clrLimeGreen
#property indicator_width2 2
//--- Input Parameters (Defaulted to IC Markets Server Time: GMT+2 / GMT+3 DST)
input group "1. Session Window (Broker Server Time)"
input int InpPmStartHour = 9; // Pre-Market/London Start Hour (Server)
input int InpPmStartMin = 0; // Pre-Market/London Start Min
input int InpNyOpenHour = 16; // NY Cash Open Hour (Server: 16:30)
input int InpNyOpenMin = 30; // NY Cash Open Min
input int InpNyEndHour = 19; // NY Killzone End Hour (Server: 19:00)
input int InpNyEndMin = 0; // NY Killzone End Min
input group "2. M15 Sweep & Rejection Filters"
input double InpMinSweepPts = 1.5; // Min Sweep Beyond Level (Index Points)
input double InpMinWickPct = 30.0; // Min Wick Rejection (% of M15 Range)
input double InpSlBufferPts = 2.5; // Stop-Loss Buffer (Index Points)
input group "3. Telemetry & Mobile Alerts"
input bool InpPopupAlert = true; // Desktop Popup & Sound Alert
input bool InpMobilePush = true; // Send MT5 Mobile Push Notification
//--- Indicator Buffers
double BearishSweepBuffer[];
double BullishSweepBuffer[];
//--- State Tracking
datetime g_lastBarTime = 0;
datetime g_lastBearAlertDay = 0;
datetime g_lastBullAlertDay = 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BearishSweepBuffer, INDICATOR_DATA);
SetIndexBuffer(1, BullishSweepBuffer, INDICATOR_DATA);
PlotIndexSetInteger(0, PLOT_ARROW, 234); // Down Arrow
PlotIndexSetInteger(1, PLOT_ARROW, 233); // Up Arrow
ArraySetAsSeries(BearishSweepBuffer, true);
ArraySetAsSeries(BullishSweepBuffer, true);
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0);
if(_Period != PERIOD_M15)
Print("WARNING: Indicator is specifically calibrated for the M15 timeframe.");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Deinitialization |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, "NYSwp_");
}
//+------------------------------------------------------------------+
//| Helper: Convert HH:MM on a given bar's date into datetime |
//+------------------------------------------------------------------+
datetime GetSessionTime(datetime barTime, int hour, int minute)
{
MqlDateTime dt;
TimeToStruct(barTime, dt);
dt.hour = hour;
dt.min = minute;
dt.sec = 0;
return StructToTime(dt);
}
//+------------------------------------------------------------------+
//| 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 < 100) return(0);
ArraySetAsSeries(time, true);
ArraySetAsSeries(open, true);
ArraySetAsSeries(high, true);
ArraySetAsSeries(low, true);
ArraySetAsSeries(close, true);
int limit = rates_total - prev_calculated;
if(prev_calculated == 0)
{
ArrayInitialize(BearishSweepBuffer, 0.0);
ArrayInitialize(BullishSweepBuffer, 0.0);
limit = MathMin(rates_total - 2, 1500); // Scan last ~15 days on init
}
// Only run full evaluation when a new bar opens (evaluating closed bar index 1)
bool isNewBar = (time[0] != g_lastBarTime);
if(!isNewBar && prev_calculated > 0) return(rates_total);
g_lastBarTime = time[0];
int startIdx = (prev_calculated == 0) ? limit : 1;
for(int i = startIdx; i >= 1; i--)
{
BearishSweepBuffer[i] = 0.0;
BullishSweepBuffer[i] = 0.0;
datetime barTime = time[i];
datetime dayAnchor = GetSessionTime(barTime, 0, 0);
datetime pmStart = GetSessionTime(barTime, InpPmStartHour, InpPmStartMin);
datetime nyOpen = GetSessionTime(barTime, InpNyOpenHour, InpNyOpenMin);
datetime nyEnd = GetSessionTime(barTime, InpNyEndHour, InpNyEndMin);
// Only evaluate bars falling inside the NY Open Killzone
if(barTime < nyOpen || barTime >= nyEnd) continue;
// Scan backwards to find Pre-Market High & Low for this day
double pmHigh = -DBL_MAX;
double pmLow = DBL_MAX;
bool foundPm = false;
for(int j = i + 1; j < rates_total; j++)
{
if(time[j] < pmStart) break;
if(time[j] >= pmStart && time[j] < nyOpen)
{
if(high[j] > pmHigh) pmHigh = high[j];
if(low[j] < pmLow) pmLow = low[j];
foundPm = true;
}
}
if(!foundPm) continue;
// Draw Pre-Market High & Low projection lines for the day
string highObj = "NYSwp_High_" + TimeToString(dayAnchor, TIME_DATE);
string lowObj = "NYSwp_Low_" + TimeToString(dayAnchor, TIME_DATE);
if(ObjectFind(0, highObj) < 0)
{
ObjectCreate(0, highObj, OBJ_TREND, 0, pmStart, pmHigh, nyEnd, pmHigh);
ObjectSetInteger(0, highObj, OBJ_COLOR, clrDodgerBlue);
ObjectSetInteger(0, highObj, OBJ_STYLE, STYLE_DOT);
ObjectSetInteger(0, highObj, OBJ_WIDTH, 2);
ObjectSetInteger(0, highObj, OBJ_RAY_RIGHT, false);
}
if(ObjectFind(0, lowObj) < 0)
{
ObjectCreate(0, lowObj, OBJ_TREND, 0, pmStart, pmLow, nyEnd, pmLow);
ObjectSetInteger(0, lowObj, OBJ_COLOR, clrDodgerBlue);
ObjectSetInteger(0, lowObj, OBJ_STYLE, STYLE_DOT);
ObjectSetInteger(0, lowObj, OBJ_WIDTH, 2);
ObjectSetInteger(0, lowObj, OBJ_RAY_RIGHT, false);
}
// Evaluate M15 Candle Geometry
double rng = high[i] - low[i];
if(rng <= 0.0) continue;
// 1. Check Bearish Liquidity Sweep (PM High Swept & Closed Inside)
double bearSweepDist = high[i] - pmHigh;
double upperWick = high[i] - MathMax(open[i], close[i]);
double upperWickPct = (upperWick / rng) * 100.0;
if(bearSweepDist >= InpMinSweepPts && close[i] < pmHigh && upperWickPct >= InpMinWickPct)
{
BearishSweepBuffer[i] = high[i] + (rng * 0.25);
if(i == 1 && g_lastBearAlertDay != dayAnchor)
{
g_lastBearAlertDay = dayAnchor;
double sl = high[i] + InpSlBufferPts;
double risk = sl - close[i];
double tp1 = close[i] - (risk * 1.5);
double tp2 = close[i] - (risk * 3.0);
FireAlerts("SHORT (PM High Sweep)", close[i], sl, tp1, tp2);
}
}
// 2. Check Bullish Liquidity Sweep (PM Low Swept & Closed Inside)
double bullSweepDist = pmLow - low[i];
double lowerWick = MathMin(open[i], close[i]) - low[i];
double lowerWickPct = (lowerWick / rng) * 100.0;
if(bullSweepDist >= InpMinSweepPts && close[i] > pmLow && lowerWickPct >= InpMinWickPct)
{
BullishSweepBuffer[i] = low[i] - (rng * 0.25);
if(i == 1 && g_lastBullAlertDay != dayAnchor)
{
g_lastBullAlertDay = dayAnchor;
double sl = low[i] - InpSlBufferPts;
double risk = close[i] - sl;
double tp1 = close[i] + (risk * 1.5);
double tp2 = close[i] + (risk * 3.0);
FireAlerts("LONG (PM Low Sweep)", close[i], sl, tp1, tp2);
}
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//| Dispatch Desktop & Mobile Push Notifications |
//+------------------------------------------------------------------+
void FireAlerts(string signalType, double entry, double sl, double tp1, double tp2)
{
string msg = StringFormat("%s M15 %s | Entry: %.2f | SL: %.2f | TP1(1.5R): %.2f | TP2(3R): %.2f",
_Symbol, signalType, entry, sl, tp1, tp2);
if(InpPopupAlert) Alert(msg);
if(InpMobilePush) SendNotification(msg);
}
//+------------------------------------------------------------------+