# Cumulative Delta

```
//------------------------------------------------------------------------------
//
// Индикатор CumulativeDelta. Copyright (c) 2023 Tiger Trade Capital AG. 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;
        }
    }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://support.tiger.com/razrabotka-dlya-tiger.trade-windows/primery-indikatorov/cumulative-delta.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
