Код индикатора VWAP
//------------------------------------------------------------------------------ // // Индикатор VWAP. Copyright (c) 2019 Ilya Smirnov. 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); } } }