# Профиль объёма

```
//------------------------------------------------------------------------------
//
// Графический объект VolumeProfile. 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.Data;
using TigerTrade.Chart.Objects.Common;
using TigerTrade.Core.UI.Converters;
using TigerTrade.Dx;
using TigerTrade.Dx.Enums;
 
namespace TigerTrade.Chart.Objects.Custom
{
    [TypeConverter(typeof(EnumDescriptionTypeConverter))]
    [DataContract(Name = "VolumeProfileType", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Objects.Custom")]
    public enum VolumeProfileType
    {
        [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
    }
 
    [TypeConverter(typeof(EnumDescriptionTypeConverter))]
    [DataContract(Name = "VolumeProfileMaximumType", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Objects.Custom")]
    public enum VolumeProfileMaximumType
    {
        [EnumMember(Value = "Volume"), Description("Volume")]
        Volume,
        [EnumMember(Value = "Trades"), Description("Trades")]
        Trades,
        [EnumMember(Value = "Delta"), Description("Delta")]
        Delta,
        [EnumMember(Value = "DeltaPlus"), Description("Delta+")]
        DeltaPlus,
        [EnumMember(Value = "DeltaMinus"), Description("Delta-")]
        DeltaMinus,
        [EnumMember(Value = "Bid"), Description("Bid")]
        Bid,
        [EnumMember(Value = "Ask"), Description("Ask")]
        Ask
    }
 
    [DataContract(Name = "VolumeProfileObject", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Objects.Custom")]
    [ChartObject("X_VolumeProfile", "Профиль объёма", 2, Type = typeof(VolumeProfileObject))]
    public sealed class VolumeProfileObject : ObjectBase
    {
        private VolumeProfileType _profileType;
 
        [DataMember(Name = "ProfileType")]
        [Category("Профиль"), DisplayName("Тип")]
        public VolumeProfileType ProfileType
        {
            get => _profileType;
            set
            {
                if (value == _profileType)
                {
                    return;
                }
 
                _profileType = value;
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _profileBrush;
 
        private XColor _profileColor;
 
        [DataMember(Name = "ProfileColor")]
        [Category("Профиль"), DisplayName("Цвет")]
        public XColor ProfileColor
        {
            get => _profileColor;
            set
            {
                if (value == _profileColor)
                {
                    return;
                }
 
                _profileColor = value;
 
                _profileBrush = new XBrush(_profileColor);
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _profile2Brush;
 
        private XColor _profile2Color;
 
        [DataMember(Name = "Profile2Color")]
        [Category("Профиль"), DisplayName("Цвет 2")]
        public XColor Profile2Color
        {
            get => _profile2Color;
            set
            {
                if (value == _profile2Color)
                {
                    return;
                }
 
                _profile2Color = value;
 
                _profile2Brush = new XBrush(_profile2Color);
 
                OnPropertyChanged();
            }
        }
 
        private bool _extendProfile;
 
        [DataMember(Name = "ExtendProfile")]
        [Category("Профиль"), DisplayName("Расширить")]
        public bool ExtendProfile
        {
            get => _extendProfile;
            set
            {
                if (value == _extendProfile)
                {
                    return;
                }
 
                _extendProfile = value;
 
                OnPropertyChanged();
            }
        }
 
        private bool _showCumValue;
 
        [DataMember(Name = "ShowCumValue")]
        [Category("Профиль"), DisplayName("Отобр. общ. знач.")]
        public bool ShowCumValue
        {
            get => _showCumValue;
            set
            {
                if (value == _showCumValue)
                {
                    return;
                }
 
                _showCumValue = value;
 
                OnPropertyChanged();
            }
        }
 
        private bool _showValues;
 
        [DataMember(Name = "ShowValues")]
        [Category("Значения"), DisplayName("Отображать")]
        public bool ShowValues
        {
            get => _showValues;
            set
            {
                if (value == _showValues)
                {
                    return;
                }
 
                _showValues = value;
 
                OnPropertyChanged();
            }
        }
 
        private bool _minimizeValues;
 
        [DataMember(Name = "MinimizeValues")]
        [Category("Значения"), DisplayName("Минимизировать")]
        public bool MinimizeValues
        {
            get => _minimizeValues;
            set
            {
                if (value == _minimizeValues)
                {
                    return;
                }
 
                _minimizeValues = value;
 
                OnPropertyChanged();
            }
        }
 
        private int _roundValues;
 
        [DefaultValue(0)]
        [DataMember(Name = "RoundValues")]
        [Category("Значения"), DisplayName("Округлять")]
        public int RoundValues
        {
            get => _roundValues;
            set
            {
                value = Math.Max(-4, Math.Min(4, value));
 
                if (value == _roundValues)
                {
                    return;
                }
 
                _roundValues = value;
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _valuesBrush;
 
        private XColor _valuesColor;
 
        [DataMember(Name = "ValuesColor")]
        [Category("Значения"), DisplayName("Цвет")]
        public XColor ValuesColor
        {
            get => _valuesColor;
            set
            {
                if (value == _valuesColor)
                {
                    return;
                }
 
                _valuesColor = value;
 
                _valuesBrush = new XBrush(_valuesColor);
 
                OnPropertyChanged();
            }
        }
 
        private VolumeProfileMaximumType _maximumType;
 
        [DataMember(Name = "MaximumType")]
        [Category("Максимум"), DisplayName("Тип")]
        public VolumeProfileMaximumType MaximumType
        {
            get => _maximumType;
            set
            {
                if (value == _maximumType)
                {
                    return;
                }
 
                _maximumType = value;
 
                OnPropertyChanged();
            }
        }
 
        private bool _showMaximum;
 
        [DataMember(Name = "ShowMaximum")]
        [Category("Максимум"), DisplayName("Отображать")]
        public bool ShowMaximum
        {
            get => _showMaximum;
            set
            {
                if (value == _showMaximum)
                {
                    return;
                }
 
                _showMaximum = value;
 
                OnPropertyChanged();
            }
        }
 
        private bool _showMaximumValue;
 
        [DataMember(Name = "ShowMaximumValue")]
        [Category("Максимум"), DisplayName("Значение")]
        public bool ShowMaximumValue
        {
            get => _showMaximumValue;
            set
            {
                if (value == _showMaximumValue)
                {
                    return;
                }
 
                _showMaximumValue = value;
 
                OnPropertyChanged();
            }
        }
 
        private bool _extendMaximum;
 
        [DataMember(Name = "ExtendMaximum")]
        [Category("Максимум"), DisplayName("Продлить")]
        public bool ExtendMaximum
        {
            get => _extendMaximum;
            set
            {
                if (value == _extendMaximum)
                {
                    return;
                }
 
                _extendMaximum = value;
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _maximumBrush;
 
        private XColor _maximumColor;
 
        [DataMember(Name = "MaximumColor")]
        [Category("Максимум"), DisplayName("Цвет")]
        public XColor MaximumColor
        {
            get => _maximumColor;
            set
            {
                if (value == _maximumColor)
                {
                    return;
                }
 
                _maximumColor = value;
 
                _maximumBrush = new XBrush(_maximumColor);
 
                OnPropertyChanged();
            }
        }
 
        private bool _showValueArea;
 
        [DataMember(Name = "ShowValueArea")]
        [Category("Value Area"), DisplayName("Отображать")]
        public bool ShowValueArea
        {
            get => _showValueArea;
            set
            {
                if (value == _showValueArea)
                {
                    return;
                }
 
                _showValueArea = value;
 
                OnPropertyChanged();
            }
        }
 
        private bool _extendValueArea;
 
        [DataMember(Name = "ExtendValueArea")]
        [Category("Value Area"), DisplayName("Продлить")]
        public bool ExtendValueArea
        {
            get => _extendValueArea;
            set
            {
                if (value == _extendValueArea)
                {
                    return;
                }
 
                _extendValueArea = value;
 
                OnPropertyChanged();
            }
        }
 
        private int _valueAreaPercent;
 
        [DataMember(Name = "ValueAreaPercent")]
        [Category("Value Area"), DisplayName("ValueArea %")]
        public int ValueAreaPercent
        {
            get => _valueAreaPercent;
            set
            {
                value = Math.Max(0, Math.Min(100, value));
 
                if (value == 0)
                {
                    value = 70;
                }
 
                if (value == _valueAreaPercent)
                {
                    return;
                }
 
                _valueAreaPercent = value;
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _valueAreaBrush;
 
        private XColor _valueAreaColor;
 
        [DataMember(Name = "ValueAreaColor")]
        [Category("Value Area"), DisplayName("Цвет")]
        public XColor ValueAreaColor
        {
            get => _valueAreaColor;
            set
            {
                if (value == _valueAreaColor)
                {
                    return;
                }
 
                _valueAreaColor = value;
 
                _valueAreaBrush = new XBrush(_valueAreaColor);
 
                OnPropertyChanged();
            }
        }
 
        private bool _enableFilter;
 
        [DataMember(Name = "EnableFilter")]
        [Category("Фильтр"), DisplayName("Включить")]
        public bool EnableFilter
        {
            get => _enableFilter;
            set
            {
                if (value == _enableFilter)
                {
                    return;
                }
 
                _enableFilter = value;
 
                OnPropertyChanged();
            }
        }
 
        private int _filterMin;
 
        [DataMember(Name = "FilterMin")]
        [Category("Фильтр"), DisplayName("Минимум")]
        public int FilterMin
        {
            get => _filterMin;
            set
            {
                value = Math.Max(0, value);
 
                if (value == _filterMin)
                {
                    return;
                }
 
                _filterMin = value;
 
                OnPropertyChanged();
            }
        }
 
        private int _filterMax;
 
        [DataMember(Name = "FilterMax")]
        [Category("Фильтр"), DisplayName("Максимум")]
        public int FilterMax
        {
            get => _filterMax;
            set
            {
                value = Math.Max(0, value);
 
                if (value == _filterMax)
                {
                    return;
                }
 
                _filterMax = value;
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _filterBrush;
 
        private XColor _filterColor;
 
        [DataMember(Name = "FilterColor")]
        [Category("Фильтр"), DisplayName("Цвет")]
        public XColor FilterColor
        {
            get => _filterColor;
            set
            {
                if (value == _filterColor)
                {
                    return;
                }
 
                _filterColor = value;
 
                _filterBrush = new XBrush(_filterColor);
 
                OnPropertyChanged();
            }
        }
 
        private bool _drawBorder;
 
        [DataMember(Name = "DrawBorder")]
        [Category("Граница"), DisplayName("Граница")]
        public bool DrawBorder
        {
            get => _drawBorder;
            set
            {
                if (value == _drawBorder)
                {
                    return;
                }
 
                _drawBorder = value;
 
                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, LineStyle);
 
                OnPropertyChanged();
            }
        }
 
        private int _lineWidth;
 
        [DataMember(Name = "LineWidth")]
        [Category("Граница"), DisplayName("Толщина линии")]
        public int LineWidth
        {
            get => _lineWidth;
            set
            {
                value = Math.Max(1, Math.Min(10, value));
 
                if (value == _lineWidth)
                {
                    return;
                }
 
                _lineWidth = value;
 
                _linePen = new XPen(_lineBrush, _lineWidth, LineStyle);
 
                OnPropertyChanged();
            }
        }
 
        private XDashStyle _lineStyle;
 
        [DataMember(Name = "LineStyle")]
        [Category("Граница"), DisplayName("Стиль линии")]
        public XDashStyle LineStyle
        {
            get => _lineStyle;
            set
            {
                if (value == _lineStyle)
                {
                    return;
                }
 
                _lineStyle = value;
 
                _linePen = new XPen(_lineBrush, LineWidth, _lineStyle);
 
                OnPropertyChanged();
            }
        }
 
        private bool _drawBack;
 
        [DataMember(Name = "DrawBack")]
        [Category("Фон"), DisplayName("Фон")]
        public bool DrawBack
        {
            get => _drawBack;
            set
            {
                if (value == _drawBack)
                {
                    return;
                }
 
                _drawBack = value;
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _backBrush;
 
        private XColor _backColor;
 
        [DataMember(Name = "BackColor")]
        [Category("Фон"), DisplayName("Цвет фона")]
        public XColor BackColor
        {
            get => _backColor;
            set
            {
                if (value == _backColor)
                {
                    return;
                }
 
                _backColor = value;
 
                _backBrush = new XBrush(_backColor);
 
                OnPropertyChanged();
            }
        }
 
        protected override int PenWidth => LineWidth;
 
        private bool _isObjectInArea;
 
        public class RectangleInfo
        {
            public Point ControlPoint1;
            public Point ControlPoint2;
 
            public Point ExtraPoint1;
            public Point ExtraPoint2;
 
            public Rect Rectangle;
        }
 
        private RectangleInfo _rectInfo;
 
        public VolumeProfileObject()
        {
            ProfileType = VolumeProfileType.Volume;
            ProfileColor = Color.FromArgb(127, 70, 130, 180);
            Profile2Color = Color.FromArgb(127, 178, 34, 34);
            ExtendProfile = false;
            ShowCumValue = false;
 
            ShowValues = false;
            MinimizeValues = false;
            ValuesColor = Color.FromArgb(255, 255, 255, 255);
 
            MaximumType = VolumeProfileMaximumType.Volume;
            ShowMaximum = true;
            ShowMaximumValue = true;
            ExtendMaximum = false;
            MaximumColor = Color.FromArgb(127, 178, 34, 34);
 
            ShowValueArea = true;
            ExtendValueArea = false;
            ValueAreaPercent = 70;
            ValueAreaColor = Color.FromArgb(127, 128, 128, 128);
 
            EnableFilter = false;
            FilterMin = 0;
            FilterMax = 0;
            FilterColor = Color.FromArgb(127, 46, 139, 87);
 
            DrawBorder = true;
            LineColor = Colors.Black;
            LineWidth = 1;
            LineStyle = XDashStyle.Solid;
 
            DrawBack = true;
            BackColor = Color.FromArgb(30, 0, 0, 0);
        }
 
        protected override void Prepare()
        {
            var point1 = ToPoint(ControlPoints[0]);
            var point2 = ToPoint(ControlPoints[1]);
 
            var ep1 = ToPoint(ExtraPoints[0]);
            var ep2 = ToPoint(ExtraPoints[1]);
 
            var w = Canvas.ColumnWidth / 2.0;
            var h = Canvas.StepHeight / 2.0;
 
            if (point1.X > point2.X)
            {
                point1.X += w;
                point2.X -= w;
            }
            else
            {
                point1.X -= w;
                point2.X += w;
            }
 
            if (point1.Y > point2.Y)
            {
                point1.Y += h;
                point2.Y -= h;
            }
            else
            {
                point1.Y -= h;
                point2.Y += h;
            }
 
            if (ep1.X > ep2.X)
            {
                ep1.X += w;
                ep2.X -= w;
            }
            else
            {
                ep1.X -= w;
                ep2.X += w;
            }
 
            if (ep1.Y > ep2.Y)
            {
                ep1.Y += h;
                ep2.Y -= h;
            }
            else
            {
                ep1.Y -= h;
                ep2.Y += h;
            }
 
            _rectInfo = new RectangleInfo
            {
                ControlPoint1 = point1,
                ControlPoint2 = point2,
                ExtraPoint1 = ep1,
                ExtraPoint2 = ep2,
                Rectangle = new Rect(point1, point2)
            };
 
            _isObjectInArea = Canvas.Rect.IntersectsWith(_rectInfo.Rectangle);
        }
 
        protected override void Draw(DxVisualQueue visual, ref List<ObjectLabelInfo> labels)
        {
            if (!ExtendProfile)
            {
                if (DrawBack)
                {
                    visual.FillRectangle(_backBrush, _rectInfo.Rectangle);
                }
 
                if (DrawBorder)
                {
                    visual.DrawRectangle(_linePen, _rectInfo.Rectangle);
                }
            }
 
            if (!Canvas.IsStock)
            {
                return;
            }
 
            var index1 = 0;
            var index2 = 1;
 
            if (ControlPoints[0].X > ControlPoints[1].X)
            {
                index1 = 1;
                index2 = 0;
            }
 
            var bar1 = Canvas.DateToIndex(ControlPoints[index1].X, 0);
            var bar2 = Canvas.DateToIndex(ControlPoints[index2].X, 0);
 
            var step = DataProvider.Step;
 
            var maxPrice = (long)(Math.Max(ControlPoints[0].Y, ControlPoints[1].Y) / step);
            var minPrice = (long)(Math.Min(ControlPoints[0].Y, ControlPoints[1].Y) / step);
 
            var profile = new RawCluster(DateTime.MinValue);
 
            if (ExtendProfile)
            {
                for (var i = bar1; i <= bar2; i++)
                {
                    var cluster = DataProvider.GetRawCluster(i);
 
                    if (cluster == null)
                    {
                        continue;
                    }
 
                    foreach (var item in cluster.Items)
                    {
                        profile.AddItem(item);
                    }
 
                    maxPrice = Math.Max(maxPrice, cluster.High);
                    minPrice = Math.Min(minPrice, cluster.Low);
                }
            }
            else
            {
                for (var i = bar1; i <= bar2; i++)
                {
                    var cluster = DataProvider.GetRawCluster(i);
 
                    if (cluster == null)
                    {
                        continue;
                    }
 
                    foreach (var item in cluster.Items)
                    {
                        if (item.Price >= minPrice && item.Price <= maxPrice)
                        {
                            profile.AddItem(item);
                        }
                    }
                }
            }
 
            profile.UpdateData();
 
            if (profile.Volume <= 0)
            {
                return;
            }
 
            var valueArea = ShowValueArea ? profile.GetValueArea(ValueAreaPercent) : null;
 
            var p1 = ToPoint(ControlPoints[index1]);
            var p2 = ToPoint(ControlPoints[index2]);
 
            switch (ProfileType)
            {
                case VolumeProfileType.Volume:
 
                    DrawVolume(visual, profile, valueArea, p1, p2, ref labels);
 
                    break;
 
                case VolumeProfileType.Trades:
 
                    DrawTrades(visual, profile, valueArea, p1, p2, ref labels);
 
                    break;
 
                case VolumeProfileType.Delta:
 
                    DrawDelta(visual, profile, valueArea, p1, p2, ref labels);
 
                    break;
 
                case VolumeProfileType.BidAsk:
 
                    DrawBidAsk(visual, profile, valueArea, p1, p2, ref labels);
 
                    break;
            }
 
            if (ShowCumValue)
            {
                DrawValues(visual, profile);
            }
        }
 
        private void DrawValues(DxVisualQueue visual, IRawCluster profile)
        {
            var symbol = DataProvider.Symbol;
 
            var mainRect = _rectInfo.Rectangle;
 
            var height = Canvas.ChartFont.GetHeight();
 
            var rect = new Rect(new Point(mainRect.Left + 2, mainRect.Bottom + 4),
                new Point(mainRect.Right - 2, mainRect.Bottom + height + 4));
 
            switch (ProfileType)
            {
                case VolumeProfileType.Volume:
 
                    var volumeText = symbol.FormatRawSize(profile.Volume, RoundValues, MinimizeValues);
 
                    visual.DrawString($"V: {volumeText}", Canvas.ChartFont, _valuesBrush, rect, XTextAlignment.Right);
 
                    break;
 
                case VolumeProfileType.Trades:
 
                    var tradesText = symbol.FormatTrades(profile.Trades, RoundValues, MinimizeValues);
 
                    visual.DrawString($"T: {tradesText}", Canvas.ChartFont, _valuesBrush, rect, XTextAlignment.Right);
 
                    break;
 
                case VolumeProfileType.Delta:
 
                    var deltaText = symbol.FormatRawSize(profile.Delta, RoundValues, MinimizeValues);
 
                    visual.DrawString($"D: {deltaText}", Canvas.ChartFont, _valuesBrush, rect, XTextAlignment.Right);
 
                    break;
 
                case VolumeProfileType.BidAsk:
 
                    var bidText = symbol.FormatRawSize(profile.Bid, RoundValues, MinimizeValues);
                    var askText = symbol.FormatRawSize(profile.Ask, RoundValues, MinimizeValues);
 
                    visual.DrawString($"B: {bidText} A: {askText}", Canvas.ChartFont, _valuesBrush, rect,
                        XTextAlignment.Right);
 
                    break;
            }
        }
 
        private bool CheckMaximum(IRawClusterItem item, IRawClusterMaxValues maxValues)
        {
            switch (MaximumType)
            {
                case VolumeProfileMaximumType.Volume:
 
                    return item.Volume == maxValues.MaxVolume;
 
                case VolumeProfileMaximumType.Trades:
 
                    return item.Trades == maxValues.MaxTrades;
 
                case VolumeProfileMaximumType.Delta:
 
                    return Math.Abs(item.Delta) ==
                           Math.Max(Math.Abs(maxValues.MaxDelta), Math.Abs(maxValues.MinDelta));
 
                case VolumeProfileMaximumType.DeltaPlus:
 
                    return item.Delta > 0 && item.Delta == maxValues.MaxDelta;
 
                case VolumeProfileMaximumType.DeltaMinus:
 
                    return item.Delta < 0 && item.Delta == maxValues.MinDelta;
 
                case VolumeProfileMaximumType.Bid:
 
                    return item.Bid == maxValues.MaxBid;
 
                case VolumeProfileMaximumType.Ask:
 
                    return item.Ask == maxValues.MaxAsk;
            }
 
            return false;
        }
 
        private string FormatMaximum(IRawClusterItem item)
        {
            switch (MaximumType)
            {
                case VolumeProfileMaximumType.Volume:
 
                    return DataProvider.Symbol.FormatRawSize(item.Volume, RoundValues, MinimizeValues);
 
                case VolumeProfileMaximumType.Trades:
 
                    return DataProvider.Symbol.FormatTrades(item.Trades, RoundValues, MinimizeValues);
 
                case VolumeProfileMaximumType.Delta:
                case VolumeProfileMaximumType.DeltaPlus:
                case VolumeProfileMaximumType.DeltaMinus:
 
                    return DataProvider.Symbol.FormatRawSize(item.Delta, RoundValues, MinimizeValues);
 
                case VolumeProfileMaximumType.Bid:
 
                    return DataProvider.Symbol.FormatRawSize(item.Bid, RoundValues, MinimizeValues);
 
                case VolumeProfileMaximumType.Ask:
 
                    return DataProvider.Symbol.FormatRawSize(item.Ask, RoundValues, MinimizeValues);
            }
 
            return "";
        }
 
        private void DrawVolume(DxVisualQueue visual, IRawCluster profile, IRawClusterValueArea valueArea,
            Point p1, Point p2, ref List<ObjectLabelInfo> labels)
        {
            var colorRects = new List<Tuple<Rect, XBrush>>();
            var colorRects2 = new List<Tuple<Rect, XBrush>>();
            var valueRects = new List<Tuple<Rect, string>>();
            var valueRects2 = new List<Tuple<Rect, string>>();
 
            var step = DataProvider.Step;
            var symbol = DataProvider.Symbol;
 
            var height = Math.Max(Canvas.StepHeight, 1);
 
            var fontSize = Math.Min(height - 2, 18) * 96 / 72;
 
            fontSize = Math.Min(fontSize, Canvas.ChartFont.Size);
 
            var normalFont = new XFont(Canvas.ChartFont.Name, fontSize);
 
            var dist = Math.Max(p2.X - p1.X + Canvas.ColumnWidth - LineWidth, 0);
 
            var left = p1.X - Canvas.ColumnWidth / 2.0 + Math.Ceiling(LineWidth / 2.0);
 
            if (ExtendProfile)
            {
                if (DrawBack)
                {
                    visual.FillRectangle(_backBrush,
                        new Rect(new Point(left, Canvas.Rect.Top), new Point(left + dist, Canvas.Rect.Bottom)));
                }
 
                if (DrawBorder)
                {
                    visual.DrawLine(_linePen, new Point(left, Canvas.Rect.Top),
                        new Point(left, Canvas.Rect.Bottom));
                    visual.DrawLine(_linePen, new Point(left + dist, Canvas.Rect.Top),
                        new Point(left + dist, Canvas.Rect.Bottom));
                }
            }
 
            if (profile.High - profile.Low > 100000)
            {
                return;
            }
 
            var maxValues = profile.MaxValues;
 
            var roundValues = RoundValues;
 
            var prevX = (int)left;
            var prevY = (int)GetY((profile.High + .5) * step);
 
            var points = new List<Point>
            {
                new Point(prevX, prevY)
            };
 
            for (var j = profile.High; j >= profile.Low; j--)
            {
                var item = profile.GetItem(j) ?? new RawClusterItem(j);
 
                var width = item.Volume > 0 ? Math.Min(dist / maxValues.MaxVolume * item.Volume, dist) : 0;
 
                var currX = (int)(left + width);
                var currY = (int)GetY((j - .5) * step);
 
                var currHeight = Math.Max(currY - prevY, height);
 
                if (currY <= prevY && points.Count > 2)
                {
                    if (currX > prevX)
                    {
                        points[points.Count - 2] = new Point(currX, points[points.Count - 2].Y);
                        points[points.Count - 1] = new Point(currX, points[points.Count - 1].Y);
 
                        prevX = currX;
                    }
                }
                else
                {
                    points.Add(new Point(currX, prevY));
                    points.Add(new Point(currX, currY));
 
                    prevX = currX;
                }
 
                prevY = currY;
 
                var topY = points[points.Count - 2].Y;
 
                if (ShowMaximum && CheckMaximum(item, maxValues))
                {
                    colorRects2.Add(
                        new Tuple<Rect, XBrush>(
                            ExtendMaximum
                                ? new Rect(new Point(left, topY),
                                    new Point(Canvas.Rect.Right + 1, topY + currHeight))
                                : new Rect(left, topY, dist, currHeight), _maximumBrush));
 
                    if (ExtendMaximum)
                    {
                        labels.Add(new ObjectLabelInfo(j * step, MaximumColor));
                    }
 
                    if (ShowMaximumValue)
                    {
                        var h = Canvas.ChartFont.GetHeight();
 
                        valueRects2.Add(new Tuple<Rect, string>(
                            new Rect(left + 2, topY - h - 2, Math.Max(dist - 4, 1), h),
                            FormatMaximum(item)));
                    }
                }
                else if (valueArea != null && (item.Price == valueArea.Vah || item.Price == valueArea.Val))
                {
                    colorRects2.Add(
                        new Tuple<Rect, XBrush>(
                            ExtendValueArea
                                ? new Rect(new Point(left, topY), new Point(Canvas.Rect.Right + 1, topY + currHeight))
                                : new Rect(left, topY, dist, currHeight), _valueAreaBrush));
 
                    if (ExtendValueArea)
                    {
                        labels.Add(new ObjectLabelInfo(j * step, ValueAreaColor));
                    }
                }
                else if (EnableFilter && item.Volume >= symbol.CorrectSizeFilter(FilterMin) &&
                         (FilterMax == 0 || item.Volume <= symbol.CorrectSizeFilter(FilterMax)))
                {
                    if (colorRects.Count > 0)
                    {
                        var lastRect = colorRects[colorRects.Count - 1].Item1;
 
                        if ((int)lastRect.Y == (int)topY)
                        {
                            if (width > lastRect.Width)
                            {
                                colorRects[colorRects.Count - 1] =
                                    new Tuple<Rect, XBrush>(new Rect(left, topY, width, lastRect.Height), _filterBrush);
                            }
                        }
                        else
                        {
                            colorRects.Add(new Tuple<Rect, XBrush>(new Rect(left, topY, width, currHeight),
                                _filterBrush));
                        }
                    }
                    else
                    {
                        colorRects.Add(new Tuple<Rect, XBrush>(new Rect(left, topY, width, currHeight), _filterBrush));
                    }
                }
 
                if (ShowValues && height > 7 && item.Volume > 0)
                {
                    valueRects.Add(new Tuple<Rect, string>(new Rect(left + 2, topY, dist, height),
                        symbol.FormatRawSize(item.Volume, roundValues, MinimizeValues)));
                }
            }
 
            points.Add(new Point(left, prevY));
 
            visual.FillPolygon(_profileBrush, points.ToArray());
 
            foreach (var colorRect in colorRects)
            {
                visual.FillRectangle(colorRect.Item2, colorRect.Item1);
            }
 
            foreach (var colorRect in colorRects2)
            {
                visual.FillRectangle(colorRect.Item2, colorRect.Item1);
            }
 
            foreach (var valueRect in valueRects)
            {
                visual.DrawString(valueRect.Item2, normalFont, _valuesBrush, valueRect.Item1);
            }
 
            foreach (var valueRect in valueRects2)
            {
                visual.DrawString(valueRect.Item2, Canvas.ChartFont, _valuesBrush, valueRect.Item1, XTextAlignment.Right);
            }
        }
 
        private void DrawTrades(DxVisualQueue visual, IRawCluster profile, IRawClusterValueArea valueArea,
            Point p1, Point p2, ref List<ObjectLabelInfo> labels)
        {
            var colorRects = new List<Tuple<Rect, XBrush>>();
            var colorRects2 = new List<Tuple<Rect, XBrush>>();
            var valueRects = new List<Tuple<Rect, string>>();
            var valueRects2 = new List<Tuple<Rect, string>>();
 
            var step = DataProvider.Step;
            var symbol = DataProvider.Symbol;
 
            var height = Math.Max(Canvas.StepHeight, 1);
 
            var fontSize = Math.Min(height - 2, 18) * 96 / 72;
 
            fontSize = Math.Min(fontSize, Canvas.ChartFont.Size);
 
            var normalFont = new XFont(Canvas.ChartFont.Name, fontSize);
 
            var dist = p2.X - p1.X + Canvas.ColumnWidth - LineWidth;
 
            var left = p1.X - Canvas.ColumnWidth / 2.0 + Math.Ceiling(LineWidth / 2.0);
 
            if (ExtendProfile)
            {
                if (DrawBack)
                {
                    visual.FillRectangle(_backBrush,
                        new Rect(new Point(left, Canvas.Rect.Top), new Point(left + dist, Canvas.Rect.Bottom)));
                }
 
                if (DrawBorder)
                {
                    visual.DrawLine(_linePen, new Point(left, Canvas.Rect.Top),
                        new Point(left, Canvas.Rect.Bottom));
                    visual.DrawLine(_linePen, new Point(left + dist, Canvas.Rect.Top),
                        new Point(left + dist, Canvas.Rect.Bottom));
                }
            }
 
            if (profile.High - profile.Low > 10000)
            {
                return;
            }
 
            var maxValues = profile.MaxValues;
 
            var roundValues = RoundValues;
 
            var prevX = (int)left;
            var prevY = (int)GetY((profile.High + .5) * step);
 
            var points = new List<Point>
            {
                new Point(prevX, prevY)
            };
 
            for (var j = profile.High; j >= profile.Low; j--)
            {
                var item = profile.GetItem(j) ?? new RawClusterItem(j);
 
                var width = item.Trades > 0 ? Math.Min(dist / maxValues.MaxTrades * item.Trades, dist) : 0;
 
                var currX = (int)(left + width);
                var currY = (int)GetY((j - .5) * step);
 
                var currHeight = Math.Max(currY - prevY, height);
 
                if (currY <= prevY && points.Count > 2)
                {
                    if (currX > prevX)
                    {
                        points[points.Count - 2] = new Point(currX, points[points.Count - 2].Y);
                        points[points.Count - 1] = new Point(currX, points[points.Count - 1].Y);
 
                        prevX = currX;
                    }
                }
                else
                {
                    points.Add(new Point(currX, prevY));
                    points.Add(new Point(currX, currY));
 
                    prevX = currX;
                }
 
                prevY = currY;
 
                var topY = points[points.Count - 2].Y;
 
                if (ShowMaximum && CheckMaximum(item, maxValues))
                {
                    colorRects2.Add(
                        new Tuple<Rect, XBrush>(
                            ExtendMaximum
                                ? new Rect(new Point(left, topY),
                                    new Point(Canvas.Rect.Right + 1, topY + currHeight))
                                : new Rect(left, topY, dist, currHeight), _maximumBrush));
 
                    if (ExtendMaximum)
                    {
                        labels.Add(new ObjectLabelInfo(j * step, MaximumColor));
                    }
 
                    if (ShowMaximumValue)
                    {
                        var h = Canvas.ChartFont.GetHeight();
 
                        valueRects2.Add(new Tuple<Rect, string>(
                            new Rect(left + 2, topY - h - 2, Math.Max(dist - 4, 1), h),
                            FormatMaximum(item)));
                    }
                }
                else if (valueArea != null && (item.Price == valueArea.Vah || item.Price == valueArea.Val))
                {
                    colorRects2.Add(
                        new Tuple<Rect, XBrush>(
                            ExtendValueArea
                                ? new Rect(new Point(left, topY), new Point(Canvas.Rect.Right + 1, topY + currHeight))
                                : new Rect(left, topY, dist, currHeight), _valueAreaBrush));
 
                    if (ExtendValueArea)
                    {
                        labels.Add(new ObjectLabelInfo(j * step, ValueAreaColor));
                    }
                }
                else if (EnableFilter && item.Trades >= FilterMin &&
                         (FilterMax == 0 || item.Trades <= FilterMax))
                {
                    if (colorRects.Count > 0)
                    {
                        var lastRect = colorRects[colorRects.Count - 1].Item1;
 
                        if ((int)lastRect.Y == (int)topY)
                        {
                            if (width > lastRect.Width)
                            {
                                colorRects[colorRects.Count - 1] =
                                    new Tuple<Rect, XBrush>(new Rect(left, topY, width, lastRect.Height), _filterBrush);
                            }
                        }
                        else
                        {
                            colorRects.Add(new Tuple<Rect, XBrush>(new Rect(left, topY, width, currHeight),
                                _filterBrush));
                        }
                    }
                    else
                    {
                        colorRects.Add(new Tuple<Rect, XBrush>(new Rect(left, topY, width, currHeight), _filterBrush));
                    }
                }
 
                if (ShowValues && height > 7 && item.Trades > 0)
                {
                    valueRects.Add(new Tuple<Rect, string>(new Rect(left + 2, topY, dist, height),
                        symbol.FormatTrades(item.Trades, roundValues, MinimizeValues)));
                }
            }
 
            points.Add(new Point(left, prevY));
 
            visual.FillPolygon(_profileBrush, points.ToArray());
 
            foreach (var colorRect in colorRects)
            {
                visual.FillRectangle(colorRect.Item2, colorRect.Item1);
            }
 
            foreach (var colorRect in colorRects2)
            {
                visual.FillRectangle(colorRect.Item2, colorRect.Item1);
            }
 
            foreach (var valueRect in valueRects)
            {
                visual.DrawString(valueRect.Item2, normalFont, _valuesBrush, valueRect.Item1);
            }
 
            foreach (var valueRect in valueRects2)
            {
                visual.DrawString(valueRect.Item2, Canvas.ChartFont, _valuesBrush, valueRect.Item1, XTextAlignment.Right);
            }
        }
 
        private void DrawDelta(DxVisualQueue visual, IRawCluster profile, IRawClusterValueArea valueArea,
            Point p1, Point p2, ref List<ObjectLabelInfo> labels)
        {
            var colorRects = new List<Tuple<Rect, XBrush>>();
            var colorRectsLeft = new List<Tuple<Rect, XBrush>>();
            var colorRectsRight = new List<Tuple<Rect, XBrush>>();
            var valueLeftRects = new List<Tuple<Rect, string>>();
            var valueRightRects = new List<Tuple<Rect, string>>();
            var valueRects2 = new List<Tuple<Rect, string>>();
 
            var step = DataProvider.Step;
            var symbol = DataProvider.Symbol;
 
            var height = Math.Max(Canvas.StepHeight, 1);
 
            var fontSize = Math.Min(height - 2, 18) * 96 / 72;
 
            fontSize = Math.Min(fontSize, Canvas.ChartFont.Size);
 
            var normalFont = new XFont(Canvas.ChartFont.Name, fontSize);
 
            var dist = p2.X - p1.X + Canvas.ColumnWidth - LineWidth;
 
            var left = p1.X - Canvas.ColumnWidth / 2.0 + Math.Ceiling(LineWidth / 2.0);
 
            if (ExtendProfile)
            {
                if (DrawBack)
                {
                    visual.FillRectangle(_backBrush,
                        new Rect(new Point(left, Canvas.Rect.Top), new Point(left + dist, Canvas.Rect.Bottom)));
                }
 
                if (DrawBorder)
                {
                    visual.DrawLine(_linePen, new Point(left, Canvas.Rect.Top),
                        new Point(left, Canvas.Rect.Bottom));
                    visual.DrawLine(_linePen, new Point(left + dist, Canvas.Rect.Top),
                        new Point(left + dist, Canvas.Rect.Bottom));
                }
            }
 
            if (profile.High - profile.Low > 10000)
            {
                return;
            }
 
            var maxValues = profile.MaxValues;
 
            var roundValues = RoundValues;
 
            var center = left + dist / 2.0;
 
            var prevX = (int)center;
            var prevY = (int)GetY((profile.High + .5) * step);
 
            var pointsRight = new List<Point>
            {
                new Point(prevX, prevY)
            };
 
            for (var j = profile.High; j >= profile.Low; j--)
            {
                var item = profile.GetItem(j) ?? new RawClusterItem(j);
 
                var width = item.Delta > 0
                    ? Math.Min(dist / Math.Max(Math.Abs(maxValues.MinDelta), Math.Abs(maxValues.MaxDelta)) *
                               Math.Abs(item.Delta), dist) / 2.0
                    : 0;
 
                var currX = (int)(center + width);
                var currY = (int)GetY((j - .5) * step);
 
                var currHeight = Math.Max(currY - prevY, height);
 
                if (currY <= prevY && pointsRight.Count > 2)
                {
                    if (currX > prevX)
                    {
                        pointsRight[pointsRight.Count - 2] = new Point(currX, pointsRight[pointsRight.Count - 2].Y);
                        pointsRight[pointsRight.Count - 1] = new Point(currX, pointsRight[pointsRight.Count - 1].Y);
 
                        prevX = currX;
                    }
                }
                else
                {
                    pointsRight.Add(new Point(currX, prevY));
                    pointsRight.Add(new Point(currX, currY));
 
                    prevX = currX;
                }
 
                prevY = currY;
 
                var topY = pointsRight[pointsRight.Count - 2].Y;
 
                if (ShowMaximum && CheckMaximum(item, maxValues))
                {
                    colorRects.Add(
                        new Tuple<Rect, XBrush>(
                            ExtendMaximum
                                ? new Rect(new Point(left, topY),
                                    new Point(Canvas.Rect.Right + 1, topY + currHeight))
                                : new Rect(left, topY, dist, currHeight), _maximumBrush));
 
                    if (ExtendMaximum)
                    {
                        labels.Add(new ObjectLabelInfo(j * step, MaximumColor));
                    }
 
                    if (ShowMaximumValue)
                    {
                        var h = Canvas.ChartFont.GetHeight();
 
                        valueRects2.Add(new Tuple<Rect, string>(
                            new Rect(left + 2, topY - h - 2, Math.Max(dist - 4, 1), h),
                            FormatMaximum(item)));
                    }
                }
                else if (valueArea != null && (item.Price == valueArea.Vah || item.Price == valueArea.Val))
                {
                    colorRects.Add(
                        new Tuple<Rect, XBrush>(
                            ExtendValueArea
                                ? new Rect(new Point(left, topY), new Point(Canvas.Rect.Right + 1, topY + currHeight))
                                : new Rect(left, topY, dist, currHeight), _valueAreaBrush));
 
                    if (ExtendValueArea)
                    {
                        labels.Add(new ObjectLabelInfo(j * step, ValueAreaColor));
                    }
                }
                else if (EnableFilter)
                {
                    if (item.Delta > 0 && item.Delta >= symbol.CorrectSizeFilter(FilterMin) &&
                        (FilterMax == 0 || item.Delta <= symbol.CorrectSizeFilter(FilterMax)))
                    {
                        if (colorRectsRight.Count > 0)
                        {
                            var lastRect = colorRectsRight[colorRectsRight.Count - 1].Item1;
 
                            if ((int)lastRect.Y == (int)topY)
                            {
                                if (width > lastRect.Width)
                                {
                                    colorRectsRight[colorRectsRight.Count - 1] =
                                        new Tuple<Rect, XBrush>(new Rect(center, topY, width, lastRect.Height), _filterBrush);
                                }
                            }
                            else
                            {
                                colorRectsRight.Add(new Tuple<Rect, XBrush>(new Rect(center, topY, width, currHeight),
                                    _filterBrush));
                            }
                        }
                        else
                        {
                            colorRectsRight.Add(new Tuple<Rect, XBrush>(new Rect(center, topY, width, currHeight), _filterBrush));
                        }
                    }
                }
 
                if (ShowValues && height > 7 && item.Delta > 0)
                {
                    valueRightRects.Add(new Tuple<Rect, string>(new Rect(center + 2, topY, dist / 2.0, height),
                        symbol.FormatRawSize(item.Delta, roundValues, MinimizeValues)));
                }
            }
 
            pointsRight.Add(new Point(center, prevY));
 
            prevX = (int)center;
            prevY = (int)GetY((profile.High + .5) * step);
 
            var pointsLeft = new List<Point>
            {
                new Point(prevX, prevY)
            };
 
            for (var j = profile.High; j >= profile.Low; j--)
            {
                var item = profile.GetItem(j) ?? new RawClusterItem(j);
 
                var width = item.Delta < 0
                    ? Math.Min(dist / Math.Max(Math.Abs(maxValues.MinDelta), Math.Abs(maxValues.MaxDelta)) *
                               Math.Abs(item.Delta), dist) / 2.0
                    : 0;
 
                var currX = (int)(center - width);
                var currY = (int)GetY((j - .5) * step);
 
                var currHeight = Math.Max(currY - prevY, height);
 
                if (currY <= prevY && pointsLeft.Count > 2)
                {
                    if (currX < prevX)
                    {
                        pointsLeft[pointsLeft.Count - 2] = new Point(currX, pointsLeft[pointsLeft.Count - 2].Y);
                        pointsLeft[pointsLeft.Count - 1] = new Point(currX, pointsLeft[pointsLeft.Count - 1].Y);
 
                        prevX = currX;
                    }
                }
                else
                {
                    pointsLeft.Add(new Point(currX, prevY));
                    pointsLeft.Add(new Point(currX, currY));
 
                    prevX = currX;
                }
 
                prevY = currY;
 
                var topY = pointsLeft[pointsLeft.Count - 2].Y;
 
                if (ShowMaximum && CheckMaximum(item, maxValues))
                {
                }
                else if (EnableFilter)
                {
                    if (item.Delta < 0 && -item.Delta >= symbol.CorrectSizeFilter(FilterMin) &&
                        (FilterMax == 0 || -item.Delta <= symbol.CorrectSizeFilter(FilterMax)))
                    {
                        if (colorRectsLeft.Count > 0)
                        {
                            var lastRect = colorRectsLeft[colorRectsLeft.Count - 1].Item1;
 
                            if ((int)lastRect.Y == (int)topY)
                            {
                                if (width > lastRect.Width)
                                {
                                    colorRectsLeft[colorRectsLeft.Count - 1] =
                                        new Tuple<Rect, XBrush>(new Rect(center - width, topY, width, lastRect.Height), _filterBrush);
                                }
                            }
                            else
                            {
                                colorRectsLeft.Add(new Tuple<Rect, XBrush>(new Rect(center - width, topY, width, currHeight),
                                    _filterBrush));
                            }
                        }
                        else
                        {
                            colorRectsLeft.Add(new Tuple<Rect, XBrush>(new Rect(center - width, topY, width, currHeight), _filterBrush));
                        }
                    }
                }
 
                if (ShowValues && height > 7 && item.Delta < 0)
                {
                    valueLeftRects.Add(new Tuple<Rect, string>(new Rect(left, topY, dist / 2.0 - 2, height),
                        symbol.FormatRawSize(item.Delta, roundValues, MinimizeValues)));
                }
            }
 
            pointsLeft.Add(new Point(center, prevY));
 
            visual.FillPolygon(_profile2Brush, pointsLeft.ToArray());
            visual.FillPolygon(_profileBrush, pointsRight.ToArray());
 
            foreach (var colorRect in colorRectsLeft)
            {
                visual.FillRectangle(colorRect.Item2, colorRect.Item1);
            }
 
            foreach (var colorRect in colorRectsRight)
            {
                visual.FillRectangle(colorRect.Item2, colorRect.Item1);
            }
 
            foreach (var colorRect in colorRects)
            {
                visual.FillRectangle(colorRect.Item2, colorRect.Item1);
            }
 
            foreach (var valueRect in valueLeftRects)
            {
                visual.DrawString(valueRect.Item2, normalFont, _valuesBrush, valueRect.Item1, XTextAlignment.Right);
            }
 
            foreach (var valueRect in valueRightRects)
            {
                visual.DrawString(valueRect.Item2, normalFont, _valuesBrush, valueRect.Item1);
            }
 
            foreach (var valueRect in valueRects2)
            {
                visual.DrawString(valueRect.Item2, Canvas.ChartFont, _valuesBrush, valueRect.Item1, XTextAlignment.Right);
            }
        }
 
        private void DrawBidAsk(DxVisualQueue visual, IRawCluster profile, IRawClusterValueArea valueArea,
            Point p1, Point p2, ref List<ObjectLabelInfo> labels)
        {
            var colorRects = new List<Tuple<Rect, XBrush>>();
            var colorRectsLeft = new List<Tuple<Rect, XBrush>>();
            var colorRectsRight = new List<Tuple<Rect, XBrush>>();
            var valueLeftRects = new List<Tuple<Rect, string>>();
            var valueRightRects = new List<Tuple<Rect, string>>();
            var valueRects2 = new List<Tuple<Rect, string>>();
 
            var step = DataProvider.Step;
            var symbol = DataProvider.Symbol;
 
            var height = Math.Max(Canvas.StepHeight, 1);
 
            var fontSize = Math.Min(height - 2, 18) * 96 / 72;
 
            fontSize = Math.Min(fontSize, Canvas.ChartFont.Size);
 
            var normalFont = new XFont(Canvas.ChartFont.Name, fontSize);
 
            var dist = p2.X - p1.X + Canvas.ColumnWidth - LineWidth;
 
            var left = p1.X - Canvas.ColumnWidth / 2.0 + Math.Ceiling(LineWidth / 2.0);
 
            if (ExtendProfile)
            {
                if (DrawBack)
                {
                    visual.FillRectangle(_backBrush,
                        new Rect(new Point(left, Canvas.Rect.Top), new Point(left + dist, Canvas.Rect.Bottom)));
                }
 
                if (DrawBorder)
                {
                    visual.DrawLine(_linePen, new Point(left, Canvas.Rect.Top),
                        new Point(left, Canvas.Rect.Bottom));
                    visual.DrawLine(_linePen, new Point(left + dist, Canvas.Rect.Top),
                        new Point(left + dist, Canvas.Rect.Bottom));
                }
            }
 
            if (profile.High - profile.Low > 10000)
            {
                return;
            }
 
            var maxValues = profile.MaxValues;
 
            var roundValues = RoundValues;
 
            var center = Math.Floor(left + dist / 2.0);
 
            var prevX = (int)center;
            var prevY = (int)GetY((profile.High + .5) * step);
 
            var pointsRight = new List<Point>
            {
                new Point(prevX, prevY)
            };
 
            for (var j = profile.High; j >= profile.Low; j--)
            {
                var item = profile.GetItem(j) ?? new RawClusterItem(j);
 
                var askWidth =
                    (int)(Math.Min(dist / Math.Max(maxValues.MaxBid, maxValues.MaxAsk) * item.Ask, dist) / 2.0);
 
                var currX = (int)(center + askWidth);
                var currY = (int)GetY((j - .5) * step);
 
                var currHeight = Math.Max(currY - prevY, height);
 
                if (currY <= prevY && pointsRight.Count > 2)
                {
                    if (currX > prevX)
                    {
                        pointsRight[pointsRight.Count - 2] = new Point(currX, pointsRight[pointsRight.Count - 2].Y);
                        pointsRight[pointsRight.Count - 1] = new Point(currX, pointsRight[pointsRight.Count - 1].Y);
 
                        prevX = currX;
                    }
                }
                else
                {
                    pointsRight.Add(new Point(currX, prevY));
                    pointsRight.Add(new Point(currX, currY));
 
                    prevX = currX;
                }
 
                prevY = currY;
 
                var topY = pointsRight[pointsRight.Count - 2].Y;
 
                if (ShowMaximum && CheckMaximum(item, maxValues))
                {
                    colorRects.Add(
                        new Tuple<Rect, XBrush>(
                            ExtendMaximum
                                ? new Rect(new Point(left, topY),
                                    new Point(Canvas.Rect.Right + 1, topY + currHeight))
                                : new Rect(left, topY, dist, currHeight), _maximumBrush));
 
                    if (ExtendMaximum)
                    {
                        labels.Add(new ObjectLabelInfo(j * step, MaximumColor));
                    }
 
                    if (ShowMaximumValue)
                    {
                        var h = Canvas.ChartFont.GetHeight();
 
                        valueRects2.Add(new Tuple<Rect, string>(
                            new Rect(left + 2, topY - h - 2, Math.Max(dist - 4, 1), h),
                            FormatMaximum(item)));
                    }
                }
                else if (valueArea != null && (item.Price == valueArea.Vah || item.Price == valueArea.Val))
                {
                    colorRects.Add(
                        new Tuple<Rect, XBrush>(
                            ExtendValueArea
                                ? new Rect(new Point(left, topY), new Point(Canvas.Rect.Right + 1, topY + currHeight))
                                : new Rect(left, topY, dist, currHeight), _valueAreaBrush));
 
                    if (ExtendValueArea)
                    {
                        labels.Add(new ObjectLabelInfo(j * step, ValueAreaColor));
                    }
                }
                else if (EnableFilter)
                {
                    if (item.Ask >= symbol.CorrectSizeFilter(FilterMin) &&
                        (FilterMax == 0 || item.Ask <= symbol.CorrectSizeFilter(FilterMax)))
                    {
                        if (colorRectsRight.Count > 0)
                        {
                            var lastRect = colorRectsRight[colorRectsRight.Count - 1].Item1;
 
                            if ((int)lastRect.Y == (int)topY)
                            {
                                if (askWidth > lastRect.Width)
                                {
                                    colorRectsRight[colorRectsRight.Count - 1] =
                                        new Tuple<Rect, XBrush>(new Rect(center, topY, askWidth, lastRect.Height),
                                            _filterBrush);
                                }
                            }
                            else
                            {
                                colorRectsRight.Add(
                                    new Tuple<Rect, XBrush>(new Rect(center, topY, askWidth, currHeight),
                                        _filterBrush));
                            }
                        }
                        else
                        {
                            colorRectsRight.Add(new Tuple<Rect, XBrush>(new Rect(center, topY, askWidth, currHeight),
                                _filterBrush));
                        }
                    }
                }
 
                if (ShowValues && height > 7 && item.Ask > 0)
                {
                    valueRightRects.Add(new Tuple<Rect, string>(new Rect(center + 2, topY, dist / 2.0, height),
                        symbol.FormatRawSize(item.Ask, roundValues, MinimizeValues)));
                }
            }
 
            pointsRight.Add(new Point(center, prevY));
 
            prevX = (int)center;
            prevY = (int)GetY((profile.High + .5) * step);
 
            var pointsLeft = new List<Point>
            {
                new Point(prevX, prevY)
            };
 
            for (var j = profile.High; j >= profile.Low; j--)
            {
                var item = profile.GetItem(j) ?? new RawClusterItem(j);
 
                var bidWidth =
                    (int)(Math.Min(dist / Math.Max(maxValues.MaxBid, maxValues.MaxAsk) * item.Bid, dist) / 2.0);
 
                var currX = (int)(center - bidWidth);
                var currY = (int)GetY((j - .5) * step);
 
                var currHeight = Math.Max(currY - prevY, height);
 
                if (currY <= prevY && pointsLeft.Count > 2)
                {
                    if (currX < prevX)
                    {
                        pointsLeft[pointsLeft.Count - 2] = new Point(currX, pointsLeft[pointsLeft.Count - 2].Y);
                        pointsLeft[pointsLeft.Count - 1] = new Point(currX, pointsLeft[pointsLeft.Count - 1].Y);
 
                        prevX = currX;
                    }
                }
                else
                {
                    pointsLeft.Add(new Point(currX, prevY));
                    pointsLeft.Add(new Point(currX, currY));
 
                    prevX = currX;
                }
 
                prevY = currY;
 
                var topY = pointsLeft[pointsLeft.Count - 2].Y;
 
                if (ShowMaximum && CheckMaximum(item, maxValues))
                {
                }
                else if (EnableFilter)
                {
                    if (item.Bid >= symbol.CorrectSizeFilter(FilterMin) &&
                        (FilterMax == 0 || item.Bid <= symbol.CorrectSizeFilter(FilterMax)))
                    {
                        if (colorRectsLeft.Count > 0)
                        {
                            var lastRect = colorRectsLeft[colorRectsLeft.Count - 1].Item1;
 
                            if ((int)lastRect.Y == (int)topY)
                            {
                                if (bidWidth > lastRect.Width)
                                {
                                    colorRectsLeft[colorRectsLeft.Count - 1] =
                                        new Tuple<Rect, XBrush>(
                                            new Rect(center - bidWidth, topY, bidWidth, lastRect.Height), _filterBrush);
                                }
                            }
                            else
                            {
                                colorRectsLeft.Add(new Tuple<Rect, XBrush>(
                                    new Rect(center - bidWidth, topY, bidWidth, currHeight), _filterBrush));
                            }
                        }
                        else
                        {
                            colorRectsLeft.Add(
                                new Tuple<Rect, XBrush>(new Rect(center - bidWidth, topY, bidWidth, currHeight),
                                    _filterBrush));
                        }
                    }
                }
 
                if (ShowValues && height > 7 && item.Bid > 0)
                {
                    valueLeftRects.Add(new Tuple<Rect, string>(new Rect(left, topY, dist / 2.0 - 2, height),
                        symbol.FormatRawSize(item.Bid, roundValues, MinimizeValues)));
                }
            }
 
            pointsLeft.Add(new Point(center, prevY));
 
            visual.FillPolygon(_profile2Brush, pointsLeft.ToArray());
            visual.FillPolygon(_profileBrush, pointsRight.ToArray());
 
            foreach (var colorRect in colorRectsLeft)
            {
                visual.FillRectangle(colorRect.Item2, colorRect.Item1);
            }
 
            foreach (var colorRect in colorRectsRight)
            {
                visual.FillRectangle(colorRect.Item2, colorRect.Item1);
            }
 
            foreach (var colorRect in colorRects)
            {
                visual.FillRectangle(colorRect.Item2, colorRect.Item1);
            }
 
            foreach (var valueRect in valueLeftRects)
            {
                visual.DrawString(valueRect.Item2, normalFont, _valuesBrush, valueRect.Item1, XTextAlignment.Right);
            }
 
            foreach (var valueRect in valueRightRects)
            {
                visual.DrawString(valueRect.Item2, normalFont, _valuesBrush, valueRect.Item1);
            }
 
            foreach (var valueRect in valueRects2)
            {
                visual.DrawString(valueRect.Item2, Canvas.ChartFont, _valuesBrush, valueRect.Item1, XTextAlignment.Right);
            }
        }
 
        public override void DrawControlPoints(DxVisualQueue visual)
        {
            if (_rectInfo == null)
            {
                return;
            }
 
            DrawControlPoint(visual, _rectInfo.ControlPoint1);
            DrawControlPoint(visual, _rectInfo.ControlPoint2);
 
            DrawControlPoint(visual, _rectInfo.ExtraPoint1);
            DrawControlPoint(visual, _rectInfo.ExtraPoint2);
        }
 
        public override int GetControlPoint(int x, int y)
        {
            if (Canvas == null || _rectInfo == null)
            {
                return -1;
            }
 
            var points = new[] { _rectInfo.ControlPoint1, _rectInfo.ControlPoint2 };
 
            for (var i = 0; i < points.Length; i++)
            {
                var distX = points[i].X - x;
                var distY = points[i].Y - y;
 
                if (distX * distX + distY * distY < 9.0 + PenWidth / 2.0)
                {
                    return i;
                }
            }
 
            return -1;
        }
 
        public override int GetExtraPoint(int x, int y)
        {
            if (Canvas == null || _rectInfo == null)
            {
                return -1;
            }
 
            var points = new[] { _rectInfo.ExtraPoint1, _rectInfo.ExtraPoint2 };
 
            for (var i = 0; i < points.Length; i++)
            {
                var distX = points[i].X - x;
                var distY = points[i].Y - y;
 
                if (distX * distX + distY * distY < 9.0 + PenWidth / 2.0)
                {
                    return i;
                }
            }
 
            return -1;
        }
 
        protected override bool IsObjectInArea()
        {
            return _isObjectInArea;
        }
 
        protected override bool InObject(int x, int y)
        {
            if (_rectInfo == null)
            {
                return false;
            }
 
            return _rectInfo.Rectangle != Rect.Empty && _rectInfo.Rectangle.Contains(x, y);
        }
 
        protected override int GetMinDist(int x, int y)
        {
            var rect = _rectInfo.Rectangle;
 
            var dx = Math.Min(rect.X + rect.Width - x, x - rect.X);
            var dy = Math.Min(rect.Y + rect.Height - y, y - rect.Y);
 
            var result = Math.Min(dx, dy);
 
            return result > 0 ? (int)result : -1;
        }
 
        public override ObjectPoint[] ExtraPoints
        {
            get
            {
                var cp1 = ControlPoints[0];
                var cp2 = ControlPoints[1];
 
                var ep1 = new ObjectPoint(cp2.X, cp1.Y);
                var ep2 = new ObjectPoint(cp1.X, cp2.Y);
 
                var extraPoints = new[] { ep1, ep2 };
 
                return extraPoints;
            }
        }
 
        public override void ExtraPointChanged(int index, ObjectPoint op)
        {
            switch (index)
            {
                case 0:
 
                    ControlPoints[1].X = op.X;
                    ControlPoints[0].Y = op.Y;
 
                    break;
 
                case 1:
 
                    ControlPoints[0].X = op.X;
                    ControlPoints[1].Y = op.Y;
 
                    break;
            }
        }
 
        public override void ApplyTheme(IChartTheme theme)
        {
            base.ApplyTheme(theme);
 
            LineColor = theme.ChartObjectLineColor;
            BackColor = theme.ChartObjectFillColor;
        }
 
        public override void CopyTemplate(ObjectBase objectBase, bool style)
        {
            if (objectBase is VolumeProfileObject obj)
            {
                ProfileType = obj.ProfileType;
                ProfileColor = obj.ProfileColor;
                Profile2Color = obj.Profile2Color;
                ExtendProfile = obj.ExtendProfile;
                ShowCumValue = obj.ShowCumValue;
 
                ShowValues = obj.ShowValues;
                MinimizeValues = obj.MinimizeValues;
                RoundValues = obj.RoundValues;
                ValuesColor = obj.ValuesColor;
 
                MaximumType = obj.MaximumType;
                ShowMaximum = obj.ShowMaximum;
                ShowMaximumValue = obj.ShowMaximumValue;
                ExtendMaximum = obj.ExtendMaximum;
                MaximumColor = obj.MaximumColor;
 
                ShowValueArea = obj.ShowValueArea;
                ExtendValueArea = obj.ExtendValueArea;
                ValueAreaPercent = obj.ValueAreaPercent;
                ValueAreaColor = obj.ValueAreaColor;
 
                EnableFilter = obj.EnableFilter;
                FilterMin = obj.FilterMin;
                FilterMax = obj.FilterMax;
                FilterColor = obj.FilterColor;
 
                DrawBorder = obj.DrawBorder;
                LineColor = obj.LineColor;
                LineWidth = obj.LineWidth;
                LineStyle = obj.LineStyle;
 
                DrawBack = obj.DrawBack;
                BackColor = obj.BackColor;
            }
 
            base.CopyTemplate(objectBase, style);
        }
    }
}
```


---

# 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-graficheskikh-obektov/profil-obyoma.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.
