Advanced SessionVWAP Crossover Stepped by Naya
49 downloads / 31 views / Created: 09.06.2025 Average Rating: 0
Indicator Description
This indicator calculates two VWAP lines (Fast and Slow) that reset at customizable times, then applies trading logic between them.
Key Features:
- Uses both Fast and Slow VWAP with adjustable anchor periods
- Supports Daily, Weekly, Monthly or Session-based anchoring
- Reset intervals can be set from 30 to 1440 minutes in 30-minute steps
- Precise anchor times for session mode (specific hour/minute)
- Offers 8 different logic options for generating signals
- Works as both entry and exit filter in strategies
Parameters:
1. Logic selection (8 options including crossovers and trend combinations)
2. Fast and Slow anchor period types
3. Fast and Slow reset periods (30-1440 mins)
4. Session start times for both VWAPs
Use Case:
Best for intraday strategies using session-based VWAP dynamics or multi-timeframe VWAP analysis.
Key Features:
- Uses both Fast and Slow VWAP with adjustable anchor periods
- Supports Daily, Weekly, Monthly or Session-based anchoring
- Reset intervals can be set from 30 to 1440 minutes in 30-minute steps
- Precise anchor times for session mode (specific hour/minute)
- Offers 8 different logic options for generating signals
- Works as both entry and exit filter in strategies
Parameters:
1. Logic selection (8 options including crossovers and trend combinations)
2. Fast and Slow anchor period types
3. Fast and Slow reset periods (30-1440 mins)
4. Session start times for both VWAPs
Use Case:
Best for intraday strategies using session-based VWAP dynamics or multi-timeframe VWAP analysis.
Comments
using System;
using System.Collections.Generic;
using System.Drawing;
using ForexStrategyBuilder.Infrastructure.Entities;
using ForexStrategyBuilder.Infrastructure.Enums;
using ForexStrategyBuilder.Infrastructure.Interfaces;
namespace ForexStrategyBuilder.Indicators.Custom
{
///
/// Advanced SessionVWAP Crossover Stepped Indicator
/// Computes two anchored VWAPs (Fast and Slow) that reset at specified times/periods,
/// then applies crossover or rising/falling logic between them.
/// Uses list parameters for reset periods and anchor periods.
///
public class AdvancedSessionVWAPCrossoverStepped : Indicator
{
///
/// Constructor: sets basic properties.
///
public AdvancedSessionVWAPCrossoverStepped()
{
IndicatorName = "AdvancedSessionVWAPCrossoverStepped";
PossibleSlots = SlotTypes.OpenFilter | SlotTypes.CloseFilter;
IndicatorAuthor = "NAYA,+237674724684";
IndicatorVersion = "1.0";
IndicatorDescription = "Crossover between two SessionVWAP lines (Fast vs. Slow) with stepped parameters.";
}
///
/// Sets up all ListParam, NumParam, and CheckParam entries.
///
public override void Initialize(SlotTypes slotType)
{
SlotType = slotType;
// 1) Logic selection (same eight options as SessionVWAPCrossover)
IndParam.ListParam[0].Caption = "Logic";
IndParam.ListParam[0].ItemList = new[]
{
"Fast SessionVWAP crosses Slow SessionVWAP upward",
"Fast SessionVWAP crosses Slow SessionVWAP downward",
"Fast SessionVWAP is higher than Slow SessionVWAP",
"Fast SessionVWAP is lower than Slow SessionVWAP",
"Fast SessionVWAP rises, Slow SessionVWAP falls",
"Fast SessionVWAP falls, Slow SessionVWAP rises",
"Fast SessionVWAP rises, Slow SessionVWAP rises",
"Fast SessionVWAP falls, Slow SessionVWAP falls"
};
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 indicator.";
// 2) Fast Anchor Period selection
IndParam.ListParam[1].Caption = "Fast Anchor Period";
IndParam.ListParam[1].ItemList = new[] { "Daily", "Weekly", "Monthly", "Session" };
IndParam.ListParam[1].Index = 3; // Default to Session
IndParam.ListParam[1].Text = "Session";
IndParam.ListParam[1].Enabled = true;
IndParam.ListParam[1].ToolTip = "Select the anchor period for Fast VWAP calculation.";
// 3) Fast Reset Period selection (list for stepped values)
IndParam.ListParam[2].Caption = "Fast Reset Period (minutes)";
var fastResetPeriods = new List();
for (int i = 30; i <= 1440; i += 30)
fastResetPeriods.Add(i.ToString());
IndParam.ListParam[2].ItemList = fastResetPeriods.ToArray();
IndParam.ListParam[2].Index = 0; // Default to 60 minutes
IndParam.ListParam[2].Text = IndParam.ListParam[2].ItemList[0];
IndParam.ListParam[2].Enabled = true;
IndParam.ListParam[2].ToolTip = "Fast VWAP reset period in minutes (60-1440 in steps of 60).";
// 4) Slow Anchor Period selection
IndParam.ListParam[3].Caption = "Slow Anchor Period";
IndParam.ListParam[3].ItemList = new[] { "Daily", "Weekly", "Monthly", "Session" };
IndParam.ListParam[3].Index = 3; // Default to Session
IndParam.ListParam[3].Text = "Session";
IndParam.ListParam[3].Enabled = true;
IndParam.ListParam[3].ToolTip = "Select the anchor period for Slow VWAP calculation.";
// 5) Slow Reset Period selection (list for stepped values)
IndParam.ListParam[4].Caption = "Slow Reset Period (minutes)";
var slowResetPeriods = new List();
for (int i = 30; i <= 1440; i += 30)
slowResetPeriods.Add(i.ToString());
IndParam.ListParam[4].ItemList = slowResetPeriods.ToArray();
IndParam.ListParam[4].Index = 3; // Default to 240 minutes (4 hours)
IndParam.ListParam[4].Text = IndParam.ListParam[4].ItemList[3];
IndParam.ListParam[4].Enabled = true;
IndParam.ListParam[4].ToolTip = "Slow VWAP reset period in minutes (60-1440 in steps of 60).";
// 6) Numeric parameters for FAST SessionVWAP (only for Session mode)
IndParam.NumParam[0].Caption = "Fast Anchor Hour";
IndParam.NumParam[0].Value = 0;
IndParam.NumParam[0].Min = 0;
IndParam.NumParam[0].Max = 23;
IndParam.NumParam[0].Enabled = true;
IndParam.NumParam[0].ToolTip = "Hour (0–23) at which the Fast VWAP anchor begins (Session only).";
IndParam.NumParam[1].Caption = "Fast Anchor Minute";
IndParam.NumParam[1].Value = 0;
IndParam.NumParam[1].Min = 0;
IndParam.NumParam[1].Max = 0;
IndParam.NumParam[1].Enabled = true;
IndParam.NumParam[1].ToolTip = "Minute (0–59) at which the Fast VWAP anchor begins (Session only).";
// 7) Numeric parameters for SLOW SessionVWAP (only for Session mode)
IndParam.NumParam[2].Caption = "Slow Anchor Hour";
IndParam.NumParam[2].Value = 0;
IndParam.NumParam[2].Min = 0;
IndParam.NumParam[2].Max = 23;
IndParam.NumParam[2].Enabled = true;
IndParam.NumParam[2].ToolTip = "Hour (0–23) at which the Slow VWAP anchor begins (Session only).";
IndParam.NumParam[3].Caption = "Slow Anchor Minute";
IndParam.NumParam[3].Value = 0;
IndParam.NumParam[3].Min = 0;
IndParam.NumParam[3].Max = 0;
IndParam.NumParam[3].Enabled = true;
IndParam.NumParam[3].ToolTip = "Minute (0–59) at which the Slow VWAP anchor begins (Session only).";
// 8) Checkbox: Use previous bar's value for signals
IndParam.CheckParam[0].Caption = "Use previous bar value";
IndParam.CheckParam[0].Enabled = true;
IndParam.CheckParam[0].ToolTip = "If checked, signals use the VWAP value from the previous bar.";
return;
}
///
/// Helper: calculates a VWAP array based on anchor period type and reset period.
///
private double[] CalculateAdvancedVWAP(int anchorPeriodIndex, int anchorHour, int anchorMinute, int resetPeriod)
{
int bars = Bars;
double[] vwapBuffer = new double[bars];
double cumPV = 0.0;
double cumV = 0.0;
DateTime currentPeriodTime = DateTime.MinValue;
// We start at bar index = 3
for (int iBar = 3; iBar < bars; iBar++)
{
DateTime barTime = Time[iBar];
DateTime periodStart;
if (anchorPeriodIndex < 3) // Daily, Weekly, Monthly
{
// Use AVWAP logic for Daily/Weekly/Monthly
periodStart = GetAnchorStartTime(barTime, anchorPeriodIndex + 1);
}
else // Session
{
// Use Session VWAP with stepped times
DateTime adjustedTime = barTime.AddMinutes((int)Period);
periodStart = GetLastResetTimeStepped(adjustedTime, anchorHour, anchorMinute, resetPeriod);
}
// If new anchor/reset period → reset cumulatives
if (periodStart != currentPeriodTime)
{
cumPV = 0.0;
cumV = 0.0;
currentPeriodTime = periodStart;
}
// Typical price = (Open + High + Low + Close) / 4
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;
}
return vwapBuffer;
}
///
/// Helper: returns anchor start time for Daily/Weekly/Monthly periods.
///
private DateTime GetAnchorStartTime(DateTime currentTime, int anchorType)
{
// 1 = Daily, 2 = Weekly (Monday), 3 = Monthly
switch (anchorType)
{
case 1: return currentTime.Date;
case 2:
DateTime monday = currentTime.Date;
while (monday.DayOfWeek != DayOfWeek.Monday)
monday = monday.AddDays(-1);
return monday;
case 3: return new DateTime(currentTime.Year, currentTime.Month, 1);
default: return currentTime.Date;
}
}
///
/// Helper: returns the "last reset" DateTime for Session mode with stepped periods.
///
private DateTime GetLastResetTimeStepped(DateTime currentTime, int anchorHour, int anchorMinute, int resetPeriod)
{
// Calculate minutes since midnight for currentTime
int currentMinutes = currentTime.Hour * 60 + currentTime.Minute;
int anchorMinutes = anchorHour * 60 + anchorMinute;
DateTime lastReset;
if (currentMinutes >= anchorMinutes)
{
// Today's anchor has passed
int minutesSinceAnchor = currentMinutes - anchorMinutes;
int periodsSinceAnchor = minutesSinceAnchor / resetPeriod;
int totalOffsetInMinutes = periodsSinceAnchor * resetPeriod;
lastReset = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, anchorHour, anchorMinute, 0)
.AddMinutes(totalOffsetInMinutes);
}
else
{
// Today's anchor hasn't occurred → look back to previous day
int minutesSinceMidnight = currentMinutes;
int minutesSincePrevDayAnchor = (1440 - anchorMinutes) + minutesSinceMidnight;
int periodsSincePrevDayAnchor = minutesSincePrevDayAnchor / resetPeriod;
int totalOffsetFromPrevDayAnchorMin = periodsSincePrevDayAnchor * resetPeriod;
DateTime prevDayAnchor = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, anchorHour, anchorMinute, 0).AddDays(-1);
lastReset = prevDayAnchor.AddMinutes(totalOffsetFromPrevDayAnchorMin);
}
return lastReset;
}
///
/// Main Calculate() routine: computes both Fast and Slow VWAP lines, then applies
/// the selected logic to produce Component[2]/Component[3] signals for OpenFilter/CloseFilter.
///
public override void Calculate(IDataSet dataSet)
{
DataSet = dataSet;
// 1) Read parameters
int fastAnchorPeriodIndex = IndParam.ListParam[1].Index;
int fastResetPeriod = int.Parse(IndParam.ListParam[2].Text);
int slowAnchorPeriodIndex = IndParam.ListParam[3].Index;
int slowResetPeriod = int.Parse(IndParam.ListParam[4].Text);
int fastAnchorHour = (int)IndParam.NumParam[0].Value;
int fastAnchorMinute = (int)IndParam.NumParam[1].Value;
int slowAnchorHour = (int)IndParam.NumParam[2].Value;
int slowAnchorMinute = (int)IndParam.NumParam[3].Value;
// 2) "Use previous bar's value?" checkbox
int usePrev = IndParam.CheckParam[0].Checked ? 1 : 0;
// 3) Compute the Fast and Slow VWAP arrays
double[] fastVWAP = CalculateAdvancedVWAP(fastAnchorPeriodIndex, fastAnchorHour, fastAnchorMinute, fastResetPeriod);
double[] slowVWAP = CalculateAdvancedVWAP(slowAnchorPeriodIndex, slowAnchorHour, slowAnchorMinute, slowResetPeriod);
// 4) Build an "oscillator" = fastVWAP – slowVWAP for crossover logic
int firstBar = 3 + usePrev + 2;
double[] oscillator = new double[Bars];
for (int bar = firstBar; bar < Bars; bar++)
{
oscillator[bar] = fastVWAP[bar] - slowVWAP[bar];
}
// 5) Create 4 components:
// [0] = Fast VWAP line
// [1] = Slow VWAP line
// [2] = "Allow Long" or "Force Close Long" (depending on slot)
// [3] = "Allow Short" or "Force Close Short"
Component = new IndicatorComp[4];
// Fast VWAP line
Component[0] = new IndicatorComp
{
CompName = "Fast VWAP",
DataType = IndComponentType.IndicatorValue,
ChartType = IndChartType.Line,
ChartColor = Color.Gold,
FirstBar = firstBar,
Value = fastVWAP
};
// Slow VWAP line
Component[1] = new IndicatorComp
{
CompName = "Slow VWAP",
DataType = IndComponentType.IndicatorValue,
ChartType = IndChartType.Line,
ChartColor = Color.DarkBlue,
FirstBar = firstBar,
Value = slowVWAP
};
// The two "signal" / "filter" components (no-chart until we assign DataType below)
Component[2] = new IndicatorComp
{
ChartType = IndChartType.NoChart,
FirstBar = firstBar,
Value = new double[Bars]
};
Component[3] = new IndicatorComp
{
ChartType = IndChartType.NoChart,
FirstBar = firstBar,
Value = new double[Bars]
};
// 6) Assign DataType & CompName for the two filter/signal components
if (SlotType == SlotTypes.OpenFilter)
{
Component[2].DataType = IndComponentType.AllowOpenLong;
Component[2].CompName = "Is long entry allowed";
Component[3].DataType = IndComponentType.AllowOpenShort;
Component[3].CompName = "Is short entry allowed";
}
else if (SlotType == SlotTypes.CloseFilter)
{
Component[2].DataType = IndComponentType.ForceCloseLong;
Component[2].CompName = "Close out long position";
Component[3].DataType = IndComponentType.ForceCloseShort;
Component[3].CompName = "Close out short position";
}
// 7) Determine which logic rule to use
IndicatorLogic logicRule = IndicatorLogic.It_does_not_act_as_a_filter;
string logicText = IndParam.ListParam[0].Text;
switch (logicText)
{
case "Fast SessionVWAP crosses Slow SessionVWAP upward":
logicRule = IndicatorLogic.The_indicator_crosses_the_level_line_upward;
break;
case "Fast SessionVWAP crosses Slow SessionVWAP downward":
logicRule = IndicatorLogic.The_indicator_crosses_the_level_line_downward;
break;
case "Fast SessionVWAP is higher than Slow SessionVWAP":
logicRule = IndicatorLogic.The_indicator_is_higher_than_the_level_line;
break;
case "Fast SessionVWAP is lower than Slow SessionVWAP":
logicRule = IndicatorLogic.The_indicator_is_lower_than_the_level_line;
break;
case "Fast SessionVWAP rises, Slow SessionVWAP falls":
for (int bar = firstBar + usePrev; bar < Bars; bar++)
{
Component[2].Value[bar] = (fastVWAP[bar - usePrev] > fastVWAP[bar - usePrev - 1] + Sigma() &&
slowVWAP[bar - usePrev] < slowVWAP[bar - usePrev - 1] - Sigma())
? 1 : 0;
Component[3].Value[bar] = (fastVWAP[bar - usePrev] < fastVWAP[bar - usePrev - 1] - Sigma() &&
slowVWAP[bar - usePrev] > slowVWAP[bar - usePrev - 1] + Sigma())
? 1 : 0;
}
break;
case "Fast SessionVWAP falls, Slow SessionVWAP rises":
for (int bar = firstBar + usePrev; bar < Bars; bar++)
{
Component[2].Value[bar] = (fastVWAP[bar - usePrev] < fastVWAP[bar - usePrev - 1] - Sigma() &&
slowVWAP[bar - usePrev] > slowVWAP[bar - usePrev - 1] + Sigma())
? 1 : 0;
Component[3].Value[bar] = (fastVWAP[bar - usePrev] > fastVWAP[bar - usePrev - 1] + Sigma() &&
slowVWAP[bar - usePrev] < slowVWAP[bar - usePrev - 1] - Sigma())
? 1 : 0;
}
break;
case "Fast SessionVWAP rises, Slow SessionVWAP rises":
for (int bar = firstBar + usePrev; bar < Bars; bar++)
{
Component[2].Value[bar] = (fastVWAP[bar - usePrev] > fastVWAP[bar - usePrev - 1] + Sigma() &&
slowVWAP[bar - usePrev] > slowVWAP[bar - usePrev - 1] + Sigma())
? 1 : 0;
Component[3].Value[bar] = (fastVWAP[bar - usePrev] < fastVWAP[bar - usePrev - 1] - Sigma() &&
slowVWAP[bar - usePrev] < slowVWAP[bar - usePrev - 1] - Sigma())
? 1 : 0;
}
break;
case "Fast SessionVWAP falls, Slow SessionVWAP falls":
for (int bar = firstBar + usePrev; bar < Bars; bar++)
{
Component[2].Value[bar] = (fastVWAP[bar - usePrev] < fastVWAP[bar - usePrev - 1] - Sigma() &&
slowVWAP[bar - usePrev] < slowVWAP[bar - usePrev - 1] - Sigma())
? 1 : 0;
Component[3].Value[bar] = (fastVWAP[bar - usePrev] > fastVWAP[bar - usePrev - 1] + Sigma() &&
slowVWAP[bar - usePrev] > slowVWAP[bar - usePrev - 1] + Sigma())
? 1 : 0;
}
break;
default:
break;
}
// 8) If a simple cross / comparison logic was chosen, let OscillatorLogic handle it
if (logicRule != IndicatorLogic.It_does_not_act_as_a_filter)
{
OscillatorLogic(firstBar, usePrev, oscillator, 0, 0, ref Component[2], ref Component[3], logicRule);
}
return;
}
///
/// Builds the textual description of the chosen logic for FSB's strategy report.
///
public override void SetDescription()
{
string baseString = IndicatorName + (IndParam.CheckParam[0].Checked ? "* (" : " (");
// Build parameter strings for Fast and Slow
string fastPeriodText = IndParam.ListParam[1].Text;
string slowPeriodText = IndParam.ListParam[3].Text;
string fastParamString = fastPeriodText == "Session"
? "Fast: Session {IndParam.NumParam[0].Value:00}:{IndParam.NumParam[1].Value:00}, {IndParam.ListParam[2].Text} min"
: "Fast: {fastPeriodText}, {IndParam.ListParam[2].Text} min";
string slowParamString = slowPeriodText == "Session"
? "Slow: Session {IndParam.NumParam[2].Value:00}:{IndParam.NumParam[3].Value:00}, {IndParam.ListParam[4].Text} min"
: "Slow: {slowPeriodText}, {IndParam.ListParam[4].Text} min";
string paramString = fastParamString + " | " + slowParamString + ")";
EntryFilterLongDescription = baseString + paramString + "; Fast VWAP ";
EntryFilterShortDescription = baseString + paramString + "; Fast VWAP ";
ExitFilterLongDescription = baseString + paramString + "; Fast VWAP ";
ExitFilterShortDescription = baseString + paramString + "; Fast VWAP ";
string logic = IndParam.ListParam[0].Text;
switch (logic)
{
case "Fast SessionVWAP crosses Slow SessionVWAP upward":
EntryFilterLongDescription += "crosses Slow VWAP upward";
EntryFilterShortDescription += "crosses Slow VWAP downward";
ExitFilterLongDescription += "crosses Slow VWAP upward";
ExitFilterShortDescription += "crosses Slow VWAP downward";
break;
case "Fast SessionVWAP crosses Slow SessionVWAP downward":
EntryFilterLongDescription += "crosses Slow VWAP downward";
EntryFilterShortDescription += "crosses Slow VWAP upward";
ExitFilterLongDescription += "crosses Slow VWAP downward";
ExitFilterShortDescription += "crosses Slow VWAP upward";
break;
case "Fast SessionVWAP is higher than Slow SessionVWAP":
EntryFilterLongDescription += "is higher than Slow VWAP";
EntryFilterShortDescription += "is lower than Slow VWAP";
ExitFilterLongDescription += "is higher than Slow VWAP";
ExitFilterShortDescription += "is lower than Slow VWAP";
break;
case "Fast SessionVWAP is lower than Slow SessionVWAP":
EntryFilterLongDescription += "is lower than Slow VWAP";
EntryFilterShortDescription += "is higher than Slow VWAP";
ExitFilterLongDescription += "is lower than Slow VWAP";
ExitFilterShortDescription += "is higher than Slow VWAP";
break;
case "Fast SessionVWAP rises, Slow SessionVWAP falls":
EntryFilterLongDescription += "rises while Slow VWAP falls";
EntryFilterShortDescription += "falls while Slow VWAP rises";
ExitFilterLongDescription += "rises while Slow VWAP falls";
ExitFilterShortDescription += "falls while Slow VWAP rises";
break;
case "Fast SessionVWAP falls, Slow SessionVWAP rises":
EntryFilterLongDescription += "falls while Slow VWAP rises";
EntryFilterShortDescription += "rises while Slow VWAP falls";
ExitFilterLongDescription += "falls while Slow VWAP rises";
ExitFilterShortDescription += "rises while Slow VWAP falls";
break;
case "Fast SessionVWAP rises, Slow SessionVWAP rises":
EntryFilterLongDescription += "and Slow VWAP are both rising";
EntryFilterShortDescription += "and Slow VWAP are both falling";
ExitFilterLongDescription += "and Slow VWAP are both rising";
ExitFilterShortDescription += "and Slow VWAP are both falling";
break;
case "Fast SessionVWAP falls, Slow SessionVWAP falls":
EntryFilterLongDescription += "and Slow VWAP are both falling";
EntryFilterShortDescription += "and Slow VWAP are both rising";
ExitFilterLongDescription += "and Slow VWAP are both falling";
ExitFilterShortDescription += "and Slow VWAP are both rising";
break;
default:
break;
}
}
///
/// Returns a string with our indicator name and all parameters in parentheses.
///
public override string ToString()
{
string prefix = IndicatorName + (IndParam.CheckParam[0].Checked ? "* (" : " (");
string fastPeriodText = IndParam.ListParam[1].Text;
string slowPeriodText = IndParam.ListParam[3].Text;
string fastString = fastPeriodText == "Session"
? "{IndParam.NumParam[0].Value:00}:{IndParam.NumParam[1].Value:00}, {IndParam.ListParam[2].Text}"
: "{fastPeriodText}, {IndParam.ListParam[2].Text}";
string slowString = slowPeriodText == "Session"
? "{IndParam.NumParam[2].Value:00}:{IndParam.NumParam[3].Value:00}, {IndParam.ListParam[4].Text}"
: "{slowPeriodText}, {IndParam.ListParam[4].Text}";
return prefix + "Fast: " + fastString + " | Slow: " + slowString + ")";
}
}
}
//+------------------------------------------------------------------+ //| AdvancedSessionVWAPCrossoverStepped.mqh | //| Copyright (C) 2025 NAYA +237674724684 | //| NAYA +237674724684 | //+------------------------------------------------------------------+ #property copyright "Copyright (C) 2025 NAYA +237674724684" #property link "NAYA +237674724684" #property version "1.0" #property strict #include <Forexsb.com/Indicator.mqh> #include <Forexsb.com/Enumerations.mqh> //+------------------------------------------------------------------+ //| Class AdvancedSessionVWAPCrossoverStepped | //+------------------------------------------------------------------+ class AdvancedSessionVWAPCrossoverStepped : public Indicator { public: AdvancedSessionVWAPCrossoverStepped(SlotTypes slotType); virtual void Calculate(DataSet &dataSet); private: void CalculateAdvancedVWAP(int anchorPeriodIndex, int anchorHour, int anchorMinute, int resetPeriod, double &vwap[]); datetime GetAnchorStartTime(datetime currentTime, int anchorType); datetime GetLastResetTimeStepped(datetime currentTime, int anchorHour, int anchorMinute, int resetPeriod); int TimeDayCount(const MqlDateTime &dt); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ AdvancedSessionVWAPCrossoverStepped::AdvancedSessionVWAPCrossoverStepped(SlotTypes slotType) { SlotType = slotType; IndicatorName = "AdvancedSessionVWAPCrossoverStepped"; WarningMessage = "Crossover between two SessionVWAP lines (Fast vs. Slow) with stepped parameters."; IsAllowLTF = true; ExecTime = ExecutionTime_DuringTheBar; IsSeparateChart = false; IsDiscreteValues = false; IsDefaultGroupAll = false; } //+------------------------------------------------------------------+ //| Calculate indicator values | //+------------------------------------------------------------------+ void AdvancedSessionVWAPCrossoverStepped::Calculate(DataSet &dataSet) { Data = GetPointer(dataSet); // 1) Read parameters int fastAnchorPeriodIndex = (int)ListParam[1].Index; int fastResetPeriod = (int)StringToInteger(ListParam[2].Text); int slowAnchorPeriodIndex = (int)ListParam[3].Index; int slowResetPeriod = (int)StringToInteger(ListParam[4].Text); int fastAnchorHour = (int)NumParam[0].Value; int fastAnchorMinute = (int)NumParam[1].Value; int slowAnchorHour = (int)NumParam[2].Value; int slowAnchorMinute = (int)NumParam[3].Value; // 2) "Use previous bar's value?" checkbox int previous = CheckParam[0].Checked ? 1 : 0; // 3) Compute the Fast and Slow VWAP arrays double fastVWAP[]; CalculateAdvancedVWAP(fastAnchorPeriodIndex, fastAnchorHour, fastAnchorMinute, fastResetPeriod, fastVWAP); double slowVWAP[]; CalculateAdvancedVWAP(slowAnchorPeriodIndex, slowAnchorHour, slowAnchorMinute, slowResetPeriod, slowVWAP); // 4) Build an oscillator = fastVWAP - slowVWAP for crossover logic int firstBar = 3 + previous + 2; double oscillator[]; ArrayResize(oscillator, Data.Bars); ArrayInitialize(oscillator, 0.0); for(int bar = firstBar; bar < Data.Bars; bar++) { oscillator[bar] = fastVWAP[bar] - slowVWAP[bar]; } // 5) Create 4 components: // [0] = Fast VWAP line // [1] = Slow VWAP line // [2] = "Allow Long" or "Force Close Long" (depending on slot) // [3] = "Allow Short" or "Force Close Short" Component[0].CompName = "Fast VWAP"; Component[0].DataType = IndComponentType_IndicatorValue; Component[0].FirstBar = firstBar; ArrayResize(Component[0].Value, Data.Bars); ArrayCopy(Component[0].Value, fastVWAP); Component[1].CompName = "Slow VWAP"; Component[1].DataType = IndComponentType_IndicatorValue; Component[1].FirstBar = firstBar; ArrayResize(Component[1].Value, Data.Bars); ArrayCopy(Component[1].Value, slowVWAP); // The two "signal" / "filter" components ArrayResize(Component[2].Value, Data.Bars); ArrayInitialize(Component[2].Value, 0.0); Component[2].FirstBar = firstBar; ArrayResize(Component[3].Value, Data.Bars); ArrayInitialize(Component[3].Value, 0.0); Component[3].FirstBar = firstBar; // 6) Assign DataType & CompName for the two filter/signal components if(SlotType == SlotTypes_OpenFilter) { Component[2].DataType = IndComponentType_AllowOpenLong; Component[2].CompName = "Is long entry allowed"; Component[3].DataType = IndComponentType_AllowOpenShort; Component[3].CompName = "Is short entry allowed"; } else if(SlotType == SlotTypes_CloseFilter) { Component[2].DataType = IndComponentType_ForceCloseLong; Component[2].CompName = "Close out long position"; Component[3].DataType = IndComponentType_ForceCloseShort; Component[3].CompName = "Close out short position"; } // 7) Determine which logic rule to use int logicIndex = (int)ListParam[0].Index; switch(logicIndex) { // 0: "Fast SessionVWAP crosses Slow SessionVWAP upward" case 0: for(int bar = firstBar + previous; bar < Data.Bars; bar++) { bool longCondition = (oscillator[bar - previous] > 0 && oscillator[bar - previous - 1] <= 0); Component[2].Value[bar] = longCondition ? 1.0 : 0.0; bool shortCondition = (oscillator[bar - previous] < 0 && oscillator[bar - previous - 1] >= 0); Component[3].Value[bar] = shortCondition ? 1.0 : 0.0; } break; // 1: "Fast SessionVWAP crosses Slow SessionVWAP downward" case 1: for(int bar = firstBar + previous; bar < Data.Bars; bar++) { bool longCondition = (oscillator[bar - previous] < 0 && oscillator[bar - previous - 1] >= 0); Component[2].Value[bar] = longCondition ? 1.0 : 0.0; bool shortCondition = (oscillator[bar - previous] > 0 && oscillator[bar - previous - 1] <= 0); Component[3].Value[bar] = shortCondition ? 1.0 : 0.0; } break; // 2: "Fast SessionVWAP is higher than Slow SessionVWAP" case 2: for(int bar = firstBar + previous; bar < Data.Bars; bar++) { Component[2].Value[bar] = (oscillator[bar - previous] > 0) ? 1.0 : 0.0; Component[3].Value[bar] = (oscillator[bar - previous] < 0) ? 1.0 : 0.0; } break; // 3: "Fast SessionVWAP is lower than Slow SessionVWAP" case 3: for(int bar = firstBar + previous; bar < Data.Bars; bar++) { Component[2].Value[bar] = (oscillator[bar - previous] < 0) ? 1.0 : 0.0; Component[3].Value[bar] = (oscillator[bar - previous] > 0) ? 1.0 : 0.0; } break; // 4: "Fast SessionVWAP rises, Slow SessionVWAP falls" case 4: for(int bar = firstBar + previous; bar < Data.Bars; bar++) { bool longCond = (fastVWAP[bar - previous] > fastVWAP[bar - previous - 1] + Sigma() && slowVWAP[bar - previous] < slowVWAP[bar - previous - 1] - Sigma()); bool shortCond = (fastVWAP[bar - previous] < fastVWAP[bar - previous - 1] - Sigma() && slowVWAP[bar - previous] > slowVWAP[bar - previous - 1] + Sigma()); Component[2].Value[bar] = longCond ? 1.0 : 0.0; Component[3].Value[bar] = shortCond ? 1.0 : 0.0; } break; // 5: "Fast SessionVWAP falls, Slow SessionVWAP rises" case 5: for(int bar = firstBar + previous; bar < Data.Bars; bar++) { bool longCond = (fastVWAP[bar - previous] < fastVWAP[bar - previous - 1] - Sigma() && slowVWAP[bar - previous] > slowVWAP[bar - previous - 1] + Sigma()); bool shortCond = (fastVWAP[bar - previous] > fastVWAP[bar - previous - 1] + Sigma() && slowVWAP[bar - previous] < slowVWAP[bar - previous - 1] - Sigma()); Component[2].Value[bar] = longCond ? 1.0 : 0.0; Component[3].Value[bar] = shortCond ? 1.0 : 0.0; } break; // 6: "Fast SessionVWAP rises, Slow SessionVWAP rises" case 6: for(int bar = firstBar + previous; bar < Data.Bars; bar++) { bool longCond = (fastVWAP[bar - previous] > fastVWAP[bar - previous - 1] + Sigma() && slowVWAP[bar - previous] > slowVWAP[bar - previous - 1] + Sigma()); bool shortCond = (fastVWAP[bar - previous] < fastVWAP[bar - previous - 1] - Sigma() && slowVWAP[bar - previous] < slowVWAP[bar - previous - 1] - Sigma()); Component[2].Value[bar] = longCond ? 1.0 : 0.0; Component[3].Value[bar] = shortCond ? 1.0 : 0.0; } break; // 7: "Fast SessionVWAP falls, Slow SessionVWAP falls" case 7: for(int bar = firstBar + previous; bar < Data.Bars; bar++) { bool longCond = (fastVWAP[bar - previous] < fastVWAP[bar - previous - 1] - Sigma() && slowVWAP[bar - previous] < slowVWAP[bar - previous - 1] - Sigma()); bool shortCond = (fastVWAP[bar - previous] > fastVWAP[bar - previous - 1] + Sigma() && slowVWAP[bar - previous] > slowVWAP[bar - previous - 1] + Sigma()); Component[2].Value[bar] = longCond ? 1.0 : 0.0; Component[3].Value[bar] = shortCond ? 1.0 : 0.0; } break; default: break; } } //+------------------------------------------------------------------+ //| Calculate Advanced VWAP | //+------------------------------------------------------------------+ void AdvancedSessionVWAPCrossoverStepped::CalculateAdvancedVWAP(int anchorPeriodIndex, int anchorHour, int anchorMinute, int resetPeriod, double &vwap[]) { int bars = Data.Bars; ArrayResize(vwap, bars); ArrayInitialize(vwap, 0.0); double cumPV = 0.0; double cumV = 0.0; datetime currentPeriodTime = 0; // We start at bar index = 3 for(int iBar = 3; iBar < bars; iBar++) { datetime barTime = Data.Time[iBar]; datetime periodStart; if(anchorPeriodIndex < 3) // Daily, Weekly, Monthly { // Use AVWAP logic for Daily/Weekly/Monthly periodStart = GetAnchorStartTime(barTime, anchorPeriodIndex + 1); } else // Session { // Use Session VWAP with stepped times datetime adjustedTime = barTime + (Period() * 60); periodStart = GetLastResetTimeStepped(adjustedTime, anchorHour, anchorMinute, resetPeriod); } // If new anchor/reset period → reset cumulatives if(periodStart != currentPeriodTime) { cumPV = 0.0; cumV = 0.0; currentPeriodTime = periodStart; } // Typical price = (Open + High + Low + Close) / 4 double typicalPrice = (Data.Open[iBar] + Data.High[iBar] + Data.Low[iBar] + Data.Close[iBar]) / 4.0; double volume = Data.Volume[iBar]; cumPV += typicalPrice * volume; cumV += volume; vwap[iBar] = (cumV > 0.0) ? (cumPV / cumV) : 0.0; } } //+------------------------------------------------------------------+ //| Get Anchor Start Time | //+------------------------------------------------------------------+ datetime AdvancedSessionVWAPCrossoverStepped::GetAnchorStartTime(datetime currentTime, int anchorType) { // 1 = Daily, 2 = Weekly (Monday), 3 = Monthly MqlDateTime s; TimeToStruct(currentTime, s); switch(anchorType) { case 1: // Daily s.hour = s.min = s.sec = 0; break; case 2: // Weekly (Monday) while(s.day_of_week != 1) // Monday==1 { currentTime -= 86400; TimeToStruct(currentTime, s); } s.hour = s.min = s.sec = 0; break; case 3: // Monthly s.day = 1; s.hour = s.min = s.sec = 0; break; default: s.hour = s.min = s.sec = 0; break; } return StructToTime(s); } //+------------------------------------------------------------------+ //| Get Last Reset Time Stepped | //+------------------------------------------------------------------+ datetime AdvancedSessionVWAPCrossoverStepped::GetLastResetTimeStepped(datetime currentTime, int anchorHour, int anchorMinute, int resetPeriod) { MqlDateTime currentMqlTime; TimeToStruct(currentTime, currentMqlTime); // Calculate minutes since midnight for currentTime int currentMinutes = currentMqlTime.hour * 60 + currentMqlTime.min; int anchorMinutes = anchorHour * 60 + anchorMinute; datetime lastReset; if(currentMinutes >= anchorMinutes) { // Today's anchor has passed int minutesSinceAnchor = currentMinutes - anchorMinutes; int periodsSinceAnchor = minutesSinceAnchor / resetPeriod; int totalOffsetInMinutes = periodsSinceAnchor * resetPeriod; MqlDateTime resetTime = currentMqlTime; resetTime.hour = anchorHour; resetTime.min = anchorMinute; resetTime.sec = 0; lastReset = StructToTime(resetTime) + (totalOffsetInMinutes * 60); } else { // Today's anchor hasn't occurred → look back to the previous day int minutesSinceMidnight = currentMinutes; int minutesSincePrevDayAnchor = (1440 - anchorMinutes) + minutesSinceMidnight; int periodsSincePrevDayAnchor = minutesSincePrevDayAnchor / resetPeriod; int totalOffsetFromPrevDayAnchorMin = periodsSincePrevDayAnchor * resetPeriod; MqlDateTime prevDay = currentMqlTime; prevDay.day--; if(prevDay.day == 0) { prevDay.mon--; if(prevDay.mon == 0) { prevDay.mon = 12; prevDay.year--; } prevDay.day = TimeDayCount(prevDay); } MqlDateTime resetTime = prevDay; resetTime.hour = anchorHour; resetTime.min = anchorMinute; resetTime.sec = 0; lastReset = StructToTime(resetTime) + (totalOffsetFromPrevDayAnchorMin * 60); } return lastReset; } //+------------------------------------------------------------------+ //| Helper function to get days in month | //+------------------------------------------------------------------+ int AdvancedSessionVWAPCrossoverStepped::TimeDayCount(const MqlDateTime &dt) { if(dt.mon == 2) { // February if((dt.year % 4 == 0 && dt.year % 100 != 0) || dt.year % 400 == 0) return 29; // Leap year else return 28; } else if(dt.mon == 4 || dt.mon == 6 || dt.mon == 9 || dt.mon == 11) { return 30; } return 31; }
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.;
Copyright © 2006 - 2025, Forex Software Ltd.;