示例#1
0
    // BOLLINGER BANDS
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <BollingerBandsResult> GetBollingerBands <TQuote>(
        this IEnumerable <TQuote> quotes,
        int lookbackPeriods       = 20,
        double standardDeviations = 2)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.Close);

        // check parameter arguments
        ValidateBollingerBands(lookbackPeriods, standardDeviations);

        // initialize
        List <BollingerBandsResult> results = new(bdList.Count);

        // roll through quotes
        for (int i = 0; i < bdList.Count; i++)
        {
            BasicD  q     = bdList[i];
            decimal close = (decimal)q.Value;
            int     index = i + 1;

            BollingerBandsResult r = new()
            {
                Date = q.Date
            };

            if (index >= lookbackPeriods)
            {
                double[] periodClose = new double[lookbackPeriods];
                double   sum         = 0;
                int      n           = 0;

                for (int p = index - lookbackPeriods; p < index; p++)
                {
                    BasicD d = bdList[p];
                    periodClose[n] = d.Value;
                    sum           += d.Value;
                    n++;
                }

                double periodAvg = sum / lookbackPeriods;
                double stdDev    = Functions.StdDev(periodClose);

                r.Sma       = (decimal)periodAvg;
                r.UpperBand = (decimal)(periodAvg + (standardDeviations * stdDev));
                r.LowerBand = (decimal)(periodAvg - (standardDeviations * stdDev));

                r.PercentB = (r.UpperBand == r.LowerBand) ? null
                    : (double)((close - r.LowerBand) / (r.UpperBand - r.LowerBand));

                r.ZScore = (stdDev == 0) ? null : (double)(close - r.Sma) / stdDev;
                r.Width  = (periodAvg == 0) ? null : (double)(r.UpperBand - r.LowerBand) / periodAvg;
            }

            results.Add(r);
        }

        return(results);
    }
示例#2
0
    // internals
    private static List <StdDevResult> CalcStdDev(
        List <BasicD> bdList, int lookbackPeriods, int?smaPeriods = null)
    {
        // check parameter arguments
        ValidateStdDev(lookbackPeriods, smaPeriods);

        // initialize
        List <StdDevResult> results = new(bdList.Count);

        // roll through quotes
        for (int i = 0; i < bdList.Count; i++)
        {
            BasicD bd    = bdList[i];
            int    index = i + 1;

            StdDevResult result = new()
            {
                Date = bd.Date,
            };

            if (index >= lookbackPeriods)
            {
                double[] periodValues = new double[lookbackPeriods];
                double   sum          = 0;
                int      n            = 0;

                for (int p = index - lookbackPeriods; p < index; p++)
                {
                    BasicD d = bdList[p];
                    periodValues[n] = d.Value;
                    sum            += d.Value;
                    n++;
                }

                double periodAvg = sum / lookbackPeriods;

                result.StdDev = Functions.StdDev(periodValues);
                result.Mean   = periodAvg;

                result.ZScore = (result.StdDev == 0) ? null
                    : (bd.Value - periodAvg) / result.StdDev;
            }

            results.Add(result);

            // optional SMA
            if (smaPeriods != null && index >= lookbackPeriods + smaPeriods - 1)
            {
                double sumSma = 0;
                for (int p = index - (int)smaPeriods; p < index; p++)
                {
                    sumSma += (double)results[p].StdDev;
                }

                result.StdDevSma = sumSma / smaPeriods;
            }
        }

        return(results);
    }
示例#3
0
    // ULCER INDEX (UI)
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable<UlcerIndexResult> GetUlcerIndex<TQuote>(
        this IEnumerable<TQuote> quotes,
        int lookbackPeriods = 14)
        where TQuote : IQuote
    {
        // convert quotes
        List<BasicD> bdList = quotes.ConvertToBasic(CandlePart.Close);

        // check parameter arguments
        ValidateUlcer(lookbackPeriods);

        // initialize
        List<UlcerIndexResult> results = new(bdList.Count);

        // roll through quotes
        for (int i = 0; i < bdList.Count; i++)
        {
            BasicD q = bdList[i];
            int index = i + 1;

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

            if (index >= lookbackPeriods)
            {
                double? sumSquared = 0;
                for (int p = index - lookbackPeriods; p < index; p++)
                {
                    BasicD d = bdList[p];
                    int dIndex = p + 1;

                    double maxClose = 0;
                    for (int s = index - lookbackPeriods; s < dIndex; s++)
                    {
                        BasicD dd = bdList[s];
                        if (dd.Value > maxClose)
                        {
                            maxClose = dd.Value;
                        }
                    }

                    double? percentDrawdown = (maxClose == 0) ? null
                        : 100 * (double)((d.Value - maxClose) / maxClose);

                    sumSquared += percentDrawdown * percentDrawdown;
                }

                result.UI = (sumSquared == null) ? null
                    : Math.Sqrt((double)sumSquared / lookbackPeriods);
            }

            results.Add(result);
        }

        return results;
    }
示例#4
0
    // CORRELATION COEFFICIENT
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <CorrResult> GetCorrelation <TQuote>(
        this IEnumerable <TQuote> quotesA,
        IEnumerable <TQuote> quotesB,
        int lookbackPeriods)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdListA = quotesA.ConvertToBasic(CandlePart.Close);
        List <BasicD> bdListB = quotesB.ConvertToBasic(CandlePart.Close);

        // check parameter arguments
        ValidateCorrelation(quotesA, quotesB, lookbackPeriods);

        // initialize
        List <CorrResult> results = new(bdListA.Count);

        // roll through quotes
        for (int i = 0; i < bdListA.Count; i++)
        {
            BasicD a     = bdListA[i];
            BasicD b     = bdListB[i];
            int    index = i + 1;

            if (a.Date != b.Date)
            {
                throw new InvalidQuotesException(nameof(quotesA), a.Date,
                                                 "Date sequence does not match.  Correlation requires matching dates in provided histories.");
            }

            CorrResult r = new()
            {
                Date = a.Date
            };

            // calculate correlation
            if (index >= lookbackPeriods)
            {
                double[] dataA = new double[lookbackPeriods];
                double[] dataB = new double[lookbackPeriods];
                int      z     = 0;

                for (int p = index - lookbackPeriods; p < index; p++)
                {
                    dataA[z] = bdListA[p].Value;
                    dataB[z] = bdListB[p].Value;

                    z++;
                }

                r.CalcCorrelation(dataA, dataB);
            }

            results.Add(r);
        }

        return(results);
    }
示例#5
0
    // CHAIKIN MONEY FLOW
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <CmfResult> GetCmf <TQuote>(
        this IEnumerable <TQuote> quotes,
        int lookbackPeriods = 20)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.Volume);

        // check parameter arguments
        ValidateCmf(lookbackPeriods);

        // initialize
        List <CmfResult> results    = new(bdList.Count);
        List <AdlResult> adlResults = GetAdl(quotes).ToList();

        // roll through quotes
        for (int i = 0; i < adlResults.Count; i++)
        {
            AdlResult r     = adlResults[i];
            int       index = i + 1;

            CmfResult result = new()
            {
                Date = r.Date,
                MoneyFlowMultiplier = r.MoneyFlowMultiplier,
                MoneyFlowVolume     = r.MoneyFlowVolume
            };

            if (index >= lookbackPeriods)
            {
                double sumMfv = 0;
                double sumVol = 0;

                for (int p = index - lookbackPeriods; p < index; p++)
                {
                    BasicD q = bdList[p];
                    sumVol += q.Value;

                    AdlResult d = adlResults[p];
                    sumMfv += (double)d.MoneyFlowVolume;
                }

                double avgMfv = sumMfv / lookbackPeriods;
                double avgVol = sumVol / lookbackPeriods;

                if (avgVol != 0)
                {
                    result.Cmf = avgMfv / avgVol;
                }
            }

            results.Add(result);
        }

        return(results);
    }
示例#6
0
    // AWESOME OSCILLATOR
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <AwesomeResult> GetAwesome <TQuote>(
        this IEnumerable <TQuote> quotes,
        int fastPeriods = 5,
        int slowPeriods = 34)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.HL2);

        // check parameter arguments
        ValidateAwesome(fastPeriods, slowPeriods);

        // initialize
        int length = bdList.Count;
        List <AwesomeResult> results = new();

        double[] pr = new double[length]; // median price

        // roll through quotes
        for (int i = 0; i < length; i++)
        {
            BasicD q = bdList[i];
            pr[i] = q.Value;
            int index = i + 1;

            AwesomeResult r = new()
            {
                Date = q.Date
            };

            if (index >= slowPeriods)
            {
                double sumSlow = 0;
                double sumFast = 0;

                for (int p = index - slowPeriods; p < index; p++)
                {
                    sumSlow += pr[p];

                    if (p >= index - fastPeriods)
                    {
                        sumFast += pr[p];
                    }
                }

                r.Oscillator = (sumFast / fastPeriods) - (sumSlow / slowPeriods);
                r.Normalized = (pr[i] != 0) ? 100 * r.Oscillator / pr[i] : null;
            }

            results.Add(r);
        }

        return(results);
    }
示例#7
0
    // SCHAFF TREND CYCLE (STC)
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <StcResult> GetStc <TQuote>(
        this IEnumerable <TQuote> quotes,
        int cyclePeriods = 10,
        int fastPeriods  = 23,
        int slowPeriods  = 50)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> quotesList = quotes.ConvertToBasic(CandlePart.Close);

        // check parameter arguments
        ValidateStc(cyclePeriods, fastPeriods, slowPeriods);

        // get stochastic of macd
        IEnumerable <StochResult> stochMacd = quotes
                                              .GetMacd(fastPeriods, slowPeriods, 1)
                                              .Where(x => x.Macd != null)
                                              .Select(x => new Quote
        {
            Date  = x.Date,
            High  = (decimal)x.Macd,
            Low   = (decimal)x.Macd,
            Close = (decimal)x.Macd
        })
                                              .GetStoch(cyclePeriods, 1, 3);

        // initialize results
        // to ensure same length as original quotes
        int length               = quotesList.Count;
        int initPeriods          = Math.Min(slowPeriods - 1, length);
        List <StcResult> results = new(length);

        for (int i = 0; i < initPeriods; i++)
        {
            BasicD q = quotesList[i];
            results.Add(new StcResult()
            {
                Date = q.Date
            });
        }

        // add stoch results
        // TODO: see if List Add works faster
        results.AddRange(
            stochMacd
            .Select(x => new StcResult
        {
            Date = x.Date,
            Stc  = x.Oscillator
        }));

        return(results);
    }
示例#8
0
    // SMOOTHED MOVING AVERAGE
    /// <include file='./info.xml' path='indicator/type[@name="Main"]/*' />
    ///
    public static IEnumerable <SmmaResult> GetSmma <TQuote>(
        this IEnumerable <TQuote> quotes,
        int lookbackPeriods)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> quotesList = quotes.ConvertToBasic(CandlePart.Close);

        // check parameter arguments
        ValidateSmma(lookbackPeriods);

        // initialize
        List <SmmaResult> results   = new(quotesList.Count);
        double?           prevValue = null;

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

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

            // calculate SMMA
            if (index > lookbackPeriods)
            {
                result.Smma = (decimal)((prevValue * (lookbackPeriods - 1)) + q.Value)
                              / lookbackPeriods;
            }

            // first SMMA calculated as simple SMA
            else if (index == lookbackPeriods)
            {
                double sumClose = 0;
                for (int p = index - lookbackPeriods; p < index; p++)
                {
                    BasicD d = quotesList[p];
                    sumClose += d.Value;
                }

                result.Smma = (decimal)(sumClose / lookbackPeriods);
            }

            prevValue = (double?)result.Smma;
            results.Add(result);
        }

        return(results);
    }
示例#9
0
    // HURST EXPONENT
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <HurstResult> GetHurst <TQuote>(
        this IEnumerable <TQuote> quotes,
        int lookbackPeriods = 100)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.Close);

        // check parameter arguments
        ValidateHurst(lookbackPeriods);

        // initialize
        int length = bdList.Count;
        List <HurstResult> results = new(length);

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

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

            if (index > lookbackPeriods)
            {
                // get evaluation batch
                double[] values = new double[lookbackPeriods];

                int x = 0;
                for (int p = index - lookbackPeriods; p < index; p++)
                {
                    // compile return values
                    if (bdList[p - 1].Value != 0)
                    {
                        values[x] = (bdList[p].Value / bdList[p - 1].Value) - 1;
                    }

                    x++;
                }

                // calculate hurst exponent
                result.HurstExponent = CalcHurst(values);
            }

            results.Add(result);
        }

        return(results);
    }
示例#10
0
    // RATE OF CHANGE (ROC)
    /// <include file='./info.xml' path='indicator/type[@name="Main"]/*' />
    ///
    public static IEnumerable <RocResult> GetRoc <TQuote>(
        this IEnumerable <TQuote> quotes,
        int lookbackPeriods,
        int?smaPeriods = null)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.Close);

        // check parameter arguments
        ValidateRoc(lookbackPeriods, smaPeriods);

        // initialize
        List <RocResult> results = new(bdList.Count);

        // roll through quotes
        for (int i = 0; i < bdList.Count; i++)
        {
            BasicD q     = bdList[i];
            int    index = i + 1;

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

            if (index > lookbackPeriods)
            {
                BasicD back = bdList[index - lookbackPeriods - 1];

                result.Roc = (back.Value == 0) ? null
                    : 100d * (q.Value - back.Value) / back.Value;
            }

            results.Add(result);

            // optional SMA
            if (smaPeriods != null && index >= lookbackPeriods + smaPeriods)
            {
                double?sumSma = 0;
                for (int p = index - (int)smaPeriods; p < index; p++)
                {
                    sumSma += results[p].Roc;
                }

                result.RocSma = sumSma / smaPeriods;
            }
        }

        return(results);
    }
示例#11
0
    // SIMPLE MOVING AVERAGE (EXTENDED VERSION)
    /// <include file='./info.xml' path='indicator/type[@name="Extended"]/*' />
    ///
    public static IEnumerable <SmaExtendedResult> GetSmaExtended <TQuote>(
        this IEnumerable <TQuote> quotes,
        int lookbackPeriods)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> quotesList = quotes.ConvertToBasic(CandlePart.Close);

        // initialize
        List <SmaExtendedResult> results = GetSma(quotes, lookbackPeriods)
                                           .Select(x => new SmaExtendedResult {
            Date = x.Date, Sma = x.Sma
        })
                                           .ToList();

        // roll through quotes
        for (int i = lookbackPeriods - 1; i < results.Count; i++)
        {
            int index             = i + 1;
            SmaExtendedResult r   = results[i];
            double            sma = (double)r.Sma;

            double sumMad  = 0;
            double sumMse  = 0;
            double?sumMape = 0;

            for (int p = index - lookbackPeriods; p < index; p++)
            {
                BasicD d     = quotesList[p];
                double close = d.Value;

                sumMad += Math.Abs(close - sma);
                sumMse += (close - sma) * (close - sma);

                sumMape += (close == 0) ? null
                    : Math.Abs(close - sma) / close;
            }

            // mean absolute deviation
            r.Mad = sumMad / lookbackPeriods;

            // mean squared error
            r.Mse = sumMse / lookbackPeriods;

            // mean absolute percent error
            r.Mape = sumMape / lookbackPeriods;
        }

        return(results);
    }
示例#12
0
    // standard calculation
    private static List <EmaResult> CalcEma(
        this List <BasicD> bdList, int lookbackPeriods)
    {
        // check parameter arguments
        ValidateEma(lookbackPeriods);

        // initialize
        int length = bdList.Count;
        List <EmaResult> results = new(length);

        double k           = 2d / (lookbackPeriods + 1);
        double?lastEma     = 0;
        int    initPeriods = Math.Min(lookbackPeriods, length);

        for (int i = 0; i < initPeriods; i++)
        {
            lastEma += bdList[i].Value;
        }

        lastEma /= lookbackPeriods;

        // roll through quotes
        for (int i = 0; i < length; i++)
        {
            BasicD h     = bdList[i];
            int    index = i + 1;

            EmaResult result = new()
            {
                Date = h.Date
            };

            if (index > lookbackPeriods)
            {
                double?ema = lastEma + (k * (h.Value - lastEma));
                result.Ema = (decimal?)ema;
                lastEma    = ema;
            }
            else if (index == lookbackPeriods)
            {
                result.Ema = (decimal?)lastEma;
            }

            results.Add(result);
        }

        return(results);
    }
示例#13
0
    // WEIGHTED MOVING AVERAGE
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <WmaResult> GetWma <TQuote>(
        this IEnumerable <TQuote> quotes,
        int lookbackPeriods,
        CandlePart candlePart = CandlePart.Close)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdList = quotes.ConvertToBasic(candlePart);

        // check parameter arguments
        ValidateWma(lookbackPeriods);

        // initialize
        List <WmaResult> results = new(bdList.Count);
        double           divisor = (double)lookbackPeriods * (lookbackPeriods + 1) / 2d;

        // roll through quotes
        for (int i = 0; i < bdList.Count; i++)
        {
            BasicD q     = bdList[i];
            int    index = i + 1;

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

            if (index >= lookbackPeriods)
            {
                double wma = 0;
                for (int p = index - lookbackPeriods; p < index; p++)
                {
                    BasicD d = bdList[p];
                    wma += (double)d.Value * (lookbackPeriods - (index - p - 1)) / divisor;
                }

                result.Wma = (decimal)wma;
            }

            results.Add(result);
        }

        return(results);
    }
示例#14
0
    public void ConvertToBasic()
    {
        // compose basic data
        List <BasicD> o = quotes.ConvertToBasic(CandlePart.Open);
        List <BasicD> h = quotes.ConvertToBasic(CandlePart.High);
        List <BasicD> l = quotes.ConvertToBasic(CandlePart.Low);
        List <BasicD> c = quotes.ConvertToBasic(CandlePart.Close);
        List <BasicD> v = quotes.ConvertToBasic(CandlePart.Volume);
        List <BasicD> x = quotes.ConvertToBasic(CandlePart.HL2);

        // assertions

        // should always be the same number of results as there is quotes
        Assert.AreEqual(502, c.Count);

        // samples
        BasicD ro = o[501];
        BasicD rh = h[501];
        BasicD rl = l[501];
        BasicD rc = c[501];
        BasicD rv = v[501];
        BasicD rx = x[501];

        // proper last date
        DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", EnglishCulture);

        Assert.AreEqual(lastDate, rc.Date);

        // last values should be correct
        Assert.AreEqual(244.92, ro.Value);
        Assert.AreEqual(245.54, rh.Value);
        Assert.AreEqual(242.87, rl.Value);
        Assert.AreEqual(245.28, rc.Value);
        Assert.AreEqual(147031456, rv.Value);
        Assert.AreEqual(244.205, rx.Value);
    }
示例#15
0
    // internals
    private static IEnumerable <SmaResult> CalcSma(
        this List <BasicD> bdList,
        int lookbackPeriods)
    {
        // note: pre-validated
        // initialize
        List <SmaResult> results = new(bdList.Count);

        // roll through quotes
        for (int i = 0; i < bdList.Count; i++)
        {
            BasicD q     = bdList[i];
            int    index = i + 1;

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

            if (index >= lookbackPeriods)
            {
                double sumSma = 0;
                for (int p = index - lookbackPeriods; p < index; p++)
                {
                    BasicD d = bdList[p];
                    sumSma += d.Value;
                }

                result.Sma = (decimal)sumSma / lookbackPeriods;
            }

            results.Add(result);
        }

        return(results);
    }
示例#16
0
    // BETA COEFFICIENT
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <BetaResult> GetBeta <TQuote>(
        IEnumerable <TQuote> quotesMarket,
        IEnumerable <TQuote> quotesEval,
        int lookbackPeriods,
        BetaType type = BetaType.Standard)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdListEval = quotesEval.ConvertToBasic(CandlePart.Close);
        List <BasicD> bdListMrkt = quotesMarket.ConvertToBasic(CandlePart.Close);

        // check parameter arguments
        ValidateBeta(quotesMarket, quotesEval, lookbackPeriods);

        // initialize
        List <BetaResult> results = new(bdListEval.Count);
        bool calcSd = type is BetaType.All or BetaType.Standard;
        bool calcUp = type is BetaType.All or BetaType.Up;
        bool calcDn = type is BetaType.All or BetaType.Down;

        // roll through quotes
        for (int i = 0; i < bdListEval.Count; i++)
        {
            BasicD e = bdListEval[i];

            BetaResult r = new()
            {
                Date = e.Date
            };
            results.Add(r);

            // skip warmup periods
            if (i < lookbackPeriods - 1)
            {
                continue;
            }

            // calculate standard beta
            if (calcSd)
            {
                r.CalcBeta(
                    i, lookbackPeriods, bdListMrkt, bdListEval, BetaType.Standard);
            }

            // calculate up/down betas
            if (i >= lookbackPeriods)
            {
                if (calcDn)
                {
                    r.CalcBeta(
                        i, lookbackPeriods, bdListMrkt, bdListEval, BetaType.Down);
                }

                if (calcUp)
                {
                    r.CalcBeta(
                        i, lookbackPeriods, bdListMrkt, bdListEval, BetaType.Up);
                }
            }

            // ratio and convexity
            if (type == BetaType.All && r.BetaUp != null && r.BetaDown != null)
            {
                r.Ratio     = (r.BetaDown != 0) ? r.BetaUp / r.BetaDown : null;
                r.Convexity = (r.BetaUp - r.BetaDown) * (r.BetaUp - r.BetaDown);
            }
        }

        return(results);
    }
示例#17
0
    // MOTHER of ADAPTIVE MOVING AVERAGES (MAMA)
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <MamaResult> GetMama <TQuote>(
        this IEnumerable <TQuote> quotes,
        double fastLimit = 0.5,
        double slowLimit = 0.05)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.HL2);

        // check parameter arguments
        ValidateMama(fastLimit, slowLimit);

        // initialize
        int length = bdList.Count;
        List <MamaResult> results = new(length);

        double sumPr = 0d;

        double[] pr = new double[length]; // price
        double[] sm = new double[length]; // smooth
        double[] dt = new double[length]; // detrender
        double[] pd = new double[length]; // period

        double[] q1 = new double[length]; // quadrature
        double[] i1 = new double[length]; // in-phase

        double jI;
        double jQ;

        double[] q2 = new double[length]; // adj. quadrature
        double[] i2 = new double[length]; // adj. in-phase

        double[] re = new double[length];
        double[] im = new double[length];

        double[] ph = new double[length]; // phase

        // roll through quotes
        for (int i = 0; i < length; i++)
        {
            BasicD q = bdList[i];
            pr[i] = q.Value;

            MamaResult r = new()
            {
                Date = q.Date,
            };

            if (i > 5)
            {
                double adj = (0.075 * pd[i - 1]) + 0.54;

                // smooth and detrender
                sm[i] = ((4 * pr[i]) + (3 * pr[i - 1]) + (2 * pr[i - 2]) + pr[i - 3]) / 10;
                dt[i] = ((0.0962 * sm[i]) + (0.5769 * sm[i - 2]) - (0.5769 * sm[i - 4]) - (0.0962 * sm[i - 6])) * adj;

                // in-phase and quadrature
                q1[i] = ((0.0962 * dt[i]) + (0.5769 * dt[i - 2]) - (0.5769 * dt[i - 4]) - (0.0962 * dt[i - 6])) * adj;
                i1[i] = dt[i - 3];

                // advance the phases by 90 degrees
                jI = ((0.0962 * i1[i]) + (0.5769 * i1[i - 2]) - (0.5769 * i1[i - 4]) - (0.0962 * i1[i - 6])) * adj;
                jQ = ((0.0962 * q1[i]) + (0.5769 * q1[i - 2]) - (0.5769 * q1[i - 4]) - (0.0962 * q1[i - 6])) * adj;

                // phasor addition for 3-bar averaging
                i2[i] = i1[i] - jQ;
                q2[i] = q1[i] + jI;

                i2[i] = (0.2 * i2[i]) + (0.8 * i2[i - 1]);  // smoothing it
                q2[i] = (0.2 * q2[i]) + (0.8 * q2[i - 1]);

                // homodyne discriminator
                re[i] = (i2[i] * i2[i - 1]) + (q2[i] * q2[i - 1]);
                im[i] = (i2[i] * q2[i - 1]) - (q2[i] * i2[i - 1]);

                re[i] = (0.2 * re[i]) + (0.8 * re[i - 1]);  // smoothing it
                im[i] = (0.2 * im[i]) + (0.8 * im[i - 1]);

                // calculate period
                if (im[i] != 0 && re[i] != 0)
                {
                    pd[i] = 2 * Math.PI / Math.Atan(im[i] / re[i]);
                }

                // adjust period to thresholds
                pd[i] = (pd[i] > 1.5 * pd[i - 1]) ? 1.5 * pd[i - 1] : pd[i];
                pd[i] = (pd[i] < 0.67 * pd[i - 1]) ? 0.67 * pd[i - 1] : pd[i];
                pd[i] = (pd[i] < 6d) ? 6d : pd[i];
                pd[i] = (pd[i] > 50d) ? 50d : pd[i];

                // smooth the period
                pd[i] = (0.2 * pd[i]) + (0.8 * pd[i - 1]);

                // determine phase position
                ph[i] = (i1[i] != 0) ? Math.Atan(q1[i] / i1[i]) * 180 / Math.PI : 0;

                // change in phase
                double delta = Math.Max(ph[i - 1] - ph[i], 1d);

                // adaptive alpha value
                double alpha = Math.Max(fastLimit / delta, slowLimit);

                // final indicators
                r.Mama = (decimal)((alpha * pr[i]) + ((1d - alpha) * (double)results[i - 1].Mama));
                r.Fama = (decimal)((0.5d * alpha * (double)r.Mama) + ((1d - (0.5d * alpha)) * (double)results[i - 1].Fama));
            }

            // initialization period
            else
            {
                sumPr += pr[i];

                if (i == 5)
                {
                    r.Mama = (decimal)sumPr / 6m;
                    r.Fama = r.Mama;
                }

                pd[i] = 0;
                sm[i] = 0;
                dt[i] = 0;

                i1[i] = 0;
                q1[i] = 0;
                i2[i] = 0;
                q2[i] = 0;

                re[i] = 0;
                im[i] = 0;

                ph[i] = 0;
            }

            results.Add(r);
        }

        return(results);
    }
示例#18
0
    // PRICE VOLUME OSCILLATOR (PVO)
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <PvoResult> GetPvo <TQuote>(
        this IEnumerable <TQuote> quotes,
        int fastPeriods   = 12,
        int slowPeriods   = 26,
        int signalPeriods = 9)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.Volume);

        // check parameter arguments
        ValidatePvo(fastPeriods, slowPeriods, signalPeriods);

        // initialize
        List <EmaResult> emaFast = CalcEma(bdList, fastPeriods);
        List <EmaResult> emaSlow = CalcEma(bdList, slowPeriods);

        int              length  = bdList.Count;
        List <BasicD>    emaDiff = new();
        List <PvoResult> results = new(length);

        // roll through quotes
        for (int i = 0; i < length; i++)
        {
            BasicD    h  = bdList[i];
            EmaResult df = emaFast[i];
            EmaResult ds = emaSlow[i];

            PvoResult result = new()
            {
                Date = h.Date
            };

            if (df?.Ema != null && ds?.Ema != null)
            {
                double?pvo = (ds.Ema != 0) ?
                             100 * (double)((df.Ema - ds.Ema) / ds.Ema) : null;

                result.Pvo = (decimal?)pvo;

                // temp data for interim EMA of PVO
                BasicD diff = new()
                {
                    Date  = h.Date,
                    Value = (double)pvo
                };

                emaDiff.Add(diff);
            }

            results.Add(result);
        }

        // add signal and histogram to result
        List <EmaResult> emaSignal = CalcEma(emaDiff, signalPeriods);

        for (int d = slowPeriods - 1; d < length; d++)
        {
            PvoResult r  = results[d];
            EmaResult ds = emaSignal[d + 1 - slowPeriods];

            r.Signal    = ds.Ema;
            r.Histogram = r.Pvo - r.Signal;
        }

        return(results);
    }
示例#19
0
    // MOVING AVERAGE CONVERGENCE/DIVERGENCE (MACD) OSCILLATOR
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <MacdResult> GetMacd <TQuote>(
        this IEnumerable <TQuote> quotes,
        int fastPeriods   = 12,
        int slowPeriods   = 26,
        int signalPeriods = 9)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.Close);

        // check parameter arguments
        ValidateMacd(fastPeriods, slowPeriods, signalPeriods);

        // initialize
        List <EmaResult> emaFast = CalcEma(bdList, fastPeriods);
        List <EmaResult> emaSlow = CalcEma(bdList, slowPeriods);

        int               length  = bdList.Count;
        List <BasicD>     emaDiff = new();
        List <MacdResult> results = new(length);

        // roll through quotes
        for (int i = 0; i < length; i++)
        {
            BasicD    h  = bdList[i];
            EmaResult df = emaFast[i];
            EmaResult ds = emaSlow[i];

            MacdResult result = new()
            {
                Date    = h.Date,
                FastEma = df.Ema,
                SlowEma = ds.Ema
            };

            if (df?.Ema != null && ds?.Ema != null)
            {
                double macd = (double)(df.Ema - ds.Ema);
                result.Macd = (decimal)macd;

                // temp data for interim EMA of macd
                BasicD diff = new()
                {
                    Date  = h.Date,
                    Value = macd
                };

                emaDiff.Add(diff);
            }

            results.Add(result);
        }

        // add signal and histogram to result
        List <EmaResult> emaSignal = CalcEma(emaDiff, signalPeriods);

        for (int d = slowPeriods - 1; d < length; d++)
        {
            MacdResult r  = results[d];
            EmaResult  ds = emaSignal[d + 1 - slowPeriods];

            r.Signal    = ds.Ema;
            r.Histogram = r.Macd - r.Signal;
        }

        return(results);
    }
示例#20
0
    // WILLIAMS ALLIGATOR
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <AlligatorResult> GetAlligator <TQuote>(
        this IEnumerable <TQuote> quotes,
        int jawPeriods   = 13,
        int jawOffset    = 8,
        int teethPeriods = 8,
        int teethOffset  = 5,
        int lipsPeriods  = 5,
        int lipsOffset   = 3)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.HL2);

        // check parameter arguments
        ValidateAlligator(
            jawPeriods,
            jawOffset,
            teethPeriods,
            teethOffset,
            lipsPeriods,
            lipsOffset);

        // initialize
        int length = bdList.Count;

        double[] pr = new double[length]; // median price

        List <AlligatorResult> results =
            bdList
            .Select(x => new AlligatorResult
        {
            Date = x.Date
        })
            .ToList();

        // roll through quotes
        for (int i = 0; i < length; i++)
        {
            BasicD q     = bdList[i];
            int    index = i + 1;
            pr[i] = q.Value;

            // only calculate jaw if the array index + offset is still in valid range
            if (i + jawOffset < length)
            {
                AlligatorResult jawResult = results[i + jawOffset];

                // calculate alligator's jaw
                // first value: calculate SMA
                if (index == jawPeriods)
                {
                    double sumMedianPrice = 0;
                    for (int p = index - jawPeriods; p < index; p++)
                    {
                        sumMedianPrice += pr[p];
                    }

                    jawResult.Jaw = (decimal)sumMedianPrice / jawPeriods;
                }

                // remaining values: SMMA
                else if (index > jawPeriods)
                {
                    double?prevValue = (double?)results[i + jawOffset - 1].Jaw;
                    jawResult.Jaw = (decimal?)((prevValue * (jawPeriods - 1)) + pr[i]) / jawPeriods;
                }
            }

            // only calculate teeth if the array index + offset is still in valid range
            if (i + teethOffset < length)
            {
                AlligatorResult teethResult = results[i + teethOffset];

                // calculate alligator's teeth
                // first value: calculate SMA
                if (index == teethPeriods)
                {
                    double sumMedianPrice = 0;
                    for (int p = index - teethPeriods; p < index; p++)
                    {
                        sumMedianPrice += pr[p];
                    }

                    teethResult.Teeth = (decimal?)sumMedianPrice / teethPeriods;
                }

                // remaining values: SMMA
                else if (index > teethPeriods)
                {
                    double?prevValue = (double?)results[i + teethOffset - 1].Teeth;
                    teethResult.Teeth = (decimal?)((prevValue * (teethPeriods - 1)) + pr[i]) / teethPeriods;
                }
            }

            // only calculate lips if the array index + offset is still in valid range
            if (i + lipsOffset < length)
            {
                AlligatorResult lipsResult = results[i + lipsOffset];

                // calculate alligator's lips
                // first value: calculate SMA
                if (index == lipsPeriods)
                {
                    double sumMedianPrice = 0;
                    for (int p = index - lipsPeriods; p < index; p++)
                    {
                        sumMedianPrice += pr[p];
                    }

                    lipsResult.Lips = (decimal)sumMedianPrice / lipsPeriods;
                }

                // remaining values: SMMA
                else if (index > lipsPeriods)
                {
                    double?prevValue = (double?)results[i + lipsOffset - 1].Lips;
                    lipsResult.Lips = (decimal?)((prevValue * (lipsPeriods - 1)) + pr[i]) / lipsPeriods;
                }
            }
        }

        return(results);
    }
示例#21
0
    // TILLSON T3 MOVING AVERAGE (T3)
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <T3Result> GetT3 <TQuote>(
        this IEnumerable <TQuote> quotes,
        int lookbackPeriods = 5,
        double volumeFactor = 0.7)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.Close);

        // check parameter arguments
        ValidateT3(lookbackPeriods, volumeFactor);

        // initialize
        int             length  = bdList.Count;
        List <T3Result> results = new(length);

        double k  = 2d / (lookbackPeriods + 1);
        double a  = volumeFactor;
        double c1 = -a * a * a;
        double c2 = (3 * a * a) + (3 * a * a * a);
        double c3 = (-6 * a * a) - (3 * a) - (3 * a * a * a);
        double c4 = 1 + (3 * a) + (a * a * a) + (3 * a * a);

        double e1 = 0, e2 = 0, e3 = 0, e4 = 0, e5 = 0, e6 = 0;
        double sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0, sum5 = 0, sum6 = 0;

        // roll through quotes
        for (int i = 0; i < length; i++)
        {
            BasicD   q = bdList[i];
            T3Result r = new()
            {
                Date = q.Date
            };

            // first smoothing
            if (i > lookbackPeriods - 1)
            {
                e1 += k * (q.Value - e1);

                // second smoothing
                if (i > 2 * (lookbackPeriods - 1))
                {
                    e2 += k * (e1 - e2);

                    // third smoothing
                    if (i > 3 * (lookbackPeriods - 1))
                    {
                        e3 += k * (e2 - e3);

                        // fourth smoothing
                        if (i > 4 * (lookbackPeriods - 1))
                        {
                            e4 += k * (e3 - e4);

                            // fifth smoothing
                            if (i > 5 * (lookbackPeriods - 1))
                            {
                                e5 += k * (e4 - e5);

                                // sixth smoothing
                                if (i > 6 * (lookbackPeriods - 1))
                                {
                                    e6 += k * (e5 - e6);

                                    // T3 moving average
                                    r.T3 = (decimal?)((c1 * e6) + (c2 * e5) + (c3 * e4) + (c4 * e3));
                                }

                                // sixth warmup
                                else
                                {
                                    sum6 += e5;

                                    if (i == 6 * (lookbackPeriods - 1))
                                    {
                                        e6 = sum6 / lookbackPeriods;

                                        // initial T3 moving average
                                        r.T3 = (decimal?)((c1 * e6) + (c2 * e5) + (c3 * e4) + (c4 * e3));
                                    }
                                }
                            }

                            // fifth warmup
                            else
                            {
                                sum5 += e4;

                                if (i == 5 * (lookbackPeriods - 1))
                                {
                                    sum6 = e5 = sum5 / lookbackPeriods;
                                }
                            }
                        }

                        // fourth warmup
                        else
                        {
                            sum4 += e3;

                            if (i == 4 * (lookbackPeriods - 1))
                            {
                                sum5 = e4 = sum4 / lookbackPeriods;
                            }
                        }
                    }

                    // third warmup
                    else
                    {
                        sum3 += e2;

                        if (i == 3 * (lookbackPeriods - 1))
                        {
                            sum4 = e3 = sum3 / lookbackPeriods;
                        }
                    }
                }

                // second warmup
                else
                {
                    sum2 += e1;

                    if (i == 2 * (lookbackPeriods - 1))
                    {
                        sum3 = e2 = sum2 / lookbackPeriods;
                    }
                }
            }

            // first warmup
            else
            {
                sum1 += (double)q.Value;

                if (i == lookbackPeriods - 1)
                {
                    sum2 = e1 = sum1 / lookbackPeriods;
                }
            }

            results.Add(r);
        }

        return(results);
    }
示例#22
0
    // VOLATILITY SYSTEM (STOP)
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <VolatilityStopResult> GetVolatilityStop <TQuote>(
        this IEnumerable <TQuote> quotes,
        int lookbackPeriods = 7,
        double multiplier   = 3)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.Close);

        // check parameter arguments
        ValidateVolatilityStop(lookbackPeriods, multiplier);

        // initialize
        int length = bdList.Count;
        List <VolatilityStopResult> results = new(length);

        if (length == 0)
        {
            return(results);
        }

        List <AtrResult> atrList = quotes.GetAtr(lookbackPeriods).ToList();

        // initial trend (guess)
        int    initPeriods = Math.Min(length, lookbackPeriods);
        double sic         = (double)bdList[0].Value;
        bool   isLong      = (double)bdList[initPeriods - 1].Value > sic;

        for (int i = 0; i < initPeriods; i++)
        {
            BasicD q     = bdList[i];
            double close = (double)q.Value;
            sic = isLong ? Math.Max(sic, close) : Math.Min(sic, close);
            results.Add(new VolatilityStopResult()
            {
                Date = q.Date
            });
        }

        // roll through quotes
        for (int i = lookbackPeriods; i < length; i++)
        {
            BasicD q     = bdList[i];
            double close = (double)q.Value;

            // average true range × multiplier constant
            double arc = (double)atrList[i - 1].Atr * multiplier;

            VolatilityStopResult r = new()
            {
                Date = q.Date,

                // stop and reverse threshold
                Sar = (decimal?)(isLong ? sic - arc : sic + arc)
            };
            results.Add(r);

            // add SAR as separate bands
            if (isLong)
            {
                r.LowerBand = r.Sar;
            }
            else
            {
                r.UpperBand = r.Sar;
            }

            // evaluate stop and reverse
            if ((isLong && (decimal?)q.Value < r.Sar) ||
                (!isLong && (decimal?)q.Value > r.Sar))
            {
                r.IsStop = true;
                sic      = close;
                isLong   = !isLong;
            }
            else
            {
                r.IsStop = false;

                // significant close adjustment
                // extreme favorable close while in trade
                sic = isLong ? Math.Max(sic, close) : Math.Min(sic, close);
            }
        }

        // remove first trend to stop, since it is a guess
        VolatilityStopResult firstStop = results
                                         .Where(x => x.IsStop == true)
                                         .OrderBy(x => x.Date)
                                         .FirstOrDefault();

        if (firstStop != null)
        {
            int cutIndex = results.IndexOf(firstStop);

            for (int d = 0; d <= cutIndex; d++)
            {
                VolatilityStopResult r = results[d];
                r.Sar       = null;
                r.UpperBand = null;
                r.LowerBand = null;
                r.IsStop    = null;
            }
        }

        return(results);
    }
示例#23
0
    // KAUFMAN's ADAPTIVE MOVING AVERAGE
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <KamaResult> GetKama <TQuote>(
        this IEnumerable <TQuote> quotes,
        int erPeriods   = 10,
        int fastPeriods = 2,
        int slowPeriods = 30)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> quotesList = quotes.ConvertToBasic(CandlePart.Close);

        // check parameter arguments
        ValidateKama(erPeriods, fastPeriods, slowPeriods);

        // initialize
        List <KamaResult> results = new(quotesList.Count);
        double            scFast  = 2d / (fastPeriods + 1);
        double            scSlow  = 2d / (slowPeriods + 1);

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

            KamaResult r = new()
            {
                Date = q.Date
            };

            if (index > erPeriods)
            {
                // ER period change
                double change = Math.Abs(q.Value - quotesList[i - erPeriods].Value);

                // volatility
                double sumPV = 0;
                for (int p = i - erPeriods + 1; p <= i; p++)
                {
                    sumPV += Math.Abs(quotesList[p].Value - quotesList[p - 1].Value);
                }

                if (sumPV != 0)
                {
                    // efficiency ratio
                    double er = change / sumPV;
                    r.ER = er;

                    // smoothing constant
                    double sc = (er * (scFast - scSlow)) + scSlow;  // squared later

                    // kama calculation
                    double?pk = (double?)results[i - 1].Kama;   // prior KAMA
                    r.Kama = (decimal?)(pk + (sc * sc * (q.Value - pk)));
                }

                // handle flatline case
                else
                {
                    r.ER   = 0;
                    r.Kama = (decimal?)q.Value;
                }
            }

            // initial value
            else if (index == erPeriods)
            {
                r.Kama = (decimal?)q.Value;
            }

            results.Add(r);
        }

        return(results);
    }
示例#24
0
    // parameter validation
    private static List <ConnorsRsiResult> CalcConnorsRsiBaseline(
        List <BasicD> bdList, int rsiPeriods, int rankPeriods)
    {
        // initialize
        List <RsiResult> rsiResults = CalcRsi(bdList, rsiPeriods);

        int length = bdList.Count;
        List <ConnorsRsiResult> results = new(length);

        double?[] gain = new double?[length];

        double?lastClose = null;
        int    streak    = 0;

        // compose interim results
        for (int i = 0; i < length; i++)
        {
            BasicD q     = bdList[i];
            int    index = i + 1;

            ConnorsRsiResult r = new()
            {
                Date     = q.Date,
                RsiClose = rsiResults[i].Rsi
            };
            results.Add(r);

            // bypass for first record
            if (lastClose == null)
            {
                lastClose = q.Value;
                continue;
            }

            // streak of up or down
            if (q.Value == lastClose)
            {
                streak = 0;
            }
            else if (q.Value > lastClose)
            {
                if (streak >= 0)
                {
                    streak++;
                }
                else
                {
                    streak = 1;
                }
            }
            else // h.Value < lastClose
            {
                if (streak <= 0)
                {
                    streak--;
                }
                else
                {
                    streak = -1;
                }
            }

            r.Streak = streak;

            // percentile rank
            gain[i] = (lastClose <= 0) ? null
                    : (q.Value - lastClose) / lastClose;

            if (index > rankPeriods)
            {
                int qty = 0;
                for (int p = index - rankPeriods - 1; p < index; p++)
                {
                    if (gain[p] < gain[i])
                    {
                        qty++;
                    }
                }

                r.PercentRank = 100 * qty / rankPeriods;
            }

            lastClose = q.Value;
        }

        return(results);
    }
示例#25
0
    // TRUE STRENGTH INDEX (TSI)
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <TsiResult> GetTsi <TQuote>(
        this IEnumerable <TQuote> quotes,
        int lookbackPeriods = 25,
        int smoothPeriods   = 13,
        int signalPeriods   = 7)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.Close);

        // check parameter arguments
        ValidateTsi(lookbackPeriods, smoothPeriods, signalPeriods);

        // initialize
        int    length = bdList.Count;
        double mult1  = 2d / (lookbackPeriods + 1);
        double mult2  = 2d / (smoothPeriods + 1);
        double multS  = 2d / (signalPeriods + 1);
        double?sumS   = 0;

        List <TsiResult> results = new(length);

        double[] c     = new double[length]; // price change
        double[] cs1   = new double[length]; // smooth 1
        double[] cs2   = new double[length]; // smooth 2
        double   sumC  = 0;
        double   sumC1 = 0;

        double[] a     = new double[length]; // abs of price change
        double[] as1   = new double[length]; // smooth 1
        double[] as2   = new double[length]; // smooth 2
        double   sumA  = 0;
        double   sumA1 = 0;

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

            TsiResult r = new()
            {
                Date = q.Date
            };
            results.Add(r);

            // skip first period
            if (i == 0)
            {
                continue;
            }

            // price change
            c[i] = q.Value - bdList[i - 1].Value;
            a[i] = Math.Abs(c[i]);

            // smoothing
            if (index > lookbackPeriods + 1)
            {
                // first smoothing
                cs1[i] = ((c[i] - cs1[i - 1]) * mult1) + cs1[i - 1];
                as1[i] = ((a[i] - as1[i - 1]) * mult1) + as1[i - 1];

                // second smoothing
                if (index > lookbackPeriods + smoothPeriods)
                {
                    cs2[i] = ((cs1[i] - cs2[i - 1]) * mult2) + cs2[i - 1];
                    as2[i] = ((as1[i] - as2[i - 1]) * mult2) + as2[i - 1];

                    r.Tsi = (as2[i] != 0) ? 100d * (cs2[i] / as2[i]) : null;

                    // signal line
                    if (signalPeriods > 0)
                    {
                        if (index >= lookbackPeriods + smoothPeriods + signalPeriods)
                        {
                            r.Signal = ((r.Tsi - results[i - 1].Signal) * multS)
                                       + results[i - 1].Signal;
                        }

                        // initialize signal
                        else
                        {
                            sumS += r.Tsi;

                            if (index == lookbackPeriods + smoothPeriods + signalPeriods - 1)
                            {
                                r.Signal = sumS / signalPeriods;
                            }
                        }
                    }
                }

                // prepare second smoothing
                else
                {
                    sumC1 += cs1[i];
                    sumA1 += as1[i];

                    // inialize second smoothing
                    if (index == lookbackPeriods + smoothPeriods)
                    {
                        cs2[i] = sumC1 / smoothPeriods;
                        as2[i] = sumA1 / smoothPeriods;

                        r.Tsi = (as2[i] != 0) ? 100 * cs2[i] / as2[i] : null;
                        sumS  = r.Tsi;
                    }
                }
            }

            // prepare first smoothing
            else
            {
                sumC += c[i];
                sumA += a[i];

                // initialize first smoothing
                if (index == lookbackPeriods + 1)
                {
                    cs1[i] = sumC / lookbackPeriods;
                    as1[i] = sumA / lookbackPeriods;

                    sumC1 = cs1[i];
                    sumA1 = as1[i];
                }
            }
        }

        return(results);
    }
示例#26
0
    // internals
    private static List <RsiResult> CalcRsi(List <BasicD> bdList, int lookbackPeriods)
    {
        // check parameter arguments
        ValidateRsi(lookbackPeriods);

        // initialize
        int    length  = bdList.Count;
        double avgGain = 0;
        double avgLoss = 0;

        List <RsiResult> results = new(length);

        double[] gain = new double[length]; // gain
        double[] loss = new double[length]; // loss
        double   lastValue;

        if (length == 0)
        {
            return(results);
        }
        else
        {
            lastValue = bdList[0].Value;
        }

        // roll through quotes
        for (int i = 0; i < bdList.Count; i++)
        {
            BasicD h     = bdList[i];
            int    index = i + 1;

            RsiResult r = new()
            {
                Date = h.Date
            };
            results.Add(r);

            gain[i]   = (h.Value > lastValue) ? h.Value - lastValue : 0;
            loss[i]   = (h.Value < lastValue) ? lastValue - h.Value : 0;
            lastValue = h.Value;

            // calculate RSI
            if (index > lookbackPeriods + 1)
            {
                avgGain = ((avgGain * (lookbackPeriods - 1)) + gain[i]) / lookbackPeriods;
                avgLoss = ((avgLoss * (lookbackPeriods - 1)) + loss[i]) / lookbackPeriods;

                if (avgLoss > 0)
                {
                    double rs = avgGain / avgLoss;
                    r.Rsi = 100 - (100 / (1 + rs));
                }
                else
                {
                    r.Rsi = 100;
                }
            }

            // initialize average gain
            else if (index == lookbackPeriods + 1)
            {
                double sumGain = 0;
                double sumLoss = 0;

                for (int p = 1; p <= lookbackPeriods; p++)
                {
                    sumGain += gain[p];
                    sumLoss += loss[p];
                }

                avgGain = sumGain / lookbackPeriods;
                avgLoss = sumLoss / lookbackPeriods;

                r.Rsi = (avgLoss > 0) ? 100 - (100 / (1 + (avgGain / avgLoss))) : 100;
            }
        }

        return(results);
    }
示例#27
0
    // ARNAUD LEGOUX MOVING AVERAGE
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <AlmaResult> GetAlma <TQuote>(
        this IEnumerable <TQuote> quotes,
        int lookbackPeriods = 9,
        double offset       = 0.85,
        double sigma        = 6)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.Close);

        // check parameter arguments
        ValidateAlma(lookbackPeriods, offset, sigma);

        // initialize
        List <AlmaResult> results = new(bdList.Count);

        // determine price weights
        double m = offset * (lookbackPeriods - 1);
        double s = lookbackPeriods / sigma;

        double[] weight = new double[lookbackPeriods];
        double   norm   = 0;

        for (int i = 0; i < lookbackPeriods; i++)
        {
            double wt = Math.Exp(-((i - m) * (i - m)) / (2 * s * s));
            weight[i] = wt;
            norm     += wt;
        }

        // roll through quotes
        for (int i = 0; i < bdList.Count; i++)
        {
            BasicD q     = bdList[i];
            int    index = i + 1;

            AlmaResult r = new()
            {
                Date = q.Date
            };

            if (index >= lookbackPeriods)
            {
                double weightedSum = 0;
                int    n           = 0;

                for (int p = index - lookbackPeriods; p < index; p++)
                {
                    BasicD d = bdList[p];
                    weightedSum += weight[n] * d.Value;
                    n++;
                }

                r.Alma = (decimal)(weightedSum / norm);
            }

            results.Add(r);
        }

        return(results);
    }
示例#28
0
    // SLOPE AND LINEAR REGRESSION
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <SlopeResult> GetSlope <TQuote>(
        this IEnumerable <TQuote> quotes,
        int lookbackPeriods)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.Close);

        // check parameter arguments
        ValidateSlope(lookbackPeriods);

        // initialize
        int length = bdList.Count;
        List <SlopeResult> results = new(length);

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

            SlopeResult r = new()
            {
                Date = q.Date
            };

            results.Add(r);

            // skip initialization period
            if (index < lookbackPeriods)
            {
                continue;
            }

            // get averages for period
            double sumX = 0;
            double sumY = 0;

            for (int p = index - lookbackPeriods; p < index; p++)
            {
                BasicD d = bdList[p];

                sumX += p + 1d;
                sumY += d.Value;
            }

            double avgX = sumX / lookbackPeriods;
            double avgY = sumY / lookbackPeriods;

            // least squares method
            double sumSqX  = 0;
            double sumSqY  = 0;
            double sumSqXY = 0;

            for (int p = index - lookbackPeriods; p < index; p++)
            {
                BasicD d = bdList[p];

                double devX = p + 1d - avgX;
                double devY = d.Value - avgY;

                sumSqX  += devX * devX;
                sumSqY  += devY * devY;
                sumSqXY += devX * devY;
            }

            r.Slope     = sumSqXY / sumSqX;
            r.Intercept = avgY - (r.Slope * avgX);

            // calculate Standard Deviation and R-Squared
            double stdDevX = Math.Sqrt((double)sumSqX / lookbackPeriods);
            double stdDevY = Math.Sqrt((double)sumSqY / lookbackPeriods);
            r.StdDev = stdDevY;

            if (stdDevX * stdDevY != 0)
            {
                double arrr = (double)sumSqXY / (stdDevX * stdDevY) / lookbackPeriods;
                r.RSquared = arrr * arrr;
            }
        }

        // add last Line (y = mx + b)
        if (length >= lookbackPeriods)
        {
            SlopeResult last = results.LastOrDefault();
            for (int p = length - lookbackPeriods; p < length; p++)
            {
                SlopeResult d = results[p];
                d.Line = (decimal?)((last.Slope * (p + 1)) + last.Intercept);
            }
        }

        return(results);
    }
示例#29
0
    // HILBERT TRANSFORM - INSTANTANEOUS TRENDLINE (HTL)
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <HtlResult> GetHtTrendline <TQuote>(
        this IEnumerable <TQuote> quotes)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.HL2);

        // initialize
        int length = bdList.Count;
        List <HtlResult> results = new(length);

        double[] pr = new double[length]; // price
        double[] sp = new double[length]; // smooth price
        double[] dt = new double[length]; // detrender
        double[] pd = new double[length]; // period

        double[] q1 = new double[length]; // quadrature
        double[] i1 = new double[length]; // in-phase

        double jI;
        double jQ;

        double[] q2 = new double[length]; // adj. quadrature
        double[] i2 = new double[length]; // adj. in-phase

        double[] re = new double[length];
        double[] im = new double[length];

        double[] sd = new double[length]; // smooth period
        double[] it = new double[length]; // instantaneous trend (raw)

        // roll through quotes
        for (int i = 0; i < length; i++)
        {
            BasicD q = bdList[i];
            pr[i] = q.Value;

            HtlResult r = new()
            {
                Date = q.Date,
            };

            if (i > 5)
            {
                double adj = (0.075 * pd[i - 1]) + 0.54;

                // smooth and detrender
                sp[i] = ((4 * pr[i]) + (3 * pr[i - 1]) + (2 * pr[i - 2]) + pr[i - 3]) / 10;
                dt[i] = ((0.0962 * sp[i]) + (0.5769 * sp[i - 2]) - (0.5769 * sp[i - 4]) - (0.0962 * sp[i - 6])) * adj;

                // in-phase and quadrature
                q1[i] = ((0.0962 * dt[i]) + (0.5769 * dt[i - 2]) - (0.5769 * dt[i - 4]) - (0.0962 * dt[i - 6])) * adj;
                i1[i] = dt[i - 3];

                // advance the phases by 90 degrees
                jI = ((0.0962 * i1[i]) + (0.5769 * i1[i - 2]) - (0.5769 * i1[i - 4]) - (0.0962 * i1[i - 6])) * adj;
                jQ = ((0.0962 * q1[i]) + (0.5769 * q1[i - 2]) - (0.5769 * q1[i - 4]) - (0.0962 * q1[i - 6])) * adj;

                // phasor addition for 3-bar averaging
                i2[i] = i1[i] - jQ;
                q2[i] = q1[i] + jI;

                i2[i] = (0.2 * i2[i]) + (0.8 * i2[i - 1]);  // smoothing it
                q2[i] = (0.2 * q2[i]) + (0.8 * q2[i - 1]);

                // homodyne discriminator
                re[i] = (i2[i] * i2[i - 1]) + (q2[i] * q2[i - 1]);
                im[i] = (i2[i] * q2[i - 1]) - (q2[i] * i2[i - 1]);

                re[i] = (0.2 * re[i]) + (0.8 * re[i - 1]);  // smoothing it
                im[i] = (0.2 * im[i]) + (0.8 * im[i - 1]);

                // calculate period
                if (im[i] != 0 && re[i] != 0)
                {
                    pd[i] = 2 * Math.PI / Math.Atan(im[i] / re[i]);
                }

                // adjust period to thresholds
                pd[i] = (pd[i] > 1.5 * pd[i - 1]) ? 1.5 * pd[i - 1] : pd[i];
                pd[i] = (pd[i] < 0.67 * pd[i - 1]) ? 0.67 * pd[i - 1] : pd[i];
                pd[i] = (pd[i] < 6d) ? 6d : pd[i];
                pd[i] = (pd[i] > 50d) ? 50d : pd[i];

                // smooth the period
                pd[i] = (0.2 * pd[i]) + (0.8 * pd[i - 1]);
                sd[i] = (0.33 * pd[i]) + (0.67 * sd[i - 1]);

                // smooth dominant cycle period
                int    dcPeriods = (int)(sd[i] + 0.5);
                double sumPr     = 0;
                for (int d = i - dcPeriods + 1; d <= i; d++)
                {
                    if (d >= 0)
                    {
                        sumPr += pr[d];
                    }

                    // handle insufficient lookback quotes (trim scope)
                    else
                    {
                        dcPeriods--;
                    }
                }

                it[i] = dcPeriods > 0 ? sumPr / dcPeriods : pr[i];

                // final indicators
                r.Trendline = i >= 11 // 12th bar
                    ? (decimal)((4 * it[i]) + (3 * it[i - 1]) + (2 * it[i - 2]) + it[i - 3]) / 10m
                    : (decimal)pr[i];

                r.SmoothPrice = (decimal)((4 * pr[i]) + (3 * pr[i - 1]) + (2 * pr[i - 2]) + pr[i - 3]) / 10m;
            }

            // initialization period
            else
            {
                r.Trendline   = (decimal)pr[i];
                r.SmoothPrice = null;

                pd[i] = 0;
                sp[i] = 0;
                dt[i] = 0;

                i1[i] = 0;
                q1[i] = 0;
                i2[i] = 0;
                q2[i] = 0;

                re[i] = 0;
                im[i] = 0;

                sd[i] = 0;
            }

            results.Add(r);
        }

        return(results);
    }
示例#30
0
    // PRICE RELATIVE STRENGTH
    /// <include file='./info.xml' path='indicator/*' />
    ///
    public static IEnumerable <PrsResult> GetPrs <TQuote>(
        this IEnumerable <TQuote> historyBase,
        IEnumerable <TQuote> historyEval,
        int?lookbackPeriods = null,
        int?smaPeriods      = null)
        where TQuote : IQuote
    {
        // convert quotes
        List <BasicD> bdBaseList = historyBase.ConvertToBasic(CandlePart.Close);
        List <BasicD> bdEvalList = historyEval.ConvertToBasic(CandlePart.Close);

        // check parameter arguments
        ValidatePriceRelative(historyBase, historyEval, lookbackPeriods, smaPeriods);

        // initialize
        List <PrsResult> results = new(bdEvalList.Count);

        // roll through quotes
        for (int i = 0; i < bdEvalList.Count; i++)
        {
            BasicD bi    = bdBaseList[i];
            BasicD ei    = bdEvalList[i];
            int    index = i + 1;

            if (ei.Date != bi.Date)
            {
                throw new InvalidQuotesException(nameof(historyEval), ei.Date,
                                                 "Date sequence does not match.  Price Relative requires matching dates in provided histories.");
            }

            PrsResult r = new()
            {
                Date = ei.Date,
                Prs  = (bi.Value == 0) ? null : (ei.Value / bi.Value) // relative strength ratio
            };
            results.Add(r);

            if (lookbackPeriods != null && index > lookbackPeriods)
            {
                BasicD bo = bdBaseList[i - (int)lookbackPeriods];
                BasicD eo = bdEvalList[i - (int)lookbackPeriods];

                if (bo.Value != 0 && eo.Value != 0)
                {
                    double pctB = (bi.Value - bo.Value) / bo.Value;
                    double pctE = (ei.Value - eo.Value) / eo.Value;

                    r.PrsPercent = pctE - pctB;
                }
            }

            // optional moving average of PRS
            if (smaPeriods != null && index >= smaPeriods)
            {
                double?sumRs = 0;
                for (int p = index - (int)smaPeriods; p < index; p++)
                {
                    PrsResult d = results[p];
                    sumRs += d.Prs;
                }

                r.PrsSma = sumRs / smaPeriods;
            }
        }

        return(results);
    }