// generate combined signals per date
 private IEnumerable<TimedSignal> GenerateCombinedSignals( Dictionary<DateTime, IList<Signal>> dateSignalsMap )
 {
     foreach ( var entry in dateSignalsMap )
     {
         var combinedSignal = new TimedSignal( entry.Key, Create( entry.Value ) );
         yield return combinedSignal;
     }
 }
        public ISignalSeries Generate(IPriceSeries referencePrices, IPriceSeries indicatorPoints)
        {
            var signals            = new List <TimedSignal>();
            var signalDaysInterval = ClosedInterval.FromOffsetLength(MinDaysAfterCutForSignal, KeepSignalForMaxDays);

            var prevPrice = referencePrices.First();
            TimedValue <DateTime, double> prevIndicatorPoint = null;
            TimedSignal activeSignal = null;

            foreach (var price in referencePrices.Skip(1))
            {
                var indicatorPoint = indicatorPoints.TryGet(price.Time);
                if (indicatorPoint == null)
                {
                    signals.Add(new TimedSignal(price.Time, Signal.None));
                    continue;
                }

                if (prevIndicatorPoint == null)
                {
                    prevIndicatorPoint = indicatorPoint;
                    continue;
                }

                if (prevPrice.Value < prevIndicatorPoint.Value && price.Value > indicatorPoint.Value)
                {
                    var signal = new TimedSignal(indicatorPoint.Time, new BuySignal());

                    if (signalDaysInterval.IsEmpty)
                    {
                        signals.Add(signal);
                    }
                    else
                    {
                        activeSignal = signal;
                    }
                }
                else if (prevPrice.Value > prevIndicatorPoint.Value && price.Value < indicatorPoint.Value)
                {
                    var signal = new TimedSignal(indicatorPoint.Time, new SellSignal());

                    if (signalDaysInterval.IsEmpty)
                    {
                        signals.Add(signal);
                    }
                    else
                    {
                        activeSignal = signal;
                    }
                }

                if (activeSignal != null)
                {
                    // we have a cut signal -> handle it
                    int daysSinceCut = (int)Math.Round((price.Time - activeSignal.Time).TotalDays);

                    // if we are in defined range -> add the signal
                    if (signalDaysInterval.Includes(daysSinceCut))
                    {
                        signals.Add(new TimedSignal(indicatorPoint.Time, activeSignal.Value));
                    }

                    if (daysSinceCut > signalDaysInterval.Max)
                    {
                        // left the interval -> reset the signal
                        activeSignal = null;
                    }
                }

                prevPrice          = price;
                prevIndicatorPoint = indicatorPoint;
            }

            return(new SignalSeries(referencePrices, indicatorPoints.Identifier, signals));
        }
        public ISignalSeries Generate( IPriceSeries referencePrices, IPriceSeries indicatorPoints )
        {
            var signals = new List<TimedSignal>();
            var signalDaysInterval = ClosedInterval.FromOffsetLength( MinDaysAfterCutForSignal, KeepSignalForMaxDays );

            var prevPrice = referencePrices.First();
            TimedValue<DateTime,double> prevIndicatorPoint = null;
            TimedSignal activeSignal = null;
            foreach ( var price in referencePrices.Skip( 1 ) )
            {
                var indicatorPoint = indicatorPoints.TryGet( price.Time );
                if ( indicatorPoint == null )
                {
                    signals.Add( new TimedSignal( price.Time, Signal.None ) );
                    continue;
                }

                if ( prevIndicatorPoint == null )
                {
                    prevIndicatorPoint = indicatorPoint;
                    continue;
                }

                if ( prevPrice.Value < prevIndicatorPoint.Value && price.Value > indicatorPoint.Value )
                {
                    var signal = new TimedSignal( indicatorPoint.Time, new BuySignal() );

                    if ( signalDaysInterval.IsEmpty )
                    {
                        signals.Add( signal );
                    }
                    else
                    {
                        activeSignal = signal;
                    }
                }
                else if ( prevPrice.Value > prevIndicatorPoint.Value && price.Value < indicatorPoint.Value )
                {
                    var signal = new TimedSignal( indicatorPoint.Time, new SellSignal() );

                    if ( signalDaysInterval.IsEmpty )
                    {
                        signals.Add( signal );
                    }
                    else
                    {
                        activeSignal = signal;
                    }
                }

                if ( activeSignal != null )
                {
                    // we have a cut signal -> handle it
                    int daysSinceCut = (int)Math.Round( ( price.Time - activeSignal.Time ).TotalDays );

                    // if we are in defined range -> add the signal
                    if ( signalDaysInterval.Includes( daysSinceCut ) )
                    {
                        signals.Add( new TimedSignal( indicatorPoint.Time, activeSignal.Value ) );
                    }

                    if ( daysSinceCut > signalDaysInterval.Max )
                    {
                        // left the interval -> reset the signal
                        activeSignal = null;
                    }
                }

                prevPrice = price;
                prevIndicatorPoint = indicatorPoint;
            }

            return new SignalSeries( referencePrices, indicatorPoints.Identifier, signals );
        }