Bar Timer
//------------------------------------------------------------------------------
//
// Индикатор BarTimer. 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.Alerts;
using TigerTrade.Chart.Base;
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;
using TigerTrade.Dx.Enums;
namespace TigerTrade.Chart.Indicators.Custom
{
[TypeConverter(typeof(EnumDescriptionTypeConverter))]
[DataContract(Name = "BarTimerLocation", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
public enum BarTimerLocation
{
[EnumMember(Value = "LeftTop"), Description("Слева сверху")]
LeftTop,
[EnumMember(Value = "LeftMiddle"), Description("Слева посередине")]
LeftMiddle,
[EnumMember(Value = "LeftBottom"), Description("Слева снизу")]
LeftBottom,
[EnumMember(Value = "CenterTop"), Description("По центру сверху")]
CenterTop,
[EnumMember(Value = "CenterMiddle"), Description("По центру посередине")]
CenterMiddle,
[EnumMember(Value = "CenterBottom"), Description("По центру снизу")]
CenterBottom,
[EnumMember(Value = "RightTop"), Description("Справа сверху")]
RightTop,
[EnumMember(Value = "RightMiddle"), Description("Справа посередине")]
RightMiddle,
[EnumMember(Value = "RightBottom"), Description("Справа снизу")]
RightBottom,
}
[DataContract(Name = "BarTimerIndicator", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
[Indicator("X_BarTimer", "*BarTimer", true, Type = typeof(BarTimerIndicator))]
internal sealed class BarTimerIndicator : IndicatorBase
{
private BarTimerLocation _location;
[DataMember(Name = "Location"), DefaultValue(BarTimerLocation.RightBottom)]
[Category("Параметры"), DisplayName("Расположение")]
public BarTimerLocation Location
{
get => _location;
set
{
if (value == _location)
{
return;
}
_location = value;
OnPropertyChanged();
}
}
private int _offsetX;
[DataMember(Name = "OffsetX")]
[Category("Параметры"), DisplayName("Отступ по X")]
public int OffsetX
{
get => _offsetX;
set
{
value = Math.Max(0, Math.Min(1000, value));
if (value == _offsetX)
{
return;
}
_offsetX = value;
OnPropertyChanged();
}
}
private int _offsetY;
[DataMember(Name = "OffsetY")]
[Category("Параметры"), DisplayName("Отступ по Y")]
public int OffsetY
{
get => _offsetY;
set
{
value = Math.Max(0, Math.Min(1000, value));
if (value == _offsetY)
{
return;
}
_offsetY = value;
OnPropertyChanged();
}
}
private ChartAlertSettings _alert;
[DataMember(Name = "Alert"), Browsable(true)]
[Category("Параметры"), DisplayName("Оповещение")]
public ChartAlertSettings Alert
{
get => _alert ?? (_alert = new ChartAlertSettings());
set
{
if (Equals(value, _alert))
{
return;
}
_alert = 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();
}
}
private XBrush _borderBrush;
private XPen _borderPen;
private XColor _borderColor;
[DataMember(Name = "BorderColor")]
[Category("Стиль"), DisplayName("Цвет границы")]
public XColor BorderColor
{
get => _borderColor;
set
{
if (value == _borderColor)
{
return;
}
_borderColor = value;
_borderBrush = new XBrush(_borderColor);
_borderPen = new XPen(_borderBrush, 1);
OnPropertyChanged();
}
}
private XBrush _textBrush;
private XColor _textColor;
[DataMember(Name = "TextColor")]
[Category("Стиль"), DisplayName("Цвет текста")]
public XColor TextColor
{
get => _textColor;
set
{
if (value == _textColor)
{
return;
}
_textColor = value;
_textBrush = new XBrush(_textColor);
OnPropertyChanged();
}
}
private int _textSize;
[DataMember(Name = "TextSize")]
[Category("Стиль"), DisplayName("Размер текста")]
public int TextSize
{
get => _textSize;
set
{
value = Math.Max(8, Math.Min(50, value));
if (value == _textSize)
{
return;
}
_textSize = value;
OnPropertyChanged();
}
}
[Browsable(false)]
public override bool ShowIndicatorValues => false;
[Browsable(false)]
public override bool ShowIndicatorLabels => false;
[Browsable(false)]
public override IndicatorCalculation Calculation => IndicatorCalculation.OnEachTick;
private DateTime _lastDateTime;
private long _lastDiff = long.MinValue;
public BarTimerIndicator()
{
ShowIndicatorTitle = false;
Location = BarTimerLocation.RightBottom;
OffsetX = 10;
OffsetY = 10;
BackColor = Colors.Black;
BorderColor = Colors.Silver;
TextColor = Colors.White;
TextSize = 16;
}
protected override void Execute()
{
if (ClearData)
{
_lastDiff = long.MinValue;
}
_lastDateTime = DateTime.Now;
}
public override void Render(DxVisualQueue visual)
{
var lastCluster = DataProvider.GetCluster(DataProvider.Count - 1);
if (lastCluster == null)
{
return;
}
var period = DataProvider.Period;
var alert = false;
string text;
switch (period.Type)
{
case ChartPeriodType.Second:
case ChartPeriodType.Minute:
case ChartPeriodType.Hour:
string format;
switch (period.Type)
{
case ChartPeriodType.Second:
format = @"ss";
break;
case ChartPeriodType.Minute:
format = @"mm\:ss";
break;
default:
format = @"hh\:mm\:ss";
break;
}
var interval = GetTimeSeconds();
var closeTime = lastCluster.Time.AddSeconds(interval);
var currentTime = TimeHelper.GetCurrTime(DataProvider.Symbol.Exchange);
while (closeTime < currentTime)
{
closeTime = closeTime.AddSeconds(interval);
}
var timeDiff = closeTime - currentTime;
if (_lastDiff != long.MinValue && timeDiff > TimeSpan.FromTicks(_lastDiff))
{
alert = true;
}
_lastDiff = timeDiff.Ticks;
text = timeDiff.ToString(format);
break;
case ChartPeriodType.Day:
case ChartPeriodType.Week:
case ChartPeriodType.Month:
case ChartPeriodType.Year:
// not working
return;
case ChartPeriodType.Tick:
var ticksDiff = period.Interval - lastCluster.Trades;
if (_lastDiff != long.MinValue && ticksDiff > _lastDiff)
{
alert = true;
}
_lastDiff = ticksDiff;
text = (ticksDiff > 0 ? ticksDiff : period.Interval).ToString("N0");
break;
case ChartPeriodType.Volume:
var volumeDiff = period.Interval - (long)lastCluster.Volume;
if (_lastDiff != long.MinValue && volumeDiff > _lastDiff)
{
alert = true;
}
_lastDiff = volumeDiff;
text = (volumeDiff > 0 ? volumeDiff : period.Interval).ToString("N0");
break;
case ChartPeriodType.Delta:
var deltaDiff = period.Interval - (long)Math.Abs(lastCluster.Delta);
if (_lastDiff != long.MinValue && deltaDiff > _lastDiff)
{
alert = true;
}
_lastDiff = deltaDiff;
text = (deltaDiff > 0 ? deltaDiff : period.Interval).ToString("N0");
break;
case ChartPeriodType.Range:
var rangeDiff = period.Interval - (long)((lastCluster.High - lastCluster.Low) / (decimal)DataProvider.Step);
if (_lastDiff != long.MinValue && rangeDiff > _lastDiff)
{
alert = true;
}
_lastDiff = rangeDiff;
text = (rangeDiff > 0 ? rangeDiff : period.Interval).ToString("N0");
break;
default:
return;
}
var font = new XFont(Canvas.ChartFont.Name, TextSize);
var textSize = font.GetSize(text);
var x = 0.0;
var y = 0.0;
var width = textSize.Width + 10;
var height = textSize.Height + 10;
var rect = Canvas.Rect;
switch (Location)
{
case BarTimerLocation.LeftTop:
case BarTimerLocation.CenterTop:
case BarTimerLocation.RightTop:
y = rect.Top + OffsetY - 1;
break;
case BarTimerLocation.LeftMiddle:
case BarTimerLocation.CenterMiddle:
case BarTimerLocation.RightMiddle:
y = (rect.Top + rect.Bottom - height) / 2.0;
break;
case BarTimerLocation.LeftBottom:
case BarTimerLocation.CenterBottom:
case BarTimerLocation.RightBottom:
y = rect.Bottom - OffsetY - height - 1;
break;
}
switch (Location)
{
case BarTimerLocation.LeftTop:
case BarTimerLocation.LeftMiddle:
case BarTimerLocation.LeftBottom:
x = rect.X + OffsetX - 1;
break;
case BarTimerLocation.CenterTop:
case BarTimerLocation.CenterMiddle:
case BarTimerLocation.CenterBottom:
x = rect.Right - (rect.Width + width) / 2.0;
x = Math.Max(x, rect.Left);
break;
case BarTimerLocation.RightTop:
case BarTimerLocation.RightMiddle:
case BarTimerLocation.RightBottom:
x = rect.Right - width - OffsetX;
x = Math.Max(x, rect.Left);
break;
}
var rect1 = new Rect(x, y, width, height);
visual.FillRectangle(_backBrush, rect1);
visual.DrawRectangle(_borderPen, rect1);
visual.DrawString(text, new XFont(Canvas.ChartFont.Name, TextSize), _textBrush, rect1,
XTextAlignment.Center);
if (alert && Alert.IsActive)
{
AddAlert(Alert, "BarTimer");
}
}
public int GetTimeSeconds()
{
switch (DataProvider.Period.Type)
{
case ChartPeriodType.Second:
return DataProvider.Period.Interval;
case ChartPeriodType.Minute:
return DataProvider.Period.Interval * 60;
case ChartPeriodType.Hour:
return DataProvider.Period.Interval * 60 * 60;
case ChartPeriodType.Day:
return DataProvider.Period.Interval * 60 * 60 * 24;
case ChartPeriodType.Week:
return DataProvider.Period.Interval * 60 * 60 * 24 * 7;
case ChartPeriodType.Month:
return DataProvider.Period.Interval * 60 * 60 * 24 * 30;
default:
return 0;
}
}
public override bool CheckNeedRedraw()
{
if (DataProvider == null)
{
return false;
}
switch (DataProvider.Period.Type)
{
case ChartPeriodType.Day:
case ChartPeriodType.Week:
case ChartPeriodType.Month:
case ChartPeriodType.Year:
case ChartPeriodType.Tick:
case ChartPeriodType.Volume:
case ChartPeriodType.Delta:
case ChartPeriodType.Range:
return false;
}
if (DateTime.Now - _lastDateTime < TimeSpan.FromSeconds(1))
{
return false;
}
_lastDateTime = DateTime.Now;
return true;
}
public override void ApplyColors(IChartTheme theme)
{
BackColor = theme.ChartBackColor;
BorderColor = theme.ChartAxisColor;
TextColor = theme.ChartFontColor;
base.ApplyColors(theme);
}
public override void CopyTemplate(IndicatorBase indicator, bool style)
{
var i = (BarTimerIndicator)indicator;
Location = i.Location;
OffsetX = i.OffsetX;
OffsetY = i.OffsetY;
Alert.Copy(i.Alert, !style);
OnPropertyChanged(nameof(Alert));
BackColor = i.BackColor;
BorderColor = i.BorderColor;
TextColor = i.TextColor;
TextSize = i.TextSize;
base.CopyTemplate(indicator, style);
}
}
}
Last updated