im big fan of trading or scalping support and resistance zones, because there is higher probability of volatility.
For scalping, identifying the "liquidity zone" between the wick and the body of a reversal candle is often the most accurate way to define an ideal buying or selling range. This indicator includes that exact price-action logic, alongside an option to use a fixed-pip width.
MQL4 Source Code
1.) Open MetaTrader 4 and press F4 to open the MetaEditor.
2.) In the Navigator panel, right-click Indicators -> New File -> Custom Indicator.
3.) Name it Scalping_SR_Zones and click Finish.
4.) Replace all the default code with the script below and click Compile.
Code: Select all
//+------------------------------------------------------------------+
//| Scalping_SR_Zones.mq4 |
//| Professional S&R for Scalping |
//+------------------------------------------------------------------+
#property copyright "Professional Trading Solutions"
#property version "1.00"
#property strict
#property indicator_chart_window
enum ENUM_ZONE_CALC {
ZONE_WICK_BODY = 0, // Wick to Body (Price Action)
ZONE_FIXED_PIPS = 1 // Fixed Pips Width
};
//--- Input Parameters
input string sec1 = "--- Zone Settings ---";
input int SwingBars = 12; // Swing Detection Bars (Left/Right)
input int MaxZones = 4; // Maximum Active Zones per side
input ENUM_ZONE_CALC ZoneCalcMethod = ZONE_WICK_BODY; // Zone Calculation Method
input double ZonePips = 3.0; // Zone Width (if Fixed Pips selected)
input string sec2 = "--- Visual Settings ---";
input color ResistanceCol = clrLightCoral; // Resistance Zone Color
input color SupportCol = clrLightGreen; // Support Zone Color
input int ProjectBars = 15; // How many bars to project the zone forward
//--- Global Variables
double pipsMult;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Determine pip multiplier for 3/5 digit brokers
pipsMult = (_Digits == 5 || _Digits == 3) ? _Point * 10 : _Point;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Clean up chart objects upon removal
ObjectsDeleteAll(0, "SR_ZONE_");
}
//+------------------------------------------------------------------+
//| 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[])
{
// Ensure we have enough data to calculate swings
if(rates_total < SwingBars * 2 + 1) return(0);
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(Symbol(), 0, 0);
// Performance optimization: Only recalculate zones when a new bar opens
// This prevents chart flickering and reduces CPU load significantly.
if(lastBarTime == currentBarTime) return(rates_total);
lastBarTime = currentBarTime;
int resFound = 0;
int supFound = 0;
// Scan backwards from the most recently closed candles
for(int i = SwingBars + 1; i < iBars(Symbol(), 0) - SwingBars; i++)
{
// Stop scanning if we have found the maximum allowed recent zones
if(resFound >= MaxZones && supFound >= MaxZones) break;
bool isRes = true;
bool isSup = true;
double currentHigh = iHigh(Symbol(), 0, i);
double currentLow = iLow(Symbol(), 0, i);
// Check surrounding bars to confirm a Swing High or Swing Low
for(int j = 1; j <= SwingBars; j++)
{
if(currentHigh <= iHigh(Symbol(), 0, i+j) || currentHigh <= iHigh(Symbol(), 0, i-j)) isRes = false;
if(currentLow >= iLow(Symbol(), 0, i+j) || currentLow >= iLow(Symbol(), 0, i-j)) isSup = false;
}
// If a Resistance Swing High is confirmed
if(isRes && resFound < MaxZones)
{
double topEdge, botEdge;
if(ZoneCalcMethod == ZONE_FIXED_PIPS)
{
topEdge = currentHigh;
botEdge = currentHigh - (ZonePips * pipsMult);
}
else
{
topEdge = currentHigh;
botEdge = MathMax(iOpen(Symbol(), 0, i), iClose(Symbol(), 0, i));
if(topEdge == botEdge) botEdge = topEdge - (_Point * 10); // Fallback if no wick exists
}
DrawZone("SR_ZONE_RES_" + IntegerToString(resFound), iTime(Symbol(), 0, i), topEdge, currentBarTime + PeriodSeconds() * ProjectBars, botEdge, ResistanceCol);
resFound++;
}
// If a Support Swing Low is confirmed
if(isSup && supFound < MaxZones)
{
double botEdge, topEdge;
if(ZoneCalcMethod == ZONE_FIXED_PIPS)
{
botEdge = currentLow;
topEdge = currentLow + (ZonePips * pipsMult);
}
else
{
botEdge = currentLow;
topEdge = MathMin(iOpen(Symbol(), 0, i), iClose(Symbol(), 0, i));
if(topEdge == botEdge) topEdge = botEdge + (_Point * 10); // Fallback if no wick exists
}
DrawZone("SR_ZONE_SUP_" + IntegerToString(supFound), iTime(Symbol(), 0, i), topEdge, currentBarTime + PeriodSeconds() * ProjectBars, botEdge, SupportCol);
supFound++;
}
}
// Cleanup any residual old zones if the market shifted and fewer zones were found
for(int k = resFound; k < MaxZones; k++) ObjectDelete(0, "SR_ZONE_RES_" + IntegerToString(k));
for(int k = supFound; k < MaxZones; k++) ObjectDelete(0, "SR_ZONE_SUP_" + IntegerToString(k));
return(rates_total);
}
//+------------------------------------------------------------------+
//| Helper function to draw or update rectangle zones |
//+------------------------------------------------------------------+
void DrawZone(string name, datetime time1, double price1, datetime time2, double price2, color clr)
{
if(ObjectFind(0, name) < 0)
{
// Create new object if it doesn't exist
ObjectCreate(0, name, OBJ_RECTANGLE, 0, time1, price1, time2, price2);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_BACK, true);
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
}
else
{
// Efficiently update existing object coordinates
ObjectSetInteger(0, name, OBJPROP_TIME, 0, time1);
ObjectSetDouble(0, name, OBJPROP_PRICE, 0, price1);
ObjectSetInteger(0, name, OBJPROP_TIME, 1, time2);
ObjectSetDouble(0, name, OBJPROP_PRICE, 1, price2);
}
}
//+------------------------------------------------------------------+Zone Method (ZONE_WICK_BODY): Institutional order flow often sits inside the wick of a reversal candle. This default setting automatically draws the support zone from the lowest point of the wick up to the candle's closing/opening body. This highlights your precise entry and stop-loss buffer ranges.
Timeframes: For scalping, attach this to the M1, M5, or M15 charts. The algorithm automatically calculates the zones based on the timeframe it is placed on.
Swing Bars: This determines how "major" the zone is. The default of 12 means a candle must be the absolute high/low compared to the 12 candles to its left and right. If you want faster, more aggressive scalping zones on a 1-minute chart, reduce SwingBars to 5 or 7.