// TRIPLE EXPONENTIAL MOVING AVERAGE /// <include file='./info.xml' path='indicator/*' /> /// public static IEnumerable <TemaResult> GetTripleEma <TQuote>( this IEnumerable <TQuote> quotes, int lookbackPeriods) where TQuote : IQuote { // convert quotes List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.Close); // check parameter arguments ValidateTema(lookbackPeriods); // initialize List <TemaResult> results = new(bdList.Count); List <EmaResult> emaN1 = CalcEma(bdList, lookbackPeriods); List <BasicD> bd2 = emaN1 .Where(x => x.Ema != null) .Select(x => new BasicD { Date = x.Date, Value = (double)x.Ema }) .ToList(); List <EmaResult> emaN2 = CalcEma(bd2, lookbackPeriods); List <BasicD> bd3 = emaN2 .Where(x => x.Ema != null) .Select(x => new BasicD { Date = x.Date, Value = (double)x.Ema }) .ToList(); List <EmaResult> emaN3 = CalcEma(bd3, lookbackPeriods); // compose final results for (int i = 0; i < emaN1.Count; i++) { EmaResult e1 = emaN1[i]; int index = i + 1; TemaResult result = new() { Date = e1.Date }; if (index >= (3 * lookbackPeriods) - 2) { EmaResult e2 = emaN2[index - lookbackPeriods]; EmaResult e3 = emaN3[index - (2 * lookbackPeriods) + 1]; result.Tema = (3 * e1.Ema) - (3 * e2.Ema) + e3.Ema; } results.Add(result); } return(results); }
public void Convergence() { foreach (int qty in convergeQuantities) { IEnumerable <Quote> h = HistoryTestData.GetLong(115 + qty); IEnumerable <EmaResult> r = Indicator.GetEma(h, 15); EmaResult l = r.LastOrDefault(); Console.WriteLine("EMA on {0:d} with {1,4} periods: {2:N8}", l.Date, h.Count(), l.Ema); } }
public void Find() { IEnumerable <Quote> quotes = TestData.GetDefault(); IEnumerable <EmaResult> emaResults = Indicator.GetEma(quotes, 20); // find specific date DateTime findDate = DateTime.ParseExact("2018-12-31", "yyyy-MM-dd", EnglishCulture); EmaResult r = emaResults.Find(findDate); Assert.AreEqual(249.3519m, Math.Round((decimal)r.Ema, 4)); }
public void Ema() { foreach (int qty in QuotesQuantities) { IEnumerable <Quote> quotes = TestData.GetLongish(qty); IEnumerable <EmaResult> r = quotes.GetEma(15); EmaResult l = r.LastOrDefault(); Console.WriteLine( "EMA(15) on {0:d} with {1,4} periods: {2:N8}", l.Date, quotes.Count(), l.Ema); } }
public void Removed() { List <EmaResult> results = quotes.GetEma(20) .RemoveWarmupPeriods() .ToList(); // assertions Assert.AreEqual(502 - (20 + 100), results.Count); EmaResult last = results.LastOrDefault(); Assert.AreEqual(249.3519m, Math.Round((decimal)last.Ema, 4)); }
// KELTNER CHANNELS /// <include file='./info.xml' path='indicator/*' /> /// public static IEnumerable <KeltnerResult> GetKeltner <TQuote>( this IEnumerable <TQuote> quotes, int emaPeriods = 20, decimal multiplier = 2, int atrPeriods = 10) where TQuote : IQuote { // sort quotes List <TQuote> quotesList = quotes.SortToList(); // check parameter arguments ValidateKeltner(emaPeriods, multiplier, atrPeriods); // initialize List <KeltnerResult> results = new(quotesList.Count); List <EmaResult> emaResults = GetEma(quotes, emaPeriods).ToList(); List <AtrResult> atrResults = GetAtr(quotes, atrPeriods).ToList(); int lookbackPeriods = Math.Max(emaPeriods, atrPeriods); // roll through quotes for (int i = 0; i < quotesList.Count; i++) { TQuote q = quotesList[i]; int index = i + 1; KeltnerResult result = new() { Date = q.Date }; if (index >= lookbackPeriods) { EmaResult ema = emaResults[i]; AtrResult atr = atrResults[i]; result.UpperBand = ema.Ema + (multiplier * atr.Atr); result.LowerBand = ema.Ema - (multiplier * atr.Atr); result.Centerline = ema.Ema; result.Width = (result.Centerline == 0) ? null : (result.UpperBand - result.LowerBand) / result.Centerline; } results.Add(result); } return(results); }
public void GetEmaTest() { int lookbackPeriod = 20; IEnumerable <EmaResult> results = Indicator.GetEma(history, lookbackPeriod); // assertions // proper quantities // should always be the same number of results as there is history Assert.AreEqual(502, results.Count()); Assert.AreEqual(502 - lookbackPeriod + 1, results.Where(x => x.Ema != null).Count()); // sample value EmaResult ema = results.Where(x => x.Date == DateTime.Parse("12/31/2018")).FirstOrDefault(); Assert.AreEqual((decimal)249.3519, Math.Round((decimal)ema.Ema, 4)); }
public void GetDoubleEmaTest() { int lookbackPeriod = 20; IEnumerable <EmaResult> results = Indicator.GetDoubleEma(history, lookbackPeriod); // assertions // proper quantities // should always be the same number of results as there is history Assert.AreEqual(502, results.Count()); Assert.AreEqual(502 - 2 * lookbackPeriod + 1, results.Where(x => x.Ema != null).Count()); // sample value EmaResult r = results.Where(x => x.Index == 502).FirstOrDefault(); Assert.AreEqual(241.1677m, Math.Round((decimal)r.Ema, 4)); }
// DOUBLE EXPONENTIAL MOVING AVERAGE /// <include file='./info.xml' path='indicator/*' /> /// public static IEnumerable <DemaResult> GetDoubleEma <TQuote>( this IEnumerable <TQuote> quotes, int lookbackPeriods) where TQuote : IQuote { // convert quotes List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.Close); // check parameter arguments ValidateDema(lookbackPeriods); // initialize List <DemaResult> results = new(bdList.Count); List <EmaResult> emaN = CalcEma(bdList, lookbackPeriods); List <BasicD> bd2 = emaN .Where(x => x.Ema != null) .Select(x => new BasicD { Date = x.Date, Value = (double)x.Ema }) .ToList(); // note: ToList seems to be required when changing data List <EmaResult> emaN2 = CalcEma(bd2, lookbackPeriods); // compose final results for (int i = 0; i < emaN.Count; i++) { EmaResult e1 = emaN[i]; int index = i + 1; DemaResult result = new() { Date = e1.Date }; if (index >= (2 * lookbackPeriods) - 1) { EmaResult e2 = emaN2[index - lookbackPeriods]; result.Dema = (2 * e1.Ema) - e2.Ema; } results.Add(result); } return(results); }
// CHAIKIN OSCILLATOR /// <include file='./info.xml' path='indicator/*' /> /// public static IEnumerable <ChaikinOscResult> GetChaikinOsc <TQuote>( this IEnumerable <TQuote> quotes, int fastPeriods = 3, int slowPeriods = 10) where TQuote : IQuote { // check parameter arguments ValidateChaikinOsc(fastPeriods, slowPeriods); // money flow List <ChaikinOscResult> results = GetAdl(quotes) .Select(r => new ChaikinOscResult { Date = r.Date, MoneyFlowMultiplier = r.MoneyFlowMultiplier, MoneyFlowVolume = r.MoneyFlowVolume, Adl = r.Adl }) .ToList(); // EMA of ADL List <BasicD> bdAdl = results .Select(x => new BasicD { Date = x.Date, Value = x.Adl }) .ToList(); List <EmaResult> adlEmaSlow = CalcEma(bdAdl, slowPeriods); List <EmaResult> adlEmaFast = CalcEma(bdAdl, fastPeriods); // add Oscillator for (int i = slowPeriods - 1; i < results.Count; i++) { ChaikinOscResult r = results[i]; EmaResult f = adlEmaFast[i]; EmaResult s = adlEmaSlow[i]; r.Oscillator = (double)(f.Ema - s.Ema); } return(results); }
public void CustomQuoteClass() { List <MyGenericQuote> myGenericHistory = HistoryTestData.Get() .Select(x => new MyGenericQuote { CloseDate = x.Date, Open = x.Open, High = x.High, Low = x.Low, CloseValue = x.Close, Volume = x.Volume, MyOtherProperty = 123456 }) .ToList(); List <EmaResult> results = Indicator.GetEma(myGenericHistory, 20) .ToList(); // assertions // proper quantities // should always be the same number of results as there is history Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Where(x => x.Ema != null).Count()); // sample values EmaResult r1 = results[501]; Assert.AreEqual(249.3519m, Math.Round((decimal)r1.Ema, 4)); EmaResult r2 = results[249]; Assert.AreEqual(255.3873m, Math.Round((decimal)r2.Ema, 4)); EmaResult r3 = results[29]; Assert.AreEqual(216.6228m, Math.Round((decimal)r3.Ema, 4)); }
// 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); }
// TRIPLE EMA OSCILLATOR (TRIX) /// <include file='./info.xml' path='indicator/*' /> /// public static IEnumerable <TrixResult> GetTrix <TQuote>( this IEnumerable <TQuote> quotes, int lookbackPeriods, int?signalPeriods = null) where TQuote : IQuote { // convert quotes List <BasicD> bdList = quotes.ConvertToBasic(CandlePart.Close); // check parameter arguments ValidateTrix(lookbackPeriods); // initialize List <TrixResult> results = new(bdList.Count); decimal? lastEma = null; List <EmaResult> emaN1 = CalcEma(bdList, lookbackPeriods); List <BasicD> bd2 = emaN1 .Where(x => x.Ema != null) .Select(x => new BasicD { Date = x.Date, Value = (double)x.Ema }) .ToList(); List <EmaResult> emaN2 = CalcEma(bd2, lookbackPeriods); List <BasicD> bd3 = emaN2 .Where(x => x.Ema != null) .Select(x => new BasicD { Date = x.Date, Value = (double)x.Ema }) .ToList(); List <EmaResult> emaN3 = CalcEma(bd3, lookbackPeriods); // compose final results for (int i = 0; i < emaN1.Count; i++) { EmaResult e1 = emaN1[i]; int index = i + 1; TrixResult result = new() { Date = e1.Date }; results.Add(result); if (index >= (3 * lookbackPeriods) - 2) { EmaResult e2 = emaN2[index - lookbackPeriods]; EmaResult e3 = emaN3[index - (2 * lookbackPeriods) + 1]; result.Ema3 = e3.Ema; if (lastEma is not null and not 0) { result.Trix = 100 * (e3.Ema - lastEma) / lastEma; } lastEma = e3.Ema; // optional SMA signal GetTrixSignal(signalPeriods, index, lookbackPeriods, results); } } return(results); }
// 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); }