public override object DeserializeProperty(XmlNode parent)
        {
            var sets = new List<MultyTimeframeIndexSettings>();
            foreach (var child in parent.ChildNodes)
            {
                if (child is XmlElement == false) continue;
                var node = (XmlElement) child;
                if (node.Name != "chartSeries") continue;

                var set = new MultyTimeframeIndexSettings();
                set.chartId = node.Attributes["chartId"].Value;
                set.FullyQualifiedSeriesDestName = node.Attributes["seriesDest"].Value;
                set.FullyQualifiedSeriesSrcName = node.Attributes["seriesSrc"].Value;
                set.TimeframeAndSymbol = node.Attributes["tfSmb"].Value;
                set.DiverType = (IndicatorDiver.DivergenceType)
                    Enum.Parse(typeof(IndicatorDiver.DivergenceType), node.Attributes["diverType"].Value);
                set.maxPastExtremum = node.Attributes["maxPastExtr"].Value.ToInt();
                set.periodExtremum = node.Attributes["periodExtremum"].Value.ToInt();
                set.marginUpper = node.Attributes["marginUpper"].Value.ToDoubleUniform();
                set.marginLower = node.Attributes["marginLower"].Value.ToDoubleUniform();

                sets.Add(set);
            }
            return sets;
        }
        public override object DeserializeProperty(XmlNode parent)
        {
            var sets = new List <MultyTimeframeIndexSettings>();

            foreach (var child in parent.ChildNodes)
            {
                if (child is XmlElement == false)
                {
                    continue;
                }
                var node = (XmlElement)child;
                if (node.Name != "chartSeries")
                {
                    continue;
                }

                var set = new MultyTimeframeIndexSettings();
                set.chartId = node.Attributes["chartId"].Value;
                set.FullyQualifiedSeriesDestName = node.Attributes["seriesDest"].Value;
                set.FullyQualifiedSeriesSrcName  = node.Attributes["seriesSrc"].Value;
                set.TimeframeAndSymbol           = node.Attributes["tfSmb"].Value;
                set.DiverType = (IndicatorDiver.DivergenceType)
                                Enum.Parse(typeof(IndicatorDiver.DivergenceType), node.Attributes["diverType"].Value);
                set.maxPastExtremum = node.Attributes["maxPastExtr"].Value.ToInt();
                set.periodExtremum  = node.Attributes["periodExtremum"].Value.ToInt();
                set.marginUpper     = node.Attributes["marginUpper"].Value.ToDoubleUniform();
                set.marginLower     = node.Attributes["marginLower"].Value.ToDoubleUniform();

                sets.Add(set);
            }
            return(sets);
        }
        /// <summary>
        /// построить дивергенции
        /// </summary>
        private void BuildDivers(MultyTimeframeIndexSettings src, Dictionary <int, List <DiverMarker> > signs)
        {
            // получить диверы
            var divers =
                src.DiverType == IndicatorDiver.DivergenceType.Классические
                    ? FindDivergencePointsClassic(src)
                    : FindDivergencePointsQuasi(src);

            // добавить их в словарь
            foreach (var diver in divers)
            {
                // пересчитать индекс свечки
                var index = diver.end;
                if (src.seriesDest is StockSeries)
                {
                    var time = ((StockSeries)src.seriesDest).GetCandleOpenTimeByIndex(index);
                    index = owner.StockSeries.GetIndexByCandleOpen(time);
                }

                // создать для свечки список маркеров или добавить готовый
                var marker = new DiverMarker(src.InverseDivergency ? -diver.sign : diver.sign,
                                             src.TimeframeAndSymbol);
                List <DiverMarker> markers;
                signs.TryGetValue(index, out markers);
                if (markers == null)
                {
                    markers = new List <DiverMarker> {
                        marker
                    };
                    signs.Add(index, markers);
                }
                else
                {
                    markers.Add(marker);
                }
            }
        }
        /// <summary>
        /// Принять изменения
        /// </summary>
        private void BtnOkClick(object sender, EventArgs e)
        {
            DialogResult = DialogResult.Cancel;
            if (cbChart.SelectedIndex < 0) return;
            var chart = (CandleChartControlTag)cbChart.Items[cbChart.SelectedIndex];

            if (sets == null) sets = new MultyTimeframeIndexSettings();
            sets.chartId = chart.chart.UniqueId;
            sets.InverseDivergency = cbInverse.Checked;
            sets.FullyQualifiedSeriesSrcName = cbSeriesSrc.Text;
            sets.FullyQualifiedSeriesDestName = cbSeriesDest.Text;
            sets.TimeframeAndSymbol = cbChart.Text;
            sets.DiverType = (IndicatorDiver.DivergenceType)cbDiverType.SelectedItem;
            sets.periodExtremum = tbPeriodExtremum.Text.ToInt();
            sets.maxPastExtremum = tbMaxPastExtremum.Text.ToInt();
            // интервал
            var parts = tbIntervalMargins.Text.Split(new[] {' ', ',', ';', (char) 9},
                                                     StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length == 2)
            {
                sets.marginLower = (double)(parts[0].ToDecimalUniformSafe() ?? 0);
                sets.marginUpper = (double)(parts[1].ToDecimalUniformSafe() ?? 0);
            }

            DialogResult = DialogResult.OK;
        }
 public SetupChartSeriesDlg(List<CandleChartControl> charts, MultyTimeframeIndexSettings sets)
     : this()
 {
     this.charts = charts;
     this.sets = sets;
 }
        private List<DiverSpan> FindDivergencePointsQuasi(MultyTimeframeIndexSettings src)
        {
            var divers = new List<DiverSpan>();
            var srcCount = src.seriesDest.DataCount;

            float? lastPeak = null;
            var peakPrice = 0f;
            var extremumSign = 0;
            var startIndex = 0;

            for (var i = 0; i < srcCount; i++)
            {
                var index = src.GetSourcePrice(i) ?? 0;
                var indexNext = i < srcCount ? (src.GetSourcePrice(i + 1) ?? 0) : index;
                // если находимся в зоне перекупленности / перепроданности,
                // если след. точка ниже (выше для перепроданности),
                // если текущая точка выше последнего максимума (ниже минимума...)

                var margUp = index >= src.marginUpper;
                var margDn = index <= src.marginLower;

                if ((margUp && indexNext < index &&
                    index > (lastPeak.HasValue ? lastPeak.Value : float.MinValue)) ||
                    (margDn && indexNext > index && index < (lastPeak.HasValue ? lastPeak.Value : float.MaxValue)))
                {// + или - экстремум
                    peakPrice = src.GetDestPrice(i) ?? 0;
                    extremumSign = margUp ? 1 : -1;
                    startIndex = i;
                    lastPeak = index;
                    continue;
                }
                if (!margUp && margDn == false)
                {// конец отсчета экстремума
                    extremumSign = 0;
                    lastPeak = null;
                    continue;
                }
                // отсчет экстремума - сравнить дельту цены и дельту индекса
                var deltaPrice = (src.GetDestPrice(i) ?? 0) - peakPrice;
                var deltaIndex = index - lastPeak;

                if (extremumSign > 0)
                    if (deltaPrice > 0 && deltaIndex < 0)
                    // медвежий дивер
                    {
                        divers.Add(new DiverSpan(startIndex, i, -1));
                        peakPrice = src.GetDestPrice(i) ?? 0;
                    }
                if (extremumSign < 0)
                    if (deltaPrice < 0 && deltaIndex > 0)
                    // бычий дивер
                    {
                        divers.Add(new DiverSpan(startIndex, i, 1));
                        peakPrice = src.GetDestPrice(i) ?? 0;
                    }
            }
            return divers;
        }
        /// <summary>
        /// построить дивергенции
        /// </summary>
        private void BuildDivers(MultyTimeframeIndexSettings src, Dictionary<int, List<DiverMarker>> signs)
        {
            // получить диверы
            var divers =
                src.DiverType == IndicatorDiver.DivergenceType.Классические
                    ? FindDivergencePointsClassic(src)
                    : FindDivergencePointsQuasi(src);

            // добавить их в словарь
            foreach (var diver in divers)
            {
                // пересчитать индекс свечки
                var index = diver.end;
                if (src.seriesDest is StockSeries)
                {
                    var time = ((StockSeries) src.seriesDest).GetCandleOpenTimeByIndex(index);
                    index = owner.StockSeries.GetIndexByCandleOpen(time);
                }

                // создать для свечки список маркеров или добавить готовый
                var marker = new DiverMarker(src.InverseDivergency ? -diver.sign : diver.sign,
                    src.TimeframeAndSymbol);
                List<DiverMarker> markers;
                signs.TryGetValue(index, out markers);
                if (markers == null)
                {
                    markers = new List<DiverMarker> { marker };
                    signs.Add(index, markers);
                }
                else
                    markers.Add(marker);
            }
        }
        private static IEnumerable<DiverSpan> FindDivergencePointsClassic(MultyTimeframeIndexSettings src)
        {
            var diverPairs = new List<DiverSpan>();
            var srcCount = src.seriesDest.DataCount;

            // найти экстремумы
            var extremums = new List<Point>(); // index - sign
            for (var i = src.periodExtremum; i < srcCount - src.periodExtremum - 1; i++)
            {
                bool isPeak = true, isLow = true;
                var price = src.GetDestPrice(i) ?? 0;
                for (var j = i - src.periodExtremum; j <= i + src.periodExtremum; j++)
                {
                    if (j == i) continue;
                    var priceNear = src.GetDestPrice(j);
                    if (priceNear > price) isPeak = false;
                    if (priceNear < price) isLow = false;
                    if (!isPeak && isLow == false) break;
                }
                if (isPeak) extremums.Add(new Point(i, 1));
                if (isLow) extremums.Add(new Point(i, -1));
            }

            // от каждого экстремумы поиск вперед до M баров - поиск экстремума на интервале [i-N...i+1]
            foreach (var extr in extremums)
            {
                var signExtr = extr.Y;
                var indexExtr = extr.X;
                var priceExtr = src.GetDestPrice(indexExtr);

                for (var i = extr.X + 1; i <= (extr.X + src.maxPastExtremum); i++)
                {
                    if (i >= (srcCount - 1)) break;
                    // новый "экстремум" должен быть выше старого
                    var price = src.GetDestPrice(i) ?? 0;
                    if ((signExtr > 0 && price <= priceExtr) ||
                        (signExtr < 0 && price >= priceExtr)) continue;
                    // за новым "экстремумом" должна следовать точка ниже (выше для минимума)
                    var nextPrice = src.GetDestPrice(i + 1) ?? 0;
                    if ((signExtr > 0 && price <= nextPrice) ||
                        (signExtr < 0 && price >= nextPrice)) continue;
                    // сравнение с дельтой индекса
                    // !! смещение индекса
                    var srcPriceI = src.GetSourcePrice(i) ?? 0;
                    var srcPriceEx = src.GetSourcePrice(indexExtr) ?? 0;
                    var deltaIndex = Math.Sign(srcPriceI - srcPriceEx);
                    if (signExtr == deltaIndex) continue;
                    // экстремум найден
                    priceExtr = price;
                    diverPairs.Add(new DiverSpan(indexExtr, i, -signExtr));
                }
            }

            return diverPairs;
        }
        private List <DiverSpan> FindDivergencePointsQuasi(MultyTimeframeIndexSettings src)
        {
            var divers   = new List <DiverSpan>();
            var srcCount = src.seriesDest.DataCount;

            float?lastPeak     = null;
            var   peakPrice    = 0f;
            var   extremumSign = 0;
            var   startIndex   = 0;

            for (var i = 0; i < srcCount; i++)
            {
                var index     = src.GetSourcePrice(i) ?? 0;
                var indexNext = i < srcCount ? (src.GetSourcePrice(i + 1) ?? 0) : index;
                // если находимся в зоне перекупленности / перепроданности,
                // если след. точка ниже (выше для перепроданности),
                // если текущая точка выше последнего максимума (ниже минимума...)

                var margUp = index >= src.marginUpper;
                var margDn = index <= src.marginLower;

                if ((margUp && indexNext < index &&
                     index > (lastPeak.HasValue ? lastPeak.Value : float.MinValue)) ||
                    (margDn && indexNext > index && index < (lastPeak.HasValue ? lastPeak.Value : float.MaxValue)))
                {// + или - экстремум
                    peakPrice    = src.GetDestPrice(i) ?? 0;
                    extremumSign = margUp ? 1 : -1;
                    startIndex   = i;
                    lastPeak     = index;
                    continue;
                }
                if (!margUp && margDn == false)
                {// конец отсчета экстремума
                    extremumSign = 0;
                    lastPeak     = null;
                    continue;
                }
                // отсчет экстремума - сравнить дельту цены и дельту индекса
                var deltaPrice = (src.GetDestPrice(i) ?? 0) - peakPrice;
                var deltaIndex = index - lastPeak;

                if (extremumSign > 0)
                {
                    if (deltaPrice > 0 && deltaIndex < 0)
                    // медвежий дивер
                    {
                        divers.Add(new DiverSpan(startIndex, i, -1));
                        peakPrice = src.GetDestPrice(i) ?? 0;
                    }
                }
                if (extremumSign < 0)
                {
                    if (deltaPrice < 0 && deltaIndex > 0)
                    // бычий дивер
                    {
                        divers.Add(new DiverSpan(startIndex, i, 1));
                        peakPrice = src.GetDestPrice(i) ?? 0;
                    }
                }
            }
            return(divers);
        }
        private static IEnumerable <DiverSpan> FindDivergencePointsClassic(MultyTimeframeIndexSettings src)
        {
            var diverPairs = new List <DiverSpan>();
            var srcCount   = src.seriesDest.DataCount;

            // найти экстремумы
            var extremums = new List <Point>(); // index - sign

            for (var i = src.periodExtremum; i < srcCount - src.periodExtremum - 1; i++)
            {
                bool isPeak = true, isLow = true;
                var  price = src.GetDestPrice(i) ?? 0;
                for (var j = i - src.periodExtremum; j <= i + src.periodExtremum; j++)
                {
                    if (j == i)
                    {
                        continue;
                    }
                    var priceNear = src.GetDestPrice(j);
                    if (priceNear > price)
                    {
                        isPeak = false;
                    }
                    if (priceNear < price)
                    {
                        isLow = false;
                    }
                    if (!isPeak && isLow == false)
                    {
                        break;
                    }
                }
                if (isPeak)
                {
                    extremums.Add(new Point(i, 1));
                }
                if (isLow)
                {
                    extremums.Add(new Point(i, -1));
                }
            }

            // от каждого экстремумы поиск вперед до M баров - поиск экстремума на интервале [i-N...i+1]
            foreach (var extr in extremums)
            {
                var signExtr  = extr.Y;
                var indexExtr = extr.X;
                var priceExtr = src.GetDestPrice(indexExtr);

                for (var i = extr.X + 1; i <= (extr.X + src.maxPastExtremum); i++)
                {
                    if (i >= (srcCount - 1))
                    {
                        break;
                    }
                    // новый "экстремум" должен быть выше старого
                    var price = src.GetDestPrice(i) ?? 0;
                    if ((signExtr > 0 && price <= priceExtr) ||
                        (signExtr < 0 && price >= priceExtr))
                    {
                        continue;
                    }
                    // за новым "экстремумом" должна следовать точка ниже (выше для минимума)
                    var nextPrice = src.GetDestPrice(i + 1) ?? 0;
                    if ((signExtr > 0 && price <= nextPrice) ||
                        (signExtr < 0 && price >= nextPrice))
                    {
                        continue;
                    }
                    // сравнение с дельтой индекса
                    // !! смещение индекса
                    var srcPriceI  = src.GetSourcePrice(i) ?? 0;
                    var srcPriceEx = src.GetSourcePrice(indexExtr) ?? 0;
                    var deltaIndex = Math.Sign(srcPriceI - srcPriceEx);
                    if (signExtr == deltaIndex)
                    {
                        continue;
                    }
                    // экстремум найден
                    priceExtr = price;
                    diverPairs.Add(new DiverSpan(indexExtr, i, -signExtr));
                }
            }

            return(diverPairs);
        }