ClusterSearch

Тема в разделе "Примеры индикаторов", создана пользователем Support, 26 июл 2019.

  1. Support

    Support Администратор
    Команда форума

    Регистрация:
    5 сен 2015
    Сообщения:
    1 003
    Симпатии:
    191
    Код индикатора ClusterSearch
    1. //------------------------------------------------------------------------------
    2. //
    3. // Индикатор ClusterSearch. Copyright (c) 2019 Ilya Smirnov. All rights reserved.
    4. //
    5. //------------------------------------------------------------------------------
    6.  
    7. using System;
    8. using System.Collections.Generic;
    9. using System.ComponentModel;
    10. using System.Runtime.Serialization;
    11. using System.Windows;
    12. using System.Windows.Input;
    13. using System.Windows.Media;
    14. using TigerTrade.Chart.Alerts;
    15. using TigerTrade.Chart.Base.Enums;
    16. using TigerTrade.Chart.Data;
    17. using TigerTrade.Chart.Indicators.Common;
    18. using TigerTrade.Chart.Indicators.Enums;
    19. using TigerTrade.Core.UI.Converters;
    20. using TigerTrade.Dx;
    21.  
    22. namespace TigerTrade.Chart.Indicators.Custom
    23. {
    24.     [TypeConverter(typeof(EnumDescriptionTypeConverter))]
    25.     [DataContract(Name = "ClusterSearchDataType", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
    26.     public enum ClusterSearchDataType
    27.     {
    28.         [EnumMember(Value = "Volume"), Description("Volume")]
    29.         Volume,
    30.         [EnumMember(Value = "MaxVolume"), Description("Max Volume")]
    31.         MaxVol,
    32.         [EnumMember(Value = "Trades"), Description("Trades")]
    33.         Trades,
    34.         [EnumMember(Value = "Bid"), Description("Bid")]
    35.         Bid,
    36.         [EnumMember(Value = "Ask"), Description("Ask")]
    37.         Ask,
    38.         [EnumMember(Value = "Delta"), Description("Delta")]
    39.         Delta,
    40.         [EnumMember(Value = "DeltaPlus"), Description("Delta+")]
    41.         DeltaPlus,
    42.         [EnumMember(Value = "DeltaMinus"), Description("Delta-")]
    43.         DeltaMinus
    44.     }
    45.  
    46.     [TypeConverter(typeof(EnumDescriptionTypeConverter))]
    47.     [DataContract(Name = "ClusterSearchObjectType", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
    48.     public enum ClusterSearchObjectType
    49.     {
    50.         [EnumMember(Value = "Rectangle"), Description("Квадрат")]
    51.         Rectangle,
    52.         [EnumMember(Value = "Triangle"), Description("Треугольник")]
    53.         Triangle,
    54.         [EnumMember(Value = "Diamond"), Description("Ромб")]
    55.         Diamond,
    56.         [EnumMember(Value = "Circle"), Description("Круг")]
    57.         Circle,
    58.         [EnumMember(Value = "SelectionOnly"), Description("Только выделение")]
    59.         SelectionOnly
    60.     }
    61.  
    62.     [TypeConverter(typeof(EnumDescriptionTypeConverter))]
    63.     [DataContract(Name = "ClusterSearchBarDirection", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
    64.     public enum ClusterSearchBarDirection
    65.     {
    66.         [EnumMember(Value = "Any"), Description("Любое")]
    67.         Any,
    68.         [EnumMember(Value = "Up"), Description("Рост")]
    69.         Up,
    70.         [EnumMember(Value = "Down"), Description("Падение")]
    71.         Down
    72.     }
    73.  
    74.     [TypeConverter(typeof(EnumDescriptionTypeConverter))]
    75.     [DataContract(Name = "ClusterSearchPriceLocation", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
    76.     public enum ClusterSearchPriceLocation
    77.     {
    78.         [EnumMember(Value = "Any"), Description("Любое")]
    79.         Any,
    80.         [EnumMember(Value = "High"), Description("High")]
    81.         High,
    82.         [EnumMember(Value = "Low"), Description("Low")]
    83.         Low,
    84.         [EnumMember(Value = "HighLow"), Description("High или Low")]
    85.         HighLow,
    86.         [EnumMember(Value = "Body"), Description("Тело")]
    87.         Body,
    88.         [EnumMember(Value = "Wick"), Description("Тень")]
    89.         Wick,
    90.         [EnumMember(Value = "UpperWick"), Description("Верхняя тень")]
    91.         UpperWick,
    92.         [EnumMember(Value = "LowerWick"), Description("Нижняя тень")]
    93.         LowerWick
    94.     }
    95.  
    96.     [TypeConverter(typeof(EnumDescriptionTypeConverter))]
    97.     [DataContract(Name = "ClusterSearchPriceRangeDirection", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
    98.     public enum ClusterSearchPriceRangeDirection
    99.     {
    100.         [EnumMember(Value = "All"), Description("Оба")]
    101.         All,
    102.         [EnumMember(Value = "Downward"), Description("Сверху вниз")]
    103.         Downward,
    104.         [EnumMember(Value = "Upward"), Description("Снизу вверх")]
    105.         Upward
    106.     }
    107.  
    108.     [DataContract(Name = "ClusterSearchIndicator", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")]
    109.     [Indicator("X_ClusterSearch", "*ClusterSearch", true, Type = typeof(ClusterSearchIndicator))]
    110.     internal sealed class ClusterSearchIndicator : IndicatorBase, IContainsSelection
    111.     {
    112.         private class ClusterSearchItem
    113.         {
    114.             public DateTime Time { get; }
    115.             public long PriceHigh { get; }
    116.             public long PriceLow { get; }
    117.             public long Value { get; }
    118.  
    119.             public ClusterSearchItem(DateTime time, long priceHigh, long priceLow, long value)
    120.             {
    121.                 Time = time;
    122.                 PriceHigh = priceHigh;
    123.                 PriceLow = priceLow;
    124.                 Value = value;
    125.             }
    126.         }
    127.  
    128.         private class ClusterSearchRect
    129.         {
    130.             private Rect _rect;
    131.  
    132.             private readonly ClusterSearchItem _item;
    133.  
    134.             public ClusterSearchRect(Rect rect, ClusterSearchItem item)
    135.             {
    136.                 _rect = rect;
    137.                 _item = item;
    138.             }
    139.  
    140.             public bool Contains(Point p)
    141.             {
    142.                 return _rect.Contains(p);
    143.             }
    144.  
    145.             public string GetLabel(ClusterSearchDataType type, IChartDataProvider dp, string name)
    146.             {
    147.                 var price = dp.Symbol.FormatRawPrice((_item.PriceHigh + _item.PriceLow) / 2, true);
    148.  
    149.                 var val = type == ClusterSearchDataType.Trades
    150.                     ? dp.Symbol.FormatTrades(_item.Value)
    151.                     : dp.Symbol.FormatRawSizeShort(_item.Value);
    152.  
    153.                 var time = dp.Symbol.FormatTime(_item.Time, "HH:mm:ss");
    154.  
    155.                 return $"{name} {time} {price} x {val}";
    156.             }
    157.         }
    158.  
    159.         private class ClusterSearchBar
    160.         {
    161.             public List<ClusterSearchItem> Items { get; }
    162.             public HashSet<long> Selections { get; }
    163.             public HashSet<long> SingleSelection { get; }
    164.  
    165.             private readonly HashSet<long> _signals;
    166.  
    167.             public ClusterSearchBar()
    168.             {
    169.                 Items = new List<ClusterSearchItem>();
    170.                 Selections = new HashSet<long>();
    171.                 SingleSelection = new HashSet<long>();
    172.  
    173.                 _signals = new HashSet<long>();
    174.             }
    175.  
    176.             public void Add(ClusterSearchItem item)
    177.             {
    178.                 Items.Add(item);
    179.             }
    180.  
    181.             public bool CheckSignal(long price)
    182.             {
    183.                 if (_signals.Contains(price))
    184.                 {
    185.                     return false;
    186.                 }
    187.  
    188.                 _signals.Add(price);
    189.  
    190.                 return true;
    191.             }
    192.  
    193.             public void Clear()
    194.             {
    195.                 Items.Clear();
    196.                 Selections.Clear();
    197.                 SingleSelection.Clear();
    198.             }
    199.  
    200.             public void Update()
    201.             {
    202.                 ClusterSearchItem maxItem = null;
    203.  
    204.                 foreach (var item in Items)
    205.                 {
    206.                     if (maxItem == null || maxItem.Value < item.Value)
    207.                     {
    208.                         maxItem = item;
    209.                     }
    210.  
    211.                     for (var price = item.PriceLow; price <= item.PriceHigh; price++)
    212.                     {
    213.                         if (!Selections.Contains(price))
    214.                         {
    215.                             Selections.Add(price);
    216.                         }
    217.                     }
    218.                 }
    219.  
    220.                 if (maxItem == null)
    221.                 {
    222.                     return;
    223.                 }
    224.  
    225.                 for (var price = maxItem.PriceLow; price <= maxItem.PriceHigh; price++)
    226.                 {
    227.                     if (!SingleSelection.Contains(price))
    228.                     {
    229.                         SingleSelection.Add(price);
    230.                     }
    231.                 }
    232.             }
    233.         }
    234.  
    235.         private ClusterSearchDataType _type;
    236.  
    237.         [DataMember(Name = "Type"), DefaultValue(ClusterSearchDataType.Volume)]
    238.         [Category("Параметры"), DisplayName("Тип")]
    239.         public ClusterSearchDataType Type
    240.         {
    241.             get => _type;
    242.             set
    243.             {
    244.                 if (value == _type)
    245.                 {
    246.                     return;
    247.                 }
    248.  
    249.                 _type = value;
    250.  
    251.                 Clear();
    252.  
    253.                 OnPropertyChanged();
    254.                 OnPropertyChanged(nameof(Title));
    255.             }
    256.         }
    257.  
    258.         private IndicatorIntParam _minimumParam;
    259.  
    260.         [DataMember(Name = "MinimumParam")]
    261.         public IndicatorIntParam MinimumParam
    262.         {
    263.             get => _minimumParam ?? (_minimumParam = new IndicatorIntParam(1000));
    264.             set => _minimumParam = value;
    265.         }
    266.  
    267.         [DefaultValue(1000)]
    268.         [Category("Параметры"), DisplayName("Минимум")]
    269.         public int Minimum
    270.         {
    271.             get => MinimumParam.Get(SettingsLongKey);
    272.             set
    273.             {
    274.                 if (!MinimumParam.Set(SettingsLongKey, value, 0))
    275.                 {
    276.                     return;
    277.                 }
    278.  
    279.                 Clear();
    280.  
    281.                 OnPropertyChanged();
    282.                 OnPropertyChanged(nameof(Title));
    283.             }
    284.         }
    285.  
    286.         private IndicatorNullIntParam _maximumParam;
    287.  
    288.         [DataMember(Name = "MaximumParam")]
    289.         public IndicatorNullIntParam MaximumParam
    290.         {
    291.             get => _maximumParam ?? (_maximumParam = new IndicatorNullIntParam(null));
    292.             set => _maximumParam = value;
    293.         }
    294.  
    295.         [DefaultValue(null)]
    296.         [Category("Параметры"), DisplayName("Максимум")]
    297.         public int? Maximum
    298.         {
    299.             get => MaximumParam.Get(SettingsLongKey);
    300.             set
    301.             {
    302.                 if (!MaximumParam.Set(SettingsLongKey, value, 0))
    303.                 {
    304.                     return;
    305.                 }
    306.  
    307.                 Clear();
    308.  
    309.                 OnPropertyChanged();
    310.                 OnPropertyChanged(nameof(Title));
    311.             }
    312.         }
    313.  
    314.         private ChartAlertSettings _alert;
    315.  
    316.         [DataMember(Name = "Alert"), Browsable(true)]
    317.         [Category("Параметры"), DisplayName("Оповещение")]
    318.         public ChartAlertSettings Alert
    319.         {
    320.             get => _alert ?? (_alert = new ChartAlertSettings());
    321.             set
    322.             {
    323.                 if (Equals(value, _alert))
    324.                 {
    325.                     return;
    326.                 }
    327.  
    328.                 _alert = value;
    329.  
    330.                 OnPropertyChanged();
    331.             }
    332.         }
    333.  
    334.         private XBrush _selectionBrush;
    335.  
    336.         private XColor _selectionColor;
    337.  
    338.         [DataMember(Name = "SelectionColor")]
    339.         [Category("Стиль"), DisplayName("Цвет выделения")]
    340.         public XColor SelectionColor
    341.         {
    342.             get => _selectionColor;
    343.             set
    344.             {
    345.                 if (value == _selectionColor)
    346.                 {
    347.                     return;
    348.                 }
    349.  
    350.                 _selectionColor = value;
    351.  
    352.                 _selectionBrush = new XBrush(_selectionColor);
    353.  
    354.                 OnPropertyChanged();
    355.             }
    356.         }
    357.  
    358.         private XBrush _objectBackBrush;
    359.  
    360.         private XColor _objectBackColor;
    361.  
    362.         [DataMember(Name = "ObjectColor")]
    363.         [Category("Стиль"), DisplayName("Цвет фона объекта")]
    364.         public XColor ObjectBackColor
    365.         {
    366.             get => _objectBackColor;
    367.             set
    368.             {
    369.                 if (value == _objectBackColor)
    370.                 {
    371.                     return;
    372.                 }
    373.  
    374.                 _objectBackColor = value;
    375.  
    376.                 _objectBackBrush = new XBrush(_objectBackColor);
    377.  
    378.                 OnPropertyChanged();
    379.             }
    380.         }
    381.  
    382.         private XBrush _objectBorderBrush;
    383.  
    384.         private XPen _objectBorderPen;
    385.  
    386.         private XColor _objectBorderColor;
    387.  
    388.         [DataMember(Name = "ObjectBorderColor")]
    389.         [Category("Стиль"), DisplayName("Цвет границы объекта")]
    390.         public XColor ObjectBorderColor
    391.         {
    392.             get => _objectBorderColor;
    393.             set
    394.             {
    395.                 if (value == _objectBorderColor)
    396.                 {
    397.                     return;
    398.                 }
    399.  
    400.                 _objectBorderColor = value;
    401.  
    402.                 _objectBorderBrush = new XBrush(_objectBorderColor);
    403.                 _objectBorderPen = new XPen(_objectBorderBrush, 1);
    404.  
    405.                 OnPropertyChanged();
    406.             }
    407.         }
    408.  
    409.         private ClusterSearchObjectType _objectType;
    410.  
    411.         [DataMember(Name = "ObjectType")]
    412.         [Category("Стиль"), DisplayName("Тип фигуры")]
    413.         public ClusterSearchObjectType ObjectType
    414.         {
    415.             get => _objectType;
    416.             set
    417.             {
    418.                 if (value == _objectType)
    419.                 {
    420.                     return;
    421.                 }
    422.  
    423.                 _objectType = value;
    424.  
    425.                 OnPropertyChanged();
    426.             }
    427.         }
    428.  
    429.         private int _objectMinSize;
    430.  
    431.         [DataMember(Name = "ObjectMinSize")]
    432.         [Category("Стиль"), DisplayName("Мин. размер")]
    433.         public int ObjectMinSize
    434.         {
    435.             get => _objectMinSize;
    436.             set
    437.             {
    438.                 value = Math.Max(10, Math.Min(value, 200));
    439.  
    440.                 if (value == _objectMinSize)
    441.                 {
    442.                     return;
    443.                 }
    444.  
    445.                 _objectMinSize = value;
    446.  
    447.                 OnPropertyChanged();
    448.             }
    449.         }
    450.  
    451.         private int _objectMaxSize;
    452.  
    453.         [DataMember(Name = "ObjectMaxSize")]
    454.         [Category("Стиль"), DisplayName("Макс. размер")]
    455.         public int ObjectMaxSize
    456.         {
    457.             get => _objectMaxSize;
    458.             set
    459.             {
    460.                 value = Math.Min(200, Math.Max(value, 10));
    461.  
    462.                 if (value == _objectMaxSize)
    463.                 {
    464.                     return;
    465.                 }
    466.  
    467.                 _objectMaxSize = value;
    468.  
    469.                 OnPropertyChanged();
    470.             }
    471.         }
    472.  
    473.         private int _barsRange;
    474.  
    475.         [DataMember(Name = "BarsRange"), DefaultValue(1)]
    476.         [Category("Фильтры"), DisplayName("Объедин. баров")]
    477.         public int BarsRange
    478.         {
    479.             get => _barsRange;
    480.             set
    481.             {
    482.                 value = Math.Max(1, value);
    483.  
    484.                 if (value == _barsRange)
    485.                 {
    486.                     return;
    487.                 }
    488.  
    489.                 _barsRange = value;
    490.  
    491.                 Clear();
    492.  
    493.                 OnPropertyChanged();
    494.             }
    495.         }
    496.  
    497.         private int _priceRange;
    498.  
    499.         [DataMember(Name = "PriceRange"), DefaultValue(1)]
    500.         [Category("Фильтры"), DisplayName("Объедин. цен")]
    501.         public int PriceRange
    502.         {
    503.             get => _priceRange;
    504.             set
    505.             {
    506.                 value = Math.Max(1, value);
    507.  
    508.                 if (value == _priceRange)
    509.                 {
    510.                     return;
    511.                 }
    512.  
    513.                 _priceRange = value;
    514.  
    515.                 Clear();
    516.  
    517.                 OnPropertyChanged();
    518.             }
    519.         }
    520.  
    521.         private ClusterSearchPriceRangeDirection _priceRangeDir;
    522.  
    523.         [DataMember(Name = "PriceRangeDir"), DefaultValue(ClusterSearchPriceRangeDirection.All)]
    524.         [Category("Фильтры"), DisplayName("Напр. объедин. цен")]
    525.         public ClusterSearchPriceRangeDirection PriceRangeDir
    526.         {
    527.             get => _priceRangeDir;
    528.             set
    529.             {
    530.                 if (value == _priceRangeDir)
    531.                 {
    532.                     return;
    533.                 }
    534.  
    535.                 _priceRangeDir = value;
    536.  
    537.                 Clear();
    538.  
    539.                 OnPropertyChanged();
    540.             }
    541.         }
    542.  
    543.         private IndicatorNullIntParam _minValueParam;
    544.  
    545.         [DataMember(Name = "MinValueParam")]
    546.         public IndicatorNullIntParam MinValueParam
    547.         {
    548.             get => _minValueParam ?? (_minValueParam = new IndicatorNullIntParam());
    549.             set => _minValueParam = value;
    550.         }
    551.  
    552.         [DefaultValue(null)]
    553.         [Category("Фильтры"), DisplayName("Мин. значение")]
    554.         public int? MinValue
    555.         {
    556.             get => MinValueParam.Get(SettingsLongKey);
    557.             set
    558.             {
    559.                 if (!MinValueParam.Set(SettingsLongKey, value, 0))
    560.                 {
    561.                     return;
    562.                 }
    563.  
    564.                 Clear();
    565.  
    566.                 OnPropertyChanged();
    567.             }
    568.         }
    569.  
    570.         private int? _minDelta;
    571.  
    572.         [DataMember(Name = "MinDelta"), DefaultValue(null)]
    573.         [Category("Фильтры"), DisplayName("Мин. дельта")]
    574.         public int? MinDelta
    575.         {
    576.             get => _minDelta;
    577.             set
    578.             {
    579.                 if (value == _minDelta)
    580.                 {
    581.                     return;
    582.                 }
    583.  
    584.                 _minDelta = value;
    585.  
    586.                 Clear();
    587.  
    588.                 OnPropertyChanged();
    589.             }
    590.         }
    591.  
    592.         private int? _bidAskImbalance;
    593.  
    594.         [DataMember(Name = "BidAskImbalance"), DefaultValue(null)]
    595.         [Category("Фильтры"), DisplayName("Перевес BidAsk")]
    596.         public int? BidAskImbalance
    597.         {
    598.             get => _bidAskImbalance;
    599.             set
    600.             {
    601.                 if (value == _bidAskImbalance)
    602.                 {
    603.                     return;
    604.                 }
    605.  
    606.                 _bidAskImbalance = value;
    607.  
    608.                 Clear();
    609.  
    610.                 OnPropertyChanged();
    611.             }
    612.         }
    613.  
    614.         private int? _rangeFromHigh;
    615.  
    616.         [DataMember(Name = "RangeFromHigh"), DefaultValue(null)]
    617.         [Category("Фильтры"), DisplayName("Диапазон от High")]
    618.         public int? RangeFromHigh
    619.         {
    620.             get => _rangeFromHigh;
    621.             set
    622.             {
    623.                 if (value.HasValue && value.Value < 1 && _rangeFromHigh.HasValue)
    624.                 {
    625.                     value = null;
    626.                 }
    627.                 else if (value.HasValue && value.Value < 1 && !_rangeFromHigh.HasValue)
    628.                 {
    629.                     value = 1;
    630.                 }
    631.  
    632.                 if (value == _rangeFromHigh)
    633.                 {
    634.                     return;
    635.                 }
    636.  
    637.                 _rangeFromHigh = value;
    638.  
    639.                 Clear();
    640.  
    641.                 OnPropertyChanged();
    642.             }
    643.         }
    644.  
    645.         private int? _rangeFromLow;
    646.  
    647.         [DataMember(Name = "RangeFromLow"), DefaultValue(null)]
    648.         [Category("Фильтры"), DisplayName("Диапазон от Low")]
    649.         public int? RangeFromLow
    650.         {
    651.             get => _rangeFromLow;
    652.             set
    653.             {
    654.                 if (value.HasValue && value.Value < 0)
    655.                 {
    656.                     value = null;
    657.                 }
    658.  
    659.                 if (value == _rangeFromLow)
    660.                 {
    661.                     return;
    662.                 }
    663.  
    664.                 _rangeFromLow = value;
    665.  
    666.                 Clear();
    667.  
    668.                 OnPropertyChanged();
    669.             }
    670.         }
    671.  
    672.         private int? _minAvgTrade;
    673.  
    674.         [DataMember(Name = "MinAvgTrade"), DefaultValue(null)]
    675.         [Category("Фильтры"), DisplayName("Мин. средний трейд")]
    676.         public int? MinAvgTrade
    677.         {
    678.             get => _minAvgTrade;
    679.             set
    680.             {
    681.                 if (value.HasValue && value.Value < 0)
    682.                 {
    683.                     value = null;
    684.                 }
    685.  
    686.                 if (value == _minAvgTrade)
    687.                 {
    688.                     return;
    689.                 }
    690.  
    691.                 _minAvgTrade = value;
    692.  
    693.                 Clear();
    694.  
    695.                 OnPropertyChanged();
    696.             }
    697.         }
    698.  
    699.         private int? _maxAvgTrade;
    700.  
    701.         [DataMember(Name = "MaxAvgTrade"), DefaultValue(null)]
    702.         [Category("Фильтры"), DisplayName("Макс. средний трейд")]
    703.         public int? MaxAvgTrade
    704.         {
    705.             get => _maxAvgTrade;
    706.             set
    707.             {
    708.                 if (value.HasValue && value.Value < 0)
    709.                 {
    710.                     value = null;
    711.                 }
    712.  
    713.                 if (value == _maxAvgTrade)
    714.                 {
    715.                     return;
    716.                 }
    717.  
    718.                 _maxAvgTrade = value;
    719.  
    720.                 Clear();
    721.  
    722.                 OnPropertyChanged();
    723.             }
    724.         }
    725.  
    726.         private ClusterSearchBarDirection _barDirection;
    727.  
    728.         [DataMember(Name = "BarDirection"), DefaultValue(ClusterSearchBarDirection.Any)]
    729.         [Category("Фильтры"), DisplayName("Направление бара")]
    730.         public ClusterSearchBarDirection BarDirection
    731.         {
    732.             get => _barDirection;
    733.             set
    734.             {
    735.                 if (value == _barDirection)
    736.                 {
    737.                     return;
    738.                 }
    739.  
    740.                 _barDirection = value;
    741.  
    742.                 Clear();
    743.  
    744.                 OnPropertyChanged();
    745.             }
    746.         }
    747.  
    748.         private ClusterSearchPriceLocation _priceLocation;
    749.  
    750.         [DataMember(Name = "PriceLocation"), DefaultValue(ClusterSearchPriceLocation.Any)]
    751.         [Category("Фильтры"), DisplayName("Расположение цены")]
    752.         public ClusterSearchPriceLocation PriceLocation
    753.         {
    754.             get => _priceLocation;
    755.             set
    756.             {
    757.                 if (value == _priceLocation)
    758.                 {
    759.                     return;
    760.                 }
    761.  
    762.                 _priceLocation = value;
    763.  
    764.                 Clear();
    765.  
    766.                 OnPropertyChanged();
    767.             }
    768.         }
    769.  
    770.         private bool _singleSelection;
    771.  
    772.         [DataMember(Name = "SingleSelection"), DefaultValue(false)]
    773.         [Category("Фильтры"), DisplayName("Одно выдел. в баре")]
    774.         public bool SingleSelection
    775.         {
    776.             get => _singleSelection;
    777.             set
    778.             {
    779.                 if (value == _singleSelection)
    780.                 {
    781.                     return;
    782.                 }
    783.  
    784.                 _singleSelection = value;
    785.  
    786.                 Clear();
    787.  
    788.                 OnPropertyChanged();
    789.             }
    790.         }
    791.  
    792.         private bool _useTimeFilter;
    793.  
    794.         [DataMember(Name = "UseTimeFilter")]
    795.         [Category("Фильтр по времени"), DisplayName("Включить фильтр")]
    796.         public bool UseTimeFilter
    797.         {
    798.             get => _useTimeFilter;
    799.             set
    800.             {
    801.                 if (value == _useTimeFilter)
    802.                 {
    803.                     return;
    804.                 }
    805.  
    806.                 _useTimeFilter = value;
    807.  
    808.                 Clear();
    809.  
    810.                 OnPropertyChanged();
    811.             }
    812.         }
    813.  
    814.         private TimeSpan _startTime;
    815.  
    816.         [DataMember(Name = "StartTime")]
    817.         [Category("Фильтр по времени"), DisplayName("Начальное время")]
    818.         public TimeSpan StartTime
    819.         {
    820.             get => _startTime;
    821.             set
    822.             {
    823.                 if (value == _startTime)
    824.                 {
    825.                     return;
    826.                 }
    827.  
    828.                 _startTime = value;
    829.  
    830.                 Clear();
    831.  
    832.                 OnPropertyChanged();
    833.             }
    834.         }
    835.  
    836.         private TimeSpan _endTime;
    837.  
    838.         [DataMember(Name = "EndTime")]
    839.         [Category("Фильтр по времени"), DisplayName("Конечное время")]
    840.         public TimeSpan EndTime
    841.         {
    842.             get => _endTime;
    843.             set
    844.             {
    845.                 if (value == _endTime)
    846.                 {
    847.                     return;
    848.                 }
    849.  
    850.                 _endTime = value;
    851.  
    852.                 Clear();
    853.  
    854.                 OnPropertyChanged();
    855.             }
    856.         }
    857.  
    858.         [Browsable(false)]
    859.         public override bool ShowIndicatorValues => false;
    860.  
    861.         [Browsable(false)]
    862.         public override bool ShowIndicatorLabels => false;
    863.  
    864.         [Browsable(false)]
    865.         public override IndicatorCalculation Calculation => IndicatorCalculation.OnEachTick;
    866.  
    867.         private long _minValue;
    868.         private long _maxValue;
    869.  
    870.         private int _lastFullID;
    871.  
    872.         private Dictionary<int, ClusterSearchBar> _bars;
    873.         private Dictionary<int, ClusterSearchBar> Bars => _bars ?? (_bars = new Dictionary<int, ClusterSearchBar>());
    874.  
    875.         private List<ClusterSearchRect> _rects;
    876.      
    877.         public ClusterSearchIndicator()
    878.         {
    879.             Type = ClusterSearchDataType.Volume;
    880.  
    881.             SelectionColor = Color.FromArgb(255, 178, 34, 34);
    882.             ObjectBackColor = Color.FromArgb(127, 30, 144, 255);
    883.             ObjectBorderColor = Color.FromArgb(255, 30, 144, 255);
    884.             ObjectType = ClusterSearchObjectType.Diamond;
    885.             ObjectMinSize = 20;
    886.             ObjectMaxSize = 80;
    887.  
    888.             BarsRange = 1;
    889.             PriceRange = 1;
    890.             PriceRangeDir = ClusterSearchPriceRangeDirection.All;
    891.             MinDelta = null;
    892.             BidAskImbalance = null;
    893.             RangeFromHigh = null;
    894.             RangeFromLow = null;
    895.             MaxAvgTrade = null;
    896.             MinAvgTrade = null;
    897.             BarDirection = ClusterSearchBarDirection.Any;
    898.             PriceLocation = ClusterSearchPriceLocation.Any;
    899.             SingleSelection = false;
    900.  
    901.             UseTimeFilter = false;
    902.             StartTime = TimeSpan.Zero;
    903.             EndTime = TimeSpan.Zero;
    904.         }
    905.  
    906.         private void Clear()
    907.         {
    908.             _maxValue = 0;
    909.             _minValue = 0;
    910.  
    911.             _lastFullID = 0;
    912.  
    913.             Bars.Clear();
    914.         }
    915.  
    916.         private bool GetValue(IRawCluster cluster, IRawClusterItem item, long? minFilter, ref long value, ref long bid, ref long ask)
    917.         {
    918.             var itemValue = 0L;
    919.  
    920.             switch (Type)
    921.             {
    922.                 case ClusterSearchDataType.Volume:
    923.  
    924.                     itemValue = item.Volume;
    925.  
    926.                     break;
    927.  
    928.                 case ClusterSearchDataType.Trades:
    929.  
    930.                     itemValue = item.Trades;
    931.  
    932.                     break;
    933.  
    934.                 case ClusterSearchDataType.Bid:
    935.  
    936.                     itemValue = item.Bid;
    937.  
    938.                     break;
    939.  
    940.                 case ClusterSearchDataType.Ask:
    941.  
    942.                     itemValue = item.Ask;
    943.  
    944.                     break;
    945.  
    946.                 case ClusterSearchDataType.Delta:
    947.                 case ClusterSearchDataType.DeltaPlus:
    948.                 case ClusterSearchDataType.DeltaMinus:
    949.  
    950.                     itemValue = item.Delta;
    951.  
    952.                     break;
    953.  
    954.                 case ClusterSearchDataType.MaxVol:
    955.  
    956.                     var maxValues = cluster.MaxValues;
    957.  
    958.                     itemValue = item.Price == maxValues.Poc ? maxValues.MaxVolume : 0;
    959.  
    960.                     break;
    961.             }
    962.  
    963.             if (minFilter.HasValue && minFilter.Value > Math.Abs(itemValue))
    964.             {
    965.                 return false;
    966.             }
    967.  
    968.             value += itemValue;
    969.             bid += item.Bid;
    970.             ask += item.Ask;
    971.  
    972.             return true;
    973.         }
    974.  
    975.         private bool CheckMinMax(long value, long min, long? max)
    976.         {
    977.             switch (Type)
    978.             {
    979.                 case ClusterSearchDataType.DeltaPlus:
    980.  
    981.                     if (value < 0)
    982.                     {
    983.                         return false;
    984.                     }
    985.  
    986.                     break;
    987.  
    988.                 case ClusterSearchDataType.DeltaMinus:
    989.  
    990.                     if (value > 0)
    991.                     {
    992.                         return false;
    993.                     }
    994.  
    995.                     break;
    996.             }
    997.  
    998.             if (Math.Abs(value) < min)
    999.             {
    1000.                 return false;
    1001.             }
    1002.  
    1003.             if (max.HasValue && Math.Abs(value) > max.Value)
    1004.             {
    1005.                 return false;
    1006.             }
    1007.  
    1008.             return true;
    1009.         }
    1010.  
    1011.         protected override void Execute()
    1012.         {
    1013.             if (ClearData)
    1014.             {
    1015.                 Clear();
    1016.             }
    1017.  
    1018.             var symbol = DataProvider.Symbol;
    1019.  
    1020.             var min = Type == ClusterSearchDataType.Trades ? Minimum : symbol.CorrectSizeFilter(Minimum);
    1021.             var max = Type == ClusterSearchDataType.Trades ? Maximum : symbol.CorrectSizeFilter(Maximum);
    1022.             var minValue = Type == ClusterSearchDataType.Trades ? MinValue : symbol.CorrectSizeFilter(MinValue);
    1023.             var minDelta = symbol.CorrectSizeFilter(MinDelta);
    1024.             var bidAskImb = symbol.CorrectSizeFilter(BidAskImbalance);
    1025.  
    1026.             var maxAvgTrade = symbol.CorrectSizeFilter(MaxAvgTrade);
    1027.             var minAvgTrade = symbol.CorrectSizeFilter(MinAvgTrade);
    1028.  
    1029.             for (var i = _lastFullID; i < DataProvider.Count; i++)
    1030.             {
    1031.                 var cluster = DataProvider.GetRawCluster(i);
    1032.  
    1033.                 if (!Bars.ContainsKey(i))
    1034.                 {
    1035.                     Bars.Add(i, new ClusterSearchBar());
    1036.                 }
    1037.  
    1038.                 var currBar = Bars[i];
    1039.  
    1040.                 currBar.Clear();
    1041.  
    1042.                 if (UseTimeFilter)
    1043.                 {
    1044.                     var openTime = symbol.ConvertTimeToLocal(cluster.OpenTime);
    1045.  
    1046.                     if (StartTime <= EndTime)
    1047.                     {
    1048.                         if (openTime < cluster.OpenTime.Date.Add(StartTime) ||
    1049.                             openTime > cluster.OpenTime.Date.Add(EndTime))
    1050.                         {
    1051.                             continue;
    1052.                         }
    1053.                     }
    1054.                     else
    1055.                     {
    1056.                         if (openTime < cluster.OpenTime.Date.Add(StartTime) &&
    1057.                             openTime > cluster.OpenTime.Date.Add(EndTime))
    1058.                         {
    1059.                             continue;
    1060.                         }
    1061.                     }
    1062.                 }
    1063.  
    1064.                 switch (BarDirection)
    1065.                 {
    1066.                     case ClusterSearchBarDirection.Up:
    1067.  
    1068.                         if (cluster.Open > cluster.Close)
    1069.                         {
    1070.                             continue;
    1071.                         }
    1072.  
    1073.                         break;
    1074.  
    1075.                     case ClusterSearchBarDirection.Down:
    1076.  
    1077.                         if (cluster.Open < cluster.Close)
    1078.                         {
    1079.                             continue;
    1080.                         }
    1081.  
    1082.                         break;
    1083.                 }
    1084.  
    1085.                 for (var price = cluster.High; price >= cluster.Low; price--)
    1086.                 {
    1087.                     var item = cluster.GetItem(price);
    1088.  
    1089.                     if (item == null)
    1090.                     {
    1091.                         continue;
    1092.                     }
    1093.  
    1094.                     switch (PriceLocation)
    1095.                     {
    1096.                         case ClusterSearchPriceLocation.High:
    1097.  
    1098.                             if (price != cluster.High)
    1099.                             {
    1100.                                 continue;
    1101.                             }
    1102.  
    1103.                             break;
    1104.  
    1105.                         case ClusterSearchPriceLocation.Low:
    1106.  
    1107.                             if (price != cluster.Low)
    1108.                             {
    1109.                                 continue;
    1110.                             }
    1111.  
    1112.                             break;
    1113.  
    1114.                         case ClusterSearchPriceLocation.HighLow:
    1115.  
    1116.                             if (price != cluster.High && price != cluster.Low)
    1117.                             {
    1118.                                 continue;
    1119.                             }
    1120.  
    1121.                             break;
    1122.  
    1123.                         case ClusterSearchPriceLocation.Body:
    1124.  
    1125.                             if (price > Math.Max(cluster.Open, cluster.Close) ||
    1126.                                 price < Math.Min(cluster.Open, cluster.Close))
    1127.                             {
    1128.                                 continue;
    1129.                             }
    1130.  
    1131.                             break;
    1132.  
    1133.                         case ClusterSearchPriceLocation.Wick:
    1134.  
    1135.                             if (price <= Math.Max(cluster.Open, cluster.Close) &&
    1136.                                 price >= Math.Min(cluster.Open, cluster.Close))
    1137.                             {
    1138.                                 continue;
    1139.                             }
    1140.  
    1141.                             break;
    1142.  
    1143.                         case ClusterSearchPriceLocation.UpperWick:
    1144.  
    1145.                             if (price <= Math.Max(cluster.Open, cluster.Close))
    1146.                             {
    1147.                                 continue;
    1148.                             }
    1149.  
    1150.                             break;
    1151.  
    1152.                         case ClusterSearchPriceLocation.LowerWick:
    1153.  
    1154.                             if (price >= Math.Min(cluster.Open, cluster.Close))
    1155.                             {
    1156.                                 continue;
    1157.                             }
    1158.  
    1159.                             break;
    1160.                     }
    1161.  
    1162.                     if (RangeFromHigh.HasValue && item.Price < cluster.High - RangeFromHigh.Value)
    1163.                     {
    1164.                         continue;
    1165.                     }
    1166.  
    1167.                     if (RangeFromLow.HasValue && item.Price > cluster.Low + RangeFromLow.Value)
    1168.                     {
    1169.                         continue;
    1170.                     }
    1171.  
    1172.                     var avgTrade = item.Volume / (double)item.Trades;
    1173.  
    1174.                     if (minAvgTrade.HasValue && avgTrade < minAvgTrade.Value)
    1175.                     {
    1176.                         continue;
    1177.                     }
    1178.  
    1179.                     if (maxAvgTrade.HasValue && avgTrade > maxAvgTrade)
    1180.                     {
    1181.                         continue;
    1182.                     }
    1183.  
    1184.                     var result = false;
    1185.  
    1186.                     var totalValue = 0L;
    1187.                     var priceHigh = 0L;
    1188.                     var priceLow = 0L;
    1189.  
    1190.                     if (PriceRangeDir == ClusterSearchPriceRangeDirection.Downward ||
    1191.                         PriceRangeDir == ClusterSearchPriceRangeDirection.All)
    1192.                     {
    1193.                         var currentResult = true;
    1194.  
    1195.                         var currentValue = 0L;
    1196.                         var currentBid = 0L;
    1197.                         var currentAsk = 0L;
    1198.  
    1199.                         var results = false;
    1200.  
    1201.                         var price2 = price;
    1202.  
    1203.                         for (var j = i - BarsRange + 1; j <= i; j++)
    1204.                         {
    1205.                             var extCluster = DataProvider.GetRawCluster(j);
    1206.  
    1207.                             if (extCluster == null)
    1208.                             {
    1209.                                 continue;
    1210.                             }
    1211.  
    1212.                             for (var p = price - PriceRange + 1; p <= price; p++)
    1213.                             {
    1214.                                 var extItem = extCluster.GetItem(p);
    1215.  
    1216.                                 if (extItem == null)
    1217.                                 {
    1218.                                     continue;
    1219.                                 }
    1220.  
    1221.                                 if (GetValue(cluster, extItem, minValue, ref currentValue, ref currentBid,
    1222.                                     ref currentAsk))
    1223.                                 {
    1224.                                     price2 = Math.Min(price2, p);
    1225.  
    1226.                                     results = true;
    1227.                                 }
    1228.                             }
    1229.                         }
    1230.  
    1231.                         if (!results || !CheckMinMax(currentValue, min, max))
    1232.                         {
    1233.                             currentResult = false;
    1234.                         }
    1235.  
    1236.                         if (currentResult && minDelta.HasValue)
    1237.                         {
    1238.                             var currentDelta = currentAsk - currentBid;
    1239.  
    1240.                             if ((minDelta.Value > 0 && currentDelta < minDelta.Value ||
    1241.                                  minDelta.Value < 0 && currentDelta > minDelta.Value))
    1242.                             {
    1243.                                 currentResult = false;
    1244.                             }
    1245.                         }
    1246.  
    1247.                         if (currentResult && bidAskImb.HasValue)
    1248.                         {
    1249.                             var ratio = bidAskImb.Value / 100.0;
    1250.  
    1251.                             var skip = true;
    1252.  
    1253.                             if (ratio > 0 && currentAsk > currentBid * ratio)
    1254.                             {
    1255.                                 skip = false;
    1256.                             }
    1257.                             else if (ratio < 0 && currentBid > currentAsk * -ratio)
    1258.                             {
    1259.                                 skip = false;
    1260.                             }
    1261.  
    1262.                             if (skip)
    1263.                             {
    1264.                                 currentResult = false;
    1265.                             }
    1266.                         }
    1267.  
    1268.                         if (currentResult)
    1269.                         {
    1270.                             totalValue = currentValue;
    1271.                             priceHigh = Math.Max(price, price2);
    1272.                             priceLow = Math.Min(price, price2);
    1273.  
    1274.                             result = true;
    1275.                         }
    1276.                     }
    1277.  
    1278.                     if (!result && (PriceRangeDir == ClusterSearchPriceRangeDirection.Upward ||
    1279.                                     PriceRangeDir == ClusterSearchPriceRangeDirection.All))
    1280.                     {
    1281.                         var currentResult = true;
    1282.  
    1283.                         var currentValue = 0L;
    1284.                         var currentBid = 0L;
    1285.                         var currentAsk = 0L;
    1286.  
    1287.                         var results = false;
    1288.  
    1289.                         var price2 = price;
    1290.  
    1291.                         for (var j = i - BarsRange + 1; j <= i; j++)
    1292.                         {
    1293.                             var extCluster = DataProvider.GetRawCluster(j);
    1294.  
    1295.                             if (extCluster == null)
    1296.                             {
    1297.                                 continue;
    1298.                             }
    1299.  
    1300.                             for (var p = price + PriceRange - 1; p >= price; p--)
    1301.                             {
    1302.                                 var extItem = extCluster.GetItem(p);
    1303.  
    1304.                                 if (extItem == null)
    1305.                                 {
    1306.                                     continue;
    1307.                                 }
    1308.  
    1309.                                 if (GetValue(cluster, extItem, minValue, ref currentValue, ref currentBid,
    1310.                                     ref currentAsk))
    1311.                                 {
    1312.                                     price2 = Math.Max(price2, p);
    1313.  
    1314.                                     results = true;
    1315.                                 }
    1316.                             }
    1317.                         }
    1318.  
    1319.                         if (!results || !CheckMinMax(currentValue, min, max))
    1320.                         {
    1321.                             currentResult = false;
    1322.                         }
    1323.  
    1324.                         if (currentResult && minDelta.HasValue)
    1325.                         {
    1326.                             var currentDelta = currentAsk - currentBid;
    1327.  
    1328.                             if ((minDelta.Value > 0 && currentDelta < minDelta.Value ||
    1329.                                  minDelta.Value < 0 && currentDelta > minDelta.Value))
    1330.                             {
    1331.                                 currentResult = false;
    1332.                             }
    1333.                         }
    1334.  
    1335.                         if (currentResult && bidAskImb.HasValue)
    1336.                         {
    1337.                             var ratio = bidAskImb.Value / 100.0;
    1338.  
    1339.                             var skip = true;
    1340.  
    1341.                             if (ratio > 0 && currentAsk > currentBid * ratio)
    1342.                             {
    1343.                                 skip = false;
    1344.                             }
    1345.                             else if (ratio < 0 && currentBid > currentAsk * -ratio)
    1346.                             {
    1347.                                 skip = false;
    1348.                             }
    1349.  
    1350.                             if (skip)
    1351.                             {
    1352.                                 currentResult = false;
    1353.                             }
    1354.                         }
    1355.  
    1356.                         if (currentResult)
    1357.                         {
    1358.                             totalValue = currentValue;
    1359.                             priceHigh = Math.Max(price, price2);
    1360.                             priceLow = Math.Min(price, price2);
    1361.  
    1362.                             result = true;
    1363.                         }
    1364.                     }
    1365.  
    1366.                     if (!result)
    1367.                     {
    1368.                         continue;
    1369.                     }
    1370.  
    1371.                     currBar.Add(new ClusterSearchItem(cluster.Time, priceHigh, priceLow, totalValue));
    1372.  
    1373.                     if (Alert.IsActive && i == DataProvider.Count - 1 &&
    1374.                         currBar.CheckSignal(price))
    1375.                     {
    1376.                         AddAlert(Alert, GetAlertText(price, totalValue));
    1377.                     }
    1378.  
    1379.                     var absValue = Math.Abs(totalValue);
    1380.  
    1381.                     _maxValue = _maxValue == 0 ? absValue : Math.Max(_maxValue, absValue);
    1382.                     _minValue = _minValue == 0 ? absValue : Math.Min(_minValue, absValue);
    1383.                 }
    1384.  
    1385.                 currBar.Update();
    1386.             }
    1387.  
    1388.             _lastFullID = Math.Max(DataProvider.Count - 2, 0);
    1389.         }
    1390.  
    1391.         private string GetAlertText(long price, long value)
    1392.         {
    1393.             var text = "Value";
    1394.  
    1395.             switch (Type)
    1396.             {
    1397.                 case ClusterSearchDataType.Volume:
    1398.  
    1399.                     text = "Volume";
    1400.  
    1401.                     break;
    1402.  
    1403.                 case ClusterSearchDataType.MaxVol:
    1404.  
    1405.                     text = "MaxVol";
    1406.  
    1407.                     break;
    1408.  
    1409.                 case ClusterSearchDataType.Trades:
    1410.  
    1411.                     text = "Trades";
    1412.  
    1413.                     break;
    1414.  
    1415.                 case ClusterSearchDataType.Bid:
    1416.  
    1417.                     text = "Bid";
    1418.  
    1419.                     break;
    1420.  
    1421.                 case ClusterSearchDataType.Ask:
    1422.  
    1423.                     text = "Ask";
    1424.  
    1425.                     break;
    1426.  
    1427.                 case ClusterSearchDataType.Delta:
    1428.  
    1429.                     text = "Delta";
    1430.  
    1431.                     break;
    1432.  
    1433.                 case ClusterSearchDataType.DeltaPlus:
    1434.  
    1435.                     text = "Delta+";
    1436.  
    1437.                     break;
    1438.  
    1439.                 case ClusterSearchDataType.DeltaMinus:
    1440.  
    1441.                     text = "Delta-";
    1442.  
    1443.                     break;
    1444.             }
    1445.  
    1446.             var val = Type == ClusterSearchDataType.Trades
    1447.                 ? DataProvider.Symbol.FormatTrades(value)
    1448.                 : DataProvider.Symbol.FormatRawSizeShort(value);
    1449.  
    1450.             return $"ClusterSearch: {text}: {val}, Price: {DataProvider.Symbol.FormatRawPrice(price, true)}.";
    1451.         }
    1452.  
    1453.         public override void Render(DxVisualQueue visual)
    1454.         {
    1455.             base.Render(visual);
    1456.  
    1457.             if (_rects == null)
    1458.             {
    1459.                 _rects = new List<ClusterSearchRect>();
    1460.             }
    1461.             else
    1462.             {
    1463.                 _rects.Clear();
    1464.             }
    1465.  
    1466.             var minSize = Math.Max(10, Math.Min(200, ObjectMinSize));
    1467.             var maxSize = Math.Min(200, Math.Max(ObjectMaxSize, 10));
    1468.  
    1469.             if (maxSize < minSize)
    1470.             {
    1471.                 maxSize = minSize;
    1472.             }
    1473.  
    1474.             var baseWidth = (maxSize - minSize) / 9.0;
    1475.  
    1476.             var range = _maxValue - _minValue;
    1477.  
    1478.             for (var c = 0; c < Canvas.Count; c++)
    1479.             {
    1480.                 var index = Canvas.GetIndex(c);
    1481.  
    1482.                 if (!Bars.ContainsKey(index))
    1483.                 {
    1484.                     continue;
    1485.                 }
    1486.  
    1487.                 var items = Bars[index].Items;
    1488.  
    1489.                 for (var i = 0; i < items.Count; i++)
    1490.                 {
    1491.                     var searchItem = items[i];
    1492.  
    1493.                     if (SingleSelection)
    1494.                     {
    1495.                         var time = searchItem.Time;
    1496.  
    1497.                         for (var j = i + 1; j < items.Count; j++)
    1498.                         {
    1499.                             var nextItem = items[j];
    1500.  
    1501.                             if (nextItem.Time == time)
    1502.                             {
    1503.                                 if (Math.Abs(nextItem.Value) > Math.Abs(searchItem.Value))
    1504.                                 {
    1505.                                     searchItem = nextItem;
    1506.                                 }
    1507.  
    1508.                                 i = j;
    1509.                             }
    1510.                             else
    1511.                             {
    1512.                                 break;
    1513.                             }
    1514.                         }
    1515.                     }
    1516.  
    1517.                     var step = DataProvider.Step;
    1518.  
    1519.                     var x = (int)GetX(Canvas.DateToIndex(searchItem.Time, -1));
    1520.                     var y1 = (int)GetY((searchItem.PriceHigh + .5) * step);
    1521.                     var y2 = (int)GetY((searchItem.PriceLow - .5) * step);
    1522.                     var y = (int)((y1 + y2) / 2.0);
    1523.  
    1524.                     var sizeStep = 9.0;
    1525.  
    1526.                     if (Math.Abs(searchItem.Value) != _maxValue && range > 0)
    1527.                     {
    1528.                         sizeStep = (Math.Abs(searchItem.Value) - _minValue) / (range / 9.0);
    1529.                     }
    1530.  
    1531.                     var width = (int)((minSize + baseWidth * sizeStep) / 2.0);
    1532.  
    1533.                     _rects.Add(
    1534.                         new ClusterSearchRect(
    1535.                             new Rect(new Point(x - width, y - width), new Point(x + width, y + width)),
    1536.                             searchItem));
    1537.  
    1538.                     switch (ObjectType)
    1539.                     {
    1540.                         case ClusterSearchObjectType.Rectangle:
    1541.                             {
    1542.                                 var rect = new Rect(new Point(x - width, y - width), new Point(x + width, y + width));
    1543.  
    1544.                                 visual.FillRectangle(_objectBackBrush, rect);
    1545.                                 visual.DrawRectangle(_objectBorderPen, rect);
    1546.  
    1547.                                 break;
    1548.                             }
    1549.  
    1550.                         case ClusterSearchObjectType.Triangle:
    1551.                             {
    1552.                                 var points = new Point[3];
    1553.  
    1554.                                 points[0] = new Point(x, y - width);
    1555.                                 points[1] = new Point(x + width, y + width);
    1556.                                 points[2] = new Point(x - width, y + width);
    1557.  
    1558.                                 visual.FillPolygon(_objectBackBrush, points);
    1559.                                 visual.DrawPolygon(_objectBorderPen, points);
    1560.  
    1561.                                 break;
    1562.                             }
    1563.  
    1564.                         case ClusterSearchObjectType.Diamond:
    1565.                             {
    1566.                                 var points = new Point[4];
    1567.  
    1568.                                 points[0] = new Point(x - width, y);
    1569.                                 points[1] = new Point(x, y - width);
    1570.                                 points[2] = new Point(x + width, y);
    1571.                                 points[3] = new Point(x, y + width);
    1572.  
    1573.                                 visual.FillPolygon(_objectBackBrush, points);
    1574.                                 visual.DrawPolygon(_objectBorderPen, points);
    1575.  
    1576.                                 break;
    1577.                             }
    1578.                         case ClusterSearchObjectType.Circle:
    1579.                             {
    1580.                                 visual.FillEllipse(_objectBackBrush, new Point(x, y), width, width);
    1581.                                 visual.DrawEllipse(_objectBorderPen, new Point(x, y), width, width);
    1582.  
    1583.                                 break;
    1584.                             }
    1585.                     }
    1586.  
    1587.                     if (Canvas.StockType == ChartStockType.Clusters)
    1588.                     {
    1589.                         continue;
    1590.                     }
    1591.  
    1592.                     var selectionWidth = (int)Math.Max(Canvas.ColumnWidth / 2.0 - 1, 2.0);
    1593.  
    1594.                     if (Type == ClusterSearchDataType.Bid)
    1595.                     {
    1596.                         visual.FillRectangle(_selectionBrush,
    1597.                             new Rect(new Point(x - selectionWidth, y1), new Point(x + 1, y2)));
    1598.                     }
    1599.                     else if (Type == ClusterSearchDataType.Ask)
    1600.                     {
    1601.                         visual.FillRectangle(_selectionBrush,
    1602.                             new Rect(new Point(x, y1), new Point(x + selectionWidth + 1, y2)));
    1603.                     }
    1604.                     else
    1605.                     {
    1606.                         visual.FillRectangle(_selectionBrush,
    1607.                             new Rect(new Point(x - selectionWidth, y1), new Point(x + selectionWidth + 1, y2)));
    1608.  
    1609.                     }
    1610.                 }
    1611.             }
    1612.         }
    1613.  
    1614.         public override void RenderCursor(DxVisualQueue visual, int cursorPos, Point cursorCenter, ref int topPos)
    1615.         {
    1616.             if ((Keyboard.Modifiers & ModifierKeys.Control) == 0 || _rects == null || _rects.Count == 0)
    1617.             {
    1618.                 return;
    1619.             }
    1620.  
    1621.             var textLabels = new List<string>();
    1622.  
    1623.             foreach (var rect in _rects)
    1624.             {
    1625.                 if (rect.Contains(cursorCenter))
    1626.                 {
    1627.                     textLabels.Add(rect.GetLabel(Type, DataProvider, ToString()));
    1628.                 }
    1629.             }
    1630.  
    1631.             if (textLabels.Count == 0)
    1632.             {
    1633.                 return;
    1634.             }
    1635.  
    1636.             var left = cursorCenter.X + 15;
    1637.             var top = cursorCenter.Y + 13 + topPos;
    1638.             var width = 0.0;
    1639.             var height = 0.0;
    1640.  
    1641.             var textRects = new List<Tuple<string, Rect>>();
    1642.  
    1643.             foreach (var textLabel in textLabels)
    1644.             {
    1645.                 var size = Canvas.ChartFont.GetSize(textLabel);
    1646.  
    1647.                 width = Math.Max(width, size.Width);
    1648.  
    1649.                 var textRect = new Rect(left, top + height + 2, width, size.Height + 2);
    1650.  
    1651.                 height += textRect.Height + 2;
    1652.  
    1653.                 textRects.Add(new Tuple<string, Rect>(textLabel, textRect));
    1654.             }
    1655.  
    1656.             var boxX = cursorCenter.X + 10;
    1657.             var leftCorrect = 0.0;
    1658.  
    1659.             if (boxX + width + 10 >= Canvas.Rect.Right)
    1660.             {
    1661.                 boxX -= width + 30;
    1662.                 leftCorrect = -(width + 30);
    1663.             }
    1664.  
    1665.             var boxY = cursorCenter.Y + 10;
    1666.             var topCorrect = 0.0;
    1667.  
    1668.             if (topPos == 0)
    1669.             {
    1670.                 if (boxY + height + 10 >= Canvas.Rect.Bottom)
    1671.                 {
    1672.                     boxY -= height + 30;
    1673.                     topCorrect = -(height + 30);
    1674.                 }
    1675.             }
    1676.  
    1677.             var boxRect = new Rect(boxX, boxY + topPos,
    1678.                 width + 10, height + 7);
    1679.  
    1680.             topPos += (int)boxRect.Height + 5 + (int)topCorrect;
    1681.  
    1682.             visual.FillRectangle(Canvas.Theme.ChartBackBrush, boxRect);
    1683.  
    1684.             visual.DrawRectangle(new XPen(new XBrush(Canvas.Theme.ChartAxisColor), 1), boxRect);
    1685.  
    1686.             foreach (var textRect in textRects)
    1687.             {
    1688.                 visual.DrawString(textRect.Item1, Canvas.ChartFont, Canvas.Theme.ChartFontBrush,
    1689.                     new Rect(textRect.Item2.X + leftCorrect, textRect.Item2.Y + topCorrect, textRect.Item2.Width,
    1690.                         textRect.Item2.Height));
    1691.             }
    1692.         }
    1693.  
    1694.         public override IndicatorTitleInfo GetTitle()
    1695.         {
    1696.             return new IndicatorTitleInfo(Title, _selectionBrush);
    1697.         }
    1698.  
    1699.         public override void CopyTemplate(IndicatorBase indicator, bool style)
    1700.         {
    1701.             var i = (ClusterSearchIndicator)indicator;
    1702.  
    1703.             Type = i.Type;
    1704.  
    1705.             Alert.Copy(i.Alert, !style);
    1706.  
    1707.             OnPropertyChanged(nameof(Alert));
    1708.  
    1709.             MaximumParam.Copy(i.MaximumParam);
    1710.             MinimumParam.Copy(i.MinimumParam);
    1711.  
    1712.             SelectionColor = i.SelectionColor;
    1713.             ObjectBackColor = i.ObjectBackColor;
    1714.             ObjectBorderColor = i.ObjectBorderColor;
    1715.             ObjectType = i.ObjectType;
    1716.             ObjectMinSize = i.ObjectMinSize;
    1717.             ObjectMaxSize = i.ObjectMaxSize;
    1718.  
    1719.             BarsRange = i.BarsRange;
    1720.             PriceRange = i.PriceRange;
    1721.             PriceRangeDir = i.PriceRangeDir;
    1722.             MinValueParam.Copy(i.MinValueParam);
    1723.             MinDelta = i.MinDelta;
    1724.             BidAskImbalance = i.BidAskImbalance;
    1725.             RangeFromHigh = i.RangeFromHigh;
    1726.             RangeFromLow = i.RangeFromLow;
    1727.             MaxAvgTrade = i.MaxAvgTrade;
    1728.             MinAvgTrade = i.MinAvgTrade;
    1729.             BarDirection = i.BarDirection;
    1730.             PriceLocation = i.PriceLocation;
    1731.             SingleSelection = i.SingleSelection;
    1732.  
    1733.             UseTimeFilter = i.UseTimeFilter;
    1734.             StartTime = i.StartTime;
    1735.             EndTime = i.EndTime;
    1736.  
    1737.             OnPropertyChanged(nameof(Maximum));
    1738.             OnPropertyChanged(nameof(Minimum));
    1739.  
    1740.             base.CopyTemplate(indicator, style);
    1741.         }
    1742.  
    1743.         public XBrush GetSelection(int index, long price, int type)
    1744.         {
    1745.             if (!ShowIndicator || !Bars.ContainsKey(index))
    1746.             {
    1747.                 return null;
    1748.             }
    1749.  
    1750.             switch (Type)
    1751.             {
    1752.                 case ClusterSearchDataType.Volume:
    1753.                 case ClusterSearchDataType.MaxVol:
    1754.  
    1755.                     if (type == 1)
    1756.                     {
    1757.                         return null;
    1758.                     }
    1759.  
    1760.                     break;
    1761.  
    1762.                 case ClusterSearchDataType.Trades:
    1763.  
    1764.                     if (type == 2)
    1765.                     {
    1766.                         return null;
    1767.                     }
    1768.  
    1769.                     break;
    1770.  
    1771.                 case ClusterSearchDataType.Bid:
    1772.  
    1773.                     if (type == 3)
    1774.                     {
    1775.                         return null;
    1776.                     }
    1777.  
    1778.                     break;
    1779.  
    1780.                 case ClusterSearchDataType.Ask:
    1781.  
    1782.                     if (type == 4)
    1783.                     {
    1784.                         return null;
    1785.                     }
    1786.  
    1787.                     break;
    1788.  
    1789.                 case ClusterSearchDataType.Delta:
    1790.                 case ClusterSearchDataType.DeltaPlus:
    1791.                 case ClusterSearchDataType.DeltaMinus:
    1792.  
    1793.                     if (type == 5)
    1794.                     {
    1795.                         return null;
    1796.                     }
    1797.  
    1798.                     break;
    1799.             }
    1800.  
    1801.             return (SingleSelection
    1802.                 ? Bars[index].SingleSelection.Contains(price)
    1803.                 : Bars[index].Selections.Contains(price))
    1804.                 ? _selectionBrush
    1805.                 : null;
    1806.         }
    1807.  
    1808.         public override string ToString()
    1809.         {
    1810.             return Maximum == null ? $"*CS({Type}, {Minimum}+)" : $"*CS({Type}, {Minimum}-{Maximum ?? 0})";
    1811.         }
    1812.     }
    1813. }
     
    #1 Support, 26 июл 2019
    Последнее редактирование: 26 июл 2019