// TRIPLE EXPONENTIAL MOVING AVERAGE public static IEnumerable <EmaResult> GetTripleEma <TQuote>( IEnumerable <TQuote> history, int lookbackPeriod) where TQuote : IQuote { // convert history to basic format List <BasicData> bdList = history.ConvertToBasic("C"); // check parameter arguments ValidateTema(bdList, lookbackPeriod); // initialize List <EmaResult> results = new List <EmaResult>(bdList.Count); List <EmaResult> emaN1 = CalcEma(bdList, lookbackPeriod).ToList(); List <BasicData> bd2 = emaN1 .Where(x => x.Ema != null) .Select(x => new BasicData { Date = x.Date, Value = (decimal)x.Ema }) .ToList(); List <EmaResult> emaN2 = CalcEma(bd2, lookbackPeriod).ToList(); List <BasicData> bd3 = emaN2 .Where(x => x.Ema != null) .Select(x => new BasicData { Date = x.Date, Value = (decimal)x.Ema }) .ToList(); List <EmaResult> emaN3 = CalcEma(bd3, lookbackPeriod).ToList(); // compose final results for (int i = 0; i < emaN1.Count; i++) { EmaResult e1 = emaN1[i]; int index = i + 1; EmaResult result = new EmaResult { Date = e1.Date }; if (index >= 3 * lookbackPeriod - 2) { EmaResult e2 = emaN2[index - lookbackPeriod]; EmaResult e3 = emaN3[index - 2 * lookbackPeriod + 1]; result.Ema = 3 * e1.Ema - 3 * e2.Ema + e3.Ema; } results.Add(result); } return(results); }
// TRIPLE EXPONENTIAL MOVING AVERAGE public static IEnumerable <EmaResult> GetTripleEma(IEnumerable <Quote> history, int lookbackPeriod) { // convert history to basic format IEnumerable <BasicData> bd = Cleaners.ConvertHistoryToBasic(history, "C"); // validate parameters ValidateTema(bd, lookbackPeriod); // initialize List <EmaResult> results = new List <EmaResult>(); List <EmaResult> emaN1 = CalcEma(bd, lookbackPeriod).ToList(); List <BasicData> bd2 = emaN1 .Where(x => x.Ema != null) .Select(x => new BasicData { Index = null, Date = x.Date, Value = (decimal)x.Ema }) .ToList(); List <EmaResult> emaN2 = CalcEma(bd2, lookbackPeriod).ToList(); List <BasicData> bd3 = emaN2 .Where(x => x.Ema != null) .Select(x => new BasicData { Index = null, Date = x.Date, Value = (decimal)x.Ema }) .ToList(); List <EmaResult> emaN3 = CalcEma(bd3, lookbackPeriod).ToList(); // compose final results for (int i = 0; i < emaN1.Count; i++) { EmaResult e1 = emaN1[i]; EmaResult result = new EmaResult { Index = e1.Index, Date = e1.Date }; if (e1.Index >= 3 * lookbackPeriod - 2) { EmaResult e2 = emaN2[e1.Index - lookbackPeriod]; EmaResult e3 = emaN3[e2.Index - lookbackPeriod]; result.Ema = 3 * e1.Ema - 3 * e2.Ema + e3.Ema; } results.Add(result); } return(results); }
// SIMPLE MOVING AVERAGE public static IEnumerable <ChaikinOscResult> GetChaikinOsc( IEnumerable <Quote> history, int fastPeriod = 3, int slowPeriod = 10) { // clean quotes history = Cleaners.PrepareHistory(history); // check parameters ValidateChaikinOsc(history, fastPeriod, slowPeriod); // initialize List <ChaikinOscResult> results = new List <ChaikinOscResult>(); IEnumerable <AdlResult> adlResults = GetAdl(history); // EMA of ADL IEnumerable <BasicData> adlBasicData = adlResults .Select(x => new BasicData { Index = x.Index, Date = x.Date, Value = x.Adl }); IEnumerable <EmaResult> adlEmaSlow = CalcEma(adlBasicData, slowPeriod); IEnumerable <EmaResult> adlEmaFast = CalcEma(adlBasicData, fastPeriod); // roll through history foreach (AdlResult r in adlResults) { ChaikinOscResult result = new ChaikinOscResult { Index = r.Index, Date = r.Date, MoneyFlowMultiplier = r.MoneyFlowMultiplier, MoneyFlowVolume = r.MoneyFlowVolume, Adl = r.Adl }; // add Oscillator if (r.Index >= slowPeriod) { EmaResult f = adlEmaFast.Where(x => x.Index == r.Index).FirstOrDefault(); EmaResult s = adlEmaSlow.Where(x => x.Index == r.Index).FirstOrDefault(); result.Oscillator = f.Ema - s.Ema; } results.Add(result); } return(results); }
// SIMPLE MOVING AVERAGE public static IEnumerable <ChaikinOscResult> GetChaikinOsc( IEnumerable <Quote> history, int fastPeriod = 3, int slowPeriod = 10) { // clean quotes Cleaners.PrepareHistory(history); // check parameters ValidateChaikinOsc(history, fastPeriod, slowPeriod); // initialize IEnumerable <AdlResult> adlResults = GetAdl(history); // money flow List <ChaikinOscResult> results = adlResults .Select(r => new ChaikinOscResult { Index = r.Index, Date = r.Date, MoneyFlowMultiplier = r.MoneyFlowMultiplier, MoneyFlowVolume = r.MoneyFlowVolume, Adl = r.Adl }) .ToList(); // EMA of ADL IEnumerable <BasicData> adlBasicData = adlResults .Select(x => new BasicData { Index = x.Index, Date = x.Date, Value = x.Adl }); List <EmaResult> adlEmaSlow = CalcEma(adlBasicData, slowPeriod).ToList(); List <EmaResult> adlEmaFast = CalcEma(adlBasicData, fastPeriod).ToList(); // add Oscillator for (int i = slowPeriod - 1; i < results.Count; i++) { ChaikinOscResult r = results[i]; EmaResult f = adlEmaFast[i]; EmaResult s = adlEmaSlow[i]; r.Oscillator = f.Ema - s.Ema; } return(results); }
// EXPONENTIAL MOVING AVERAGE public static IEnumerable <EmaResult> GetEma(IEnumerable <Quote> history, int lookbackPeriod) { // clean quotes history = Cleaners.PrepareHistory(history); // check exceptions int qtyHistory = history.Count(); int minHistory = 2 * lookbackPeriod; if (qtyHistory < minHistory) { throw new BadHistoryException("Insufficient history provided for EMA. " + string.Format("You provided {0} periods of history when {1} is required. " + "Since this uses a smoothing technique, " + "we recommend you use at least 250 data points prior to the intended " + "usage date for maximum precision.", qtyHistory, minHistory)); } // initialize List <EmaResult> results = new List <EmaResult>(); // initialize EMA decimal k = 2 / (decimal)(lookbackPeriod + 1); decimal lastEma = history .Where(x => x.Index < lookbackPeriod) .Select(x => x.Close) .Average(); // roll through history foreach (Quote h in history) { EmaResult result = new EmaResult { Index = (int)h.Index, Date = h.Date }; if (h.Index >= lookbackPeriod) { result.Ema = lastEma + k * (h.Close - lastEma); lastEma = (decimal)result.Ema; } results.Add(result); } return(results); }
// DONCHIAN CHANNEL public static IEnumerable <KeltnerResult> GetKeltner( IEnumerable <Quote> history, int emaPeriod = 20, decimal multiplier = 2, int atrPeriod = 10) { // clean quotes history = Cleaners.PrepareHistory(history); // validate parameters ValidateKeltner(history, emaPeriod, multiplier, atrPeriod); // initialize List <KeltnerResult> results = new List <KeltnerResult>(); IEnumerable <EmaResult> emaResults = GetEma(history, emaPeriod); IEnumerable <AtrResult> atrResults = GetAtr(history, atrPeriod); int lookbackPeriod = Math.Max(emaPeriod, atrPeriod); decimal?prevWidth = null; // roll through history foreach (Quote h in history) { KeltnerResult result = new KeltnerResult { Index = (int)h.Index, Date = h.Date }; if (h.Index >= lookbackPeriod) { IEnumerable <Quote> period = history .Where(x => x.Index > (h.Index - lookbackPeriod) && x.Index <= h.Index); EmaResult ema = emaResults.Where(x => x.Index == h.Index).FirstOrDefault(); AtrResult atr = atrResults.Where(x => x.Index == h.Index).FirstOrDefault(); 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; // for next iteration prevWidth = result.Width; } results.Add(result); } return(results); }
// DONCHIAN CHANNEL public static IEnumerable <KeltnerResult> GetKeltner <TQuote>( IEnumerable <TQuote> history, int emaPeriod = 20, decimal multiplier = 2, int atrPeriod = 10) where TQuote : IQuote { // clean quotes List <TQuote> historyList = history.Sort(); // validate parameters ValidateKeltner(history, emaPeriod, multiplier, atrPeriod); // initialize List <KeltnerResult> results = new List <KeltnerResult>(); List <EmaResult> emaResults = GetEma(history, emaPeriod).ToList(); List <AtrResult> atrResults = GetAtr(history, atrPeriod).ToList(); int lookbackPeriod = Math.Max(emaPeriod, atrPeriod); // roll through history for (int i = 0; i < historyList.Count; i++) { TQuote h = historyList[i]; int index = i + 1; KeltnerResult result = new KeltnerResult { Date = h.Date }; if (index >= lookbackPeriod) { 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); }
// DOUBLE EXPONENTIAL MOVING AVERAGE /// <include file='./info.xml' path='indicators/type[@name="DEMA"]/*' /> /// public static IEnumerable <EmaResult> GetDoubleEma <TQuote>( this IEnumerable <TQuote> history, int lookbackPeriod) where TQuote : IQuote { // convert history to basic format List <BasicData> bdList = history.ConvertToBasic("C"); // check parameter arguments ValidateDema(bdList, lookbackPeriod); // initialize List <EmaResult> results = new(bdList.Count); List <EmaResult> emaN = CalcEma(bdList, lookbackPeriod).ToList(); List <BasicData> bd2 = emaN .Where(x => x.Ema != null) .Select(x => new BasicData { Date = x.Date, Value = (decimal)x.Ema }) .ToList(); // note: ToList seems to be required when changing data List <EmaResult> emaN2 = CalcEma(bd2, lookbackPeriod).ToList(); // compose final results for (int i = 0; i < emaN.Count; i++) { EmaResult e1 = emaN[i]; int index = i + 1; EmaResult result = new() { Date = e1.Date }; if (index >= 2 * lookbackPeriod - 1) { EmaResult e2 = emaN2[index - lookbackPeriod]; result.Ema = 2 * e1.Ema - e2.Ema; } results.Add(result); } return(results); }
private static IEnumerable <EmaResult> CalcEma( List <BasicData> bdList, int lookbackPeriod) { // check parameter arguments ValidateEma(bdList, lookbackPeriod); // initialize List <EmaResult> results = new List <EmaResult>(bdList.Count); decimal k = 2 / (decimal)(lookbackPeriod + 1); decimal lastEma = 0; for (int i = 0; i < lookbackPeriod; i++) { lastEma += bdList[i].Value; } lastEma /= lookbackPeriod; // roll through history for (int i = 0; i < bdList.Count; i++) { BasicData h = bdList[i]; int index = i + 1; EmaResult result = new EmaResult { Date = h.Date }; if (index > lookbackPeriod) { result.Ema = lastEma + k * (h.Value - lastEma); lastEma = (decimal)result.Ema; } else if (index == lookbackPeriod) { result.Ema = lastEma; } results.Add(result); } return(results); }
// CHAIKIN OSCILLATOR public static IEnumerable <ChaikinOscResult> GetChaikinOsc <TQuote>( IEnumerable <TQuote> history, int fastPeriod = 3, int slowPeriod = 10) where TQuote : IQuote { // check parameters ValidateChaikinOsc(history, fastPeriod, slowPeriod); // money flow List <ChaikinOscResult> results = GetAdl(history) .Select(r => new ChaikinOscResult { Date = r.Date, MoneyFlowMultiplier = r.MoneyFlowMultiplier, MoneyFlowVolume = r.MoneyFlowVolume, Adl = r.Adl }) .ToList(); // EMA of ADL List <BasicData> adlBasicData = results .Select(x => new BasicData { Date = x.Date, Value = x.Adl }) .ToList(); List <EmaResult> adlEmaSlow = CalcEma(adlBasicData, slowPeriod).ToList(); List <EmaResult> adlEmaFast = CalcEma(adlBasicData, fastPeriod).ToList(); // add Oscillator for (int i = slowPeriod - 1; i < results.Count; i++) { ChaikinOscResult r = results[i]; EmaResult f = adlEmaFast[i]; EmaResult s = adlEmaSlow[i]; r.Oscillator = f.Ema - s.Ema; } return(results); }
private static IEnumerable <EmaResult> CalcEma(IEnumerable <BasicData> basicData, int lookbackPeriod) { // clean quotes basicData = Cleaners.PrepareBasicData(basicData); // validate parameters ValidateEma(basicData, lookbackPeriod); // initialize List <EmaResult> results = new List <EmaResult>(); // initialize EMA decimal k = 2 / (decimal)(lookbackPeriod + 1); decimal lastEma = basicData .Where(x => x.Index <= lookbackPeriod) .ToList() .Select(x => x.Value) .Average(); // roll through history foreach (BasicData h in basicData) { EmaResult result = new EmaResult { Index = (int)h.Index, Date = h.Date }; if (h.Index > lookbackPeriod) { result.Ema = lastEma + k * (h.Value - lastEma); lastEma = (decimal)result.Ema; } else if (h.Index == lookbackPeriod) { result.Ema = lastEma; } results.Add(result); } return(results); }
// DOUBLE EXPONENTIAL MOVING AVERAGE public static IEnumerable <EmaResult> GetDoubleEma(IEnumerable <Quote> history, int lookbackPeriod) { // convert history to basic format IEnumerable <BasicData> bd = Cleaners.ConvertHistoryToBasic(history, "C"); // validate parameters ValidateDema(bd, lookbackPeriod); // initialize List <EmaResult> results = new List <EmaResult>(); List <EmaResult> emaN = CalcEma(bd, lookbackPeriod).ToList(); List <BasicData> bd2 = emaN .Where(x => x.Ema != null) .Select(x => new BasicData { Index = null, Date = x.Date, Value = (decimal)x.Ema }) .ToList(); // note: ToList seems to be required when changing data List <EmaResult> emaN2 = CalcEma(bd2, lookbackPeriod).ToList(); // compose final results for (int i = 0; i < emaN.Count; i++) { EmaResult e1 = emaN[i]; EmaResult result = new EmaResult { Index = e1.Index, Date = e1.Date }; if (e1.Index >= 2 * lookbackPeriod - 1) { EmaResult e2 = emaN2[e1.Index - lookbackPeriod]; result.Ema = 2 * e1.Ema - e2.Ema; } results.Add(result); } return(results); }
// DOUBLE EXPONENTIAL MOVING AVERAGE public static IEnumerable <EmaResult> GetDoubleEma(IEnumerable <Quote> history, int lookbackPeriod) { // convert history to basic format IEnumerable <BasicData> bd = Cleaners.ConvertHistoryToBasic(history, "C"); // validate parameters ValidateDema(bd, lookbackPeriod); // initialize List <EmaResult> results = new List <EmaResult>(); IEnumerable <EmaResult> emaN = CalcEma(bd, lookbackPeriod); List <BasicData> bd2 = emaN .Where(x => x.Ema != null) .Select(x => new BasicData { Index = null, Date = x.Date, Value = (decimal)x.Ema }) .ToList(); // note: ToList seems to be required when changing data IEnumerable <EmaResult> emaN2 = CalcEma(bd2, lookbackPeriod); // compose final results foreach (EmaResult h in emaN) { EmaResult result = new EmaResult { Index = h.Index, Date = h.Date }; if (h.Index >= 2 * lookbackPeriod) { EmaResult emaEma = emaN2.Where(x => x.Index == h.Index - lookbackPeriod + 1).FirstOrDefault(); result.Ema = 2 * h.Ema - emaEma.Ema; } results.Add(result); } return(results); }
// EXPONENTIAL MOVING AVERAGE public static IEnumerable <EmaResult> GetEma(IEnumerable <Quote> history, int lookbackPeriod) { // clean quotes history = Cleaners.PrepareHistory(history); // validate parameters ValidateEma(history, lookbackPeriod); // initialize List <EmaResult> results = new List <EmaResult>(); // initialize EMA decimal k = 2 / (decimal)(lookbackPeriod + 1); decimal lastEma = history .Where(x => x.Index < lookbackPeriod) .Select(x => x.Close) .Average(); // roll through history foreach (Quote h in history) { EmaResult result = new EmaResult { Index = (int)h.Index, Date = h.Date }; if (h.Index >= lookbackPeriod) { result.Ema = lastEma + k * (h.Close - lastEma); lastEma = (decimal)result.Ema; } results.Add(result); } return(results); }
// MOVING AVERAGE CONVERGENCE/DIVERGENCE (MACD) OSCILLATOR public static IEnumerable <MacdResult> GetMacd(IEnumerable <Quote> history, int fastPeriod = 12, int slowPeriod = 26, int signalPeriod = 9) { // clean quotes history = Cleaners.PrepareHistory(history); // check parameters ValidateMacd(history, fastPeriod, slowPeriod, signalPeriod); // initialize IEnumerable <EmaResult> emaFast = GetEma(history, fastPeriod); IEnumerable <EmaResult> emaSlow = GetEma(history, slowPeriod); List <BasicData> emaDiff = new List <BasicData>(); List <MacdResult> results = new List <MacdResult>(); foreach (Quote h in history) { EmaResult df = emaFast.Where(x => x.Date == h.Date).FirstOrDefault(); EmaResult ds = emaSlow.Where(x => x.Date == h.Date).FirstOrDefault(); MacdResult result = new MacdResult { Index = (int)h.Index, Date = h.Date }; if (df?.Ema != null && ds?.Ema != null) { decimal macd = (decimal)df.Ema - (decimal)ds.Ema; result.Macd = macd; // temp data for interim EMA of macd BasicData diff = new BasicData { Date = h.Date, Index = h.Index - slowPeriod, Value = macd }; emaDiff.Add(diff); } results.Add(result); } IEnumerable <EmaResult> emaSignal = CalcEma(emaDiff, signalPeriod); decimal?prevMacd = null; decimal?prevSignal = null; // add signal, histogram to result foreach (MacdResult r in results) { EmaResult ds = emaSignal.Where(x => x.Date == r.Date).FirstOrDefault(); if (ds?.Ema == null) { continue; } r.Signal = ds.Ema; r.Histogram = r.Macd - r.Signal; // trend and divergence if (prevMacd != null && prevSignal != null) { r.IsBullish = (r.Macd > r.Signal); r.IsDiverging = (Math.Abs((decimal)r.Macd - (decimal)r.Signal) > Math.Abs((decimal)prevMacd - (decimal)prevSignal)); } // store for next iteration prevMacd = r.Macd; prevSignal = r.Signal; } return(results); }
// TRIPLE EMA OSCILLATOR (TRIX) /// <include file='./info.xml' path='indicator/*' /> /// public static IEnumerable <TrixResult> GetTrix <TQuote>( this IEnumerable <TQuote> history, int lookbackPeriod, int?signalPeriod = null) where TQuote : IQuote { // convert history to basic format List <BasicData> bdList = history.ConvertToBasic("C"); // check parameter arguments ValidateTrix(bdList, lookbackPeriod); // initialize List <TrixResult> results = new(bdList.Count); decimal? lastEma = null; List <EmaResult> emaN1 = CalcEma(bdList, lookbackPeriod).ToList(); List <BasicData> bd2 = emaN1 .Where(x => x.Ema != null) .Select(x => new BasicData { Date = x.Date, Value = (decimal)x.Ema }) .ToList(); List <EmaResult> emaN2 = CalcEma(bd2, lookbackPeriod).ToList(); List <BasicData> bd3 = emaN2 .Where(x => x.Ema != null) .Select(x => new BasicData { Date = x.Date, Value = (decimal)x.Ema }) .ToList(); List <EmaResult> emaN3 = CalcEma(bd3, lookbackPeriod).ToList(); // 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 * lookbackPeriod - 2) { EmaResult e2 = emaN2[index - lookbackPeriod]; EmaResult e3 = emaN3[index - 2 * lookbackPeriod + 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(signalPeriod, index, lookbackPeriod, results); } } return(results); }
// PRICE VOLUME OSCILLATOR (PVO) public static IEnumerable <PvoResult> GetPvo <TQuote>( IEnumerable <TQuote> history, int fastPeriod = 12, int slowPeriod = 26, int signalPeriod = 9) where TQuote : IQuote { // convert history to basic format List <BasicData> bdList = history.ConvertToBasic("V"); // check parameter arguments ValidatePvo(history, fastPeriod, slowPeriod, signalPeriod); // initialize List <EmaResult> emaFast = CalcEma(bdList, fastPeriod).ToList(); List <EmaResult> emaSlow = CalcEma(bdList, slowPeriod).ToList(); int size = bdList.Count; List <BasicData> emaDiff = new List <BasicData>(); List <PvoResult> results = new List <PvoResult>(size); // roll through history for (int i = 0; i < size; i++) { BasicData h = bdList[i]; EmaResult df = emaFast[i]; EmaResult ds = emaSlow[i]; PvoResult result = new PvoResult { Date = h.Date }; if (df?.Ema != null && ds?.Ema != null) { decimal?pvo = (ds.Ema != 0) ? 100 * (df.Ema - ds.Ema) / ds.Ema : null; result.Pvo = pvo; // temp data for interim EMA of PVO BasicData diff = new BasicData { Date = h.Date, Value = (decimal)pvo }; emaDiff.Add(diff); } results.Add(result); } // add signal and histogram to result List <EmaResult> emaSignal = CalcEma(emaDiff, signalPeriod).ToList(); for (int d = slowPeriod - 1; d < size; d++) { PvoResult r = results[d]; EmaResult ds = emaSignal[d + 1 - slowPeriod]; r.Signal = ds.Ema; r.Histogram = r.Pvo - r.Signal; } return(results); }
// MOVING AVERAGE CONVERGENCE/DIVERGENCE (MACD) OSCILLATOR public static IEnumerable <MacdResult> GetMacd <TQuote>( IEnumerable <TQuote> history, int fastPeriod = 12, int slowPeriod = 26, int signalPeriod = 9) where TQuote : IQuote { // convert history to basic format List <BasicData> bdList = Cleaners.ConvertHistoryToBasic(history, "C"); // check parameter arguments ValidateMacd(history, fastPeriod, slowPeriod, signalPeriod); // initialize List <EmaResult> emaFast = CalcEma(bdList, fastPeriod).ToList(); List <EmaResult> emaSlow = CalcEma(bdList, slowPeriod).ToList(); int size = bdList.Count; List <BasicData> emaDiff = new List <BasicData>(); List <MacdResult> results = new List <MacdResult>(size); // roll through history for (int i = 0; i < size; i++) { BasicData h = bdList[i]; EmaResult df = emaFast[i]; EmaResult ds = emaSlow[i]; MacdResult result = new MacdResult { Date = h.Date }; if (df?.Ema != null && ds?.Ema != null) { decimal macd = (decimal)df.Ema - (decimal)ds.Ema; result.Macd = macd; // temp data for interim EMA of macd BasicData diff = new BasicData { Date = h.Date, Value = macd }; emaDiff.Add(diff); } results.Add(result); } // add signal and histogram to result List <EmaResult> emaSignal = CalcEma(emaDiff, signalPeriod).ToList(); for (int d = slowPeriod - 1; d < size; d++) { MacdResult r = results[d]; EmaResult ds = emaSignal[d + 1 - slowPeriod]; r.Signal = ds.Ema; r.Histogram = r.Macd - r.Signal; } return(results); }
// MOVING AVERAGE CONVERGENCE/DIVERGENCE (MACD) OSCILLATOR public static IEnumerable <MacdResult> GetMacd(IEnumerable <Quote> history, int fastPeriod = 12, int slowPeriod = 26, int signalPeriod = 9) { // clean quotes history = Cleaners.PrepareHistory(history); // check for exceptions if (slowPeriod <= fastPeriod) { throw new BadParameterException("Fast period must be smaller than the slow period."); } int qtyHistory = history.Count(); int minHistory = 2 * slowPeriod + signalPeriod; if (qtyHistory < minHistory) { throw new BadHistoryException("Insufficient history provided for MACD. " + string.Format("You provided {0} periods of history when {1} is required. " + "Since this uses a smoothing technique, " + "we recommend you use at least 250 data points prior to the intended " + "usage date for maximum precision.", qtyHistory, minHistory)); } // initialize IEnumerable <EmaResult> emaFast = GetEma(history, fastPeriod); IEnumerable <EmaResult> emaSlow = GetEma(history, slowPeriod); List <Quote> emaDiff = new List <Quote>(); List <MacdResult> results = new List <MacdResult>(); foreach (Quote h in history) { EmaResult df = emaFast.Where(x => x.Date == h.Date).FirstOrDefault(); EmaResult ds = emaSlow.Where(x => x.Date == h.Date).FirstOrDefault(); MacdResult result = new MacdResult { Index = (int)h.Index, Date = h.Date }; if (df?.Ema != null && ds?.Ema != null) { decimal macd = (decimal)df.Ema - (decimal)ds.Ema; result.Macd = macd; // temp data for interim EMA of macd Quote diff = new Quote { Date = h.Date, Index = h.Index - slowPeriod, Close = macd }; emaDiff.Add(diff); } results.Add(result); } IEnumerable <EmaResult> emaSignal = GetEma(emaDiff, signalPeriod); decimal?prevMacd = null; decimal?prevSignal = null; // add signal, histogram to result foreach (MacdResult r in results) { EmaResult ds = emaSignal.Where(x => x.Date == r.Date).FirstOrDefault(); if (ds?.Ema == null) { continue; } r.Signal = ds.Ema; r.Histogram = r.Macd - r.Signal; // trend and divergence if (prevMacd != null && prevSignal != null) { r.IsBullish = (r.Macd > r.Signal) ? true : false; r.IsDiverging = (Math.Abs((decimal)r.Macd - (decimal)r.Signal) > Math.Abs((decimal)prevMacd - (decimal)prevSignal)) ? true : false; } // store for next iteration prevMacd = r.Macd; prevSignal = r.Signal; } return(results); }
// TRIPLE EMA OSCILLATOR (TRIX) public static IEnumerable <TrixResult> GetTrix <TQuote>( IEnumerable <TQuote> history, int lookbackPeriod, int?signalPeriod = null) where TQuote : IQuote { // convert history to basic format List <BasicData> bd = Cleaners.ConvertHistoryToBasic(history, "C"); // validate parameters ValidateTrix(bd, lookbackPeriod); // initialize List <TrixResult> results = new List <TrixResult>(); decimal? lastEma = null; List <EmaResult> emaN1 = CalcEma(bd, lookbackPeriod).ToList(); List <BasicData> bd2 = emaN1 .Where(x => x.Ema != null) .Select(x => new BasicData { Date = x.Date, Value = (decimal)x.Ema }) .ToList(); List <EmaResult> emaN2 = CalcEma(bd2, lookbackPeriod).ToList(); List <BasicData> bd3 = emaN2 .Where(x => x.Ema != null) .Select(x => new BasicData { Date = x.Date, Value = (decimal)x.Ema }) .ToList(); List <EmaResult> emaN3 = CalcEma(bd3, lookbackPeriod).ToList(); // compose final results for (int i = 0; i < emaN1.Count; i++) { EmaResult e1 = emaN1[i]; int index = i + 1; TrixResult result = new TrixResult { Date = e1.Date }; results.Add(result); if (index >= 3 * lookbackPeriod - 2) { EmaResult e2 = emaN2[index - lookbackPeriod]; EmaResult e3 = emaN3[index - 2 * lookbackPeriod + 1]; result.Ema3 = e3.Ema; if (lastEma != null && lastEma != 0) { result.Trix = 100 * (e3.Ema - lastEma) / lastEma; } lastEma = e3.Ema; // optional SMA if (signalPeriod != null && index >= 3 * lookbackPeriod - 2 + signalPeriod) { decimal sumSma = 0m; for (int p = index - (int)signalPeriod; p < index; p++) { sumSma += (decimal)results[p].Trix; } result.Signal = sumSma / signalPeriod; } } } return(results); }
// MOVING AVERAGE CONVERGENCE/DIVERGENCE (MACD) OSCILLATOR public static IEnumerable <MacdResult> GetMacd <TQuote>( IEnumerable <TQuote> history, int fastPeriod = 12, int slowPeriod = 26, int signalPeriod = 9) where TQuote : IQuote { // clean quotes List <TQuote> historyList = history.Sort(); // check parameters ValidateMacd(history, fastPeriod, slowPeriod, signalPeriod); // initialize List <EmaResult> emaFast = GetEma(history, fastPeriod).ToList(); List <EmaResult> emaSlow = GetEma(history, slowPeriod).ToList(); List <BasicData> emaDiff = new List <BasicData>(); List <MacdResult> results = new List <MacdResult>(); for (int i = 0; i < historyList.Count; i++) { TQuote h = historyList[i]; EmaResult df = emaFast[i]; EmaResult ds = emaSlow[i]; MacdResult result = new MacdResult { Date = h.Date }; if (df?.Ema != null && ds?.Ema != null) { decimal macd = (decimal)df.Ema - (decimal)ds.Ema; result.Macd = macd; // temp data for interim EMA of macd BasicData diff = new BasicData { Date = h.Date, Value = macd }; emaDiff.Add(diff); } results.Add(result); } // add signal and histogram to result List <EmaResult> emaSignal = CalcEma(emaDiff, signalPeriod).ToList(); for (int d = slowPeriod - 1; d < results.Count; d++) { MacdResult r = results[d]; EmaResult ds = emaSignal[d + 1 - slowPeriod]; r.Signal = ds.Ema; r.Histogram = r.Macd - r.Signal; } return(results); }