SessionVWAPATRBandsStepped by Naya
596 downloads / 326 views / Created: 11.06.2025 Average Rating: 0
Indicator Description
A custom trading indicator that combines Session Volume-Weighted Average Price (VWAP) with ATR-based bands. The indicator resets at configurable stepped time intervals (from 15 to 1440 minutes in 15-minute increments).
Key Features:
- Calculates VWAP starting from a user-defined anchor time (hour and minute)
- Resets calculations at regular intervals (15min to 24hr steps)
- Plots upper/lower bands using ATR (Average True Range) width
- Multiple trading logic options for entries/exits/filters
- Works on all timeframes
Parameters:
1. Logic Selection (varies by slot type)
2. Anchor Hour (0-23)
3. Anchor Minute (0-59 in 15min steps)
4. Reset Period (15-1440 minutes in 15min steps)
5. ATR Period (2-50)
6. ATR Multiplier (0.2-5.0)
Typical Applications:
- Mean reversion strategies
- Breakout strategies
- Trend confirmation
- Volatility-based position sizing
Key Features:
- Calculates VWAP starting from a user-defined anchor time (hour and minute)
- Resets calculations at regular intervals (15min to 24hr steps)
- Plots upper/lower bands using ATR (Average True Range) width
- Multiple trading logic options for entries/exits/filters
- Works on all timeframes
Parameters:
1. Logic Selection (varies by slot type)
2. Anchor Hour (0-23)
3. Anchor Minute (0-59 in 15min steps)
4. Reset Period (15-1440 minutes in 15min steps)
5. ATR Period (2-50)
6. ATR Multiplier (0.2-5.0)
Typical Applications:
- Mean reversion strategies
- Breakout strategies
- Trend confirmation
- Volatility-based position sizing
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 SessionVWAPATRBandsStepped : Indicator
{
public SessionVWAPATRBandsStepped()
{
IndicatorName = "SessionVWAPATRBandsStepped";
PossibleSlots = SlotTypes.Open | SlotTypes.OpenFilter | SlotTypes.Close | SlotTypes.CloseFilter;
IndicatorAuthor = "NAYA,+237674724684";
IndicatorVersion = "1.0";
IndicatorDescription = "SessionVWAP with ATR-based bands that resets at stepped periods (15-1440 min).";
}
public override void Initialize(SlotTypes slotType)
{
SlotType = slotType;
// 1) Logic selection
IndParam.ListParam[0].Caption = "Logic";
switch (SlotType)
{
case SlotTypes.Open:
IndParam.ListParam[0].ItemList = new[]
{
"Enter long at Upper Band",
"Enter long at Lower Band"
};
break;
case SlotTypes.OpenFilter:
IndParam.ListParam[0].ItemList = new[]
{
"The bar opens below Upper Band",
"The bar opens above Upper Band",
"The bar opens below Lower Band",
"The bar opens above Lower Band",
"The position opens above Upper Band",
"The position opens below Upper Band",
"The position opens above Lower Band",
"The position opens below Lower Band",
"The bar opens below Upper Band after opening above it",
"The bar opens above Upper Band after opening below it",
"The bar opens below Lower Band after opening above it",
"The bar opens above Lower Band after opening below it"
};
break;
case SlotTypes.Close:
IndParam.ListParam[0].ItemList = new[]
{
"Exit long at Upper Band",
"Exit long at Lower Band"
};
break;
case SlotTypes.CloseFilter:
IndParam.ListParam[0].ItemList = new[]
{
"The bar closes below Upper Band",
"The bar closes above Upper Band",
"The bar closes below Lower Band",
"The bar closes above Lower Band"
};
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 indicator.";
// 2) 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).";
// 3) 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"));
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).";
// 4) Reset Period (15-1440 minutes, stepped 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());
int defaultResetIndex = resetList.IndexOf("60");
if (defaultResetIndex < 0) defaultResetIndex = 3;
IndParam.ListParam[3].ItemList = resetList.ToArray();
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).";
// 5) ATR Period
IndParam.NumParam[0].Caption = "ATR Period";
IndParam.NumParam[0].Value = 10;
IndParam.NumParam[0].Min = 2;
IndParam.NumParam[0].Max = 50;
IndParam.NumParam[0].Enabled = true;
IndParam.NumParam[0].ToolTip = "Average True Range period for bands calculation.";
// 6) ATR Multiplier
IndParam.NumParam[1].Caption = "ATR Multiplier";
IndParam.NumParam[1].Value = 2.0;
IndParam.NumParam[1].Min = 0.2;
IndParam.NumParam[1].Max = 5;
IndParam.NumParam[1].Point = 1;
IndParam.NumParam[1].Enabled = true;
IndParam.NumParam[1].ToolTip = "ATR multiplier for bands width.";
// 7) Use previous bar value
IndParam.CheckParam[0].Caption = "Use previous bar value";
IndParam.CheckParam[0].Enabled = true;
IndParam.CheckParam[0].ToolTip = "Use the indicator value from the previous bar.";
}
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 atrPeriod = (int)IndParam.NumParam[0].Value;
double atrMultiplier = IndParam.NumParam[1].Value;
bool usePrevious = IndParam.CheckParam[0].Checked;
int previous = usePrevious ? 1 : 0;
int firstBar = Math.Max(atrPeriod, 3) + previous + 2;
int bars = Bars;
// Calculate SessionVWAP with stepped reset periods
double[] vwap = CalculateSessionVWAP(anchorHour, anchorMinute, resetPeriod);
// Calculate ATR
double[] atr = CalculateATR(atrPeriod);
// Calculate bands
double[] upperBand = new double[bars];
double[] lowerBand = new double[bars];
for (int bar = atrPeriod; bar < bars; bar++)
{
upperBand[bar] = vwap[bar] + atr[bar] * atrMultiplier;
lowerBand[bar] = vwap[bar] - atr[bar] * atrMultiplier;
}
// Create components
Component = new IndicatorComp[5];
// Upper Band
Component[0] = new IndicatorComp
{
CompName = "Upper Band",
DataType = IndComponentType.IndicatorValue,
ChartType = IndChartType.Line,
ChartColor = Color.Blue,
FirstBar = firstBar,
Value = upperBand
};
// SessionVWAP
Component[1] = new IndicatorComp
{
CompName = "SessionVWAP",
DataType = IndComponentType.IndicatorValue,
ChartType = IndChartType.Line,
ChartColor = Color.Gold,
FirstBar = firstBar,
Value = vwap
};
// Lower Band
Component[2] = new IndicatorComp
{
CompName = "Lower Band",
DataType = IndComponentType.IndicatorValue,
ChartType = IndChartType.Line,
ChartColor = Color.Blue,
FirstBar = firstBar,
Value = lowerBand
};
// Signal components
Component[3] = new IndicatorComp
{
ChartType = IndChartType.NoChart,
FirstBar = firstBar,
Value = new double[bars]
};
Component[4] = new IndicatorComp
{
ChartType = IndChartType.NoChart,
FirstBar = firstBar,
Value = new double[bars]
};
// Set component types based on slot type
if (SlotType == SlotTypes.Open)
{
Component[3].DataType = IndComponentType.OpenLongPrice;
Component[3].CompName = "Long position entry price";
Component[4].DataType = IndComponentType.OpenShortPrice;
Component[4].CompName = "Short position entry price";
}
else if (SlotType == SlotTypes.OpenFilter)
{
Component[3].DataType = IndComponentType.AllowOpenLong;
Component[3].CompName = "Is long entry allowed";
Component[4].DataType = IndComponentType.AllowOpenShort;
Component[4].CompName = "Is short entry allowed";
}
else if (SlotType == SlotTypes.Close)
{
Component[3].DataType = IndComponentType.CloseLongPrice;
Component[3].CompName = "Long position closing price";
Component[4].DataType = IndComponentType.CloseShortPrice;
Component[4].CompName = "Short position closing price";
}
else if (SlotType == SlotTypes.CloseFilter)
{
Component[3].DataType = IndComponentType.ForceCloseLong;
Component[3].CompName = "Close out long position";
Component[4].DataType = IndComponentType.ForceCloseShort;
Component[4].CompName = "Close out short position";
}
// Apply logic
if (SlotType == SlotTypes.Open || SlotType == SlotTypes.Close)
{
for (int bar = firstBar; bar < bars; bar++)
{
double open = Open[bar];
double upperValue = upperBand[bar - previous];
double lowerValue = lowerBand[bar - previous];
// Handle price gaps
if ((upperBand[bar - previous - 1] > High[bar - 1] && upperValue < open) ||
(upperBand[bar - previous - 1] < Low[bar - 1] && upperValue > open) ||
(Close[bar - 1] < upperValue && upperValue < open) ||
(Close[bar - 1] > upperValue && upperValue > open))
{
upperValue = open;
}
if ((lowerBand[bar - previous - 1] > High[bar - 1] && lowerValue < open) ||
(lowerBand[bar - previous - 1] < Low[bar - 1] && lowerValue > open) ||
(Close[bar - 1] < lowerValue && lowerValue < open) ||
(Close[bar - 1] > lowerValue && lowerValue > open))
{
lowerValue = open;
}
if (IndParam.ListParam[0].Text.StartsWith("Enter long at Upper Band") ||
IndParam.ListParam[0].Text.StartsWith("Exit long at Upper Band"))
{
Component[3].Value[bar] = upperValue;
Component[4].Value[bar] = lowerValue;
}
else
{
Component[3].Value[bar] = lowerValue;
Component[4].Value[bar] = upperValue;
}
}
}
else if (SlotType == SlotTypes.OpenFilter || SlotType == SlotTypes.CloseFilter)
{
switch (IndParam.ListParam[0].Text)
{
case "The bar opens below Upper Band":
BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, ref Component[3], ref Component[4],
BandIndLogic.The_bar_opens_below_the_Upper_Band);
break;
case "The bar opens above Upper Band":
BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, ref Component[3], ref Component[4],
BandIndLogic.The_bar_opens_above_the_Upper_Band);
break;
case "The bar opens below Lower Band":
BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, ref Component[3], ref Component[4],
BandIndLogic.The_bar_opens_below_the_Lower_Band);
break;
case "The bar opens above Lower Band":
BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, ref Component[3], ref Component[4],
BandIndLogic.The_bar_opens_above_the_Lower_Band);
break;
case "The position opens above Upper Band":
Component[0].PosPriceDependence = PositionPriceDependence.BuyHigherSellLower;
Component[2].PosPriceDependence = PositionPriceDependence.BuyLowerSellHigher;
Component[3].DataType = IndComponentType.Other;
Component[4].DataType = IndComponentType.Other;
Component[3].ShowInDynInfo = false;
Component[4].ShowInDynInfo = false;
break;
case "The position opens below Upper Band":
Component[0].PosPriceDependence = PositionPriceDependence.BuyLowerSellHigher;
Component[2].PosPriceDependence = PositionPriceDependence.BuyHigherSellLower;
Component[3].DataType = IndComponentType.Other;
Component[4].DataType = IndComponentType.Other;
Component[3].ShowInDynInfo = false;
Component[4].ShowInDynInfo = false;
break;
case "The position opens above Lower Band":
Component[0].PosPriceDependence = PositionPriceDependence.BuyHigherSellLower;
Component[2].PosPriceDependence = PositionPriceDependence.BuyLowerSellHigher;
Component[3].DataType = IndComponentType.Other;
Component[4].DataType = IndComponentType.Other;
Component[3].ShowInDynInfo = false;
Component[4].ShowInDynInfo = false;
break;
case "The position opens below Lower Band":
Component[0].PosPriceDependence = PositionPriceDependence.BuyLowerSellHigher;
Component[2].PosPriceDependence = PositionPriceDependence.BuyHigherSellLower;
Component[3].DataType = IndComponentType.Other;
Component[4].DataType = IndComponentType.Other;
Component[3].ShowInDynInfo = false;
Component[4].ShowInDynInfo = false;
break;
case "The bar opens below Upper Band after opening above it":
BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, ref Component[3], ref Component[4],
BandIndLogic.The_bar_opens_below_the_Upper_Band_after_opening_above_it);
break;
case "The bar opens above Upper Band after opening below it":
BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, ref Component[3], ref Component[4],
BandIndLogic.The_bar_opens_above_the_Upper_Band_after_opening_below_it);
break;
case "The bar opens below Lower Band after opening above it":
BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, ref Component[3], ref Component[4],
BandIndLogic.The_bar_opens_below_the_Lower_Band_after_opening_above_it);
break;
case "The bar opens above Lower Band after opening below it":
BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, ref Component[3], ref Component[4],
BandIndLogic.The_bar_opens_above_the_Lower_Band_after_opening_below_it);
break;
case "The bar closes below Upper Band":
BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, ref Component[3], ref Component[4],
BandIndLogic.The_bar_closes_below_the_Upper_Band);
break;
case "The bar closes above Upper Band":
BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, ref Component[3], ref Component[4],
BandIndLogic.The_bar_closes_above_the_Upper_Band);
break;
case "The bar closes below Lower Band":
BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, ref Component[3], ref Component[4],
BandIndLogic.The_bar_closes_below_the_Lower_Band);
break;
case "The bar closes above Lower Band":
BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, ref Component[3], ref Component[4],
BandIndLogic.The_bar_closes_above_the_Lower_Band);
break;
}
}
}
private double[] CalculateSessionVWAP(int anchorHour, int anchorMinute, int resetPeriod)
{
int bars = Bars;
double[] vwapBuffer = new double[bars];
double cumPV = 0.0;
double cumV = 0.0;
DateTime currentReset = DateTime.MinValue;
for (int iBar = 3; iBar < bars; iBar++)
{
DateTime barTime = Time[iBar]; // Using bar opening time directly
DateTime lastReset = GetLastResetTime(barTime, anchorHour, anchorMinute, resetPeriod);
if (lastReset != currentReset)
{
cumPV = 0.0;
cumV = 0.0;
currentReset = lastReset;
}
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;
}
private double[] CalculateATR(int atrPeriod)
{
int bars = Bars;
double[] atr = new double[bars];
// Calculate True Range
for (int bar = 1; bar < bars; bar++)
{
double trueRange = Math.Max(Math.Max(
High[bar] - Low[bar],
Math.Abs(High[bar] - Close[bar - 1])),
Math.Abs(Low[bar] - Close[bar - 1]));
atr[bar] = trueRange;
}
// Smooth ATR
for (int bar = atrPeriod + 1; bar < bars; bar++)
{
double sum = 0;
for (int i = 0; i < atrPeriod; i++)
{
sum += atr[bar - i];
}
atr[bar] = sum / atrPeriod;
}
return atr;
}
private DateTime GetLastResetTime(DateTime currentTime, int anchorHour, int anchorMinute, int resetPeriod)
{
DateTime anchorToday = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, anchorHour, anchorMinute, 0);
// Calculate time difference between current time and anchor time
TimeSpan timeDiff = currentTime - anchorToday;
if (timeDiff.TotalMinutes >= 0)
{
// Current time is after anchor time today
int periods = (int)(timeDiff.TotalMinutes / resetPeriod);
return anchorToday.AddMinutes(periods * resetPeriod);
}
else
{
// Current time is before anchor time today - look at previous day
DateTime prevDayAnchor = anchorToday.AddDays(-1);
TimeSpan prevDiff = currentTime - prevDayAnchor;
int periods = (int)(prevDiff.TotalMinutes / resetPeriod);
return prevDayAnchor.AddMinutes(periods * resetPeriod);
}
}
public override void SetDescription()
{
switch (IndParam.ListParam[0].Text)
{
case "Enter long at Upper Band":
EntryPointLongDescription = "at Upper Band of " + ToString();
EntryPointShortDescription = "at Lower Band of " + ToString();
break;
case "Enter long at Lower Band":
EntryPointLongDescription = "at Lower Band of " + ToString();
EntryPointShortDescription = "at Upper Band of " + ToString();
break;
case "Exit long at Upper Band":
ExitPointLongDescription = "at Upper Band of " + ToString();
ExitPointShortDescription = "at Lower Band of " + ToString();
break;
case "Exit long at Lower Band":
ExitPointLongDescription = "at Lower Band of " + ToString();
ExitPointShortDescription = "at Upper Band of " + ToString();
break;
case "The bar opens below Upper Band":
EntryFilterLongDescription = "the bar opens below Upper Band of " + ToString();
EntryFilterShortDescription = "the bar opens above Lower Band of " + ToString();
break;
case "The bar opens above Upper Band":
EntryFilterLongDescription = "the bar opens above Upper Band of " + ToString();
EntryFilterShortDescription = "the bar opens below Lower Band of " + ToString();
break;
case "The bar opens below Lower Band":
EntryFilterLongDescription = "the bar opens below Lower Band of " + ToString();
EntryFilterShortDescription = "the bar opens above Upper Band of " + ToString();
break;
case "The bar opens above Lower Band":
EntryFilterLongDescription = "the bar opens above Lower Band of " + ToString();
EntryFilterShortDescription = "the bar opens below Upper Band of " + ToString();
break;
case "The position opens above Upper Band":
EntryFilterLongDescription = "the position opening price is higher than Upper Band of " + ToString();
EntryFilterShortDescription = "the position opening price is lower than Lower Band of " + ToString();
break;
case "The position opens below Upper Band":
EntryFilterLongDescription = "the position opening price is lower than Upper Band of " + ToString();
EntryFilterShortDescription = "the position opening price is higher than Lower Band of " + ToString();
break;
case "The position opens above Lower Band":
EntryFilterLongDescription = "the position opening price is higher than Lower Band of " + ToString();
EntryFilterShortDescription = "the position opening price is lower than Upper Band of " + ToString();
break;
case "The position opens below Lower Band":
EntryFilterLongDescription = "the position opening price is lower than Lower Band of " + ToString();
EntryFilterShortDescription = "the position opening price is higher than Upper Band of " + ToString();
break;
case "The bar opens below Upper Band after opening above it":
EntryFilterLongDescription = "the bar opens below Upper Band of " + ToString() + " after the previous bar has opened above it";
EntryFilterShortDescription = "the bar opens above Lower Band of " + ToString() + " after the previous bar has opened below it";
break;
case "The bar opens above Upper Band after opening below it":
EntryFilterLongDescription = "the bar opens above Upper Band of " + ToString() + " after the previous bar has opened below it";
EntryFilterShortDescription = "the bar opens below Lower Band of " + ToString() + " after the previous bar has opened above it";
break;
case "The bar opens below Lower Band after opening above it":
EntryFilterLongDescription = "the bar opens below Lower Band of " + ToString() + " after the previous bar has opened above it";
EntryFilterShortDescription = "the bar opens above Upper Band of " + ToString() + " after the previous bar has opened below it";
break;
case "The bar opens above Lower Band after opening below it":
EntryFilterLongDescription = "the bar opens above Lower Band of " + ToString() + " after the previous bar has opened below it";
EntryFilterShortDescription = "the bar opens below Upper Band of " + ToString() + " after the previous bar has opened above it";
break;
case "The bar closes below Upper Band":
ExitFilterLongDescription = "the bar closes below Upper Band of " + ToString();
ExitFilterShortDescription = "the bar closes above Lower Band of " + ToString();
break;
case "The bar closes above Upper Band":
ExitFilterLongDescription = "the bar closes above Upper Band of " + ToString();
ExitFilterShortDescription = "the bar closes below Lower Band of " + ToString();
break;
case "The bar closes below Lower Band":
ExitFilterLongDescription = "the bar closes below Lower Band of " + ToString();
ExitFilterShortDescription = "the bar closes above Upper Band of " + ToString();
break;
case "The bar closes above Lower Band":
ExitFilterLongDescription = "the bar closes above Lower Band of " + ToString();
ExitFilterShortDescription = "the bar closes below Upper Band of " + ToString();
break;
}
}
public override string ToString()
{
return IndicatorName +
(IndParam.CheckParam[0].Checked ? "* (" : " (") +
"Anchor: " + IndParam.ListParam[1].Text + ":" + IndParam.ListParam[2].Text + ", " +
"Reset: " + IndParam.ListParam[3].Text + " min, " +
"ATR Period: " + IndParam.NumParam[0].ValueToString + ", " +
"ATR Mult: " + IndParam.NumParam[1].ValueToString + ")";
}
}
}
#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 SessionVWAPATRBandsStepped : public Indicator { public: SessionVWAPATRBandsStepped(SlotTypes slotType) { SlotType = slotType; IndicatorName = "SessionVWAPATRBandsStepped"; WarningMessage = "SessionVWAP with ATR-based bands that resets at stepped periods (15-1440 min)."; IsAllowLTF = true; ExecTime = ExecutionTime_DuringTheBar; IsSeparateChart = false; IsDiscreteValues = false; IsDefaultGroupAll = false; } virtual void Calculate(DataSet &dataSet); private: datetime GetLastResetTime(datetime currentTime, int anchorHour, int anchorMinute, int resetPeriod); void CalculateATR(int atrPeriod, double &atrBuffer[]); void CalculateSessionVWAP(int anchorHour, int anchorMinute, int resetPeriod, double &vwapBuffer[]); }; void SessionVWAPATRBandsStepped::Calculate(DataSet &dataSet) { Data = GetPointer(dataSet); // Read parameters int anchorHour = (int)StringToInteger(ListParam[1].Text); int anchorMinute = (int)StringToInteger(ListParam[2].Text); int resetPeriod = (int)StringToInteger(ListParam[3].Text); int atrPeriod = (int)NumParam[0].Value; double atrMultiplier = NumParam[1].Value; int previous = CheckParam[0].Checked ? 1 : 0; int firstBar = MathMax(atrPeriod, 3) + previous + 2; int bars = Data.Bars; // Calculate components double vwapBuffer[]; ArrayResize(vwapBuffer, bars); ArrayInitialize(vwapBuffer, 0.0); CalculateSessionVWAP(anchorHour, anchorMinute, resetPeriod, vwapBuffer); double atrBuffer[]; ArrayResize(atrBuffer, bars); ArrayInitialize(atrBuffer, 0.0); CalculateATR(atrPeriod, atrBuffer); // Calculate bands double upperBand[]; ArrayResize(upperBand, bars); ArrayInitialize(upperBand, 0.0); double lowerBand[]; ArrayResize(lowerBand, bars); ArrayInitialize(lowerBand, 0.0); for(int bar = atrPeriod; bar < bars; bar++) { upperBand[bar] = vwapBuffer[bar] + atrBuffer[bar] * atrMultiplier; lowerBand[bar] = vwapBuffer[bar] - atrBuffer[bar] * atrMultiplier; } // Create components ArrayResize(Component[0].Value, bars); Component[0].CompName = "Upper Band"; Component[0].DataType = IndComponentType_IndicatorValue; Component[0].FirstBar = firstBar; ArrayCopy(Component[0].Value, upperBand); ArrayResize(Component[1].Value, bars); Component[1].CompName = "SessionVWAP"; Component[1].DataType = IndComponentType_IndicatorValue; Component[1].FirstBar = firstBar; ArrayCopy(Component[1].Value, vwapBuffer); ArrayResize(Component[2].Value, bars); Component[2].CompName = "Lower Band"; Component[2].DataType = IndComponentType_IndicatorValue; Component[2].FirstBar = firstBar; ArrayCopy(Component[2].Value, lowerBand); ArrayResize(Component[3].Value, bars); ArrayInitialize(Component[3].Value, 0.0); Component[3].FirstBar = firstBar; ArrayResize(Component[4].Value, bars); ArrayInitialize(Component[4].Value, 0.0); Component[4].FirstBar = firstBar; // Set component types based on slot type if(SlotType == SlotTypes_Open) { Component[3].DataType = IndComponentType_OpenLongPrice; Component[3].CompName = "Long position entry price"; Component[4].DataType = IndComponentType_OpenShortPrice; Component[4].CompName = "Short position entry price"; } else if(SlotType == SlotTypes_OpenFilter) { Component[3].DataType = IndComponentType_AllowOpenLong; Component[3].CompName = "Is long entry allowed"; Component[4].DataType = IndComponentType_AllowOpenShort; Component[4].CompName = "Is short entry allowed"; } else if(SlotType == SlotTypes_Close) { Component[3].DataType = IndComponentType_CloseLongPrice; Component[3].CompName = "Long position closing price"; Component[4].DataType = IndComponentType_CloseShortPrice; Component[4].CompName = "Short position closing price"; } else if(SlotType == SlotTypes_CloseFilter) { Component[3].DataType = IndComponentType_ForceCloseLong; Component[3].CompName = "Close out long position"; Component[4].DataType = IndComponentType_ForceCloseShort; Component[4].CompName = "Close out short position"; } // Apply logic if(SlotType == SlotTypes_Open || SlotType == SlotTypes_Close) { for(int bar = firstBar; bar < bars; bar++) { double open = Data.Open[bar]; double upperValue = upperBand[bar - previous]; double lowerValue = lowerBand[bar - previous]; // Handle price gaps exactly like C# version if((upperBand[bar - previous - 1] > Data.High[bar - 1] && upperValue < open) || (upperBand[bar - previous - 1] < Data.Low[bar - 1] && upperValue > open) || (Data.Close[bar - 1] < upperValue && upperValue < open) || (Data.Close[bar - 1] > upperValue && upperValue > open)) { upperValue = open; } if((lowerBand[bar - previous - 1] > Data.High[bar - 1] && lowerValue < open) || (lowerBand[bar - previous - 1] < Data.Low[bar - 1] && lowerValue > open) || (Data.Close[bar - 1] < lowerValue && lowerValue < open) || (Data.Close[bar - 1] > lowerValue && lowerValue > open)) { lowerValue = open; } if(StringFind(ListParam[0].Text, "Enter long at Upper Band") != -1 || StringFind(ListParam[0].Text, "Exit long at Upper Band") != -1) { Component[3].Value[bar] = upperValue; Component[4].Value[bar] = lowerValue; } else { Component[3].Value[bar] = lowerValue; Component[4].Value[bar] = upperValue; } } } else if(SlotType == SlotTypes_OpenFilter || SlotType == SlotTypes_CloseFilter) { if(ListParam[0].Text == "The bar opens below Upper Band") BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, Component[3], Component[4], BandIndLogic_The_bar_opens_below_the_Upper_Band); else if(ListParam[0].Text == "The bar opens above Upper Band") BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, Component[3], Component[4], BandIndLogic_The_bar_opens_above_the_Upper_Band); else if(ListParam[0].Text == "The bar opens below Lower Band") BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, Component[3], Component[4], BandIndLogic_The_bar_opens_below_the_Lower_Band); else if(ListParam[0].Text == "The bar opens above Lower Band") BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, Component[3], Component[4], BandIndLogic_The_bar_opens_above_the_Lower_Band); else if(ListParam[0].Text == "The position opens above Upper Band") { Component[0].PosPriceDependence = PositionPriceDependence_PriceBuyHigher; Component[2].PosPriceDependence = PositionPriceDependence_PriceSellLower; Component[3].DataType = IndComponentType_Other; Component[4].DataType = IndComponentType_Other; Component[3].ShowInDynInfo = false; Component[4].ShowInDynInfo = false; } else if(ListParam[0].Text == "The position opens below Upper Band") { Component[0].PosPriceDependence = PositionPriceDependence_PriceBuyLower; Component[2].PosPriceDependence = PositionPriceDependence_PriceSellHigher; Component[3].DataType = IndComponentType_Other; Component[4].DataType = IndComponentType_Other; Component[3].ShowInDynInfo = false; Component[4].ShowInDynInfo = false; } else if(ListParam[0].Text == "The position opens above Lower Band") { Component[0].PosPriceDependence = PositionPriceDependence_PriceBuyHigher; Component[2].PosPriceDependence = PositionPriceDependence_PriceSellLower; Component[3].DataType = IndComponentType_Other; Component[4].DataType = IndComponentType_Other; Component[3].ShowInDynInfo = false; Component[4].ShowInDynInfo = false; } else if(ListParam[0].Text == "The position opens below Lower Band") { Component[0].PosPriceDependence = PositionPriceDependence_PriceBuyLower; Component[2].PosPriceDependence = PositionPriceDependence_PriceSellHigher; Component[3].DataType = IndComponentType_Other; Component[4].DataType = IndComponentType_Other; Component[3].ShowInDynInfo = false; Component[4].ShowInDynInfo = false; } else if(ListParam[0].Text == "The bar opens below Upper Band after opening above it") BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, Component[3], Component[4], BandIndLogic_The_bar_opens_below_Upper_Band_after_above); else if(ListParam[0].Text == "The bar opens above Upper Band after opening below it") BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, Component[3], Component[4], BandIndLogic_The_bar_opens_above_Upper_Band_after_below); else if(ListParam[0].Text == "The bar opens below Lower Band after opening above it") BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, Component[3], Component[4], BandIndLogic_The_bar_opens_below_Lower_Band_after_above); else if(ListParam[0].Text == "The bar opens above Lower Band after opening below it") BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, Component[3], Component[4], BandIndLogic_The_bar_opens_above_Lower_Band_after_below); else if(ListParam[0].Text == "The bar closes below Upper Band") BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, Component[3], Component[4], BandIndLogic_The_bar_closes_below_the_Upper_Band); else if(ListParam[0].Text == "The bar closes above Upper Band") BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, Component[3], Component[4], BandIndLogic_The_bar_closes_above_the_Upper_Band); else if(ListParam[0].Text == "The bar closes below Lower Band") BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, Component[3], Component[4], BandIndLogic_The_bar_closes_below_the_Lower_Band); else if(ListParam[0].Text == "The bar closes above Lower Band") BandIndicatorLogic(firstBar, previous, upperBand, lowerBand, Component[3], Component[4], BandIndLogic_The_bar_closes_above_the_Lower_Band); } } void SessionVWAPATRBandsStepped::CalculateSessionVWAP(int anchorHour, int anchorMinute, int resetPeriod, double &vwapBuffer[]) { int bars = Data.Bars; double cumPV = 0.0; double cumV = 0.0; datetime currentResetTime = 0; for(int bar = 3; bar < bars; bar++) { datetime barTime = Data.Time[bar]; datetime lastReset = GetLastResetTime(barTime, anchorHour, anchorMinute, resetPeriod); if(lastReset != currentResetTime) { cumPV = 0.0; cumV = 0.0; currentResetTime = lastReset; } double typicalPrice = (Data.Open[bar] + Data.High[bar] + Data.Low[bar] + Data.Close[bar]) / 4.0; double volume = (double)Data.Volume[bar]; cumPV += typicalPrice * volume; cumV += volume; vwapBuffer[bar] = (cumV > 0.0) ? (cumPV / cumV) : 0.0; } } datetime SessionVWAPATRBandsStepped::GetLastResetTime(datetime currentTime, int anchorHour, int anchorMinute, int resetPeriod) { MqlDateTime tm; TimeToStruct(currentTime, tm); // Create anchor time for today MqlDateTime anchorStruct = tm; anchorStruct.hour = anchorHour; anchorStruct.min = anchorMinute; anchorStruct.sec = 0; datetime anchorToday = StructToTime(anchorStruct); if(currentTime >= anchorToday) { // Current time is after anchor time today int minutesSinceAnchor = (int)((currentTime - anchorToday) / 60); int periods = minutesSinceAnchor / resetPeriod; return anchorToday + (periods * resetPeriod * 60); } else { // Current time is before anchor time today - look at previous day datetime prevDayAnchor = anchorToday - 86400; int minutesSincePrev = (int)((currentTime - prevDayAnchor) / 60); int periods = minutesSincePrev / resetPeriod; return prevDayAnchor + (periods * resetPeriod * 60); } } void SessionVWAPATRBandsStepped::CalculateATR(int atrPeriod, double &atrBuffer[]) { int bars = Data.Bars; // Calculate True Range for(int bar = 1; bar < bars; bar++) { double trueRange = MathMax(MathMax( Data.High[bar] - Data.Low[bar], MathAbs(Data.High[bar] - Data.Close[bar-1])), MathAbs(Data.Low[bar] - Data.Close[bar-1])); atrBuffer[bar] = trueRange; } // Smooth ATR for(int bar = atrPeriod + 1; bar < bars; bar++) { double sum = 0; for(int i = 0; i < atrPeriod; i++) { sum += atrBuffer[bar - i]; } atrBuffer[bar] = sum / atrPeriod; } }
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.;