The code of the Volume Profile graphic object
//------------------------------------------------------------------------------ // // Графический объект VolumeProfile. Copyright (c) 2020 Ilya Smirnov. All rights reserved. // //------------------------------------------------------------------------------ using System; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.Serialization; using System.Windows; using System.Windows.Media; using TigerTrade.Chart.Base; using TigerTrade.Chart.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); } } }