> For the complete documentation index, see [llms.txt](https://support.tiger.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://support.tiger.com/razrabotka-dlya-tiger.trade-windows/primery-indikatorov/external-chart.md).

# 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.Chart.Localization;
using TigerTrade.Core.UI.Converters;
using TigerTrade.Core.Utils.Time;
using TigerTrade.Dx;

namespace TigerTrade.Chart.Indicators.List
{
    [TypeConverter(typeof(EnumDescriptionTypeConverter))]
    [DataContract(Name = "ExternalChartPeriodType", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators")]
    public enum ExternalChartPeriodType
    {
        [EnumMember(Value = "Minute"), DescriptionLocalized("ChartIndicatorsMinute")]
        Minute,
        [EnumMember(Value = "Hour"), DescriptionLocalized("ChartIndicatorsHour")]
        Hour,
        [EnumMember(Value = "Day"), DescriptionLocalized("ChartIndicatorsDay")]
        Day,
        [EnumMember(Value = "Week"), DescriptionLocalized("ChartIndicatorsWeek")]
        Week,
        [EnumMember(Value = "Month"), DescriptionLocalized("ChartIndicatorsMonth")]
        Month
    }

    [DataContract(Name = "ExternalChartBorderType", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators")]
    [TypeConverter(typeof(EnumDescriptionTypeConverter))]
    public enum ExternalChartBorderType
    {
        [EnumMember(Value = "None"), DescriptionLocalized("ChartIndicatorsBorderNone")]
        None,
        [EnumMember(Value = "Box"), DescriptionLocalized("ChartIndicatorsBorderBox")]
        Box,
        [EnumMember(Value = "ColoredBox"), DescriptionLocalized("ChartIndicatorsBorderColoredBox")]
        ColoredBox,
        [EnumMember(Value = "Candle"), DescriptionLocalized("ChartIndicatorsBorderCandle")]
        Candle,
        [EnumMember(Value = "ColoredCandle"), DescriptionLocalized("ChartIndicatorsBorderColoredCandle")]
        ColoredCandle
    }

    [DataContract(Name = "ExternalChartIndicator", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators")]
    [Indicator("ExternalChart", "ExternalChart", true, Type = typeof(ExternalChartIndicator))]
    internal sealed class ExternalChartIndicator : IndicatorBase
    {
        [Browsable(false)]
        public override ChartDataType ChartDataType => ChartDataType.Bar;

        // params

        private ExternalChartPeriodType _periodType;

        [DataMember(Name = "PeriodType")]
        [CategoryLocalized("ChartIndicatorsPeriod"), DisplayNameLocalized("ChartIndicatorsPeriodType")]
        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")]
        [CategoryLocalized("ChartIndicatorsPeriod"), DisplayNameLocalized("ChartIndicatorsPeriodValue")]
        public int PeriodValue
        {
            get => _periodValue;
            set
            {
                value = Math.Max(1, value);

                if (value == _periodValue)
                {
                    return;
                }

                _periodValue = value;

                Clear();

                OnPropertyChanged();
            }
        }

        // back

        private bool _showBack;

        [DataMember(Name = "ShowBack")]
        [CategoryLocalized("ChartIndicatorsBack"), DisplayNameLocalized("ChartIndicatorsShowBack")]
        public bool ShowBack
        {
            get => _showBack;
            set
            {
                if (value == _showBack)
                {
                    return;
                }

                _showBack = value;

                OnPropertyChanged();
            }
        }

        private XBrush _backBrush;

        private XColor _backColor;

        [DataMember(Name = "BackColor")]
        [CategoryLocalized("ChartIndicatorsBack"), DisplayNameLocalized("ChartIndicatorsBackColor")]
        public XColor BackColor
        {
            get => _backColor;
            set
            {
                if (value == _backColor)
                {
                    return;
                }

                _backColor = value;

                _backBrush = new XBrush(_backColor);

                OnPropertyChanged();
            }
        }

        // grid

        private bool _showGrid;

        [DataMember(Name = "ShowGrid")]
        [CategoryLocalized("ChartIndicatorsGrid"), DisplayNameLocalized("ChartIndicatorsShowGrid")]
        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")]
        [CategoryLocalized("ChartIndicatorsGrid"), DisplayNameLocalized("ChartIndicatorsGridColor")]
        public XColor GridColor
        {
            get => _gridColor;
            set
            {
                if (value == _gridColor)
                {
                    return;
                }

                _gridColor = value;

                _gridBrush = new XBrush(_gridColor);

                _gridPen = new XPen(_gridBrush, 1);

                OnPropertyChanged();
            }
        }

        // border

        private ExternalChartBorderType _borderType;

        [DataMember(Name = "BorderType")]
        [CategoryLocalized("ChartIndicatorsBorder"), DisplayNameLocalized("ChartIndicatorsBorderType")]
        public ExternalChartBorderType BorderType
        {
            get => _borderType;
            set
            {
                if (value == _borderType)
                {
                    return;
                }

                _borderType = value;

                OnPropertyChanged();
            }
        }

        private int _borderWidth;

        [DataMember(Name = "BorderWidth")]
        [CategoryLocalized("ChartIndicatorsBorder"), DisplayNameLocalized("ChartIndicatorsBorderWidth")]
        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")]
        [CategoryLocalized("ChartIndicatorsBorder"), DisplayNameLocalized("ChartIndicatorsBorderColor")]
        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, DataProvider.Scale);
            }
        }

        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 heightCorrect = step / 2m;

            if (Canvas.StockType != ChartStockType.Clusters && !ShowGrid)
            {
                heightCorrect = 0m;
            }

            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 + heightCorrect);
                var lowY = (int) GetY(bar.Low - heightCorrect);

                var bodyHighY = isUp
                    ? (int) GetY(bar.Close + heightCorrect)
                    : (int) GetY(bar.Open + heightCorrect);

                var bodyLowY = !isUp
                    ? (int) GetY(bar.Close - heightCorrect)
                    : (int) GetY(bar.Open - heightCorrect);

                // отрисовка фона

                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;
            BorderColor = 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, int priceScale)
            {
                if (_new)
                {
                    Open = cluster.Open * priceScale;
                    High = cluster.High * priceScale;
                    Low = cluster.Low * priceScale;

                    _new = false;
                }
                else
                {
                    High = Math.Max(High, cluster.High * priceScale);
                    Low = Math.Min(Low, cluster.Low * priceScale);
                }

                Close = cluster.Close * priceScale;

                EndBar = bar;
            }
        }
    }
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://support.tiger.com/razrabotka-dlya-tiger.trade-windows/primery-indikatorov/external-chart.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
