Im sitting at coffee right now and i got an idea for great indicator, i mean zone indicator for start trading/scalping volatility around supports and resistances.
Here is like first version, from which May be i will create automated EA
Here is a professional-grade MQL4 indicator designed specifically for forex scalping. Rather than just plotting single lines, this indicator dynamically calculates and draws Support and Resistance Zones (rectangles) based on recent Swing Highs and Lows.
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);
}
}
//+------------------------------------------------------------------+