Delta Indicator Code
//------------------------------------------------------------------------------ // // Delta. Copyright (c) 2019 Ilya Smirnov. 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.Alerts; using TigerTrade.Chart.Base; using TigerTrade.Chart.Indicators.Common; using TigerTrade.Chart.Indicators.Enums; using TigerTrade.Core.UI.Converters; using TigerTrade.Dx; namespace TigerTrade.Chart.Indicators.Custom { [TypeConverter(typeof(EnumDescriptionTypeConverter))] [DataContract(Name = "DeltaViewType", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")] public enum DeltaViewType { [EnumMember(Value = "Columns"), Description("Колонки")] Columns, [EnumMember(Value = "Candles"), Description("Свечи")] Candles } [DataContract(Name = "DeltaIndicator", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")] [Indicator("X_Delta", "*Delta", false, Type = typeof(DeltaIndicator))] internal sealed class DeltaIndicator : IndicatorBase { private XBrush _deltaPlusBrush; private XPen _deltaPlusPen; private XColor _deltaPlusColor; [DataMember(Name = "DeltaPlusColor")] [Category("Стиль"), DisplayName("Delta+")] public XColor DeltaPlusColor { get => _deltaPlusColor; set { if (value == _deltaPlusColor) { return; } _deltaPlusColor = value; _deltaPlusBrush = new XBrush(_deltaPlusColor); _deltaPlusPen = new XPen(_deltaPlusBrush, 1); OnPropertyChanged(); } } private XBrush _deltaMinusBrush; private XPen _deltaMinusPen; private XColor _deltaMinusColor; [DataMember(Name = "DeltaMinusColor")] [Category("Стиль"), DisplayName("Delta-")] public XColor DeltaMinusColor { get => _deltaMinusColor; set { if (value == _deltaMinusColor) { return; } _deltaMinusColor = value; _deltaMinusBrush = new XBrush(_deltaMinusColor); _deltaMinusPen = new XPen(_deltaMinusBrush, 1); OnPropertyChanged(); } } private DeltaViewType _type; [DataMember(Name = "DeltaType"), DefaultValue(DeltaViewType.Columns)] [Category("Параметры"), DisplayName("Тип")] public DeltaViewType Type { get => _type; set { if (value == _type) { return; } _type = value; OnPropertyChanged(); } } private bool _minimize; [DataMember(Name = "Minimize"), DefaultValue(false)] [Category("Параметры"), DisplayName("Минимизировать")] public bool Minimize { get => _minimize; set { if (value == _minimize) { return; } _minimize = value; OnPropertyChanged(); } } private IndicatorNullIntParam _maxDeltaParam; [DataMember(Name = "MaxDeltaParam")] public IndicatorNullIntParam MaxDeltaParam { get => _maxDeltaParam ?? (_maxDeltaParam = new IndicatorNullIntParam(null)); set => _maxDeltaParam = value; } [DefaultValue(null)] [Category("Параметры"), DisplayName("Макс. дельта")] public int? MaxDelta { get => MaxDeltaParam.Get(SettingsLongKey); set { if (!MaxDeltaParam.Set(SettingsLongKey, value, 0)) { return; } OnPropertyChanged(); } } private IndicatorNullIntParam _filter1MinParam; [DataMember(Name = "Filter1MinParam")] public IndicatorNullIntParam Filter1MinParam { get => _filter1MinParam ?? (_filter1MinParam = new IndicatorNullIntParam(null)); set => _filter1MinParam = value; } [DefaultValue(null)] [Category("Фильтр 1"), DisplayName("Минимум")] public int? Filter1Min { get => Filter1MinParam.Get(SettingsLongKey); set { if (!Filter1MinParam.Set(SettingsLongKey, value, 0)) { return; } OnPropertyChanged(); } } private IndicatorNullIntParam _filter1MaxParam; [DataMember(Name = "Filter1MaxParam")] public IndicatorNullIntParam Filter1MaxParam { get => _filter1MaxParam ?? (_filter1MaxParam = new IndicatorNullIntParam(null)); set => _filter1MaxParam = value; } [DefaultValue(null)] [Category("Фильтр 1"), DisplayName("Максимум")] public int? Filter1Max { get => Filter1MaxParam.Get(SettingsLongKey); set { if (!Filter1MaxParam.Set(SettingsLongKey, value, 0)) { return; } OnPropertyChanged(); } } private XBrush _filter1PlusBrush; private XPen _filter1PlusPen; private XColor _filter1PlusColor; [DataMember(Name = "Filter1PlusColor")] [Category("Фильтр 1"), DisplayName("Цвет+")] public XColor Filter1PlusColor { get => _filter1PlusColor; set { if (value == _filter1PlusColor) { return; } _filter1PlusColor = value; _filter1PlusBrush = new XBrush(_filter1PlusColor); _filter1PlusPen = new XPen(_filter1PlusBrush, 1); OnPropertyChanged(); } } private XBrush _filter1MinusBrush; private XPen _filter1MinusPen; private XColor _filter1MinusColor; [DataMember(Name = "Filter1MinusColor")] [Category("Фильтр 1"), DisplayName("Цвет-")] public XColor Filter1MinusColor { get => _filter1MinusColor; set { if (value == _filter1MinusColor) { return; } _filter1MinusColor = value; _filter1MinusBrush = new XBrush(_filter1MinusColor); _filter1MinusPen = new XPen(_filter1MinusBrush, 1); OnPropertyChanged(); } } private ChartAlertSettings _filter1Alert; [DataMember(Name = "Filter1Alert")] [Category("Фильтр 1"), DisplayName("Оповещение")] public ChartAlertSettings Filter1Alert { get => _filter1Alert ?? (_filter1Alert = new ChartAlertSettings()); set { if (Equals(value, _filter1Alert)) { return; } _filter1Alert = value; OnPropertyChanged(); } } private IndicatorNullIntParam _filter2MinParam; [DataMember(Name = "Filter2MinParam")] public IndicatorNullIntParam Filter2MinParam { get => _filter2MinParam ?? (_filter2MinParam = new IndicatorNullIntParam(null)); set => _filter2MinParam = value; } [DefaultValue(null)] [Category("Фильтр 2"), DisplayName("Минимум")] public int? Filter2Min { get => Filter2MinParam.Get(SettingsLongKey); set { if (!Filter2MinParam.Set(SettingsLongKey, value, 0)) { return; } OnPropertyChanged(); } } private IndicatorNullIntParam _filter2MaxParam; [DataMember(Name = "Filter2MaxParam")] public IndicatorNullIntParam Filter2MaxParam { get => _filter2MaxParam ?? (_filter2MaxParam = new IndicatorNullIntParam(null)); set => _filter2MaxParam = value; } [DefaultValue(null)] [Category("Фильтр 2"), DisplayName("Максимум")] public int? Filter2Max { get => Filter2MaxParam.Get(SettingsLongKey); set { if (!Filter2MaxParam.Set(SettingsLongKey, value, 0)) { return; } OnPropertyChanged(); } } private XBrush _filter2PlusBrush; private XPen _filter2PlusPen; private XColor _filter2PlusColor; [DataMember(Name = "Filter2PlusColor")] [Category("Фильтр 2"), DisplayName("Цвет+")] public XColor Filter2PlusColor { get => _filter2PlusColor; set { if (value == _filter2PlusColor) { return; } _filter2PlusColor = value; _filter2PlusBrush = new XBrush(_filter2PlusColor); _filter2PlusPen = new XPen(_filter2PlusBrush, 1); OnPropertyChanged(); } } private XBrush _filter2MinusBrush; private XPen _filter2MinusPen; private XColor _filter2MinusColor; [DataMember(Name = "Filter2MinusColor")] [Category("Фильтр 2"), DisplayName("Цвет-")] public XColor Filter2MinusColor { get => _filter2MinusColor; set { if (value == _filter2MinusColor) { return; } _filter2MinusColor = value; _filter2MinusBrush = new XBrush(_filter2MinusColor); _filter2MinusPen = new XPen(_filter2MinusBrush, 1); OnPropertyChanged(); } } private ChartAlertSettings _filter2Alert; [DataMember(Name = "Filter2Alert")] [Category("Фильтр 2"), DisplayName("Оповещение")] public ChartAlertSettings Filter2Alert { get => _filter2Alert ?? (_filter2Alert = new ChartAlertSettings()); set { if (Equals(value, _filter2Alert)) { return; } _filter2Alert = value; OnPropertyChanged(); } } private IndicatorNullIntParam _filter3MinParam; [DataMember(Name = "Filter3MinParam")] public IndicatorNullIntParam Filter3MinParam { get => _filter3MinParam ?? (_filter3MinParam = new IndicatorNullIntParam(null)); set => _filter3MinParam = value; } [DefaultValue(null)] [Category("Фильтр 3"), DisplayName("Минимум")] public int? Filter3Min { get => Filter3MinParam.Get(SettingsLongKey); set { if (!Filter3MinParam.Set(SettingsLongKey, value, 0)) { return; } OnPropertyChanged(); } } private IndicatorNullIntParam _filter3MaxParam; [DataMember(Name = "Filter3MaxParam")] public IndicatorNullIntParam Filter3MaxParam { get => _filter3MaxParam ?? (_filter3MaxParam = new IndicatorNullIntParam(null)); set => _filter3MaxParam = value; } [DefaultValue(null)] [Category("Фильтр 3"), DisplayName("Максимум")] public int? Filter3Max { get => Filter3MaxParam.Get(SettingsLongKey); set { if (!Filter3MaxParam.Set(SettingsLongKey, value, 0)) { return; } OnPropertyChanged(); } } private XBrush _filter3PlusBrush; private XPen _filter3PlusPen; private XColor _filter3PlusColor; [DataMember(Name = "Filter3PlusColor")] [Category("Фильтр 3"), DisplayName("Цвет+")] public XColor Filter3PlusColor { get => _filter3PlusColor; set { if (value == _filter3PlusColor) { return; } _filter3PlusColor = value; _filter3PlusBrush = new XBrush(_filter3PlusColor); _filter3PlusPen = new XPen(_filter3PlusBrush, 1); OnPropertyChanged(); } } private XBrush _filter3MinusBrush; private XPen _filter3MinusPen; private XColor _filter3MinusColor; [DataMember(Name = "Filter3MinusColor")] [Category("Фильтр 3"), DisplayName("Цвет-")] public XColor Filter3MinusColor { get => _filter3MinusColor; set { if (value == _filter3MinusColor) { return; } _filter3MinusColor = value; _filter3MinusBrush = new XBrush(_filter3MinusColor); _filter3MinusPen = new XPen(_filter3MinusBrush, 1); OnPropertyChanged(); } } private ChartAlertSettings _filter3Alert; [DataMember(Name = "Filter3Alert")] [Category("Фильтр 3"), DisplayName("Оповещение")] public ChartAlertSettings Filter3Alert { get => _filter3Alert ?? (_filter3Alert = new ChartAlertSettings()); set { if (Equals(value, _filter3Alert)) { return; } _filter3Alert = value; OnPropertyChanged(); } } [Browsable(false)] public override IndicatorCalculation Calculation => IndicatorCalculation.OnEachTick; public override bool IntegerValues => true; private int _filter1SignalIndex; private int _filter2SignalIndex; private int _filter3SignalIndex; public DeltaIndicator() { DeltaPlusColor = Color.FromArgb(255, 30, 144, 255); DeltaMinusColor = Color.FromArgb(255, 178, 34, 34); Type = DeltaViewType.Columns; Minimize = false; Filter1PlusColor = Colors.Orange; Filter1MinusColor = Colors.Orange; Filter2PlusColor = Colors.Orange; Filter2MinusColor = Colors.Orange; Filter3PlusColor = Colors.Orange; Filter3MinusColor = Colors.Orange; _filter1SignalIndex = -1; _filter2SignalIndex = -1; _filter3SignalIndex = -1; } protected override void Execute() { if (ClearData) { _filter1SignalIndex = -1; _filter2SignalIndex = -1; _filter3SignalIndex = -1; } var index = DataProvider.Count; var cluster = DataProvider.GetCluster(index - 1); if (cluster == null) { return; } var realDelta = cluster.Delta; var absDelta = Math.Abs(realDelta); if (Filter1Alert.IsActive && _filter1SignalIndex < index) { var min = Filter1Min ?? -1; var max = Filter1Max ?? -1; if (min >= 0 && absDelta >= min && (max < 0 || absDelta <= max)) { _filter1SignalIndex = index; AddAlert(Filter1Alert, $"Delta: фильтр 1, значение {DataProvider.Symbol.FormatSizeFull(realDelta)}."); } } if (Filter2Alert.IsActive && _filter2SignalIndex < index) { var min = Filter2Min ?? -1; var max = Filter2Max ?? -1; if (min >= 0 && absDelta >= min && (max < 0 || absDelta <= max)) { _filter2SignalIndex = index; AddAlert(Filter2Alert, $"Delta: фильтр 2, значение {DataProvider.Symbol.FormatSizeFull(realDelta)}."); } } if (Filter3Alert.IsActive && _filter3SignalIndex < index) { var min = Filter3Min ?? -1; var max = Filter3Max ?? -1; if (min >= 0 && absDelta >= min && (max < 0 || absDelta <= max)) { _filter3SignalIndex = index; AddAlert(Filter3Alert, $"Delta: фильтр 3, значение {DataProvider.Symbol.FormatSizeFull(realDelta)}."); } } } public override bool GetMinMax(out double min, out double max) { min = double.MaxValue; max = double.MinValue; if (Type == DeltaViewType.Candles) { for (var i = 0; i < Canvas.Count; i++) { var index = Canvas.GetIndex(i); var cluster = DataProvider.GetCluster(index); if (cluster == null || cluster.Delta == 0) { continue; } var deltaHigh = (double)cluster.DeltaHigh; var deltaLow = (double)cluster.DeltaLow; if (Minimize && cluster.Delta < 0) { max = Math.Max(max, -deltaLow); min = Math.Min(min, -deltaHigh); } else { min = Math.Min(min, deltaLow); max = Math.Max(max, deltaHigh); } } } else { for (var i = 0; i < Canvas.Count; i++) { var index = Canvas.GetIndex(i); var cluster = DataProvider.GetCluster(index); if (cluster == null || cluster.Delta == 0) { continue; } var delta = (double)cluster.Delta; if (Minimize) { max = Math.Max(max, Math.Abs(delta)); } else { min = Math.Min(min, delta); max = Math.Max(max, delta); } } if (Minimize) { min = 0.0; } } var maxDelta = MaxDelta ?? -1; if (maxDelta >= 0 && max > maxDelta) { max = maxDelta; } if (maxDelta >= 0 && min < -maxDelta) { min = -maxDelta; } return true; } public override void Render(DxVisualQueue visual) { var min1 = Filter1Min ?? -1; var max1 = Filter1Max ?? -1; var min2 = Filter2Min ?? -1; var max2 = Filter2Max ?? -1; var min3 = Filter3Min ?? -1; var max3 = Filter3Max ?? -1; var maxDelta = MaxDelta ?? -1; var maxPoint = (int)GetY((double)maxDelta); var minPoint = (int)GetY((double)-maxDelta); var zeroPoint = (int)GetY(0.0); switch (Type) { case DeltaViewType.Candles: { var width = (int)(Canvas.ColumnWidth * Canvas.ColumnPercent + 0.4); var halfWith = Math.Max((int)(width / 2.0), 1.0); var openY = zeroPoint; for (var i = 0; i < Canvas.Count; i++) { var index = Canvas.GetIndex(i); var cluster = DataProvider.GetCluster(index); if (cluster == null || cluster.Delta == 0) { continue; } var delta = cluster.Delta; var isUp = delta > 0; var centerX = (int)Canvas.GetX(index); var highY = (int)GetY(cluster.DeltaHigh); var lowY = (int)GetY(cluster.DeltaLow); var closeY = (int)GetY(delta); if (Minimize && delta < 0) { highY = (int) GetY(-cluster.DeltaLow); lowY = (int) GetY(-cluster.DeltaHigh); closeY = (int) GetY(-delta); } if (maxDelta >= 0) { if (delta > 0 || Minimize) { closeY = Math.Max(closeY, maxPoint); highY = Math.Max(highY, maxPoint); lowY = Math.Min(lowY, minPoint); } else { closeY = Math.Min(closeY, minPoint); highY = Math.Max(highY, maxPoint); lowY = Math.Min(lowY, minPoint); } } var topY = Math.Min(openY, closeY); var bottomY = Math.Max(openY, closeY); var height = bottomY - topY; var backBrush = isUp ? _deltaPlusBrush : _deltaMinusBrush; var backPen = isUp ? _deltaPlusPen : _deltaMinusPen; var absDelta = Math.Abs(delta); if (min1 >= 0 && absDelta >= min1 && (max1 < 0 || absDelta <= max1)) { if (delta > 0) { backBrush = _filter1PlusBrush; backPen = _filter1PlusPen; } else { backBrush = _filter1MinusBrush; backPen = _filter1MinusPen; } } else if (min2 >= 0 && absDelta >= min2 && (max2 < 0 || absDelta <= max2)) { if (delta > 0) { backBrush = _filter2PlusBrush; backPen = _filter2PlusPen; } else { backBrush = _filter2MinusBrush; backPen = _filter2MinusPen; } } else if (min3 >= 0 && absDelta >= min3 && (max3 < 0 || absDelta <= max3)) { if (delta > 0) { backBrush = _filter3PlusBrush; backPen = _filter3PlusPen; } else { backBrush = _filter3MinusBrush; backPen = _filter3MinusPen; } } if (height == 0 || width <= 1) { if (highY == lowY) { lowY++; } visual.DrawLine(backPen, new Point(centerX, highY), new Point(centerX, lowY)); } else { visual.DrawLine(backPen, new Point(centerX, highY), new Point(centerX, topY)); visual.DrawLine(backPen, new Point(centerX, bottomY), new Point(centerX, lowY)); } if (width > 1) { if (height == 0) { visual.DrawLine(backPen, new Point(centerX - halfWith, topY), new Point(centerX + halfWith + 1, topY)); } else { visual.FillRectangle(backBrush, new Rect(centerX - halfWith, topY, halfWith*2 + 1, height)); } } } } break; case DeltaViewType.Columns: { var width = Math.Max(Canvas.ColumnWidth - 1, 1); for (var i = 0; i < Canvas.Count; i++) { var index = Canvas.GetIndex(i); var cluster = DataProvider.GetCluster(index); if (cluster == null || cluster.Delta == 0) { continue; } var delta = cluster.Delta; var x = Canvas.GetX(index); var y = GetY(Minimize ? Math.Abs(delta) : delta); if (maxDelta >= 0) { if (Minimize) { y = Math.Max(y, maxPoint); } else { y = delta > 0 ? Math.Max(y, maxPoint) : Math.Min(y, minPoint); } } var h = zeroPoint - (int)y; if (h < 0.0) { y += h; h = -h; } if (Minimize || delta > 0) { if (h < 1) { y = zeroPoint - 1; h = 1; } } else { if (h < 1) { y = zeroPoint; h = 1; } } var isUp = delta > 0; XBrush filterBrush = null; XPen filterPen = null; var absDelta = Math.Abs(delta); if (min1 >= 0 && absDelta >= min1 && (max1 < 0 || absDelta <= max1)) { if (delta > 0) { filterBrush = _filter1PlusBrush; filterPen = _filter1PlusPen; } else { filterBrush = _filter1MinusBrush; filterPen = _filter1MinusPen; } } else if (min2 >= 0 && absDelta >= min2 && (max2 < 0 || absDelta <= max2)) { if (delta > 0) { filterBrush = _filter2PlusBrush; filterPen = _filter2PlusPen; } else { filterBrush = _filter2MinusBrush; filterPen = _filter2MinusPen; } } else if (min3 >= 0 && absDelta >= min3 && (max3 < 0 || absDelta <= max3)) { if (delta > 0) { filterBrush = _filter3PlusBrush; filterPen = _filter3PlusPen; } else { filterBrush = _filter3MinusBrush; filterPen = _filter3MinusPen; } } if (width > 1.0) { var rect = new Rect(x - width / 2.0, y, width, h); if (filterBrush != null) { visual.FillRectangle(filterBrush, rect); } else { visual.FillRectangle(isUp ? _deltaPlusBrush : _deltaMinusBrush, rect); } } else { if (filterBrush != null) { visual.DrawLine(filterPen, x, y, x, y + h); } else { visual.DrawLine(isUp ? _deltaPlusPen : _deltaMinusPen, x, y, x, y + h); } } } } break; } } public override List<IndicatorValueInfo> GetValues(int cursorPos) { var info = new List<IndicatorValueInfo>(); var cluster = DataProvider.GetCluster(cursorPos); if (cluster == null) { return info; } var s = Canvas.FormatValue((double)cluster.Delta); info.Add(new IndicatorValueInfo(s, Canvas.Theme.ChartFontBrush)); return info; } public override void GetLabels(ref List<IndicatorLabelInfo> labels) { if (DataProvider.Count <= Canvas.Start) { return; } var cluster = DataProvider.GetCluster(DataProvider.Count - 1 - Canvas.Start); if (cluster == null) { return; } var delta = Minimize ? Math.Abs(cluster.Delta) : cluster.Delta; labels.Add(new IndicatorLabelInfo((double)delta, cluster.Delta > 0 ? _deltaPlusColor : _deltaMinusColor)); } public override bool CheckAlert(double value, int distance, ref int lastIndex, ref double lastValue) { if (DataProvider.Count == lastIndex && value == lastValue) { return false; } var cluster = DataProvider.GetCluster(DataProvider.Count - 1); if (cluster == null) { return false; } var result = false; var delta = (double)cluster.Delta; if (value > 0 && delta >= value - distance) { result = true; } else if (value < 0 && delta <= value + distance) { result = true; } if (result == false) { return false; } lastIndex = DataProvider.Count; lastValue = value; return true; } public override void ApplyColors(IChartTheme theme) { DeltaPlusColor = theme.PaletteColor6; DeltaMinusColor = theme.PaletteColor7; base.ApplyColors(theme); } public override void CopyTemplate(IndicatorBase indicator, bool style) { var i = (DeltaIndicator)indicator; DeltaPlusColor = i.DeltaPlusColor; DeltaMinusColor = i.DeltaMinusColor; Type = i.Type; Minimize = i.Minimize; MaxDeltaParam.Copy(i.MaxDeltaParam); Filter1MinParam.Copy(i.Filter1MinParam); Filter1MaxParam.Copy(i.Filter1MaxParam); OnPropertyChanged(nameof(Filter1Min)); OnPropertyChanged(nameof(Filter1Max)); Filter1PlusColor = i.Filter1PlusColor; Filter1MinusColor = i.Filter1MinusColor; Filter1Alert.Copy(i.Filter1Alert, !style); OnPropertyChanged(nameof(Filter1Alert)); Filter2MinParam.Copy(i.Filter2MinParam); Filter2MaxParam.Copy(i.Filter2MaxParam); OnPropertyChanged(nameof(Filter2Min)); OnPropertyChanged(nameof(Filter2Max)); Filter2PlusColor = i.Filter2PlusColor; Filter2MinusColor = i.Filter2MinusColor; Filter2Alert.Copy(i.Filter2Alert, !style); OnPropertyChanged(nameof(Filter2Alert)); Filter3MinParam.Copy(i.Filter3MinParam); Filter3MaxParam.Copy(i.Filter3MaxParam); OnPropertyChanged(nameof(Filter3Min)); OnPropertyChanged(nameof(Filter3Max)); Filter3PlusColor = i.Filter3PlusColor; Filter3MinusColor = i.Filter3MinusColor; Filter3Alert.Copy(i.Filter3Alert, !style); OnPropertyChanged(nameof(Filter3Alert)); base.CopyTemplate(indicator, style); } } }