Histogram

//------------------------------------------------------------------------------
//
// Индикатор Histogram. 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.Enums;
using TigerTrade.Chart.Data;
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
{
    [DataContract(Name = "HistogramPeriodType", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
    [TypeConverter(typeof(EnumDescriptionTypeConverter))]
    public enum HistogramPeriodType
    {
        [EnumMember(Value = "Minute"), Description("Минута")]
        Minute,
        [EnumMember(Value = "Hour"), Description("Час")]
        Hour,
        [EnumMember(Value = "Day"), Description("День")]
        Day,
        [EnumMember(Value = "Week"), Description("Неделя")]
        Week,
        [EnumMember(Value = "Month"), Description("Месяц")]
        Month,
        [EnumMember(Value = "AllBars"), Description("Все бары")]
        AllBars,
        [EnumMember(Value = "CustomDate"), Description("Свой интервал")]
        CustomDate
    }
 
    [DataContract(Name = "HistogramViewType", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
    [TypeConverter(typeof(EnumDescriptionTypeConverter))]
    public enum HistogramViewType
    {
        [EnumMember(Value = "Volume"), Description("Volume")]
        Volume,
        [EnumMember(Value = "Trades"), Description("Trades")]
        Trades,
        [EnumMember(Value = "Delta"), Description("Delta")]
        Delta,
        [EnumMember(Value = "BidAsk"), Description("Bid x Ask")]
        BidAsk,
    }
 
    [DataContract(Name = "HistogramCellViewType", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
    [TypeConverter(typeof(EnumDescriptionTypeConverter))]
    public enum HistogramCellViewType
    {
        [EnumMember(Value = "Solid"), Description("Без отступов")]
        Solid,
        [EnumMember(Value = "Bars"), Description("С отступами")]
        Bars,
        [EnumMember(Value = "BorderedBars"), Description("С границей")]
        BorderedBars
    }
 
    [DataContract(Name = "HistogramIndicator", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
    [Indicator("Z_Histogram", "*Histogram", true, Type = typeof(HistogramIndicator))]
    internal sealed class HistogramIndicator : IndicatorBase
    {
        private HistogramPeriodType _periodType;
 
        [DataMember(Name = "PeriodType")]
        [Category("Период"), DisplayName("Интервал")]
        public HistogramPeriodType PeriodType
        {
            get => _periodType;
            set
            {
                if (value == _periodType)
                {
                    return;
                }
 
                _periodType = value;
 
                _periodValue = _periodType == HistogramPeriodType.Minute ? 15 : 1;
 
                Clear();
 
                OnPropertyChanged();
                OnPropertyChanged(nameof(PeriodValue));
                OnPropertyChanged(nameof(PeriodRevers));
                OnPropertyChanged(nameof(StartDate));
                OnPropertyChanged(nameof(EndDate));
            }
        }
 
        private int _periodValue;
 
        [DataMember(Name = "PeriodValue")]
        [Category("Период"), DisplayName("Значение")]
        public int PeriodValue
        {
            get => _periodValue;
            set
            {
                value = Math.Max(1, value);
 
                if (value == _periodValue)
                {
                    return;
                }
 
                _periodValue = value;
 
                Clear();
 
                OnPropertyChanged();
            }
        }
 
        private bool _periodRevers;
 
        [DataMember(Name = "PeriodRevers")]
        [Category("Период"), DisplayName("От последнего бара")]
        public bool PeriodRevers
        {
            get => _periodRevers;
            set
            {
                if (value == _periodRevers)
                {
                    return;
                }
 
                _periodRevers = value;
 
                Clear();
 
                OnPropertyChanged();
            }
        }
 
        private DateTime? _startDate;
 
        [DataMember(Name = "StartDate")]
        [Category("Период"), DisplayName("Начальная дата")]
        public DateTime? StartDate
        {
            get => _startDate;
            set
            {
                if (value.Equals(_startDate))
                {
                    return;
                }
 
                _startDate = value;
 
                Clear();
 
                OnPropertyChanged();
            }
        }
 
        private DateTime? _endDate;
 
        [DataMember(Name = "EndDate")]
        [Category("Период"), DisplayName("Конечная дата")]
        public DateTime? EndDate
        {
            get => _endDate;
            set
            {
                if (value.Equals(_endDate))
                {
                    return;
                }
 
                _endDate = value;
 
                Clear();
 
                OnPropertyChanged();
            }
        }
 
        private HistogramViewType _histogramViewType;
 
        [DataMember(Name = "HistogramViewType"), DefaultValue(HistogramViewType.Volume)]
        [Category("Параметры"), DisplayName("Вид гистограммы")]
        public HistogramViewType HistogramViewType
        {
            get => _histogramViewType;
            set
            {
                if (value == _histogramViewType)
                {
                    return;
                }
 
                _histogramViewType = value;
 
                OnPropertyChanged();
            }
        }
 
        private HistogramCellViewType _histogramCellViewType;
 
        [DataMember(Name = "HistogramCellViewType"), DefaultValue(HistogramCellViewType.Solid)]
        [Category("Параметры"), DisplayName("Вид ячейки")]
        public HistogramCellViewType HistogramCellViewType
        {
            get => _histogramCellViewType;
            set
            {
                if (value == _histogramCellViewType)
                {
                    return;
                }
 
                _histogramCellViewType = value;
 
                OnPropertyChanged();
            }
        }
 
        private bool _histogramGradient;
 
        [DataMember(Name = "HistogramGradient"), DefaultValue(true)]
        [Category("Параметры"), DisplayName("Градиент")]
        public bool HistogramGradient
        {
            get => _histogramGradient;
            set
            {
                if (value == _histogramGradient)
                {
                    return;
                }
 
                _histogramGradient = value;
 
                OnPropertyChanged();
            }
        }
 
        private bool _histogramShowValues;
 
        [DataMember(Name = "HistogramShowValues"), DefaultValue(true)]
        [Category("Параметры"), DisplayName("Отображать значения")]
        public bool HistogramShowValues
        {
            get => _histogramShowValues;
            set
            {
                if (value == _histogramShowValues)
                {
                    return;
                }
 
                _histogramShowValues = value;
 
                OnPropertyChanged();
            }
        }
 
        private bool _histogramMinimizeValues;
 
        [DataMember(Name = "HistogramMinimizeValues")]
        [Category("Параметры"), DisplayName("Минимизировать значения")]
        public bool HistogramMinimizeValues
        {
            get => _histogramMinimizeValues;
            set
            {
                if (value == _histogramMinimizeValues)
                {
                    return;
                }
 
                _histogramMinimizeValues = value;
 
                OnPropertyChanged();
            }
        }
 
        private IndicatorIntParam _histogramRoundValueParam;
 
        [DataMember(Name = "HistogramRoundValuesParam")]
        public IndicatorIntParam HistogramRoundValuesParam
        {
            get => _histogramRoundValueParam ?? (_histogramRoundValueParam = new IndicatorIntParam(0));
            set => _histogramRoundValueParam = value;
        }
 
        [DefaultValue(0)]
        [Category("Параметры"), DisplayName("Округлять значения")]
        public int HistogramRoundValues
        {
            get => HistogramRoundValuesParam.Get(SettingsLongKey);
            set
            {
                if (!HistogramRoundValuesParam.Set(SettingsLongKey, value, -4, 4))
                {
                    return;
                }
 
                OnPropertyChanged();
            }
        }
 
        private bool _histogramShowValueArea;
 
        [DataMember(Name = "HistogramShowValueArea"), DefaultValue(true)]
        [Category("Параметры"), DisplayName("Отображать Value Area")]
        public bool HistogramShowValueArea
        {
            get => _histogramShowValueArea;
            set
            {
                if (value == _histogramShowValueArea)
                {
                    return;
                }
 
                _histogramShowValueArea = value;
 
                OnPropertyChanged();
            }
        }
 
        private bool _histogramExtendValueArea;
 
        [DataMember(Name = "HistogramExtendValueArea"), DefaultValue(false)]
        [Category("Параметры"), DisplayName("Продлить Value Area")]
        public bool HistogramExtendValueArea
        {
            get => _histogramExtendValueArea;
            set
            {
                if (value == _histogramExtendValueArea)
                {
                    return;
                }
 
                _histogramExtendValueArea = value;
 
                OnPropertyChanged();
            }
        }
 
        private int _histogramValueAreaPercent;
 
        [DataMember(Name = "HistogramValueAreaPercent")]
        [Category("Параметры"), DisplayName("ValueArea %")]
        public int HistogramValueAreaPercent
        {
            get => _histogramValueAreaPercent;
            set
            {
                value = Math.Max(0, Math.Min(100, value));
 
                if (value == 0)
                {
                    value = 70;
                }
 
                if (value == _histogramValueAreaPercent)
                {
                    return;
                }
 
                _histogramValueAreaPercent = value;
 
                Clear();
 
                OnPropertyChanged();
            }
        }
 
        private bool _histogramShowPoc;
 
        [DataMember(Name = "HistogramShowPoc"), DefaultValue(true)]
        [Category("Параметры"), DisplayName("Отображать POC")]
        public bool HistogramShowPoc
        {
            get => _histogramShowPoc;
            set
            {
                if (value == _histogramShowPoc)
                {
                    return;
                }
 
                _histogramShowPoc = value;
 
                OnPropertyChanged();
            }
        }
 
        private bool _histogramExtendPoc;
 
        [DataMember(Name = "HistogramExtendPoc"), DefaultValue(false)]
        [Category("Параметры"), DisplayName("Продлить POC")]
        public bool HistogramExtendPoc
        {
            get => _histogramExtendPoc;
            set
            {
                if (value == _histogramExtendPoc)
                {
                    return;
                }
 
                _histogramExtendPoc = value;
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _volumeBrush;
 
        private XColor _volumeColor;
 
        [DataMember(Name = "VolumeColor")]
        [Category("Стиль"), DisplayName("Volume")]
        public XColor VolumeColor
        {
            get => _volumeColor;
            set
            {
                if (value == _volumeColor)
                {
                    return;
                }
 
                _volumeColor = value;
 
                _volumeBrush = new XBrush(_volumeColor);
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _tradesBrush;
 
        private XColor _tradesColor;
 
        [DataMember(Name = "TradesColor")]
        [Category("Стиль"), DisplayName("Trades")]
        public XColor TradesColor
        {
            get => _tradesColor;
            set
            {
                if (value == _tradesColor)
                {
                    return;
                }
 
                _tradesColor = value;
 
                _tradesBrush = new XBrush(_tradesColor);
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _deltaPlusBrush;
 
        private XColor _deltaPlusColor;
 
        [DataMember(Name = "DeltaPlusColor")]
        [Category("Стиль"), DisplayName("Delta+")]
        public XColor DeltaPlusColor
        {
            get => _deltaPlusColor;
            set
            {
                if (value == _deltaPlusColor)
                {
                    return;
                }
 
                _deltaPlusColor = value;
 
                _deltaPlusBrush = new XBrush(_deltaPlusColor);
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _deltaMinusBrush;
 
        private XColor _deltaMinusColor;
 
        [DataMember(Name = "DeltaMinusColor")]
        [Category("Стиль"), DisplayName("Delta-")]
        public XColor DeltaMinusColor
        {
            get => _deltaMinusColor;
            set
            {
                if (value == _deltaMinusColor)
                {
                    return;
                }
 
                _deltaMinusColor = value;
 
                _deltaMinusBrush = new XBrush(_deltaMinusColor);
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _bidBrush;
 
        private XColor _bidColor;
 
        [DataMember(Name = "BidColor")]
        [Category("Стиль"), DisplayName("Bid")]
        public XColor BidColor
        {
            get => _bidColor;
            set
            {
                if (value == _bidColor)
                {
                    return;
                }
 
                _bidColor = value;
 
                _bidBrush = new XBrush(_bidColor);
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _askBrush;
 
        private XColor _askColor;
 
        [DataMember(Name = "AskColor")]
        [Category("Стиль"), DisplayName("Ask")]
        public XColor AskColor
        {
            get => _askColor;
            set
            {
                if (value == _askColor)
                {
                    return;
                }
 
                _askColor = value;
 
                _askBrush = new XBrush(_askColor);
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _valueAreaBrush;
 
        private XColor _valueAreaColor;
 
        [DataMember(Name = "ValueAreaColor")]
        [Category("Стиль"), DisplayName("Value Area")]
        public XColor ValueAreaColor
        {
            get => _valueAreaColor;
            set
            {
                if (value == _valueAreaColor)
                {
                    return;
                }
 
                _valueAreaColor = value;
 
                _valueAreaBrush = new XBrush(_valueAreaColor);
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _maximumBrush;
 
        private XColor _maximumColor;
 
        [DataMember(Name = "MaximumColor")]
        [Category("Стиль"), DisplayName("POC")]
        public XColor MaximumColor
        {
            get => _maximumColor;
            set
            {
                if (value == _maximumColor)
                {
                    return;
                }
 
                _maximumColor = value;
 
                _maximumBrush = new XBrush(_maximumColor);
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _cellBorderBrush;
 
        private XPen _cellBorderPen;
 
        private XColor _cellBorderColor;
 
        [DataMember(Name = "CellBorderColor")]
        [Category("Стиль"), DisplayName("Граница ячейки")]
        public XColor CellBorderColor
        {
            get => _cellBorderColor;
            set
            {
                if (value == _cellBorderColor)
                {
                    return;
                }
 
                _cellBorderColor = value;
 
                _cellBorderBrush = new XBrush(_cellBorderColor);
                _cellBorderPen = new XPen(_cellBorderBrush, 1);
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _textBrush;
 
        private XColor _textColor;
 
        [DataMember(Name = "TextColor")]
        [Category("Стиль"), DisplayName("Текст")]
        public XColor TextColor
        {
            get => _textColor;
            set
            {
                if (value == _textColor)
                {
                    return;
                }
 
                _textColor = value;
 
                _textBrush = new XBrush(_textColor);
 
                OnPropertyChanged();
            }
        }
 
        [Browsable(false)]
        public override bool ShowIndicatorValues => false;
 
        [Browsable(false)]
        public override IndicatorCalculation Calculation => IndicatorCalculation.OnEachTick;
 
        private List<IndicatorLabelInfo> _labels;
 
        private long _lastSequense;
 
        private RawCluster _histogram;
 
        public HistogramIndicator()
        {
            ShowIndicatorTitle = false;
 
            PeriodType = HistogramPeriodType.Day;
            PeriodValue = 1;
 
            HistogramViewType = HistogramViewType.Volume;
            HistogramCellViewType = HistogramCellViewType.Solid;
 
            HistogramGradient = true;
            HistogramShowValues = true;
            HistogramMinimizeValues = false;
            HistogramShowValueArea = true;
            HistogramValueAreaPercent = 70;
            HistogramExtendValueArea = false;
            HistogramShowPoc = true;
            HistogramExtendPoc = false;
 
            VolumeColor = Color.FromArgb(255, 70, 130, 180);
            TradesColor = Color.FromArgb(255, 70, 130, 180);
            DeltaPlusColor = Color.FromArgb(255, 46, 139, 87);
            DeltaMinusColor = Color.FromArgb(255, 178, 34, 34);
            BidColor = Color.FromArgb(255, 178, 34, 34);
            AskColor = Color.FromArgb(255, 70, 130, 180);
            ValueAreaColor = Color.FromArgb(255, 128, 128, 128);
            MaximumColor = Color.FromArgb(255, 178, 34, 34);
            CellBorderColor = Color.FromArgb(255, 127, 127, 127);
            TextColor = Color.FromArgb(255, 255, 255, 255);
        }
 
        protected override void Execute()
        {
            if (_labels == null)
            {
                _labels = new List<IndicatorLabelInfo>();
            }
 
            if (_histogram == null)
            {
                _histogram = new RawCluster(DateTime.MinValue);
            }
 
            if (ClearData)
            {
                Clear();
            }
 
            if (PeriodType == HistogramPeriodType.CustomDate)
            {
                if (_lastSequense == -1)
                {
                    _histogram = new RawCluster(DateTime.MinValue);
 
                    for (var i = 0; i < DataProvider.Count; i++)
                    {
                        var cluster = DataProvider.GetRawCluster(i);
 
                        if (cluster.Time < StartDate || cluster.Time > EndDate)
                        {
                            continue;
                        }
 
                        _histogram.AddCluster(cluster);
                    }
 
                    _lastSequense = 1;
                }
                else
                {
                    var ticks = DataProvider.GetRawTicks();
 
                    foreach (var tick in ticks)
                    {
                        _histogram.AddTick(tick, DataProvider.Scale);
                    }
                }
 
                _histogram.UpdateData();
 
                return;
            }
 
            if (PeriodRevers && PeriodType != HistogramPeriodType.AllBars)
            {
                if (_lastSequense != DataProvider.Count)
                {
                    var interval = GetTimeSeconds();
 
                    var lastBar = DataProvider.GetRawCluster(DataProvider.Count - 1);
 
                    var startTime = lastBar?.OpenTime ?? TimeHelper.GetCurrTime(DataProvider.Symbol.Exchange);
 
                    startTime = startTime.AddSeconds(-interval);
 
                    _histogram = new RawCluster(DateTime.MinValue);
 
                    for (var i = 0; i < DataProvider.Count; i++)
                    {
                        var cluster = DataProvider.GetRawCluster(i);
 
                        if (cluster.Time < startTime)
                        {
                            continue;
                        }
 
                        _histogram.AddCluster(cluster);
                    }
 
                    _lastSequense = DataProvider.Count;
                }
                else
                {
                    var ticks = DataProvider.GetRawTicks();
 
                    foreach (var tick in ticks)
                    {
                        _histogram.AddTick(tick, DataProvider.Scale);
                    }
                }
 
                _histogram.UpdateData();
 
                return;
            }
 
            var timeOffset = TimeHelper.GetSessionOffset(DataProvider.Symbol.Exchange);
 
            var lastCluster = DataProvider.GetRawCluster(DataProvider.Count - 1);
 
            if (lastCluster == null)
            {
                return;
            }
 
            var currSequence = GetSequence(lastCluster.Time, timeOffset);
 
            if (_lastSequense != currSequence)
            {
                _histogram = new RawCluster(DateTime.MinValue);
 
                _lastSequense = currSequence;
 
                for (var i = DataProvider.Count - 1; i >= 0; i--)
                {
                    var cluster = DataProvider.GetRawCluster(i);
 
                    var newSequence = GetSequence(cluster.Time, timeOffset);
 
                    if (_lastSequense != newSequence)
                    {
                        break;
                    }
 
                    _histogram.AddCluster(cluster);
                }
            }
            else
            {
                var ticks = DataProvider.GetRawTicks();
 
                foreach (var tick in ticks)
                {
                    _histogram.AddTick(tick, DataProvider.Scale);
                }
            }
 
            _histogram.UpdateData();
        }
 
        private int GetSequence(DateTime date, double offset)
        {
            var cycleBase = ChartPeriodType.Hour;
 
            switch (PeriodType)
            {
                case HistogramPeriodType.Minute:
 
                    cycleBase = ChartPeriodType.Minute;
 
                    break;
 
                case HistogramPeriodType.Hour:
 
                    cycleBase = ChartPeriodType.Hour;
 
                    break;
 
                case HistogramPeriodType.Day:
 
                    cycleBase = ChartPeriodType.Day;
 
                    break;
 
                case HistogramPeriodType.Week:
 
                    cycleBase = ChartPeriodType.Week;
 
                    break;
 
                case HistogramPeriodType.Month:
 
                    cycleBase = ChartPeriodType.Month;
 
                    break;
 
                case HistogramPeriodType.AllBars:
 
                    return 0;
            }
 
            return DataProvider.Period.GetSequence(cycleBase, PeriodValue, date, offset);
        }
 
        public int GetTimeSeconds()
        {
            switch (PeriodType)
            {
                case HistogramPeriodType.Minute:
 
                    return PeriodValue * 60;
 
                case HistogramPeriodType.Hour:
 
                    return PeriodValue * 60 * 60;
 
                case HistogramPeriodType.Day:
 
                    return PeriodValue * 60 * 60 * 24;
 
                case HistogramPeriodType.Week:
 
                    return PeriodValue * 60 * 60 * 24 * 7;
 
                case HistogramPeriodType.Month:
 
                    return PeriodValue * 60 * 60 * 24 * 30;
 
                default:
 
                    return 0;
            }
        }
 
        private void Clear()
        {
            _lastSequense = -1;
        }
 
        public override void Render(DxVisualQueue visual)
        {
            _labels.Clear();
 
            if (_histogram == null)
            {
                return;
            }
 
            var dp = DataProvider;
            var step = dp.Step;
            var symbol = dp.Symbol;
 
            var rect = Canvas.Rect;
 
            var width = rect.Width * 0.2;
 
            var heightCorrect = HistogramCellViewType == HistogramCellViewType.Bars ||
                                HistogramCellViewType == HistogramCellViewType.BorderedBars
                ? -1
                : 0;
 
            var height = Math.Max(GetY(0.0) - GetY(dp.Step), 1);
 
            var fontSize = Math.Min(height + heightCorrect - 2, 18) * 96.0 / 72.0;
 
            fontSize = Math.Min(fontSize, Canvas.ChartFont.Size);
 
            var textFont = new XFont(Canvas.ChartFont.Name, fontSize);
 
            var minPrice = (long)(Canvas.MinY / step) - 1;
            var maxPrice = (long)(Canvas.MaxY / step) + 1;
 
            if (maxPrice - minPrice > 100000)
            {
                return;
            }
 
            var valueArea = HistogramShowValueArea ? _histogram.GetValueArea(HistogramValueAreaPercent) : null;
 
            maxPrice = Math.Min(_histogram.High, maxPrice + 1);
            minPrice = Math.Max(_histogram.Low, minPrice - 1);
 
            var prevY = (int)GetY(maxPrice * step + step / 2.0);
 
            var roundValues = HistogramRoundValues;
 
            var maxValues = _histogram.MaxValues;
 
            for (var price = maxPrice; price >= minPrice; price--)
            {
                var currY = (int)GetY(price * step - step / 2.0);
 
                var currHeight = Math.Max((currY - prevY) + heightCorrect, 1);
 
                var y = prevY;
 
                prevY = currY;
 
                var item = _histogram.GetItem(price);
 
                if (item == null)
                {
                    continue;
                }
 
                var barWidth = 0.0;
                var valueText = "";
 
                switch (HistogramViewType)
                {
                    case HistogramViewType.Volume:
                        {
                            barWidth = width / maxValues.MaxVolume * item.Volume;
 
                            var fillBrush = HistogramGradient
                                ? new XBrush(VolumeColor.ChangeOpacity(item.Volume, maxValues.MaxVolume,
                                    Canvas.Theme.ChartBackColor))
                                : _volumeBrush;
 
                            var fillRect = new Rect(rect.Left, y, barWidth, currHeight);
 
                            visual.FillRectangle(fillBrush, fillRect);
 
                            valueText = symbol.FormatRawSize(item.Volume, roundValues, HistogramMinimizeValues);
 
                            break;
                        }
 
                    case HistogramViewType.Trades:
                        {
                            barWidth = width / maxValues.MaxTrades * item.Trades;
 
                            var fillBrush = HistogramGradient
                                ? new XBrush(TradesColor.ChangeOpacity(item.Trades, maxValues.MaxTrades,
                                    Canvas.Theme.ChartBackColor))
                                : _tradesBrush;
 
                            var fillRect = new Rect(rect.Left, y, barWidth, currHeight);
 
                            visual.FillRectangle(fillBrush, fillRect);
 
                            valueText = symbol.FormatTrades(item.Trades, roundValues, HistogramMinimizeValues);
 
                            break;
                        }
                    case HistogramViewType.Delta:
                        {
                            var maxDelta = Math.Max(maxValues.MaxDelta, -maxValues.MinDelta);
 
                            barWidth = width / maxDelta * Math.Abs(item.Delta);
 
                            var fillBrush = item.Delta > 0
                                ? (HistogramGradient
                                    ? new XBrush(DeltaPlusColor.ChangeOpacity(item.Delta, maxDelta,
                                        Canvas.Theme.ChartBackColor))
                                    : _deltaPlusBrush)
                                : (HistogramGradient
                                    ? new XBrush(DeltaMinusColor.ChangeOpacity(-item.Delta, maxDelta,
                                        Canvas.Theme.ChartBackColor))
                                    : _deltaMinusBrush);
 
                            var fillRect = new Rect(rect.Left, y, barWidth, currHeight);
 
                            visual.FillRectangle(fillBrush, fillRect);
 
                            valueText = symbol.FormatRawSize(item.Delta, roundValues, HistogramMinimizeValues);
 
                            break;
                        }
                    case HistogramViewType.BidAsk:
                        {
                            barWidth = width / maxValues.MaxVolume * item.Volume;
 
                            var redWidth = barWidth / item.Volume * item.Bid;
 
                            var bidBrush = HistogramGradient
                                ? new XBrush(BidColor.ChangeOpacity(item.Bid,
                                    Math.Max(maxValues.MaxBid, maxValues.MaxAsk), Canvas.Theme.ChartBackColor))
                                : _bidBrush;
 
                            var bidRect = new Rect(rect.Left, y, redWidth, currHeight);
 
                            var askBrush = HistogramGradient
                                ? new XBrush(AskColor.ChangeOpacity(item.Ask,
                                    Math.Max(maxValues.MaxBid, maxValues.MaxAsk), Canvas.Theme.ChartBackColor))
                                : _askBrush;
 
                            var askRect = new Rect(rect.Left + redWidth, y, barWidth - redWidth, currHeight);
 
                            visual.FillRectangle(bidBrush, bidRect);
                            visual.FillRectangle(askBrush, askRect);
 
                            valueText = symbol.FormatRawSize(item.Volume, roundValues, HistogramMinimizeValues);
 
                            break;
                        }
                }
 
                if (HistogramCellViewType == HistogramCellViewType.BorderedBars &&
                    height + heightCorrect > 3)
                {
                    visual.DrawRectangle(_cellBorderPen,
                        new Rect(rect.Left - 1, y, barWidth, currHeight - 1));
                }
 
                if (price == maxValues.Poc && HistogramShowPoc)
                {
                    var pocRect = new Rect(rect.Left, y, width, currHeight);
 
                    if (HistogramExtendPoc)
                    {
                        visual.DrawLine(new XPen(_maximumBrush, 3),
                            new Point(rect.Left, y + currHeight / 2),
                            new Point(rect.Right, y + currHeight / 2));
 
                        _labels.Add(new IndicatorLabelInfo(price * step, MaximumColor));
                    }
                    else
                    {
                        visual.DrawLine(new XPen(_maximumBrush, 3),
                            new Point(rect.Left, y + currHeight / 2),
                            new Point(rect.Left + width, y + currHeight / 2));
                    }
 
                    var pocTextSize = Canvas.ChartFontBold.GetSize("POC");
 
                    visual.DrawString("POC", Canvas.ChartFontBold, _maximumBrush,
                        pocRect.Right - pocTextSize.Width, pocRect.Top - pocTextSize.Height - 4);
                }
                else if (valueArea != null && price == valueArea.Vah)
                {
                    var vahRect = new Rect(rect.Left, y, width, currHeight);
 
                    if (HistogramExtendValueArea)
                    {
                        visual.DrawLine(new XPen(_valueAreaBrush, 3),
                            new Point(rect.Left, y + currHeight / 2),
                            new Point(rect.Right, y + currHeight / 2));
 
                        _labels.Add(new IndicatorLabelInfo(price * step, ValueAreaColor));
                    }
                    else
                    {
                        visual.DrawLine(new XPen(_valueAreaBrush, 3),
                            new Point(rect.Left, y + currHeight / 2),
                            new Point(rect.Left + width, y + currHeight / 2));
                    }
 
                    var vahTextSize = Canvas.ChartFontBold.GetSize("VAH");
 
                    visual.DrawString("VAH", Canvas.ChartFontBold, _valueAreaBrush,
                        vahRect.Right - vahTextSize.Width, vahRect.Top - vahTextSize.Height - 4);
                }
                else if (valueArea != null && price == valueArea.Val)
                {
                    var valRect = new Rect(rect.Left, y, width, currHeight);
 
                    if (HistogramExtendValueArea)
                    {
                        visual.DrawLine(new XPen(_valueAreaBrush, 3),
                            new Point(rect.Left, y + currHeight / 2),
                            new Point(rect.Right, y + currHeight / 2));
 
                        _labels.Add(new IndicatorLabelInfo(price * step, ValueAreaColor));
                    }
                    else
                    {
                        visual.DrawLine(new XPen(_valueAreaBrush, 3),
                            new Point(rect.Left, y + currHeight / 2),
                            new Point(rect.Left + width, y + currHeight / 2));
                    }
 
                    var valTextSize = Canvas.ChartFontBold.GetSize("VAL");
 
                    visual.DrawString("VAL", Canvas.ChartFontBold, _valueAreaBrush,
                        valRect.Right - valTextSize.Width, valRect.Top - valTextSize.Height - 4);
                }
 
                if (HistogramShowValues && height > 7 && y > 15)
                {
                    visual.DrawString(valueText, textFont, _textBrush,
                        new Rect(rect.Left + 2, y, width, currHeight));
                }
            }
        }
 
        public override void GetLabels(ref List<IndicatorLabelInfo> labels)
        {
            labels.AddRange(_labels);
        }
 
        public override void CopyTemplate(IndicatorBase indicator, bool style)
        {
            var i = (HistogramIndicator)indicator;
 
            PeriodType = i.PeriodType;
            PeriodValue = i.PeriodValue;
            PeriodRevers = i.PeriodRevers;
            StartDate = i.StartDate;
            EndDate = i.EndDate;
 
            HistogramViewType = i.HistogramViewType;
            HistogramCellViewType = i.HistogramCellViewType;
            HistogramGradient = i.HistogramGradient;
            HistogramShowValues = i.HistogramShowValues;
            HistogramMinimizeValues = i.HistogramMinimizeValues;
            HistogramShowValueArea = i.HistogramShowValueArea;
            HistogramExtendValueArea = i.HistogramExtendValueArea;
            HistogramValueAreaPercent = i.HistogramValueAreaPercent;
            HistogramShowPoc = i.HistogramShowPoc;
            HistogramExtendPoc = i.HistogramExtendPoc;
 
            HistogramRoundValuesParam.Copy(i.HistogramRoundValuesParam);
 
            VolumeColor = i.VolumeColor;
            TradesColor = i.TradesColor;
            DeltaPlusColor = i.DeltaPlusColor;
            DeltaMinusColor = i.DeltaMinusColor;
            BidColor = i.BidColor;
            AskColor = i.AskColor;
            ValueAreaColor = i.ValueAreaColor;
            MaximumColor = i.MaximumColor;
            CellBorderColor = i.CellBorderColor;
            TextColor = i.TextColor;
 
            base.CopyTemplate(indicator, style);
        }
 
        public override bool GetPropertyVisibility(string propertyName)
        {
            switch (propertyName)
            {
                case nameof(StartDate):
                case nameof(EndDate):
 
                    return PeriodType == HistogramPeriodType.CustomDate;
 
                case nameof(PeriodValue):
 
                    return PeriodType != HistogramPeriodType.CustomDate && PeriodType != HistogramPeriodType.AllBars;
 
                case nameof(PeriodRevers):
 
                    return PeriodType != HistogramPeriodType.CustomDate && PeriodType != HistogramPeriodType.AllBars;
 
            }
 
            return base.GetPropertyVisibility(propertyName);
        }
    }
}

Last updated