DepthOfMarket
//------------------------------------------------------------------------------
//
// Indicator DepthOfMarket. Copyright (c) 2023 Tiger Trade Capital AG. All rights reserved.
//
//------------------------------------------------------------------------------
using System;
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Media;
using TigerTrade.Chart.Base;
using TigerTrade.Chart.Indicators.Common;
using TigerTrade.Chart.Indicators.Enums;
using TigerTrade.Core.UI.Converters;
using TigerTrade.Dx;
using TigerTrade.Dx.Enums;
namespace TigerTrade.Chart.Indicators.Custom
{
[TypeConverter(typeof(EnumDescriptionTypeConverter))]
[DataContract(Name = "DepthOfMarketScaleAlignment", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
public enum DepthOfMarketScaleAlignment
{
[EnumMember(Value = "Left"), Description("Слева")]
Left,
[EnumMember(Value = "Right"), Description("Справа")]
Right
}
[DataContract(Name = "DepthOfMarketIndicator", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
[Indicator("X_DepthOfMarket", "*DepthOfMarket", true, Type = typeof(DepthOfMarketIndicator))]
internal sealed class DepthOfMarketIndicator : IndicatorBase
{
private int _scaleWidth;
[DataMember(Name = "ScaleWidth")]
[Category("Параметры"), DisplayName("Ширина шкалы")]
public int ScaleWidth
{
get => _scaleWidth;
set
{
value = Math.Max(0, value);
if (value == _scaleWidth)
{
return;
}
_scaleWidth = value;
OnPropertyChanged();
}
}
private IndicatorNullIntParam _scaleVolumeParam;
[DataMember(Name = "ScaleVolumeParam")]
public IndicatorNullIntParam ScaleVolumeParam
{
get => _scaleVolumeParam ?? (_scaleVolumeParam = new IndicatorNullIntParam(null));
set => _scaleVolumeParam = value;
}
[DefaultValue(null)]
[Category("Параметры"), DisplayName("Объём шкалы")]
public int? ScaleVolume
{
get => ScaleVolumeParam.Get(SettingsShortKey);
set
{
if (!ScaleVolumeParam.Set(SettingsShortKey, value, 1))
{
return;
}
OnPropertyChanged();
}
}
private DepthOfMarketScaleAlignment _scaleAlignment;
[DataMember(Name = "DepthOfMarketScaleAlignment")]
[Category("Параметры"), DisplayName("Расположение шкалы")]
public DepthOfMarketScaleAlignment ScaleAlignment
{
get => _scaleAlignment;
set
{
if (value == _scaleAlignment)
{
return;
}
_scaleAlignment = value;
OnPropertyChanged();
}
}
private bool _addMargins;
[DataMember(Name = "AddMargins")]
[Category("Параметры"), DisplayName("Добавить отступы")]
public bool AddMargins
{
get => _addMargins;
set
{
if (value == _addMargins)
{
return;
}
_addMargins = 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 IndicatorIntParam _roundValueParam;
[DataMember(Name = "RoundValueParam")]
public IndicatorIntParam RoundValuesParam
{
get => _roundValueParam ?? (_roundValueParam = new IndicatorIntParam(0));
set => _roundValueParam = value;
}
[DefaultValue(0)]
[Category("Параметры"), DisplayName("Округлять значения")]
public int RoundValues
{
get => RoundValuesParam.Get(SettingsLongKey);
set
{
if (!RoundValuesParam.Set(SettingsLongKey, value, -4, 4))
{
return;
}
OnPropertyChanged();
}
}
private XBrush _bidQuoteBrush;
private XColor _quoteBuyColor;
[DataMember(Name = "BidQuoteColor")]
[Category("Стиль"), DisplayName("Заявка на покупку")]
public XColor BidQuoteColor
{
get => _quoteBuyColor;
set
{
if (_quoteBuyColor == value)
{
return;
}
_quoteBuyColor = value;
_bidQuoteBrush = new XBrush(value);
OnPropertyChanged();
}
}
private XBrush _bestBidQuoteBrush;
private XColor _quoteBestBuyColor;
[DataMember(Name = "BestBidQuoteColor")]
[Category("Стиль"), DisplayName("Лучшая заявка на покупку")]
public XColor BestBidQuoteColor
{
get => _quoteBestBuyColor;
set
{
if (_quoteBestBuyColor == value)
{
return;
}
_quoteBestBuyColor = value;
_bestBidQuoteBrush = new XBrush(value);
OnPropertyChanged();
}
}
private XBrush _askQuoteBrush;
private XColor _quoteSellColor;
[DataMember(Name = "AskQuoteColor")]
[Category("Стиль"), DisplayName("Заявка на продажу")]
public XColor AskQuoteColor
{
get => _quoteSellColor;
set
{
if (_quoteSellColor == value)
{
return;
}
_quoteSellColor = value;
_askQuoteBrush = new XBrush(value);
OnPropertyChanged();
}
}
private XBrush _bestAskQuoteBrush;
private XColor _quoteBestSellColor;
[DataMember(Name = "BestAskQuoteColor")]
[Category("Стиль"), DisplayName("Лучшая заявка на прдажу")]
public XColor BestAskQuoteColor
{
get => _quoteBestSellColor;
set
{
if (_quoteBestSellColor == value)
{
return;
}
_quoteBestSellColor = value;
_bestAskQuoteBrush = new XBrush(value);
OnPropertyChanged();
}
}
private XBrush _emptyQuoteBrush;
private XColor _quoteEmptyColor;
[Browsable(false)]
[DataMember(Name = "EmptyQuoteColor")]
[Category("Стиль"), DisplayName("Цена без заявок")]
public XColor EmptyQuoteColor
{
get => _quoteEmptyColor;
set
{
if (_quoteEmptyColor == value)
{
return;
}
_quoteEmptyColor = value;
_emptyQuoteBrush = new XBrush(value);
OnPropertyChanged();
}
}
private XBrush _bidVolumeBrush;
private XColor _quoteBuyVolumeColor;
[DataMember(Name = "BidVolumeColor")]
[Category("Стиль"), DisplayName("Шкала покупок")]
public XColor BidVolumeColor
{
get => _quoteBuyVolumeColor;
set
{
if (_quoteBuyVolumeColor == value)
{
return;
}
_quoteBuyVolumeColor = value;
_bidVolumeBrush = new XBrush(value);
OnPropertyChanged();
}
}
private XBrush _askVolumeBrush;
private XColor _quoteSellVolumeColor;
[DataMember(Name = "AskVolumeColor")]
[Category("Стиль"), DisplayName("Шкала продаж")]
public XColor AskVolumeColor
{
get => _quoteSellVolumeColor;
set
{
if (_quoteSellVolumeColor == value)
{
return;
}
_quoteSellVolumeColor = value;
_askVolumeBrush = new XBrush(value);
OnPropertyChanged();
}
}
private XBrush _textBrush;
private XColor _textColor;
[DataMember(Name = "TextColor")]
[Category("Стиль"), DisplayName("Цвет текста")]
public XColor TextColor
{
get => _textColor;
set
{
if (_textColor == value)
{
return;
}
_textColor = value;
_textBrush = new XBrush(value);
OnPropertyChanged();
}
}
private XBrush _backBrush;
private XColor _backColor;
[DataMember(Name = "BackColor")]
[Category("Стиль"), DisplayName("Цвет фона")]
public XColor BackColor
{
get => _backColor;
set
{
if (_backColor == value)
{
return;
}
_backColor = value;
_backBrush = new XBrush(value);
OnPropertyChanged();
}
}
[Browsable(false)]
public override bool ShowIndicatorValues => false;
[Browsable(false)]
public override bool ShowIndicatorLabels => false;
[Browsable(false)]
public override IndicatorCalculation Calculation => IndicatorCalculation.OnEachTick;
public DepthOfMarketIndicator()
{
ShowIndicatorTitle = false;
ScaleWidth = 0;
ScaleAlignment = DepthOfMarketScaleAlignment.Right;
AddMargins = false;
ShowValues = true;
MinimizeValues = false;
BidQuoteColor = Colors.Transparent;
BestBidQuoteColor = Colors.Transparent;
AskQuoteColor = Colors.Transparent;
BestAskQuoteColor = Colors.Transparent;
EmptyQuoteColor = Colors.Transparent;
BidVolumeColor = Color.FromArgb(255, 30, 144, 255);
AskVolumeColor = Color.FromArgb(255, 178, 34, 34);
TextColor = Colors.White;
BackColor = Colors.Transparent;
}
protected override void Execute()
{
}
public override void Render(DxVisualQueue visual)
{
var dp = DataProvider;
var step = dp.Step;
var symbol = dp.Symbol;
var md = dp.GetRawMarketDepth();
var r = Canvas.Rect;
var maxWidth = ScaleWidth > 0 ? ScaleWidth : r.Width * 0.1;
var maxVolume = ScaleVolume > 0 ? symbol.CorrectSizeFilter(ScaleVolume.Value) : md.MaxSize;
if (maxWidth < 5 || maxVolume < 1)
{
return;
}
var heightCorrect = AddMargins ? -1 : 0;
var height = Math.Max(GetY(0.0) - GetY(step), 1);
var fontSize = Math.Min(height + heightCorrect - 2, 18) * 96.0 / 72.0;
fontSize = Math.Min(fontSize, Canvas.ChartFont.Size);
var textFont = new XFont(Canvas.ChartFont.Name, fontSize);
visual.FillRectangle(_backBrush, new Rect(r.Right - maxWidth, r.Y, maxWidth, r.Height));
var asksY1 = (int)GetY(md.MaxAskPrice * step + step / 2.0);
var asksY2 = (int)GetY(md.MinAskPrice * step + step / 2.0);
visual.FillRectangle(_askQuoteBrush,
new Rect(new Point(r.Right - maxWidth, asksY1), new Point(r.Right, asksY2)));
var bidsY1 = (int)GetY(md.MaxBidPrice * step - step / 2.0);
var bidsY2 = (int)GetY(md.MinBidPrice * step - step / 2.0);
visual.FillRectangle(_bidQuoteBrush,
new Rect(new Point(r.Right - maxWidth, bidsY1), new Point(r.Right, bidsY2)));
var prevY = (int)GetY(md.MaxAskPrice * step + step / 2.0);
var maxPrice = Math.Min((long)Math.Round(Canvas.GetValue(r.Top) / step), md.MaxAskPrice);
var minPrice = Math.Max((long)Math.Round(Canvas.GetValue(r.Bottom) / step), md.MinAskPrice);
if (maxPrice - minPrice > 5000)
{
return;
}
var roundValues = RoundValues;
for (var price = maxPrice; price >= minPrice; price--)
{
var currY = (int)GetY(price * step - step / 2.0);
var currHeight = Math.Max(currY - prevY, 1);
var y = prevY;
prevY = currY;
if (!md.AskQuotes.ContainsKey(price) || currY < r.Top)
{
continue;
}
if (price == md.MinAskPrice)
{
var quoteRect = new Rect(new Point(r.Right - maxWidth, y), new Point(r.Right, y + currHeight));
visual.FillRectangle(_bestAskQuoteBrush, quoteRect);
}
var volume = md.AskQuotes[price];
var text = symbol.FormatRawSize(volume, roundValues, MinimizeValues);
var textWidth = textFont.GetWidth(text);
var width = (int)Math.Min(volume * maxWidth / maxVolume, maxWidth);
if (ShowValues && height > 8)
{
width = (int)Math.Min(Math.Max(width, textWidth), maxWidth);
}
var volumeRect = ScaleAlignment == DepthOfMarketScaleAlignment.Right
? new Rect(new Point(r.Right - width, y),
new Point(r.Right, y + Math.Max(currHeight + heightCorrect, 1)))
: new Rect(new Point(r.Right - maxWidth, y),
new Point(r.Right - (maxWidth - width), y + Math.Max(currHeight + heightCorrect, 1)));
visual.FillRectangle(_askVolumeBrush, volumeRect);
if (ShowValues && height > 8)
{
visual.DrawString(text, textFont, _textBrush, volumeRect,
ScaleAlignment == DepthOfMarketScaleAlignment.Right ? XTextAlignment.Right : XTextAlignment.Left);
}
}
prevY = (int)GetY(md.MaxBidPrice * step + step / 2.0);
maxPrice = Math.Min((long)Math.Round(Canvas.GetValue(r.Top) / step), md.MaxBidPrice);
minPrice = Math.Max((long)Math.Round(Canvas.GetValue(r.Bottom) / step), md.MinBidPrice);
if (maxPrice - minPrice > 5000)
{
return;
}
for (var price = maxPrice; price >= minPrice; price--)
{
var currY = (int)GetY(price * step - step / 2.0);
var currHeight = Math.Max((currY - prevY), 1);
var y = prevY;
prevY = currY;
if (!md.BidQuotes.ContainsKey(price) || y > r.Bottom)
{
continue;
}
if (price == md.MaxBidPrice)
{
var quoteRect = new Rect(new Point(r.Right - maxWidth, y), new Point(r.Right, y + currHeight));
visual.FillRectangle(_bestBidQuoteBrush, quoteRect);
}
var volume = md.BidQuotes[price];
var text = symbol.FormatRawSize(volume, roundValues, MinimizeValues);
var textWidth = textFont.GetWidth(text);
var width = (int)Math.Min(volume * maxWidth / maxVolume, maxWidth);
if (ShowValues && height > 8)
{
width = (int)Math.Min(Math.Max(width, textWidth), maxWidth);
}
var volumeRect = ScaleAlignment == DepthOfMarketScaleAlignment.Right
? new Rect(new Point(r.Right - width, y),
new Point(r.Right, y + Math.Max(currHeight + heightCorrect, 1)))
: new Rect(new Point(r.Right - maxWidth, y),
new Point(r.Right - (maxWidth - width), y + Math.Max(currHeight + heightCorrect, 1)));
visual.FillRectangle(_bidVolumeBrush, volumeRect);
if (ShowValues && height > 8)
{
visual.DrawString(text, textFont, _textBrush, volumeRect,
ScaleAlignment == DepthOfMarketScaleAlignment.Right ? XTextAlignment.Right : XTextAlignment.Left);
}
}
}
public override void ApplyColors(IChartTheme theme)
{
BidVolumeColor = theme.PaletteColor6;
AskVolumeColor = theme.PaletteColor7;
TextColor = theme.ChartFontColor;
base.ApplyColors(theme);
}
public override void CopyTemplate(IndicatorBase indicator, bool style)
{
var i = (DepthOfMarketIndicator)indicator;
ScaleWidth = i.ScaleWidth;
ScaleAlignment = i.ScaleAlignment;
AddMargins = i.AddMargins;
ShowValues = i.ShowValues;
MinimizeValues = i.MinimizeValues;
ScaleVolumeParam.Copy(i.ScaleVolumeParam);
RoundValuesParam.Copy(i.RoundValuesParam);
BidQuoteColor = i.BidQuoteColor;
BestBidQuoteColor = i.BestBidQuoteColor;
AskQuoteColor = i.AskQuoteColor;
BestAskQuoteColor = i.BestAskQuoteColor;
EmptyQuoteColor = i.EmptyQuoteColor;
BidVolumeColor = i.BidVolumeColor;
AskVolumeColor = i.AskVolumeColor;
TextColor = i.TextColor;
BackColor = i.BackColor;
OnPropertyChanged(nameof(ScaleVolume));
base.CopyTemplate(indicator, style);
}
}
}
Last updated