Key Differences & Mechanics in MT5
Custom VWAP Calculation: Because MT5 lacks a native iVWAP function, the CalculateVWAP() method is built directly into the EA. Every time OnTick() runs, it identifies the very first bar of the current day (PERIOD_D1, 0) and iterates forward to accumulate the Typical Price (HLC/3) and Tick Volume.
Timezone Shift: Pine Script allowed forcing a specific timezone (America/New_York). MQL5 relies entirely on Broker Server Time. You must look at your MT5 Market Watch clock and manually adjust the StartTime ("10:00") and EndTime ("16:00") inputs to align with your broker's local time offset for the New York Open.
Volume Type: Pine Script's volume typically refers to aggregate share volume in equities. In MT5, particularly if you are trading CFDs or Forex, real volume often returns 0. The EA defaults to iTickVolume(), which accurately reflects tick activity/liquidity for MT5 assets.
Stop Loss Placement: Unlike the Pine version which holds a virtual stop loss line, this EA sends a hard Stop Loss directly to your broker using the ATR calculation when the trade opens (trade.Buy and trade.Sell). The Take Profit remains dynamic and is executed as a market order when the Bid/Ask crosses the VWAP line.
VWAP Reversion Scalping
Re: VWAP Reversion Scalping
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: VWAP Reversion Scalping
Here is the strategy translated into a cBot (C#) for cTrader.
Like the MQL5 version, cTrader does not have a native Daily Anchored VWAP indicator with bands built into its core library, so this cBot calculates the VWAP and standard deviation dynamically by iterating over the current day's bars.
Like the MQL5 version, cTrader does not have a native Daily Anchored VWAP indicator with bands built into its core library, so this cBot calculates the VWAP and standard deviation dynamically by iterating over the current day's bars.
Code: Select all
using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class VWAP_RSI_Mean_Reversion : Robot
{
// =========================================================================
// INPUTS
// =========================================================================
[Parameter("Lot Size", DefaultValue = 0.1, Group = "Position Settings")]
public double LotSize { get; set; }
[Parameter("Label", DefaultValue = "VWAP_MeanRev", Group = "Position Settings")]
public string BotLabel { get; set; }
[Parameter("VWAP Band Multiplier", DefaultValue = 2.0, Group = "VWAP Settings")]
public double StDevMulti { get; set; }
[Parameter("RSI Length", DefaultValue = 14, Group = "RSI Settings")]
public int RsiLen { get; set; }
[Parameter("Overbought Level", DefaultValue = 70.0, Group = "RSI Settings")]
public double RsiOB { get; set; }
[Parameter("Oversold Level", DefaultValue = 30.0, Group = "RSI Settings")]
public double RsiOS { get; set; }
[Parameter("Use ADX Trend Filter", DefaultValue = true, Group = "Trend Filter (ADX)")]
public bool UseAdxFilter { get; set; }
[Parameter("ADX Length", DefaultValue = 14, Group = "Trend Filter (ADX)")]
public int AdxLen { get; set; }
[Parameter("ADX Max Threshold", DefaultValue = 25.0, Group = "Trend Filter (ADX)")]
public double AdxThreshold { get; set; }
[Parameter("Use Session Filter", DefaultValue = true, Group = "Session Filter")]
public bool UseTimeFilter { get; set; }
[Parameter("Start Time (HH:MM:SS)", DefaultValue = "10:00:00", Group = "Session Filter")]
public string StartTimeString { get; set; }
[Parameter("End Time (HH:MM:SS)", DefaultValue = "16:00:00", Group = "Session Filter")]
public string EndTimeString { get; set; }
[Parameter("Close Trades at End of Session", DefaultValue = true, Group = "Session Filter")]
public bool CloseAtEOD { get; set; }
[Parameter("Use ATR Stop Loss", DefaultValue = true, Group = "Risk Management")]
public bool UseStopLoss { get; set; }
[Parameter("ATR Length", DefaultValue = 14, Group = "Risk Management")]
public int AtrLen { get; set; }
[Parameter("ATR Multiplier", DefaultValue = 3.0, Group = "Risk Management")]
public double AtrMulti { get; set; }
// =========================================================================
// GLOBAL VARIABLES
// =========================================================================
private RelativeStrengthIndex _rsi;
private DirectionalMovementSystem _adx;
private AverageTrueRange _atr;
private TimeSpan _startTime;
private TimeSpan _endTime;
private double _vwapValue;
private double _upperBand;
private double _lowerBand;
protected override void OnStart()
{
// Initialize Indicators
_rsi = Indicators.RelativeStrengthIndex(Bars.ClosePrices, RsiLen);
_adx = Indicators.DirectionalMovementSystem(AdxLen);
_atr = Indicators.AverageTrueRange(AtrLen, MovingAverageType.Simple);
// Parse session times
if (!TimeSpan.TryParse(StartTimeString, out _startTime))
Print("Error parsing Start Time. Please use HH:MM:SS format.");
if (!TimeSpan.TryParse(EndTimeString, out _endTime))
Print("Error parsing End Time. Please use HH:MM:SS format.");
}
protected override void OnTick()
{
// 1. Calculate Daily VWAP
if (!CalculateVWAP()) return;
// 2. Fetch Current Indicator Values (Index: Last(0) = current unclosed bar)
double rsiValue = _rsi.Result.Last(0);
double adxValue = _adx.DIX.Last(0); // DIX is the ADX line in cTrader
double atrValue = _atr.Result.Last(0);
double closePrice = Bars.ClosePrices.Last(0);
bool isTrending = UseAdxFilter && (adxValue > AdxThreshold);
bool inSession = IsInSession();
var activePositions = Positions.FindAll(BotLabel, SymbolName);
bool hasLong = activePositions.Any(p => p.TradeType == TradeType.Buy);
bool hasShort = activePositions.Any(p => p.TradeType == TradeType.Sell);
// 3. Manage Open Positions (Exits)
foreach (var pos in activePositions)
{
// EOD Close Trigger
if (UseTimeFilter && CloseAtEOD && !inSession)
{
ClosePosition(pos);
continue;
}
// Dynamic VWAP Target Exit
if (pos.TradeType == TradeType.Buy && Symbol.Bid >= _vwapValue)
{
ClosePosition(pos);
}
else if (pos.TradeType == TradeType.Sell && Symbol.Ask <= _vwapValue)
{
ClosePosition(pos);
}
}
// 4. Entry Logic
if (inSession && !isTrending)
{
double volumeInUnits = Symbol.QuantityToVolumeInUnits(LotSize);
// Long Condition
if (!hasLong && closePrice < _lowerBand && rsiValue < RsiOS)
{
// Convert ATR absolute value to Pips for cTrader execution
double slPips = UseStopLoss ? (atrValue * AtrMulti) / Symbol.PipSize : 0;
ExecuteMarketOrder(TradeType.Buy, SymbolName, volumeInUnits, BotLabel, UseStopLoss ? slPips : (double?)null, null);
}
// Short Condition
if (!hasShort && closePrice > _upperBand && rsiValue > RsiOB)
{
double slPips = UseStopLoss ? (atrValue * AtrMulti) / Symbol.PipSize : 0;
ExecuteMarketOrder(TradeType.Sell, SymbolName, volumeInUnits, BotLabel, UseStopLoss ? slPips : (double?)null, null);
}
}
}
// =========================================================================
// HELPER FUNCTIONS
// =========================================================================
private bool CalculateVWAP()
{
if (Bars.Count == 0) return false;
DateTime currentDay = Server.Time.Date;
int startIndex = Bars.Count - 1;
// Step backward to find the first bar of the current day
while (startIndex >= 0 && Bars.OpenTimes[startIndex].Date == currentDay)
{
startIndex--;
}
startIndex++; // Step forward one to land on the first bar of today
double sumPv = 0;
double sumV = 0;
double sumP2v = 0;
for (int i = startIndex; i < Bars.Count; i++)
{
double h = Bars.HighPrices[i];
double l = Bars.LowPrices[i];
double c = Bars.ClosePrices[i];
double typPrice = (h + l + c) / 3.0;
double vol = Bars.TickVolumes[i];
sumPv += typPrice * vol;
sumV += vol;
sumP2v += (typPrice * typPrice) * vol;
}
if (sumV == 0) return false;
_vwapValue = sumPv / sumV;
double variance = (sumP2v / sumV) - (_vwapValue * _vwapValue);
double stDev = variance > 0 ? Math.Sqrt(variance) : 0.0;
_upperBand = _vwapValue + (StDevMulti * stDev);
_lowerBand = _vwapValue - (StDevMulti * stDev);
return true;
}
private bool IsInSession()
{
if (!UseTimeFilter) return true;
TimeSpan now = Server.Time.TimeOfDay;
if (_startTime < _endTime)
return now >= _startTime && now < _endTime;
else
return now >= _startTime || now < _endTime; // Handles midnight crossover
}
}
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: VWAP Reversion Scalping
Key cTrader/C# Specific Mechanics
Pips vs. Price for Stop Loss: Unlike Pine or MT5 (where you calculate the exact price level for a stop loss), cTrader's ExecuteMarketOrder method demands the Stop Loss be defined in Pips. The cBot dynamically converts the ATR price range into a Pip value ((atrValue * AtrMulti) / Symbol.PipSize).
Volume In Units: Forex and CFD orders in cTrader are executed in underlying units (e.g., 100,000 for 1 standard lot of EURUSD), not literal "Lot" decimals. The cBot utilizes the Symbol.QuantityToVolumeInUnits(LotSize) method to map your input lot size safely to the exchange format.
ADX Naming Convention: cTrader calls the ADX indicator the DirectionalMovementSystem. Furthermore, what traders traditionally call the "ADX Line" is named DIX in cTrader's API (_adx.DIX.Last(0)).
Server Time: Pine Script lets you force an exchange timezone (like New York). cTrader runs entirely on Server.Time. Therefore, when you enter 10:00:00 into the Start Time parameter, you must ensure that aligns with your specific broker's server clock for your desired trading window.
Pips vs. Price for Stop Loss: Unlike Pine or MT5 (where you calculate the exact price level for a stop loss), cTrader's ExecuteMarketOrder method demands the Stop Loss be defined in Pips. The cBot dynamically converts the ATR price range into a Pip value ((atrValue * AtrMulti) / Symbol.PipSize).
Volume In Units: Forex and CFD orders in cTrader are executed in underlying units (e.g., 100,000 for 1 standard lot of EURUSD), not literal "Lot" decimals. The cBot utilizes the Symbol.QuantityToVolumeInUnits(LotSize) method to map your input lot size safely to the exchange format.
ADX Naming Convention: cTrader calls the ADX indicator the DirectionalMovementSystem. Furthermore, what traders traditionally call the "ADX Line" is named DIX in cTrader's API (_adx.DIX.Last(0)).
Server Time: Pine Script lets you force an exchange timezone (like New York). cTrader runs entirely on Server.Time. Therefore, when you enter 10:00:00 into the Start Time parameter, you must ensure that aligns with your specific broker's server clock for your desired trading window.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.