Cumulative Delta indicator Code
//------------------------------------------------------------------------------ // // CumulativeDelta. Copyright (c) 2019 Ilya Smirnov. All rights reserved. // //------------------------------------------------------------------------------ using System; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.Serialization; using System.Windows; using System.Windows.Media; using TigerTrade.Chart.Base; using TigerTrade.Chart.Base.Enums; using TigerTrade.Chart.Indicators.Common; using TigerTrade.Chart.Indicators.Enums; using TigerTrade.Core.Utils.Time; using TigerTrade.Dx; namespace TigerTrade.Chart.Indicators.Custom { [DataContract(Name = "CumulativeDeltaIndicator", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")] [Indicator("X_CumulativeDelta", "*CumulativeDelta", false, Type = typeof(CumulativeDeltaIndicator))] internal sealed class CumulativeDeltaIndicator : IndicatorBase { private IndicatorPeriodType _period; [DataMember(Name = "Period")] [Category("Параметры"), DisplayName("Период")] public IndicatorPeriodType Period { get => _period; set { if (value == _period) { return; } _period = value; Clear(); OnPropertyChanged(); } } private IndicatorViewType _type; [DataMember(Name = "Type")] [Category("Параметры"), DisplayName("Тип")] public IndicatorViewType Type { get => _type; set { if (value == _type) { return; } _type = value; OnPropertyChanged(); OnPropertyChanged(nameof(UpColor)); OnPropertyChanged(nameof(DownColor)); OnPropertyChanged(nameof(LineColor)); OnPropertyChanged(nameof(LineWidth)); } } private XBrush _upBrush; private XPen _upPen; private XColor _upColor; [DataMember(Name = "UpColor")] [Category("Стиль"), DisplayName("Цвет при росте")] public XColor UpColor { get => _upColor; set { if (value == _upColor) { return; } _upColor = value; _upBrush = new XBrush(_upColor); _upPen = new XPen(_upBrush, 1); OnPropertyChanged(); } } private XBrush _downBrush; private XPen _downPen; private XColor _downColor; [DataMember(Name = "DownColor")] [Category("Стиль"), DisplayName("Цвет при падении")] public XColor DownColor { get => _downColor; set { if (value == _downColor) { return; } _downColor = value; _downBrush = new XBrush(_downColor); _downPen = new XPen(_downBrush, 1); OnPropertyChanged(); } } private XBrush _lineBrush; private XPen _linePen; private XColor _lineColor; [DataMember(Name = "LineColor")] [Category("Стиль"), DisplayName("Цвет линии")] public XColor LineColor { get => _lineColor; set { if (value == _lineColor) { return; } _lineColor = value; _lineBrush = new XBrush(_lineColor); _linePen = new XPen(_lineBrush, LineWidth); OnPropertyChanged(); } } private int _lineWidth; [DataMember(Name = "LineWidth")] [Category("Стиль"), DisplayName("Толщина линии")] public int LineWidth { get => _lineWidth; set { value = Math.Max(1, Math.Min(9, value)); if (value == _lineWidth) { return; } _lineWidth = value; _linePen = new XPen(_lineBrush, _lineWidth); OnPropertyChanged(); } } [Browsable(false)] public override IndicatorCalculation Calculation => IndicatorCalculation.OnEachTick; public override bool IntegerValues => true; private class CumDeltaItem { public long Open; public long High; public long Low; public long Close; public int Seq = -1; } private int _lastFullID; private List<CumDeltaItem> _items; private List<CumDeltaItem> Items => _items ?? (_items = new List<CumDeltaItem>()); private void Clear() { _lastFullID = 0; Items.Clear(); } public CumulativeDeltaIndicator() { Period = IndicatorPeriodType.Day; Type = IndicatorViewType.Columns; UpColor = Color.FromArgb(255, 30, 144, 255); DownColor = Color.FromArgb(255, 178, 34, 34); LineColor = Color.FromArgb(255, 30, 144, 255); LineWidth = 1; } protected override void Execute() { if (ClearData) { Clear(); } var timeOffset = TimeHelper.GetSessionOffset(DataProvider.Symbol.Exchange); for (var i = _lastFullID; i < DataProvider.Count; i++) { var cluster = DataProvider.GetRawCluster(i); if (Items.Count < i + 1) { Items.Add(new CumDeltaItem()); } var item = Items[i]; var prevItem = Items.Count > 1 ? Items[i - 1] : new CumDeltaItem(); var sequence = 1; switch (Period) { case IndicatorPeriodType.Day: sequence = DataProvider.Period.GetSequence(ChartPeriodType.Day, 1, cluster.Time, timeOffset); break; case IndicatorPeriodType.Week: sequence = DataProvider.Period.GetSequence(ChartPeriodType.Week, 1, cluster.Time, timeOffset); break; case IndicatorPeriodType.Month: sequence = DataProvider.Period.GetSequence(ChartPeriodType.Month, 1, cluster.Time, timeOffset); break; } var cumDeltaValue = 0L; if (sequence == prevItem.Seq) { cumDeltaValue = prevItem.Close; } item.Seq = sequence; item.Open = cumDeltaValue; cumDeltaValue += cluster.Delta; item.Close = cumDeltaValue; item.High = cumDeltaValue + (cluster.DeltaHigh - cluster.Delta); item.Low = cumDeltaValue + (cluster.DeltaLow - cluster.Delta); } _lastFullID = Math.Max(DataProvider.Count - 2, 0); } public override bool GetMinMax(out double min, out double max) { min = double.MaxValue; max = double.MinValue; if (Items.Count == 0) { return false; } var longMin = long.MaxValue; var longMax = long.MinValue; if (Type == IndicatorViewType.Candles) { for (var i = 0; i < Canvas.Count; i++) { var index = Canvas.GetIndex(i); if (index < 0 || index >= Items.Count) { continue; } var item = Items[index]; if (longMin > item.Low) { longMin = item.Low; } if (longMax < item.High) { longMax = item.High; } } } else { for (var i = 0; i < Canvas.Count; i++) { var index = Canvas.GetIndex(i); if (index < 0 || index >= Items.Count) { continue; } var item = Items[index]; if (longMin > item.Close) { longMin = item.Close; } if (longMax < item.Close) { longMax = item.Close; } } } if (longMin > longMax) { return false; } min = (double) DataProvider.Symbol.GetSize(longMin); max = (double) DataProvider.Symbol.GetSize(longMax); return true; } public override void Render(DxVisualQueue visual) { var symbol = DataProvider.Symbol; switch (Type) { case IndicatorViewType.Candles: { var width = (int)(Canvas.ColumnWidth * Canvas.ColumnPercent + 0.4); var halfWith = Math.Max((int)(width / 2.0), 1.0); for (var i = 0; i < Canvas.Count; i++) { var index = Canvas.GetIndex(i); if (index < 0 || index >= Items.Count) { continue; } var item = Items[index]; var isUp = item.Close > item.Open; var centerX = (int)Canvas.GetX(index); var openY = (int)GetY(symbol.GetSize(item.Open)); var highY = (int)GetY(symbol.GetSize(item.High)); var lowY = (int)GetY(symbol.GetSize(item.Low)); var closeY = (int)GetY(symbol.GetSize(item.Close)); var topY = Math.Min(openY, closeY); var bottomY = Math.Max(openY, closeY); var height = bottomY - topY; var backBrush = isUp ? _upBrush : _downBrush; var backPen = isUp ? _upPen : _downPen; if (height == 0 || width <= 1) { if (highY == lowY) { lowY++; } visual.DrawLine(backPen, new Point(centerX, highY), new Point(centerX, lowY)); } else { visual.DrawLine(backPen, new Point(centerX, highY), new Point(centerX, topY)); visual.DrawLine(backPen, new Point(centerX, bottomY), new Point(centerX, lowY)); } if (width > 1) { if (height == 0) { visual.DrawLine(backPen, new Point(centerX - halfWith, topY), new Point(centerX + halfWith + 1, topY)); } else { visual.FillRectangle(backBrush, new Rect(centerX - halfWith, topY, halfWith * 2 + 1, height)); } } } } break; case IndicatorViewType.Columns: { var zeroPoint = GetY(0.0); var width = Math.Max(Canvas.ColumnWidth - 1, 1); for (var i = 0; i < Canvas.Count; i++) { var index = Canvas.GetIndex(i); if (index < 0 || index >= Items.Count) { continue; } var item = Items[index]; var x = Canvas.GetX(index); var y = GetY(symbol.GetSize(item.Open)); var h = (int)zeroPoint - (int)y; var isUp = h > 0; if (h < 0.0) { y += h; h = -h; } h = Math.Max(1, h); if (width > 1.0) { var rect = new Rect(x - width / 2.0, y, width, h); visual.FillRectangle(isUp ? _upBrush : _downBrush, rect); } else { visual.DrawLine(isUp ? _upPen : _downPen, x, y, x, y + h); } } } break; case IndicatorViewType.Line: { if (Items.Count < 2) { return; } var points = new Point[Canvas.Count]; for (var i = 0; i < Canvas.Count; i++) { var index = Canvas.GetIndex(i); if (index < 0 || index >= Items.Count) { continue; } var item = Items[index]; var x = Canvas.GetX(index); var y = GetY(symbol.GetSize(item.Open)); points[i] = new Point(x, y); } visual.DrawLines(_linePen, points); } break; } } public override List<IndicatorValueInfo> GetValues(int cursorPos) { var info = new List<IndicatorValueInfo>(); if (cursorPos >= 0 && cursorPos < Items.Count) { var s = Canvas.FormatValue((double)DataProvider.Symbol.GetSize(Items[cursorPos].Close)); info.Add(new IndicatorValueInfo(s, Type == IndicatorViewType.Line ? _lineBrush : Canvas.Theme.ChartFontBrush)); } return info; } public override void GetLabels(ref List<IndicatorLabelInfo> labels) { if (Items.Count == 0) { return; } if (DataProvider.Count <= Canvas.Start) { return; } var index = DataProvider.Count - 1 - Canvas.Start; if (index < 0 && index >= Items.Count) { return; } var item = Items[index]; var lastValue = (double)DataProvider.Symbol.GetSize(item.Close); if (Type == IndicatorViewType.Line) { labels.Add(new IndicatorLabelInfo(lastValue, _lineColor)); return; } var isUp = false; switch (Type) { case IndicatorViewType.Line: case IndicatorViewType.Columns: isUp = !double.IsNaN(lastValue) && lastValue > 0; break; case IndicatorViewType.Candles: isUp = !double.IsNaN(lastValue) && item.Close > item.Open; break; } labels.Add(new IndicatorLabelInfo(lastValue, isUp ? UpColor : DownColor)); } public override void ApplyColors(IChartTheme theme) { UpColor = theme.PaletteColor6; DownColor = theme.PaletteColor7; LineColor = theme.GetNextColor(); base.ApplyColors(theme); } public override void CopyTemplate(IndicatorBase indicator, bool style) { var i = (CumulativeDeltaIndicator)indicator; Period = i.Period; Type = i.Type; UpColor = i.UpColor; DownColor = i.DownColor; LineColor = i.LineColor; LineWidth = i.LineWidth; base.CopyTemplate(indicator, style); } public override string ToString() { return $"{Name} ({Period})"; } public override bool GetPropertyVisibility(string propertyName) { switch (propertyName) { case "LineWidth": case "LineColor": return Type == IndicatorViewType.Line; case "UpColor": case "DownColor": return Type != IndicatorViewType.Line; } return true; } } }