Code of the Bar Timer indicator
//------------------------------------------------------------------------------ // // BarTimer. Copyright (c) 2019 Ilya Smirnov. 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); } } }