VWAP
//------------------------------------------------------------------------------
//
// Indicator VWAP. Copyright (c) 2023 Tiger Trade Capital AG. All rights reserved.
//
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.Serialization;
using System.Windows.Media;
using TigerTrade.Chart.Base;
using TigerTrade.Chart.Base.Enums;
using TigerTrade.Chart.Indicators.Common;
using TigerTrade.Chart.Indicators.Drawings;
using TigerTrade.Chart.Indicators.Drawings.Enums;
using TigerTrade.Chart.Indicators.Enums;
using TigerTrade.Core.Utils.Time;
namespace TigerTrade.Chart.Indicators.Custom
{
[DataContract(Name = "VWAPIndicator", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
[Indicator("X_VWAP", "*VWAP", true, Type = typeof(VWAPIndicator))]
internal sealed class VWAPIndicator : IndicatorBase
{
private IndicatorPeriodType _period;
[DataMember(Name = "Period")]
[Category("Параметры"), DisplayName("Период")]
public IndicatorPeriodType Period
{
get => _period;
set
{
if (value == _period)
{
return;
}
_period = value;
StdDevCache.Clear();
OnPropertyChanged();
}
}
private TimeSpan? _startTime;
[DataMember(Name = "StartTime")]
[Category("Параметры"), DisplayName("Время")]
public TimeSpan? StartTime
{
get => _startTime;
set
{
if (value.Equals(_startTime))
{
return;
}
_startTime = value;
StdDevCache.Clear();
OnPropertyChanged();
}
}
private IndicatorPriceType _priceType;
[DataMember(Name = "PriceType")]
[Category("Параметры"), DisplayName("Цена")]
public IndicatorPriceType PriceType
{
get => _priceType;
set
{
if (value == _priceType)
{
return;
}
_priceType = value;
StdDevCache.Clear();
OnPropertyChanged();
}
}
private List<decimal> _stdDevs;
[DataMember(Name = "StdDevs")]
[Category("Параметры"), DisplayName("StdDevs")]
public List<decimal> StdDevs
{
get => _stdDevs ?? (StdDevs = new List<decimal>());
set
{
if (Equals(value, _stdDevs))
{
return;
}
_stdDevs = value;
OnPropertyChanged();
}
}
private ChartSeries _vwapSeries;
[DataMember(Name = "VWAP")]
[Category("ChartIndicatorsCharts"), DisplayName("VWAP")]
public ChartSeries VWAPSeries
{
get => _vwapSeries ?? (_vwapSeries = new ChartSeries(ChartSeriesType.Line, Colors.Blue));
private set
{
if (Equals(value, _vwapSeries))
{
return;
}
_vwapSeries = value;
OnPropertyChanged();
}
}
private ChartSeries _stdDevSeries;
[DataMember(Name = "StdDev")]
[Category("ChartIndicatorsCharts"), DisplayName("StdDev")]
public ChartSeries StdDevSeries
{
get => _stdDevSeries ?? (_stdDevSeries = new ChartSeries(ChartSeriesType.Line, Colors.Red));
private set
{
if (Equals(value, _stdDevSeries))
{
return;
}
_stdDevSeries = value;
OnPropertyChanged();
}
}
public VWAPIndicator()
{
Period = IndicatorPeriodType.Day;
StartTime = null;
PriceType = IndicatorPriceType.Median;
StdDevs.Add(1);
StdDevs.Add(2);
}
private List<double> _stdDevCache;
private List<double> StdDevCache => _stdDevCache ?? (_stdDevCache = new List<double>(1000));
protected override void Execute()
{
var date = Helper.Date;
var price = Helper.Price(_priceType);
var vol = Helper.Volume;
var vwap = new double[date.Length];
var stdDev = new double[date.Length];
var splits = new bool[date.Length];
var calcStdDevs = StdDevs.Count > 0;
if (date.Length < StdDevCache.Count)
{
StdDevCache.Clear();
}
var lastSequence = -1;
var cumVolumePrice = 0.0;
var cumVolume = 0.0;
var newDayIndex = 0;
double timeOffset;
if (StartTime.HasValue)
{
var oaBaseDate = DateTime.FromOADate(0);
timeOffset = -oaBaseDate.Add(StartTime.Value).ToOADate();
}
else
{
timeOffset = TimeHelper.GetSessionOffset(DataProvider.Symbol.Exchange);
}
for (var i = 0; i < date.Length; i++)
{
var sequence = -1;
switch (Period)
{
case IndicatorPeriodType.Day:
sequence = DataProvider.Period.GetSequence(ChartPeriodType.Day, 1, date[i], timeOffset);
break;
case IndicatorPeriodType.Week:
sequence = DataProvider.Period.GetSequence(ChartPeriodType.Week, 1, date[i], timeOffset);
break;
case IndicatorPeriodType.Month:
sequence = DataProvider.Period.GetSequence(ChartPeriodType.Month, 1, date[i], timeOffset);
break;
}
if (sequence != lastSequence)
{
lastSequence = sequence;
newDayIndex = i;
cumVolumePrice = 0.0;
cumVolume = 0.0;
splits[i] = true;
}
cumVolumePrice += price[i] * vol[i];
cumVolume += vol[i];
if (cumVolume == 0)
{
continue;
}
vwap[i] = cumVolumePrice / cumVolume;
if (calcStdDevs)
{
if (i < StdDevCache.Count)
{
stdDev[i] = StdDevCache[i];
}
else
{
var variance = 0.0;
for (var j = newDayIndex; j < i; j++)
{
variance += (vol[j]/cumVolume)*(price[j] - vwap[i])*(price[j] - vwap[i]);
}
stdDev[i] = Math.Sqrt(variance);
StdDevCache.Add(stdDev[i]);
}
}
}
var vwapSeries = new IndicatorSeriesData(vwap, VWAPSeries)
{
Style =
{
DisableMinMax = true
}
};
Series.Add(vwapSeries);
if (calcStdDevs)
{
foreach (var dev in StdDevs)
{
if (dev <= 0)
{
continue;
}
var stdDevPos = new double[date.Length];
var stdDevNeg = new double[date.Length];
var d = (double) dev;
for (var i = 0; i < date.Length; i++)
{
stdDevPos[i] = vwap[i] + stdDev[i] * d;
stdDevNeg[i] = vwap[i] - stdDev[i] * d;
}
var stdDevPosSeries = new IndicatorSeriesData(stdDevPos, StdDevSeries)
{
Style = {DisableMinMax = true}
};
var stdDevNegSeries = new IndicatorSeriesData(stdDevNeg, StdDevSeries)
{
Style = {DisableMinMax = true}
};
Series.Add(stdDevPosSeries, stdDevNegSeries);
}
}
foreach (var series in Series)
{
series.UserData["S"] = splits;
series.UserData["SE"] = false;
}
}
public override void ApplyColors(IChartTheme theme)
{
VWAPSeries.AllColors = theme.GetNextColor();
StdDevSeries.AllColors = theme.GetNextColor();
base.ApplyColors(theme);
}
public override void CopyTemplate(IndicatorBase indicator, bool style)
{
var i = (VWAPIndicator)indicator;
Period = i.Period;
PriceType = i.PriceType;
StdDevs = i.StdDevs.ToList();
VWAPSeries.CopyTheme(i.VWAPSeries);
StdDevSeries.CopyTheme(i.StdDevSeries);
base.CopyTemplate(indicator, style);
}
}
}
Last updated