SessionVWAPStepped by Naya

612 downloads / 372 views / Created: 06.06.2025
 Average Rating: 0

Indicator Description

SessionVWAPStepped is a custom trading indicator that calculates Volume-Weighted Average Price (VWAP) with flexible reset periods. Unlike standard VWAP that resets daily, this version can reset every 15 minutes up to 24 hours (in 15-minute steps).

Main Features:
- Calculates VWAP starting from a user-defined anchor time (selectable hour and minute)
- Automatically resets calculations at chosen intervals (15, 30, 60 minutes etc.)
- Includes bar shifting option to adjust indicator position
- Offers previous bar value option for smoother signals
- Works for entries, exits and filters in trading strategies

Parameters:
1. Anchor Time - Sets start time for calculations (hour and minute dropdowns)
2. Reset Period - How often to reset VWAP (15-1440 minutes in 15-min steps)
3. Shift - Moves indicator forward/backward by bars (0-2 bars)
4. Use Previous Bar - Uses prior bar's value for signals

Trading Uses:
- Enter trades at VWAP price
- Exit trades at VWAP price
- Filter trades based on price position relative to VWAP
- Create signals when price crosses VWAP
- Identify trends based on VWAP direction

This indicator plots as a line on price charts, showing where VWAP resets occur. It helps traders identify average prices for specific time segments rather than just full trading days.

Created by NAYA, this version improves usability with dropdown menus while keeping all original VWAP functionality. It works well for traders who need VWAP aligned with their preferred trading sessions rather than default daily resets.

Comments

using System; using System.Drawing; using System.Collections.Generic; using ForexStrategyBuilder.Infrastructure.Entities; using ForexStrategyBuilder.Infrastructure.Enums; using ForexStrategyBuilder.Infrastructure.Interfaces; namespace ForexStrategyBuilder.Indicators.Custom { public class SessionVWAPStepped : Indicator { public SessionVWAPStepped() { IndicatorName = "SessionVWAPStepped"; PossibleSlots = SlotTypes.Open | SlotTypes.OpenFilter | SlotTypes.Close | SlotTypes.CloseFilter; IndicatorAuthor = "NAYA, +237674724684"; IndicatorVersion = "1.2"; IndicatorDescription = "Anchored VWAP that resets at stepped periods (15-1440 min) with broker time sync"; } public override void Initialize(SlotTypes slotType) { SlotType = slotType; // Logic selection IndParam.ListParam[0].Caption = "Logic"; switch (SlotType) { case SlotTypes.Open: IndParam.ListParam[0].ItemList = new[] { "Enter the market at the SessionVWAPStepped" }; break; case SlotTypes.OpenFilter: IndParam.ListParam[0].ItemList = new[] { "The SessionVWAPStepped rises", "The SessionVWAPStepped falls", "The bar opens above the SessionVWAPStepped", "The bar opens below the SessionVWAPStepped", "The bar opens above the SessionVWAPStepped after opening below it", "The bar opens below the SessionVWAPStepped after opening above it", "The position opens above the SessionVWAPStepped", "The position opens below the SessionVWAPStepped" }; break; case SlotTypes.Close: IndParam.ListParam[0].ItemList = new[] { "Exit the market at the SessionVWAPStepped" }; break; case SlotTypes.CloseFilter: IndParam.ListParam[0].ItemList = new[] { "The SessionVWAPStepped rises", "The SessionVWAPStepped falls", "The bar closes below the SessionVWAPStepped", "The bar closes above the SessionVWAPStepped" }; break; default: IndParam.ListParam[0].ItemList = new[] { "Not Defined" }; break; } IndParam.ListParam[0].Index = 0; IndParam.ListParam[0].Text = IndParam.ListParam[0].ItemList[0]; IndParam.ListParam[0].Enabled = true; IndParam.ListParam[0].ToolTip = "Logic of application of the SessionVWAPStepped."; // Anchor Hour (0-23) IndParam.ListParam[1].Caption = "Anchor Hour (0-23)"; var hourList = new List(); for (int h = 0; h <= 23; h++) hourList.Add(h.ToString("00")); IndParam.ListParam[1].ItemList = hourList.ToArray(); IndParam.ListParam[1].Index = 0; IndParam.ListParam[1].Text = IndParam.ListParam[1].ItemList[0]; IndParam.ListParam[1].Enabled = true; IndParam.ListParam[1].ToolTip = "Hour component of the anchor time (0-23)."; // Anchor Minute (0-59, stepped by 15) IndParam.ListParam[2].Caption = "Anchor Minute (0-59)"; var minuteList = new List(); for (int m = 0; m <= 45; m += 15) minuteList.Add(m.ToString("00")); minuteList.Add("59"); // Add 59 for precise anchor times IndParam.ListParam[2].ItemList = minuteList.ToArray(); IndParam.ListParam[2].Index = 0; IndParam.ListParam[2].Text = IndParam.ListParam[2].ItemList[0]; IndParam.ListParam[2].Enabled = true; IndParam.ListParam[2].ToolTip = "Minute component of the anchor time (0-59)."; // Reset Period (15-1440 by 15) IndParam.ListParam[3].Caption = "Reset Period (minutes)"; var resetList = new List(); for (int m = 60; m <= 1440; m += 30) resetList.Add(m.ToString()); IndParam.ListParam[3].ItemList = resetList.ToArray(); int defaultResetIndex = resetList.IndexOf("60"); if (defaultResetIndex < 0) defaultResetIndex = 3; IndParam.ListParam[3].Index = defaultResetIndex; IndParam.ListParam[3].Text = resetList[defaultResetIndex]; IndParam.ListParam[3].Enabled = true; IndParam.ListParam[3].ToolTip = "Select how many minutes before VWAP resets (15-1440 by 15)."; // Shift (0-10 bars) IndParam.ListParam[4].Caption = "Shift (bars)"; var shiftList = new List(); for (int s = 0; s <= 10; s++) shiftList.Add(s.ToString()); IndParam.ListParam[4].ItemList = shiftList.ToArray(); IndParam.ListParam[4].Index = 0; IndParam.ListParam[4].Text = IndParam.ListParam[4].ItemList[0]; IndParam.ListParam[4].Enabled = false; IndParam.ListParam[4].ToolTip = "Number of bars to shift the SessionVWAPStepped."; // Use previous bar value IndParam.CheckParam[0].Caption = "Use previous bar value"; IndParam.CheckParam[0].Enabled = true; IndParam.CheckParam[0].ToolTip = "Use the previous bar's VWAP value for signals."; } public override void Calculate(IDataSet dataSet) { DataSet = dataSet; // Read parameters int anchorHour = int.Parse(IndParam.ListParam[1].Text); int anchorMinute = int.Parse(IndParam.ListParam[2].Text); int resetPeriod = int.Parse(IndParam.ListParam[3].Text); int shift = int.Parse(IndParam.ListParam[4].Text); int usePrev = IndParam.CheckParam[0].Checked ? 1 : 0; int firstBar = 3; int bars = Bars; double[] vwapBuffer = new double[bars]; double cumPV = 0.0; double cumV = 0.0; DateTime currentReset = DateTime.MinValue; // Main calculation loop for (int iBar = firstBar; iBar < bars; iBar++) { DateTime barTime = Time[iBar]; // MT4 CSV timestamp (opening time) DateTime barEndTime = barTime.AddMinutes((int)Period); // Bar end time DateTime nextReset = GetNextResetTime(barEndTime, anchorHour, anchorMinute, resetPeriod); if (nextReset != currentReset) { cumPV = 0.0; cumV = 0.0; currentReset = nextReset; } double typicalPrice = (Open[iBar] + High[iBar] + Low[iBar] + Close[iBar]) / 4.0; double volume = Volume[iBar]; cumPV += typicalPrice * volume; cumV += volume; vwapBuffer[iBar] = (cumV > 0.0) ? (cumPV / cumV) : 0.0; } // Apply shift double[] shiftedVWAP = new double[bars]; for (int bar = 0; bar < bars; bar++) { if (bar >= shift && bar - shift < bars) shiftedVWAP[bar] = vwapBuffer[bar - shift]; else shiftedVWAP[bar] = 0.0; } // Build components if (SlotType == SlotTypes.Open || SlotType == SlotTypes.Close) { Component = new IndicatorComp[2]; Component[1] = new IndicatorComp { Value = new double[bars] }; for (int iBar = firstBar + shift; iBar < bars; iBar++) { double v0 = shiftedVWAP[iBar - usePrev]; double v1 = shiftedVWAP[iBar - usePrev - 1]; double outPrice = v0; if ((v1 > High[iBar - 1] && v0 < Open[iBar]) || (v1 < Low[iBar - 1] && v0 > Open[iBar]) || (Close[iBar - 1] < v0 && v0 < Open[iBar]) || (Close[iBar - 1] > v0 && v0 > Open[iBar])) { outPrice = Open[iBar]; } Component[1].Value[iBar] = outPrice; } } else { Component = new IndicatorComp[3]; Component[1] = new IndicatorComp { ChartType = IndChartType.NoChart, FirstBar = firstBar + shift, Value = new double[bars] }; Component[2] = new IndicatorComp { ChartType = IndChartType.NoChart, FirstBar = firstBar + shift, Value = new double[bars] }; } // Main VWAP line Component[0] = new IndicatorComp { CompName = "SessionVWAPStepped Value", DataType = IndComponentType.IndicatorValue, ChartType = IndChartType.Line, ChartColor = Color.DodgerBlue, FirstBar = firstBar + shift, Value = shiftedVWAP }; // Assign component types switch (SlotType) { case SlotTypes.Open: Component[1].CompName = "Position opening price"; Component[1].DataType = IndComponentType.OpenPrice; break; case SlotTypes.OpenFilter: Component[1].CompName = "Is long entry allowed"; Component[1].DataType = IndComponentType.AllowOpenLong; Component[2].CompName = "Is short entry allowed"; Component[2].DataType = IndComponentType.AllowOpenShort; break; case SlotTypes.Close: Component[1].CompName = "Position closing price"; Component[1].DataType = IndComponentType.ClosePrice; break; case SlotTypes.CloseFilter: Component[1].CompName = "Close out long position"; Component[1].DataType = IndComponentType.ForceCloseLong; Component[2].CompName = "Close out short position"; Component[2].DataType = IndComponentType.ForceCloseShort; break; } // Apply filter logic if (SlotType == SlotTypes.OpenFilter || SlotType == SlotTypes.CloseFilter) { switch (IndParam.ListParam[0].Text) { case "The SessionVWAPStepped rises": IndicatorRisesLogic(firstBar + shift, usePrev, shiftedVWAP, ref Component[1], ref Component[2]); break; case "The SessionVWAPStepped falls": IndicatorFallsLogic(firstBar + shift, usePrev, shiftedVWAP, ref Component[1], ref Component[2]); break; case "The bar opens above the SessionVWAPStepped": BarOpensAboveIndicatorLogic(firstBar + shift, usePrev, shiftedVWAP, ref Component[1], ref Component[2]); break; case "The bar opens below the SessionVWAPStepped": BarOpensBelowIndicatorLogic(firstBar + shift, usePrev, shiftedVWAP, ref Component[1], ref Component[2]); break; case "The bar opens above the SessionVWAPStepped after opening below it": BarOpensAboveIndicatorAfterOpeningBelowLogic(firstBar + shift, usePrev, shiftedVWAP, ref Component[1], ref Component[2]); break; case "The bar opens below the SessionVWAPStepped after opening above it": BarOpensBelowIndicatorAfterOpeningAboveLogic(firstBar + shift, usePrev, shiftedVWAP, ref Component[1], ref Component[2]); break; case "The position opens above the SessionVWAPStepped": Component[0].PosPriceDependence = PositionPriceDependence.BuyHigherSellLower; Component[0].UsePreviousBar = usePrev; Component[1].ShowInDynInfo = false; Component[2].ShowInDynInfo = false; break; case "The position opens below the SessionVWAPStepped": Component[0].PosPriceDependence = PositionPriceDependence.BuyLowerSellHigher; Component[0].UsePreviousBar = usePrev; Component[1].ShowInDynInfo = false; Component[2].ShowInDynInfo = false; break; case "The bar closes below the SessionVWAPStepped": BarClosesBelowIndicatorLogic(firstBar + shift, usePrev, shiftedVWAP, ref Component[1], ref Component[2]); break; case "The bar closes above the SessionVWAPStepped": BarClosesAboveIndicatorLogic(firstBar + shift, usePrev, shiftedVWAP, ref Component[1], ref Component[2]); break; } } } private DateTime GetNextResetTime(DateTime barEndTime, int anchorHour, int anchorMinute, int resetPeriod) { DateTime anchorToday = new DateTime( barEndTime.Year, barEndTime.Month, barEndTime.Day, anchorHour, anchorMinute, 0); if (barEndTime >= anchorToday) { TimeSpan timeSinceAnchor = barEndTime - anchorToday; int periods = (int)(timeSinceAnchor.TotalMinutes / resetPeriod); return anchorToday.AddMinutes((periods + 1) * resetPeriod); } else { DateTime anchorYesterday = anchorToday.AddDays(-1); TimeSpan timeSinceAnchor = barEndTime - anchorYesterday; int periods = (int)(timeSinceAnchor.TotalMinutes / resetPeriod); return anchorYesterday.AddMinutes((periods + 1) * resetPeriod); } } public override void SetDescription() { EntryPointLongDescription = "at the " + ToString(); EntryPointShortDescription = "at the " + ToString(); ExitPointLongDescription = "at the " + ToString(); ExitPointShortDescription = "at the " + ToString(); string logic = IndParam.ListParam[0].Text; switch (logic) { case "The SessionVWAPStepped rises": EntryFilterLongDescription = "the " + ToString() + " rises"; EntryFilterShortDescription = "the " + ToString() + " falls"; ExitFilterLongDescription = "the " + ToString() + " rises"; ExitFilterShortDescription = "the " + ToString() + " falls"; break; case "The SessionVWAPStepped falls": EntryFilterLongDescription = "the " + ToString() + " falls"; EntryFilterShortDescription = "the " + ToString() + " rises"; ExitFilterLongDescription = "the " + ToString() + " falls"; ExitFilterShortDescription = "the " + ToString() + " rises"; break; case "The bar opens above the SessionVWAPStepped": EntryFilterLongDescription = "the bar opens above the " + ToString(); EntryFilterShortDescription = "the bar opens below the " + ToString(); break; case "The bar opens below the SessionVWAPStepped": EntryFilterLongDescription = "the bar opens below the " + ToString(); EntryFilterShortDescription = "the bar opens above the " + ToString(); break; case "The position opens above the SessionVWAPStepped": EntryFilterLongDescription = "the position opening price is higher than the " + ToString(); EntryFilterShortDescription = "the position opening price is lower than the " + ToString(); break; case "The position opens below the SessionVWAPStepped": EntryFilterLongDescription = "the position opening price is lower than the " + ToString(); EntryFilterShortDescription = "the position opening price is higher than the " + ToString(); break; case "The bar closes below the SessionVWAPStepped": ExitFilterLongDescription = "the bar closes below the " + ToString(); ExitFilterShortDescription = "the bar closes above the " + ToString(); break; case "The bar closes above the SessionVWAPStepped": ExitFilterLongDescription = "the bar closes above the " + ToString(); ExitFilterShortDescription = "the bar closes below the " + ToString(); break; } } public override string ToString() { bool usePrev = IndParam.CheckParam[0].Checked; return IndicatorName + (usePrev ? "* (" : " (") + "Anchor: " + IndParam.ListParam[1].Text + ":" + IndParam.ListParam[2].Text + ", " + "Reset: " + IndParam.ListParam[3].Text + " min, " + "Shift: " + IndParam.ListParam[4].Text + ")"; } } }
#property copyright "Copyright (C) 2025 NAYA +237674724684" #property link "https://forexsb.com" #property version "1.2" #property strict #include <Forexsb.com/Indicator.mqh> #include <Forexsb.com/Enumerations.mqh> class SessionVWAPStepped : public Indicator { public: SessionVWAPStepped(SlotTypes slotType) { SlotType = slotType; IndicatorName = "SessionVWAPStepped"; WarningMessage = "Anchored VWAP that resets at stepped periods (15-1440 min) with broker time sync"; IsAllowLTF = true; ExecTime = ExecutionTime_DuringTheBar; IsSeparateChart = false; IsDiscreteValues = false; IsDefaultGroupAll = false; } virtual void Calculate(DataSet &dataSet); private: datetime GetNextResetTime(datetime barTime, int anchorHour, int anchorMinute, int resetPeriod); }; void SessionVWAPStepped::Calculate(DataSet &dataSet) { Data = GetPointer(dataSet); // Read parameters string logic = ListParam[0].Text; int anchorHour = (int)StringToInteger(ListParam[1].Text); int anchorMinute = (int)StringToInteger(ListParam[2].Text); int resetPeriod = (int)StringToInteger(ListParam[3].Text); int shift = (int)StringToInteger(ListParam[4].Text); bool usePrevious = CheckParam[0].Checked; const int firstBar = 3; int bars = Data.Bars; // Allocate buffers double vwapBuffer[]; ArrayResize(vwapBuffer, bars); ArrayInitialize(vwapBuffer, 0.0); double shiftedVWAP[]; ArrayResize(shiftedVWAP, bars); ArrayInitialize(shiftedVWAP, 0.0); double cumPV = 0.0; double cumV = 0.0; datetime currentResetTime = 0; // Main calculation loop for(int bar = firstBar; bar < bars; bar++) { datetime barTime = Data.Time[bar]; // MT4 bar opening time datetime barEndTime = barTime + Period() * 60; // Bar end time (broker timezone) datetime nextReset = GetNextResetTime(barEndTime, anchorHour, anchorMinute, resetPeriod); if(nextReset != currentResetTime) { cumPV = 0.0; cumV = 0.0; currentResetTime = nextReset; } double tp = (Data.Open[bar] + Data.High[bar] + Data.Low[bar] + Data.Close[bar]) / 4.0; long vol = Data.Volume[bar]; cumPV += tp * (double)vol; cumV += (double)vol; vwapBuffer[bar] = (cumV > 0.0) ? (cumPV / cumV) : 0.0; } // Apply shift for(int bar = 0; bar < bars; bar++) { int src = bar - shift; if(src >= 0 && src < bars) shiftedVWAP[bar] = vwapBuffer[src]; else shiftedVWAP[bar] = 0.0; } // Build components if(SlotType == SlotTypes_Open || SlotType == SlotTypes_Close) { ArrayResize(Component[1].Value, bars); ArrayInitialize(Component[1].Value, 0.0); for(int bar = firstBar + shift; bar < bars; bar++) { int prevBar = usePrevious ? 1 : 0; double v0 = shiftedVWAP[bar - prevBar]; double v1 = shiftedVWAP[bar - prevBar - 1]; double outPrice = v0; if((v1 > Data.High[bar - 1] && v0 < Data.Open[bar]) || (v1 < Data.Low[bar - 1] && v0 > Data.Open[bar]) || (Data.Close[bar - 1] < v0 && v0 < Data.Open[bar]) || (Data.Close[bar - 1] > v0 && v0 > Data.Open[bar])) { outPrice = Data.Open[bar]; } Component[1].Value[bar] = outPrice; } } else { ArrayResize(Component[1].Value, bars); ArrayResize(Component[2].Value, bars); Component[1].FirstBar = firstBar + shift; Component[2].FirstBar = firstBar + shift; } // Main VWAP line ArrayResize(Component[0].Value, bars); Component[0].CompName = "SessionVWAPStepped Value"; Component[0].DataType = IndComponentType_IndicatorValue; Component[0].FirstBar = firstBar + shift; ArrayCopy(Component[0].Value, shiftedVWAP); // Assign component types if(SlotType == SlotTypes_Open) { Component[1].CompName = "Position opening price"; Component[1].DataType = IndComponentType_OpenPrice; } else if(SlotType == SlotTypes_OpenFilter) { Component[1].CompName = "Is long entry allowed"; Component[1].DataType = IndComponentType_AllowOpenLong; Component[2].CompName = "Is short entry allowed"; Component[2].DataType = IndComponentType_AllowOpenShort; } else if(SlotType == SlotTypes_Close) { Component[1].CompName = "Position closing price"; Component[1].DataType = IndComponentType_ClosePrice; } else if(SlotType == SlotTypes_CloseFilter) { Component[1].CompName = "Close out long position"; Component[1].DataType = IndComponentType_ForceCloseLong; Component[2].CompName = "Close out short position"; Component[2].DataType = IndComponentType_ForceCloseShort; } // Apply filter logic if(SlotType == SlotTypes_OpenFilter || SlotType == SlotTypes_CloseFilter) { int usePrev = usePrevious ? 1 : 0; if(logic == "The SessionVWAPStepped rises") IndicatorRisesLogic(firstBar + shift, usePrev, shiftedVWAP, Component[1], Component[2]); else if(logic == "The SessionVWAPStepped falls") IndicatorFallsLogic(firstBar + shift, usePrev, shiftedVWAP, Component[1], Component[2]); else if(logic == "The bar opens above the SessionVWAPStepped") BarOpensAboveIndicatorLogic(firstBar + shift, usePrev, shiftedVWAP, Component[1], Component[2]); else if(logic == "The bar opens below the SessionVWAPStepped") BarOpensBelowIndicatorLogic(firstBar + shift, usePrev, shiftedVWAP, Component[1], Component[2]); else if(logic == "The bar opens above the SessionVWAPStepped after opening below it") BarOpensAboveIndicatorAfterOpeningBelowLogic(firstBar + shift, usePrev, shiftedVWAP, Component[1], Component[2]); else if(logic == "The bar opens below the SessionVWAPStepped after opening above it") BarOpensBelowIndicatorAfterOpeningAboveLogic(firstBar + shift, usePrev, shiftedVWAP, Component[1], Component[2]); else if(logic == "The position opens above the SessionVWAPStepped") { Component[0].PosPriceDependence = PositionPriceDependence_BuyHigherSellLower; Component[0].UsePreviousBar = usePrev; Component[1].ShowInDynInfo = false; Component[2].ShowInDynInfo = false; } else if(logic == "The position opens below the SessionVWAPStepped") { Component[0].PosPriceDependence = PositionPriceDependence_BuyLowerSellHigher; Component[0].UsePreviousBar = usePrev; Component[1].ShowInDynInfo = false; Component[2].ShowInDynInfo = false; } else if(logic == "The bar closes below the SessionVWAPStepped") BarClosesBelowIndicatorLogic(firstBar + shift, usePrev, shiftedVWAP, Component[1], Component[2]); else if(logic == "The bar closes above the SessionVWAPStepped") BarClosesAboveIndicatorLogic(firstBar + shift, usePrev, shiftedVWAP, Component[1], Component[2]); } } datetime SessionVWAPStepped::GetNextResetTime(datetime barEndTime, int anchorHour, int anchorMinute, int resetPeriod) { MqlDateTime tm; TimeToStruct(barEndTime, tm); // Create anchor time for today in broker timezone MqlDateTime anchorStruct = tm; anchorStruct.hour = anchorHour; anchorStruct.min = anchorMinute; anchorStruct.sec = 0; datetime anchorToday = StructToTime(anchorStruct); if(barEndTime >= anchorToday) { // Calculate time since today's anchor int minutesSinceAnchor = (int)((barEndTime - anchorToday) / 60); int periods = minutesSinceAnchor / resetPeriod; return anchorToday + (periods + 1) * resetPeriod * 60; } else { // Use yesterday's anchor time datetime anchorYesterday = anchorToday - 86400; int minutesSinceAnchor = (int)((barEndTime - anchorYesterday) / 60); int periods = minutesSinceAnchor / resetPeriod; return anchorYesterday + (periods + 1) * resetPeriod * 60; } }
Risk warning: Forex, spread bets and CFD are leveraged products. They may not be suitable for you as they carry a high degree of risk to your capital and you can lose more than your initial investment. You should ensure you understand all of the risks.
Copyright © 2006 - 2025, Forex Software Ltd.;