DOM.cs
DOM.cs
Technical;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Drawing;
using System.Linq;
using System.Runtime.CompilerServices;
using Newtonsoft.Json;
using OFT.Attributes;
using OFT.Localization;
using OFT.Rendering;
using OFT.Rendering.Context;
using OFT.Rendering.Helpers;
using OFT.Rendering.Tools;
using Utils.Common.Logging;
[Category(IndicatorCategories.OrderBook)]
[DisplayName("Depth Of Market")]
[Display(ResourceType = typeof(Strings), Description =
nameof(Strings.DOMDescription))]
[HelpLink("https://help.atas.net/en/support/solutions/articles/72000602367")]
public class DOM : Indicator
{
#region Nested types
#endregion
}
#endregion
#region Static and constants
#endregion
#region Fields
#endregion
#region Properties
[Range(0, 1000)]
[Display(ResourceType = typeof(Strings), Name =
nameof(Strings.CustomPriceLevelsHeight), GroupName = nameof(Strings.Other),
Description = nameof(Strings.CustomPriceLevelsHeightDescription), Order = 310)]
public int PriceLevelsHeight
{
get => _priceLevelsHeight;
set => _priceLevelsHeight = value;
}
#region ctor
public DOM()
: base(true)
{
DrawAbovePrice = true;
DenyToChangePanel = true;
_upScale.IsHidden = _downScale.IsHidden = true;
_upScale.ShowCurrentValue = _downScale.ShowCurrentValue = false;
_upScale.Color = _downScale.Color = Color.Transparent.Convert();
DataSeries[0] = _upScale;
DataSeries.Add(_downScale);
EnableCustomDrawing = true;
SubscribeToDrawingEvents(DrawingLayouts.Final);
UseAutoSize = true;
ProportionVolume = 100;
Width = 200;
RightToLeft = true;
ShowCumulativeValues = true;
Scale = 20;
FilterColors.CollectionChanged += FiltersChanged;
}
#endregion
lock (_locker)
{
var depths = MarketDepthInfo.GetMarketDepthSnapshot();
_mDepth = mDepth;
if (_mDepth.Count == 0)
{
_maxPrice = _minPrice = GetCandle(CurrentBar -
1).Close;
return;
}
ResetColors();
sum = 0m;
foreach (var (price, level) in _mDepth.Where(x =>
x.Value.DataType is MarketDataType.Bid).OrderByDescending(x => x.Key))
{
sum += level.Volume;
_cumulativeBid[price] = sum;
}
}
}
return;
}
if (UseScale)
{
_upScale[CurrentBar - 2] = 0;
_downScale[CurrentBar - 2] = 0;
if (_maxPrice != 0)
_upScale[CurrentBar - 1] = _maxPrice +
InstrumentInfo.TickSize * (_scale + 3);
if (_minPrice != 0)
_downScale[CurrentBar - 1] = _minPrice -
InstrumentInfo.TickSize * (_scale + 3);
}
}
if (chartInfo.PriceChartContainer.TotalBars == -1)
return;
if (CurrentBar <= 0)
return;
lock (_locker)
{
if (_mDepth.Count == 0)
return;
}
var height =
(int)Math.Floor(chartInfo.PriceChartContainer.PriceRowHeight) - 1;
if (PriceLevelsHeight != 0)
height = PriceLevelsHeight - 2;
lock (_locker)
{
if (VisualMode is not Mode.Common)
DrawCumulative(context);
}
lock (_locker)
{
maxVolume = _mDepth.Where(md => md.Key <=
maxVisiblePrice && md.Key >= minVisiblePrice)
.Select(x => x.Value.Volume)
.DefaultIfEmpty(0)
.Max();
}
}
else
maxVolume = ProportionVolume;
decimal currentPrice;
try
{
currentPrice = GetCandle(CurrentBar - 1).Close;
}
catch (Exception e)
{
this.LogDebug("Chart does not contains bars", e);
return;
}
DrawBackGround(context, currentPriceY);
lock (_locker)
{
var stringRects = new List<(string Text, Rectangle
Rect)>();
int y;
if (PriceLevelsHeight == 0)
{
y =
chartInfo.GetYByPrice(priceDepth.Price);
height = Math.Abs(y -
chartInfo.GetYByPrice(priceDepth.Price - instrumentInfo.TickSize)) - 1;
if (height < 1)
height = 1;
}
else
{
height = PriceLevelsHeight - 1;
if (height < 1)
height = 1;
var diff = (priceDepth.Price -
firstPrice) / instrumentInfo.TickSize;
y = currentPriceY - height * ((int)diff +
1) - (int)diff - 15;
}
if (!UseAutoSize)
width = Math.Min(width, Width);
if (priceDepth.Price == _minAsk)
{
var bestRect = new Rectangle(new
Point(chartInfo.Region.Width - Width, y),
new Size(Width, height));
context.FillRectangle(_bestAskBackGround,
bestRect);
}
var x1 = RightToLeft
? chartInfo.Region.Width - width
: chartInfo.Region.Width - Width;
var x2 = x1 + width;
var botY = y + height;
if (!
_filteredColors.TryGetValue(priceDepth.Price, out var fillColor))
fillColor = _askColor;
if (_font.Size > 4)
{
var renderText =
chartInfo.TryGetMinimizedVolumeString(priceDepth.Volume, priceDepth.Price);
var textWidth =
context.MeasureString(renderText, _font).Width + 5;
stringRects.Add((renderText,
textRect));
}
}
else
_asksHistogram.AddPrice(RightToLeft ?
x2 : x1, RightToLeft ? x1 : x2, botY, y - 1);
}
}
int y;
if (PriceLevelsHeight == 0)
{
y =
chartInfo.GetYByPrice(priceDepth.Price);
height = Math.Abs(y -
chartInfo.GetYByPrice(priceDepth.Price - instrumentInfo.TickSize)) - 1;
if (height < 1)
height = 1;
}
else
{
height = PriceLevelsHeight - 1;
if (height < 1)
height = 1;
var diff = (firstPrice -
priceDepth.Price) / instrumentInfo.TickSize;
y = currentPriceY + height * ((int)diff +
spread - 1) + (int)diff - 15;
}
if (y > chartInfo.Region.Bottom)
continue;
if (!UseAutoSize)
width = Math.Min(width, Width);
if (priceDepth.Price == _maxBid)
{
var bestRect = new Rectangle(new
Point(chartInfo.Region.Width - Width, y),
new Size(Width, height));
context.FillRectangle(_bestBidBackGround,
bestRect);
}
var x1 = RightToLeft
? chartInfo.Region.Width - width
: chartInfo.Region.Width - Width;
var x2 = x1 + width;
var botY = y + height;
if (!
_filteredColors.TryGetValue(priceDepth.Price, out var fillColor))
fillColor = _bidColor;
stringRects.Add((renderText,
textRect));
}
context.FillRectangle(fillColor, rect);
}
else
_bidsHistogram.AddPrice(RightToLeft ?
x2 : x1, RightToLeft ? x1 : x2, botY, y - 1);
}
}
if (ShowCumulativeValues)
DrawCumulativeValues(context);
}
}
RedrawChart(_emptyRedrawArg);
}
_filteredColors.Remove(depth.Price);
if (isCumulative)
{
if (depth.DataType is MarketDataType.Bid)
_cumulativeBid.Remove(depth.Price);
else
_cumulativeAsk.Remove(depth.Price);
}
if (depth.Volume != 0)
{
_mDepth[depth.Price] = depth;
_filteredColors[depth.Price] = filterColor.Color;
break;
}
}
else
_mDepth.Remove(depth.Price);
if (_mDepth.Count == 0)
{
if (isCumulative)
{
_cumulativeAsk = new SortedList<decimal, decimal>();
_cumulativeBid = new SortedList<decimal, decimal>();
}
return;
}
if (UseScale || isCumulative)
{
if (depth.Price >= _maxPrice || depth.Volume == 0)
{
if (depth.Price >= _maxPrice && depth.Volume != 0)
_maxPrice = depth.Price;
else if (depth.Price >= _maxPrice && depth.Volume ==
0)
_maxPrice = _mDepth.Keys.LastOrDefault();
if (UseScale)
_upScale[CurrentBar - 1] = _maxPrice +
InstrumentInfo.TickSize * (_scale + 3);
}
if (UseScale)
_downScale[CurrentBar - 1] = _minPrice -
InstrumentInfo.TickSize * (_scale + 3);
}
}
if (depth.Price == _maxVolume.Price)
{
if (depth.Volume >= _maxVolume.Volume)
_maxVolume.Volume = depth.Volume;
else
{
var priceLevel = _mDepth.Values
.OrderByDescending(x => x.Volume)
.First();
_maxVolume.Price = priceLevel.Price;
_maxVolume.Volume = priceLevel.Volume;
}
}
else
{
if (depth.Volume > _maxVolume.Volume)
{
_maxVolume.Price = depth.Price;
_maxVolume.Volume = depth.Volume;
}
}
if (isCumulative)
{
if (depth.DataType is MarketDataType.Ask)
{
var sum = _cumulativeAsk.LastOrDefault(x => x.Key <
depth.Price).Value;
sum += level.Value.Volume;
_cumulativeBid[level.Key] = sum;
}
}
}
}
RedrawChart(_emptyRedrawArg);
}
_bidColor = ChartInfo.ColorsStore.UpCandleColor;
_askColor = ChartInfo.ColorsStore.DownCandleColor;
_textColor = ChartInfo.ColorsStore.FootprintMaximumVolumeTextColor;
CumulativeBidColor = cumulativeBid;
CumulativeAskColor = cumulativeAsk;
}
#endregion
if (totalVolume == 0)
return;
var font = new RenderFont("Arial", 9);
if (askRowWidth > 0)
{
var askRect = new Rectangle(new Point(ChartInfo.Region.Width -
askRowWidth, yRect),
new Size(askRowWidth, _unitedVolumeHeight));
context.FillRectangle(_volumeAskColor, askRect);
context.DrawString(askStr, font, _bidColor, askRect,
_stringLeftFormat);
}
if (bidRowWidth > 0)
{
var bidRect = new Rectangle(new Point(ChartInfo.Region.Width -
maxWidth, yRect),
new Size(bidRowWidth, _unitedVolumeHeight));
context.FillRectangle(_volumeBidColor, bidRect);
context.DrawString(bidStr, font, _askColor, bidRect,
_stringRightFormat);
}
}
var x = RightToLeft
? Container.Region.Width - levelWidth
: Container.Region.Width - Width + levelWidth;
var y1 = ChartInfo.GetYByPrice(price -
InstrumentInfo.TickSize);
var x = RightToLeft
? Container.Region.Width - levelWidth
: Container.Region.Width - Width + levelWidth;
var y1 = ChartInfo.GetYByPrice(price -
InstrumentInfo.TickSize);
_cumulativeHistogram.Draw(context, true);
if (VisualMode is Mode.Cumulative)
{
foreach (var (price, volume) in _cumulativeBid)
DrawText(context, price, volume);
var y = ChartInfo.GetYByPrice(price);
var renderText = ChartInfo.TryGetMinimizedVolumeString(volume,price);
var textWidth = context.MeasureString(renderText, _font).Width + 5;
if (!RightToLeft)
{
textRect = new Rectangle(new Point(ChartInfo.Region.Width -
Width, y),
new Size(textWidth,
(int)ChartInfo.PriceChartContainer.PriceRowHeight));
}
if (_font.Size >= 6)
{
context.DrawString(renderText,
_font,
_textColor,
textRect,
form);
}
}
context.FillRectangle(_askBackGround, fullRect);
context.FillRectangle(_bidBackGround, fullRect);
}
else
{
var spread = (int)((_minAsk - _maxBid) /
InstrumentInfo.TickSize);
var y = priceY - 15;
if (e.OldItems != null)
{
foreach (var item in e.OldItems)
((INotifyPropertyChanged)item).PropertyChanged -=
ItemPropertyChanged;
}
lock(_locker)
_sortedFilters = new
List<FilterColor>(FilterColors.OrderByDescending(x => x.Value));
ResetColors();
}
ResetColors();
}
private void ResetColors()
{
_filteredColors.Clear();
_filteredColors[arg.Price] = filterColor.Color;
break;
}
}
}
return 0;
}
#endregion
}
#endregion
#region Properties
#endregion
#region Events
#endregion
#endregion
}