Cluster Statistic
//------------------------------------------------------------------------------
//
// Indicator ClusterStatistic. Copyright (c) 2023 Tiger Trade Capital AG. All rights reserved.
//
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.Serialization;
using System.Windows;
using TigerTrade.Chart.Base.Enums;
using TigerTrade.Chart.Indicators.Common;
using TigerTrade.Chart.Indicators.Enums;
using TigerTrade.Core.UI.Converters;
using TigerTrade.Core.Utils.Time;
using TigerTrade.Dx;
namespace TigerTrade.Chart.Indicators.Custom
{
[TypeConverter(typeof(EnumDescriptionTypeConverter))]
[DataContract(Name = "ClusterStatisticPeriodType", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
public enum ClusterStatisticPeriodType
{
[EnumMember(Value = "Day"), Description("День")]
Day,
[EnumMember(Value = "Week"), Description("Неделя")]
Week,
[EnumMember(Value = "Month"), Description("Месяц")]
Month,
[EnumMember(Value = "VisibleBars"), Description("Видимые бары")]
VisibleBars,
[EnumMember(Value = "AllBars"), Description("Все бары")]
AllBars
}
[DataContract(Name = "ClusterStatisticIndicator", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
[Indicator("X_ClusterStatistic", "*ClusterStatistic", false, Type = typeof(ClusterStatisticIndicator))]
internal sealed class ClusterStatisticIndicator : IndicatorBase
{
private ClusterStatisticPeriodType _period;
[DataMember(Name = "Period")]
[Category("Параметры"), DisplayName("Период")]
public ClusterStatisticPeriodType Period
{
get => _period;
set
{
if (value == _period)
{
return;
}
_period = 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 _roundValuesParam;
[DataMember(Name = "RoundValueParam")]
public IndicatorIntParam RoundValuesParam
{
get => _roundValuesParam ?? (_roundValuesParam = new IndicatorIntParam(0));
set => _roundValuesParam = value;
}
[DefaultValue(0)]
[Category("Параметры"), DisplayName("Округлять значения")]
public int RoundValues
{
get => RoundValuesParam.Get(SettingsLongKey);
set
{
if (!RoundValuesParam.Set(SettingsLongKey, value, -4, 4))
{
return;
}
OnPropertyChanged();
}
}
private bool _ask;
[DataMember(Name = "ShowAsk")]
[Category("Вывод данных"), DisplayName("Ask")]
public bool ShowAsk
{
get => _ask;
set
{
if (value == _ask)
{
return;
}
_ask = value;
OnPropertyChanged();
}
}
private bool _bid;
[DataMember(Name = "ShowBid")]
[Category("Вывод данных"), DisplayName("Bid")]
public bool ShowBid
{
get => _bid;
set
{
if (value == _bid)
{
return;
}
_bid = value;
OnPropertyChanged();
}
}
private bool _delta;
[DataMember(Name = "ShowDelta")]
[Category("Вывод данных"), DisplayName("Delta")]
public bool ShowDelta
{
get => _delta;
set
{
if (value == _delta)
{
return;
}
_delta = value;
OnPropertyChanged();
}
}
private bool _volume;
[DataMember(Name = "ShowVolume")]
[Category("Вывод данных"), DisplayName("Volume")]
public bool ShowVolume
{
get => _volume;
set
{
if (value == _volume)
{
return;
}
_volume = value;
OnPropertyChanged();
}
}
private bool _trades;
[DataMember(Name = "ShowTrades")]
[Category("Вывод данных"), DisplayName("Trades")]
public bool ShowTrades
{
get => _trades;
set
{
if (value == _trades)
{
return;
}
_trades = 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 ClusterStatisticIndicator()
{
ShowIndicatorTitle = false;
Period = ClusterStatisticPeriodType.Day;
MinimizeValues = false;
ShowAsk = true;
ShowBid = true;
ShowDelta = true;
ShowVolume = true;
ShowTrades = true;
}
protected override void Execute()
{
}
public override void Render(DxVisualQueue visual)
{
var labels = new List<string>();
if (ShowVolume)
{
labels.Add("Volume");
}
if (ShowTrades)
{
labels.Add("Trades");
}
if (ShowDelta)
{
labels.Add("Delta");
}
if (ShowBid)
{
labels.Add("Bid");
}
if (ShowAsk)
{
labels.Add("Ask");
}
if (labels.Count == 0)
{
return;
}
var symbol = DataProvider.Symbol;
var linePen = new XPen(new XBrush(Canvas.Theme.ChartAxisColor), 1);
var lastSequence = -1;
var maxVolume = 0m;
var maxTrades = 0L;
var maxDelta = 0m;
var updateMaxValues = true;
switch (Period)
{
case ClusterStatisticPeriodType.AllBars:
{
updateMaxValues = false;
var count = DataProvider.Count;
for (var i = 0; i < count; i++)
{
var cluster = DataProvider.GetCluster(i);
if (cluster == null)
{
continue;
}
var maxValues = cluster.MaxValues;
maxVolume = Math.Max(maxVolume, cluster.Volume);
maxTrades = Math.Max(maxTrades, cluster.Trades);
maxDelta = Math.Max(maxDelta, Math.Max(maxValues.MaxDelta, -maxValues.MinDelta));
}
break;
}
case ClusterStatisticPeriodType.VisibleBars:
{
updateMaxValues = false;
for (var i = 0; i < Canvas.Count; i++)
{
var cluster = DataProvider.GetCluster(Canvas.GetIndex(i));
if (cluster == null)
{
continue;
}
var maxValues = cluster.MaxValues;
maxVolume = Math.Max(maxVolume, cluster.Volume);
maxTrades = Math.Max(maxTrades, cluster.Trades);
maxDelta = Math.Max(maxDelta, Math.Max(maxValues.MaxDelta, -maxValues.MinDelta));
}
break;
}
}
var baseHeight = (int)Canvas.ChartFont.GetHeight() + 4;
var height = labels.Count * baseHeight + 2;
var prevLeft = 0.0;
var columnWidth = Canvas.ColumnWidth;
var r = Canvas.Rect;
var roundValues = RoundValues;
var timeOffset = TimeHelper.GetSessionOffset(DataProvider.Symbol.Exchange);
for (var i = 0; i < Canvas.Count; i++)
{
var x = Canvas.GetXX(i);
var left = x - columnWidth * .5;
if (i == 0)
{
prevLeft = left + columnWidth;
var backRect = new Rect(new Point(r.Left, r.Bottom - baseHeight*labels.Count - 2),
new Point(prevLeft, r.Bottom));
visual.FillRectangle(XBrush.White, backRect);
visual.FillRectangle(Canvas.Theme.ChartBackBrush, backRect);
for (var j = 1; j <= labels.Count; j++)
{
var h = baseHeight * j + 2;
visual.DrawLine(linePen,
new Point(r.Left, r.Bottom - h),
new Point(x + columnWidth * .5, r.Bottom - h));
}
}
visual.DrawLine(linePen,
new Point(left + columnWidth, r.Bottom - height),
new Point(left + columnWidth, r.Bottom));
var index = Canvas.GetIndex(i);
var cluster = DataProvider.GetCluster(index);
if (cluster == null)
{
continue;
}
if (updateMaxValues)
{
var sequence = 1;
switch (Period)
{
case ClusterStatisticPeriodType.Day:
sequence = DataProvider.Period.GetSequence(ChartPeriodType.Day, 1, cluster.Time, timeOffset);
break;
case ClusterStatisticPeriodType.Week:
sequence = DataProvider.Period.GetSequence(ChartPeriodType.Week, 1, cluster.Time, timeOffset);
break;
case ClusterStatisticPeriodType.Month:
sequence = DataProvider.Period.GetSequence(ChartPeriodType.Month, 1, cluster.Time, timeOffset);
break;
}
if (sequence != lastSequence)
{
lastSequence = sequence;
maxVolume = 0;
maxTrades = 0;
maxDelta = 0;
for (var j = index; j >= 0; j--)
{
var cl = DataProvider.GetCluster(j);
if (cl == null)
{
continue;
}
var newSequence = 1;
switch (Period)
{
case ClusterStatisticPeriodType.Day:
newSequence = DataProvider.Period.GetSequence(ChartPeriodType.Day, 1, cl.Time, timeOffset);
break;
case ClusterStatisticPeriodType.Week:
newSequence = DataProvider.Period.GetSequence(ChartPeriodType.Week, 1, cl.Time, timeOffset);
break;
case ClusterStatisticPeriodType.Month:
newSequence = DataProvider.Period.GetSequence(ChartPeriodType.Month, 1, cl.Time, timeOffset);
break;
}
if (newSequence != sequence)
{
break;
}
var maxValues = cl.MaxValues;
maxVolume = Math.Max(maxVolume, cl.Volume);
maxTrades = Math.Max(maxTrades, cl.Trades);
maxDelta = Math.Max(maxDelta, Math.Max(maxValues.MaxDelta, -maxValues.MinDelta));
}
}
}
var deltaBrush = new XBrush(
(cluster.Delta > 0
? Canvas.Theme.ClusterDeltaPlusColor
: Canvas.Theme.ClusterDeltaMinusColor).ChangeOpacity(Math.Abs(cluster.Delta), maxDelta));
var volumeBrush = new XBrush(Canvas.Theme.ClusterVolumeColor.ChangeOpacity(cluster.Volume, maxVolume));
var tradesBrush = new XBrush(Canvas.Theme.ClusterTradesColor.ChangeOpacity(cluster.Trades, maxTrades));
var textBrush = new XBrush(Canvas.Theme.ClusterTextColor);
for (var j = 1; j <= labels.Count; j++)
{
var label = labels[j - 1];
var h = baseHeight * j + 2;
var cellRect = new Rect(new Point((int)left, r.Bottom - h + 1),
new Point((int)prevLeft, r.Bottom - (h - baseHeight) + (j == 1 ? 2 : 0)));
var cellTextRect = new Rect(new Point(left + 2, r.Bottom - h),
new Point(left + columnWidth - 2, r.Bottom - (h - baseHeight)));
var value = "";
switch (label)
{
case "Volume":
value = symbol.FormatSize(cluster.Volume, roundValues, MinimizeValues);
break;
case "Trades":
value = symbol.FormatTrades(cluster.Trades, roundValues, MinimizeValues);
break;
case "Delta":
value = symbol.FormatSize(cluster.Delta, roundValues, MinimizeValues);
break;
case "Bid":
value = symbol.FormatSize(cluster.Bid, roundValues, MinimizeValues);
break;
case "Ask":
value = symbol.FormatSize(cluster.Ask, roundValues, MinimizeValues);
break;
}
if (!string.IsNullOrEmpty(value))
{
visual.FillRectangle(
label == "Volume" ? volumeBrush : (label == "Trades" ? tradesBrush : deltaBrush), cellRect);
visual.DrawString(value, Canvas.ChartFont, textBrush, cellTextRect);
}
}
prevLeft = left;
}
// labels
var labelMaxWidth = labels.Aggregate(0,
(current, label) => (int)Math.Max(current, Canvas.ChartFont.GetWidth(label))) + 5;
visual.FillRectangle(Canvas.Theme.ChartBackBrush,
new Rect(new Point(r.Left, r.Bottom - height), new Point(r.Left + labelMaxWidth, r.Bottom)));
visual.DrawLine(linePen,
new Point(r.Left + labelMaxWidth, r.Bottom - height),
new Point(r.Left + labelMaxWidth, r.Bottom));
var labelHeight = 2;
foreach (var label in labels)
{
labelHeight += baseHeight;
visual.DrawLine(linePen, new Point(r.Left, r.Bottom - labelHeight),
new Point(r.Left + labelMaxWidth, r.Bottom - labelHeight));
var labelTextRect = new Rect(new Point(r.Left + 2, r.Bottom - labelHeight),
new Point(r.Left + labelMaxWidth - 2, r.Bottom - (labelHeight - baseHeight)));
visual.DrawString(label, Canvas.ChartFont, Canvas.Theme.ChartFontBrush, labelTextRect);
}
}
public override void CopyTemplate(IndicatorBase indicator, bool style)
{
var i = (ClusterStatisticIndicator)indicator;
Period = i.Period;
MinimizeValues = i.MinimizeValues;
RoundValuesParam.Copy(i.RoundValuesParam);
ShowAsk = i.ShowAsk;
ShowBid = i.ShowBid;
ShowDelta = i.ShowDelta;
ShowVolume = i.ShowVolume;
ShowTrades = i.ShowTrades;
base.CopyTemplate(indicator, style);
}
}
}
Last updated