# Bar Search

```
//------------------------------------------------------------------------------
//
// Индикатор BarSearch. Copyright (c) 2023 Tiger Trade Capital AG. All rights reserved.
//
//------------------------------------------------------------------------------
 
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using TigerTrade.Chart.Alerts;
using TigerTrade.Chart.Annotations;
using TigerTrade.Chart.Base;
using TigerTrade.Chart.Indicators.Common;
using TigerTrade.Chart.Indicators.Enums;
using TigerTrade.Chart.Indicators.Sources;
using TigerTrade.Core.UI.Common;
using TigerTrade.Core.UI.Converters;
using TigerTrade.Core.Utils.Time;
using TigerTrade.Dx;
 
namespace TigerTrade.Chart.Indicators.Custom
{
    [TypeConverter(typeof(EnumDescriptionTypeConverter))]
    [DataContract(Name = "BarSearchConditions", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
    public enum BarSearchConditions
    {
        [EnumMember(Value = "GreaterThenSource"), Description("Источник 1 > Источник 2")]
        GreaterThenSource,
        [EnumMember(Value = "GreaterThenValue"), Description("Источник 1 >= Значение")]
        GreaterThenValue,
        [EnumMember(Value = "LessThenSource"), Description("Источник 1 < Источник 2")]
        LessThenSource,
        [EnumMember(Value = "LessThenValue"), Description("Источник 1 <= Значение")]
        LessThenValue,
        [EnumMember(Value = "EqualSource"), Description("Источник 1 = Источник 2")]
        EqualSource,
        [EnumMember(Value = "EqualValue"), Description("Источник 1 = Значение")]
        EqualValue,
    }
 
    [TypeConverter(typeof(ExpandableObjectConverter)), ReadOnly(true)]
    [DataContract(Name = "BarSearchCondition", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
    internal sealed class BarSearchCondition : INotifyPropertyChanged, IDynamicProperty
    {
        private BarSearchConditions _conditions;
 
        [DataMember(Name = "Condition")]
        [DisplayName("Условие")]
        public BarSearchConditions Condition
        {
            get => _conditions;
            set
            {
                if (Equals(value, _conditions))
                {
                    return;
                }
 
                _conditions = value;
 
                OnPropertyChanged();
                OnPropertyChanged(nameof(Source2));
                OnPropertyChanged(nameof(Value));
            }
        }
 
        private IndicatorSourceBase _source1;
       
        [DataMember(Name = "Source1")]
        [DisplayName("Источник 1")]
        public IndicatorSourceBase Source1
        {
            get => _source1 ?? (_source1 = new StockSource());
            set
            {
                if (value == _source1)
                {
                    return;
                }
 
                _source1 = value;
 
                OnPropertyChanged();
            }
        }
 
        private IndicatorSourceBase _source2;
 
        [DataMember(Name = "Source2")]
        [DisplayName("Источник 2")]
        public IndicatorSourceBase Source2
        {
            get => _source2 ?? (_source2 = new StockSource());
            set
            {
                if (value == _source2)
                {
                    return;
                }
 
                _source2 = value;
 
                OnPropertyChanged();
            }
        }
 
        private decimal _value;
       
        [DataMember(Name = "Value")]
        [DisplayName("Значение")]
        public decimal Value
        {
            get => _value;
            set
            {
                if (value == _value)
                {
                    return;
                }
 
                _value = value;
 
                OnPropertyChanged();
            }
        }
 
        private double[] _source1Cache;
        private double[] _source2Cache;
 
        public void Clear()
        {
            _source1Cache = null;
            _source2Cache = null;
        }
 
        private double[] GetSource1(IndicatorsHelper helper, int barIndex)
        {
            if (_source1Cache != null && _source1Cache.Length - 1 > barIndex)
            {
                return _source1Cache;
            }
 
            _source1Cache = Source1.GetSeries(helper);
 
            if (_source1Cache == null || _source1Cache.Length < barIndex)
            {
                return null;
            }
 
            return _source1Cache;
        }
 
        private double[] GetSource2(IndicatorsHelper helper, int barIndex)
        {
            if (_source2Cache != null && _source2Cache.Length - 1 > barIndex)
            {
                return _source2Cache;
            }
 
            _source2Cache = Source2.GetSeries(helper);
 
            if (_source2Cache == null || _source2Cache.Length < barIndex)
            {
                return null;
            }
 
            return _source2Cache;
        }
 
        public bool Check(IndicatorsHelper helper, int barIndex)
        {
            var source1 = GetSource1(helper, barIndex);
 
            switch (Condition)
            {
                case BarSearchConditions.GreaterThenSource:
                {
                    var source2 = GetSource2(helper, barIndex);
 
                    if (source2 == null)
                    {
                        return false;
                    }
 
                    if (source1[barIndex] > source2[barIndex])
                    {
                        return true;
                    }
 
                    break;
                }
                case BarSearchConditions.GreaterThenValue:
                {
                    if (source1[barIndex] >= (double)Value)
                    {
                        return true;
                    }
 
                    break;
                }
                case BarSearchConditions.LessThenSource:
                {
                    var source2 = GetSource2(helper, barIndex);
 
                    if (source2 == null)
                    {
                        return false;
                    }
 
                    if (source1[barIndex] < source2[barIndex])
                    {
                        return true;
                    }
 
                    break;
                }
                case BarSearchConditions.LessThenValue:
                {
                    if (source1[barIndex] <= (double)Value)
                    {
                        return true;
                    }
 
                    break;
                }
                case BarSearchConditions.EqualSource:
                {
                    var source2 = GetSource2(helper, barIndex);
 
                    if (source2 == null)
                    {
                        return false;
                    }
 
                    if (source1[barIndex] == source2[barIndex])
                    {
                        return true;
                    }
 
                    break;
                }
                case BarSearchConditions.EqualValue:
                {
                    if (source1[barIndex] == (double)Value)
                    {
                        return true;
                    }
 
                    break;
                }
            }
 
            return false;
        }
 
        public void Copy(BarSearchCondition condition)
        {
            Source1 = condition.Source1.CloneSource();
            Source2 = condition.Source2.CloneSource();
 
            Value = condition.Value;
        }
 
        [Browsable(false)]
        public bool Updated { get; set; }
 
        public event PropertyChangedEventHandler PropertyChanged;
 
        [NotifyPropertyChangedInvocator]
        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            Updated = true;
 
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
 
        public override string ToString()
        {
            return "Условие";
        }
 
        public bool GetPropertyHasStandardValues(string propertyName)
        {
            return false;
        }
 
        public bool GetPropertyReadOnly(string propertyName)
        {
            return false;
        }
 
        public IEnumerable<object> GetPropertyStandardValues(string propertyName)
        {
            return null;
        }
 
        public bool GetPropertyVisibility(string propertyName)
        {
            switch (propertyName)
            {
                case "Source2":
 
                    return Condition == BarSearchConditions.GreaterThenSource ||
                           Condition == BarSearchConditions.LessThenSource ||
                           Condition == BarSearchConditions.EqualSource;
 
                case "Value":
 
                    return Condition == BarSearchConditions.GreaterThenValue ||
                           Condition == BarSearchConditions.LessThenValue ||
                           Condition == BarSearchConditions.EqualValue;
            }
 
            return true;
        }
    }
 
    internal sealed class AlertData
    {
        public int LastIndex { get; set; }
        public DateTime LastTime { get; set; }
 
        public AlertData()
        {
            Clear();
        }
 
        public void Clear()
        {
            LastIndex = -1;
            LastTime = DateTime.MinValue;
        }
    }
 
    [DataContract(Name = "BarSearchIndicator", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
    [Indicator("X_BarSearch", "*BarSearch", true, Type = typeof(BarSearchIndicator))]
    internal sealed class BarSearchIndicator : IndicatorBase, IContainsConditions
    {
        private XBrush _selectionBrush;
 
        private XPen _selectionPen;
 
        private XColor _selectionColor;
 
        [DataMember(Name = "SelectionColor")]
        [Category("Стиль"), DisplayName("Цвет выделения")]
        public XColor SelectionColor
        {
            get => _selectionColor;
            set
            {
                if (value == _selectionColor)
                {
                    return;
                }
 
                _selectionColor = value;
 
                _selectionBrush = new XBrush(_selectionColor);
                _selectionPen = new XPen(_selectionBrush, 1);
 
                OnPropertyChanged();
            }
        }
 
        private List<BarSearchCondition> _conditions;
 
        [DataMember(Name = "Conditions")]
        [Category("Параметры"), DisplayName("Условия")]
        public List<BarSearchCondition> Conditions
        {
            get => _conditions ?? (_conditions = new List<BarSearchCondition>());
            set => _conditions = value;
        }
 
        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();
            }
        }
 
        [Browsable(false)]
        public override bool ShowIndicatorValues => false;
 
        [Browsable(false)]
        public override bool ShowIndicatorLabels => false;
 
        [Browsable(false)]
        public override IndicatorCalculation Calculation => IndicatorCalculation.OnBarClose;
 
        private int _lastFullID;
 
        private Dictionary<int, bool> _bars;
        private Dictionary<int, bool> Bars => _bars ?? (_bars = new Dictionary<int, bool>());
 
        private AlertData _alertData;
 
        private void Clear()
        {
            _lastFullID = 0;
 
            Bars.Clear();
 
            foreach (var condition in Conditions)
            {
                condition.Clear();
            }
 
            _alertData?.Clear();
        }
 
        protected override void Execute()
        {
            var clear = false;
 
            foreach (var condition in Conditions)
            {
                if (!condition.Updated)
                {
                    continue;
                }
 
                condition.Updated = false;
 
                clear = true;
            }
 
            if (clear || ClearData)
            {
                Clear();
            }
 
            for (var i = _lastFullID; i < DataProvider.Count; i++)
            {
                if (!Bars.ContainsKey(i))
                {
                    Bars.Add(i, false);
                }
 
                var result = Conditions.Count > 0;
 
                foreach (var condition in Conditions)
                {
                    if (!condition.Check(Helper, i))
                    {
                        result = false;
 
                        break;
                    }
                }
 
                Bars[i] = result;
 
                if (result && _lastFullID > 0)
                {
                    AddAlert(i);
                }
            }
 
            _lastFullID = Math.Max(DataProvider.Count - 2, 0);
        }
 
        private void AddAlert(int index)
        {
            if (_alertData == null)
            {
                _alertData = new AlertData();
            }
 
            if (!Alert.IsActive || _alertData.LastIndex >= index)
            {
                return;
            }
 
            var currTime = TimeHelper.LocalTime;
 
            _alertData.LastIndex = index;
            _alertData.LastTime = currTime;
 
            AddAlert(Alert, "BarSearch Alert");
        }
 
        public override void ApplyColors(IChartTheme theme)
        {
            SelectionColor = theme.GetNextColor();
 
            base.ApplyColors(theme);
        }
 
        public override void CopyTemplate(IndicatorBase indicator, bool style)
        {
            var i = (BarSearchIndicator)indicator;
 
            SelectionColor = i.SelectionColor;
 
            Conditions.Clear();
 
            if (!style)
            {
                foreach (var condition in i.Conditions)
                {
                    var newCondition = new BarSearchCondition();
 
                    newCondition.Copy(condition);
 
                    Conditions.Add(newCondition);
                }
            }
 
            Alert.Copy(i.Alert, !style);
 
            OnPropertyChanged(nameof(Alert));
 
            base.CopyTemplate(indicator, style);
        }
 
        public XBrush GetBrush(int index, bool isUp)
        {
            if (index > 0 && index < Bars.Count)
            {
                if (Bars[index])
                {
                    return _selectionBrush;
                }
            }
 
            return null;
        }
    }
}
 
```


---

# 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-search.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.
