Beispiel #1
0
    // MOVING AVERAGE ENVELOPES
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <MaEnvelopeResult> GetMaEnvelopes <TQuote>(
        this IEnumerable <TQuote> quotes,
        int lookbackPeriods,
        double percentOffset     = 2.5,
        MaType movingAverageType = MaType.SMA)
        where TQuote : IQuote
    {
        // check parameter arguments
        // note: most validations are done in variant methods
        ValidateMaEnvelopes(percentOffset);

        // initialize
        decimal offsetRatio = (decimal)percentOffset / 100m;

        // get envelopes variant
        return(movingAverageType switch
        {
            MaType.ALMA => quotes.MaEnvAlma(lookbackPeriods, offsetRatio),
            MaType.DEMA => quotes.MaEnvDema(lookbackPeriods, offsetRatio),
            MaType.EMA => quotes.MaEnvEma(lookbackPeriods, offsetRatio),
            MaType.EPMA => quotes.MaEnvEpma(lookbackPeriods, offsetRatio),
            MaType.HMA => quotes.MaEnvHma(lookbackPeriods, offsetRatio),
            MaType.SMA => quotes.MaEnvSma(lookbackPeriods, offsetRatio),
            MaType.SMMA => quotes.MaEnvSmma(lookbackPeriods, offsetRatio),
            MaType.TEMA => quotes.MaEnvTema(lookbackPeriods, offsetRatio),
            MaType.WMA => quotes.MaEnvWma(lookbackPeriods, offsetRatio),

            _ => throw new ArgumentOutOfRangeException(
                nameof(movingAverageType), movingAverageType,
                string.Format(
                    EnglishCulture,
                    "Moving Average Envelopes does not support {0}.",
                    Enum.GetName(typeof(MaType), movingAverageType)))
        });
Beispiel #2
0
    // internals
    private static List <StochResult> SmoothOscillator(
        List <StochResult> results,
        int length,
        int lookbackPeriods,
        int smoothPeriods,
        MaType movingAverageType)
    {
        // temporarily store interim smoothed oscillator
        double?[] smooth = new double?[length]; // smoothed value

        if (movingAverageType is MaType.SMA)
        {
            int smoothIndex = lookbackPeriods + smoothPeriods - 2;

            for (int i = smoothIndex; i < length; i++)
            {
                int index = i + 1;

                double?sumOsc = 0;
                for (int p = index - smoothPeriods; p < index; p++)
                {
                    sumOsc += (double?)results[p].Oscillator;
                }

                smooth[i] = sumOsc / smoothPeriods;
            }
        }
        else if (movingAverageType is MaType.SMMA)
        {
            // initialize with unsmoothed value
            double?k = (double?)results[lookbackPeriods - 1].Oscillator;

            for (int i = lookbackPeriods - 1; i < length; i++)
            {
                k = (k == null) ? (double?)results[i].Oscillator : k; // reset if null

                k         = ((k * (smoothPeriods - 1)) + (double?)results[i].Oscillator) / smoothPeriods;
                smooth[i] = k;
            }
        }
        else
        {
            return(results);
        }

        // replace oscillator
        for (int i = 0; i < length; i++)
        {
            results[i].Oscillator = (smooth[i] != null) ? (decimal?)smooth[i] : null;
        }

        return(results);
    }
Beispiel #3
0
    // parameter validation
    private static void ValidateStoch(
        int lookbackPeriods,
        int signalPeriods,
        int smoothPeriods,
        decimal kFactor,
        decimal dFactor,
        MaType movingAverageType)
    {
        // check parameter arguments
        if (lookbackPeriods <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods,
                                                  "Lookback periods must be greater than 0 for Stochastic.");
        }

        if (signalPeriods <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods,
                                                  "Signal periods must be greater than 0 for Stochastic.");
        }

        if (smoothPeriods <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods,
                                                  "Smooth periods must be greater than 0 for Stochastic.");
        }

        if (kFactor <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(kFactor), kFactor,
                                                  "kFactor must be greater than 0 for Stochastic.");
        }

        if (dFactor <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(dFactor), dFactor,
                                                  "dFactor must be greater than 0 for Stochastic.");
        }

        if (movingAverageType is not MaType.SMA and not MaType.SMMA)
        {
            throw new ArgumentOutOfRangeException(nameof(dFactor), dFactor,
                                                  "Stochastic only supports SMA and SMMA moving average types.");
        }
    }
Beispiel #4
0
    /// <include file='./info.xml' path='indicator/type[@name="Extended"]/*' />
    ///
    public static IEnumerable <StochResult> GetStoch <TQuote>(
        this IEnumerable <TQuote> quotes,
        int lookbackPeriods,
        int signalPeriods,
        int smoothPeriods,
        decimal kFactor,
        decimal dFactor,
        MaType movingAverageType)
        where TQuote : IQuote
    {
        // convert quotes
        List <QuoteD> quotesList = quotes.ConvertToList();

        // check parameter arguments
        ValidateStoch(
            lookbackPeriods, signalPeriods, smoothPeriods,
            kFactor, dFactor, movingAverageType);

        // initialize
        int length = quotesList.Count;
        List <StochResult> results = new(length);

        // roll through quotes
        for (int i = 0; i < length; i++)
        {
            QuoteD q     = quotesList[i];
            int    index = i + 1;

            StochResult result = new()
            {
                Date = q.Date
            };

            if (index >= lookbackPeriods)
            {
                double?highHigh = double.MinValue;
                double?lowLow   = double.MaxValue;

                for (int p = index - lookbackPeriods; p < index; p++)
                {
                    QuoteD x = quotesList[p];

                    if (x.High > highHigh)
                    {
                        highHigh = x.High;
                    }

                    if (x.Low < lowLow)
                    {
                        lowLow = x.Low;
                    }
                }

                result.Oscillator = lowLow != highHigh
                    ? 100 * (decimal?)((q.Close - lowLow) / (highHigh - lowLow))
                    : 0;
            }

            results.Add(result);
        }

        // smooth the oscillator
        if (smoothPeriods > 1)
        {
            results = SmoothOscillator(
                results, length, lookbackPeriods, smoothPeriods, movingAverageType);
        }

        // handle insufficient length
        if (length < lookbackPeriods - 1)
        {
            return(results);
        }

        // signal (%D) and %J
        int    signalIndex = lookbackPeriods + smoothPeriods + signalPeriods - 2;
        double?s           = (double?)results[lookbackPeriods - 1].Oscillator;

        for (int i = lookbackPeriods - 1; i < length; i++)
        {
            StochResult r     = results[i];
            int         index = i + 1;

            // add signal

            if (signalPeriods <= 1)
            {
                r.Signal = r.Oscillator;
            }

            // SMA case
            else if (index >= signalIndex && movingAverageType is MaType.SMA)
            {
                double?sumOsc = 0;
                for (int p = index - signalPeriods; p < index; p++)
                {
                    StochResult x = results[p];
                    sumOsc += (double?)x.Oscillator;
                }

                r.Signal = (decimal?)(sumOsc / signalPeriods);
            }

            // SMMA case
            else if (i >= lookbackPeriods - 1 && movingAverageType is MaType.SMMA)
            {
                s = (s == null) ? (double?)results[i].Oscillator : s; // reset if null

                s        = ((s * (signalPeriods - 1)) + (double?)results[i].Oscillator) / signalPeriods;
                r.Signal = (decimal?)s;
            }

            // %J
            r.PercentJ = (kFactor * r.Oscillator) - (dFactor * r.Signal);
        }

        return(results);
    }
Beispiel #5
0
        // MOVING AVERAGE ENVELOPES
        public static IEnumerable <MaEnvelopeResult> GetMaEnvelopes <TQuote>(
            IEnumerable <TQuote> history,
            int lookbackPeriod,
            double percentOffset     = 2.5,
            MaType movingAverageType = MaType.SMA)
            where TQuote : IQuote
        {
            // check parameter arguments
            // note: most validations are done in variant methods
            ValidateMaEnvelopes(percentOffset);

            // initialize
            decimal offsetRatio = (decimal)percentOffset / 100m;

            // get envelopes variant
            return(movingAverageType switch
            {
                MaType.ALMA => GetAlma(history, lookbackPeriod)
                .Select(x => new MaEnvelopeResult
                {
                    Date = x.Date,
                    Centerline = x.Alma,
                    UpperEnvelope = x.Alma + x.Alma * offsetRatio,
                    LowerEnvelope = x.Alma - x.Alma * offsetRatio
                }),

                MaType.DEMA => GetDoubleEma(history, lookbackPeriod)
                .Select(x => new MaEnvelopeResult
                {
                    Date = x.Date,
                    Centerline = x.Ema,
                    UpperEnvelope = x.Ema + x.Ema * offsetRatio,
                    LowerEnvelope = x.Ema - x.Ema * offsetRatio
                }),

                MaType.EMA => GetEma(history, lookbackPeriod)
                .Select(x => new MaEnvelopeResult
                {
                    Date = x.Date,
                    Centerline = x.Ema,
                    UpperEnvelope = x.Ema + x.Ema * offsetRatio,
                    LowerEnvelope = x.Ema - x.Ema * offsetRatio
                }),

                MaType.HMA => GetHma(history, lookbackPeriod)
                .Select(x => new MaEnvelopeResult
                {
                    Date = x.Date,
                    Centerline = x.Hma,
                    UpperEnvelope = x.Hma + x.Hma * offsetRatio,
                    LowerEnvelope = x.Hma - x.Hma * offsetRatio
                }),

                MaType.SMA => GetSma(history, lookbackPeriod)
                .Select(x => new MaEnvelopeResult
                {
                    Date = x.Date,
                    Centerline = x.Sma,
                    UpperEnvelope = x.Sma + x.Sma * offsetRatio,
                    LowerEnvelope = x.Sma - x.Sma * offsetRatio
                }),

                MaType.TEMA => GetTripleEma(history, lookbackPeriod)
                .Select(x => new MaEnvelopeResult
                {
                    Date = x.Date,
                    Centerline = x.Ema,
                    UpperEnvelope = x.Ema + x.Ema * offsetRatio,
                    LowerEnvelope = x.Ema - x.Ema * offsetRatio
                }),

                MaType.WMA => GetWma(history, lookbackPeriod)
                .Select(x => new MaEnvelopeResult
                {
                    Date = x.Date,
                    Centerline = x.Wma,
                    UpperEnvelope = x.Wma + x.Wma * offsetRatio,
                    LowerEnvelope = x.Wma - x.Wma * offsetRatio
                }),

                _ => throw new ArgumentOutOfRangeException(
                    nameof(movingAverageType), movingAverageType,
                    string.Format(englishCulture,
                                  "Moving Average Envelopes does not support {0}.",
                                  Enum.GetName(typeof(MaType), movingAverageType)))
            });