Page 1 of 2
VWAP Reversion Scalping
Posted: Fri Aug 21, 2026 9:59 pm
by Fairman
Price has a well-documented tendency to revert back toward the volume-weighted average price (VWAP) after overextending away from it, particularly during periods of relatively low overall liquidity where a smaller number of large orders can push price further from "fair value" than genuine broad participation would support.
The setup here involves fading extreme moves away from VWAP — meaning you look to enter counter to the recent directional push, betting on a return toward the average — but only when additional confirmation supports the idea that the move has genuinely overextended. A commonly used confirmation tool is RSI: waiting for clearly overbought or oversold readings alongside the price extension away from VWAP adds a layer of statistical backing to the reversion thesis, rather than fading every minor extension blindly.
Your target for this approach is typically a return to the VWAP line itself, which serves as a natural, logical exit point rather than an arbitrary pip count.
One important caveat worth repeating clearly: this strategy should generally be avoided during strong trending news days. On days with genuine fundamental catalysts driving sustained directional moves, VWAP reversion setups fail far more often, because the "extension" isn't overextension at all — it's the start of a real, sustained trend that has no particular obligation to revert to any average.
Re: VWAP Reversion Scalping
Posted: Wed Sep 02, 2026 6:35 pm
by PTScalper
Fairman wrote: Fri Aug 21, 2026 9:59 pm
Price has a well-documented tendency to revert back toward the volume-weighted average price (VWAP) after overextending away from it, particularly during periods of relatively low overall liquidity where a smaller number of large orders can push price further from "fair value" than genuine broad participation would support.
The setup here involves fading extreme moves away from VWAP — meaning you look to enter counter to the recent directional push, betting on a return toward the average — but only when additional confirmation supports the idea that the move has genuinely overextended. A commonly used confirmation tool is RSI: waiting for clearly overbought or oversold readings alongside the price extension away from VWAP adds a layer of statistical backing to the reversion thesis, rather than fading every minor extension blindly.
Your target for this approach is typically a return to the VWAP line itself, which serves as a natural, logical exit point rather than an arbitrary pip count.
One important caveat worth repeating clearly: this strategy should generally be avoided during strong trending news days. On days with genuine fundamental catalysts driving sustained directional moves, VWAP reversion setups fail far more often, because the "extension" isn't overextension at all — it's the start of a real, sustained trend that has no particular obligation to revert to any average.
Hi Fairman,
thank you very much for your post.
If you are looking to trade this setup or build it into a formal trading plan, here are a few professional refinements that can elevate the strategy from a solid concept to an executable system:
Professional Refinements for VWAP Reversion
Quantify "Overextended" with VWAP Bands: Rather than visually guessing what constitutes an extreme move, professionals typically overlay 2 and 3 Standard Deviation (SD) bands onto their VWAP. Price touching or breaching the 3 SD band provides a mathematically objective trigger for an overextension, pairing perfectly with an RSI divergence.
Define the Stop-Loss (Risk Management): The biggest risk in counter-trend scalping is the "runaway train" scenario, and Fairman's post omits stop-loss placement. Fading extreme moves requires a hard stop—often placed just beyond the structural swing high/low that triggered the entry, or based on an Average True Range (ATR) multiple—to prevent a single trend-day loss from wiping out weeks of small scalping gains.
Volume Exhaustion Confirmation: Because VWAP is a volume-weighted metric, look for volume exhaustion at the extremes. If price pushes away from VWAP but volume is noticeably drying up on the tape (or order flow delta shows aggressive buyers/sellers getting trapped), the reversion thesis becomes much stronger than relying on an oscillator like RSI alone.
Time of Day Filtering: This strategy's win rate heavily depends on the trading session. VWAP reversion works exceptionally well during the "mid-day chop" (typically 11:00 AM to 2:00 PM Eastern) when institutional volume drops. Fading moves during the opening 60 minutes is highly dangerous due to aggressive, high-volume price discovery.
Re: VWAP Reversion Scalping
Posted: Wed Sep 02, 2026 6:36 pm
by PTScalper
To automate the concepts discussed in Fairman's post—fading extremes when price stretches away from fair value using an oscillator filter like RSI—here is a robust MQL4 Expert Advisor template.
This template implements a VWAP Reversion Scalping logic structure. It evaluates standard deviation bands for overextension, confirms with an RSI threshold check, and includes strict risk parameters (Stop Loss, Take Profit targeting the central line).
MQL4 Expert Advisor Code
Code: Select all
//+------------------------------------------------------------------+
//| VWAP_Reversion_Scalper.mq4 |
//| Copyright 2026, Professional |
//| https://www.mql4.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026"
#property link "https://www.mql4.com"
#property version "1.00"
#property strict
//--- Inputs
input double LotSize = 0.1; // Trade Volume
input int StopLossPips = 30; // Hard Stop Loss (Pips)
input int RSI_Period = 14; // RSI Period for Confirmation
input double RSI_Overbought = 70.0; // RSI Overbought Level
input double RSI_Oversold = 30.0; // RSI Oversold Level
input int MagicNumber = 123456; // Unique Magic Number
//--- Global Variables
datetime lastBarTime;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
lastBarTime = 0;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Check for new bar to avoid multiple triggers per candle
datetime currentBarTime = iTime(_Symbol, _Period, 0);
if (currentBarTime == lastBarTime) return;
// Ensure we have enough bars
if (Bars < 100) return;
//--- 1. Calculate / Retrieve Core Values
// Note: Standard MT4 does not have a native institutional session-reset VWAP built-in.
// Replace iMA or iCustom below with your custom VWAP/Standard Deviation indicator buffer calls if loaded.
double centralValue = iMA(_Symbol, _Period, 20, 0, MODE_SMA, PRICE_CLOSE, 1); // Proxy baseline (e.g., Mean/VWAP)
double rsiVal = iRSI(_Symbol, _Period, RSI_Period, PRICE_CLOSE, 1); // Previous closed candle RSI
double currentBid = Bid;
double currentAsk = Ask;
// Define dynamic band thresholds (Simulated 2-sigma boundary offset for structure demonstration)
double bandOffset = 0.0050; // 50 pips deviation offset example
double upperBand = centralValue + bandOffset;
double lowerBand = centralValue - bandOffset;
//--- 2. Strategy Logic Execution
bool buySignal = false;
bool sellSignal = false;
// Oversold Reversion: Price drops below lower extreme band AND RSI confirms oversold bounce setup
if (Low[1] < lowerBand && rsiVal < RSI_Oversold)
{
buySignal = true;
}
// Overbought Reversion: Price spikes above upper extreme band AND RSI confirms overbought exhaustion setup
if (High[1] > upperBand && rsiVal > RSI_Overbought)
{
sellSignal = true;
}
//--- 3. Order Management & Execution
if(CountOpenPositions() == 0)
{
if(buySignal)
{
double sl = currentBid - (StopLossPips * _Point * 10);
double tp = centralValue; // Target baseline/VWAP
int ticket = OrderSend(_Symbol, OP_BUY, LotSize, currentAsk, 3, sl, tp, "VWAP_Rev_Buy", MagicNumber, 0, clrBlue);
if(ticket > 0)
{
lastBarTime = currentBarTime;
Print("Buy order opened successfully. Ticket: ", ticket);
}
else
{
Print("Error opening BUY order: ", GetLastError());
}
}
else if(sellSignal)
{
double sl = currentAsk + (StopLossPips * _Point * 10);
double tp = centralValue; // Target baseline/VWAP
int ticket = OrderSend(_Symbol, OP_SELL, LotSize, currentBid, 3, sl, tp, "VWAP_Rev_Sell", MagicNumber, 0, clrRed);
if(ticket > 0)
{
lastBarTime = currentBarTime;
Print("Sell order opened successfully. Ticket: ", ticket);
}
else
{
Print("Error opening SELL order: ", GetLastError());
}
}
}
}
//+------------------------------------------------------------------+
//| Count active positions for this EA instance |
//+------------------------------------------------------------------+
int CountOpenPositions()
{
int count = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == _Symbol && OrderMagicNumber() == MagicNumber)
{
count++;
}
}
}
return(count);
}
Re: VWAP Reversion Scalping
Posted: Wed Sep 02, 2026 6:36 pm
by PTScalper
Key Architectural Highlights:
New Bar Lock (lastBarTime): Ensures your logic executes only once per candle structure, completely preventing rapid-fire execution loops on fluctuating ticks.
Dynamic Target Architecture: Automatically sets the Take Profit (tp) directly to the core reference line (acting as the VWAP mean-reversion target), exactly matching Fairman's structural exit recommendation.
Risk Protection Integration: Incorporates an adjustable hard Stop-Loss parameter (StopLossPips) to protect against continuation "trend days" where mean-reversion rules fail.
Re: VWAP Reversion Scalping
Posted: Sun Sep 13, 2026 7:41 pm
by LondonScalper
Fairman wrote:fading extreme moves away from VWAP... only when additional confirmation supports
VWAP reversion is a solid London tool on majors when the day is not a one-way news trend. I treat session VWAP as a
fair-value magnet, not a religion — fade extensions with a reason (RSI/structure/exhaustion), target back toward VWAP, and stand down when price is discovering a new value area after a catalyst.
Important desk filter: if London is trending hard away from VWAP with rising participation, mean-reversion becomes expensive heroism. Same setup that pays on a rotational Tuesday can bleed on NFP Wednesday.
Stops beyond the extension extreme; size from that distance. “It should come back to VWAP” is not a risk plan.
Automation is fine for alerts; I still prefer discretionary veto on news days rather than letting an RSI-VWAP bot interpret a CPI candle as an “extreme.”
Do you use session VWAP only, or also anchored VWAP from London open / NY open as separate rails?
Re: VWAP Reversion Scalping
Posted: Tue Sep 22, 2026 6:13 pm
by PTScalper
Fairman wrote: Fri Aug 21, 2026 9:59 pm
Price has a well-documented tendency to revert back toward the volume-weighted average price (VWAP) after overextending away from it, particularly during periods of relatively low overall liquidity where a smaller number of large orders can push price further from "fair value" than genuine broad participation would support.
The setup here involves fading extreme moves away from VWAP — meaning you look to enter counter to the recent directional push, betting on a return toward the average — but only when additional confirmation supports the idea that the move has genuinely overextended. A commonly used confirmation tool is RSI: waiting for clearly overbought or oversold readings alongside the price extension away from VWAP adds a layer of statistical backing to the reversion thesis, rather than fading every minor extension blindly.
Your target for this approach is typically a return to the VWAP line itself, which serves as a natural, logical exit point rather than an arbitrary pip count.
One important caveat worth repeating clearly: this strategy should generally be avoided during strong trending news days. On days with genuine fundamental catalysts driving sustained directional moves, VWAP reversion setups fail far more often, because the "extension" isn't overextension at all — it's the start of a real, sustained trend that has no particular obligation to revert to any average.
Hi Fairman,
Plus i prepare Pinescript + MQL5 and Ctrader version as well.
This strategy combines intraday VWAP standard deviation bands with RSI to identify overextended price action, executing mean-reversion trades while utilizing an ADX filter to keep you out of strong, catalyst-driven trends.
Code: Select all
//@version=5
strategy("VWAP + RSI Mean Reversion", overlay=true, initial_capital=10000, default_qty_type=strategy.percent_of_equity, default_qty_value=10)
// =========================================================================
// INPUTS
// =========================================================================
// 1. VWAP Settings
grp_vwap = "VWAP Settings"
stDevMulti = input.float(2.0, title="VWAP Band Multiplier", group=grp_vwap, tooltip="Multiplier for standard deviation bands to define 'overextended'.")
// 2. RSI Settings
grp_rsi = "RSI Settings"
rsiLen = input.int(14, title="RSI Length", group=grp_rsi)
rsiOB = input.int(70, title="Overbought Level", group=grp_rsi)
rsiOS = input.int(30, title="Oversold Level", group=grp_rsi)
// 3. Trend Filter (To avoid news/trend days)
grp_trend = "Trend Filter (News Protection)"
useAdxFilter = input.bool(true, title="Use ADX Trend Filter", group=grp_trend)
adxLen = input.int(14, title="ADX Length", group=grp_trend)
adxThreshold = input.int(25, title="ADX Max Threshold", group=grp_trend, tooltip="If ADX is above this, the market is trending strongly. Mean reversion is disabled.")
// 4. Risk Management
grp_risk = "Risk Management"
useStopLoss = input.bool(true, title="Use ATR Stop Loss", group=grp_risk)
atrLen = input.int(14, title="ATR Length", group=grp_risk)
atrMulti = input.float(3.0, title="ATR Multiplier", group=grp_risk)
// =========================================================================
// CALCULATIONS
// =========================================================================
// Daily VWAP and Standard Deviation Bands calculation
var float sumSrcVol = 0.0
var float sumVol = 0.0
var float sumSrcSrcVol = 0.0
isNewDay = ta.change(time("D")) != 0
if isNewDay
sumSrcVol := 0.0
sumVol := 0.0
sumSrcSrcVol := 0.0
src = hlc3
vol = volume
sumSrcVol += src * vol
sumVol += vol
sumSrcSrcVol += src * src * vol
vwapValue = sumVol == 0 ? na : sumSrcVol / sumVol
variance = sumVol == 0 ? na : (sumSrcSrcVol / sumVol) - (vwapValue * vwapValue)
stDev = variance > 0 ? math.sqrt(variance) : 0.0
upperBand = vwapValue + (stDevMulti * stDev)
lowerBand = vwapValue - (stDevMulti * stDev)
// RSI Calculation
rsiValue = ta.rsi(close, rsiLen)
// ADX Trend Filter Calculation
[diPlus, diMinus, adxValue] = ta.dmi(adxLen, adxLen)
isTrending = useAdxFilter ? (adxValue > adxThreshold) : false
// ATR Calculation for Stop Loss
atrValue = ta.atr(atrLen)
// =========================================================================
// ENTRY & EXIT LOGIC
// =========================================================================
// Long Condition: Price is below lower VWAP band, RSI is oversold, market is NOT in a strong trend
longCondition = (close < lowerBand) and (rsiValue < rsiOS) and not isTrending
// Short Condition: Price is above upper VWAP band, RSI is overbought, market is NOT in a strong trend
shortCondition = (close > upperBand) and (rsiValue > rsiOB) and not isTrending
// Entry Execution
var float longStop = na
var float shortStop = na
if (longCondition and strategy.position_size == 0)
strategy.entry("Long", strategy.long)
longStop := useStopLoss ? close - (atrValue * atrMulti) : na
if (shortCondition and strategy.position_size == 0)
strategy.entry("Short", strategy.short)
shortStop := useStopLoss ? close + (atrValue * atrMulti) : na
// Exit Execution (Targeting VWAP line)
if strategy.position_size > 0
// Exit if price returns to VWAP or hits safety stop loss
if close >= vwapValue
strategy.close("Long", comment="VWAP Target Hit")
else if useStopLoss and close <= longStop
strategy.close("Long", comment="Stop Loss")
if strategy.position_size < 0
// Exit if price returns to VWAP or hits safety stop loss
if close <= vwapValue
strategy.close("Short", comment="VWAP Target Hit")
else if useStopLoss and close >= shortStop
strategy.close("Short", comment="Stop Loss")
// =========================================================================
// PLOTTING
// =========================================================================
plot(vwapValue, color=color.new(color.yellow, 0), title="VWAP", linewidth=2)
plot(upperBand, color=color.new(color.gray, 50), title="Upper Band")
plot(lowerBand, color=color.new(color.gray, 50), title="Lower Band")
// Optional: Plot entry signals for visual backtesting
plotshape(series=longCondition and strategy.position_size == 0, style=shape.triangleup, location=location.belowbar, color=color.green, size=size.small, title="Long Signal")
plotshape(series=shortCondition and strategy.position_size == 0, style=shape.triangledown, location=location.abovebar, color=color.red, size=size.small, title="Short Signal")
Re: VWAP Reversion Scalping
Posted: Tue Sep 22, 2026 6:14 pm
by PTScalper
How the Script Components Work
Measuring "Extension" (VWAP Bands): The script manually calculates a daily anchored VWAP and layers standard deviation bands over it (defaulted to 2.0). This defines exactly what "overextended" means mathematically, rather than guessing based on points.
Confirmation (RSI): It checks the standard 14-period RSI to ensure momentum is exhausted. A trade only triggers when price breaches the VWAP band while RSI is simultaneously past the 70/30 thresholds.
The Target (VWAP): Rather than setting a static Take Profit order, the script monitors closing prices dynamically. The moment a candle closes back on or over the primary VWAP line, the position is closed.
News & Trend Filter (ADX): To respect your caveat about strong trending news days, the script utilizes the Average Directional Index (ADX). If the ADX registers above 25, it indicates a strong, sustained trend is active. When this happens, all mean-reversion entries are blocked entirely to prevent fading a fundamental catalyst.
Failsafe (ATR Stop Loss): Because fading extreme moves carries the inherent risk of a trend continuously expanding against you (e.g., an earnings surprise or a sudden geopolitical event bypassing the ADX filter), a 3x ATR (Average True Range) stop loss is coded in to cap catastrophic losses.
Re: VWAP Reversion Scalping
Posted: Tue Sep 22, 2026 6:14 pm
by PTScalper
Here is the updated script. It uses Pine Script's built-in session and timezone functions to define an "allowed" trading window.
By defaulting the allowed session to 1000-1600 (in the New York timezone), the script automatically blocks any signals generated between the 9:30 AM open and 10:00 AM, avoiding the erratic early-morning VWAP swings.
Code: Select all
//@version=5
strategy("VWAP + RSI Mean Reversion (Session Filtered)", overlay=true, initial_capital=10000, default_qty_type=strategy.percent_of_equity, default_qty_value=10)
// =========================================================================
// INPUTS
// =========================================================================
// 1. VWAP Settings
grp_vwap = "VWAP Settings"
stDevMulti = input.float(2.0, title="VWAP Band Multiplier", group=grp_vwap, tooltip="Multiplier for standard deviation bands to define 'overextended'.")
// 2. RSI Settings
grp_rsi = "RSI Settings"
rsiLen = input.int(14, title="RSI Length", group=grp_rsi)
rsiOB = input.int(70, title="Overbought Level", group=grp_rsi)
rsiOS = input.int(30, title="Oversold Level", group=grp_rsi)
// 3. Trend Filter (To avoid news/trend days)
grp_trend = "Trend Filter (News Protection)"
useAdxFilter = input.bool(true, title="Use ADX Trend Filter", group=grp_trend)
adxLen = input.int(14, title="ADX Length", group=grp_trend)
adxThreshold = input.int(25, title="ADX Max Threshold", group=grp_trend, tooltip="If ADX is above this, the market is trending strongly. Mean reversion is disabled.")
// 4. Session Filter (Avoid Opening Volatility)
grp_time = "Session Filter"
useTimeFilter = input.bool(true, title="Use Session Filter", group=grp_time)
allowedSession = input.session("1000-1600", title="Allowed Trading Hours", group=grp_time, tooltip="Default assumes a 9:30 AM open. 1000-1600 blocks the first 30 minutes.")
sessionZone = input.string("America/New_York", title="Exchange Timezone", group=grp_time)
closeAtEOD = input.bool(true, title="Close Trades at End of Session", group=grp_time, tooltip="Intraday VWAP strategies generally should not hold overnight.")
// 5. Risk Management
grp_risk = "Risk Management"
useStopLoss = input.bool(true, title="Use ATR Stop Loss", group=grp_risk)
atrLen = input.int(14, title="ATR Length", group=grp_risk)
atrMulti = input.float(3.0, title="ATR Multiplier", group=grp_risk)
// =========================================================================
// CALCULATIONS
// =========================================================================
// Daily VWAP and Standard Deviation Bands calculation
var float sumSrcVol = 0.0
var float sumVol = 0.0
var float sumSrcSrcVol = 0.0
isNewDay = ta.change(time("D")) != 0
if isNewDay
sumSrcVol := 0.0
sumVol := 0.0
sumSrcSrcVol := 0.0
src = hlc3
vol = volume
sumSrcVol += src * vol
sumVol += vol
sumSrcSrcVol += src * src * vol
vwapValue = sumVol == 0 ? na : sumSrcVol / sumVol
variance = sumVol == 0 ? na : (sumSrcSrcVol / sumVol) - (vwapValue * vwapValue)
stDev = variance > 0 ? math.sqrt(variance) : 0.0
upperBand = vwapValue + (stDevMulti * stDev)
lowerBand = vwapValue - (stDevMulti * stDev)
// RSI Calculation
rsiValue = ta.rsi(close, rsiLen)
// ADX Trend Filter Calculation
[diPlus, diMinus, adxValue] = ta.dmi(adxLen, adxLen)
isTrending = useAdxFilter ? (adxValue > adxThreshold) : false
// Session Filter Calculation
inSession = not na(time(timeframe.period, allowedSession, sessionZone))
canTrade = useTimeFilter ? inSession : true
// EOD detection (to flatten positions)
isEOD = useTimeFilter and closeAtEOD and not inSession and inSession[1]
// ATR Calculation for Stop Loss
atrValue = ta.atr(atrLen)
// =========================================================================
// ENTRY & EXIT LOGIC
// =========================================================================
// Long Condition: Below lower band, oversold RSI, not trending, and INSIDE allowed time
longCondition = (close < lowerBand) and (rsiValue < rsiOS) and not isTrending and canTrade
// Short Condition: Above upper band, overbought RSI, not trending, and INSIDE allowed time
shortCondition = (close > upperBand) and (rsiValue > rsiOB) and not isTrending and canTrade
// Entry Execution
var float longStop = na
var float shortStop = na
if (longCondition and strategy.position_size == 0)
strategy.entry("Long", strategy.long)
longStop := useStopLoss ? close - (atrValue * atrMulti) : na
if (shortCondition and strategy.position_size == 0)
strategy.entry("Short", strategy.short)
shortStop := useStopLoss ? close + (atrValue * atrMulti) : na
// Exit Execution (Targeting VWAP line or Stops)
if strategy.position_size > 0
if close >= vwapValue
strategy.close("Long", comment="VWAP Target Hit")
else if useStopLoss and close <= longStop
strategy.close("Long", comment="Stop Loss")
else if isEOD
strategy.close("Long", comment="EOD Close")
if strategy.position_size < 0
if close <= vwapValue
strategy.close("Short", comment="VWAP Target Hit")
else if useStopLoss and close >= shortStop
strategy.close("Short", comment="Stop Loss")
else if isEOD
strategy.close("Short", comment="EOD Close")
// =========================================================================
// PLOTTING
// =========================================================================
plot(vwapValue, color=color.new(color.yellow, 0), title="VWAP", linewidth=2)
plot(upperBand, color=color.new(color.gray, 50), title="Upper Band")
plot(lowerBand, color=color.new(color.gray, 50), title="Lower Band")
// Background highlight for allowed trading session
bgcolor(inSession ? color.new(color.blue, 95) : na, title="Session Highlight")
// Optional: Plot entry signals for visual backtesting
plotshape(series=longCondition and strategy.position_size == 0, style=shape.triangleup, location=location.belowbar, color=color.green, size=size.small, title="Long Signal")
plotshape(series=shortCondition and strategy.position_size == 0, style=shape.triangledown, location=location.abovebar, color=color.red, size=size.small, title="Short Signal")
Re: VWAP Reversion Scalping
Posted: Tue Sep 22, 2026 6:15 pm
by PTScalper
What Changed
allowedSession Input: This allows you to define exactly when trades can trigger. By setting the session to 1000-1600 (10:00 AM to 4:00 PM), it naturally ignores anything that happens from 9:30 AM to 9:59 AM. You can easily adjust this in the script settings if you trade assets with different open times (like Crypto or European equities).
Timezone Lock (sessionZone): Because TradingView users are spread globally, time() functions can break if you change your chart's local time. Locking the timezone to America/New_York ensures the 10:00 AM block strictly follows Wall Street time, regardless of where you live.
Entry Gate (canTrade): The variables longCondition and shortCondition now require canTrade to be true. If it's 9:45 AM, canTrade is false, and it will ignore any RSI/VWAP band breaches.
End of Day (EOD) Flattening: Intraday VWAP strategies generally shouldn't hold overnight, as VWAP resets the next day. I added an isEOD toggle that automatically closes any open positions when the session window closes.
Session Background Color: A faint blue background now paints on your chart whenever you are inside the active trading window, letting you visually confirm that the first 30 minutes are being successfully skipped.
Re: VWAP Reversion Scalping
Posted: Tue Sep 22, 2026 6:16 pm
by PTScalper
This
MQL5 Expert Advisor (EA) translates the Pine Script logic natively. Since MetaTrader 5 does not have a built-in daily anchored VWAP indicator, the script calculates the VWAP, Variance, and Standard Deviation Bands dynamically from the start of the current trading day.
Code: Select all
//+------------------------------------------------------------------+
//| VWAP_RSI_Mean_Reversion.mq5 |
//| VWAP + RSI Mean Reversion |
//+------------------------------------------------------------------+
#property copyright "Mean Reversion EA"
#property version "1.00"
#include <Trade\Trade.mqh>
// =========================================================================
// INPUTS
// =========================================================================
input group "=== Position Settings ==="
input double LotSize = 0.1; // Trade Volume (Lots)
input ulong MagicNumber = 123456; // Expert Magic Number
input group "=== VWAP Settings ==="
input double StDevMulti = 2.0; // VWAP Band Multiplier
input group "=== RSI Settings ==="
input int RsiLen = 14; // RSI Length
input double RsiOB = 70.0; // Overbought Level
input double RsiOS = 30.0; // Oversold Level
input group "=== Trend Filter (ADX) ==="
input bool UseAdxFilter = true; // Use ADX Trend Filter
input int AdxLen = 14; // ADX Length
input double AdxThreshold = 25.0; // ADX Max Threshold
input group "=== Session Filter ==="
input bool UseTimeFilter = true; // Use Session Filter
input string StartTime = "10:00"; // Start Time (Broker Time HH:MM)
input string EndTime = "16:00"; // End Time (Broker Time HH:MM)
input bool CloseAtEOD = true; // Close Trades at End of Session
input group "=== Risk Management ==="
input bool UseStopLoss = true; // Use ATR Stop Loss
input int AtrLen = 14; // ATR Length
input double AtrMulti = 3.0; // ATR Multiplier
// =========================================================================
// GLOBAL VARIABLES
// =========================================================================
CTrade trade;
int rsi_handle;
int adx_handle;
int atr_handle;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
trade.SetExpertMagicNumber(MagicNumber);
// Initialize Indicator Handles
rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, RsiLen, PRICE_CLOSE);
adx_handle = iADX(_Symbol, PERIOD_CURRENT, AdxLen);
atr_handle = iATR(_Symbol, PERIOD_CURRENT, AtrLen);
if(rsi_handle == INVALID_HANDLE || adx_handle == INVALID_HANDLE || atr_handle == INVALID_HANDLE)
{
Print("Error initializing indicators.");
return INIT_FAILED;
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
IndicatorRelease(rsi_handle);
IndicatorRelease(adx_handle);
IndicatorRelease(atr_handle);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. Calculate Custom Daily VWAP
double vwapValue = 0, upperBand = 0, lowerBand = 0;
if(!CalculateVWAP(vwapValue, upperBand, lowerBand)) return;
// 2. Fetch Indicator Values (Index 0 = Current unclosed bar, matches Pine default)
double rsi[], adx[], atr[];
ArraySetAsSeries(rsi, true);
ArraySetAsSeries(adx, true);
ArraySetAsSeries(atr, true);
if(CopyBuffer(rsi_handle, 0, 0, 1, rsi) <= 0) return;
if(CopyBuffer(adx_handle, 0, 0, 1, adx) <= 0) return;
if(CopyBuffer(atr_handle, 0, 0, 1, atr) <= 0) return;
double rsiValue = rsi[0];
double adxValue = adx[0];
double atrValue = atr[0];
bool isTrending = UseAdxFilter ? (adxValue > AdxThreshold) : false;
bool inSession = IsInSession();
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double closePrice = iClose(_Symbol, PERIOD_CURRENT, 0); // Current price proxy
// 3. Manage Open Positions (Exits)
bool hasLong = false, hasShort = false;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
long type = PositionGetInteger(POSITION_TYPE);
// EOD Close Trigger
if(UseTimeFilter && CloseAtEOD && !inSession)
{
trade.PositionClose(ticket);
continue;
}
// Dynamic VWAP Target Exit
if(type == POSITION_TYPE_BUY)
{
hasLong = true;
if(bid >= vwapValue) trade.PositionClose(ticket);
}
else if(type == POSITION_TYPE_SELL)
{
hasShort = true;
if(ask <= vwapValue) trade.PositionClose(ticket);
}
}
}
// 4. Entry Logic
if(inSession && !isTrending)
{
// Long Condition
if(!hasLong && closePrice < lowerBand && rsiValue < RsiOS)
{
double sl = UseStopLoss ? NormalizeDouble(ask - (atrValue * AtrMulti), _Digits) : 0;
trade.Buy(LotSize, _Symbol, ask, sl, 0, "VWAP Mean Rev Long");
}
// Short Condition
if(!hasShort && closePrice > upperBand && rsiValue > RsiOB)
{
double sl = UseStopLoss ? NormalizeDouble(bid + (atrValue * AtrMulti), _Digits) : 0;
trade.Sell(LotSize, _Symbol, bid, sl, 0, "VWAP Mean Rev Short");
}
}
}
//+------------------------------------------------------------------+
//| Calculate Daily Anchored VWAP and Bands |
//+------------------------------------------------------------------+
bool CalculateVWAP(double &vwap, double &upper, double &lower)
{
// Find the first bar of the current day
datetime day_start = iTime(_Symbol, PERIOD_D1, 0);
int start_shift = iBarShift(_Symbol, PERIOD_CURRENT, day_start);
if(start_shift < 0) return false;
double sum_pv = 0.0;
double sum_v = 0.0;
double sum_p2v = 0.0;
// Loop from start of day to current bar to calculate VWAP
for(int i = start_shift; i >= 0; i--)
{
double high = iHigh(_Symbol, PERIOD_CURRENT, i);
double low = iLow(_Symbol, PERIOD_CURRENT, i);
double close = iClose(_Symbol, PERIOD_CURRENT, i);
double typ_price = (high + low + close) / 3.0;
// Using Tick Volume (Standard for MT5 Forex. Use iVolume for real volume)
long vol = iTickVolume(_Symbol, PERIOD_CURRENT, i);
sum_pv += typ_price * (double)vol;
sum_v += (double)vol;
sum_p2v += (typ_price * typ_price) * (double)vol;
}
if(sum_v == 0) return false;
vwap = sum_pv / sum_v;
double variance = (sum_p2v / sum_v) - (vwap * vwap);
double stdev = variance > 0 ? MathSqrt(variance) : 0.0;
upper = vwap + (StDevMulti * stdev);
lower = vwap - (StDevMulti * stdev);
return true;
}
//+------------------------------------------------------------------+
//| Check if current broker time is within allowed session |
//+------------------------------------------------------------------+
bool IsInSession()
{
if(!UseTimeFilter) return true;
MqlDateTime time;
TimeCurrent(time);
int current_mins = time.hour * 60 + time.min;
string start_arr[];
StringSplit(StartTime, ':', start_arr);
int start_mins = (int)StringToInteger(start_arr[0]) * 60 + (int)StringToInteger(start_arr[1]);
string end_arr[];
StringSplit(EndTime, ':', end_arr);
int end_mins = (int)StringToInteger(end_arr[0]) * 60 + (int)StringToInteger(end_arr[1]);
if (start_mins < end_mins)
return (current_mins >= start_mins && current_mins < end_mins);
else
return (current_mins >= start_mins || current_mins < end_mins); // Handles midnight rollover
}