Cluster Statistical indicator Code
//------------------------------------------------------------------------------ // // ClusterStatistic. 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; using TigerTrade.Chart.Base.Enums; using TigerTrade.Chart.Indicators.Common; using TigerTrade.Chart.Indicators.Enums; using TigerTrade.Core.UI.Converters; using TigerTrade.Core.Utils.Time; using TigerTrade.Dx; namespace TigerTrade.Chart.Indicators.Custom { [TypeConverter(typeof(EnumDescriptionTypeConverter))] [DataContract(Name = "ClusterStatisticPeriodType", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")] public enum ClusterStatisticPeriodType { [EnumMember(Value = "Day"), Description("День")] Day, [EnumMember(Value = "Week"), Description("Неделя")] Week, [EnumMember(Value = "Month"), Description("Месяц")] Month, [EnumMember(Value = "VisibleBars"), Description("Видимые бары")] VisibleBars, [EnumMember(Value = "AllBars"), Description("Все бары")] AllBars } [DataContract(Name = "ClusterStatisticIndicator", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")] [Indicator("X_ClusterStatistic", "*ClusterStatistic", false, Type = typeof(ClusterStatisticIndicator))] internal sealed class ClusterStatisticIndicator : IndicatorBase { private ClusterStatisticPeriodType _period; [DataMember(Name = "Period")] [Category("Параметры"), DisplayName("Период")] public ClusterStatisticPeriodType Period { get => _period; set { if (value == _period) { return; } _period = value; OnPropertyChanged(); } } private bool _minimizeValues; [DataMember(Name = "MinimizeValues")] [Category("Параметры"), DisplayName("Минимизировать значения")] public bool MinimizeValues { get => _minimizeValues; set { if (value == _minimizeValues) { return; } _minimizeValues = value; OnPropertyChanged(); } } private IndicatorIntParam _roundValuesParam; [DataMember(Name = "RoundValueParam")] public IndicatorIntParam RoundValuesParam { get => _roundValuesParam ?? (_roundValuesParam = new IndicatorIntParam(0)); set => _roundValuesParam = value; } [DefaultValue(0)] [Category("Параметры"), DisplayName("Округлять значения")] public int RoundValues { get => RoundValuesParam.Get(SettingsLongKey); set { if (!RoundValuesParam.Set(SettingsLongKey, value, -4, 4)) { return; } OnPropertyChanged(); } } private bool _ask; [DataMember(Name = "ShowAsk")] [Category("Вывод данных"), DisplayName("Ask")] public bool ShowAsk { get => _ask; set { if (value == _ask) { return; } _ask = value; OnPropertyChanged(); } } private bool _bid; [DataMember(Name = "ShowBid")] [Category("Вывод данных"), DisplayName("Bid")] public bool ShowBid { get => _bid; set { if (value == _bid) { return; } _bid = value; OnPropertyChanged(); } } private bool _delta; [DataMember(Name = "ShowDelta")] [Category("Вывод данных"), DisplayName("Delta")] public bool ShowDelta { get => _delta; set { if (value == _delta) { return; } _delta = value; OnPropertyChanged(); } } private bool _volume; [DataMember(Name = "ShowVolume")] [Category("Вывод данных"), DisplayName("Volume")] public bool ShowVolume { get => _volume; set { if (value == _volume) { return; } _volume = value; OnPropertyChanged(); } } private bool _trades; [DataMember(Name = "ShowTrades")] [Category("Вывод данных"), DisplayName("Trades")] public bool ShowTrades { get => _trades; set { if (value == _trades) { return; } _trades = value; OnPropertyChanged(); } } [Browsable(false)] public override bool ShowIndicatorValues => false; [Browsable(false)] public override bool ShowIndicatorLabels => false; [Browsable(false)] public override IndicatorCalculation Calculation => IndicatorCalculation.OnEachTick; public ClusterStatisticIndicator() { ShowIndicatorTitle = false; Period = ClusterStatisticPeriodType.Day; MinimizeValues = false; ShowAsk = true; ShowBid = true; ShowDelta = true; ShowVolume = true; ShowTrades = true; } protected override void Execute() { } public override void Render(DxVisualQueue visual) { var labels = new List<string>(); if (ShowVolume) { labels.Add("Volume"); } if (ShowTrades) { labels.Add("Trades"); } if (ShowDelta) { labels.Add("Delta"); } if (ShowBid) { labels.Add("Bid"); } if (ShowAsk) { labels.Add("Ask"); } if (labels.Count == 0) { return; } var symbol = DataProvider.Symbol; var linePen = new XPen(new XBrush(Canvas.Theme.ChartAxisColor), 1); var lastSequence = -1; var maxVolume = 0m; var maxTrades = 0L; var maxDelta = 0m; var updateMaxValues = true; switch (Period) { case ClusterStatisticPeriodType.AllBars: { updateMaxValues = false; var count = DataProvider.Count; for (var i = 0; i < count; i++) { var cluster = DataProvider.GetCluster(i); if (cluster == null) { continue; } var maxValues = cluster.MaxValues; maxVolume = Math.Max(maxVolume, cluster.Volume); maxTrades = Math.Max(maxTrades, cluster.Trades); maxDelta = Math.Max(maxDelta, Math.Max(maxValues.MaxDelta, -maxValues.MinDelta)); } break; } case ClusterStatisticPeriodType.VisibleBars: { updateMaxValues = false; for (var i = 0; i < Canvas.Count; i++) { var cluster = DataProvider.GetCluster(Canvas.GetIndex(i)); if (cluster == null) { continue; } var maxValues = cluster.MaxValues; maxVolume = Math.Max(maxVolume, cluster.Volume); maxTrades = Math.Max(maxTrades, cluster.Trades); maxDelta = Math.Max(maxDelta, Math.Max(maxValues.MaxDelta, -maxValues.MinDelta)); } break; } } var baseHeight = (int)Canvas.ChartFont.GetHeight() + 4; var height = labels.Count * baseHeight + 2; var prevLeft = 0.0; var columnWidth = Canvas.ColumnWidth; var r = Canvas.Rect; var roundValues = RoundValues; var timeOffset = TimeHelper.GetSessionOffset(DataProvider.Symbol.Exchange); for (var i = 0; i < Canvas.Count; i++) { var x = Canvas.GetXX(i); var left = x - columnWidth * .5; if (i == 0) { prevLeft = left + columnWidth; var backRect = new Rect(new Point(r.Left, r.Bottom - baseHeight*labels.Count - 2), new Point(prevLeft, r.Bottom)); visual.FillRectangle(XBrush.White, backRect); visual.FillRectangle(Canvas.Theme.ChartBackBrush, backRect); for (var j = 1; j <= labels.Count; j++) { var h = baseHeight * j + 2; visual.DrawLine(linePen, new Point(r.Left, r.Bottom - h), new Point(x + columnWidth * .5, r.Bottom - h)); } } visual.DrawLine(linePen, new Point(left + columnWidth, r.Bottom - height), new Point(left + columnWidth, r.Bottom)); var index = Canvas.GetIndex(i); var cluster = DataProvider.GetCluster(index); if (cluster == null) { continue; } if (updateMaxValues) { var sequence = 1; switch (Period) { case ClusterStatisticPeriodType.Day: sequence = DataProvider.Period.GetSequence(ChartPeriodType.Day, 1, cluster.Time, timeOffset); break; case ClusterStatisticPeriodType.Week: sequence = DataProvider.Period.GetSequence(ChartPeriodType.Week, 1, cluster.Time, timeOffset); break; case ClusterStatisticPeriodType.Month: sequence = DataProvider.Period.GetSequence(ChartPeriodType.Month, 1, cluster.Time, timeOffset); break; } if (sequence != lastSequence) { lastSequence = sequence; maxVolume = 0; maxTrades = 0; maxDelta = 0; for (var j = index; j >= 0; j--) { var cl = DataProvider.GetCluster(j); if (cl == null) { continue; } var newSequence = 1; switch (Period) { case ClusterStatisticPeriodType.Day: newSequence = DataProvider.Period.GetSequence(ChartPeriodType.Day, 1, cl.Time, timeOffset); break; case ClusterStatisticPeriodType.Week: newSequence = DataProvider.Period.GetSequence(ChartPeriodType.Week, 1, cl.Time, timeOffset); break; case ClusterStatisticPeriodType.Month: newSequence = DataProvider.Period.GetSequence(ChartPeriodType.Month, 1, cl.Time, timeOffset); break; } if (newSequence != sequence) { break; } var maxValues = cl.MaxValues; maxVolume = Math.Max(maxVolume, cl.Volume); maxTrades = Math.Max(maxTrades, cl.Trades); maxDelta = Math.Max(maxDelta, Math.Max(maxValues.MaxDelta, -maxValues.MinDelta)); } } } var deltaBrush = new XBrush( (cluster.Delta > 0 ? Canvas.Theme.ClusterDeltaPlusColor : Canvas.Theme.ClusterDeltaMinusColor).ChangeOpacity(Math.Abs(cluster.Delta), maxDelta)); var volumeBrush = new XBrush(Canvas.Theme.ClusterVolumeColor.ChangeOpacity(cluster.Volume, maxVolume)); var tradesBrush = new XBrush(Canvas.Theme.ClusterTradesColor.ChangeOpacity(cluster.Trades, maxTrades)); var textBrush = new XBrush(Canvas.Theme.ClusterTextColor); for (var j = 1; j <= labels.Count; j++) { var label = labels[j - 1]; var h = baseHeight * j + 2; var cellRect = new Rect(new Point((int)left, r.Bottom - h + 1), new Point((int)prevLeft, r.Bottom - (h - baseHeight) + (j == 1 ? 2 : 0))); var cellTextRect = new Rect(new Point(left + 2, r.Bottom - h), new Point(left + columnWidth - 2, r.Bottom - (h - baseHeight))); var value = ""; switch (label) { case "Volume": value = symbol.FormatSize(cluster.Volume, roundValues, MinimizeValues); break; case "Trades": value = symbol.FormatTrades(cluster.Trades, roundValues, MinimizeValues); break; case "Delta": value = symbol.FormatSize(cluster.Delta, roundValues, MinimizeValues); break; case "Bid": value = symbol.FormatSize(cluster.Bid, roundValues, MinimizeValues); break; case "Ask": value = symbol.FormatSize(cluster.Ask, roundValues, MinimizeValues); break; } if (!string.IsNullOrEmpty(value)) { visual.FillRectangle( label == "Volume" ? volumeBrush : (label == "Trades" ? tradesBrush : deltaBrush), cellRect); visual.DrawString(value, Canvas.ChartFont, textBrush, cellTextRect); } } prevLeft = left; } // labels var labelMaxWidth = labels.Aggregate(0, (current, label) => (int)Math.Max(current, Canvas.ChartFont.GetWidth(label))) + 5; visual.FillRectangle(Canvas.Theme.ChartBackBrush, new Rect(new Point(r.Left, r.Bottom - height), new Point(r.Left + labelMaxWidth, r.Bottom))); visual.DrawLine(linePen, new Point(r.Left + labelMaxWidth, r.Bottom - height), new Point(r.Left + labelMaxWidth, r.Bottom)); var labelHeight = 2; foreach (var label in labels) { labelHeight += baseHeight; visual.DrawLine(linePen, new Point(r.Left, r.Bottom - labelHeight), new Point(r.Left + labelMaxWidth, r.Bottom - labelHeight)); var labelTextRect = new Rect(new Point(r.Left + 2, r.Bottom - labelHeight), new Point(r.Left + labelMaxWidth - 2, r.Bottom - (labelHeight - baseHeight))); visual.DrawString(label, Canvas.ChartFont, Canvas.Theme.ChartFontBrush, labelTextRect); } } public override void CopyTemplate(IndicatorBase indicator, bool style) { var i = (ClusterStatisticIndicator)indicator; Period = i.Period; MinimizeValues = i.MinimizeValues; RoundValuesParam.Copy(i.RoundValuesParam); ShowAsk = i.ShowAsk; ShowBid = i.ShowBid; ShowDelta = i.ShowDelta; ShowVolume = i.ShowVolume; ShowTrades = i.ShowTrades; base.CopyTemplate(indicator, style); } } }