# External Chart

```
/--------------------------------------------------------------------------------
//
// Индикатор ExternalChart. Copyright (c) 2023 Tiger Trade Capital AG. All rights reserved.
//
//--------------------------------------------------------------------------------
 
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Media;
using TigerTrade.Chart.Base.Enums;
using TigerTrade.Chart.Data;
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 = "ExternalChartPeriodType", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
    public enum ExternalChartPeriodType
    {
        [EnumMember(Value = "Minute"), Description("Минута")]
        Minute,
        [EnumMember(Value = "Hour"), Description("Час")]
        Hour,
        [EnumMember(Value = "Day"), Description("День")]
        Day,
        [EnumMember(Value = "Week"), Description("Неделя")]
        Week,
        [EnumMember(Value = "Month"), Description("Месяц")]
        Month
    }
 
    [DataContract(Name = "ExternalChartBorderType", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
    [TypeConverter(typeof(EnumDescriptionTypeConverter))]
    public enum ExternalChartBorderType
    {
        [EnumMember(Value = "None"), Description("Скрыть")]
        None,
        [EnumMember(Value = "Box"), Description("Коробка")]
        Box,
        [EnumMember(Value = "ColoredBox"), Description("Коробка с раскраской")]
        ColoredBox,
        [EnumMember(Value = "Candle"), Description("Свеча")]
        Candle,
        [EnumMember(Value = "ColoredCandle"), Description("Свеча с раскраской")]
        ColoredCandle
    }
 
    [DataContract(Name = "ExternalChartIndicator", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
    [Indicator("Z_ExternalChart", "*ExternalChart", true, Type = typeof(ExternalChartIndicator))]
    internal sealed class ExternalChartIndicator : IndicatorBase
    {
        private ExternalChartPeriodType _periodType;
 
        [DataMember(Name = "PeriodType")]
        [Category("Период"), DisplayName("Интервал")]
        public ExternalChartPeriodType PeriodType
        {
            get => _periodType;
            set
            {
                if (value == _periodType)
                {
                    return;
                }
 
                _periodType = value;
 
                _periodValue = _periodType == ExternalChartPeriodType.Minute ? 15 : 1;
 
                Clear();
 
                OnPropertyChanged();
                OnPropertyChanged(nameof(PeriodValue));
            }
        }
 
        private int _periodValue;
 
        [DataMember(Name = "PeriodValue")]
        [Category("Период"), DisplayName("Значение")]
        public int PeriodValue
        {
            get => _periodValue;
            set
            {
                value = Math.Max(1, value);
 
                if (value == _periodValue)
                {
                    return;
                }
 
                _periodValue = value;
 
                Clear();
 
                OnPropertyChanged();
            }
        }
 
        private bool _showBack;
 
        [DataMember(Name = "ShowBack")]
        [Category("Фон"), DisplayName("Отображать фон")]
        public bool ShowBack
        {
            get => _showBack;
            set
            {
                if (value == _showBack)
                {
                    return;
                }
 
                _showBack = 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 bool _showGrid;
 
        [DataMember(Name = "ShowGrid")]
        [Category("Сетка"), DisplayName("Отображать сетку")]
        public bool ShowGrid
        {
            get => _showGrid;
            set
            {
                if (value == _showGrid)
                {
                    return;
                }
 
                _showGrid = value;
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _gridBrush;
 
        private XPen _gridPen;
 
        private XColor _gridColor;
 
        [DataMember(Name = "GridColor")]
        [Category("Сетка"), DisplayName("Цвет Сетки")]
        public XColor GridColor
        {
            get => _gridColor;
            set
            {
                if (value == _gridColor)
                {
                    return;
                }
 
                _gridColor = value;
 
                _gridBrush = new XBrush(_gridColor);
 
                _gridPen = new XPen(_gridBrush, 1);
 
                OnPropertyChanged();
            }
        }
 
        private ExternalChartBorderType _borderType;
 
        [DataMember(Name = "BorderType")]
        [Category("Граница"), DisplayName("Тип границы")]
        public ExternalChartBorderType BorderType
        {
            get => _borderType;
            set
            {
                if (value == _borderType)
                {
                    return;
                }
 
                _borderType = value;
 
                OnPropertyChanged();
            }
        }
 
        private int _borderWidth;
 
        [DataMember(Name = "BorderWidth")]
        [Category("Граница"), DisplayName("Ширина границы")]
        public int BorderWidth
        {
            get => _borderWidth;
            set
            {
                value = Math.Max(1, value);
 
                if (value == _borderWidth)
                {
                    return;
                }
 
                _borderWidth = value;
 
                OnPropertyChanged();
            }
        }
 
        private XBrush _borderBrush;
 
        private XColor _borderColor;
 
        [DataMember(Name = "BorderColor")]
        [Category("Граница"), DisplayName("Цвет границы")]
        public XColor BorderColor
        {
            get => _borderColor;
            set
            {
                if (value == _borderColor)
                {
                    return;
                }
 
                _borderColor = value;
 
                _borderBrush = new XBrush(_borderColor);
 
                OnPropertyChanged();
            }
        }
 
        [Browsable(false)]
        public override bool ShowIndicatorValues => false;
 
        [Browsable(false)]
        public override bool ShowIndicatorLabels => false;
 
        [Browsable(false)]
        public override IndicatorCalculation Calculation => IndicatorCalculation.OnPriceChange;
 
        private List<ExternalBar> _bars;
 
        private List<ExternalBar> Bars => _bars ?? (_bars = new List<ExternalBar>());
 
        public ExternalChartIndicator()
        {
            ShowIndicatorTitle = false;
 
            PeriodType = ExternalChartPeriodType.Hour;
            PeriodValue = 1;
 
            ShowBack = true;
            BackColor = Color.FromArgb(30, 70, 130, 180);
 
            ShowGrid = false;
            GridColor = Color.FromArgb(255, 70, 130, 180);
 
            BorderType = ExternalChartBorderType.ColoredBox;
            BorderWidth = 1;
            BorderColor = Color.FromArgb(255, 120, 120, 120);
        }
 
        private int _lastFullID;
 
        private void Clear()
        {
            _lastFullID = 0;
 
            Bars.Clear();
        }
 
        private int GetSequence(DateTime date, double offset)
        {
            var periodType = ChartPeriodType.Hour;
 
            switch (PeriodType)
            {
                case ExternalChartPeriodType.Minute:
 
                    periodType = ChartPeriodType.Minute;
 
                    break;
 
                case ExternalChartPeriodType.Hour:
 
                    periodType = ChartPeriodType.Hour;
 
                    break;
 
                case ExternalChartPeriodType.Day:
 
                    periodType = ChartPeriodType.Day;
 
                    break;
 
                case ExternalChartPeriodType.Week:
 
                    periodType = ChartPeriodType.Week;
 
                    break;
 
                case ExternalChartPeriodType.Month:
 
                    periodType = ChartPeriodType.Month;
 
                    break;
            }
 
            return DataProvider.Period.GetSequence(periodType, PeriodValue, date, offset);
        }
 
        protected override void Execute()
        {
            if (ClearData)
            {
                Clear();
            }
 
            if (Bars.Count > 0 && !Bars[Bars.Count - 1].Completed)
            {
                Bars.RemoveAt(Bars.Count - 1);
            }
 
            var timeOffset = TimeHelper.GetSessionOffset(DataProvider.Symbol.Exchange);
 
            var lastSequence = -1;
 
            for (var i = _lastFullID; i < DataProvider.Count; i++)
            {
                var cluster = DataProvider.GetCluster(i);
 
                var currSequence = GetSequence(cluster.Time, timeOffset);
 
                if (Bars.Count == 0 || currSequence != lastSequence)
                {
                    lastSequence = currSequence;
 
                    if (Bars.Count > 0 && i > _lastFullID)
                    {
                        _lastFullID = i;
 
                        Bars[Bars.Count - 1].Completed = true;
                    }
 
                    Bars.Add(new ExternalBar(i));
                }
 
                Bars[Bars.Count - 1].Update(cluster, i);
            }
        }
 
        public override void Render(DxVisualQueue visual)
        {
            if (Bars.Count == 0)
            {
                return;
            }
 
            var startIndex = Canvas.Stop;
            var endIndex = Canvas.Stop + Canvas.Count;
 
            var step = (decimal)DataProvider.Step;
 
            var columnWidth2 = Canvas.ColumnWidth / 2.0;
 
            var prevRight = int.MinValue;
 
            foreach (var bar in Bars)
            {
                if (bar.EndBar < startIndex || bar.StartBar > endIndex)
                {
                    continue;
                }
 
                var x1 = Canvas.GetX(bar.StartBar);
                var x2 = Canvas.GetX(bar.EndBar);
 
                var left = (int)(x1 - columnWidth2);
                var right = (int)(x2 + columnWidth2 - 1);
 
                if (prevRight != int.MinValue)
                {
                    left = prevRight;
                }
 
                prevRight = right;
 
                var centerX = (int)((x1 + x2) / 2.0);
 
                var isUp = bar.Open < bar.Close;
 
                var highY = (int)GetY(bar.High + step / 2m);
                var lowY = (int)GetY(bar.Low - step / 2m);
 
                var bodyHighY = isUp
                    ? (int)GetY(bar.Close + step / 2m)
                    : (int)GetY(bar.Open + step / 2m);
 
                var bodyLowY = !isUp
                    ? (int)GetY(bar.Close - step / 2m)
                    : (int)GetY(bar.Open - step / 2m);
 
                if (ShowBack)
                {
                    visual.FillRectangle(_backBrush,
                        new Rect(new Point(left, highY - 1), new Point(right, lowY)));
                }
 
                if (ShowGrid)
                {
                    for (var j = bar.High + step; j >= bar.Low; j -= step)
                    {
                        var currY = (int)GetY(j - step / 2m) - 1;
 
                        visual.DrawLine(_gridPen, new Point(left, currY), new Point(right, currY));
                    }
 
                    for (var j = bar.StartBar; j <= bar.EndBar; j++)
                    {
                        var barLeft = j == bar.StartBar ? left : Canvas.GetX(j) - columnWidth2 - 1;
 
                        visual.DrawLine(_gridPen, new Point(barLeft, highY - 1), new Point(barLeft, lowY));
 
                        if (j == bar.EndBar)
                        {
                            visual.DrawLine(_gridPen, new Point(right, highY - 1), new Point(right, lowY));
                        }
                    }
                }
 
                if (BorderType != ExternalChartBorderType.None)
                {
                    var borderBrush = _borderBrush;
 
                    if (BorderType == ExternalChartBorderType.ColoredCandle ||
                        BorderType == ExternalChartBorderType.ColoredBox)
                    {
                        borderBrush = new XBrush(isUp ? Canvas.Theme.ClusterUpBarColor : Canvas.Theme.ClusterDownBarColor);
                    }
 
                    var borderPen = new XPen(borderBrush, BorderWidth);
 
                    if (BorderType == ExternalChartBorderType.Candle ||
                        BorderType == ExternalChartBorderType.ColoredCandle)
                    {
                        var correct = (int)Math.Ceiling(_borderWidth / 2.0);
 
                        visual.DrawRectangle(borderPen, new Rect(new Point(left + correct, bodyHighY), new Point(right - correct, bodyLowY)));
 
                        visual.DrawLine(borderPen, new Point(centerX, highY), new Point(centerX, bodyHighY));
 
                        visual.DrawLine(borderPen, new Point(centerX, bodyLowY), new Point(centerX, lowY));
                    }
                    else if (BorderType == ExternalChartBorderType.Box ||
                             BorderType == ExternalChartBorderType.ColoredBox)
                    {
                        var correct = (int)Math.Ceiling(_borderWidth / 2.0);
 
                        visual.DrawRectangle(borderPen, new Rect(new Point(left + correct, highY), new Point(right - correct, lowY)));
                    }
                }
            }
        }
 
        public override void CopyTemplate(IndicatorBase indicator, bool style)
        {
            var i = (ExternalChartIndicator)indicator;
 
            PeriodType = i.PeriodType;
            PeriodValue = i.PeriodValue;
 
            ShowBack = i.ShowBack;
            BackColor = i.BackColor;
 
            ShowGrid = i.ShowGrid;
            GridColor = i.GridColor;
 
            BorderType = i.BorderType;
            BorderWidth = i.BorderWidth;
            BackColor = i.BorderColor;
 
            base.CopyTemplate(indicator, style);
        }
 
        private class ExternalBar
        {
            public readonly int StartBar;
            public int EndBar;
            public bool Completed;
 
            public decimal Open;
            public decimal High;
            public decimal Low;
            public decimal Close;
 
            private bool _new;
 
            public ExternalBar(int startBar)
            {
                StartBar = startBar;
 
                _new = true;
            }
 
            public void Update(IChartCluster cluster, int bar)
            {
                if (_new)
                {
                    Open = cluster.Open;
                    High = cluster.High;
                    Low = cluster.Low;
 
                    _new = false;
                }
                else
                {
                    High = Math.Max(High, cluster.High);
                    Low = Math.Min(Low, cluster.Low);
                }
 
                Close = cluster.Close;
 
                EndBar = bar;
            }
        }
    }
}
```


---

# 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/external-chart.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.
