# 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);
        }
    }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://support.tiger.com/razrabotka-dlya-tiger.trade-windows/primery-indikatorov/bar-timer.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
