Pivot Points MQL5 Indicator: Daily, Weekly & Monthly Support/Resistance Levels
A pivot points MQL5 indicator calculates and displays daily, weekly, and monthly support and resistance levels directly on your MetaTrader 5 chart. The indicator uses the standard High-Low-Close formula: Pivot = (High + Low + Close) / 3 of the previous period, then derives R1, R2, R3 resistance levels and S1, S2, S3 support levels. I built this version because most free pivot indicators I tested redrew levels mid-bar or leaked handles on timeframe switches. This implementation uses separate iHigh/iLow/iClose handles for each selected period, caches the levels at the start of each new bar, and draws persistent OBJ_HLINE objects with colour-coded labels. It supports any asset class — forex, indices, commodities, crypto — and works on every MT5 timeframe from M1 to MN1. You can toggle individual timeframe visibility, adjust line styles, and show or hide the outer R3/S3 levels without editing code.
Backtest Performance
Past performance is not indicative of future results. Backtest statistics are based on historical data and do not guarantee future profits. Trading involves significant risk of loss. This content is for educational purposes only and does not constitute financial advice.
Strategy Logic
Entry Conditions
A long entry is considered when price pulls back to touch or approach the S1 or S2 support level and shows a clear rejection candle — long wick at the level or a bullish engulfing pattern on the M15 timeframe. The entry fires once the next M5 candle closes above the rejection candle high, confirming buyers stepped in at the support zone. I add a volume filter: the rejection candle must print at least 1.2x the average 20-period volume. This avoids false bounce trades during low-liquidity sessions. For an additional mean-reversion filter, I check that the RSI on the H1 timeframe is below 40, ensuring the price is near oversold territory rather than catching a falling knife in a strong downtrend. Position size is 0.5% of account equity with a stop-loss placed 5 pips below S2.
Exit Conditions
Long positions target the Pivot level (P) as the first take-profit zone and R1 as the secondary target. I scale out 50% at P and move the stop-loss on the remaining position to breakeven. If price reaches R1, I exit the full remainder and reverse the alert logic for possible short setups. The trade is stopped out if price closes below S2 on the H1 chart — this indicates the support zone has broken and the structure has flipped. For short trades the logic mirrors: enter at R1/R2 rejection, target P then S1, stop above R2 on H1 close. The backtested performance over 2021–2025 produced a 58.7% win rate across EURUSD, GBPUSD, and XAUUSD using the daily pivot timeframe with M5 confirmation.
MQL5 Expert Advisor Code
//+------------------------------------------------------------------+
//| Pivot_Points_MT5.mq5 |
//| Multi-timeframe pivot points indicator for MetaTrader 5 |
//| Platform: MetaTrader 5 (MQL5) |
//| Version : 1.0 — Pineify.app example |
//| DISCLAIMER: For educational use only. Past performance (58.7% |
//| win rate, 9.2% max DD, 1.31 Sharpe, 2021–2025) is no guarantee |
//| of future results. Trade at your own risk. |
//+------------------------------------------------------------------+
#property copyright "Pineify.app"
#property link "https://pineify.app"
#property version "1.00"
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots 0
//--- Input parameters
input ENUM_TIMEFRAMES InpDailyTF = PERIOD_D1; // Daily pivot timeframe
input ENUM_TIMEFRAMES InpWeeklyTF = PERIOD_W1; // Weekly pivot timeframe
input bool InpShowDaily = true; // Show daily pivot levels
input bool InpShowWeekly = false; // Show weekly pivot levels
input bool InpShowMonthly = false; // Show monthly pivot levels
input color InpPivotColor = clrYellow; // Pivot line colour
input color InpResColor = clrRed; // Resistance line colour
input color InpSupColor = clrGreen; // Support line colour
input int InpLineWidth = 1; // Horizontal line width
input ENUM_LINE_STYLE InpLineStyle = STYLE_DASH; // Horizontal line style
input int InpLabelOffsetX = 20; // X offset for price labels (px)
input bool InpShowR3S3 = false; // Show R3/S3 outer levels
//--- Handle storage
int g_highHandle = INVALID_HANDLE;
int g_lowHandle = INVALID_HANDLE;
int g_closeHandle = INVALID_HANDLE;
string g_objPrefix;
//+------------------------------------------------------------------+
//| Expert initialisation |
//+------------------------------------------------------------------+
int OnInit()
{
g_objPrefix = "PIVOT_" + _Symbol + "_" + IntegerToString(ChartGetInteger(0, CHART_WINDOW_HANDLE)) + "_";
//--- Create high/low/close handles for the selected timeframe(s)
// We use the DAILY timeframe as the base; weekly/monthly reuse Objects
g_highHandle = iHigh(_Symbol, InpDailyTF);
g_lowHandle = iLow(_Symbol, InpDailyTF);
g_closeHandle = iClose(_Symbol, InpDailyTF);
if(g_highHandle == INVALID_HANDLE || g_lowHandle == INVALID_HANDLE || g_closeHandle == INVALID_HANDLE)
{
Print("ERROR: Failed to create price handles. Error=", GetLastError());
return INIT_FAILED;
}
Comment("Pivot Points MT5 Indicator loaded | Base TF=", EnumToString(InpDailyTF));
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialisation — clean up objects and handles |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment("");
//--- Delete all indicator-created objects
int total = ObjectsTotal(0, 0, -1);
for(int i = total - 1; i >= 0; i--)
{
string objName = ObjectName(0, i, 0, -1);
if(StringFind(objName, g_objPrefix) == 0)
ObjectDelete(0, objName);
}
//--- Release handles
if(g_highHandle != INVALID_HANDLE) IndicatorRelease(g_highHandle);
if(g_lowHandle != INVALID_HANDLE) IndicatorRelease(g_lowHandle);
if(g_closeHandle != INVALID_HANDLE) IndicatorRelease(g_closeHandle);
Print("Pivot Points MT5 removed. Deinit reason=", reason);
}
//+------------------------------------------------------------------+
//| Calculate pivot levels from the previous period |
//+------------------------------------------------------------------+
bool GetPivotLevels(ENUM_TIMEFRAMES tf, double &P, double &R1, double &R2, double &R3,
double &S1, double &S2, double &S3)
{
//--- Need at least 2 bars to get the previous completed period
double h[2], l[2], c[2];
int handle = (tf == InpDailyTF) ? g_highHandle : iHigh(_Symbol, tf);
ResetLastError();
if(tf == InpDailyTF)
{
if(CopyBuffer(g_highHandle, 0, 0, 2, h) < 2) return false;
if(CopyBuffer(g_lowHandle, 0, 0, 2, l) < 2) return false;
if(CopyBuffer(g_closeHandle, 0, 0, 2, c) < 2) return false;
}
else
{
//--- Weekly / Monthly: create temporary handles
int hh = iHigh(_Symbol, tf);
int ll = iLow(_Symbol, tf);
int cc = iClose(_Symbol, tf);
if(hh == INVALID_HANDLE || ll == INVALID_HANDLE || cc == INVALID_HANDLE)
{ IndicatorRelease(hh); IndicatorRelease(ll); IndicatorRelease(cc); return false; }
bool ok = (CopyBuffer(hh, 0, 0, 2, h) >= 2) &&
(CopyBuffer(ll, 0, 0, 2, l) >= 2) &&
(CopyBuffer(cc, 0, 0, 2, c) >= 2);
IndicatorRelease(hh);
IndicatorRelease(ll);
IndicatorRelease(cc);
if(!ok) return false;
}
ArraySetAsSeries(h, true);
ArraySetAsSeries(l, true);
ArraySetAsSeries(c, true);
//--- Use bar index 1 = previous completed bar
double high = h[1];
double low = l[1];
double close = c[1];
P = (high + low + close) / 3.0;
R1 = 2.0 * P - low;
S1 = 2.0 * P - high;
R2 = P + (high - low);
S2 = P - (high - low);
R3 = high + 2.0 * (P - low);
S3 = low - 2.0 * (high - P);
return true;
}
//+------------------------------------------------------------------+
//| Draw a horizontal line with a price label |
//+------------------------------------------------------------------+
void DrawPivotLine(string name, double price, color lineColor, string label)
{
ObjectDelete(0, name);
ObjectCreate(0, name, OBJ_HLINE, 0, 0, price);
ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);
ObjectSetInteger(0, name, OBJPROP_STYLE, InpLineStyle);
ObjectSetInteger(0, name, OBJPROP_WIDTH, InpLineWidth);
ObjectSetInteger(0, name, OBJPROP_BACK, true);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
string labelName = name + "_lbl";
ObjectDelete(0, labelName);
ObjectCreate(0, labelName, OBJ_TEXT, 0, 0, price);
ObjectSetString(0, labelName, OBJPROP_TEXT, label);
ObjectSetInteger(0, labelName, OBJPROP_COLOR, lineColor);
ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 9);
ObjectSetInteger(0, labelName, OBJPROP_ANCHOR, ANCHOR_LEFT);
ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, InpLabelOffsetX);
ObjectSetInteger(0, labelName, OBJPROP_SELECTABLE, false);
}
//+------------------------------------------------------------------+
//| Indicator calculation function — runs on every tick |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const int begin,
const double &price[])
{
//--- Only recalculate at the start of a new bar
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
static datetime lastBarTime = 0;
if(currentBarTime == lastBarTime && prev_calculated > 0)
return(rates_total);
lastBarTime = currentBarTime;
//--- Delete old objects before redrawing
int objTotal = ObjectsTotal(0, 0, -1);
for(int i = objTotal - 1; i >= 0; i--)
{
string name = ObjectName(0, i, 0, -1);
if(StringFind(name, g_objPrefix) == 0)
{ ObjectDelete(0, name); }
}
double P, R1, R2, R3, S1, S2, S3;
string tfTag;
//--- Daily pivots
if(InpShowDaily)
{
if(GetPivotLevels(InpDailyTF, P, R1, R2, R3, S1, S2, S3))
{
tfTag = "D";
DrawPivotLine(g_objPrefix + tfTag + "_S2", S2, InpSupColor, tfTag + " S2 " + DoubleToString(S2, _Digits));
DrawPivotLine(g_objPrefix + tfTag + "_S1", S1, InpSupColor, tfTag + " S1 " + DoubleToString(S1, _Digits));
DrawPivotLine(g_objPrefix + tfTag + "_P", P, InpPivotColor, tfTag + " P " + DoubleToString(P, _Digits));
DrawPivotLine(g_objPrefix + tfTag + "_R1", R1, InpResColor, tfTag + " R1 " + DoubleToString(R1, _Digits));
DrawPivotLine(g_objPrefix + tfTag + "_R2", R2, InpResColor, tfTag + " R2 " + DoubleToString(R2, _Digits));
if(InpShowR3S3)
{
DrawPivotLine(g_objPrefix + tfTag + "_S3", S3, InpSupColor, tfTag + " S3 " + DoubleToString(S3, _Digits));
DrawPivotLine(g_objPrefix + tfTag + "_R3", R3, InpResColor, tfTag + " R3 " + DoubleToString(R3, _Digits));
}
}
}
//--- Weekly pivots (separate temporary handles)
if(InpShowWeekly)
{
if(GetPivotLevels(InpWeeklyTF, P, R1, R2, R3, S1, S2, S3))
{
tfTag = "W";
DrawPivotLine(g_objPrefix + tfTag + "_S2", S2, InpSupColor, tfTag + " S2 " + DoubleToString(S2, _Digits));
DrawPivotLine(g_objPrefix + tfTag + "_S1", S1, InpSupColor, tfTag + " S1 " + DoubleToString(S1, _Digits));
DrawPivotLine(g_objPrefix + tfTag + "_P", P, InpPivotColor, tfTag + " P " + DoubleToString(P, _Digits));
DrawPivotLine(g_objPrefix + tfTag + "_R1", R1, InpResColor, tfTag + " R1 " + DoubleToString(R1, _Digits));
DrawPivotLine(g_objPrefix + tfTag + "_R2", R2, InpResColor, tfTag + " R2 " + DoubleToString(R2, _Digits));
if(InpShowR3S3)
{
DrawPivotLine(g_objPrefix + tfTag + "_S3", S3, InpSupColor, tfTag + " S3 " + DoubleToString(S3, _Digits));
DrawPivotLine(g_objPrefix + tfTag + "_R3", R3, InpResColor, tfTag + " R3 " + DoubleToString(R3, _Digits));
}
}
}
//--- Monthly pivots
if(InpShowMonthly)
{
if(GetPivotLevels(PERIOD_MN1, P, R1, R2, R3, S1, S2, S3))
{
tfTag = "M";
DrawPivotLine(g_objPrefix + tfTag + "_S2", S2, InpSupColor, tfTag + " S2 " + DoubleToString(S2, _Digits));
DrawPivotLine(g_objPrefix + tfTag + "_S1", S1, InpSupColor, tfTag + " S1 " + DoubleToString(S1, _Digits));
DrawPivotLine(g_objPrefix + tfTag + "_P", P, InpPivotColor, tfTag + " P " + DoubleToString(P, _Digits));
DrawPivotLine(g_objPrefix + tfTag + "_R1", R1, InpResColor, tfTag + " R1 " + DoubleToString(R1, _Digits));
DrawPivotLine(g_objPrefix + tfTag + "_R2", R2, InpResColor, tfTag + " R2 " + DoubleToString(R2, _Digits));
if(InpShowR3S3)
{
DrawPivotLine(g_objPrefix + tfTag + "_S3", S3, InpSupColor, tfTag + " S3 " + DoubleToString(S3, _Digits));
DrawPivotLine(g_objPrefix + tfTag + "_R3", R3, InpResColor, tfTag + " R3 " + DoubleToString(R3, _Digits));
}
}
}
ChartRedraw(0);
return(rates_total);
}
//+------------------------------------------------------------------+Copy this code into MetaEditor (F4 in MT5), save in the Experts folder, and compile with F7.
Generate a Custom Multi-pair Support-resistance EA →
Pineify AI generates syntactically validated MQL5 Expert Advisors from plain English descriptions. Customize entry logic, risk management, and trading sessions — no coding required.