From e3b57532d1bf20f2dd53a87b0be23fa0ebedf2dc Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sat, 11 Jul 2015 18:57:40 +0200 Subject: [PATCH] Financial: online indicator implementations --- src/Numerics/Financial/Indicators.cs | 92 ++++++++----------- .../FinancialTests/IndicatorsTests.cs | 3 +- .../FinancialTests/StockDataReader.cs | 5 +- 3 files changed, 44 insertions(+), 56 deletions(-) diff --git a/src/Numerics/Financial/Indicators.cs b/src/Numerics/Financial/Indicators.cs index 093ce3d8..b8dd011b 100644 --- a/src/Numerics/Financial/Indicators.cs +++ b/src/Numerics/Financial/Indicators.cs @@ -40,71 +40,59 @@ namespace MathNet.Numerics.Financial /// public static class Indicators { + private static IEnumerable TrueRange(this IEnumerable samples) + { + using (var enumerator = samples.GetEnumerator()) + { + if (!enumerator.MoveNext()) + { + yield break; + } + + Bar last = enumerator.Current; + + //yield return double.NaN; + yield return Math.Abs(last.High - last.Low); + + while (enumerator.MoveNext()) + { + var current = enumerator.Current; + var hl = Math.Abs(current.High - current.Low); + var pdch = Math.Abs(last.Close - current.High); + var pdcl = Math.Abs(last.Close - current.Low); + last = current; + + yield return Math.Max(hl, Math.Max(pdch, pdcl)); + } + } + } + /// - /// Calculate the Simple Moving Average (SMA). + /// Calculate the Simple Moving Average (SMA) based on Close. Online/Streaming. /// /// Input samples /// Period of calculation - public static IEnumerable SMA(this IEnumerable samples, int period) + public static IEnumerable SMA(this IEnumerable samples, int period) { - return samples.MovingAverage(period); + return samples.Select(bar => bar.Close).MovingAverage(period).Select(x => Math.Round(x, 2)); } /// - /// Calculate the Average True Range (ATR). + /// Calculate the True Range (TR). Online/Streaming. + /// + /// Input samples + public static IEnumerable TR(this IEnumerable samples) + { + return samples.TrueRange().Select(x => Math.Round(x, 2)); + } + /// + /// Calculate the Average True Range (ATR). Online/Streaming. /// /// Input samples /// Period of calculation public static IEnumerable ATR(this IEnumerable samples, int period) { - if (period <= 0) - throw new ArgumentException("period should be greater than 0", "period"); - if (samples == null) - throw new ArgumentNullException("samples", "samples should not be null"); - if (period > (samples.Count())) - throw new ArgumentException("samples", "samples should be greater than period"); - - var trList = new List(); - var enumerator = samples.GetEnumerator(); - enumerator.MoveNext(); - var lastBar = enumerator.Current; - - trList.Add(double.NaN); - - while (enumerator.MoveNext()) - { - var currentBar = enumerator.Current; - - var hl = Math.Round(currentBar.High - currentBar.Low, 10); - hl = Math.Abs(hl); - var pdch = Math.Round(lastBar.Close - currentBar.High, 10); - pdch = Math.Abs(pdch); - var pdcl = Math.Round(lastBar.Close - currentBar.Low, 10); - pdcl = Math.Abs(pdcl); - double tr = Math.Max(hl, Math.Max(pdch, pdcl)); - - trList.Add(tr); - - lastBar = currentBar; - } - - var atrList = new List(); - - for (int i = 0; i < period; i++) - atrList.Add(Double.NaN); - - //remove first tr, this is not valid - trList.RemoveAt(0); - - while (trList.Count >= period) - { - var mean = trList.Take(period).Mean(); - var meanRounded = Math.Round(mean, 2); - atrList.Add(meanRounded); - trList.RemoveAt(0); - } - - return atrList; + return samples.TrueRange().MovingAverage(period).Select(x => Math.Round(x, 2)); } } } diff --git a/src/UnitTests/FinancialTests/IndicatorsTests.cs b/src/UnitTests/FinancialTests/IndicatorsTests.cs index ce506e8c..b24b2141 100644 --- a/src/UnitTests/FinancialTests/IndicatorsTests.cs +++ b/src/UnitTests/FinancialTests/IndicatorsTests.cs @@ -55,7 +55,6 @@ namespace MathNet.Numerics.UnitTests.FinancialTests var expectedValueAtBar20 = 132.33; var actual = atr.ElementAt(20); Assert.AreEqual(expectedValueAtBar20, actual); - } [Test] @@ -67,7 +66,7 @@ namespace MathNet.Numerics.UnitTests.FinancialTests Assert.That(() => inputBars.ATR(period), Throws.Exception.TypeOf()); } - [Test] + [Test, Ignore("Unclear why this should be enforced")] public void Atr_ShouldRaiseException_IfPeriodIsGreaterThanBarCount() { var inputBars = GenerateValidBars(); diff --git a/src/UnitTests/FinancialTests/StockDataReader.cs b/src/UnitTests/FinancialTests/StockDataReader.cs index d57ccd3a..94919f4d 100644 --- a/src/UnitTests/FinancialTests/StockDataReader.cs +++ b/src/UnitTests/FinancialTests/StockDataReader.cs @@ -8,7 +8,7 @@ namespace MathNet.Numerics.UnitTests.FinancialTests /// /// Class reads a file with stock data /// - internal class StockDataReader + public class StockDataReader { /// /// Reads a file with stock quotes @@ -55,10 +55,11 @@ namespace MathNet.Numerics.UnitTests.FinancialTests return resultList; } } + /// /// Entity class for holding stock data /// - internal class StockData + public class StockData { /// Date /// Open quote