Код индикатора Bar Search
//------------------------------------------------------------------------------ // // Индикатор BarSearch. Copyright (c) 2019 Ilya Smirnov. 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; } } }