From 1791086ee0c2a8d3f62a2f30939371139893111e Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Sat, 28 Jan 2023 01:19:08 -0500 Subject: [PATCH 01/18] rebase version branch (#1013) --- .github/build.main.yml | 2 ++ gitversion.yml | 1 + 2 files changed, 3 insertions(+) diff --git a/.github/build.main.yml b/.github/build.main.yml index f82a1585d..2eb715e33 100644 --- a/.github/build.main.yml +++ b/.github/build.main.yml @@ -1,8 +1,10 @@ trigger: - main + - v3 pr: - main + - v3 pool: name: Azure Pipelines diff --git a/gitversion.yml b/gitversion.yml index 4cb24ba14..3c3ed762a 100644 --- a/gitversion.yml +++ b/gitversion.yml @@ -1,4 +1,5 @@ mode: ContinuousDelivery +next-version: 3.0.0 commit-message-incrementing: Enabled major-version-bump-message: '\+semver:\s?(breaking|major)' From 39bf1150c1c82bcf08add39750f4f2b838e46a41 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Sun, 5 Feb 2023 16:04:00 -0500 Subject: [PATCH 02/18] streaming preview: quotes, use, EMA, SMA (#824) - handle live quotes and provide them to other subscribers - enable Use, EMA, and SMA indicator streaming --- Stock.Indicators.sln | 12 +- src/GlobalSuppressions.cs | 20 ++ src/_common/Generics/Seek.cs | 14 +- src/_common/Generics/info.xml | 19 +- src/_common/Observables/ChainProvider.cs | 194 ++++++++++ src/_common/Observables/QuoteObserver.cs | 29 ++ src/_common/Observables/QuoteProvider.cs | 186 ++++++++++ src/_common/Observables/TupleObserver.cs | 29 ++ src/_common/Observables/TupleProvider.cs | 174 +++++++++ src/_common/Quotes/Quote.Converters.cs | 11 - src/_common/Quotes/Use.Api.cs | 21 ++ src/_common/Quotes/Use.Observer.cs | 76 ++++ src/_common/Results/Result.Utilities.cs | 19 +- src/_common/Results/info.xml | 12 +- src/e-k/Ema/Ema.Api.cs | 33 +- src/e-k/Ema/Ema.Observer.cs | 144 ++++++++ src/e-k/Ema/Ema.Series.cs | 4 +- src/e-k/Ema/Ema.Stream.cs | 82 ----- src/e-k/Ema/info.xml | 23 +- src/s-z/Sma/Sma.Api.cs | 22 ++ src/s-z/Sma/Sma.Observer.cs | 144 ++++++++ src/s-z/Sma/Sma.Series.cs | 28 +- src/s-z/Sma/info.xml | 29 +- tests/indicators/_Initialize.cs | 1 + .../indicators/_common/Generics/Seek.Tests.cs | 29 +- tests/indicators/_common/Helper.Random.cs | 8 +- .../_common/Quotes/Quote.Aggregates.Tests.cs | 2 +- .../_common/Quotes/Quote.Converters.Tests.cs | 2 +- .../_common/Quotes/Quote.Validation.Tests.cs | 2 +- .../_common/Quotes/Test.Quote.Provider.cs | 61 ++++ .../_common/Quotes/Test.Use.Observer.cs | 99 ++++++ tests/indicators/e-k/Ema/Ema.Obs.Tests.cs | 100 ++++++ .../Ema/{Ema.Tests.cs => Ema.Static.Tests.cs} | 99 ++---- tests/indicators/s-z/Sma/Sma.Obs.Tests.cs | 126 +++++++ .../Sma/{Sma.Tests.cs => Sma.Static.Tests.cs} | 22 +- tests/observe/Observe.Streaming.csproj | 19 + tests/observe/Program.cs | 117 ++++++ tests/observe/README.md | 10 + tests/performance/Perf.Helpers.cs | 16 +- tests/performance/Perf.Indicators.Static.cs | 323 +++++++++++++++++ tests/performance/Perf.Indicators.Stream.cs | 62 ++++ tests/performance/Perf.Indicators.cs | 335 ------------------ 42 files changed, 2168 insertions(+), 590 deletions(-) create mode 100644 src/_common/Observables/ChainProvider.cs create mode 100644 src/_common/Observables/QuoteObserver.cs create mode 100644 src/_common/Observables/QuoteProvider.cs create mode 100644 src/_common/Observables/TupleObserver.cs create mode 100644 src/_common/Observables/TupleProvider.cs create mode 100644 src/_common/Quotes/Use.Api.cs create mode 100644 src/_common/Quotes/Use.Observer.cs create mode 100644 src/e-k/Ema/Ema.Observer.cs delete mode 100644 src/e-k/Ema/Ema.Stream.cs create mode 100644 src/s-z/Sma/Sma.Observer.cs create mode 100644 tests/indicators/_common/Quotes/Test.Quote.Provider.cs create mode 100644 tests/indicators/_common/Quotes/Test.Use.Observer.cs create mode 100644 tests/indicators/e-k/Ema/Ema.Obs.Tests.cs rename tests/indicators/e-k/Ema/{Ema.Tests.cs => Ema.Static.Tests.cs} (75%) create mode 100644 tests/indicators/s-z/Sma/Sma.Obs.Tests.cs rename tests/indicators/s-z/Sma/{Sma.Tests.cs => Sma.Static.Tests.cs} (80%) create mode 100644 tests/observe/Observe.Streaming.csproj create mode 100644 tests/observe/Program.cs create mode 100644 tests/observe/README.md create mode 100644 tests/performance/Perf.Indicators.Static.cs create mode 100644 tests/performance/Perf.Indicators.Stream.cs delete mode 100644 tests/performance/Perf.Indicators.cs diff --git a/Stock.Indicators.sln b/Stock.Indicators.sln index 77aa0aea6..9ebe65bf0 100644 --- a/Stock.Indicators.sln +++ b/Stock.Indicators.sln @@ -21,9 +21,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Performance", "tests\ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{3A4158F9-4165-4823-9526-0CFAACCF1ACC}" ProjectSection(SolutionItems) = preProject - .lgtm.yml = .lgtm.yml .github\build.main.yml = .github\build.main.yml .github\dependabot.yml = .github\dependabot.yml + gitversion.yml = gitversion.yml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Observe.Streaming", "tests\observe\Observe.Streaming.csproj", "{14DEC3AF-9AF2-4A66-8BEE-C342C6CC4307}" + ProjectSection(ProjectDependencies) = postProject + {11CD6C7E-871F-4903-AEAD-58E034C6521D} = {11CD6C7E-871F-4903-AEAD-58E034C6521D} + {8D0F1781-EDA3-4C51-B05D-D33FF1156E49} = {8D0F1781-EDA3-4C51-B05D-D33FF1156E49} EndProjectSection EndProject Global @@ -48,6 +54,10 @@ Global {3BD4837B-D197-41FD-A286-A3256D0770E1}.Debug|Any CPU.Build.0 = Debug|Any CPU {3BD4837B-D197-41FD-A286-A3256D0770E1}.Release|Any CPU.ActiveCfg = Release|Any CPU {3BD4837B-D197-41FD-A286-A3256D0770E1}.Release|Any CPU.Build.0 = Release|Any CPU + {14DEC3AF-9AF2-4A66-8BEE-C342C6CC4307}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14DEC3AF-9AF2-4A66-8BEE-C342C6CC4307}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14DEC3AF-9AF2-4A66-8BEE-C342C6CC4307}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14DEC3AF-9AF2-4A66-8BEE-C342C6CC4307}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/GlobalSuppressions.cs b/src/GlobalSuppressions.cs index c085362d6..454263ce2 100644 --- a/src/GlobalSuppressions.cs +++ b/src/GlobalSuppressions.cs @@ -39,3 +39,23 @@ Justification = "Not compatible with `or` statement (Microsoft bug)", Scope = "member", Target = "~M:Skender.Stock.Indicators.ResultUtility.Condense``1(System.Collections.Generic.IEnumerable{``0})~System.Collections.Generic.IEnumerable{``0}")] + +[assembly: SuppressMessage( + "Naming", + "CA1725:Parameter names should match base declaration", + Justification = "The microsoft OnError implementation uses reserved word Error", + Scope = "member", + Target = "~M:Skender.Stock.Indicators.QuoteObserver.OnError(System.Exception)")] + +[assembly: SuppressMessage( + "Naming", + "CA1716:Identifiers should not match keywords", + Justification = "The microsoft OnError implementation uses reserved word Error", + Scope = "member", + Target = "~M:Skender.Stock.Indicators.QuoteObserver.OnError(System.Exception)")] + +[assembly: SuppressMessage( + "Naming", + "CA1716:Identifiers should not match keywords", + Justification = "The microsoft OnError implementation uses reserved word Error", + Scope = "member", Target = "~M:Skender.Stock.Indicators.TupleObserver.OnError(System.Exception)")] diff --git a/src/_common/Generics/Seek.cs b/src/_common/Generics/Seek.cs index c2fdd5b0a..0b0387b71 100644 --- a/src/_common/Generics/Seek.cs +++ b/src/_common/Generics/Seek.cs @@ -4,12 +4,22 @@ namespace Skender.Stock.Indicators; public static class Seeking { - // FIND by DATE - /// + // FIND SERIES by DATE + /// /// public static TSeries? Find( this IEnumerable series, DateTime lookupDate) where TSeries : ISeries => series .FirstOrDefault(x => x.Date == lookupDate); + + // FIND INDEX by DATE + /// + /// + public static int FindIndex( + this List series, + DateTime lookupDate) + where TSeries : ISeries => series == null + ? -1 + : series.FindIndex(x => x.Date == lookupDate); } diff --git a/src/_common/Generics/info.xml b/src/_common/Generics/info.xml index e50729fba..3d2e60d7a 100644 --- a/src/_common/Generics/info.xml +++ b/src/_common/Generics/info.xml @@ -1,7 +1,7 @@ - + Finds time series values on a specific date. See documentation for more information. @@ -10,8 +10,21 @@ Any series type. Time series to evaluate. Exact date to lookup. - First - record in the series on the date specified. + First record in the series on the date specified. + + + + Finds time series index on a specific date. + + See documentation for more information. + + + Any series type. + Time series to evaluate. + Exact date to lookup. + + First index in the series of the date specified or -1 if not found. + Removes a specific quantity from the beginning of the time series list. diff --git a/src/_common/Observables/ChainProvider.cs b/src/_common/Observables/ChainProvider.cs new file mode 100644 index 000000000..de73cfdbc --- /dev/null +++ b/src/_common/Observables/ChainProvider.cs @@ -0,0 +1,194 @@ +namespace Skender.Stock.Indicators; + +// TUPLE OBSERVER and TUPLE PROVIDER (CHAIN STREAM) + +public abstract class ChainProvider + : TupleObserver, IObservable<(DateTime Date, double Value)> +{ + // fields + private readonly List> observers; + + // initialize + protected ChainProvider() + { + observers = new(); + ProtectedChain = new(); + Warmup = true; + } + + // properties + internal IEnumerable<(DateTime Date, double Value)> Output => ProtectedChain; + + internal List<(DateTime Date, double Value)> ProtectedChain { get; set; } + + private int OverflowCount { get; set; } + + private bool Warmup { get; set; } + + // METHODS + + // subscribe observer + public IDisposable Subscribe(IObserver<(DateTime Date, double Value)> observer) + { + if (!observers.Contains(observer)) + { + observers.Add(observer); + } + + return new Unsubscriber(observers, observer); + } + + // close all observations + public void EndTransmission() + { + foreach (IObserver<(DateTime Date, double Value)> observer in observers.ToArray()) + { + if (observers.Contains(observer)) + { + observer.OnCompleted(); + } + } + + observers.Clear(); + } + + // add one + internal void SendToChain(TResult result) + where TResult : IReusableResult + { + // candidate result + (DateTime Date, double Value) r = new(result.Date, result.Value.Null2NaN()); + + int length = ProtectedChain.Count; + + // initialize + if (length == 0 && result.Value != null) + { + // add new tuple + ProtectedChain.Add(r); + Warmup = false; + + // notify observers + NotifyObservers(r); + return; + } + + // do not proceed until first non-null Value recieved + if (Warmup && result.Value == null) + { + return; + } + else + { + Warmup = false; + } + + (DateTime lastDate, _) = ProtectedChain[length - 1]; + + // add tuple + if (r.Date > lastDate) + { + // add new tuple + ProtectedChain.Add(r); + + // notify observers + NotifyObservers(r); + } + + // same date or tuple recieved + else if (r.Date <= lastDate) + { + // check for overflow condition + // where same tuple continues (possible circular condition) + if (r.Date == lastDate) + { + OverflowCount++; + + if (OverflowCount > 100) + { + string msg = "A repeated Chain update exceeded the 100 attempt threshold. " + + "Check and remove circular chains or check your Chain provider."; + + EndTransmission(); + + throw new OverflowException(msg); + } + } + else + { + OverflowCount = 0; + } + + // seek old tuple + int foundIndex = ProtectedChain + .FindIndex(x => x.Date == r.Date); + + // found + if (foundIndex >= 0) + { + ProtectedChain[foundIndex] = r; + } + + // add missing tuple + else + { + ProtectedChain.Add(r); + + // re-sort cache + ProtectedChain = ProtectedChain + .ToSortedList(); + } + + // let observer handle old + duplicates + NotifyObservers(r); + } + } + + // add many + internal void SendToChain(IEnumerable results) + where TResult : IReusableResult + { + List added = results + .ToSortedList(); + + for (int i = 0; i < added.Count; i++) + { + SendToChain(added[i]); + } + } + + // notify observers + private void NotifyObservers((DateTime Date, double Value) tuple) + { + List> obsList = observers.ToList(); + + for (int i = 0; i < obsList.Count; i++) + { + IObserver<(DateTime Date, double Value)> obs = obsList[i]; + obs.OnNext(tuple); + } + } + + // unsubscriber + private class Unsubscriber : IDisposable + { + private readonly List> observers; + private readonly IObserver<(DateTime Date, double Value)> observer; + + // identify and save observer + public Unsubscriber(List> observers, IObserver<(DateTime Date, double Value)> observer) + { + this.observers = observers; + this.observer = observer; + } + + // remove single observer + public void Dispose() + { + if (observer != null && observers.Contains(observer)) + { + observers.Remove(observer); + } + } + } +} diff --git a/src/_common/Observables/QuoteObserver.cs b/src/_common/Observables/QuoteObserver.cs new file mode 100644 index 000000000..628364594 --- /dev/null +++ b/src/_common/Observables/QuoteObserver.cs @@ -0,0 +1,29 @@ +namespace Skender.Stock.Indicators; + +// OBSERVER of QUOTES (BOILERPLATE) + +public abstract class QuoteObserver : IObserver +{ + // fields + private IDisposable? unsubscriber; + + // properites + internal QuoteProvider? Supplier { get; set; } + + // methods + public virtual void Subscribe() + => unsubscriber = Supplier != null + ? Supplier.Subscribe(this) + : throw new ArgumentNullException(nameof(Supplier)); + + public virtual void OnCompleted() => Unsubscribe(); + + public virtual void OnError(Exception error) => throw error; + + public virtual void OnNext(Quote value) + { + // » handle new quote with override in observer + } + + public virtual void Unsubscribe() => unsubscriber?.Dispose(); +} diff --git a/src/_common/Observables/QuoteProvider.cs b/src/_common/Observables/QuoteProvider.cs new file mode 100644 index 000000000..6f9d4c01c --- /dev/null +++ b/src/_common/Observables/QuoteProvider.cs @@ -0,0 +1,186 @@ +namespace Skender.Stock.Indicators; + +// QUOTES as PROVIDER + +public class QuoteProvider : IObservable +{ + // fields + private readonly List> observers; + + // initialize + public QuoteProvider() + { + observers = new(); + ProtectedQuotes = new(); + } + + // properties + public IEnumerable Quotes => ProtectedQuotes; + + internal List ProtectedQuotes { get; private set; } + + private int OverflowCount { get; set; } + + // METHODS + + // add one + public void Add(Quote quote) + { + // validate quote + if (quote == null) + { + throw new ArgumentNullException(nameof(quote), "Quote cannot be null."); + } + + int length = ProtectedQuotes.Count; + + if (length == 0) + { + // add new quote + ProtectedQuotes.Add(quote); + + // notify observers + NotifyObservers(quote); + + return; + } + + Quote last = ProtectedQuotes[length - 1]; + + // add quote + if (quote.Date > last.Date) + { + // add new quote + ProtectedQuotes.Add(quote); + + // notify observers + NotifyObservers(quote); + } + + // same date or quote recieved + else if (quote.Date <= last.Date) + { + // check for overflow condition + // where same quote continues (possible circular condition) + if (quote.Date == last.Date) + { + OverflowCount++; + + if (OverflowCount > 100) + { + string msg = "A repeated Quote update exceeded the 100 attempt threshold. " + + "Check and remove circular chains or check your Quote provider."; + + EndTransmission(); + + throw new OverflowException(msg); + } + } + else + { + OverflowCount = 0; + } + + // seek old quote + int foundIndex = ProtectedQuotes + .FindIndex(x => x.Date == quote.Date); + + // found + if (foundIndex >= 0) + { + Quote old = ProtectedQuotes[foundIndex]; + + old.Open = quote.Open; + old.High = quote.High; + old.Low = quote.Low; + old.Close = quote.Close; + old.Volume = quote.Volume; + } + + // add missing quote + else + { + ProtectedQuotes.Add(quote); + + // re-sort cache + ProtectedQuotes = ProtectedQuotes + .ToSortedList(); + } + + // let observer handle old + duplicates + NotifyObservers(quote); + } + } + + // add many + public void Add(IEnumerable quotes) + { + List added = quotes + .ToSortedList(); + + for (int i = 0; i < added.Count; i++) + { + Add(added[i]); + } + } + + // subscribe observer + public IDisposable Subscribe(IObserver observer) + { + if (!observers.Contains(observer)) + { + observers.Add(observer); + } + + return new Unsubscriber(observers, observer); + } + + // close all observations + public void EndTransmission() + { + foreach (IObserver observer in observers.ToArray()) + { + if (observers.Contains(observer)) + { + observer.OnCompleted(); + } + } + + observers.Clear(); + } + + // notify observers + private void NotifyObservers(Quote quote) + { + List> obsList = observers.ToList(); + + for (int i = 0; i < obsList.Count; i++) + { + IObserver obs = obsList[i]; + obs.OnNext(quote); + } + } + + // unsubscriber + private class Unsubscriber : IDisposable + { + private readonly List> observers; + private readonly IObserver observer; + + // identify and save observer + public Unsubscriber(List> observers, IObserver observer) + { + this.observers = observers; + this.observer = observer; + } + + // remove single observer + public void Dispose() + { + if (observer != null && observers.Contains(observer)) + { + observers.Remove(observer); + } + } + } +} diff --git a/src/_common/Observables/TupleObserver.cs b/src/_common/Observables/TupleObserver.cs new file mode 100644 index 000000000..ae6809f85 --- /dev/null +++ b/src/_common/Observables/TupleObserver.cs @@ -0,0 +1,29 @@ +namespace Skender.Stock.Indicators; + +// OBSERVER of TUPLES (BOILERPLATE) + +public abstract class TupleObserver : IObserver<(DateTime Date, double Value)> +{ + // fields + private IDisposable? unsubscriber; + + // properites + internal TupleProvider? Supplier { get; set; } + + // methods + public virtual void Subscribe() + => unsubscriber = Supplier != null + ? Supplier.Subscribe(this) + : throw new ArgumentNullException(nameof(Supplier)); + + public virtual void OnCompleted() => Unsubscribe(); + + public virtual void OnError(Exception error) => throw error; + + public virtual void OnNext((DateTime Date, double Value) value) + { + // » handle new quote with override in observer + } + + public virtual void Unsubscribe() => unsubscriber?.Dispose(); +} diff --git a/src/_common/Observables/TupleProvider.cs b/src/_common/Observables/TupleProvider.cs new file mode 100644 index 000000000..38edc7ab7 --- /dev/null +++ b/src/_common/Observables/TupleProvider.cs @@ -0,0 +1,174 @@ +namespace Skender.Stock.Indicators; + +// QUOTE OBSERVER and TUPLE PROVIDER + +public abstract class TupleProvider + : QuoteObserver, IObservable<(DateTime Date, double Value)> +{ + // fields + private readonly List> observers; + + // initialize + protected TupleProvider() + { + observers = new(); + ProtectedTuples = new(); + } + + // properties + internal IEnumerable<(DateTime Date, double Value)> Output => ProtectedTuples; + + internal List<(DateTime Date, double Value)> ProtectedTuples { get; set; } + + private int OverflowCount { get; set; } + + // METHODS + + // subscribe observer + public IDisposable Subscribe(IObserver<(DateTime Date, double Value)> observer) + { + if (!observers.Contains(observer)) + { + observers.Add(observer); + } + + return new Unsubscriber(observers, observer); + } + + // close all observations + public void EndTransmission() + { + foreach (IObserver<(DateTime Date, double Value)> observer in observers.ToArray()) + { + if (observers.Contains(observer)) + { + observer.OnCompleted(); + } + } + + observers.Clear(); + } + + // add one + internal void AddSend((DateTime Date, double Value) tuple) + { + int length = ProtectedTuples.Count; + + if (length == 0) + { + // add new tuple + ProtectedTuples.Add(tuple); + + // notify observers + NotifyObservers(tuple); + return; + } + + (DateTime lastDate, _) = ProtectedTuples[length - 1]; + + // add tuple + if (tuple.Date > lastDate) + { + // add new tuple + ProtectedTuples.Add(tuple); + + // notify observers + NotifyObservers(tuple); + } + + // same date or tuple recieved + else if (tuple.Date <= lastDate) + { + // check for overflow condition + // where same tuple continues (possible circular condition) + if (tuple.Date == lastDate) + { + OverflowCount++; + + if (OverflowCount > 100) + { + string msg = "A repeated Tuple update exceeded the 100 attempt threshold. " + + "Check and remove circular chains or check your Tuple provider."; + + EndTransmission(); + + throw new OverflowException(msg); + } + } + else + { + OverflowCount = 0; + } + + // seek old tuple + int foundIndex = ProtectedTuples + .FindIndex(x => x.Date == tuple.Date); + + // found + if (foundIndex >= 0) + { + ProtectedTuples[foundIndex] = tuple; + } + + // add missing tuple + else + { + ProtectedTuples.Add(tuple); + + // re-sort cache + ProtectedTuples = ProtectedTuples + .ToSortedList(); + } + + // let observer handle old + duplicates + NotifyObservers(tuple); + } + } + + // add many + internal void AddSend(IEnumerable<(DateTime Date, double Value)> tuples) + { + List<(DateTime Date, double Value)> added = tuples + .ToSortedList(); + + for (int i = 0; i < added.Count; i++) + { + AddSend(added[i]); + } + } + + // notify observers + private void NotifyObservers((DateTime Date, double Value) tuple) + { + List> obsList = observers.ToList(); + + for (int i = 0; i < obsList.Count; i++) + { + IObserver<(DateTime Date, double Value)> obs = obsList[i]; + obs.OnNext(tuple); + } + } + + // unsubscriber + private class Unsubscriber : IDisposable + { + private readonly List> observers; + private readonly IObserver<(DateTime Date, double Value)> observer; + + // identify and save observer + public Unsubscriber(List> observers, IObserver<(DateTime Date, double Value)> observer) + { + this.observers = observers; + this.observer = observer; + } + + // remove single observer + public void Dispose() + { + if (observer != null && observers.Contains(observer)) + { + observers.Remove(observer); + } + } + } +} diff --git a/src/_common/Quotes/Quote.Converters.cs b/src/_common/Quotes/Quote.Converters.cs index b73c22b9b..07f45c7bf 100644 --- a/src/_common/Quotes/Quote.Converters.cs +++ b/src/_common/Quotes/Quote.Converters.cs @@ -9,17 +9,6 @@ public static partial class QuoteUtility { private static readonly CultureInfo NativeCulture = Thread.CurrentThread.CurrentUICulture; - /* STANDARD DECIMAL QUOTES */ - - // convert TQuotes to basic double tuple list - /// - /// - public static IEnumerable<(DateTime Date, double Value)> Use( - this IEnumerable quotes, - CandlePart candlePart = CandlePart.Close) - where TQuote : IQuote => quotes - .Select(x => x.ToTuple(candlePart)); - // TUPLE QUOTES // convert quotes to tuple list diff --git a/src/_common/Quotes/Use.Api.cs b/src/_common/Quotes/Use.Api.cs new file mode 100644 index 000000000..f9a3f2a45 --- /dev/null +++ b/src/_common/Quotes/Use.Api.cs @@ -0,0 +1,21 @@ +namespace Skender.Stock.Indicators; + +// USE (API) + +public static partial class QuoteUtility +{ + // convert TQuotes to basic double tuple list + /// + /// + public static IEnumerable<(DateTime Date, double Value)> Use( + this IEnumerable quotes, + CandlePart candlePart = CandlePart.Close) + where TQuote : IQuote => quotes + .Select(x => x.ToTuple(candlePart)); + + // OBSERVER, from Quote Provider + public static UseObserver Use( + this QuoteProvider provider, + CandlePart candlePart = CandlePart.Close) + => new(provider, candlePart); +} diff --git a/src/_common/Quotes/Use.Observer.cs b/src/_common/Quotes/Use.Observer.cs new file mode 100644 index 000000000..935b27753 --- /dev/null +++ b/src/_common/Quotes/Use.Observer.cs @@ -0,0 +1,76 @@ +namespace Skender.Stock.Indicators; + +// USE (STREAMING) +public class UseObserver : TupleProvider +{ + public UseObserver( + QuoteProvider? provider, + CandlePart candlePart) + { + Supplier = provider; + CandlePartSelection = candlePart; + Initialize(); + } + + // PROPERTIES + + public IEnumerable<(DateTime Date, double Value)> Results => ProtectedTuples; + + private CandlePart CandlePartSelection { get; set; } + + // NON-STATIC METHODS + + // handle quote arrival + public override void OnNext(Quote value) => HandleArrival(value); + + // add new quote + internal void HandleArrival(Quote quote) + { + // candidate result + (DateTime date, double value) r = quote.ToTuple(CandlePartSelection); + + // initialize + int length = ProtectedTuples.Count; + + if (length == 0) + { + AddSend(r); + return; + } + + // check against last entry + (DateTime lastDate, _) = ProtectedTuples[length - 1]; + + // add new + if (r.date > lastDate) + { + AddSend(r); + } + + // update last + else if (r.date == lastDate) + { + ProtectedTuples[length - 1] = r; + } + + // late arrival + else + { + AddSend(r); + throw new NotImplementedException(); + } + } + + // calculate initial cache of quotes + private void Initialize() + { + if (Supplier != null) + { + ProtectedTuples = Supplier + .ProtectedQuotes + .ToTuple(CandlePartSelection); + } + + Subscribe(); + } +} diff --git a/src/_common/Results/Result.Utilities.cs b/src/_common/Results/Result.Utilities.cs index 06fd4e49c..a7a45748c 100644 --- a/src/_common/Results/Result.Utilities.cs +++ b/src/_common/Results/Result.Utilities.cs @@ -26,17 +26,19 @@ public static IEnumerable Condense( // CONVERT TO TUPLE (default with pruning) /// /// - public static Collection<(DateTime Date, double Value)> ToTupleChainable( - this IEnumerable reusable) + public static Collection<(DateTime Date, double Value)> ToTupleChainable( + this IEnumerable reusable) + where TResult : IReusableResult => reusable .ToTuple() .ToCollection(); - internal static List<(DateTime Date, double Value)> ToTuple( - this IEnumerable reusable) + internal static List<(DateTime Date, double Value)> ToTuple( + this IEnumerable reusable) + where TResult : IReusableResult { List<(DateTime date, double value)> prices = new(); - List reList = reusable.ToList(); + List reList = reusable.ToList(); // find first non-nulled int first = reList.FindIndex(x => x.Value != null); @@ -53,10 +55,11 @@ public static IEnumerable Condense( // CONVERT TO TUPLE with non-nullable NaN value option and no pruning /// /// - public static Collection<(DateTime Date, double Value)> ToTupleNaN( - this IEnumerable reusable) + public static Collection<(DateTime Date, double Value)> ToTupleNaN( + this IEnumerable reusable) + where TResult : IReusableResult { - List reList = reusable.ToSortedList(); + List reList = reusable.ToSortedList(); int length = reList.Count; Collection<(DateTime Date, double Value)> results = new(); diff --git a/src/_common/Results/info.xml b/src/_common/Results/info.xml index 38ca78960..e78cd58a0 100644 --- a/src/_common/Results/info.xml +++ b/src/_common/Results/info.xml @@ -2,11 +2,13 @@ - Converts results into a reusable tuple with warmup periods removed and nulls converted - to NaN. See Converts results into a reusable tuple with warmup periods removed and nulls converted to NaN. + See - documentation for more information. + documentation for more information. + + Any reusable result type. Indicator results to evaluate. Collection of non-nullable tuple time series of results, without null warmup periods. @@ -16,6 +18,7 @@ href="https://dotnet.StockIndicators.dev/utilities/#using-tuple-results?utm_source=library&utm_medium=inline-help&utm_campaign=embedded"> documentation for more information. + Any reusable result type. Indicator results to evaluate. Collection of tuple time series of results with specified handline of nulls, without pruning. @@ -46,8 +49,7 @@ href="https://dotnet.StockIndicators.dev/utilities/#condense?utm_source=library&utm_medium=inline-help&utm_campaign=embedded"> documentation for more information. - Any result - type. + Any result type. Indicator results to evaluate. Time series of indicator results, condensed. diff --git a/src/e-k/Ema/Ema.Api.cs b/src/e-k/Ema/Ema.Api.cs index e57464f92..765314080 100644 --- a/src/e-k/Ema/Ema.Api.cs +++ b/src/e-k/Ema/Ema.Api.cs @@ -1,6 +1,7 @@ namespace Skender.Stock.Indicators; // EXPONENTIAL MOVING AVERAGE (API) + public static partial class Indicator { // SERIES, from TQuote @@ -28,30 +29,24 @@ public static IEnumerable GetEma( .ToSortedList() .CalcEma(lookbackPeriods); - // STREAM INITIALIZATION, from TQuote - /// + // OBSERVER, from Quote Provider + /// /// - internal static EmaBase InitEma( - this IEnumerable quotes, + public static EmaObserver GetEma( + this QuoteProvider provider, int lookbackPeriods) - where TQuote : IQuote { - // convert quotes - List<(DateTime, double)> tpList - = quotes.ToTuple(CandlePart.Close); + UseObserver useObserver = provider + .Use(CandlePart.Close); - return new EmaBase(tpList, lookbackPeriods); + return new(useObserver, lookbackPeriods); } - // STREAM INITIALIZATION, from CHAIN - internal static EmaBase InitEma( - this IEnumerable results, + // OBSERVER, from Chain Provider + /// + /// + public static EmaObserver GetEma( + this TupleProvider tupleProvider, int lookbackPeriods) - { - // convert results - List<(DateTime, double)> tpList - = results.ToTuple(); - - return new EmaBase(tpList, lookbackPeriods); - } + => new(tupleProvider, lookbackPeriods); } diff --git a/src/e-k/Ema/Ema.Observer.cs b/src/e-k/Ema/Ema.Observer.cs new file mode 100644 index 000000000..0ad2e7ba3 --- /dev/null +++ b/src/e-k/Ema/Ema.Observer.cs @@ -0,0 +1,144 @@ +namespace Skender.Stock.Indicators; + +// EXPONENTIAL MOVING AVERAGE (STREAMING) + +public class EmaObserver : ChainProvider +{ + public EmaObserver( + TupleProvider provider, + int lookbackPeriods) + { + Supplier = provider; + ProtectedResults = new(); + + LookbackPeriods = lookbackPeriods; + K = 2d / (lookbackPeriods + 1); + + Initialize(); + } + + // PROPERTIES + + public IEnumerable Results => ProtectedResults; + internal List ProtectedResults { get; set; } + + private double WarmupValue { get; set; } + private int LookbackPeriods { get; set; } + private double K { get; set; } + + // STATIC METHODS + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for EMA."); + } + } + + // incremental calculation + internal static double Increment(double newValue, double lastEma, double k) + => lastEma + (k * (newValue - lastEma)); + + // NON-STATIC METHODS + + // handle quote arrival + public override void OnNext((DateTime Date, double Value) value) => Add(value); + + // add new tuple quote + internal void Add((DateTime Date, double Value) tuple) + { + // candidate result (empty) + EmaResult r = new(tuple.Date); + + // initialize + int length = ProtectedResults.Count; + + if (length == 0) + { + ProtectedResults.Add(r); + WarmupValue += tuple.Value; + SendToChain(r); + return; + } + + // check against last entry + EmaResult last = ProtectedResults[length - 1]; + + // initialization periods + if (length < LookbackPeriods - 1) + { + // add if not duplicate + if (last.Date != r.Date) + { + ProtectedResults.Add(r); + WarmupValue += tuple.Value; + } + + return; + } + + // initialize with SMA + if (length == LookbackPeriods - 1) + { + WarmupValue += tuple.Value; + r.Ema = (WarmupValue / LookbackPeriods).NaN2Null(); + ProtectedResults.Add(r); + SendToChain(r); + return; + } + + // add new + if (r.Date > last.Date) + { + // calculate incremental value + double lastEma = (last.Ema == null) ? double.NaN : (double)last.Ema; + double newEma = Increment(tuple.Value, lastEma, K); + + r.Ema = newEma.NaN2Null(); + ProtectedResults.Add(r); + SendToChain(r); + return; + } + + // update last + else if (r.Date == last.Date) + { + // get prior last EMA + EmaResult prior = ProtectedResults[length - 2]; + + double priorEma = (prior.Ema == null) ? double.NaN : (double)prior.Ema; + last.Ema = Increment(tuple.Value, priorEma, K); + SendToChain(last); + return; + } + + // late arrival + else + { + // heal + throw new NotImplementedException(); + } + } + + // calculate with provider cache + private void Initialize() + { + if (Supplier != null) + { + List<(DateTime, double)> tuples = Supplier + .ProtectedTuples; + + for (int i = 0; i < tuples.Count; i++) + { + Add(tuples[i]); + } + + Subscribe(); + } + } +} diff --git a/src/e-k/Ema/Ema.Series.cs b/src/e-k/Ema/Ema.Series.cs index b54f3e79e..77952750d 100644 --- a/src/e-k/Ema/Ema.Series.cs +++ b/src/e-k/Ema/Ema.Series.cs @@ -8,7 +8,7 @@ internal static List CalcEma( int lookbackPeriods) { // check parameter arguments - EmaBase.Validate(lookbackPeriods); + EmaObserver.Validate(lookbackPeriods); // initialize int length = tpList.Count; @@ -35,7 +35,7 @@ internal static List CalcEma( if (i + 1 > lookbackPeriods) { - double ema = EmaBase.Increment(value, lastEma, k); + double ema = EmaObserver.Increment(value, lastEma, k); r.Ema = ema.NaN2Null(); lastEma = ema; } diff --git a/src/e-k/Ema/Ema.Stream.cs b/src/e-k/Ema/Ema.Stream.cs deleted file mode 100644 index e5472fd16..000000000 --- a/src/e-k/Ema/Ema.Stream.cs +++ /dev/null @@ -1,82 +0,0 @@ -namespace Skender.Stock.Indicators; - -// EXPONENTIAL MOVING AVERAGE (STREAMING) -internal class EmaBase -{ - // initialize base - internal EmaBase(IEnumerable<(DateTime, double)> tpQuotes, int lookbackPeriods) - { - K = 2d / (lookbackPeriods + 1); - - ProtectedResults = tpQuotes - .ToSortedList() - .CalcEma(lookbackPeriods); - } - - // properties - internal double K { get; set; } - internal List ProtectedResults { get; set; } - - public IEnumerable Results => ProtectedResults; - - // methods - public IEnumerable Add( - Quote quote, - CandlePart candlePart = CandlePart.Close) - { - if (quote == null) - { - throw new InvalidQuotesException(nameof(quote), quote, "No quote provided."); - } - - (DateTime Date, double Value) tuple = quote.ToTuple(candlePart); - return Add(tuple); - } - - public IEnumerable Add( - (DateTime Date, double Value) tuple) - { - // get last value - int lastIndex = ProtectedResults.Count - 1; - EmaResult last = ProtectedResults[lastIndex]; - - // update bar - if (tuple.Date == last.Date) - { - // get prior last EMA - EmaResult prior = ProtectedResults[lastIndex - 1]; - - double priorEma = (prior.Ema == null) ? double.NaN : (double)prior.Ema; - last.Ema = Increment(tuple.Value, priorEma, K); - } - - // add new bar - else if (tuple.Date > last.Date) - { - // calculate incremental value - double lastEma = (last.Ema == null) ? double.NaN : (double)last.Ema; - double newEma = Increment(tuple.Value, lastEma, K); - - EmaResult r = new(tuple.Date) { Ema = newEma }; - ProtectedResults.Add(r); - } - - return Results; - } - - // incremental calculation - internal static double Increment(double newValue, double lastEma, double k) - => lastEma + (k * (newValue - lastEma)); - - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for EMA."); - } - } -} diff --git a/src/e-k/Ema/info.xml b/src/e-k/Ema/info.xml index a9e87b466..5e3e3be73 100644 --- a/src/e-k/Ema/info.xml +++ b/src/e-k/Ema/info.xml @@ -16,19 +16,32 @@ Time series of EMA values. Invalid parameter value provided. - + - Extablish a streaming base for Exponential Moving Average (EMA). + Establish an observable streaming Exponential Moving Average (EMA). See documentation for more information. - Configurable Quote type. See Guide for more information. - Historical price quotes. + Observable quote provider. + Number of periods in the lookback window. + Observable EMA instance. + Invalid parameter value provided. + + + + Chain from an observable streaming Exponential Moving Average (EMA). + + See + documentation + for more information. + + + Observable from chained indicator. Number of periods in the lookback window. - EMA base that you can add Quotes to with the .Add(quote) method. + Observable EMA instance. Invalid parameter value provided. \ No newline at end of file diff --git a/src/s-z/Sma/Sma.Api.cs b/src/s-z/Sma/Sma.Api.cs index c0f3d40b5..5e21293dd 100644 --- a/src/s-z/Sma/Sma.Api.cs +++ b/src/s-z/Sma/Sma.Api.cs @@ -1,6 +1,7 @@ namespace Skender.Stock.Indicators; // SIMPLE MOVING AVERAGE (API) + public static partial class Indicator { // SERIES, from TQuote @@ -28,6 +29,27 @@ public static IEnumerable GetSma( .ToSortedList() .CalcSma(lookbackPeriods); + // OBSERVER, from Quote Provider + /// + /// + public static SmaObserver GetSma( + this QuoteProvider provider, + int lookbackPeriods) + { + UseObserver useObserver = provider + .Use(CandlePart.Close); + + return new(useObserver, lookbackPeriods); + } + + // OBSERVER, from Chain Provider + /// + /// + public static SmaObserver GetSma( + this TupleProvider tupleProvider, + int lookbackPeriods) + => new(tupleProvider, lookbackPeriods); + /// /// // ANALYSIS, from TQuote diff --git a/src/s-z/Sma/Sma.Observer.cs b/src/s-z/Sma/Sma.Observer.cs new file mode 100644 index 000000000..cc496dd92 --- /dev/null +++ b/src/s-z/Sma/Sma.Observer.cs @@ -0,0 +1,144 @@ +namespace Skender.Stock.Indicators; + +// SIMPLE MOVING AVERAGE (STREAMING) + +public class SmaObserver : ChainProvider +{ + public SmaObserver( + TupleProvider provider, + int lookbackPeriods) + { + Supplier = provider; + ProtectedResults = new(); + + LookbackPeriods = lookbackPeriods; + + Initialize(); + } + + // PROPERTIES + + public IEnumerable Results => ProtectedResults; + internal List ProtectedResults { get; set; } + + private int LookbackPeriods { get; set; } + + // STATIC METHODS + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for SMA."); + } + } + + // incremental calculation + internal static double Increment( + List<(DateTime Date, double Value)> values, + int index, + int lookbackPeriods) + { + if (index < lookbackPeriods - 1) + { + return double.NaN; + } + + double sum = 0; + for (int i = index - lookbackPeriods + 1; i <= index; i++) + { + sum += values[i].Value; + } + + return sum / lookbackPeriods; + } + + // NON-STATIC METHODS + + // handle quote arrival + public override void OnNext((DateTime Date, double Value) value) => Add(value); + + // add new tuple quote + internal void Add((DateTime Date, double Value) tp) + { + if (Supplier == null) + { + throw new ArgumentNullException(nameof(Supplier), "Could not find data source."); + } + + // candidate result + SmaResult r = new(tp.Date); + + // initialize + int lengthRes = ProtectedResults.Count; + int lengthSrc = Supplier.ProtectedTuples.Count; + + // handle first value + if (lengthRes == 0) + { + ProtectedResults.Add(r); + SendToChain(r); + return; + } + + SmaResult lastResult = ProtectedResults[lengthRes - 1]; + (DateTime lastSrcDate, double _) = Supplier.ProtectedTuples[lengthSrc - 1]; + + if (r.Date == lastSrcDate) + { + r.Sma = Increment( + Supplier.ProtectedTuples, + lengthSrc - 1, + LookbackPeriods) + .NaN2Null(); + } + + // add new + if (r.Date > lastResult.Date) + { + ProtectedResults.Add(r); + SendToChain(r); + } + + // update last + else if (r.Date == lastResult.Date) + { + lastResult.Sma = r.Sma; + SendToChain(lastResult); + } + + // late arrival + else + { + // heal + throw new NotImplementedException(); + + // existing and index in sync? + + // new and index otherwise in sync? + + // all other scenarios: unsubscribe from provider and end transmission to others? + } + } + + // calculate with provider cache + private void Initialize() + { + if (Supplier != null) + { + List<(DateTime, double)> tuples = Supplier + .ProtectedTuples; + + for (int i = 0; i < tuples.Count; i++) + { + Add(tuples[i]); + } + + Subscribe(); + } + } +} diff --git a/src/s-z/Sma/Sma.Series.cs b/src/s-z/Sma/Sma.Series.cs index 0662b9e5d..2541de516 100644 --- a/src/s-z/Sma/Sma.Series.cs +++ b/src/s-z/Sma/Sma.Series.cs @@ -8,7 +8,7 @@ internal static List CalcSma( int lookbackPeriods) { // check parameter arguments - ValidateSma(lookbackPeriods); + SmaObserver.Validate(lookbackPeriods); // initialize List results = new(tpList.Count); @@ -21,31 +21,11 @@ internal static List CalcSma( SmaResult result = new(date); results.Add(result); - if (i + 1 >= lookbackPeriods) - { - double sumSma = 0; - for (int p = i + 1 - lookbackPeriods; p <= i; p++) - { - (DateTime _, double pValue) = tpList[p]; - sumSma += pValue; - } - - result.Sma = (sumSma / lookbackPeriods).NaN2Null(); - } + result.Sma = SmaObserver + .Increment(tpList, i, lookbackPeriods) + .NaN2Null(); } return results; } - - // parameter validation - private static void ValidateSma( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for SMA."); - } - } } diff --git a/src/s-z/Sma/info.xml b/src/s-z/Sma/info.xml index fb6d7271a..386418d41 100644 --- a/src/s-z/Sma/info.xml +++ b/src/s-z/Sma/info.xml @@ -17,7 +17,34 @@ Time series of SMA values. Invalid parameter value provided. - + + + Establish an observable streaming Exponential Moving Average (EMA). + + See + documentation + for more information. + + + Observable quote provider. + Number of periods in the lookback window. + Observable EMA instance. + Invalid parameter value provided. + + + + Chain from an observable streaming Simple Moving Average (SMA). + + See + documentation + for more information. + + + Observable from chained indicator. + Number of periods in the lookback window. + Observable SMA instance. + Invalid parameter value provided. + Simple Moving Average (SMA) is the average of price over a lookback window. This extended variant includes mean absolute deviation (MAD), mean square error (MSE), and mean absolute percentage error (MAPE). diff --git a/tests/indicators/_Initialize.cs b/tests/indicators/_Initialize.cs index d8e149490..13531ce50 100644 --- a/tests/indicators/_Initialize.cs +++ b/tests/indicators/_Initialize.cs @@ -8,6 +8,7 @@ [assembly: CLSCompliant(true)] [assembly: InternalsVisibleTo("Tests.Other")] [assembly: InternalsVisibleTo("Tests.Performance")] +[assembly: InternalsVisibleTo("Observe.Streaming")] namespace Tests.Common; [TestClass] diff --git a/tests/indicators/_common/Generics/Seek.Tests.cs b/tests/indicators/_common/Generics/Seek.Tests.cs index f2b16fabd..5c0b5e7f1 100644 --- a/tests/indicators/_common/Generics/Seek.Tests.cs +++ b/tests/indicators/_common/Generics/Seek.Tests.cs @@ -7,7 +7,7 @@ namespace Tests.Common; public class Seeking : TestBase { [TestMethod] - public void Find() + public void FindSeries() { IEnumerable quotes = TestData.GetDefault(); IEnumerable emaResults = quotes.GetEma(20); @@ -18,4 +18,31 @@ public void Find() EmaResult r = emaResults.Find(findDate); Assert.AreEqual(249.3519, r.Ema.Round(4)); } + + [TestMethod] + public void FindSeriesNone() + { + IEnumerable quotes = TestData.GetDefault(); + IEnumerable emaResults = quotes.GetEma(20); + + // find specific date + DateTime findDate = DateTime.ParseExact("1928-10-29", "yyyy-MM-dd", EnglishCulture); + + EmaResult r = emaResults.Find(findDate); + Assert.IsNull(r); + } + + [TestMethod] + public void FindSeriesIndex() + { + List quotes = TestData + .GetDefault() + .ToSortedList(); + + // find specific date + DateTime findDate = DateTime.ParseExact("2018-12-31", "yyyy-MM-dd", EnglishCulture); + + int i = quotes.FindIndex(findDate); + Assert.AreEqual(501, i); + } } diff --git a/tests/indicators/_common/Helper.Random.cs b/tests/indicators/_common/Helper.Random.cs index e2ef4cbe1..d2d08b533 100644 --- a/tests/indicators/_common/Helper.Random.cs +++ b/tests/indicators/_common/Helper.Random.cs @@ -27,15 +27,15 @@ internal class RandomGbm : List public RandomGbm( int bars = 250, double volatility = 1.0, - double drift = 0.05, - double seed = 10000000.0) + double drift = 0.01, + double seed = 1000.0) { this.seed = seed; this.volatility = volatility * 0.01; - this.drift = drift * 0.01; + this.drift = drift * 0.001; for (int i = 0; i < bars; i++) { - DateTime date = DateTime.Today.AddDays(i - bars); + DateTime date = DateTime.Today.AddMinutes(i - bars); Add(date); } } diff --git a/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs b/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs index 34b23a1ed..281b4ca17 100644 --- a/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs @@ -4,7 +4,7 @@ namespace Tests.Common; [TestClass] -public class QuoteHistory : TestBase +public class QuoteAggregateTests : TestBase { [TestMethod] public void Aggregate() diff --git a/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs b/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs index c7ee494e0..37f7db72f 100644 --- a/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs @@ -5,7 +5,7 @@ namespace Tests.Common; [TestClass] -public class QuoteUtility : TestBase +public class QuoteUtilityTests : TestBase { [TestMethod] public void QuoteToSortedCollection() diff --git a/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs b/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs index 35d088693..7aa0abece 100644 --- a/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs @@ -4,7 +4,7 @@ namespace Tests.Common; [TestClass] -public class QuoteValidation : TestBase +public class QuoteValidationTests : TestBase { [TestMethod] public void Validate() diff --git a/tests/indicators/_common/Quotes/Test.Quote.Provider.cs b/tests/indicators/_common/Quotes/Test.Quote.Provider.cs new file mode 100644 index 000000000..64d0a8a4d --- /dev/null +++ b/tests/indicators/_common/Quotes/Test.Quote.Provider.cs @@ -0,0 +1,61 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Skender.Stock.Indicators; + +namespace Tests.Common; + +[TestClass] +public class QuoteSourceTests : TestBase +{ + [TestMethod] + public void Standard() + { + List quotesList = quotes + .ToSortedList(); + + int length = quotes.Count(); + + // add base quotes + QuoteProvider provider = new(); + provider.Add(quotesList.Take(200)); + + // emulate incremental quotes + for (int i = 200; i < length; i++) + { + Quote q = quotesList[i]; + provider.Add(q); + } + + // assert same as original + for (int i = 0; i < length; i++) + { + Quote o = quotesList[i]; + Quote q = provider.ProtectedQuotes[i]; + + Assert.AreEqual(o, q); + } + + provider.EndTransmission(); + } + + [TestMethod] + public void Exceptions() + { + // null quote added + QuoteProvider provider = new(); + + Assert.ThrowsException(() => + { + Quote quote = new() + { + Date = DateTime.Now + }; + + for (int i = 0; i <= 101; i++) + { + provider.Add(quote); + } + }); + + provider.EndTransmission(); + } +} diff --git a/tests/indicators/_common/Quotes/Test.Use.Observer.cs b/tests/indicators/_common/Quotes/Test.Use.Observer.cs new file mode 100644 index 000000000..bf52f9217 --- /dev/null +++ b/tests/indicators/_common/Quotes/Test.Use.Observer.cs @@ -0,0 +1,99 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Skender.Stock.Indicators; +using Tests.Common; + +namespace Tests.Indicators; + +[TestClass] +public class UseStreamTests : TestBase +{ + [TestMethod] + public void Standard() + { + List quotesList = quotes + .ToSortedList(); + + int length = quotesList.Count; + + // time-series, for comparison + List<(DateTime Date, double Value)> seriesList = quotes + .ToTuple(CandlePart.Close); + + // setup quote provider + QuoteProvider provider = new(); + + // initialize EMA observer + UseObserver observer = provider + .Use(CandlePart.Close); + + // fetch initial results + IEnumerable<(DateTime Date, double Value)> results + = observer.Results; + + // emulate adding quotes to provider + for (int i = 0; i < length; i++) + { + Quote q = quotesList[i]; + provider.Add(q); + } + + // final results + List<(DateTime Date, double Value)> resultsList + = results.ToList(); + + // assert, should equal series + for (int i = 0; i < seriesList.Count; i++) + { + (DateTime sDate, double sValue) = seriesList[i]; + (DateTime rDate, double rValue) = resultsList[i]; + + Assert.AreEqual(sDate, rDate); + Assert.AreEqual(sValue, rValue); + } + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public void Chainor() + { + List quotesList = quotes + .ToSortedList(); + + int length = quotesList.Count; + + // time-series, for comparison + List staticEma = quotes + .Use(CandlePart.HL2) + .GetEma(11) + .ToList(); + + // setup quote provider + QuoteProvider provider = new(); + + // initialize EMA observer + List streamEma = provider + .Use(CandlePart.HL2) + .GetEma(11) + .ProtectedResults; + + // emulate adding quotes to provider + for (int i = 0; i < length; i++) + { + provider.Add(quotesList[i]); + } + + provider.EndTransmission(); + + // assert, should equal series + for (int i = 0; i < length; i++) + { + EmaResult t = staticEma[i]; + EmaResult s = streamEma[i]; + + Assert.AreEqual(t.Date, s.Date); + Assert.AreEqual(t.Ema, s.Ema); + } + } +} diff --git a/tests/indicators/e-k/Ema/Ema.Obs.Tests.cs b/tests/indicators/e-k/Ema/Ema.Obs.Tests.cs new file mode 100644 index 000000000..a62556b1e --- /dev/null +++ b/tests/indicators/e-k/Ema/Ema.Obs.Tests.cs @@ -0,0 +1,100 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Skender.Stock.Indicators; +using Tests.Common; + +namespace Tests.Indicators; + +[TestClass] +public class EmaStreamTests : TestBase +{ + [TestMethod] + public void Standard() + { + List quotesList = quotes + .ToSortedList(); + + int length = quotesList.Count; + + // time-series, for comparison + List seriesList = quotes + .GetEma(20) + .ToList(); + + // setup quote provider + QuoteProvider provider = new(); + + // initialize EMA observer + EmaObserver observer = provider + .GetEma(20); + + // fetch initial results + IEnumerable results + = observer.Results; + + // emulate adding quotes to provider + for (int i = 0; i < length; i++) + { + Quote q = quotesList[i]; + provider.Add(q); + } + + // final results + List resultsList + = results.ToList(); + + // assert, should equal series + for (int i = 0; i < seriesList.Count; i++) + { + EmaResult s = seriesList[i]; + EmaResult r = resultsList[i]; + + Assert.AreEqual(s.Date, r.Date); + Assert.AreEqual(s.Ema, r.Ema); + } + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public void Usee() + { + List quotesList = quotes + .ToSortedList(); + + int length = quotesList.Count; + + // time-series, for comparison + List staticEma = quotes + .Use(CandlePart.OC2) + .GetEma(11) + .ToList(); + + // setup quote provider + QuoteProvider provider = new(); + + // initialize EMA observer + List streamEma = provider + .Use(CandlePart.OC2) + .GetEma(11) + .ProtectedResults; + + // emulate adding quotes to provider + for (int i = 0; i < length; i++) + { + provider.Add(quotesList[i]); + } + + provider.EndTransmission(); + + // assert, should equal series + for (int i = 0; i < length; i++) + { + EmaResult t = staticEma[i]; + EmaResult s = streamEma[i]; + + Assert.AreEqual(t.Date, s.Date); + Assert.AreEqual(t.Ema, s.Ema); + } + } +} diff --git a/tests/indicators/e-k/Ema/Ema.Tests.cs b/tests/indicators/e-k/Ema/Ema.Static.Tests.cs similarity index 75% rename from tests/indicators/e-k/Ema/Ema.Tests.cs rename to tests/indicators/e-k/Ema/Ema.Static.Tests.cs index 41c67e1cc..bdc4e628e 100644 --- a/tests/indicators/e-k/Ema/Ema.Tests.cs +++ b/tests/indicators/e-k/Ema/Ema.Static.Tests.cs @@ -5,7 +5,7 @@ namespace Tests.Indicators; [TestClass] -public class EmaTests : TestBase +public class EmaStaticTests : TestBase { [TestMethod] public void Standard() @@ -29,6 +29,32 @@ public void Standard() Assert.AreEqual(249.3519, r501.Ema.Round(4)); } + [TestMethod] + public void UsePart() + { + List results = quotes + .Use(CandlePart.Open) + .GetEma(20) + .ToList(); + + // assertions + + // proper quantities + // should always be the same number of results as there is quotes + Assert.AreEqual(502, results.Count); + Assert.AreEqual(483, results.Count(x => x.Ema != null)); + + // sample values + EmaResult r29 = results[29]; + Assert.AreEqual(216.2643, NullMath.Round(r29.Ema, 4)); + + EmaResult r249 = results[249]; + Assert.AreEqual(255.4875, NullMath.Round(r249.Ema, 4)); + + EmaResult r501 = results[501]; + Assert.AreEqual(249.9157, NullMath.Round(r501.Ema, 4)); + } + [TestMethod] public void UseTuple() { @@ -80,40 +106,7 @@ public void Chainor() } [TestMethod] - public void Stream() - { - List quotesList = quotes - .OrderBy(x => x.Date) - .ToList(); - - // time-series - List series = quotesList.GetEma(20).ToList(); - - // stream simulation - EmaBase emaBase = quotesList.Take(25).InitEma(20); - - for (int i = 25; i < series.Count; i++) - { - Quote q = quotesList[i]; - emaBase.Add(q); - emaBase.Add(q); // redundant - } - - List stream = emaBase.Results.ToList(); - - // assertions - for (int i = 0; i < series.Count; i++) - { - EmaResult t = series[i]; - EmaResult s = stream[i]; - - Assert.AreEqual(t.Date, s.Date); - Assert.AreEqual(t.Ema, s.Ema); - } - } - - [TestMethod] - public void Chaining() + public void ChaineeMore() { List results = quotes .GetRsi(14) @@ -139,29 +132,6 @@ public void Chaining() Assert.AreEqual(37.0728, r501.Ema.Round(4)); } - [TestMethod] - public void Custom() - { - List results = quotes - .Use(CandlePart.Open) - .GetEma(20) - .ToList(); - - // proper quantities - Assert.AreEqual(502, results.Count); - Assert.AreEqual(483, results.Count(x => x.Ema != null)); - - // sample values - EmaResult r29 = results[29]; - Assert.AreEqual(216.2643, r29.Ema.Round(4)); - - EmaResult r249 = results[249]; - Assert.AreEqual(255.4875, r249.Ema.Round(4)); - - EmaResult r501 = results[501]; - Assert.AreEqual(249.9157, r501.Ema.Round(4)); - } - [TestMethod] public void BadData() { @@ -204,16 +174,9 @@ public void Removed() Assert.AreEqual(249.3519, last.Ema.Round(4)); } + // bad lookback period [TestMethod] public void Exceptions() - { - // bad lookback period - Assert.ThrowsException(() => - quotes.GetEma(0)); - - // null quote added - EmaBase emaBase = quotes.InitEma(14); - Assert.ThrowsException(() => - emaBase.Add(null)); - } + => Assert.ThrowsException(() + => quotes.GetEma(0)); } diff --git a/tests/indicators/s-z/Sma/Sma.Obs.Tests.cs b/tests/indicators/s-z/Sma/Sma.Obs.Tests.cs new file mode 100644 index 000000000..bca4caab6 --- /dev/null +++ b/tests/indicators/s-z/Sma/Sma.Obs.Tests.cs @@ -0,0 +1,126 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Skender.Stock.Indicators; +using Tests.Common; + +namespace Tests.Indicators; + +[TestClass] +public class SmaStreamTests : TestBase +{ + [TestMethod] + public void Standard() + { + List quotesList = quotes + .ToSortedList(); + + int length = quotesList.Count; + + // time-series, for comparison + List seriesList = quotes + .GetSma(20) + .ToList(); + + // setup quote provider + QuoteProvider provider = new(); + + // initialize EMA observer + SmaObserver observer = provider + .GetSma(20); + + // fetch initial results + IEnumerable results + = observer.Results; + + // emulate adding quotes to provider + for (int i = 0; i < length; i++) + { + Quote q = quotesList[i]; + provider.Add(q); + } + + // final results + List resultsList + = results.ToList(); + + // assert, should equal series + for (int i = 0; i < seriesList.Count; i++) + { + SmaResult s = seriesList[i]; + SmaResult r = resultsList[i]; + + Assert.AreEqual(s.Date, r.Date); + Assert.AreEqual(s.Sma, r.Sma); + } + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public void Increment() + { + // baseline for comparison + List<(DateTime Date, double Value)> tpList = new() + { + new (DateTime.Parse("1/1/2000", EnglishCulture), 1d), + new (DateTime.Parse("1/2/2000", EnglishCulture), 2d), + new (DateTime.Parse("1/3/2000", EnglishCulture), 3d), + new (DateTime.Parse("1/4/2000", EnglishCulture), 4d), + new (DateTime.Parse("1/5/2000", EnglishCulture), 5d), + new (DateTime.Parse("1/6/2000", EnglishCulture), 6d), + new (DateTime.Parse("1/7/2000", EnglishCulture), 7d), + new (DateTime.Parse("1/8/2000", EnglishCulture), 8d), + new (DateTime.Parse("1/9/2000", EnglishCulture), 9d), + }; + + double sma; + + sma = SmaObserver.Increment(tpList, tpList.Count - 1, 9); + Assert.AreEqual(5d, sma); + + sma = SmaObserver.Increment(tpList, tpList.Count - 1, 10); + Assert.AreEqual(double.NaN, sma); + } + + [TestMethod] + public void Usee() + { + List quotesList = quotes + .ToSortedList(); + + int length = quotesList.Count; + + // time-series, for comparison + List staticSma = quotes + .Use(CandlePart.OC2) + .GetSma(11) + .ToList(); + + // setup quote provider + QuoteProvider provider = new(); + + // initialize EMA observer + List streamSma = provider + .Use(CandlePart.OC2) + .GetSma(11) + .ProtectedResults; + + // emulate adding quotes to provider + for (int i = 0; i < length; i++) + { + provider.Add(quotesList[i]); + } + + provider.EndTransmission(); + + // assert, should equal series + for (int i = 0; i < length; i++) + { + SmaResult t = staticSma[i]; + SmaResult s = streamSma[i]; + + Assert.AreEqual(t.Date, s.Date); + Assert.AreEqual(t.Sma, s.Sma); + } + } +} diff --git a/tests/indicators/s-z/Sma/Sma.Tests.cs b/tests/indicators/s-z/Sma/Sma.Static.Tests.cs similarity index 80% rename from tests/indicators/s-z/Sma/Sma.Tests.cs rename to tests/indicators/s-z/Sma/Sma.Static.Tests.cs index da1fcded7..f48f11c9b 100644 --- a/tests/indicators/s-z/Sma/Sma.Tests.cs +++ b/tests/indicators/s-z/Sma/Sma.Static.Tests.cs @@ -20,11 +20,11 @@ public void Standard() // sample values Assert.IsNull(results[18].Sma); - Assert.AreEqual(214.5250, Math.Round(results[19].Sma.Value, 4)); - Assert.AreEqual(215.0310, Math.Round(results[24].Sma.Value, 4)); - Assert.AreEqual(234.9350, Math.Round(results[149].Sma.Value, 4)); - Assert.AreEqual(255.5500, Math.Round(results[249].Sma.Value, 4)); - Assert.AreEqual(251.8600, Math.Round(results[501].Sma.Value, 4)); + Assert.AreEqual(214.5250, results[19].Sma.Round(4)); + Assert.AreEqual(215.0310, results[24].Sma.Round(4)); + Assert.AreEqual(234.9350, results[149].Sma.Round(4)); + Assert.AreEqual(255.5500, results[249].Sma.Round(4)); + Assert.AreEqual(251.8600, results[501].Sma.Round(4)); } [TestMethod] @@ -40,11 +40,11 @@ public void CandlePartOpen() // sample values Assert.IsNull(results[18].Sma); - Assert.AreEqual(214.3795, Math.Round(results[19].Sma.Value, 4)); - Assert.AreEqual(214.9535, Math.Round(results[24].Sma.Value, 4)); - Assert.AreEqual(234.8280, Math.Round(results[149].Sma.Value, 4)); - Assert.AreEqual(255.6915, Math.Round(results[249].Sma.Value, 4)); - Assert.AreEqual(253.1725, Math.Round(results[501].Sma.Value, 4)); + Assert.AreEqual(214.3795, results[19].Sma.Round(4)); + Assert.AreEqual(214.9535, results[24].Sma.Round(4)); + Assert.AreEqual(234.8280, results[149].Sma.Round(4)); + Assert.AreEqual(255.6915, results[249].Sma.Round(4)); + Assert.AreEqual(253.1725, results[501].Sma.Round(4)); } [TestMethod] @@ -140,7 +140,7 @@ public void Removed() // assertions Assert.AreEqual(502 - 19, results.Count); - Assert.AreEqual(251.8600, Math.Round(results.LastOrDefault().Sma.Value, 4)); + Assert.AreEqual(251.8600, results.LastOrDefault().Sma.Round(4)); } // bad lookback period diff --git a/tests/observe/Observe.Streaming.csproj b/tests/observe/Observe.Streaming.csproj new file mode 100644 index 000000000..e3d530cc2 --- /dev/null +++ b/tests/observe/Observe.Streaming.csproj @@ -0,0 +1,19 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + + + + + diff --git a/tests/observe/Program.cs b/tests/observe/Program.cs new file mode 100644 index 000000000..2df87d5e8 --- /dev/null +++ b/tests/observe/Program.cs @@ -0,0 +1,117 @@ +using Alpaca.Markets; +using Skender.Stock.Indicators; + +namespace ObserveAlpaca; + +internal class Program +{ + private static async Task Main(string[] args) + { + if (args.Any()) + { + Console.WriteLine(args); + } + + QuoteStream quoteStream = new(); + await quoteStream.SubscribeToQuotes("BTC/USD"); + } +} + +public class QuoteStream +{ + private readonly string? alpacaApiKey = Environment.GetEnvironmentVariable("AlpacaApiKey"); + private readonly string? alpacaSecret = Environment.GetEnvironmentVariable("AlpacaSecret"); + + public async Task SubscribeToQuotes(string symbol) + { + Console.WriteLine("PLEASE WAIT. QUOTES ARRIVE EVERY MINUTE."); + Console.WriteLine("Press any key to exit the process..."); + + if (alpacaApiKey == null) + { + throw new ArgumentNullException(alpacaApiKey); + } + + if (alpacaSecret == null) + { + throw new ArgumentNullException(alpacaSecret); + } + + // initialize our quote provider and a few subscribers + QuoteProvider provider = new(); + + EmaObserver ema = provider.GetEma(14); + SmaObserver sma = provider.GetSma(5); + EmaObserver emaChain = provider + .Use(CandlePart.HL2) + .GetEma(10); + + // connect to Alpaca websocket + SecretKey secretKey = new(alpacaApiKey, alpacaSecret); + + IAlpacaCryptoStreamingClient client + = Environments + .Paper + .GetAlpacaCryptoStreamingClient(secretKey); + + await client.ConnectAndAuthenticateAsync(); + + AutoResetEvent[] waitObjects = new[] // todo: is this needed? + { + new AutoResetEvent(false) + }; + + IAlpacaDataSubscription quoteSubscription + = client.GetMinuteBarSubscription(symbol); + + quoteSubscription.Received += (q) => + { + // add to our provider + provider.Add(new Quote + { + Date = q.TimeUtc, + Open = q.Open, + High = q.High, + Low = q.Low, + Close = q.Close, + Volume = q.Volume + }); + + Console.WriteLine($"{q.Symbol} {q.TimeUtc:s} ${q.Close:N2} | {q.TradeCount} trades"); + }; + + await client.SubscribeAsync(quoteSubscription); + + // to stop watching on key press + Console.ReadKey(); + + provider.EndTransmission(); + await client.UnsubscribeAsync(quoteSubscription); + await client.DisconnectAsync(); + + Console.WriteLine("-- QUOTES STORED (last 10 only) --"); + foreach (Quote? pt in provider.Quotes.TakeLast(10)) + { + Console.WriteLine($"{symbol} {pt.Date:s} ${pt.Close:N2}"); + } + + // show last 3 results for indicator results + Console.WriteLine("-- EMA(14,CLOSE) RESULTS (last 3 only) --"); + foreach (EmaResult? e in ema.Results.TakeLast(3)) + { + Console.WriteLine($"{symbol} {e.Date:s} ${e.Ema:N2}"); + } + + Console.WriteLine("-- EMA(10,HL2) CHAINED (last 3 only) --"); + foreach (EmaResult? e in emaChain.Results.TakeLast(3)) + { + Console.WriteLine($"{symbol} {e.Date:s} ${e.Ema:N2}"); + } + + Console.WriteLine("-- SMA(5) RESULTS (last 3 only) --"); + foreach (SmaResult? s in sma.Results.TakeLast(3)) + { + Console.WriteLine($"{symbol} {s.Date:s} ${s.Sma:N2}"); + } + } +} \ No newline at end of file diff --git a/tests/observe/README.md b/tests/observe/README.md new file mode 100644 index 000000000..f53a331ac --- /dev/null +++ b/tests/observe/README.md @@ -0,0 +1,10 @@ +# Prerequisite steps + +To get connected to the WebSocket in this console test project, you have to add your own Alpaca key and secret information to your local environment variables. + +Run the following command line items to set, after replacing the secret and key values. + +```bash +setx AlpacaApiKey "ALPACA_API_KEY" +setx AlpacaSecret "ALPACA_SECRET" +``` diff --git a/tests/performance/Perf.Helpers.cs b/tests/performance/Perf.Helpers.cs index 3cd086019..f1f794aed 100644 --- a/tests/performance/Perf.Helpers.cs +++ b/tests/performance/Perf.Helpers.cs @@ -18,20 +18,26 @@ public class HelperPerformance public static void SetupIntraday() => i = TestData.GetIntraday(); [Benchmark] - public object SortToList() => h.ToSortedList(); + public object ToSortedList() => h.ToSortedList(); [Benchmark] - public object ToListQuoteD() => h.ToQuoteD(); + public object ToSortedCollection() => h.ToSortedCollection(); [Benchmark] - public object Validate() => h.Validate(); + public object ToListQuoteD() => h.ToQuoteD(); [Benchmark] - public object Aggregate() => i.Aggregate(PeriodSize.FifteenMinutes); + public object ToTupleClose() => h.ToTuple(CandlePart.Close); [Benchmark] - public object ToTuple() => h.ToTuple(CandlePart.Close); + public object ToTupleOHLC4() => h.ToTuple(CandlePart.OHLC4); [Benchmark] public object ToCandleResults() => h.ToCandleResults(); + + [Benchmark] + public object Validate() => h.Validate(); + + [Benchmark] + public object Aggregate() => i.Aggregate(PeriodSize.FifteenMinutes); } diff --git a/tests/performance/Perf.Indicators.Static.cs b/tests/performance/Perf.Indicators.Static.cs new file mode 100644 index 000000000..e4bb92aaa --- /dev/null +++ b/tests/performance/Perf.Indicators.Static.cs @@ -0,0 +1,323 @@ +using BenchmarkDotNet.Attributes; +using Skender.Stock.Indicators; +using Tests.Common; + +namespace Tests.Performance; + +public class IndicatorsStatic +{ + private static IEnumerable q; + private static IEnumerable o; + private static List ql; + private static List ll; + + // SETUP + + [GlobalSetup] + public static void Setup() + { + q = TestData.GetDefault(); + ql = q.ToList(); + ll = TestData.GetLongest().ToList(); + } + + [GlobalSetup(Targets = new[] + { + nameof(GetBeta), + nameof(GetBetaUp), + nameof(GetBetaDown), + nameof(GetBetaAll), + nameof(GetCorrelation), + nameof(GetPrs), + nameof(GetPrsWithSma) + })] + public static void SetupCompare() + { + q = TestData.GetDefault(); + o = TestData.GetCompare(); + } + + // BENCHMARKS + + [Benchmark] + public object GetAdl() => q.GetAdl(); + + [Benchmark] + public object GetAdlWithSma() => q.GetAdl(14); + + [Benchmark] + public object GetAdx() => q.GetAdx(); + + [Benchmark] + public object GetAlligator() => q.GetAlligator(); + + [Benchmark] + public object GetAlma() => q.GetAlma(); + + [Benchmark] + public object GetAroon() => q.GetAroon(); + + [Benchmark] + public object GetAtr() => q.GetAtr(); + + [Benchmark] + public object GetAtrStop() => q.GetAtrStop(); + + [Benchmark] + public object GetAwesome() => q.GetAwesome(); + + [Benchmark] + public object GetBeta() => Indicator.GetBeta(q, o, 20, BetaType.Standard); + + [Benchmark] + public object GetBetaUp() => Indicator.GetBeta(q, o, 20, BetaType.Up); + + [Benchmark] + public object GetBetaDown() => Indicator.GetBeta(q, o, 20, BetaType.Down); + + [Benchmark] + public object GetBetaAll() => Indicator.GetBeta(q, o, 20, BetaType.All); + + [Benchmark] + public object GetBollingerBands() => q.GetBollingerBands(); + + [Benchmark] + public object GetBop() => q.GetBop(); + + [Benchmark] + public object GetCci() => q.GetCci(); + + [Benchmark] + public object GetChaikinOsc() => q.GetChaikinOsc(); + + [Benchmark] + public object GetChandelier() => q.GetChandelier(); + + [Benchmark] + public object GetChop() => q.GetChop(); + + [Benchmark] + public object GetCmf() => q.GetCmf(); + + [Benchmark] + public object GetCmo() => q.GetCmo(14); + + [Benchmark] + public object GetConnorsRsi() => q.GetConnorsRsi(); + + [Benchmark] + public object GetCorrelation() => q.GetCorrelation(o, 20); + + [Benchmark] + public object GetDema() => q.GetDema(14); + + [Benchmark] + public object GetDoji() => q.GetDoji(); + + [Benchmark] + public object GetDonchian() => q.GetDonchian(); + + [Benchmark] + public object GetDpo() => q.GetDpo(14); + + [Benchmark] + public object GetElderRay() => q.GetElderRay(); + + [Benchmark] + public object GetEma() => q.GetEma(14); + + [Benchmark] + public object GetEpma() => q.GetEpma(14); + + [Benchmark] + public object GetFcb() => q.GetFcb(14); + + [Benchmark] + public object GetFisherTransform() => q.GetFisherTransform(10); + + [Benchmark] + public object GetForceIndex() => q.GetForceIndex(13); + + [Benchmark] + public object GetFractal() => q.GetFractal(); + + [Benchmark] + public object GetGator() => q.GetGator(); + + [Benchmark] + public object GetHeikinAshi() => q.GetHeikinAshi(); + + [Benchmark] + public object GetHma() => q.GetHma(14); + + [Benchmark] + public object GetHtTrendline() => q.GetHtTrendline(); + + [Benchmark] + public object GetHurst() => q.GetHurst(); + + [Benchmark] + public object GetIchimoku() => q.GetIchimoku(); + + [Benchmark] + public object GetKama() => q.GetKama(); + + [Benchmark] + public object GetKlinger() => q.GetKvo(); + + [Benchmark] + public object GetKeltner() => q.GetKeltner(); + + [Benchmark] + public object GetKvo() => q.GetKvo(); + + [Benchmark] + public object GetMacd() => q.GetMacd(); + + [Benchmark] + public object GetMaEnvelopes() => q.GetMaEnvelopes(20, 2.5, MaType.SMA); + + [Benchmark] + public object GetMama() => q.GetMama(); + + [Benchmark] + public object GetMarubozu() => q.GetMarubozu(); + + [Benchmark] + public object GetMfi() => q.GetMfi(); + + [Benchmark] + public object GetObv() => q.GetObv(); + + [Benchmark] + public object GetObvWithSma() => q.GetObv(14); + + [Benchmark] + public object GetParabolicSar() => q.GetParabolicSar(); + + [Benchmark] + public object GetPivotPoints() => q.GetPivotPoints(PeriodSize.Month, PivotPointType.Standard); + + [Benchmark] + public object GetPivots() => q.GetPivots(); + + [Benchmark] + public object GetPmo() => q.GetPmo(); + + [Benchmark] + public object GetPrs() => q.GetPrs(o); + + [Benchmark] + public object GetPrsWithSma() => q.GetPrs(o, null, 5); + + [Benchmark] + public object GetPvo() => q.GetPvo(); + + [Benchmark] + public object GetRenko() => q.GetRenko(2.5m); + + [Benchmark] + public object GetRenkoAtr() => q.GetRenko(14); + + [Benchmark] + public object GetRoc() => q.GetRoc(20); + + [Benchmark] + public object GetRocWb() => q.GetRocWb(12, 3, 12); + + [Benchmark] + public object GetRocWithSma() => q.GetRoc(20, 14); + + [Benchmark] + public object GetRollingPivots() => q.GetRollingPivots(14, 1); + + [Benchmark] + public object GetRsi() => q.GetRsi(); + + [Benchmark] + public object GetSlope() => q.GetSlope(20); + + [Benchmark] + public object GetSma() => q.GetSma(10); + + [Benchmark] + public object GetSmaAnalysis() => q.GetSmaAnalysis(10); + + [Benchmark] + public object GetSmi() => q.GetSmi(5, 20, 5, 3); + + [Benchmark] + public object GetSmma() => q.GetSmma(10); + + [Benchmark] + public object GetStarcBands() => q.GetStarcBands(); + + [Benchmark] + public object GetStc() => q.GetStc(); + + [Benchmark] + public object GetStdDev() => q.GetStdDev(20); + + [Benchmark] + public object GetStdDevWithSma() => q.GetStdDev(20, 14); + + [Benchmark] + public object GetStdDevChannels() => q.GetStdDevChannels(); + + [Benchmark] + public object GetStoch() => q.GetStoch(); + + [Benchmark] + public object GetStochSMMA() => q.GetStoch(9, 3, 3, 3, 2, MaType.SMMA); + + [Benchmark] + public object GetStochRsi() => q.GetStochRsi(14, 14, 3); + + [Benchmark] + public object GetSuperTrend() => q.GetSuperTrend(); + + [Benchmark] + public object GetT3() => q.GetT3(); + + [Benchmark] + public object GetTema() => q.GetTema(14); + + [Benchmark] + public object GetTr() => q.GetTr(); + + [Benchmark] + public object GetTrix() => q.GetTrix(14); + + [Benchmark] + public object GetTrixWithSma() => q.GetTrix(14, 5); + + [Benchmark] + public object GetTsi() => q.GetTsi(); + + [Benchmark] + public object GetUlcerIndex() => q.GetUlcerIndex(); + + [Benchmark] + public object GetUltimate() => q.GetUltimate(); + + [Benchmark] + public object GetVolatilityStop() => q.GetVolatilityStop(); + + [Benchmark] + public object GetVortex() => q.GetVortex(14); + + [Benchmark] + public object GetVwap() => q.GetVwap(); + + [Benchmark] + public object GetVwma() => q.GetVwma(14); + + [Benchmark] + public object GetWilliamsR() => q.GetWilliamsR(); + + [Benchmark] + public object GetWma() => q.GetWma(14); + + [Benchmark] + public object GetZigZag() => q.GetZigZag(); +} diff --git a/tests/performance/Perf.Indicators.Stream.cs b/tests/performance/Perf.Indicators.Stream.cs new file mode 100644 index 000000000..485ffc8dd --- /dev/null +++ b/tests/performance/Perf.Indicators.Stream.cs @@ -0,0 +1,62 @@ +using BenchmarkDotNet.Attributes; +using Skender.Stock.Indicators; +using Tests.Common; + +namespace Tests.Performance; + +public class IndicatorsStreaming +{ + private static IEnumerable q; + private static List ql; + + // SETUP + + [GlobalSetup] + public void Setup() + { + q = TestData.GetDefault(); + ql = q.ToSortedList(); + } + + // BENCHMARKS + + [Benchmark] + public object GetEma() => q.GetEma(14); + + [Benchmark] + public object GetEmaStream() + { + // todo: refactor to exclude provider + QuoteProvider provider = new(); + EmaObserver observer = provider.GetEma(14); + + for (int i = 0; i < ql.Count; i++) + { + provider.Add(ql[i]); + } + + provider.EndTransmission(); + + return observer.Results; + } + + [Benchmark] + public object GetSma() => q.GetSma(10); + + [Benchmark] + public object GetSmaStream() + { + // todo: refactor to exclude provider + QuoteProvider provider = new(); + SmaObserver observer = provider.GetSma(10); + + for (int i = 0; i < ql.Count; i++) + { + provider.Add(ql[i]); + } + + provider.EndTransmission(); + + return observer.Results; + } +} diff --git a/tests/performance/Perf.Indicators.cs b/tests/performance/Perf.Indicators.cs deleted file mode 100644 index e482e21ec..000000000 --- a/tests/performance/Perf.Indicators.cs +++ /dev/null @@ -1,335 +0,0 @@ -using BenchmarkDotNet.Attributes; -using Skender.Stock.Indicators; -using Tests.Common; - -namespace Tests.Performance; - -public class IndicatorPerformance -{ - private static IEnumerable h; - private static IEnumerable ho; - private static List hList; - - // SETUP - - [GlobalSetup] - public static void Setup() - { - h = TestData.GetDefault(); - hList = h.ToList(); - } - - [GlobalSetup(Targets = new[] - { - nameof(GetBeta), - nameof(GetBetaUp), - nameof(GetBetaDown), - nameof(GetBetaAll), - nameof(GetCorrelation), - nameof(GetPrs), - nameof(GetPrsWithSma) - })] - public static void SetupCompare() - { - h = TestData.GetDefault(); - ho = TestData.GetCompare(); - } - - // BENCHMARKS - - [Benchmark] - public object GetAdl() => h.GetAdl(); - - [Benchmark] - public object GetAdlWithSma() => h.GetAdl(14); - - [Benchmark] - public object GetAdx() => h.GetAdx(); - - [Benchmark] - public object GetAlligator() => h.GetAlligator(); - - [Benchmark] - public object GetAlma() => h.GetAlma(); - - [Benchmark] - public object GetAroon() => h.GetAroon(); - - [Benchmark] - public object GetAtr() => h.GetAtr(); - - [Benchmark] - public object GetAtrStop() => h.GetAtrStop(); - - [Benchmark] - public object GetAwesome() => h.GetAwesome(); - - [Benchmark] - public object GetBeta() => Indicator.GetBeta(h, ho, 20, BetaType.Standard); - - [Benchmark] - public object GetBetaUp() => Indicator.GetBeta(h, ho, 20, BetaType.Up); - - [Benchmark] - public object GetBetaDown() => Indicator.GetBeta(h, ho, 20, BetaType.Down); - - [Benchmark] - public object GetBetaAll() => Indicator.GetBeta(h, ho, 20, BetaType.All); - - [Benchmark] - public object GetBollingerBands() => h.GetBollingerBands(); - - [Benchmark] - public object GetBop() => h.GetBop(); - - [Benchmark] - public object GetCci() => h.GetCci(); - - [Benchmark] - public object GetChaikinOsc() => h.GetChaikinOsc(); - - [Benchmark] - public object GetChandelier() => h.GetChandelier(); - - [Benchmark] - public object GetChop() => h.GetChop(); - - [Benchmark] - public object GetCmf() => h.GetCmf(); - - [Benchmark] - public object GetCmo() => h.GetCmo(14); - - [Benchmark] - public object GetConnorsRsi() => h.GetConnorsRsi(); - - [Benchmark] - public object GetCorrelation() => h.GetCorrelation(ho, 20); - - [Benchmark] - public object GetDema() => h.GetDema(14); - - [Benchmark] - public object GetDoji() => h.GetDoji(); - - [Benchmark] - public object GetDonchian() => h.GetDonchian(); - - [Benchmark] - public object GetDpo() => h.GetDpo(14); - - [Benchmark] - public object GetElderRay() => h.GetElderRay(); - - [Benchmark] - public object GetEma() => h.GetEma(14); - - [Benchmark] - public object GetEmaStream() - { - EmaBase emaBase = hList.Take(15).InitEma(14); - - for (int i = 15; i < hList.Count; i++) - { - Quote q = hList[i]; - _ = emaBase.Add(q); - } - - return emaBase.Results; - } - - [Benchmark] - public object GetEpma() => h.GetEpma(14); - - [Benchmark] - public object GetFcb() => h.GetFcb(14); - - [Benchmark] - public object GetFisherTransform() => h.GetFisherTransform(10); - - [Benchmark] - public object GetForceIndex() => h.GetForceIndex(13); - - [Benchmark] - public object GetFractal() => h.GetFractal(); - - [Benchmark] - public object GetGator() => h.GetGator(); - - [Benchmark] - public object GetHeikinAshi() => h.GetHeikinAshi(); - - [Benchmark] - public object GetHma() => h.GetHma(14); - - [Benchmark] - public object GetHtTrendline() => h.GetHtTrendline(); - - [Benchmark] - public object GetHurst() => h.GetHurst(); - - [Benchmark] - public object GetIchimoku() => h.GetIchimoku(); - - [Benchmark] - public object GetKama() => h.GetKama(); - - [Benchmark] - public object GetKlinger() => h.GetKvo(); - - [Benchmark] - public object GetKeltner() => h.GetKeltner(); - - [Benchmark] - public object GetKvo() => h.GetKvo(); - - [Benchmark] - public object GetMacd() => h.GetMacd(); - - [Benchmark] - public object GetMaEnvelopes() => h.GetMaEnvelopes(20, 2.5, MaType.SMA); - - [Benchmark] - public object GetMama() => h.GetMama(); - - [Benchmark] - public object GetMarubozu() => h.GetMarubozu(); - - [Benchmark] - public object GetMfi() => h.GetMfi(); - - [Benchmark] - public object GetObv() => h.GetObv(); - - [Benchmark] - public object GetObvWithSma() => h.GetObv(14); - - [Benchmark] - public object GetParabolicSar() => h.GetParabolicSar(); - - [Benchmark] - public object GetPivotPoints() => h.GetPivotPoints(PeriodSize.Month, PivotPointType.Standard); - - [Benchmark] - public object GetPivots() => h.GetPivots(); - - [Benchmark] - public object GetPmo() => h.GetPmo(); - - [Benchmark] - public object GetPrs() => h.GetPrs(ho); - - [Benchmark] - public object GetPrsWithSma() => h.GetPrs(ho, null, 5); - - [Benchmark] - public object GetPvo() => h.GetPvo(); - - [Benchmark] - public object GetRenko() => h.GetRenko(2.5m); - - [Benchmark] - public object GetRenkoAtr() => h.GetRenko(14); - - [Benchmark] - public object GetRoc() => h.GetRoc(20); - - [Benchmark] - public object GetRocWb() => h.GetRocWb(12, 3, 12); - - [Benchmark] - public object GetRocWithSma() => h.GetRoc(20, 14); - - [Benchmark] - public object GetRollingPivots() => h.GetRollingPivots(14, 1); - - [Benchmark] - public object GetRsi() => h.GetRsi(); - - [Benchmark] - public object GetSlope() => h.GetSlope(20); - - [Benchmark] - public object GetSma() => h.GetSma(10); - - [Benchmark] - public object GetSmaAnalysis() => h.GetSmaAnalysis(10); - - [Benchmark] - public object GetSmi() => h.GetSmi(5, 20, 5, 3); - - [Benchmark] - public object GetSmma() => h.GetSmma(10); - - [Benchmark] - public object GetStarcBands() => h.GetStarcBands(); - - [Benchmark] - public object GetStc() => h.GetStc(); - - [Benchmark] - public object GetStdDev() => h.GetStdDev(20); - - [Benchmark] - public object GetStdDevWithSma() => h.GetStdDev(20, 14); - - [Benchmark] - public object GetStdDevChannels() => h.GetStdDevChannels(); - - [Benchmark] - public object GetStoch() => h.GetStoch(); - - [Benchmark] - public object GetStochSMMA() => h.GetStoch(9, 3, 3, 3, 2, MaType.SMMA); - - [Benchmark] - public object GetStochRsi() => h.GetStochRsi(14, 14, 3); - - [Benchmark] - public object GetSuperTrend() => h.GetSuperTrend(); - - [Benchmark] - public object GetT3() => h.GetT3(); - - [Benchmark] - public object GetTema() => h.GetTema(14); - - [Benchmark] - public object GetTr() => h.GetTr(); - - [Benchmark] - public object GetTrix() => h.GetTrix(14); - - [Benchmark] - public object GetTrixWithSma() => h.GetTrix(14, 5); - - [Benchmark] - public object GetTsi() => h.GetTsi(); - - [Benchmark] - public object GetUlcerIndex() => h.GetUlcerIndex(); - - [Benchmark] - public object GetUltimate() => h.GetUltimate(); - - [Benchmark] - public object GetVolatilityStop() => h.GetVolatilityStop(); - - [Benchmark] - public object GetVortex() => h.GetVortex(14); - - [Benchmark] - public object GetVwap() => h.GetVwap(); - - [Benchmark] - public object GetVwma() => h.GetVwma(14); - - [Benchmark] - public object GetWilliamsR() => h.GetWilliamsR(); - - [Benchmark] - public object GetWma() => h.GetWma(14); - - [Benchmark] - public object GetZigZag() => h.GetZigZag(); -} From fc34f3dfd3561ba7b04bb1f34e4219f533eb5c3e Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Thu, 20 Apr 2023 02:47:44 -0400 Subject: [PATCH 03/18] fix merge conflict --- tests/performance/Perf.Indicators.Static.cs | 2 +- tests/performance/Perf.Indicators.cs | 335 -------------------- 2 files changed, 1 insertion(+), 336 deletions(-) delete mode 100644 tests/performance/Perf.Indicators.cs diff --git a/tests/performance/Perf.Indicators.Static.cs b/tests/performance/Perf.Indicators.Static.cs index e4bb92aaa..65412f505 100644 --- a/tests/performance/Perf.Indicators.Static.cs +++ b/tests/performance/Perf.Indicators.Static.cs @@ -250,7 +250,7 @@ public static void SetupCompare() public object GetSmma() => q.GetSmma(10); [Benchmark] - public object GetStarcBands() => q.GetStarcBands(); + public object GetStarcBands() => q.GetStarcBands(10); [Benchmark] public object GetStc() => q.GetStc(); diff --git a/tests/performance/Perf.Indicators.cs b/tests/performance/Perf.Indicators.cs deleted file mode 100644 index 030f31a2e..000000000 --- a/tests/performance/Perf.Indicators.cs +++ /dev/null @@ -1,335 +0,0 @@ -using BenchmarkDotNet.Attributes; -using Skender.Stock.Indicators; -using Tests.Common; - -namespace Tests.Performance; - -public class IndicatorPerformance -{ - private static IEnumerable h; - private static IEnumerable ho; - private static List hList; - - // SETUP - - [GlobalSetup] - public static void Setup() - { - h = TestData.GetDefault(); - hList = h.ToList(); - } - - [GlobalSetup(Targets = new[] - { - nameof(GetBeta), - nameof(GetBetaUp), - nameof(GetBetaDown), - nameof(GetBetaAll), - nameof(GetCorrelation), - nameof(GetPrs), - nameof(GetPrsWithSma) - })] - public static void SetupCompare() - { - h = TestData.GetDefault(); - ho = TestData.GetCompare(); - } - - // BENCHMARKS - - [Benchmark] - public object GetAdl() => h.GetAdl(); - - [Benchmark] - public object GetAdlWithSma() => h.GetAdl(14); - - [Benchmark] - public object GetAdx() => h.GetAdx(); - - [Benchmark] - public object GetAlligator() => h.GetAlligator(); - - [Benchmark] - public object GetAlma() => h.GetAlma(); - - [Benchmark] - public object GetAroon() => h.GetAroon(); - - [Benchmark] - public object GetAtr() => h.GetAtr(); - - [Benchmark] - public object GetAtrStop() => h.GetAtrStop(); - - [Benchmark] - public object GetAwesome() => h.GetAwesome(); - - [Benchmark] - public object GetBeta() => Indicator.GetBeta(h, ho, 20, BetaType.Standard); - - [Benchmark] - public object GetBetaUp() => Indicator.GetBeta(h, ho, 20, BetaType.Up); - - [Benchmark] - public object GetBetaDown() => Indicator.GetBeta(h, ho, 20, BetaType.Down); - - [Benchmark] - public object GetBetaAll() => Indicator.GetBeta(h, ho, 20, BetaType.All); - - [Benchmark] - public object GetBollingerBands() => h.GetBollingerBands(); - - [Benchmark] - public object GetBop() => h.GetBop(); - - [Benchmark] - public object GetCci() => h.GetCci(); - - [Benchmark] - public object GetChaikinOsc() => h.GetChaikinOsc(); - - [Benchmark] - public object GetChandelier() => h.GetChandelier(); - - [Benchmark] - public object GetChop() => h.GetChop(); - - [Benchmark] - public object GetCmf() => h.GetCmf(); - - [Benchmark] - public object GetCmo() => h.GetCmo(14); - - [Benchmark] - public object GetConnorsRsi() => h.GetConnorsRsi(); - - [Benchmark] - public object GetCorrelation() => h.GetCorrelation(ho, 20); - - [Benchmark] - public object GetDema() => h.GetDema(14); - - [Benchmark] - public object GetDoji() => h.GetDoji(); - - [Benchmark] - public object GetDonchian() => h.GetDonchian(); - - [Benchmark] - public object GetDpo() => h.GetDpo(14); - - [Benchmark] - public object GetElderRay() => h.GetElderRay(); - - [Benchmark] - public object GetEma() => h.GetEma(14); - - [Benchmark] - public object GetEmaStream() - { - EmaBase emaBase = hList.Take(15).InitEma(14); - - for (int i = 15; i < hList.Count; i++) - { - Quote q = hList[i]; - _ = emaBase.Add(q); - } - - return emaBase.Results; - } - - [Benchmark] - public object GetEpma() => h.GetEpma(14); - - [Benchmark] - public object GetFcb() => h.GetFcb(14); - - [Benchmark] - public object GetFisherTransform() => h.GetFisherTransform(10); - - [Benchmark] - public object GetForceIndex() => h.GetForceIndex(13); - - [Benchmark] - public object GetFractal() => h.GetFractal(); - - [Benchmark] - public object GetGator() => h.GetGator(); - - [Benchmark] - public object GetHeikinAshi() => h.GetHeikinAshi(); - - [Benchmark] - public object GetHma() => h.GetHma(14); - - [Benchmark] - public object GetHtTrendline() => h.GetHtTrendline(); - - [Benchmark] - public object GetHurst() => h.GetHurst(); - - [Benchmark] - public object GetIchimoku() => h.GetIchimoku(); - - [Benchmark] - public object GetKama() => h.GetKama(); - - [Benchmark] - public object GetKlinger() => h.GetKvo(); - - [Benchmark] - public object GetKeltner() => h.GetKeltner(); - - [Benchmark] - public object GetKvo() => h.GetKvo(); - - [Benchmark] - public object GetMacd() => h.GetMacd(); - - [Benchmark] - public object GetMaEnvelopes() => h.GetMaEnvelopes(20, 2.5, MaType.SMA); - - [Benchmark] - public object GetMama() => h.GetMama(); - - [Benchmark] - public object GetMarubozu() => h.GetMarubozu(); - - [Benchmark] - public object GetMfi() => h.GetMfi(); - - [Benchmark] - public object GetObv() => h.GetObv(); - - [Benchmark] - public object GetObvWithSma() => h.GetObv(14); - - [Benchmark] - public object GetParabolicSar() => h.GetParabolicSar(); - - [Benchmark] - public object GetPivotPoints() => h.GetPivotPoints(PeriodSize.Month, PivotPointType.Standard); - - [Benchmark] - public object GetPivots() => h.GetPivots(); - - [Benchmark] - public object GetPmo() => h.GetPmo(); - - [Benchmark] - public object GetPrs() => h.GetPrs(ho); - - [Benchmark] - public object GetPrsWithSma() => h.GetPrs(ho, null, 5); - - [Benchmark] - public object GetPvo() => h.GetPvo(); - - [Benchmark] - public object GetRenko() => h.GetRenko(2.5m); - - [Benchmark] - public object GetRenkoAtr() => h.GetRenko(14); - - [Benchmark] - public object GetRoc() => h.GetRoc(20); - - [Benchmark] - public object GetRocWb() => h.GetRocWb(12, 3, 12); - - [Benchmark] - public object GetRocWithSma() => h.GetRoc(20, 14); - - [Benchmark] - public object GetRollingPivots() => h.GetRollingPivots(14, 1); - - [Benchmark] - public object GetRsi() => h.GetRsi(); - - [Benchmark] - public object GetSlope() => h.GetSlope(20); - - [Benchmark] - public object GetSma() => h.GetSma(10); - - [Benchmark] - public object GetSmaAnalysis() => h.GetSmaAnalysis(10); - - [Benchmark] - public object GetSmi() => h.GetSmi(5, 20, 5, 3); - - [Benchmark] - public object GetSmma() => h.GetSmma(10); - - [Benchmark] - public object GetStarcBands() => h.GetStarcBands(10); - - [Benchmark] - public object GetStc() => h.GetStc(); - - [Benchmark] - public object GetStdDev() => h.GetStdDev(20); - - [Benchmark] - public object GetStdDevWithSma() => h.GetStdDev(20, 14); - - [Benchmark] - public object GetStdDevChannels() => h.GetStdDevChannels(); - - [Benchmark] - public object GetStoch() => h.GetStoch(); - - [Benchmark] - public object GetStochSMMA() => h.GetStoch(9, 3, 3, 3, 2, MaType.SMMA); - - [Benchmark] - public object GetStochRsi() => h.GetStochRsi(14, 14, 3); - - [Benchmark] - public object GetSuperTrend() => h.GetSuperTrend(); - - [Benchmark] - public object GetT3() => h.GetT3(); - - [Benchmark] - public object GetTema() => h.GetTema(14); - - [Benchmark] - public object GetTr() => h.GetTr(); - - [Benchmark] - public object GetTrix() => h.GetTrix(14); - - [Benchmark] - public object GetTrixWithSma() => h.GetTrix(14, 5); - - [Benchmark] - public object GetTsi() => h.GetTsi(); - - [Benchmark] - public object GetUlcerIndex() => h.GetUlcerIndex(); - - [Benchmark] - public object GetUltimate() => h.GetUltimate(); - - [Benchmark] - public object GetVolatilityStop() => h.GetVolatilityStop(); - - [Benchmark] - public object GetVortex() => h.GetVortex(14); - - [Benchmark] - public object GetVwap() => h.GetVwap(); - - [Benchmark] - public object GetVwma() => h.GetVwma(14); - - [Benchmark] - public object GetWilliamsR() => h.GetWilliamsR(); - - [Benchmark] - public object GetWma() => h.GetWma(14); - - [Benchmark] - public object GetZigZag() => h.GetZigZag(); -} From e38b43b74e590eb2f2d70652e78a009e43e498b9 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Wed, 3 Jan 2024 00:43:44 -0500 Subject: [PATCH 04/18] chore: Update packages (#1139) --- src/Indicators.csproj | 2 +- tests/indicators/Tests.Indicators.csproj | 2 +- tests/observe/Observe.Streaming.csproj | 4 ++-- tests/performance/Tests.Performance.csproj | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Indicators.csproj b/src/Indicators.csproj index e0a63f23f..ae189d86d 100644 --- a/src/Indicators.csproj +++ b/src/Indicators.csproj @@ -81,7 +81,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/tests/indicators/Tests.Indicators.csproj b/tests/indicators/Tests.Indicators.csproj index 64012bb63..7c032e42d 100644 --- a/tests/indicators/Tests.Indicators.csproj +++ b/tests/indicators/Tests.Indicators.csproj @@ -14,7 +14,7 @@ - + diff --git a/tests/observe/Observe.Streaming.csproj b/tests/observe/Observe.Streaming.csproj index e3d530cc2..a2af5efa2 100644 --- a/tests/observe/Observe.Streaming.csproj +++ b/tests/observe/Observe.Streaming.csproj @@ -2,13 +2,13 @@ Exe - net7.0 + net8.0 enable enable - + diff --git a/tests/performance/Tests.Performance.csproj b/tests/performance/Tests.Performance.csproj index bb68eb478..74cb35218 100644 --- a/tests/performance/Tests.Performance.csproj +++ b/tests/performance/Tests.Performance.csproj @@ -14,7 +14,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From ad39bc9281d1bfa1581aca606fa5f903872e0aa0 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Wed, 3 Jan 2024 01:17:27 -0500 Subject: [PATCH 05/18] fix: Merge `main` into `v3` (#1142) Signed-off-by: Dave Skender <8432125+DaveSkender@users.noreply.github.com> --- .editorconfig | 29 ++++++++++--------- .github/workflows/build-indicators.yml | 10 ++++++- .github/workflows/deploy-docs.yml | 4 +-- .github/workflows/semantic-pr-linter.yml | 6 ++-- docs/GemFile.lock | 18 ++++++------ src/GlobalSuppressions.cs | 7 +---- src/Indicators.csproj | 5 ---- src/_common/Enums.cs | 2 +- src/_common/Math/NullMath.cs | 8 ++--- src/_common/Math/Numerix.cs | 8 ++--- src/_common/Observables/ChainProvider.cs | 4 +-- src/_common/Observables/QuoteProvider.cs | 4 +-- src/_common/Observables/TupleProvider.cs | 4 +-- src/_common/Quotes/Quote.Aggregates.cs | 6 ---- src/_common/Quotes/Quote.Exceptions.cs | 3 -- src/_common/Quotes/Quote.Validation.cs | 4 +-- src/_common/Results/Result.Utilities.cs | 4 +-- src/_common/Results/info.xml | 2 +- src/a-d/Dpo/Dpo.Api.cs | 2 +- src/e-k/Ema/Ema.Observer.cs | 2 +- src/e-k/Epma/Epma.Api.cs | 2 +- src/e-k/Hma/Hma.Api.cs | 2 +- src/m-r/ParabolicSar/ParabolicSar.Series.cs | 2 +- src/m-r/PivotPoints/PivotPoints.Series.cs | 2 +- src/m-r/RollingPivots/RollingPivots.Series.cs | 20 ++++++------- src/s-z/Sma/Sma.Analysis.cs | 2 +- src/s-z/Sma/Sma.Observer.cs | 9 ++---- src/s-z/Tema/Tema.Api.cs | 2 +- tests/indicators/GlobalUsings.cs | 3 ++ tests/indicators/_Initialize.cs | 2 -- .../_common/Candles/Candles.Tests.cs | 3 -- .../_common/Generics/Pruning.Tests.cs | 3 -- .../indicators/_common/Generics/Seek.Tests.cs | 3 -- .../indicators/_common/Generics/Sort.Tests.cs | 2 -- .../_common/Generics/Transforms.Tests.cs | 2 -- tests/indicators/_common/Helper.Getter.cs | 2 -- tests/indicators/_common/Helper.Importer.cs | 5 ++-- tests/indicators/_common/Helper.LiveQuotes.cs | 2 -- tests/indicators/_common/Helper.Random.cs | 4 +-- .../indicators/_common/Math/Numerix.Tests.cs | 3 -- .../_common/Quotes/Quote.Aggregates.Tests.cs | 3 -- .../_common/Quotes/Quote.Converters.Tests.cs | 2 -- .../_common/Quotes/Quote.Exceptions.Tests.cs | 3 -- .../_common/Quotes/Quote.Validation.Tests.cs | 3 -- .../_common/Results/Result.Syncing.Tests.cs | 3 -- .../_common/Results/Result.Utilities.Tests.cs | 2 -- tests/indicators/a-d/Adl/Adl.Tests.cs | 4 --- tests/indicators/a-d/Adx/Adx.Tests.cs | 4 --- .../a-d/Alligator/Alligator.Tests.cs | 4 --- tests/indicators/a-d/Alma/Alma.Tests.cs | 4 --- tests/indicators/a-d/Aroon/Aroon.Tests.cs | 4 --- tests/indicators/a-d/Atr/Atr.Tests.cs | 4 --- tests/indicators/a-d/AtrStop/AtrStop.Tests.cs | 4 --- tests/indicators/a-d/Awesome/Awesome.Tests.cs | 4 --- .../a-d/BasicQuote/BasicQuote.Tests.cs | 4 --- tests/indicators/a-d/Beta/Beta.Tests.cs | 4 --- .../BollingerBands/BollingerBands.Tests.cs | 4 --- tests/indicators/a-d/Bop/Bop.Tests.cs | 4 --- tests/indicators/a-d/Cci/Cci.Tests.cs | 4 --- .../a-d/ChaikinOsc/ChaikinOsc.Tests.cs | 4 --- .../a-d/Chandelier/Chandelier.Tests.cs | 4 --- tests/indicators/a-d/Chop/Chop.Tests.cs | 4 --- tests/indicators/a-d/Cmf/Cmf.Tests.cs | 4 --- tests/indicators/a-d/Cmo/Cmo.Tests.cs | 4 --- .../a-d/ConnorsRsi/ConnorsRsi.Tests.cs | 4 --- .../a-d/Correlation/Correlation.Tests.cs | 4 --- tests/indicators/a-d/Dema/Dema.Tests.cs | 4 --- tests/indicators/a-d/Doji/Doji.Tests.cs | 4 --- .../indicators/a-d/Donchian/Donchian.Tests.cs | 4 --- tests/indicators/a-d/Dpo/Dpo.Tests.cs | 4 --- tests/indicators/a-d/Dynamic/Dynamic.Tests.cs | 4 --- .../indicators/e-k/ElderRay/ElderRay.Tests.cs | 4 --- tests/indicators/e-k/Ema/Ema.Static.Tests.cs | 4 --- tests/indicators/e-k/Epma/Epma.Tests.cs | 4 --- tests/indicators/e-k/Fcb/Fcb.Tests.cs | 4 --- .../FisherTransform/FisherTransform.Tests.cs | 4 --- .../e-k/ForceIndex/ForceIndex.Tests.cs | 4 --- tests/indicators/e-k/Fractal/Fractal.Tests.cs | 4 --- tests/indicators/e-k/Gator/Gator.Tests.cs | 4 --- .../e-k/HeikinAshi/HeikinAshi.Tests.cs | 4 --- tests/indicators/e-k/Hma/Hma.Tests.cs | 4 --- .../e-k/HtTrendline/HtTrendline.Tests.cs | 4 --- tests/indicators/e-k/Hurst/Hurst.Tests.cs | 4 --- .../indicators/e-k/Ichimoku/Ichimoku.Tests.cs | 4 --- tests/indicators/e-k/Kama/Kama.Tests.cs | 4 --- tests/indicators/e-k/Keltner/Keltner.Tests.cs | 4 --- tests/indicators/e-k/Kvo/Kvo.Tests.cs | 4 --- .../m-r/MaEnvelopes/MaEnvelopes.Tests.cs | 4 --- tests/indicators/m-r/Macd/Macd.Tests.cs | 4 --- tests/indicators/m-r/Mama/Mama.Tests.cs | 4 --- .../indicators/m-r/Marubozu/Marubozu.Tests.cs | 4 --- tests/indicators/m-r/Mfi/Mfi.Tests.cs | 4 --- tests/indicators/m-r/Obv/Obv.Tests.cs | 4 --- .../m-r/ParabolicSar/ParabolicSar.Tests.cs | 4 --- .../m-r/PivotPoints/PivotPoints.Tests.cs | 4 --- tests/indicators/m-r/Pivots/Pivots.Tests.cs | 4 --- tests/indicators/m-r/Pmo/Pmo.Tests.cs | 4 --- tests/indicators/m-r/Prs/Prs.Tests.cs | 4 --- tests/indicators/m-r/Pvo/Pvo.Tests.cs | 4 --- tests/indicators/m-r/Renko/Renko.Tests.cs | 4 --- tests/indicators/m-r/Roc/Roc.Tests.cs | 4 --- tests/indicators/m-r/RocWb/RocWb.Tests.cs | 4 --- .../m-r/RollingPivots/RollingPivots.Tests.cs | 4 --- tests/indicators/m-r/Rsi/Rsi.Tests.cs | 4 --- tests/indicators/s-z/Slope/Slope.Tests.cs | 4 --- .../indicators/s-z/Sma/Sma.Analysis.Tests.cs | 4 --- tests/indicators/s-z/Sma/Sma.Static.Tests.cs | 4 --- tests/indicators/s-z/Smi/Smi.Tests.cs | 4 --- tests/indicators/s-z/Smma/Smma.Tests.cs | 4 --- .../s-z/StarcBands/StarcBands.Tests.cs | 4 --- tests/indicators/s-z/Stc/Stc.Tests.cs | 4 --- tests/indicators/s-z/StdDev/StdDev.Tests.cs | 4 --- .../StdDevChannels/StdDevChannels.Tests.cs | 4 --- tests/indicators/s-z/Stoch/Stoch.Tests.cs | 4 --- .../indicators/s-z/StochRsi/StochRsi.Tests.cs | 4 --- .../s-z/SuperTrend/SuperTrend.Tests.cs | 4 --- tests/indicators/s-z/T3/T3.Tests.cs | 4 --- tests/indicators/s-z/Tema/Tema.Tests.cs | 4 --- tests/indicators/s-z/Tr/Tr.Tests.cs | 4 --- tests/indicators/s-z/Trix/Trix.Tests.cs | 4 --- tests/indicators/s-z/Tsi/Tsi.Tests.cs | 4 --- .../s-z/UlcerIndex/UlcerIndex.Tests.cs | 4 --- .../indicators/s-z/Ultimate/Ultimate.Tests.cs | 4 --- .../VolatilityStop/VolatilityStop.Tests.cs | 4 --- tests/indicators/s-z/Vortex/Vortex.Tests.cs | 4 --- tests/indicators/s-z/Vwap/Vwap.Tests.cs | 4 --- tests/indicators/s-z/Vwma/Vwma.Tests.cs | 4 --- .../s-z/WilliamsR/WilliamsR.Tests.cs | 4 --- tests/indicators/s-z/Wma/Wma.Tests.cs | 4 --- tests/indicators/s-z/ZigZag/ZigZag.Tests.cs | 3 -- tests/observe/Program.cs | 10 +++---- tests/other/Convergence.Tests.cs | 4 --- tests/other/CustomIndicator.Tests.cs | 9 ++---- tests/other/GlobalUsings.cs | 3 ++ tests/other/PublicApi.Tests.cs | 3 -- tests/performance/GlobalUsings.cs | 3 ++ tests/performance/Perf.Helpers.cs | 4 --- tests/performance/Perf.Internals.cs | 11 ++----- tests/performance/Program.cs | 1 + tests/performance/Tests.Performance.csproj | 4 +-- 140 files changed, 103 insertions(+), 507 deletions(-) create mode 100644 tests/indicators/GlobalUsings.cs create mode 100644 tests/other/GlobalUsings.cs create mode 100644 tests/performance/GlobalUsings.cs diff --git a/.editorconfig b/.editorconfig index a8b773fed..1c1b1008f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,6 +3,7 @@ charset = utf-8 indent_style = space indent_size = 2 trim_trailing_whitespace = true +insert_final_newline = true [*.md] max_line_length = 150 @@ -15,7 +16,9 @@ max_line_length = 150 csharp_indent_block_contents = true # IDE0161 Convert to file-scoped namespace -csharp_style_namespace_declarations= file_scoped:suggestion +csharp_style_namespace_declarations = file_scoped:suggestion +csharp_using_directive_placement = outside_namespace:warning +dotnet_style_namespace_match_folder = false:none # CA1051: Do not declare visible instance fields dotnet_diagnostic.CA1051.severity = silent @@ -25,7 +28,7 @@ dotnet_diagnostic.CA1303.severity = none # CA1720: Identifier contains type name dotnet_diagnostic.CA1720.severity = silent -dotnet_diagnostic.IDE0090.severity= suggestion +dotnet_diagnostic.IDE0090.severity = suggestion dotnet_diagnostic.SA1413.severity = silent dotnet_diagnostic.SA1200.severity = silent @@ -49,7 +52,6 @@ dotnet_diagnostic.SA1117.severity = none # SA1118: Parameter should not span multiple lines dotnet_diagnostic.SA1118.severity = silent -csharp_using_directive_placement = outside_namespace:warning # SA1202: Elements should be ordered by access dotnet_diagnostic.SA1202.severity = silent @@ -79,15 +81,15 @@ dotnet_diagnostic.SA1602.severity = silent dotnet_diagnostic.SA1633.severity = none # Misc IDE -dotnet_diagnostic.IDE0055.severity=suggestion -dotnet_diagnostic.IDE0059.severity=suggestion -dotnet_diagnostic.IDE0060.severity=suggestion -dotnet_diagnostic.IDE0062.severity=suggestion -dotnet_diagnostic.IDE0063.severity=suggestion -dotnet_diagnostic.IDE0065.severity=suggestion -dotnet_diagnostic.IDE0066.severity=suggestion -dotnet_diagnostic.IDE0071.severity=suggestion -dotnet_diagnostic.IDE0047.severity=suggestion +dotnet_diagnostic.IDE0055.severity = suggestion +dotnet_diagnostic.IDE0059.severity = suggestion +dotnet_diagnostic.IDE0060.severity = suggestion +dotnet_diagnostic.IDE0062.severity = suggestion +dotnet_diagnostic.IDE0063.severity = suggestion +dotnet_diagnostic.IDE0065.severity = suggestion +dotnet_diagnostic.IDE0066.severity = suggestion +dotnet_diagnostic.IDE0071.severity = suggestion +dotnet_diagnostic.IDE0047.severity = suggestion csharp_indent_labels = no_change csharp_prefer_simple_using_statement = true:suggestion csharp_prefer_braces = true:suggestion @@ -137,6 +139,8 @@ csharp_style_prefer_primary_constructors = true:suggestion # IDE0058: Expression value is never used dotnet_diagnostic.IDE0058.severity = none +dotnet_diagnostic.IDE0077.severity = warning +dotnet_diagnostic.IDE0100.severity = suggestion [*.{cs,vb}] tab_width = 4 @@ -216,6 +220,5 @@ dotnet_style_qualification_for_field = false:silent dotnet_style_qualification_for_property = false:silent dotnet_style_qualification_for_method = false:silent dotnet_style_qualification_for_event = false:silent -dotnet_style_namespace_match_folder= false:silent dotnet_style_allow_multiple_blank_lines_experimental = false:suggestion dotnet_style_allow_statement_immediately_after_block_experimental = false:suggestion diff --git a/.github/workflows/build-indicators.yml b/.github/workflows/build-indicators.yml index a763d4cde..04f3137d3 100644 --- a/.github/workflows/build-indicators.yml +++ b/.github/workflows/build-indicators.yml @@ -62,7 +62,7 @@ jobs: --logger trx --results-directory ./test-other - - name: Update tests summary + - name: Post tests summary uses: bibipkins/dotnet-test-reporter@v1.3.3 if: ${{ github.event_name == 'pull_request' && (success() || (failure() && (steps.test-library.conclusion == 'failure' || steps.test-other.conclusion == 'failure'))) }} with: @@ -72,3 +72,11 @@ jobs: coverage-path: ./test-indicators/**/coverage.cobertura.xml coverage-type: cobertura coverage-threshold: 95 + + - name: Post coverage to Codacy + uses: codacy/codacy-coverage-reporter-action@v1 + with: + project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + coverage-reports: ./test-indicators/**/coverage.cobertura.xml + # or a comma-separated list for multiple reports + # coverage-reports: , diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 05ceb88fa..f6b1a19d1 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -3,7 +3,7 @@ name: Deploy website on: [workflow_dispatch] concurrency: - group: cloudflare-pages + group: docs-website env: JEKYLL_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest environment: - name: cloudflare-pages + name: docs-website steps: - name: Checkout source diff --git a/.github/workflows/semantic-pr-linter.yml b/.github/workflows/semantic-pr-linter.yml index c98c1bca8..a4113f57f 100644 --- a/.github/workflows/semantic-pr-linter.yml +++ b/.github/workflows/semantic-pr-linter.yml @@ -12,7 +12,7 @@ permissions: jobs: main: - name: Validate PR title + name: validate PR title runs-on: ubuntu-latest steps: - uses: amannn/action-semantic-pull-request@v5 @@ -52,12 +52,12 @@ jobs: It looks like your proposed **_Pull request title_** needs to be adjusted. - >🚩 **ERROR:** ${{ steps.lint_pr_title.outputs.error_message }} + >🚩 **Error** » ${{ steps.lint_pr_title.outputs.error_message }} #### Pull request title naming convention Our PR title name taxonomy is `type: Subject`, where **type** is typically - *feat*, *fix*, or *chore* and **subject** is a noun that starts with a capitalized letter. + *feat*, *fix*, or *chore*, and **subject** is a phrase (proper noun) that starts with a capitalized letter. The *chore* type usually has a subject that starts with an action verb like *Add* or *Update*. Examples: `feat: Admin portal login`, `fix: Divide by zero bug in SMA`, and `chore: Update user docs`. See the [Conventional Commits specification](https://www.conventionalcommits.org) for more information. diff --git a/docs/GemFile.lock b/docs/GemFile.lock index eaea2c8b8..e85c82d9d 100644 --- a/docs/GemFile.lock +++ b/docs/GemFile.lock @@ -18,10 +18,10 @@ GEM minitest (>= 5.1) mutex_m tzinfo (~> 2.0) - addressable (2.8.5) + addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) base64 (0.2.0) - bigdecimal (3.1.4) + bigdecimal (3.1.5) coffee-script (2.4.1) coffee-script-source execjs @@ -41,7 +41,7 @@ GEM ethon (0.16.0) ffi (>= 1.15.0) execjs (2.9.1) - faraday (2.7.11) + faraday (2.8.1) base64 faraday-net_http (>= 2.0, < 3.1) ruby2_keywords (>= 0.0.4) @@ -227,7 +227,7 @@ GEM gemoji (~> 3.0) html-pipeline (~> 2.2) jekyll (>= 3.0, < 5.0) - json (2.6.3) + json (2.7.1) json-minify (0.0.3) json (> 0) kramdown (2.3.2) @@ -245,9 +245,9 @@ GEM jekyll-seo-tag (~> 2.1) minitest (5.20.0) mutex_m (0.2.0) - nokogiri (1.15.4-x64-mingw-ucrt) + nokogiri (1.15.5-x64-mingw-ucrt) racc (~> 1.4) - nokogiri (1.15.4-x86_64-linux) + nokogiri (1.15.5-x86_64-linux) racc (~> 1.4) octokit (4.25.1) faraday (>= 1, < 3) @@ -277,7 +277,7 @@ GEM unf (~> 0.1.4) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - typhoeus (1.4.0) + typhoeus (1.4.1) ethon (>= 0.9.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) @@ -285,8 +285,8 @@ GEM execjs (>= 0.3.0, < 3) unf (0.1.4) unf_ext - unf_ext (0.0.9) - unf_ext (0.0.9-x64-mingw-ucrt) + unf_ext (0.0.9.1) + unf_ext (0.0.9.1-x64-mingw-ucrt) unicode-display_width (1.8.0) wdm (0.1.1) webrick (1.8.1) diff --git a/src/GlobalSuppressions.cs b/src/GlobalSuppressions.cs index 456f0a6d4..bd9f0b667 100644 --- a/src/GlobalSuppressions.cs +++ b/src/GlobalSuppressions.cs @@ -1,8 +1,3 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage( @@ -68,4 +63,4 @@ "Naming", "CA1716:Identifiers should not match keywords", Justification = "The microsoft OnError implementation uses reserved word Error", - Scope = "member", Target = "~M:Skender.Stock.Indicators.TupleObserver.OnError(System.Exception)")] \ No newline at end of file + Scope = "member", Target = "~M:Skender.Stock.Indicators.TupleObserver.OnError(System.Exception)")] diff --git a/src/Indicators.csproj b/src/Indicators.csproj index ae189d86d..3c9961ff0 100644 --- a/src/Indicators.csproj +++ b/src/Indicators.csproj @@ -36,7 +36,6 @@ README.md https://github.com/DaveSkender/Stock.Indicators/releases Apache-2.0 - icon.png true @@ -60,19 +59,15 @@ True - True - True - True - diff --git a/src/_common/Enums.cs b/src/_common/Enums.cs index 300efadef..ba68801c3 100644 --- a/src/_common/Enums.cs +++ b/src/_common/Enums.cs @@ -72,4 +72,4 @@ public enum SyncType AppendOnly, RemoveOnly, FullMatch -} \ No newline at end of file +} diff --git a/src/_common/Math/NullMath.cs b/src/_common/Math/NullMath.cs index 0d8bd539c..71aad8c1c 100644 --- a/src/_common/Math/NullMath.cs +++ b/src/_common/Math/NullMath.cs @@ -28,17 +28,15 @@ public static decimal Round(this decimal value, int digits) => Math.Round(value, digits); public static double Null2NaN(this double? value) - => (value is null) - ? double.NaN - : (double)value; + => value ?? double.NaN; public static double? NaN2Null(this double? value) - => (value is double and double.NaN) + => (value is not null and double.NaN) ? null : value; public static double? NaN2Null(this double value) - => (value is double and double.NaN) + => double.IsNaN(value) ? null : value; } diff --git a/src/_common/Math/Numerix.cs b/src/_common/Math/Numerix.cs index 559125598..6d0e98ad2 100644 --- a/src/_common/Math/Numerix.cs +++ b/src/_common/Math/Numerix.cs @@ -72,8 +72,7 @@ public static double Slope(double[] x, double[] y) // least squares method double sumSqX = 0; - double sumSqY = 0; - double sumSqXY = 0; + double sumSqXy = 0; for (int i = 0; i < length; i++) { @@ -81,11 +80,10 @@ public static double Slope(double[] x, double[] y) double devY = y[i] - avgY; sumSqX += devX * devX; - sumSqY += devY * devY; - sumSqXY += devX * devY; + sumSqXy += devX * devY; } - double slope = sumSqXY / sumSqX; + double slope = sumSqXy / sumSqX; return slope; } diff --git a/src/_common/Observables/ChainProvider.cs b/src/_common/Observables/ChainProvider.cs index de73cfdbc..85c2b3af4 100644 --- a/src/_common/Observables/ChainProvider.cs +++ b/src/_common/Observables/ChainProvider.cs @@ -11,8 +11,8 @@ public abstract class ChainProvider // initialize protected ChainProvider() { - observers = new(); - ProtectedChain = new(); + observers = []; + ProtectedChain = []; Warmup = true; } diff --git a/src/_common/Observables/QuoteProvider.cs b/src/_common/Observables/QuoteProvider.cs index 6f9d4c01c..9f55669fb 100644 --- a/src/_common/Observables/QuoteProvider.cs +++ b/src/_common/Observables/QuoteProvider.cs @@ -10,8 +10,8 @@ public class QuoteProvider : IObservable // initialize public QuoteProvider() { - observers = new(); - ProtectedQuotes = new(); + observers = []; + ProtectedQuotes = []; } // properties diff --git a/src/_common/Observables/TupleProvider.cs b/src/_common/Observables/TupleProvider.cs index 38edc7ab7..3304496c6 100644 --- a/src/_common/Observables/TupleProvider.cs +++ b/src/_common/Observables/TupleProvider.cs @@ -11,8 +11,8 @@ public abstract class TupleProvider // initialize protected TupleProvider() { - observers = new(); - ProtectedTuples = new(); + observers = []; + ProtectedTuples = []; } // properties diff --git a/src/_common/Quotes/Quote.Aggregates.cs b/src/_common/Quotes/Quote.Aggregates.cs index 821d4ea7e..e917c9af8 100644 --- a/src/_common/Quotes/Quote.Aggregates.cs +++ b/src/_common/Quotes/Quote.Aggregates.cs @@ -45,12 +45,6 @@ public static IEnumerable Aggregate( TimeSpan timeSpan) where TQuote : IQuote { - // handle no quotes scenario - if (quotes == null || !quotes.Any()) - { - return new List(); - } - if (timeSpan <= TimeSpan.Zero) { throw new ArgumentOutOfRangeException(nameof(timeSpan), timeSpan, diff --git a/src/_common/Quotes/Quote.Exceptions.cs b/src/_common/Quotes/Quote.Exceptions.cs index 07f3a7a08..23cec65b1 100644 --- a/src/_common/Quotes/Quote.Exceptions.cs +++ b/src/_common/Quotes/Quote.Exceptions.cs @@ -1,6 +1,3 @@ -using System.Diagnostics.CodeAnalysis; -using System.Runtime.Serialization; - namespace Skender.Stock.Indicators; public class InvalidQuotesException : ArgumentOutOfRangeException diff --git a/src/_common/Quotes/Quote.Validation.cs b/src/_common/Quotes/Quote.Validation.cs index f709741e2..16d256d9d 100644 --- a/src/_common/Quotes/Quote.Validation.cs +++ b/src/_common/Quotes/Quote.Validation.cs @@ -17,10 +17,8 @@ public static IEnumerable Validate( // check for duplicates DateTime lastDate = DateTime.MinValue; - for (int i = 0; i < quotesList.Count; i++) + foreach (var q in quotesList) { - TQuote q = quotesList[i]; - if (lastDate == q.Date) { throw new InvalidQuotesException( diff --git a/src/_common/Results/Result.Utilities.cs b/src/_common/Results/Result.Utilities.cs index 03a57743b..eca3f3ac3 100644 --- a/src/_common/Results/Result.Utilities.cs +++ b/src/_common/Results/Result.Utilities.cs @@ -18,7 +18,7 @@ public static IEnumerable Condense( resultsList .RemoveAll(match: - x => x.Value is null or (double and double.NaN)); + x => x.Value is null or (not null and double.NaN)); return resultsList.ToSortedList(); } @@ -45,7 +45,7 @@ public static IEnumerable Condense( for (int i = first; i < reList.Count; i++) { - IReusableResult? r = reList[i]; + IReusableResult r = reList[i]; prices.Add(new(r.Date, r.Value.Null2NaN())); } diff --git a/src/_common/Results/info.xml b/src/_common/Results/info.xml index e78cd58a0..11ad40769 100644 --- a/src/_common/Results/info.xml +++ b/src/_common/Results/info.xml @@ -21,7 +21,7 @@ Any reusable result type. Indicator results to evaluate. Collection of tuple time series of - results with specified handline of nulls, without pruning. + results with specified handling of nulls, without pruning. Removes the recommended quantity of results from the beginning of the results list diff --git a/src/a-d/Dpo/Dpo.Api.cs b/src/a-d/Dpo/Dpo.Api.cs index 3e6b798a8..83b245ea6 100644 --- a/src/a-d/Dpo/Dpo.Api.cs +++ b/src/a-d/Dpo/Dpo.Api.cs @@ -27,4 +27,4 @@ public static IEnumerable GetDpo( int lookbackPeriods) => priceTuples .ToSortedList() .CalcDpo(lookbackPeriods); -} \ No newline at end of file +} diff --git a/src/e-k/Ema/Ema.Observer.cs b/src/e-k/Ema/Ema.Observer.cs index 0ad2e7ba3..88367dcc9 100644 --- a/src/e-k/Ema/Ema.Observer.cs +++ b/src/e-k/Ema/Ema.Observer.cs @@ -9,7 +9,7 @@ public EmaObserver( int lookbackPeriods) { Supplier = provider; - ProtectedResults = new(); + ProtectedResults = []; LookbackPeriods = lookbackPeriods; K = 2d / (lookbackPeriods + 1); diff --git a/src/e-k/Epma/Epma.Api.cs b/src/e-k/Epma/Epma.Api.cs index 26fec2ffd..b999b8a4f 100644 --- a/src/e-k/Epma/Epma.Api.cs +++ b/src/e-k/Epma/Epma.Api.cs @@ -27,4 +27,4 @@ public static IEnumerable GetEpma( int lookbackPeriods) => priceTuples .ToSortedList() .CalcEpma(lookbackPeriods); -} \ No newline at end of file +} diff --git a/src/e-k/Hma/Hma.Api.cs b/src/e-k/Hma/Hma.Api.cs index 5b71e9c34..c60107ee4 100644 --- a/src/e-k/Hma/Hma.Api.cs +++ b/src/e-k/Hma/Hma.Api.cs @@ -27,4 +27,4 @@ public static IEnumerable GetHma( int lookbackPeriods) => priceTuples .ToSortedList() .CalcHma(lookbackPeriods); -} \ No newline at end of file +} diff --git a/src/m-r/ParabolicSar/ParabolicSar.Series.cs b/src/m-r/ParabolicSar/ParabolicSar.Series.cs index f4c2b69d7..9992e5ad4 100644 --- a/src/m-r/ParabolicSar/ParabolicSar.Series.cs +++ b/src/m-r/ParabolicSar/ParabolicSar.Series.cs @@ -35,7 +35,7 @@ internal static List CalcParabolicSar( // roll through quotes for (int i = 0; i < length; i++) { - QuoteD? q = qdList[i]; + QuoteD q = qdList[i]; ParabolicSarResult r = new(q.Date); results.Add(r); diff --git a/src/m-r/PivotPoints/PivotPoints.Series.cs b/src/m-r/PivotPoints/PivotPoints.Series.cs index a36f542a2..b8223c62c 100644 --- a/src/m-r/PivotPoints/PivotPoints.Series.cs +++ b/src/m-r/PivotPoints/PivotPoints.Series.cs @@ -196,7 +196,7 @@ internal static TPivotPoint GetPivotPointWoodie( } // pivot type lookup - internal static TPivotPoint? GetPivotPoint( + internal static TPivotPoint GetPivotPoint( PivotPointType pointType, decimal open, decimal high, decimal low, decimal close) where TPivotPoint : IPivotPoint, new() => pointType switch diff --git a/src/m-r/RollingPivots/RollingPivots.Series.cs b/src/m-r/RollingPivots/RollingPivots.Series.cs index 15df8dc30..06efcc09e 100644 --- a/src/m-r/RollingPivots/RollingPivots.Series.cs +++ b/src/m-r/RollingPivots/RollingPivots.Series.cs @@ -45,18 +45,18 @@ internal static List CalcRollingPivots( } // pivot points - RollingPivotsResult? wp = GetPivotPoint( + RollingPivotsResult wp = GetPivotPoint( pointType, q.Open, windowHigh, windowLow, windowClose); - r.PP = wp?.PP; - r.S1 = wp?.S1; - r.S2 = wp?.S2; - r.S3 = wp?.S3; - r.S4 = wp?.S4; - r.R1 = wp?.R1; - r.R2 = wp?.R2; - r.R3 = wp?.R3; - r.R4 = wp?.R4; + r.PP = wp.PP; + r.S1 = wp.S1; + r.S2 = wp.S2; + r.S3 = wp.S3; + r.S4 = wp.S4; + r.R1 = wp.R1; + r.R2 = wp.R2; + r.R3 = wp.R3; + r.R4 = wp.R4; } results.Add(r); diff --git a/src/s-z/Sma/Sma.Analysis.cs b/src/s-z/Sma/Sma.Analysis.cs index 304bff964..a4cddf4ee 100644 --- a/src/s-z/Sma/Sma.Analysis.cs +++ b/src/s-z/Sma/Sma.Analysis.cs @@ -8,7 +8,7 @@ internal static IEnumerable CalcSmaAnalysis( int lookbackPeriods) { // initialize - List? results = tpList + List results = tpList .CalcSma(lookbackPeriods) .Select(x => new SmaAnalysis(x.Date) { Sma = x.Sma }) .ToList(); diff --git a/src/s-z/Sma/Sma.Observer.cs b/src/s-z/Sma/Sma.Observer.cs index cc496dd92..f0227f4cd 100644 --- a/src/s-z/Sma/Sma.Observer.cs +++ b/src/s-z/Sma/Sma.Observer.cs @@ -9,7 +9,7 @@ public SmaObserver( int lookbackPeriods) { Supplier = provider; - ProtectedResults = new(); + ProtectedResults = []; LookbackPeriods = lookbackPeriods; @@ -65,17 +65,12 @@ internal static double Increment( // add new tuple quote internal void Add((DateTime Date, double Value) tp) { - if (Supplier == null) - { - throw new ArgumentNullException(nameof(Supplier), "Could not find data source."); - } - // candidate result SmaResult r = new(tp.Date); // initialize int lengthRes = ProtectedResults.Count; - int lengthSrc = Supplier.ProtectedTuples.Count; + int lengthSrc = Supplier!.ProtectedTuples.Count; // merge fix, okay to replace // handle first value if (lengthRes == 0) diff --git a/src/s-z/Tema/Tema.Api.cs b/src/s-z/Tema/Tema.Api.cs index d7181c3e3..1955004f3 100644 --- a/src/s-z/Tema/Tema.Api.cs +++ b/src/s-z/Tema/Tema.Api.cs @@ -27,4 +27,4 @@ public static IEnumerable GetTema( int lookbackPeriods) => priceTuples .ToSortedList() .CalcTema(lookbackPeriods); -} \ No newline at end of file +} diff --git a/tests/indicators/GlobalUsings.cs b/tests/indicators/GlobalUsings.cs new file mode 100644 index 000000000..211ba4da6 --- /dev/null +++ b/tests/indicators/GlobalUsings.cs @@ -0,0 +1,3 @@ +global using Microsoft.VisualStudio.TestTools.UnitTesting; +global using Skender.Stock.Indicators; +global using Tests.Common; diff --git a/tests/indicators/_Initialize.cs b/tests/indicators/_Initialize.cs index 13531ce50..8fdecf061 100644 --- a/tests/indicators/_Initialize.cs +++ b/tests/indicators/_Initialize.cs @@ -1,7 +1,5 @@ using System.Globalization; using System.Runtime.CompilerServices; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; // GLOBALS & INITIALIZATION OF TEST DATA diff --git a/tests/indicators/_common/Candles/Candles.Tests.cs b/tests/indicators/_common/Candles/Candles.Tests.cs index f0448fdf9..2f5dbf94e 100644 --- a/tests/indicators/_common/Candles/Candles.Tests.cs +++ b/tests/indicators/_common/Candles/Candles.Tests.cs @@ -1,6 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; - namespace Tests.Common; [TestClass] diff --git a/tests/indicators/_common/Generics/Pruning.Tests.cs b/tests/indicators/_common/Generics/Pruning.Tests.cs index a4df09de7..5edd0265c 100644 --- a/tests/indicators/_common/Generics/Pruning.Tests.cs +++ b/tests/indicators/_common/Generics/Pruning.Tests.cs @@ -1,6 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; - namespace Tests.Common; [TestClass] diff --git a/tests/indicators/_common/Generics/Seek.Tests.cs b/tests/indicators/_common/Generics/Seek.Tests.cs index 5c0b5e7f1..cb312d597 100644 --- a/tests/indicators/_common/Generics/Seek.Tests.cs +++ b/tests/indicators/_common/Generics/Seek.Tests.cs @@ -1,6 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; - namespace Tests.Common; [TestClass] diff --git a/tests/indicators/_common/Generics/Sort.Tests.cs b/tests/indicators/_common/Generics/Sort.Tests.cs index 6463ec7dc..9445c0426 100644 --- a/tests/indicators/_common/Generics/Sort.Tests.cs +++ b/tests/indicators/_common/Generics/Sort.Tests.cs @@ -1,6 +1,4 @@ using System.Collections.ObjectModel; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; namespace Tests.Common; diff --git a/tests/indicators/_common/Generics/Transforms.Tests.cs b/tests/indicators/_common/Generics/Transforms.Tests.cs index 9b1574dc3..8c398518f 100644 --- a/tests/indicators/_common/Generics/Transforms.Tests.cs +++ b/tests/indicators/_common/Generics/Transforms.Tests.cs @@ -1,6 +1,4 @@ using System.Collections.ObjectModel; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; namespace Tests.Common; diff --git a/tests/indicators/_common/Helper.Getter.cs b/tests/indicators/_common/Helper.Getter.cs index 95e8b99bb..7a2daa11a 100644 --- a/tests/indicators/_common/Helper.Getter.cs +++ b/tests/indicators/_common/Helper.Getter.cs @@ -1,5 +1,3 @@ -using Skender.Stock.Indicators; - namespace Tests.Common; // IMPORT TEST DATA diff --git a/tests/indicators/_common/Helper.Importer.cs b/tests/indicators/_common/Helper.Importer.cs index 1bf34f402..b88006ca4 100644 --- a/tests/indicators/_common/Helper.Importer.cs +++ b/tests/indicators/_common/Helper.Importer.cs @@ -1,5 +1,4 @@ using System.Globalization; -using Skender.Stock.Indicators; namespace Tests.Common; @@ -30,7 +29,9 @@ internal static Quote QuoteFromCsv(string csvLine) } internal static decimal ToDecimal(this string value) - => decimal.TryParse(value, out decimal d) ? d : d; + => decimal.TryParse(value, out decimal d) ? d + : throw new NotFiniteNumberException( + $"Cannot convert `{value}`, it is not a number."); internal static decimal? ToDecimalNull(this string value) => decimal.TryParse(value, out decimal d) ? d : null; diff --git a/tests/indicators/_common/Helper.LiveQuotes.cs b/tests/indicators/_common/Helper.LiveQuotes.cs index 37bab744a..0f8a9278d 100644 --- a/tests/indicators/_common/Helper.LiveQuotes.cs +++ b/tests/indicators/_common/Helper.LiveQuotes.cs @@ -1,6 +1,4 @@ using Alpaca.Markets; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; namespace Tests.Indicators; diff --git a/tests/indicators/_common/Helper.Random.cs b/tests/indicators/_common/Helper.Random.cs index d2d08b533..0e238dcfb 100644 --- a/tests/indicators/_common/Helper.Random.cs +++ b/tests/indicators/_common/Helper.Random.cs @@ -1,5 +1,3 @@ -using Skender.Stock.Indicators; - namespace Tests.Common; /** @@ -77,4 +75,4 @@ private static double Price(double seed, double volatility, double drift) double z = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); return seed * Math.Exp(drift - (volatility * volatility * 0.5) + (volatility * z)); } -} \ No newline at end of file +} diff --git a/tests/indicators/_common/Math/Numerix.Tests.cs b/tests/indicators/_common/Math/Numerix.Tests.cs index 931ff8c67..a8cc81498 100644 --- a/tests/indicators/_common/Math/Numerix.Tests.cs +++ b/tests/indicators/_common/Math/Numerix.Tests.cs @@ -1,6 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; - namespace Tests.Common; [TestClass] diff --git a/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs b/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs index 0d676b381..44a79bd47 100644 --- a/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs @@ -1,6 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; - namespace Tests.Common; [TestClass] diff --git a/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs b/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs index 37f7db72f..f4dc08592 100644 --- a/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs @@ -1,6 +1,4 @@ using System.Collections.ObjectModel; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; namespace Tests.Common; diff --git a/tests/indicators/_common/Quotes/Quote.Exceptions.Tests.cs b/tests/indicators/_common/Quotes/Quote.Exceptions.Tests.cs index 5a0e4f09e..5a546b482 100644 --- a/tests/indicators/_common/Quotes/Quote.Exceptions.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Exceptions.Tests.cs @@ -1,6 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; - namespace Tests.Common; [TestClass] diff --git a/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs b/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs index bdb28cefd..e9b0b716f 100644 --- a/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs @@ -1,6 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; - namespace Tests.Common; [TestClass] diff --git a/tests/indicators/_common/Results/Result.Syncing.Tests.cs b/tests/indicators/_common/Results/Result.Syncing.Tests.cs index 294648b7b..e22906cdb 100644 --- a/tests/indicators/_common/Results/Result.Syncing.Tests.cs +++ b/tests/indicators/_common/Results/Result.Syncing.Tests.cs @@ -1,6 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; - namespace Tests.Common; [TestClass] diff --git a/tests/indicators/_common/Results/Result.Utilities.Tests.cs b/tests/indicators/_common/Results/Result.Utilities.Tests.cs index d50914f62..555386720 100644 --- a/tests/indicators/_common/Results/Result.Utilities.Tests.cs +++ b/tests/indicators/_common/Results/Result.Utilities.Tests.cs @@ -1,6 +1,4 @@ using System.Collections.ObjectModel; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; namespace Tests.Common; diff --git a/tests/indicators/a-d/Adl/Adl.Tests.cs b/tests/indicators/a-d/Adl/Adl.Tests.cs index a0a21f4b0..3130641c5 100644 --- a/tests/indicators/a-d/Adl/Adl.Tests.cs +++ b/tests/indicators/a-d/Adl/Adl.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Adx/Adx.Tests.cs b/tests/indicators/a-d/Adx/Adx.Tests.cs index c13d61191..81a4c4805 100644 --- a/tests/indicators/a-d/Adx/Adx.Tests.cs +++ b/tests/indicators/a-d/Adx/Adx.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Alligator/Alligator.Tests.cs b/tests/indicators/a-d/Alligator/Alligator.Tests.cs index 165e22c6c..f74aaec15 100644 --- a/tests/indicators/a-d/Alligator/Alligator.Tests.cs +++ b/tests/indicators/a-d/Alligator/Alligator.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Alma/Alma.Tests.cs b/tests/indicators/a-d/Alma/Alma.Tests.cs index d18191229..e855c5b6f 100644 --- a/tests/indicators/a-d/Alma/Alma.Tests.cs +++ b/tests/indicators/a-d/Alma/Alma.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Aroon/Aroon.Tests.cs b/tests/indicators/a-d/Aroon/Aroon.Tests.cs index d14a1cdbe..50fe8f8e5 100644 --- a/tests/indicators/a-d/Aroon/Aroon.Tests.cs +++ b/tests/indicators/a-d/Aroon/Aroon.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Atr/Atr.Tests.cs b/tests/indicators/a-d/Atr/Atr.Tests.cs index cdf363039..23267d3ea 100644 --- a/tests/indicators/a-d/Atr/Atr.Tests.cs +++ b/tests/indicators/a-d/Atr/Atr.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/AtrStop/AtrStop.Tests.cs b/tests/indicators/a-d/AtrStop/AtrStop.Tests.cs index 63df178a5..7d53fe6c5 100644 --- a/tests/indicators/a-d/AtrStop/AtrStop.Tests.cs +++ b/tests/indicators/a-d/AtrStop/AtrStop.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Awesome/Awesome.Tests.cs b/tests/indicators/a-d/Awesome/Awesome.Tests.cs index f856512b4..eab128bcc 100644 --- a/tests/indicators/a-d/Awesome/Awesome.Tests.cs +++ b/tests/indicators/a-d/Awesome/Awesome.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/BasicQuote/BasicQuote.Tests.cs b/tests/indicators/a-d/BasicQuote/BasicQuote.Tests.cs index 9be170863..45a8472a1 100644 --- a/tests/indicators/a-d/BasicQuote/BasicQuote.Tests.cs +++ b/tests/indicators/a-d/BasicQuote/BasicQuote.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Beta/Beta.Tests.cs b/tests/indicators/a-d/Beta/Beta.Tests.cs index d749bd124..67229ba06 100644 --- a/tests/indicators/a-d/Beta/Beta.Tests.cs +++ b/tests/indicators/a-d/Beta/Beta.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/BollingerBands/BollingerBands.Tests.cs b/tests/indicators/a-d/BollingerBands/BollingerBands.Tests.cs index 155eab91b..abd4cecea 100644 --- a/tests/indicators/a-d/BollingerBands/BollingerBands.Tests.cs +++ b/tests/indicators/a-d/BollingerBands/BollingerBands.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Bop/Bop.Tests.cs b/tests/indicators/a-d/Bop/Bop.Tests.cs index 6f407bbf8..3f459108a 100644 --- a/tests/indicators/a-d/Bop/Bop.Tests.cs +++ b/tests/indicators/a-d/Bop/Bop.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Cci/Cci.Tests.cs b/tests/indicators/a-d/Cci/Cci.Tests.cs index 89b1f1895..1154e6fb6 100644 --- a/tests/indicators/a-d/Cci/Cci.Tests.cs +++ b/tests/indicators/a-d/Cci/Cci.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/ChaikinOsc/ChaikinOsc.Tests.cs b/tests/indicators/a-d/ChaikinOsc/ChaikinOsc.Tests.cs index 365a2d3b9..89faa7093 100644 --- a/tests/indicators/a-d/ChaikinOsc/ChaikinOsc.Tests.cs +++ b/tests/indicators/a-d/ChaikinOsc/ChaikinOsc.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Chandelier/Chandelier.Tests.cs b/tests/indicators/a-d/Chandelier/Chandelier.Tests.cs index 09f6b543a..f100e2a7e 100644 --- a/tests/indicators/a-d/Chandelier/Chandelier.Tests.cs +++ b/tests/indicators/a-d/Chandelier/Chandelier.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Chop/Chop.Tests.cs b/tests/indicators/a-d/Chop/Chop.Tests.cs index fcd24efe0..cb316bf39 100644 --- a/tests/indicators/a-d/Chop/Chop.Tests.cs +++ b/tests/indicators/a-d/Chop/Chop.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Cmf/Cmf.Tests.cs b/tests/indicators/a-d/Cmf/Cmf.Tests.cs index 1580da26b..53d1e23fd 100644 --- a/tests/indicators/a-d/Cmf/Cmf.Tests.cs +++ b/tests/indicators/a-d/Cmf/Cmf.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Cmo/Cmo.Tests.cs b/tests/indicators/a-d/Cmo/Cmo.Tests.cs index d3d263821..52b1b82bc 100644 --- a/tests/indicators/a-d/Cmo/Cmo.Tests.cs +++ b/tests/indicators/a-d/Cmo/Cmo.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/ConnorsRsi/ConnorsRsi.Tests.cs b/tests/indicators/a-d/ConnorsRsi/ConnorsRsi.Tests.cs index 3c5902622..e28b2b9c2 100644 --- a/tests/indicators/a-d/ConnorsRsi/ConnorsRsi.Tests.cs +++ b/tests/indicators/a-d/ConnorsRsi/ConnorsRsi.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Correlation/Correlation.Tests.cs b/tests/indicators/a-d/Correlation/Correlation.Tests.cs index 9cb328e6a..53c60c0a3 100644 --- a/tests/indicators/a-d/Correlation/Correlation.Tests.cs +++ b/tests/indicators/a-d/Correlation/Correlation.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Dema/Dema.Tests.cs b/tests/indicators/a-d/Dema/Dema.Tests.cs index ad77f67af..b0a040b7c 100644 --- a/tests/indicators/a-d/Dema/Dema.Tests.cs +++ b/tests/indicators/a-d/Dema/Dema.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Doji/Doji.Tests.cs b/tests/indicators/a-d/Doji/Doji.Tests.cs index 721d61314..4e43d65b2 100644 --- a/tests/indicators/a-d/Doji/Doji.Tests.cs +++ b/tests/indicators/a-d/Doji/Doji.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Donchian/Donchian.Tests.cs b/tests/indicators/a-d/Donchian/Donchian.Tests.cs index 53146512a..d41056dad 100644 --- a/tests/indicators/a-d/Donchian/Donchian.Tests.cs +++ b/tests/indicators/a-d/Donchian/Donchian.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Dpo/Dpo.Tests.cs b/tests/indicators/a-d/Dpo/Dpo.Tests.cs index c89117e1f..2f897777b 100644 --- a/tests/indicators/a-d/Dpo/Dpo.Tests.cs +++ b/tests/indicators/a-d/Dpo/Dpo.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/a-d/Dynamic/Dynamic.Tests.cs b/tests/indicators/a-d/Dynamic/Dynamic.Tests.cs index 55fa529bf..a3fb16f0f 100644 --- a/tests/indicators/a-d/Dynamic/Dynamic.Tests.cs +++ b/tests/indicators/a-d/Dynamic/Dynamic.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/e-k/ElderRay/ElderRay.Tests.cs b/tests/indicators/e-k/ElderRay/ElderRay.Tests.cs index 887ce06c1..34af7b32b 100644 --- a/tests/indicators/e-k/ElderRay/ElderRay.Tests.cs +++ b/tests/indicators/e-k/ElderRay/ElderRay.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/e-k/Ema/Ema.Static.Tests.cs b/tests/indicators/e-k/Ema/Ema.Static.Tests.cs index bdc4e628e..7987bae6d 100644 --- a/tests/indicators/e-k/Ema/Ema.Static.Tests.cs +++ b/tests/indicators/e-k/Ema/Ema.Static.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/e-k/Epma/Epma.Tests.cs b/tests/indicators/e-k/Epma/Epma.Tests.cs index 9caa63004..5db2e5478 100644 --- a/tests/indicators/e-k/Epma/Epma.Tests.cs +++ b/tests/indicators/e-k/Epma/Epma.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/e-k/Fcb/Fcb.Tests.cs b/tests/indicators/e-k/Fcb/Fcb.Tests.cs index e63438dc7..ed51d3f43 100644 --- a/tests/indicators/e-k/Fcb/Fcb.Tests.cs +++ b/tests/indicators/e-k/Fcb/Fcb.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/e-k/FisherTransform/FisherTransform.Tests.cs b/tests/indicators/e-k/FisherTransform/FisherTransform.Tests.cs index b027c8454..5ea407a37 100644 --- a/tests/indicators/e-k/FisherTransform/FisherTransform.Tests.cs +++ b/tests/indicators/e-k/FisherTransform/FisherTransform.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/e-k/ForceIndex/ForceIndex.Tests.cs b/tests/indicators/e-k/ForceIndex/ForceIndex.Tests.cs index bbaf7bba6..0c6c37ea1 100644 --- a/tests/indicators/e-k/ForceIndex/ForceIndex.Tests.cs +++ b/tests/indicators/e-k/ForceIndex/ForceIndex.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/e-k/Fractal/Fractal.Tests.cs b/tests/indicators/e-k/Fractal/Fractal.Tests.cs index 2195c57fe..fcc7685f9 100644 --- a/tests/indicators/e-k/Fractal/Fractal.Tests.cs +++ b/tests/indicators/e-k/Fractal/Fractal.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/e-k/Gator/Gator.Tests.cs b/tests/indicators/e-k/Gator/Gator.Tests.cs index 4d373fa6d..bb7f44816 100644 --- a/tests/indicators/e-k/Gator/Gator.Tests.cs +++ b/tests/indicators/e-k/Gator/Gator.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/e-k/HeikinAshi/HeikinAshi.Tests.cs b/tests/indicators/e-k/HeikinAshi/HeikinAshi.Tests.cs index d6ed44963..cfff18c7a 100644 --- a/tests/indicators/e-k/HeikinAshi/HeikinAshi.Tests.cs +++ b/tests/indicators/e-k/HeikinAshi/HeikinAshi.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/e-k/Hma/Hma.Tests.cs b/tests/indicators/e-k/Hma/Hma.Tests.cs index aa73a258e..d773a8e69 100644 --- a/tests/indicators/e-k/Hma/Hma.Tests.cs +++ b/tests/indicators/e-k/Hma/Hma.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/e-k/HtTrendline/HtTrendline.Tests.cs b/tests/indicators/e-k/HtTrendline/HtTrendline.Tests.cs index a3784248e..c20807ea3 100644 --- a/tests/indicators/e-k/HtTrendline/HtTrendline.Tests.cs +++ b/tests/indicators/e-k/HtTrendline/HtTrendline.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/e-k/Hurst/Hurst.Tests.cs b/tests/indicators/e-k/Hurst/Hurst.Tests.cs index 9270a9b01..6cb517461 100644 --- a/tests/indicators/e-k/Hurst/Hurst.Tests.cs +++ b/tests/indicators/e-k/Hurst/Hurst.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/e-k/Ichimoku/Ichimoku.Tests.cs b/tests/indicators/e-k/Ichimoku/Ichimoku.Tests.cs index 4aabcbd37..6974c481e 100644 --- a/tests/indicators/e-k/Ichimoku/Ichimoku.Tests.cs +++ b/tests/indicators/e-k/Ichimoku/Ichimoku.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/e-k/Kama/Kama.Tests.cs b/tests/indicators/e-k/Kama/Kama.Tests.cs index 3466da638..3375f0fb4 100644 --- a/tests/indicators/e-k/Kama/Kama.Tests.cs +++ b/tests/indicators/e-k/Kama/Kama.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/e-k/Keltner/Keltner.Tests.cs b/tests/indicators/e-k/Keltner/Keltner.Tests.cs index 2e1220b7b..05c6e88e4 100644 --- a/tests/indicators/e-k/Keltner/Keltner.Tests.cs +++ b/tests/indicators/e-k/Keltner/Keltner.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/e-k/Kvo/Kvo.Tests.cs b/tests/indicators/e-k/Kvo/Kvo.Tests.cs index dec8f2bce..80e1c7869 100644 --- a/tests/indicators/e-k/Kvo/Kvo.Tests.cs +++ b/tests/indicators/e-k/Kvo/Kvo.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/MaEnvelopes/MaEnvelopes.Tests.cs b/tests/indicators/m-r/MaEnvelopes/MaEnvelopes.Tests.cs index a3287e877..d1da84130 100644 --- a/tests/indicators/m-r/MaEnvelopes/MaEnvelopes.Tests.cs +++ b/tests/indicators/m-r/MaEnvelopes/MaEnvelopes.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/Macd/Macd.Tests.cs b/tests/indicators/m-r/Macd/Macd.Tests.cs index d5c23781d..c4b135464 100644 --- a/tests/indicators/m-r/Macd/Macd.Tests.cs +++ b/tests/indicators/m-r/Macd/Macd.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/Mama/Mama.Tests.cs b/tests/indicators/m-r/Mama/Mama.Tests.cs index 2cc0573c6..114426172 100644 --- a/tests/indicators/m-r/Mama/Mama.Tests.cs +++ b/tests/indicators/m-r/Mama/Mama.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/Marubozu/Marubozu.Tests.cs b/tests/indicators/m-r/Marubozu/Marubozu.Tests.cs index c087da102..bd3f17a0c 100644 --- a/tests/indicators/m-r/Marubozu/Marubozu.Tests.cs +++ b/tests/indicators/m-r/Marubozu/Marubozu.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/Mfi/Mfi.Tests.cs b/tests/indicators/m-r/Mfi/Mfi.Tests.cs index a6bca6049..85ecf769a 100644 --- a/tests/indicators/m-r/Mfi/Mfi.Tests.cs +++ b/tests/indicators/m-r/Mfi/Mfi.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/Obv/Obv.Tests.cs b/tests/indicators/m-r/Obv/Obv.Tests.cs index caeeeb83d..fe332d810 100644 --- a/tests/indicators/m-r/Obv/Obv.Tests.cs +++ b/tests/indicators/m-r/Obv/Obv.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/ParabolicSar/ParabolicSar.Tests.cs b/tests/indicators/m-r/ParabolicSar/ParabolicSar.Tests.cs index 93928244c..36494f1d5 100644 --- a/tests/indicators/m-r/ParabolicSar/ParabolicSar.Tests.cs +++ b/tests/indicators/m-r/ParabolicSar/ParabolicSar.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/PivotPoints/PivotPoints.Tests.cs b/tests/indicators/m-r/PivotPoints/PivotPoints.Tests.cs index 3ee14eb2b..aa8e93910 100644 --- a/tests/indicators/m-r/PivotPoints/PivotPoints.Tests.cs +++ b/tests/indicators/m-r/PivotPoints/PivotPoints.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/Pivots/Pivots.Tests.cs b/tests/indicators/m-r/Pivots/Pivots.Tests.cs index 498fccf9c..cc53cb23b 100644 --- a/tests/indicators/m-r/Pivots/Pivots.Tests.cs +++ b/tests/indicators/m-r/Pivots/Pivots.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/Pmo/Pmo.Tests.cs b/tests/indicators/m-r/Pmo/Pmo.Tests.cs index 84fad465d..bc6110442 100644 --- a/tests/indicators/m-r/Pmo/Pmo.Tests.cs +++ b/tests/indicators/m-r/Pmo/Pmo.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/Prs/Prs.Tests.cs b/tests/indicators/m-r/Prs/Prs.Tests.cs index 135803ed0..e3bf0536c 100644 --- a/tests/indicators/m-r/Prs/Prs.Tests.cs +++ b/tests/indicators/m-r/Prs/Prs.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/Pvo/Pvo.Tests.cs b/tests/indicators/m-r/Pvo/Pvo.Tests.cs index 9f4a65c51..0c82c340b 100644 --- a/tests/indicators/m-r/Pvo/Pvo.Tests.cs +++ b/tests/indicators/m-r/Pvo/Pvo.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/Renko/Renko.Tests.cs b/tests/indicators/m-r/Renko/Renko.Tests.cs index e650e0257..9e94bfaa5 100644 --- a/tests/indicators/m-r/Renko/Renko.Tests.cs +++ b/tests/indicators/m-r/Renko/Renko.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/Roc/Roc.Tests.cs b/tests/indicators/m-r/Roc/Roc.Tests.cs index f33e3d94e..9933afca6 100644 --- a/tests/indicators/m-r/Roc/Roc.Tests.cs +++ b/tests/indicators/m-r/Roc/Roc.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/RocWb/RocWb.Tests.cs b/tests/indicators/m-r/RocWb/RocWb.Tests.cs index 5ea95ea91..2e5b95412 100644 --- a/tests/indicators/m-r/RocWb/RocWb.Tests.cs +++ b/tests/indicators/m-r/RocWb/RocWb.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/RollingPivots/RollingPivots.Tests.cs b/tests/indicators/m-r/RollingPivots/RollingPivots.Tests.cs index 493733705..2e422d274 100644 --- a/tests/indicators/m-r/RollingPivots/RollingPivots.Tests.cs +++ b/tests/indicators/m-r/RollingPivots/RollingPivots.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/m-r/Rsi/Rsi.Tests.cs b/tests/indicators/m-r/Rsi/Rsi.Tests.cs index c3e0b55f5..2ef69eefe 100644 --- a/tests/indicators/m-r/Rsi/Rsi.Tests.cs +++ b/tests/indicators/m-r/Rsi/Rsi.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/Slope/Slope.Tests.cs b/tests/indicators/s-z/Slope/Slope.Tests.cs index b92ca6a67..d6a51eab2 100644 --- a/tests/indicators/s-z/Slope/Slope.Tests.cs +++ b/tests/indicators/s-z/Slope/Slope.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/Sma/Sma.Analysis.Tests.cs b/tests/indicators/s-z/Sma/Sma.Analysis.Tests.cs index b2c80de3d..790bbc920 100644 --- a/tests/indicators/s-z/Sma/Sma.Analysis.Tests.cs +++ b/tests/indicators/s-z/Sma/Sma.Analysis.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/Sma/Sma.Static.Tests.cs b/tests/indicators/s-z/Sma/Sma.Static.Tests.cs index f48f11c9b..e6defb30a 100644 --- a/tests/indicators/s-z/Sma/Sma.Static.Tests.cs +++ b/tests/indicators/s-z/Sma/Sma.Static.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/Smi/Smi.Tests.cs b/tests/indicators/s-z/Smi/Smi.Tests.cs index 2e1e72493..1d0ecba39 100644 --- a/tests/indicators/s-z/Smi/Smi.Tests.cs +++ b/tests/indicators/s-z/Smi/Smi.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/Smma/Smma.Tests.cs b/tests/indicators/s-z/Smma/Smma.Tests.cs index 8291332b8..27750da1a 100644 --- a/tests/indicators/s-z/Smma/Smma.Tests.cs +++ b/tests/indicators/s-z/Smma/Smma.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/StarcBands/StarcBands.Tests.cs b/tests/indicators/s-z/StarcBands/StarcBands.Tests.cs index d33ef73de..08a7a9ce4 100644 --- a/tests/indicators/s-z/StarcBands/StarcBands.Tests.cs +++ b/tests/indicators/s-z/StarcBands/StarcBands.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/Stc/Stc.Tests.cs b/tests/indicators/s-z/Stc/Stc.Tests.cs index 23f9066fb..fda82df9b 100644 --- a/tests/indicators/s-z/Stc/Stc.Tests.cs +++ b/tests/indicators/s-z/Stc/Stc.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/StdDev/StdDev.Tests.cs b/tests/indicators/s-z/StdDev/StdDev.Tests.cs index db9ca3e96..54a51fc51 100644 --- a/tests/indicators/s-z/StdDev/StdDev.Tests.cs +++ b/tests/indicators/s-z/StdDev/StdDev.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/StdDevChannels/StdDevChannels.Tests.cs b/tests/indicators/s-z/StdDevChannels/StdDevChannels.Tests.cs index 9a4a9d074..ad6abad22 100644 --- a/tests/indicators/s-z/StdDevChannels/StdDevChannels.Tests.cs +++ b/tests/indicators/s-z/StdDevChannels/StdDevChannels.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/Stoch/Stoch.Tests.cs b/tests/indicators/s-z/Stoch/Stoch.Tests.cs index 11d9c9c5d..9abee8370 100644 --- a/tests/indicators/s-z/Stoch/Stoch.Tests.cs +++ b/tests/indicators/s-z/Stoch/Stoch.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/StochRsi/StochRsi.Tests.cs b/tests/indicators/s-z/StochRsi/StochRsi.Tests.cs index e76d65bbc..966e9b40f 100644 --- a/tests/indicators/s-z/StochRsi/StochRsi.Tests.cs +++ b/tests/indicators/s-z/StochRsi/StochRsi.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/SuperTrend/SuperTrend.Tests.cs b/tests/indicators/s-z/SuperTrend/SuperTrend.Tests.cs index b7522ab00..47232f3fd 100644 --- a/tests/indicators/s-z/SuperTrend/SuperTrend.Tests.cs +++ b/tests/indicators/s-z/SuperTrend/SuperTrend.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/T3/T3.Tests.cs b/tests/indicators/s-z/T3/T3.Tests.cs index d5698b5e0..145a773e2 100644 --- a/tests/indicators/s-z/T3/T3.Tests.cs +++ b/tests/indicators/s-z/T3/T3.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/Tema/Tema.Tests.cs b/tests/indicators/s-z/Tema/Tema.Tests.cs index e52066dd9..7970b480e 100644 --- a/tests/indicators/s-z/Tema/Tema.Tests.cs +++ b/tests/indicators/s-z/Tema/Tema.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/Tr/Tr.Tests.cs b/tests/indicators/s-z/Tr/Tr.Tests.cs index a251321f4..c7fbebe54 100644 --- a/tests/indicators/s-z/Tr/Tr.Tests.cs +++ b/tests/indicators/s-z/Tr/Tr.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/Trix/Trix.Tests.cs b/tests/indicators/s-z/Trix/Trix.Tests.cs index 81f4baab3..cd3d52d92 100644 --- a/tests/indicators/s-z/Trix/Trix.Tests.cs +++ b/tests/indicators/s-z/Trix/Trix.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/Tsi/Tsi.Tests.cs b/tests/indicators/s-z/Tsi/Tsi.Tests.cs index 90fa92c9d..84b8d7776 100644 --- a/tests/indicators/s-z/Tsi/Tsi.Tests.cs +++ b/tests/indicators/s-z/Tsi/Tsi.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/UlcerIndex/UlcerIndex.Tests.cs b/tests/indicators/s-z/UlcerIndex/UlcerIndex.Tests.cs index 17cedfaaf..f94d4e3f3 100644 --- a/tests/indicators/s-z/UlcerIndex/UlcerIndex.Tests.cs +++ b/tests/indicators/s-z/UlcerIndex/UlcerIndex.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/Ultimate/Ultimate.Tests.cs b/tests/indicators/s-z/Ultimate/Ultimate.Tests.cs index a926c926f..0ee1e7361 100644 --- a/tests/indicators/s-z/Ultimate/Ultimate.Tests.cs +++ b/tests/indicators/s-z/Ultimate/Ultimate.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/VolatilityStop/VolatilityStop.Tests.cs b/tests/indicators/s-z/VolatilityStop/VolatilityStop.Tests.cs index 1c601befc..6dbb3e82c 100644 --- a/tests/indicators/s-z/VolatilityStop/VolatilityStop.Tests.cs +++ b/tests/indicators/s-z/VolatilityStop/VolatilityStop.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/Vortex/Vortex.Tests.cs b/tests/indicators/s-z/Vortex/Vortex.Tests.cs index 38a77b3ca..7d183471b 100644 --- a/tests/indicators/s-z/Vortex/Vortex.Tests.cs +++ b/tests/indicators/s-z/Vortex/Vortex.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/Vwap/Vwap.Tests.cs b/tests/indicators/s-z/Vwap/Vwap.Tests.cs index 78ef4371d..11143e3a7 100644 --- a/tests/indicators/s-z/Vwap/Vwap.Tests.cs +++ b/tests/indicators/s-z/Vwap/Vwap.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/Vwma/Vwma.Tests.cs b/tests/indicators/s-z/Vwma/Vwma.Tests.cs index 50fe298c6..f722d9d29 100644 --- a/tests/indicators/s-z/Vwma/Vwma.Tests.cs +++ b/tests/indicators/s-z/Vwma/Vwma.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/WilliamsR/WilliamsR.Tests.cs b/tests/indicators/s-z/WilliamsR/WilliamsR.Tests.cs index 944a6aa44..b18d96745 100644 --- a/tests/indicators/s-z/WilliamsR/WilliamsR.Tests.cs +++ b/tests/indicators/s-z/WilliamsR/WilliamsR.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/Wma/Wma.Tests.cs b/tests/indicators/s-z/Wma/Wma.Tests.cs index fe1210378..e114970de 100644 --- a/tests/indicators/s-z/Wma/Wma.Tests.cs +++ b/tests/indicators/s-z/Wma/Wma.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Indicators; [TestClass] diff --git a/tests/indicators/s-z/ZigZag/ZigZag.Tests.cs b/tests/indicators/s-z/ZigZag/ZigZag.Tests.cs index 8a6ecca46..869bae9bb 100644 --- a/tests/indicators/s-z/ZigZag/ZigZag.Tests.cs +++ b/tests/indicators/s-z/ZigZag/ZigZag.Tests.cs @@ -1,7 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; -using Skender.Stock.Indicators; -using Tests.Common; namespace Tests.Indicators; diff --git a/tests/observe/Program.cs b/tests/observe/Program.cs index 2df87d5e8..ac5ca23b5 100644 --- a/tests/observe/Program.cs +++ b/tests/observe/Program.cs @@ -7,7 +7,7 @@ internal class Program { private static async Task Main(string[] args) { - if (args.Any()) + if (args.Length != 0) { Console.WriteLine(args); } @@ -56,10 +56,8 @@ IAlpacaCryptoStreamingClient client await client.ConnectAndAuthenticateAsync(); - AutoResetEvent[] waitObjects = new[] // todo: is this needed? - { - new AutoResetEvent(false) - }; + // todo: is this needed? + AutoResetEvent[] waitObjects = [new AutoResetEvent(false)]; IAlpacaDataSubscription quoteSubscription = client.GetMinuteBarSubscription(symbol); @@ -114,4 +112,4 @@ IAlpacaDataSubscription quoteSubscription Console.WriteLine($"{symbol} {s.Date:s} ${s.Sma:N2}"); } } -} \ No newline at end of file +} diff --git a/tests/other/Convergence.Tests.cs b/tests/other/Convergence.Tests.cs index 79a61e490..5c1f707c3 100644 --- a/tests/other/Convergence.Tests.cs +++ b/tests/other/Convergence.Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Convergence; [TestClass] diff --git a/tests/other/CustomIndicator.Tests.cs b/tests/other/CustomIndicator.Tests.cs index 83c5b58ac..c9250d63c 100644 --- a/tests/other/CustomIndicator.Tests.cs +++ b/tests/other/CustomIndicator.Tests.cs @@ -1,8 +1,5 @@ using System.Collections.ObjectModel; using System.Globalization; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; namespace Tests.CustomIndicators; @@ -213,7 +210,7 @@ public void TupleNaN() .ToList(); Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Sma is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Sma is not null and double.NaN)); } [TestMethod] @@ -223,7 +220,7 @@ public void NaN() .GetIndicator(50) .ToList(); - Assert.AreEqual(0, r.Count(x => x.Sma is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Sma is not null and double.NaN)); } [TestMethod] @@ -234,7 +231,7 @@ public void BadData() .ToList(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Sma is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Sma is not null and double.NaN)); } [TestMethod] diff --git a/tests/other/GlobalUsings.cs b/tests/other/GlobalUsings.cs new file mode 100644 index 000000000..211ba4da6 --- /dev/null +++ b/tests/other/GlobalUsings.cs @@ -0,0 +1,3 @@ +global using Microsoft.VisualStudio.TestTools.UnitTesting; +global using Skender.Stock.Indicators; +global using Tests.Common; diff --git a/tests/other/PublicApi.Tests.cs b/tests/other/PublicApi.Tests.cs index 82991d373..574986428 100644 --- a/tests/other/PublicApi.Tests.cs +++ b/tests/other/PublicApi.Tests.cs @@ -1,7 +1,4 @@ using System.Globalization; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; [assembly: CLSCompliant(true)] namespace Tests.PublicApi; diff --git a/tests/performance/GlobalUsings.cs b/tests/performance/GlobalUsings.cs new file mode 100644 index 000000000..373a066f6 --- /dev/null +++ b/tests/performance/GlobalUsings.cs @@ -0,0 +1,3 @@ +global using BenchmarkDotNet.Attributes; +global using Skender.Stock.Indicators; +global using Tests.Common; diff --git a/tests/performance/Perf.Helpers.cs b/tests/performance/Perf.Helpers.cs index f1f794aed..b08cd767f 100644 --- a/tests/performance/Perf.Helpers.cs +++ b/tests/performance/Perf.Helpers.cs @@ -1,7 +1,3 @@ -using BenchmarkDotNet.Attributes; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Performance; // HELPERS, both public and private diff --git a/tests/performance/Perf.Internals.cs b/tests/performance/Perf.Internals.cs index 2de4510d0..2c2611fe5 100644 --- a/tests/performance/Perf.Internals.cs +++ b/tests/performance/Perf.Internals.cs @@ -1,20 +1,15 @@ -using BenchmarkDotNet.Attributes; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Performance; // INTERNAL FUNCTIONS public class InternalsPerformance { - // standard deviation - - private double[] values; - [Params(20, 50, 250, 1000)] public int Periods; + private double[] values; + + // standard deviation [GlobalSetup(Targets = new[] { nameof(StdDev) })] public void Setup() => values = TestData.GetLongish(Periods) diff --git a/tests/performance/Program.cs b/tests/performance/Program.cs index 77da64453..b0603b80b 100644 --- a/tests/performance/Program.cs +++ b/tests/performance/Program.cs @@ -1,6 +1,7 @@ using BenchmarkDotNet.Running; [assembly: CLSCompliant(false)] + namespace Tests.Performance; public static class Program diff --git a/tests/performance/Tests.Performance.csproj b/tests/performance/Tests.Performance.csproj index 74cb35218..bd65b4df8 100644 --- a/tests/performance/Tests.Performance.csproj +++ b/tests/performance/Tests.Performance.csproj @@ -22,8 +22,8 @@ - - + + From 41f314f9d958e63d14f74d4c88c1b4e7e73ca828 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Wed, 3 Jan 2024 01:27:54 -0500 Subject: [PATCH 06/18] remove duplicate reference --- tests/performance/Tests.Performance.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/performance/Tests.Performance.csproj b/tests/performance/Tests.Performance.csproj index 75356d994..bd65b4df8 100644 --- a/tests/performance/Tests.Performance.csproj +++ b/tests/performance/Tests.Performance.csproj @@ -15,7 +15,6 @@ - all runtime; build; native; contentfiles; analyzers; buildtransitive From 0faa3f43a909f38526ce8c4646266fe90bdad771 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Wed, 3 Jan 2024 02:38:27 -0500 Subject: [PATCH 07/18] fix Sln file references --- Stock.Indicators.sln | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Stock.Indicators.sln b/Stock.Indicators.sln index 9dac3c139..40ad6dbc8 100644 --- a/Stock.Indicators.sln +++ b/Stock.Indicators.sln @@ -19,20 +19,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Other", "tests\other\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Performance", "tests\performance\Tests.Performance.csproj", "{3BD4837B-D197-41FD-A286-A3256D0770E1}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{3A4158F9-4165-4823-9526-0CFAACCF1ACC}" - ProjectSection(SolutionItems) = preProject - .github\build.main.yml = .github\build.main.yml - .github\workflows\build.yml = .github\workflows\build.yml - .github\dependabot.yml = .github\dependabot.yml - gitversion.yml = gitversion.yml - EndProjectSection -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Observe.Streaming", "tests\observe\Observe.Streaming.csproj", "{14DEC3AF-9AF2-4A66-8BEE-C342C6CC4307}" ProjectSection(ProjectDependencies) = postProject {11CD6C7E-871F-4903-AEAD-58E034C6521D} = {11CD6C7E-871F-4903-AEAD-58E034C6521D} {8D0F1781-EDA3-4C51-B05D-D33FF1156E49} = {8D0F1781-EDA3-4C51-B05D-D33FF1156E49} EndProjectSection EndProject + Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU From 49f5eb3bf6426416f4ce13edba36be4a00fec66d Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Sat, 16 Mar 2024 23:03:33 -0400 Subject: [PATCH 08/18] fix: Accommodate base merge changes (#1177) --- tests/observe/Observe.Streaming.csproj | 4 +- tests/observe/Program.cs | 71 +- tests/performance/Tests.Performance.csproj | 3 + tests/performance/helpers/Helper.Getter.cs | 7 + tests/performance/helpers/data/longest.csv | 15822 +++++++++++++++++++ 5 files changed, 15872 insertions(+), 35 deletions(-) create mode 100644 tests/performance/helpers/data/longest.csv diff --git a/tests/observe/Observe.Streaming.csproj b/tests/observe/Observe.Streaming.csproj index a2af5efa2..a6edaccf3 100644 --- a/tests/observe/Observe.Streaming.csproj +++ b/tests/observe/Observe.Streaming.csproj @@ -4,16 +4,14 @@ Exe net8.0 enable - enable - + - diff --git a/tests/observe/Program.cs b/tests/observe/Program.cs index ac5ca23b5..f7c7d0511 100644 --- a/tests/observe/Program.cs +++ b/tests/observe/Program.cs @@ -1,7 +1,7 @@ using Alpaca.Markets; using Skender.Stock.Indicators; -namespace ObserveAlpaca; +namespace ObserveStreaming; internal class Program { @@ -19,20 +19,37 @@ private static async Task Main(string[] args) public class QuoteStream { - private readonly string? alpacaApiKey = Environment.GetEnvironmentVariable("AlpacaApiKey"); - private readonly string? alpacaSecret = Environment.GetEnvironmentVariable("AlpacaSecret"); + private readonly string alpacaApiKey = Environment.GetEnvironmentVariable("ALPACA_KEY"); + private readonly string alpacaSecret = Environment.GetEnvironmentVariable("ALPACA_SECRET"); + + internal QuoteStream() + { + if (string.IsNullOrEmpty(alpacaApiKey)) + { + throw new ArgumentNullException( + alpacaApiKey, + $"API KEY missing, use `setx ALPACA_KEY \"MY_ALPACA_KEY\"` to set."); + } + + if (string.IsNullOrEmpty(alpacaSecret)) + { + throw new ArgumentNullException( + alpacaSecret, + $"API SECRET missing, use `setx ALPACA_SECRET \"MY_ALPACA_SECRET\"` to set."); + } + } public async Task SubscribeToQuotes(string symbol) { - Console.WriteLine("PLEASE WAIT. QUOTES ARRIVE EVERY MINUTE."); Console.WriteLine("Press any key to exit the process..."); + Console.WriteLine("PLEASE WAIT. QUOTES ARRIVE EVERY MINUTE."); - if (alpacaApiKey == null) + if (string.IsNullOrEmpty(alpacaApiKey)) { throw new ArgumentNullException(alpacaApiKey); } - if (alpacaSecret == null) + if (string.IsNullOrEmpty(alpacaSecret)) { throw new ArgumentNullException(alpacaSecret); } @@ -56,12 +73,24 @@ IAlpacaCryptoStreamingClient client await client.ConnectAndAuthenticateAsync(); - // todo: is this needed? + // TODO: is this needed? AutoResetEvent[] waitObjects = [new AutoResetEvent(false)]; IAlpacaDataSubscription quoteSubscription = client.GetMinuteBarSubscription(symbol); + await client.SubscribeAsync(quoteSubscription); + + // console display header + Console.WriteLine("A new quote will be shown when they arrive every minute."); + Console.WriteLine("PLEASE WAIT > 8 MINUTES BEFORE EXITING TO SEE ALL 4 INDICATORS CALCULATED."); + Console.WriteLine("Press any key to EXIT the process and to see results."); + Console.WriteLine(); + + Console.WriteLine("Date Close price SMA(3) EMA(5) EMA(7,HL2) SMA/EMA(8)"); + Console.WriteLine("----------------------------------------------------------------------------------"); + + // handle new quotes quoteSubscription.Received += (q) => { // add to our provider @@ -83,33 +112,11 @@ IAlpacaDataSubscription quoteSubscription // to stop watching on key press Console.ReadKey(); + // end observation provider.EndTransmission(); + + // close WebSocket await client.UnsubscribeAsync(quoteSubscription); await client.DisconnectAsync(); - - Console.WriteLine("-- QUOTES STORED (last 10 only) --"); - foreach (Quote? pt in provider.Quotes.TakeLast(10)) - { - Console.WriteLine($"{symbol} {pt.Date:s} ${pt.Close:N2}"); - } - - // show last 3 results for indicator results - Console.WriteLine("-- EMA(14,CLOSE) RESULTS (last 3 only) --"); - foreach (EmaResult? e in ema.Results.TakeLast(3)) - { - Console.WriteLine($"{symbol} {e.Date:s} ${e.Ema:N2}"); - } - - Console.WriteLine("-- EMA(10,HL2) CHAINED (last 3 only) --"); - foreach (EmaResult? e in emaChain.Results.TakeLast(3)) - { - Console.WriteLine($"{symbol} {e.Date:s} ${e.Ema:N2}"); - } - - Console.WriteLine("-- SMA(5) RESULTS (last 3 only) --"); - foreach (SmaResult? s in sma.Results.TakeLast(3)) - { - Console.WriteLine($"{symbol} {s.Date:s} ${s.Sma:N2}"); - } } } diff --git a/tests/performance/Tests.Performance.csproj b/tests/performance/Tests.Performance.csproj index 88912bf10..9aed26922 100644 --- a/tests/performance/Tests.Performance.csproj +++ b/tests/performance/Tests.Performance.csproj @@ -31,6 +31,9 @@ Always + + Always + Always diff --git a/tests/performance/helpers/Helper.Getter.cs b/tests/performance/helpers/Helper.Getter.cs index c1841bb6e..c82c64dcd 100644 --- a/tests/performance/helpers/Helper.Getter.cs +++ b/tests/performance/helpers/Helper.Getter.cs @@ -30,6 +30,13 @@ internal static IEnumerable GetIntraday(int days = 1564) .Take(days) .ToList(); + // LONGEST DATA ~62 years of S&P 500 daily data + internal static IEnumerable GetLongest() + => File.ReadAllLines("helpers/data/longest.csv") + .Skip(1) + .Select(Importer.QuoteFromCsv) + .ToList(); + // LONGISH DATA ~20 years of S&P 500 daily data internal static IEnumerable GetLongish(int days = 5285) => File.ReadAllLines("helpers/data/longish.csv") diff --git a/tests/performance/helpers/data/longest.csv b/tests/performance/helpers/data/longest.csv new file mode 100644 index 000000000..b753dcc33 --- /dev/null +++ b/tests/performance/helpers/data/longest.csv @@ -0,0 +1,15822 @@ +Date,Open,High,Low,Close,Volume +2012-11-15,1355.41,1360.62,1348.05,1353.33,3928870000 +2012-11-14,1374.64,1380.13,1352.5,1355.49,4109510000 +2012-11-13,1380.03,1388.81,1371.39,1374.53,3455550000 +2012-11-12,1379.86,1384.87,1377.19,1380,2567540000 +2012-11-09,1377.55,1391.39,1373.03,1379.85,3647350000 +2012-11-08,1394.53,1401.23,1377.51,1377.51,3779520000 +2012-11-07,1428.27,1428.27,1388.14,1394.53,4356490000 +2012-11-06,1417.26,1433.38,1417.26,1428.39,3306970000 +2012-11-05,1414.02,1419.9,1408.13,1417.26,2921040000 +2012-11-02,1427.59,1434.27,1412.91,1414.2,3732480000 +2012-11-01,1412.2,1428.35,1412.2,1427.59,3929890000 +2012-10-31,1410.99,1418.76,1405.95,1412.16,3577110000 +2012-10-26,1412.97,1417.09,1403.28,1411.94,3284910000 +2012-10-25,1409.74,1421.12,1405.14,1412.97,3512640000 +2012-10-24,1413.2,1420.04,1407.1,1408.75,3385970000 +2012-10-23,1433.74,1433.74,1407.56,1413.11,3587670000 +2012-10-22,1433.21,1435.46,1422.06,1433.81,3216220000 +2012-10-19,1457.34,1457.34,1429.85,1433.19,3875170000 +2012-10-18,1460.94,1464.02,1452.63,1457.34,3880030000 +2012-10-17,1454.22,1462.2,1453.35,1460.91,3655320000 +2012-10-16,1440.31,1455.51,1440.31,1454.92,3568770000 +2012-10-15,1428.75,1441.31,1427.24,1440.13,3483810000 +2012-10-12,1432.84,1438.43,1425.53,1428.59,3134750000 +2012-10-11,1432.82,1443.9,1432.82,1432.84,3672540000 +2012-10-10,1441.48,1442.52,1430.64,1432.56,3225060000 +2012-10-09,1455.9,1455.9,1441.18,1441.48,3216320000 +2012-10-08,1460.93,1460.93,1453.1,1455.88,2328720000 +2012-10-05,1461.4,1470.96,1456.89,1460.93,3172940000 +2012-10-04,1451.08,1463.14,1451.08,1461.4,3615860000 +2012-10-03,1446.05,1454.3,1441.99,1450.99,3531640000 +2012-10-02,1444.99,1451.52,1439.01,1445.75,3321790000 +2012-10-01,1440.9,1457.14,1440.9,1444.49,3505080000 +2012-09-28,1447.13,1447.13,1435.6,1440.67,3509230000 +2012-09-27,1433.36,1450.2,1433.36,1447.15,3150330000 +2012-09-26,1441.6,1441.6,1430.53,1433.32,3565380000 +2012-09-25,1456.94,1463.24,1441.59,1441.59,3739900000 +2012-09-24,1459.76,1460.72,1452.06,1456.89,3008920000 +2012-09-21,1460.34,1467.07,1459.51,1460.15,4833870000 +2012-09-20,1461.05,1461.23,1449.98,1460.26,3382520000 +2012-09-19,1459.5,1465.15,1457.88,1461.05,3451360000 +2012-09-18,1461.19,1461.47,1456.13,1459.32,3377390000 +2012-09-17,1465.42,1465.63,1457.55,1461.19,3482430000 +2012-09-14,1460.07,1474.51,1460.07,1465.77,5041990000 +2012-09-13,1436.56,1463.76,1435.34,1459.99,4606550000 +2012-09-12,1433.56,1439.15,1432.99,1436.56,3641200000 +2012-09-11,1429.13,1437.76,1429.13,1433.56,3509630000 +2012-09-10,1437.92,1438.74,1428.98,1429.08,3223670000 +2012-09-07,1432.12,1437.92,1431.45,1437.92,3717620000 +2012-09-06,1403.74,1432.12,1403.74,1432.12,3952870000 +2012-09-05,1404.94,1408.81,1401.25,1403.44,3389110000 +2012-09-04,1406.54,1409.31,1396.56,1404.94,3200310000 +2012-08-31,1400.07,1413.09,1398.96,1406.58,2938250000 +2012-08-30,1410.08,1410.08,1397.01,1399.48,2530280000 +2012-08-29,1409.32,1413.95,1406.57,1410.49,2571220000 +2012-08-28,1410.44,1413.63,1405.59,1409.3,2629090000 +2012-08-27,1411.13,1416.17,1409.11,1410.44,2472500000 +2012-08-24,1401.99,1413.46,1398.04,1411.13,2598790000 +2012-08-23,1413.49,1413.49,1400.5,1402.08,3008240000 +2012-08-22,1413.09,1416.12,1406.78,1413.49,3062690000 +2012-08-21,1418.13,1426.68,1410.86,1413.17,3282950000 +2012-08-20,1417.85,1418.13,1412.12,1418.13,2766320000 +2012-08-17,1415.84,1418.71,1414.67,1418.16,2922990000 +2012-08-16,1405.57,1417.44,1404.15,1415.51,3114100000 +2012-08-15,1403.89,1407.73,1401.83,1405.53,2655750000 +2012-08-14,1404.36,1410.03,1400.6,1403.93,2930900000 +2012-08-13,1405.87,1405.87,1397.32,1404.11,2499990000 +2012-08-10,1402.58,1405.98,1395.62,1405.87,2767980000 +2012-08-09,1402.26,1405.95,1398.8,1402.8,3119610000 +2012-08-08,1401.23,1404.14,1396.13,1402.22,3221790000 +2012-08-07,1394.46,1407.14,1394.46,1401.35,3682490000 +2012-08-06,1391.04,1399.63,1391.04,1394.23,3122050000 +2012-08-03,1365.45,1394.16,1365.45,1390.99,3751170000 +2012-08-02,1375.13,1375.13,1354.65,1365,4193740000 +2012-08-01,1379.32,1385.03,1373.35,1375.32,4440920000 +2012-07-31,1385.27,1387.16,1379.17,1379.32,3821570000 +2012-07-30,1385.94,1391.74,1381.37,1385.3,3212060000 +2012-07-27,1360.05,1389.19,1360.05,1385.97,4399010000 +2012-07-26,1338.17,1363.13,1338.17,1360.02,4429300000 +2012-07-25,1338.35,1343.98,1331.5,1337.89,3719170000 +2012-07-24,1350.52,1351.53,1329.24,1338.31,3891290000 +2012-07-23,1362.34,1362.34,1337.56,1350.52,3717180000 +2012-07-20,1376.51,1376.51,1362.19,1362.66,3925020000 +2012-07-19,1373.01,1380.39,1371.21,1376.51,4043360000 +2012-07-18,1363.58,1375.26,1358.96,1372.78,3642630000 +2012-07-17,1353.68,1365.36,1345.07,1363.67,3566680000 +2012-07-16,1356.5,1357.26,1348.51,1353.64,2862720000 +2012-07-13,1334.81,1357.7,1334.81,1356.78,3212930000 +2012-07-12,1341.29,1341.29,1325.41,1334.76,3654440000 +2012-07-11,1341.4,1345,1333.25,1341.45,3426290000 +2012-07-10,1352.96,1361.54,1336.27,1341.47,3470600000 +2012-07-09,1354.66,1354.87,1346.65,1352.46,2904860000 +2012-07-06,1367.09,1367.09,1348.03,1354.68,2745140000 +2012-07-05,1373.72,1373.85,1363.02,1367.58,3041520000 +2012-07-03,1365.75,1374.81,1363.53,1374.02,2116390000 +2012-07-02,1362.33,1366.35,1355.7,1365.51,3301650000 +2012-06-29,1330.12,1362.17,1330.12,1362.16,4590480000 +2012-06-28,1331.52,1331.52,1313.29,1329.04,3969370000 +2012-06-27,1320.71,1334.4,1320.71,1331.85,3286910000 +2012-06-26,1314.09,1324.24,1310.3,1319.99,3412940000 +2012-06-25,1334.9,1334.9,1309.27,1313.72,3501820000 +2012-06-22,1325.92,1337.82,1325.92,1335.02,5271490000 +2012-06-21,1355.43,1358.27,1324.41,1325.51,4094470000 +2012-06-20,1358.04,1361.57,1346.45,1355.69,3695700000 +2012-06-19,1344.83,1363.46,1344.83,1357.98,3815350000 +2012-06-18,1342.42,1348.22,1334.46,1344.78,3259430000 +2012-06-15,1329.19,1343.32,1329.19,1342.84,4401570000 +2012-06-14,1314.88,1333.68,1314.14,1329.1,3687720000 +2012-06-13,1324.02,1327.28,1310.51,1314.88,3506510000 +2012-06-12,1309.4,1324.31,1306.62,1324.18,3442920000 +2012-06-11,1325.72,1335.52,1307.73,1308.93,3537530000 +2012-06-08,1314.99,1325.81,1307.77,1325.66,3497190000 +2012-06-07,1316.15,1329.05,1312.68,1314.99,4258140000 +2012-06-06,1285.61,1315.13,1285.61,1315.13,4268360000 +2012-06-05,1277.82,1287.62,1274.16,1285.5,3403230000 +2012-06-04,1278.29,1282.55,1266.74,1278.18,4011960000 +2012-06-01,1309.87,1309.87,1277.25,1278.04,4669350000 +2012-05-31,1313.09,1319.74,1298.9,1310.33,4557620000 +2012-05-30,1331.25,1331.25,1310.76,1313.32,3534290000 +2012-05-29,1318.9,1334.93,1318.9,1332.42,3441640000 +2012-05-25,1320.81,1324.2,1314.23,1317.82,2872660000 +2012-05-24,1318.72,1324.14,1310.5,1320.68,3937670000 +2012-05-23,1316.02,1320.71,1296.53,1318.86,4108330000 +2012-05-22,1316.09,1328.49,1310.04,1316.63,4123680000 +2012-05-21,1295.73,1316.39,1295.73,1315.99,3786750000 +2012-05-18,1305.05,1312.24,1291.98,1295.22,4512470000 +2012-05-17,1324.82,1326.36,1304.86,1304.86,4664280000 +2012-05-16,1330.78,1341.78,1324.79,1324.8,4280420000 +2012-05-15,1338.36,1344.94,1328.41,1330.66,4114040000 +2012-05-14,1351.93,1351.93,1336.61,1338.35,3688120000 +2012-05-11,1358.11,1365.66,1348.89,1353.39,3869070000 +2012-05-10,1354.58,1365.88,1354.58,1357.99,3727990000 +2012-05-09,1363.2,1363.73,1343.13,1354.58,4288540000 +2012-05-08,1369.16,1369.16,1347.75,1363.72,4261670000 +2012-05-07,1368.79,1373.91,1363.94,1369.58,3559390000 +2012-05-04,1391.51,1391.51,1367.96,1369.1,3975140000 +2012-05-03,1402.32,1403.07,1388.71,1391.57,4004910000 +2012-05-02,1405.5,1405.5,1393.92,1402.31,3803860000 +2012-05-01,1397.86,1415.32,1395.73,1405.82,3807950000 +2012-04-30,1403.26,1403.26,1394,1397.91,3574010000 +2012-04-27,1400.19,1406.64,1397.31,1403.36,3645830000 +2012-04-26,1390.64,1402.09,1387.28,1399.98,4034700000 +2012-04-25,1372.11,1391.37,1372.11,1390.69,3998430000 +2012-04-24,1366.97,1375.57,1366.82,1371.97,3617100000 +2012-04-23,1378.53,1378.53,1358.79,1366.94,3654860000 +2012-04-20,1376.96,1387.4,1376.96,1378.53,3833320000 +2012-04-19,1385.08,1390.46,1370.3,1376.92,4180020000 +2012-04-18,1390.78,1390.78,1383.29,1385.14,3463140000 +2012-04-17,1369.57,1392.76,1369.57,1390.78,3456200000 +2012-04-16,1370.27,1379.66,1365.38,1369.57,3574780000 +2012-04-13,1387.61,1387.61,1369.85,1370.26,3631160000 +2012-04-12,1368.77,1388.13,1368.77,1387.57,3618280000 +2012-04-11,1358.98,1374.71,1358.98,1368.71,3743040000 +2012-04-10,1382.18,1383.01,1357.38,1358.59,4631730000 +2012-04-09,1397.45,1397.45,1378.24,1382.2,3468980000 +2012-04-05,1398.79,1401.6,1392.92,1398.08,3303740000 +2012-04-04,1413.09,1413.09,1394.09,1398.96,3938290000 +2012-04-03,1418.98,1419,1404.62,1413.38,3822090000 +2012-04-02,1408.47,1422.38,1404.46,1419.04,3572010000 +2012-03-30,1403.31,1410.89,1401.42,1408.47,3676890000 +2012-03-29,1405.39,1405.39,1391.56,1403.28,3832000000 +2012-03-28,1412.52,1413.65,1397.2,1405.54,3892800000 +2012-03-27,1416.55,1419.15,1411.95,1412.52,3513640000 +2012-03-26,1397.11,1416.58,1397.11,1416.51,3576950000 +2012-03-23,1392.78,1399.18,1386.87,1397.11,3472950000 +2012-03-22,1402.89,1402.89,1388.73,1392.78,3740590000 +2012-03-21,1405.52,1407.75,1400.64,1402.89,3573590000 +2012-03-20,1409.59,1409.59,1397.68,1405.52,3695280000 +2012-03-19,1404.17,1414,1402.43,1409.75,3932570000 +2012-03-16,1402.55,1405.88,1401.47,1404.17,5163950000 +2012-03-15,1394.17,1402.63,1392.78,1402.6,4271650000 +2012-03-14,1395.95,1399.42,1389.97,1394.28,4502280000 +2012-03-13,1371.92,1396.13,1371.92,1395.95,4386470000 +2012-03-12,1370.78,1373.04,1366.69,1371.09,3081870000 +2012-03-09,1365.97,1374.76,1365.97,1370.87,3639470000 +2012-03-08,1352.65,1368.72,1352.65,1365.91,3543060000 +2012-03-07,1343.39,1354.85,1343.39,1352.63,3580380000 +2012-03-06,1363.63,1363.63,1340.03,1343.36,4191060000 +2012-03-05,1369.59,1369.59,1359.13,1364.33,3429480000 +2012-03-02,1374.09,1374.53,1366.42,1369.63,3283490000 +2012-03-01,1365.9,1376.17,1365.9,1374.09,3919240000 +2012-02-29,1372.2,1378.04,1363.81,1365.68,4482370000 +2012-02-28,1367.56,1373.09,1365.97,1372.18,3579120000 +2012-02-27,1365.2,1371.94,1354.92,1367.59,3648890000 +2012-02-24,1363.46,1368.92,1363.46,1365.74,3505360000 +2012-02-23,1357.53,1364.24,1352.28,1363.46,3786450000 +2012-02-22,1362.11,1362.7,1355.53,1357.66,3633710000 +2012-02-21,1361.22,1367.76,1358.11,1362.21,3795200000 +2012-02-17,1358.06,1363.4,1357.24,1361.23,3717640000 +2012-02-16,1342.61,1359.02,1341.22,1358.04,4108880000 +2012-02-15,1350.52,1355.87,1340.8,1343.23,4080340000 +2012-02-14,1351.3,1351.3,1340.83,1350.5,3889520000 +2012-02-13,1343.06,1353.35,1343.06,1351.77,3618040000 +2012-02-10,1351.21,1351.21,1337.35,1342.64,3877580000 +2012-02-09,1349.97,1354.32,1344.63,1351.95,4209890000 +2012-02-08,1347.04,1351,1341.95,1349.96,4096730000 +2012-02-07,1344.33,1349.24,1335.92,1347.05,3742460000 +2012-02-06,1344.32,1344.36,1337.52,1344.33,3379700000 +2012-02-03,1326.21,1345.34,1326.21,1344.9,4608550000 +2012-02-02,1324.24,1329.19,1321.57,1325.54,4120920000 +2012-02-01,1312.45,1330.52,1312.45,1324.09,4504360000 +2012-01-31,1313.53,1321.41,1306.69,1312.41,4235550000 +2012-01-30,1316.16,1316.16,1300.49,1313.01,3659010000 +2012-01-27,1318.25,1320.06,1311.72,1316.33,4007380000 +2012-01-26,1326.28,1333.47,1313.6,1318.43,4522070000 +2012-01-25,1314.4,1328.3,1307.65,1326.06,4410910000 +2012-01-24,1315.96,1315.96,1306.06,1314.65,3693560000 +2012-01-23,1315.29,1322.28,1309.89,1316,3770910000 +2012-01-20,1314.49,1315.38,1309.17,1315.38,3912620000 +2012-01-19,1308.07,1315.49,1308.07,1314.5,4465890000 +2012-01-18,1293.65,1308.11,1290.99,1308.04,4096160000 +2012-01-17,1290.22,1303,1290.22,1293.67,4010490000 +2012-01-13,1294.82,1294.82,1277.58,1289.09,3692370000 +2012-01-12,1292.48,1296.82,1285.77,1295.5,4019890000 +2012-01-11,1292.02,1293.8,1285.41,1292.48,3968120000 +2012-01-10,1280.77,1296.46,1280.77,1292.08,4221960000 +2012-01-09,1277.83,1281.99,1274.55,1280.7,3371600000 +2012-01-06,1280.93,1281.84,1273.34,1277.81,3656830000 +2012-01-05,1277.3,1283.05,1265.26,1281.06,4315950000 +2012-01-04,1277.03,1278.73,1268.1,1277.3,3592580000 +2012-01-03,1258.86,1284.62,1258.86,1277.06,3943710000 +2011-12-30,1262.82,1264.12,1257.46,1257.6,2271850000 +2011-12-29,1249.75,1263.54,1249.75,1263.02,2278130000 +2011-12-28,1265.38,1265.85,1248.64,1249.64,2349980000 +2011-12-27,1265.02,1269.37,1262.3,1265.43,2130590000 +2011-12-23,1254,1265.42,1254,1265.33,2233830000 +2011-12-22,1243.72,1255.22,1243.72,1254,3492250000 +2011-12-21,1241.25,1245.09,1229.51,1243.72,2959020000 +2011-12-20,1205.72,1242.82,1205.72,1241.3,4055590000 +2011-12-19,1219.74,1224.57,1202.37,1205.35,3659820000 +2011-12-16,1216.09,1231.04,1215.2,1219.66,5345800000 +2011-12-15,1212.12,1225.6,1212.12,1215.75,3810340000 +2011-12-14,1225.73,1225.73,1209.47,1211.82,4298290000 +2011-12-13,1236.83,1249.86,1219.43,1225.73,4121570000 +2011-12-12,1255.05,1255.05,1227.25,1236.47,3600570000 +2011-12-09,1234.48,1258.25,1234.48,1255.19,3830610000 +2011-12-08,1260.87,1260.87,1231.47,1234.35,4298370000 +2011-12-07,1258.14,1267.06,1244.8,1261.01,4160540000 +2011-12-06,1257.19,1266.03,1253.03,1258.47,3734230000 +2011-12-05,1244.33,1266.73,1244.33,1257.08,4148060000 +2011-12-02,1246.03,1260.08,1243.35,1244.28,4144310000 +2011-12-01,1246.91,1251.09,1239.73,1244.58,3818680000 +2011-11-30,1196.72,1247.11,1196.72,1246.96,5801910000 +2011-11-29,1192.56,1203.67,1191.8,1195.19,3992650000 +2011-11-28,1158.67,1197.35,1158.67,1192.55,3920750000 +2011-11-25,1161.41,1172.66,1158.66,1158.67,1664200000 +2011-11-23,1187.48,1187.48,1161.79,1161.79,3798940000 +2011-11-22,1192.98,1196.81,1181.65,1188.04,3911710000 +2011-11-21,1215.62,1215.62,1183.16,1192.98,4050070000 +2011-11-18,1216.19,1223.51,1211.36,1215.65,3827610000 +2011-11-17,1236.56,1237.73,1209.43,1216.13,4596450000 +2011-11-16,1257.81,1259.61,1235.67,1236.91,4085010000 +2011-11-15,1251.7,1264.25,1244.34,1257.81,3599300000 +2011-11-14,1263.85,1263.85,1246.68,1251.78,3219680000 +2011-11-11,1240.12,1266.98,1240.12,1263.85,3357850000 +2011-11-10,1229.59,1246.22,1227.7,1239.7,4002760000 +2011-11-09,1275.18,1275.18,1226.64,1229.1,4659740000 +2011-11-08,1261.12,1277.55,1254.99,1275.92,3908490000 +2011-11-07,1253.21,1261.7,1240.75,1261.12,3429740000 +2011-11-04,1260.82,1260.82,1238.92,1253.23,3830650000 +2011-11-03,1238.25,1263.21,1234.81,1261.15,4849140000 +2011-11-02,1219.62,1242.48,1219.62,1237.9,4110530000 +2011-11-01,1251,1251,1215.42,1218.28,5645540000 +2011-10-31,1284.96,1284.96,1253.16,1253.3,4310210000 +2011-10-28,1284.39,1287.08,1277.01,1285.09,4536690000 +2011-10-27,1243.97,1292.66,1243.97,1284.59,6367610000 +2011-10-26,1229.17,1246.28,1221.06,1242,4873530000 +2011-10-25,1254.19,1254.19,1226.79,1229.05,4473970000 +2011-10-24,1238.72,1256.55,1238.72,1254.19,4309380000 +2011-10-21,1215.39,1239.03,1215.39,1238.25,4980770000 +2011-10-20,1209.92,1219.53,1197.34,1215.39,4870290000 +2011-10-19,1223.46,1229.64,1206.31,1209.88,4846390000 +2011-10-18,1200.75,1233.1,1191.48,1225.38,4840170000 +2011-10-17,1224.47,1224.47,1198.55,1200.86,4300700000 +2011-10-14,1205.65,1224.61,1205.65,1224.58,4116690000 +2011-10-13,1206.96,1207.46,1190.58,1203.66,4436270000 +2011-10-12,1196.19,1220.25,1196.19,1207.25,5355360000 +2011-10-11,1194.6,1199.24,1187.3,1195.54,4424500000 +2011-10-10,1158.15,1194.91,1158.15,1194.89,4446800000 +2011-10-07,1165.03,1171.4,1150.26,1155.46,5580380000 +2011-10-06,1144.11,1165.55,1134.95,1164.97,5098330000 +2011-10-05,1124.03,1146.07,1115.68,1144.03,2510620000 +2011-10-04,1097.42,1125.12,1074.77,1123.95,3714670000 +2011-10-03,1131.21,1138.99,1098.92,1099.23,5670340000 +2011-09-30,1159.93,1159.93,1131.34,1131.42,4416790000 +2011-09-29,1151.74,1175.87,1139.93,1160.4,5285740000 +2011-09-28,1175.39,1184.71,1150.4,1151.06,4787920000 +2011-09-27,1163.32,1195.86,1163.32,1175.38,5548130000 +2011-09-26,1136.91,1164.19,1131.07,1162.95,4762830000 +2011-09-23,1128.82,1141.72,1121.36,1136.43,5639930000 +2011-09-22,1164.55,1164.55,1114.22,1129.56,6703140000 +2011-09-21,1203.63,1206.3,1166.21,1166.76,4728550000 +2011-09-20,1204.5,1220.39,1201.29,1202.09,4315610000 +2011-09-19,1214.99,1214.99,1188.36,1204.09,4254190000 +2011-09-16,1209.21,1220.06,1204.46,1216.01,5248890000 +2011-09-15,1189.44,1209.11,1189.44,1209.11,4479730000 +2011-09-14,1173.32,1202.38,1162.73,1188.68,4986740000 +2011-09-13,1162.59,1176.41,1157.44,1172.87,4681370000 +2011-09-12,1153.5,1162.52,1136.07,1162.27,5168550000 +2011-09-09,1185.37,1185.37,1148.37,1154.23,4586370000 +2011-09-08,1197.98,1204.4,1183.34,1185.9,4465170000 +2011-09-07,1165.85,1198.62,1165.85,1198.62,4441040000 +2011-09-06,1173.97,1173.97,1140.13,1165.24,5103980000 +2011-09-02,1203.9,1203.9,1170.56,1173.97,4401740000 +2011-09-01,1219.12,1229.29,1203.85,1204.42,4780410000 +2011-08-31,1213,1230.71,1209.35,1218.89,5267840000 +2011-08-30,1209.76,1220.1,1195.77,1212.92,4572570000 +2011-08-29,1177.91,1210.28,1177.91,1210.08,4228070000 +2011-08-26,1158.85,1181.23,1135.91,1176.8,5035320000 +2011-08-25,1176.69,1190.68,1155.47,1159.27,5748420000 +2011-08-24,1162.16,1178.56,1156.3,1177.6,5315310000 +2011-08-23,1124.36,1162.35,1124.36,1162.35,5013170000 +2011-08-22,1123.55,1145.49,1121.09,1123.82,5436260000 +2011-08-19,1140.47,1154.54,1122.05,1123.53,5167560000 +2011-08-18,1189.62,1189.62,1131.03,1140.65,3234810000 +2011-08-17,1192.89,1208.47,1184.36,1193.89,4388340000 +2011-08-16,1204.22,1204.22,1180.53,1192.76,5071600000 +2011-08-15,1178.86,1204.49,1178.86,1204.49,4272850000 +2011-08-12,1172.87,1189.04,1170.74,1178.81,5640380000 +2011-08-11,1121.3,1186.29,1121.3,1172.64,3685050000 +2011-08-10,1171.77,1171.77,1118.01,1120.76,5018070000 +2011-08-09,1120.23,1172.88,1101.54,1172.53,2366660000 +2011-08-08,1198.48,1198.48,1119.28,1119.46,2615150000 +2011-08-05,1200.28,1218.11,1168.09,1199.38,5454590000 +2011-08-04,1260.23,1260.23,1199.54,1200.07,4266530000 +2011-08-03,1254.25,1261.2,1234.56,1260.34,6446940000 +2011-08-02,1286.56,1286.56,1254.03,1254.05,1674180000 +2011-08-01,1292.59,1307.38,1274.73,1286.94,4967390000 +2011-07-29,1300.12,1304.16,1282.86,1292.28,5061190000 +2011-07-28,1304.84,1316.32,1299.16,1300.67,4951800000 +2011-07-27,1331.91,1331.91,1303.49,1304.89,3479040000 +2011-07-26,1337.39,1338.51,1329.59,1331.94,4007050000 +2011-07-25,1344.32,1344.32,1331.09,1337.43,3536890000 +2011-07-22,1343.8,1346.1,1336.95,1345.02,3522830000 +2011-07-21,1325.65,1347,1325.65,1343.8,4837430000 +2011-07-20,1328.66,1330.43,1323.65,1325.84,3767420000 +2011-07-19,1307.07,1328.14,1307.07,1326.73,4304600000 +2011-07-18,1315.94,1315.94,1295.92,1305.44,4118160000 +2011-07-15,1308.87,1317.7,1307.52,1316.14,4242760000 +2011-07-14,1317.74,1326.88,1306.51,1308.87,4358570000 +2011-07-13,1314.45,1331.48,1314.45,1317.72,4060080000 +2011-07-12,1319.61,1327.17,1313.33,1313.64,4227890000 +2011-07-11,1343.31,1343.31,1316.42,1319.49,3879130000 +2011-07-08,1352.39,1352.39,1333.71,1343.8,3594360000 +2011-07-07,1339.62,1356.48,1339.62,1353.22,4069530000 +2011-07-06,1337.56,1340.94,1330.92,1339.22,3564190000 +2011-07-05,1339.59,1340.89,1334.3,1337.88,3722320000 +2011-07-01,1320.64,1341.01,1318.18,1339.67,3796930000 +2011-06-30,1307.64,1321.97,1307.64,1320.64,4200500000 +2011-06-29,1296.85,1309.21,1296.85,1307.41,4347540000 +2011-06-28,1280.21,1296.8,1280.21,1296.67,3681500000 +2011-06-27,1268.44,1284.91,1267.53,1280.1,3479070000 +2011-06-24,1283.04,1283.93,1267.24,1268.45,3665340000 +2011-06-23,1286.6,1286.6,1262.87,1283.5,4983450000 +2011-06-22,1295.48,1298.61,1286.79,1287.14,3718420000 +2011-06-21,1278.4,1297.62,1278.4,1295.52,4056150000 +2011-06-20,1271.5,1280.42,1267.56,1278.36,3464660000 +2011-06-17,1268.58,1279.82,1267.4,1271.5,4916460000 +2011-06-16,1265.53,1274.11,1258.07,1267.64,3846250000 +2011-06-15,1287.87,1287.87,1261.9,1265.42,4070500000 +2011-06-14,1272.22,1292.5,1272.22,1287.87,3500280000 +2011-06-13,1271.31,1277.04,1265.64,1271.83,4132520000 +2011-06-10,1288.6,1288.6,1268.28,1270.98,3846250000 +2011-06-09,1279.63,1294.54,1279.63,1289,3332510000 +2011-06-08,1284.63,1287.04,1277.42,1279.56,3970810000 +2011-06-07,1286.31,1296.22,1284.74,1284.94,3846250000 +2011-06-06,1300.26,1300.26,1284.72,1286.17,3555980000 +2011-06-03,1312.94,1312.94,1297.9,1300.16,3505030000 +2011-06-02,1314.55,1318.03,1305.61,1312.94,3762170000 +2011-06-01,1345.2,1345.2,1313.71,1314.55,4241090000 +2011-05-31,1331.1,1345.2,1331.1,1345.2,4696240000 +2011-05-27,1325.69,1334.62,1325.69,1331.1,3124560000 +2011-05-26,1320.64,1328.51,1314.41,1325.69,3259470000 +2011-05-25,1316.36,1325.86,1311.8,1320.47,4109670000 +2011-05-24,1317.7,1323.72,1313.87,1316.28,3846250000 +2011-05-23,1333.07,1333.07,1312.88,1317.37,3255580000 +2011-05-20,1342,1342,1330.67,1333.27,4066020000 +2011-05-19,1342.4,1346.82,1336.36,1343.6,3626110000 +2011-05-18,1328.54,1341.82,1326.59,1340.68,3922030000 +2011-05-17,1326.1,1330.42,1318.51,1328.98,4053970000 +2011-05-16,1334.77,1343.33,1327.32,1329.47,3846250000 +2011-05-13,1348.69,1350.47,1333.36,1337.77,3426660000 +2011-05-12,1339.39,1351.05,1332.03,1348.65,3777210000 +2011-05-11,1354.51,1354.51,1336.36,1342.08,3846250000 +2011-05-10,1348.34,1359.44,1348.34,1357.16,4223740000 +2011-05-09,1340.2,1349.44,1338.64,1346.29,4265250000 +2011-05-06,1340.24,1354.36,1335.58,1340.2,4223740000 +2011-05-05,1344.16,1348,1329.17,1335.1,3846250000 +2011-05-04,1355.9,1355.9,1341.5,1347.32,4223740000 +2011-05-03,1359.76,1360.84,1349.52,1356.62,4223740000 +2011-05-02,1365.21,1370.58,1358.59,1361.22,3846250000 +2011-04-29,1360.14,1364.56,1358.69,1363.61,3479070000 +2011-04-28,1353.86,1361.71,1353.6,1360.48,4036820000 +2011-04-27,1348.43,1357.49,1344.25,1355.66,4051570000 +2011-04-26,1336.75,1349.55,1336.75,1347.24,3908060000 +2011-04-25,1337.14,1337.55,1331.47,1335.25,2142130000 +2011-04-21,1333.23,1337.49,1332.83,1337.38,3587240000 +2011-04-20,1319.12,1332.66,1319.12,1330.36,4236280000 +2011-04-19,1305.99,1312.7,1303.97,1312.62,3886300000 +2011-04-18,1313.35,1313.35,1294.7,1305.14,4223740000 +2011-04-15,1314.54,1322.88,1313.68,1319.68,4223740000 +2011-04-14,1311.13,1316.79,1302.42,1314.52,3872630000 +2011-04-13,1314.03,1321.35,1309.19,1314.41,3850860000 +2011-04-12,1321.96,1321.96,1309.51,1314.16,4275490000 +2011-04-11,1329.01,1333.77,1321.06,1324.46,3478970000 +2011-04-08,1336.16,1339.46,1322.94,1328.17,3582810000 +2011-04-07,1334.82,1338.8,1326.56,1333.51,4005600000 +2011-04-06,1335.94,1339.38,1331.09,1335.54,4223740000 +2011-04-05,1332.03,1338.21,1330.03,1332.63,3852280000 +2011-04-04,1333.56,1336.74,1329.1,1332.87,4223740000 +2011-04-01,1329.48,1337.85,1328.89,1332.41,4223740000 +2011-03-31,1327.44,1329.77,1325.03,1325.83,3566270000 +2011-03-30,1321.89,1331.74,1321.89,1328.26,3809570000 +2011-03-29,1309.37,1319.45,1305.26,1319.44,3482580000 +2011-03-28,1315.45,1319.74,1310.19,1310.19,3215170000 +2011-03-25,1311.8,1319.18,1310.15,1313.8,4223740000 +2011-03-24,1300.61,1311.34,1297.74,1309.66,4223740000 +2011-03-23,1292.19,1300.51,1284.05,1297.54,3842350000 +2011-03-22,1298.29,1299.35,1292.7,1293.77,3576550000 +2011-03-21,1281.65,1300.58,1281.65,1298.38,4223730000 +2011-03-18,1276.71,1288.88,1276.18,1279.21,4685500000 +2011-03-17,1261.61,1278.88,1261.61,1273.72,4134950000 +2011-03-16,1279.46,1280.91,1249.05,1256.88,5833000000 +2011-03-15,1288.46,1288.46,1261.12,1281.87,5201400000 +2011-03-14,1301.19,1301.19,1286.37,1296.39,4050370000 +2011-03-11,1293.43,1308.35,1291.99,1304.28,3740400000 +2011-03-10,1315.72,1315.72,1294.21,1295.11,4723020000 +2011-03-09,1319.92,1323.21,1312.27,1320.02,3709520000 +2011-03-08,1311.05,1325.74,1306.86,1321.82,4531420000 +2011-03-07,1322.72,1327.68,1303.99,1310.13,3964730000 +2011-03-04,1330.73,1331.08,1312.59,1321.15,4223740000 +2011-03-03,1312.37,1332.28,1312.37,1330.97,4340470000 +2011-03-02,1305.47,1314.19,1302.58,1308.44,1025000000 +2011-03-01,1328.64,1332.09,1306.14,1306.33,1180420000 +2011-02-28,1321.61,1329.38,1320.55,1327.22,1252850000 +2011-02-25,1307.34,1320.61,1307.34,1319.88,3836030000 +2011-02-24,1307.09,1310.91,1294.26,1306.1,1222900000 +2011-02-23,1315.44,1317.91,1299.55,1307.4,1330340000 +2011-02-22,1338.91,1338.91,1312.33,1315.44,1322780000 +2011-02-18,1340.38,1344.07,1338.12,1343.01,1966450000 +2011-02-17,1334.37,1341.5,1331,1340.43,1966450000 +2011-02-16,1329.51,1337.61,1329.51,1336.32,1966450000 +2011-02-15,1330.43,1330.43,1324.61,1328.01,3926860000 +2011-02-14,1328.73,1332.96,1326.9,1332.32,3567040000 +2011-02-11,1318.66,1330.79,1316.08,1329.15,4219300000 +2011-02-10,1318.13,1322.78,1311.74,1321.87,4184610000 +2011-02-09,1322.48,1324.54,1314.89,1320.88,3922240000 +2011-02-08,1318.76,1324.87,1316.03,1324.57,3881530000 +2011-02-07,1311.85,1322.85,1311.85,1319.05,3902270000 +2011-02-04,1307.01,1311,1301.67,1310.87,3925950000 +2011-02-03,1302.77,1308.6,1294.83,1307.1,4370990000 +2011-02-02,1305.91,1307.61,1302.62,1304.03,4098260000 +2011-02-01,1289.14,1308.86,1289.14,1307.59,5164500000 +2011-01-31,1276.5,1287.17,1276.5,1286.12,4167160000 +2011-01-28,1299.63,1302.67,1275.1,1276.34,5618630000 +2011-01-27,1297.51,1301.29,1294.41,1299.54,4309190000 +2011-01-26,1291.97,1299.74,1291.97,1296.63,4730980000 +2011-01-25,1288.17,1291.26,1281.07,1291.18,4595380000 +2011-01-24,1283.29,1291.93,1282.47,1290.84,3902470000 +2011-01-21,1283.63,1291.21,1282.07,1283.35,4935320000 +2011-01-20,1280.85,1283.35,1271.26,1280.26,4935320000 +2011-01-19,1294.52,1294.6,1278.92,1281.92,4743710000 +2011-01-18,1293.22,1296.06,1290.16,1295.02,5284990000 +2011-01-14,1282.9,1293.24,1281.24,1293.24,4661590000 +2011-01-13,1285.78,1286.7,1280.47,1283.76,4310840000 +2011-01-12,1275.65,1286.87,1275.65,1285.96,4226940000 +2011-01-11,1272.58,1277.25,1269.62,1274.48,4050750000 +2011-01-10,1270.84,1271.52,1262.18,1269.75,4036450000 +2011-01-07,1274.41,1276.83,1261.7,1271.5,4963110000 +2011-01-06,1276.29,1278.17,1270.43,1273.85,4844100000 +2011-01-05,1268.78,1277.63,1265.36,1276.56,4764920000 +2011-01-04,1272.95,1274.12,1262.66,1270.2,4796420000 +2011-01-03,1257.62,1276.17,1257.62,1271.87,4286670000 +2010-12-31,1256.76,1259.34,1254.19,1257.64,1799770000 +2010-12-30,1259.44,1261.09,1256.32,1257.88,1970720000 +2010-12-29,1258.78,1262.6,1258.78,1259.78,2214380000 +2010-12-28,1259.1,1259.9,1256.22,1258.51,2478450000 +2010-12-27,1254.66,1258.43,1251.48,1257.54,1992470000 +2010-12-23,1257.53,1258.59,1254.05,1256.77,2515020000 +2010-12-22,1254.94,1259.39,1254.94,1258.84,1285590000 +2010-12-21,1249.43,1255.82,1249.43,1254.6,3479670000 +2010-12-20,1245.76,1250.2,1241.51,1247.08,3548140000 +2010-12-17,1243.63,1245.81,1239.87,1243.91,4632470000 +2010-12-16,1236.34,1243.75,1232.85,1242.87,4736820000 +2010-12-15,1241.58,1244.25,1234.01,1235.23,4407340000 +2010-12-14,1241.84,1246.59,1238.17,1241.59,4132350000 +2010-12-13,1242.52,1246.73,1240.34,1240.46,4361240000 +2010-12-10,1233.85,1240.4,1232.58,1240.4,4547310000 +2010-12-09,1230.14,1234.71,1226.85,1233,4522510000 +2010-12-08,1225.02,1228.93,1219.5,1228.28,4607590000 +2010-12-07,1227.25,1235.05,1223.25,1223.75,6970630400 +2010-12-06,1223.87,1225.8,1220.67,1223.12,3527370000 +2010-12-03,1219.93,1225.57,1216.82,1224.71,3735780000 +2010-12-02,1206.81,1221.89,1206.81,1221.53,4970800000 +2010-12-01,1186.6,1207.61,1186.6,1206.07,4548110000 +2010-11-30,1182.96,1187.4,1174.14,1180.55,4284700000 +2010-11-29,1189.08,1190.34,1173.64,1187.76,3673450000 +2010-11-26,1194.16,1194.16,1186.93,1189.4,1613820000 +2010-11-24,1183.7,1198.62,1183.7,1198.35,3384250000 +2010-11-23,1192.51,1192.51,1176.91,1180.73,4133070000 +2010-11-22,1198.07,1198.94,1184.58,1197.84,3689500000 +2010-11-19,1196.12,1199.97,1189.44,1199.73,3675390000 +2010-11-18,1183.75,1200.29,1183.75,1196.69,4687260000 +2010-11-17,1178.33,1183.56,1175.82,1178.59,3904780000 +2010-11-16,1194.79,1194.79,1173,1178.34,5116380000 +2010-11-15,1200.44,1207.43,1197.15,1197.75,3503370000 +2010-11-12,1209.07,1210.5,1194.08,1199.21,4213620000 +2010-11-11,1213.04,1215.45,1204.49,1213.54,3931120000 +2010-11-10,1213.14,1218.75,1204.33,1218.71,4561300000 +2010-11-09,1223.59,1226.84,1208.94,1213.4,4848040000 +2010-11-08,1223.24,1224.57,1217.55,1223.25,3937230000 +2010-11-05,1221.2,1227.08,1220.29,1225.85,5637460000 +2010-11-04,1198.34,1221.25,1198.34,1221.06,5695470000 +2010-11-03,1193.79,1198.3,1183.56,1197.96,4665480000 +2010-11-02,1187.86,1195.88,1187.86,1193.57,3866200000 +2010-11-01,1185.71,1195.81,1177.65,1184.38,4129180000 +2010-10-29,1183.87,1185.46,1179.7,1183.26,3537880000 +2010-10-28,1184.47,1189.53,1177.1,1183.78,4283460000 +2010-10-27,1183.84,1183.84,1171.7,1182.45,4335670000 +2010-10-26,1184.88,1187.11,1177.72,1185.64,4203680000 +2010-10-25,1184.74,1196.14,1184.74,1185.62,4221380000 +2010-10-22,1180.52,1183.93,1178.99,1183.08,3177890000 +2010-10-21,1179.82,1189.43,1171.17,1180.26,4625470000 +2010-10-20,1166.74,1182.94,1166.74,1178.17,5027880000 +2010-10-19,1178.64,1178.64,1159.71,1165.9,5600120000 +2010-10-18,1176.83,1185.53,1174.55,1184.71,4450050000 +2010-10-15,1177.47,1181.2,1167.12,1176.19,5724910000 +2010-10-14,1177.82,1178.89,1166.71,1173.81,4969410000 +2010-10-13,1171.32,1184.38,1171.32,1178.1,4969410000 +2010-10-12,1164.28,1172.58,1155.71,1169.77,4076170000 +2010-10-11,1165.32,1168.68,1162.02,1165.32,2505900000 +2010-10-08,1158.36,1167.73,1155.58,1165.15,3871420000 +2010-10-07,1161.57,1163.87,1151.41,1158.06,3910550000 +2010-10-06,1159.81,1162.33,1154.85,1159.97,4073160000 +2010-10-05,1140.68,1162.76,1140.68,1160.75,4068840000 +2010-10-04,1144.96,1148.16,1131.87,1137.03,3604110000 +2010-10-01,1143.49,1150.3,1139.42,1146.24,4298910000 +2010-09-30,1145.97,1157.16,1136.08,1141.2,4284160000 +2010-09-29,1146.75,1148.63,1140.26,1144.73,3990280000 +2010-09-28,1142.31,1150,1132.09,1147.7,4025840000 +2010-09-27,1148.64,1149.92,1142,1142.16,3587860000 +2010-09-24,1131.69,1148.9,1131.69,1148.67,4123950000 +2010-09-23,1131.1,1136.77,1122.79,1124.83,3847850000 +2010-09-22,1139.49,1144.38,1131.58,1134.28,3911070000 +2010-09-21,1142.82,1148.59,1136.22,1139.78,4175660000 +2010-09-20,1126.57,1144.86,1126.57,1142.71,3364080000 +2010-09-17,1126.39,1131.47,1122.43,1125.59,4086140000 +2010-09-16,1123.89,1125.44,1118.88,1124.66,3364080000 +2010-09-15,1119.43,1126.46,1114.63,1125.07,3369840000 +2010-09-14,1121.16,1127.36,1115.58,1121.1,4521050000 +2010-09-13,1113.38,1123.87,1113.38,1121.9,4521050000 +2010-09-10,1104.57,1110.88,1103.92,1109.55,3061160000 +2010-09-09,1101.15,1110.27,1101.15,1104.18,3387770000 +2010-09-08,1092.36,1103.26,1092.36,1098.87,3224640000 +2010-09-07,1102.6,1102.6,1091.15,1091.84,3107380000 +2010-09-03,1093.61,1105.1,1093.61,1104.51,3534500000 +2010-09-02,1080.66,1090.1,1080.39,1090.1,3704210000 +2010-09-01,1049.72,1081.3,1049.72,1080.29,4396880000 +2010-08-31,1046.88,1055.14,1040.88,1049.33,4038770000 +2010-08-30,1062.9,1064.4,1048.79,1048.92,2917990000 +2010-08-27,1049.27,1065.21,1039.7,1064.59,4102460000 +2010-08-26,1056.28,1061.45,1045.4,1047.22,3646710000 +2010-08-25,1048.98,1059.38,1039.83,1055.33,4360190000 +2010-08-24,1063.2,1063.2,1046.68,1051.87,4436330000 +2010-08-23,1073.36,1081.58,1067.08,1067.36,3210950000 +2010-08-20,1075.63,1075.63,1063.91,1071.69,3761570000 +2010-08-19,1092.44,1092.44,1070.66,1075.63,4290540000 +2010-08-18,1092.08,1099.77,1085.76,1094.16,3724260000 +2010-08-17,1081.16,1100.14,1081.16,1092.54,3968210000 +2010-08-16,1077.49,1082.62,1069.49,1079.38,3142450000 +2010-08-13,1082.22,1086.25,1079,1079.25,3328890000 +2010-08-12,1081.48,1086.72,1076.69,1083.61,4521050000 +2010-08-11,1116.89,1116.89,1088.55,1089.47,4511860000 +2010-08-10,1122.92,1127.16,1111.58,1121.06,3979360000 +2010-08-09,1122.8,1129.24,1120.91,1127.79,3191630000 +2010-08-06,1122.07,1123.06,1107.17,1121.64,3857890000 +2010-08-05,1125.78,1126.56,1118.81,1125.81,3685560000 +2010-08-04,1121.06,1128.75,1119.46,1127.24,4057850000 +2010-08-03,1125.34,1125.44,1116.76,1120.46,4071820000 +2010-08-02,1107.53,1127.3,1107.53,1125.86,4144180000 +2010-07-30,1098.44,1106.44,1088.01,1101.6,4006450000 +2010-07-29,1108.07,1115.9,1092.82,1101.53,4612420000 +2010-07-28,1112.84,1114.66,1103.11,1106.13,4002390000 +2010-07-27,1117.36,1120.95,1109.78,1113.84,4725690000 +2010-07-26,1102.89,1115.01,1101.3,1115.01,4009650000 +2010-07-23,1092.17,1103.73,1087.88,1102.66,4524570000 +2010-07-22,1072.14,1097.5,1072.14,1093.67,4826900000 +2010-07-21,1086.67,1088.96,1065.25,1069.59,4747180000 +2010-07-20,1064.53,1083.94,1056.88,1083.48,4713280000 +2010-07-19,1066.85,1074.7,1061.11,1071.25,4089500000 +2010-07-16,1093.85,1093.85,1063.32,1064.88,5297350000 +2010-07-15,1094.46,1098.66,1080.53,1096.48,4552470000 +2010-07-14,1095.61,1099.08,1087.68,1095.17,4521050000 +2010-07-13,1080.65,1099.46,1080.65,1095.34,4640460000 +2010-07-12,1077.23,1080.78,1070.45,1078.75,3426990000 +2010-07-09,1070.5,1078.16,1068.1,1077.96,3506570000 +2010-07-08,1062.92,1071.25,1058.24,1070.25,4548460000 +2010-07-07,1028.54,1060.89,1028.54,1060.27,4931220000 +2010-07-06,1028.09,1042.5,1018.35,1028.06,4691240000 +2010-07-02,1027.65,1032.95,1015.93,1022.58,3968500000 +2010-07-01,1031.1,1033.58,1010.91,1027.37,6435770000 +2010-06-30,1040.56,1048.08,1028.33,1030.71,5067080000 +2010-06-29,1071.1,1071.1,1035.18,1041.24,6136700000 +2010-06-28,1077.5,1082.6,1071.45,1074.57,3896410000 +2010-06-25,1075.1,1083.56,1067.89,1076.76,5128840000 +2010-06-24,1090.93,1090.93,1071.6,1073.69,4814830000 +2010-06-23,1095.57,1099.64,1085.31,1092.04,4526150000 +2010-06-22,1113.9,1118.5,1094.18,1095.31,4514380000 +2010-06-21,1122.79,1131.23,1108.24,1113.2,4514360000 +2010-06-18,1116.16,1121.01,1113.93,1117.51,4555360000 +2010-06-17,1115.98,1117.72,1105.87,1116.04,4557760000 +2010-06-16,1114.02,1118.74,1107.13,1114.61,5002600000 +2010-06-15,1091.21,1115.59,1091.21,1115.23,4644490000 +2010-06-14,1095,1105.91,1089.03,1089.63,4425830000 +2010-06-11,1082.65,1092.25,1077.12,1091.6,4059280000 +2010-06-10,1058.77,1087.85,1058.77,1086.84,5144780000 +2010-06-09,1062.75,1077.74,1052.25,1055.69,5983200000 +2010-06-08,1050.81,1063.15,1042.17,1062,6192750000 +2010-06-07,1065.84,1071.36,1049.86,1050.47,5467560000 +2010-06-04,1098.43,1098.43,1060.5,1064.88,6180580000 +2010-06-03,1098.82,1105.67,1091.81,1102.83,4995970000 +2010-06-02,1073.01,1098.56,1072.03,1098.38,5026360000 +2010-06-01,1087.3,1094.77,1069.89,1070.71,5271480000 +2010-05-28,1102.59,1102.59,1084.78,1089.41,4871210000 +2010-05-27,1074.27,1103.52,1074.27,1103.06,5698460000 +2010-05-26,1075.51,1090.75,1065.59,1067.95,4521050000 +2010-05-25,1067.42,1074.75,1040.78,1074.03,7329580000 +2010-05-24,1084.78,1089.95,1072.7,1073.65,5224040000 +2010-05-21,1067.26,1090.16,1055.9,1087.69,5452130000 +2010-05-20,1107.34,1107.34,1071.58,1071.59,8328569600 +2010-05-19,1119.57,1124.27,1100.66,1115.05,6765800000 +2010-05-18,1138.78,1148.66,1117.2,1120.8,6170840000 +2010-05-17,1136.52,1141.88,1114.96,1136.94,5922920000 +2010-05-14,1157.19,1157.19,1126.14,1135.68,6126400000 +2010-05-13,1170.04,1173.57,1156.14,1157.44,4870640000 +2010-05-12,1155.43,1172.87,1155.43,1171.67,5225460000 +2010-05-11,1156.39,1170.48,1147.71,1155.79,5842550000 +2010-05-10,1122.27,1163.85,1122.27,1159.73,6893700000 +2010-05-07,1127.04,1135.13,1094.15,1110.88,9472910400 +2010-05-06,1164.38,1167.58,1065.79,1128.15,10617809600 +2010-05-05,1169.24,1175.95,1158.15,1165.87,6795940000 +2010-05-04,1197.5,1197.5,1168.12,1173.6,6594720000 +2010-05-03,1188.58,1205.13,1188.58,1202.26,4938050000 +2010-04-30,1206.77,1207.99,1186.32,1186.69,6048260000 +2010-04-29,1193.3,1209.36,1193.3,1206.78,6059410000 +2010-04-28,1184.59,1195.05,1181.81,1191.36,6342310000 +2010-04-27,1209.92,1211.38,1181.62,1183.71,7454540000 +2010-04-26,1217.07,1219.8,1211.07,1212.05,5647760000 +2010-04-23,1207.87,1217.28,1205.1,1217.28,5326060000 +2010-04-22,1202.52,1210.27,1190.19,1208.67,6035780000 +2010-04-21,1207.16,1210.99,1198.85,1205.94,5724310000 +2010-04-20,1199.04,1208.58,1199.04,1207.17,5316590000 +2010-04-19,1192.06,1197.87,1183.68,1197.52,6597740000 +2010-04-16,1210.17,1210.17,1186.77,1192.13,8108470400 +2010-04-15,1210.77,1213.92,1208.5,1211.67,5995330000 +2010-04-14,1198.69,1210.65,1198.69,1210.65,5760040000 +2010-04-13,1195.94,1199.04,1188.82,1197.3,5403580000 +2010-04-12,1194.94,1199.2,1194.71,1196.48,4607090000 +2010-04-09,1187.47,1194.66,1187.15,1194.37,4511570000 +2010-04-08,1181.75,1188.55,1175.12,1186.44,4726970000 +2010-04-07,1188.23,1189.6,1177.25,1182.45,5101430000 +2010-04-06,1186.01,1191.8,1182.77,1189.44,4086180000 +2010-04-05,1178.71,1187.73,1178.71,1187.44,3881620000 +2010-04-01,1171.23,1181.43,1170.69,1178.1,4006870000 +2010-03-31,1171.75,1174.56,1165.77,1169.43,4484340000 +2010-03-30,1173.75,1177.83,1168.92,1173.27,4085000000 +2010-03-29,1167.71,1174.85,1167.71,1173.22,4375580000 +2010-03-26,1167.58,1173.93,1161.48,1166.59,4708420000 +2010-03-25,1170.03,1180.69,1165.09,1165.73,5668900000 +2010-03-24,1172.7,1173.04,1166.01,1167.72,4705750000 +2010-03-23,1166.47,1174.72,1163.83,1174.17,4411640000 +2010-03-22,1157.25,1167.82,1152.88,1165.81,4261680000 +2010-03-19,1166.68,1169.2,1155.33,1159.9,5212410000 +2010-03-18,1166.13,1167.77,1161.16,1165.83,4234510000 +2010-03-17,1159.94,1169.84,1159.94,1166.21,4963200000 +2010-03-16,1150.83,1160.28,1150.35,1159.46,4369770000 +2010-03-15,1148.53,1150.98,1141.45,1150.51,4164110000 +2010-03-12,1151.71,1153.41,1146.97,1149.99,4928160000 +2010-03-11,1143.96,1150.24,1138.99,1150.24,4669060000 +2010-03-10,1140.22,1148.26,1140.09,1145.61,5469120000 +2010-03-09,1137.56,1145.37,1134.9,1140.45,5185570000 +2010-03-08,1138.4,1141.05,1136.77,1138.5,3774680000 +2010-03-05,1125.12,1139.38,1125.12,1138.7,4133000000 +2010-03-04,1119.12,1123.73,1116.66,1122.97,3945010000 +2010-03-03,1119.36,1125.64,1116.58,1118.79,3951320000 +2010-03-02,1117.01,1123.46,1116.51,1118.31,4134680000 +2010-03-01,1105.36,1116.11,1105.36,1115.71,3847640000 +2010-02-26,1103.1,1107.24,1097.56,1104.49,3945190000 +2010-02-25,1101.24,1103.5,1086.02,1102.94,4521130000 +2010-02-24,1095.89,1106.42,1095.5,1105.24,4168360000 +2010-02-23,1107.49,1108.58,1092.18,1094.6,4521050000 +2010-02-22,1110,1112.29,1105.38,1108.01,3814440000 +2010-02-19,1105.49,1112.42,1100.8,1109.17,3944280000 +2010-02-18,1099.03,1108.24,1097.48,1106.75,3878620000 +2010-02-17,1096.14,1101.03,1094.72,1099.51,4259230000 +2010-02-16,1079.13,1095.67,1079.13,1094.87,4080770000 +2010-02-12,1075.95,1077.81,1062.97,1075.51,4160680000 +2010-02-11,1067.1,1080.04,1060.59,1078.47,4400870000 +2010-02-10,1069.68,1073.67,1059.34,1068.13,4251450000 +2010-02-09,1060.06,1079.28,1060.06,1070.52,5114260000 +2010-02-08,1065.51,1071.2,1056.51,1056.74,4089820000 +2010-02-05,1064.12,1067.13,1044.5,1066.19,6438900000 +2010-02-04,1097.25,1097.25,1062.78,1063.11,5859690000 +2010-02-03,1100.67,1102.72,1093.97,1097.28,4285450000 +2010-02-02,1090.05,1104.73,1087.96,1103.32,4749540000 +2010-02-01,1073.89,1089.38,1073.89,1089.19,4077610000 +2010-01-29,1087.61,1096.45,1071.59,1073.87,5412850000 +2010-01-28,1096.93,1100.22,1078.46,1084.53,5452400000 +2010-01-27,1091.94,1099.51,1083.11,1097.5,5319120000 +2010-01-26,1095.8,1103.69,1089.86,1092.17,4731910000 +2010-01-25,1092.4,1102.97,1092.4,1096.78,4481390000 +2010-01-22,1115.49,1115.49,1090.18,1091.76,6208650000 +2010-01-21,1138.68,1141.58,1114.84,1116.48,6874289600 +2010-01-20,1147.95,1147.95,1129.25,1138.04,4810560000 +2010-01-19,1136.03,1150.45,1135.77,1150.23,4724830000 +2010-01-15,1147.72,1147.77,1131.39,1136.03,4758730000 +2010-01-14,1145.68,1150.41,1143.8,1148.46,3915200000 +2010-01-13,1137.31,1148.4,1133.18,1145.68,4170360000 +2010-01-12,1143.81,1143.81,1131.77,1136.22,4716160000 +2010-01-11,1145.96,1149.74,1142.02,1146.98,4255780000 +2010-01-08,1140.52,1145.39,1136.22,1144.98,4389590000 +2010-01-07,1136.27,1142.46,1131.32,1141.69,5270680000 +2010-01-06,1135.71,1139.19,1133.95,1137.14,4972660000 +2010-01-05,1132.66,1136.63,1129.66,1136.52,2491020000 +2010-01-04,1116.56,1133.87,1116.56,1132.99,3991400000 +2009-12-31,1126.6,1127.64,1114.81,1115.1,2076990000 +2009-12-30,1125.53,1126.42,1121.94,1126.42,2277300000 +2009-12-29,1128.55,1130.38,1126.08,1126.2,2491020000 +2009-12-28,1127.53,1130.38,1123.51,1127.78,2716400000 +2009-12-24,1121.08,1126.48,1121.08,1126.48,1267710000 +2009-12-23,1118.84,1121.58,1116,1120.59,3166870000 +2009-12-22,1114.51,1120.27,1114.51,1118.02,3641130000 +2009-12-21,1105.31,1117.68,1105.31,1114.05,3977340000 +2009-12-18,1097.86,1103.74,1093.88,1102.47,6325890000 +2009-12-17,1106.36,1106.36,1095.88,1096.08,7615070400 +2009-12-16,1108.61,1116.21,1107.96,1109.18,4829820000 +2009-12-15,1114.11,1114.11,1105.35,1107.93,5045100000 +2009-12-14,1107.84,1114.76,1107.84,1114.11,4548490000 +2009-12-11,1103.96,1108.5,1101.34,1106.41,3791090000 +2009-12-10,1098.69,1106.25,1098.69,1102.35,3996490000 +2009-12-09,1091.07,1097.04,1085.89,1095.95,4115410000 +2009-12-08,1103.04,1103.04,1088.61,1091.94,4748030000 +2009-12-07,1105.52,1110.72,1100.83,1103.25,4103360000 +2009-12-04,1100.43,1119.13,1096.52,1105.98,5781140000 +2009-12-03,1110.59,1117.28,1098.74,1099.92,4810030000 +2009-12-02,1109.03,1115.58,1105.29,1109.24,3941340000 +2009-12-01,1098.89,1112.28,1098.89,1108.86,4249310000 +2009-11-30,1091.07,1097.24,1086.25,1095.63,3895520000 +2009-11-27,1105.47,1105.47,1083.74,1091.49,2362910000 +2009-11-25,1106.49,1111.18,1104.75,1110.63,3036350000 +2009-11-24,1105.83,1107.56,1097.63,1105.65,3700820000 +2009-11-23,1094.86,1112.38,1094.86,1106.24,3827920000 +2009-11-20,1094.66,1094.66,1086.81,1091.38,3751230000 +2009-11-19,1106.44,1106.44,1088.4,1094.9,4178030000 +2009-11-18,1109.44,1111.1,1102.7,1109.8,4293340000 +2009-11-17,1109.22,1110.52,1102.19,1110.32,3824070000 +2009-11-16,1094.13,1113.69,1094.13,1109.3,4565850000 +2009-11-13,1087.59,1097.79,1085.33,1093.48,3792610000 +2009-11-12,1098.31,1101.97,1084.9,1087.24,4160250000 +2009-11-11,1096.04,1105.37,1093.81,1098.51,4286700000 +2009-11-10,1091.86,1096.42,1087.4,1093.01,4394770000 +2009-11-09,1072.31,1093.19,1072.31,1093.08,4460030000 +2009-11-06,1064.95,1071.48,1059.32,1069.3,4277130000 +2009-11-05,1047.3,1066.65,1047.3,1066.63,4848350000 +2009-11-04,1047.14,1061,1045.15,1046.5,5635510000 +2009-11-03,1040.92,1046.36,1033.94,1045.41,5487500000 +2009-11-02,1036.18,1052.18,1029.38,1042.88,6202640000 +2009-10-30,1065.41,1065.41,1033.38,1036.19,6512420000 +2009-10-29,1043.69,1066.83,1043.69,1066.11,5595040000 +2009-10-28,1061.51,1063.26,1042.19,1042.63,6600350000 +2009-10-27,1067.54,1072.48,1060.62,1063.41,5337380000 +2009-10-26,1080.36,1091.75,1065.23,1066.95,6363380000 +2009-10-23,1095.62,1095.83,1075.49,1079.6,4767460000 +2009-10-22,1080.96,1095.21,1074.31,1092.91,5192410000 +2009-10-21,1090.36,1101.36,1080.77,1081.4,5616290000 +2009-10-20,1098.64,1098.64,1086.16,1091.06,5396930000 +2009-10-19,1088.22,1100.17,1086.48,1097.91,4619240000 +2009-10-16,1094.67,1094.67,1081.53,1087.68,4894740000 +2009-10-15,1090.36,1096.56,1086.41,1096.56,5369780000 +2009-10-14,1078.68,1093.17,1078.68,1092.02,5406420000 +2009-10-13,1074.96,1075.3,1066.71,1073.19,4320480000 +2009-10-12,1071.63,1079.46,1071.63,1076.19,3710430000 +2009-10-09,1065.28,1071.51,1063,1071.49,3763780000 +2009-10-08,1060.03,1070.67,1060.03,1065.48,4988400000 +2009-10-07,1053.65,1058.02,1050.1,1057.58,4238220000 +2009-10-06,1042.02,1060.55,1042.02,1054.72,5029840000 +2009-10-05,1026.87,1042.58,1025.92,1040.46,4313310000 +2009-10-02,1029.71,1030.6,1019.95,1025.21,5583240000 +2009-10-01,1054.91,1054.91,1029.45,1029.85,5791450000 +2009-09-30,1061.02,1063.4,1046.47,1057.08,5998860000 +2009-09-29,1063.69,1069.62,1057.83,1060.61,4949900000 +2009-09-28,1045.38,1065.13,1045.38,1062.98,3726950000 +2009-09-25,1049.48,1053.47,1041.17,1044.38,4507090000 +2009-09-24,1062.56,1066.29,1045.85,1050.78,5505610000 +2009-09-23,1072.69,1080.15,1060.39,1060.87,5531930000 +2009-09-22,1066.35,1073.81,1066.35,1071.66,5246600000 +2009-09-21,1067.14,1067.28,1057.46,1064.66,4615280000 +2009-09-18,1066.6,1071.52,1064.27,1068.3,5607970000 +2009-09-17,1067.87,1074.77,1061.2,1065.49,6668110000 +2009-09-16,1053.99,1068.76,1052.87,1068.76,6793529600 +2009-09-15,1049.03,1056.04,1043.42,1052.63,6185620000 +2009-09-14,1040.15,1049.74,1035,1049.34,4979610000 +2009-09-11,1043.92,1048.18,1038.4,1042.73,4922600000 +2009-09-10,1032.99,1044.14,1028.04,1044.14,5191380000 +2009-09-09,1025.36,1036.34,1023.97,1033.37,5202550000 +2009-09-08,1018.67,1026.07,1018.67,1025.39,5235160000 +2009-09-04,1003.84,1016.48,1001.65,1016.4,4097370000 +2009-09-03,996.12,1003.43,992.25,1003.24,4624280000 +2009-09-02,996.07,1000.34,991.97,994.75,5842730000 +2009-09-01,1019.52,1028.45,996.28,998.04,6862360000 +2009-08-31,1025.21,1025.21,1014.62,1020.62,5004560000 +2009-08-28,1031.62,1039.47,1023.13,1028.93,5785780000 +2009-08-27,1027.81,1033.33,1016.2,1030.98,5785880000 +2009-08-26,1027.35,1032.47,1021.57,1028.12,5080060000 +2009-08-25,1026.63,1037.75,1026.21,1028,5768740000 +2009-08-24,1026.59,1035.82,1022.48,1025.57,6302450000 +2009-08-21,1009.06,1027.59,1009.06,1026.13,5885550000 +2009-08-20,996.41,1008.92,996.39,1007.37,4893160000 +2009-08-19,986.88,999.61,980.62,996.46,4257000000 +2009-08-18,980.62,991.2,980.62,989.67,4198970000 +2009-08-17,998.18,998.18,978.51,979.73,4088570000 +2009-08-14,1012.23,1012.6,994.6,1004.09,4940750000 +2009-08-13,1005.86,1013.14,1000.82,1012.73,5250660000 +2009-08-12,994,1012.78,993.36,1005.81,5498170000 +2009-08-11,1005.77,1005.77,992.4,994.35,5773160000 +2009-08-10,1008.89,1010.12,1000.99,1007.1,5406080000 +2009-08-07,999.83,1018,999.83,1010.48,6827089600 +2009-08-06,1004.06,1008,992.49,997.08,6753380000 +2009-08-05,1005.41,1006.64,994.31,1002.72,7242120000 +2009-08-04,1001.41,1007.12,996.68,1005.65,5713700000 +2009-08-03,990.22,1003.61,990.22,1002.63,5603440000 +2009-07-31,986.8,993.18,982.85,987.48,5139070000 +2009-07-30,976.01,996.68,976.01,986.75,6035180000 +2009-07-29,977.66,977.76,968.65,975.15,5178770000 +2009-07-28,981.48,982.35,969.35,979.62,5490350000 +2009-07-27,978.63,982.49,972.29,982.18,4631290000 +2009-07-24,972.16,979.79,965.95,979.26,4458300000 +2009-07-23,954.07,979.42,953.27,976.29,5761650000 +2009-07-22,953.4,959.83,947.75,954.07,4634100000 +2009-07-21,951.97,956.53,943.22,954.58,5309300000 +2009-07-20,942.07,951.62,940.99,951.13,4853150000 +2009-07-17,940.56,941.89,934.65,940.38,5141380000 +2009-07-16,930.17,943.96,927.45,940.74,4898640000 +2009-07-15,910.15,933.95,910.15,932.68,5238830000 +2009-07-14,900.77,905.84,896.5,905.84,4149030000 +2009-07-13,879.57,901.05,875.32,901.05,4499440000 +2009-07-10,880.03,883.57,872.81,879.13,3912080000 +2009-07-09,881.28,887.86,878.45,882.68,4347170000 +2009-07-08,881.9,886.8,869.32,879.56,5721780000 +2009-07-07,898.6,898.6,879.93,881.03,4673300000 +2009-07-06,894.27,898.72,886.36,898.72,4712580000 +2009-07-02,921.24,921.24,896.42,896.42,3931000000 +2009-07-01,920.82,931.92,920.82,923.33,3919400000 +2009-06-30,927.15,930.01,912.86,919.32,4627570000 +2009-06-29,919.86,927.99,916.18,927.23,4211760000 +2009-06-26,918.84,922,913.03,918.9,6076660000 +2009-06-25,899.45,921.42,896.27,920.26,4911240000 +2009-06-24,896.31,910.85,896.31,900.94,4636720000 +2009-06-23,893.46,898.69,888.86,895.1,5071020000 +2009-06-22,918.13,918.13,893.04,893.04,4903940000 +2009-06-19,919.96,927.09,915.8,921.23,5713390000 +2009-06-18,910.86,921.93,907.94,918.37,4684010000 +2009-06-17,911.89,918.44,903.78,910.71,5523650000 +2009-06-16,925.6,928,911.6,911.97,4951200000 +2009-06-15,942.45,942.45,919.65,923.72,4697880000 +2009-06-12,943.44,946.3,935.66,946.21,4528120000 +2009-06-11,939.04,956.23,939.04,944.89,5500840000 +2009-06-10,942.73,949.77,927.97,939.15,5379420000 +2009-06-09,940.35,946.92,936.15,942.43,4439950000 +2009-06-08,938.12,946.33,926.44,939.14,4483430000 +2009-06-05,945.67,951.69,934.13,940.09,5277910000 +2009-06-04,932.49,942.47,929.32,942.46,5352890000 +2009-06-03,942.51,942.51,923.85,931.76,5323770000 +2009-06-02,942.87,949.38,938.46,944.74,5987340000 +2009-06-01,923.26,947.77,923.26,942.87,6370440000 +2009-05-29,907.02,920.02,903.56,919.14,6050420000 +2009-05-28,892.96,909.45,887.6,906.83,5738980000 +2009-05-27,909.95,913.84,891.87,893.06,5698800000 +2009-05-26,887,911.76,881.46,910.33,5667050000 +2009-05-22,888.68,896.65,883.75,887,5155320000 +2009-05-21,900.42,900.42,879.61,888.33,6019840000 +2009-05-20,908.62,924.6,901.37,903.47,8205060000 +2009-05-19,909.67,916.39,905.22,908.13,6616270000 +2009-05-18,886.07,910,886.07,909.71,5702150000 +2009-05-15,892.76,896.97,878.94,882.88,5439720000 +2009-05-14,884.24,898.36,882.52,893.07,6134870000 +2009-05-13,905.4,905.4,882.8,883.92,7091820000 +2009-05-12,910.52,915.57,896.46,908.35,6871750400 +2009-05-11,922.99,922.99,908.68,909.24,6150600000 +2009-05-08,909.03,930.17,909.03,929.23,8163280000 +2009-05-07,919.58,929.58,901.36,907.39,9120100000 +2009-05-06,903.95,920.28,903.95,919.53,8555040000 +2009-05-05,906.1,907.7,897.34,903.8,6882860000 +2009-05-04,879.21,907.85,879.21,907.24,7038840000 +2009-05-01,872.74,880.48,866.1,877.52,5312170000 +2009-04-30,876.59,888.7,868.51,872.81,6862540000 +2009-04-29,856.85,882.06,856.85,873.64,6101620000 +2009-04-28,854.48,864.48,847.12,855.16,6328000000 +2009-04-27,862.82,868.83,854.65,857.51,5613460000 +2009-04-24,853.91,871.8,853.91,866.23,7114440000 +2009-04-23,844.62,852.87,835.45,851.92,6563100000 +2009-04-22,847.26,861.78,840.57,843.55,7327860000 +2009-04-21,831.25,850.09,826.83,850.08,7436489600 +2009-04-20,868.27,868.27,832.39,832.39,6973960000 +2009-04-17,865.18,875.63,860.87,869.6,7352009600 +2009-04-16,854.54,870.35,847.04,865.3,6598670000 +2009-04-15,839.44,852.93,835.58,852.06,6241100000 +2009-04-14,856.88,856.88,840.25,841.5,7569840000 +2009-04-13,855.33,864.31,845.35,858.73,6434890000 +2009-04-09,829.29,856.91,829.29,856.56,7600710400 +2009-04-08,816.76,828.42,814.84,825.16,5938460000 +2009-04-07,834.12,834.12,814.53,815.55,5155580000 +2009-04-06,839.75,839.75,822.79,835.48,6210000000 +2009-04-03,835.13,842.5,826.7,842.5,5855640000 +2009-04-02,814.53,845.61,814.53,834.38,7542809600 +2009-04-01,793.59,813.62,783.32,811.08,6034140000 +2009-03-31,790.88,810.48,790.88,797.87,6089100000 +2009-03-30,809.07,809.07,779.81,787.53,5912660000 +2009-03-27,828.68,828.68,813.43,815.94,5600210000 +2009-03-26,814.06,832.98,814.06,832.86,6992960000 +2009-03-25,806.81,826.78,791.37,813.88,7687180000 +2009-03-24,820.6,823.65,805.48,806.12,6767980000 +2009-03-23,772.31,823.37,772.31,822.92,7715769600 +2009-03-20,784.58,788.91,766.2,768.54,7643720000 +2009-03-19,797.92,803.24,781.82,784.04,9033870400 +2009-03-18,776.01,803.04,765.64,794.35,9098449600 +2009-03-17,753.88,778.12,749.93,778.12,6156800000 +2009-03-16,758.84,774.53,753.37,753.89,7883540000 +2009-03-13,751.97,758.29,742.46,756.55,6787089600 +2009-03-12,720.89,752.63,714.76,750.74,7326630400 +2009-03-11,719.59,731.92,713.85,721.36,7287809600 +2009-03-10,679.28,719.6,679.28,719.6,8618329600 +2009-03-09,680.76,695.27,672.88,676.53,7277320000 +2009-03-06,684.04,699.09,666.79,683.38,7331830400 +2009-03-05,708.27,708.27,677.93,682.55,7507249600 +2009-03-04,698.6,724.12,698.6,712.87,7673620000 +2009-03-03,704.44,711.67,692.3,696.33,7583230400 +2009-03-02,729.57,729.57,699.7,700.82,7868289600 +2009-02-27,749.93,751.27,734.52,735.09,8926480000 +2009-02-26,765.76,779.42,751.75,752.83,7599969600 +2009-02-25,770.64,780.12,752.89,764.9,7483640000 +2009-02-24,744.69,775.49,744.69,773.14,7234489600 +2009-02-23,773.25,777.85,742.37,743.33,6509300000 +2009-02-20,775.87,778.69,754.25,770.05,8210590400 +2009-02-19,787.91,797.58,777.03,778.94,5746940000 +2009-02-18,791.06,796.17,780.43,788.42,5740710000 +2009-02-17,818.61,818.61,789.17,789.17,5907820000 +2009-02-13,833.95,839.43,825.21,826.84,5296650000 +2009-02-12,829.91,835.48,808.06,835.19,6476460000 +2009-02-11,827.41,838.22,822.3,833.74,5926460000 +2009-02-10,866.87,868.05,822.99,827.16,6770169600 +2009-02-09,868.24,875.01,861.65,869.89,5574370000 +2009-02-06,846.09,870.75,845.42,868.6,6484100000 +2009-02-05,831.75,850.55,819.91,845.85,6624030000 +2009-02-04,837.77,851.85,829.18,832.23,6420450000 +2009-02-03,825.69,842.6,821.98,838.51,5886310000 +2009-02-02,823.09,830.78,812.87,825.44,5673270000 +2009-01-30,845.69,851.66,821.67,825.88,5350580000 +2009-01-29,868.89,868.89,844.15,845.14,5067060000 +2009-01-28,845.73,877.86,845.73,874.09,6199180000 +2009-01-27,837.3,850.45,835.4,845.71,5353260000 +2009-01-26,832.5,852.53,827.69,836.57,6039940000 +2009-01-23,822.16,838.61,806.07,831.95,5832160000 +2009-01-22,839.74,839.74,811.29,827.5,5843830000 +2009-01-21,806.77,841.72,804.3,840.24,6467830000 +2009-01-20,849.64,849.64,804.47,805.22,6375230000 +2009-01-16,844.45,858.13,830.66,850.12,6786040000 +2009-01-15,841.99,851.59,817.04,843.74,7807350400 +2009-01-14,867.28,867.28,836.93,842.62,5407880000 +2009-01-13,869.79,877.02,862.02,871.79,5017470000 +2009-01-12,890.4,890.4,864.32,870.26,4725050000 +2009-01-09,909.91,911.93,888.31,890.35,4716500000 +2009-01-08,905.73,910,896.81,909.73,4991550000 +2009-01-07,927.45,927.45,902.37,906.65,4704940000 +2009-01-06,931.17,943.85,927.28,934.7,5392620000 +2009-01-05,929.17,936.63,919.53,927.45,5413910000 +2009-01-02,902.99,934.73,899.35,931.8,4048270000 +2008-12-31,890.59,910.32,889.67,903.25,4172940000 +2008-12-30,870.58,891.12,870.58,890.64,3627800000 +2008-12-29,872.37,873.7,857.07,869.42,3323430000 +2008-12-26,869.51,873.74,866.52,872.8,1880050000 +2008-12-24,863.87,869.79,861.44,868.15,1546550000 +2008-12-23,874.31,880.44,860.1,863.16,4051970000 +2008-12-22,887.2,887.37,857.09,871.63,4869850000 +2008-12-19,886.96,905.47,883.02,887.88,6705310000 +2008-12-18,905.98,911.02,877.44,885.28,5675000000 +2008-12-17,908.16,918.85,895.94,904.42,5907380000 +2008-12-16,871.53,914.66,871.53,913.18,6009780000 +2008-12-15,881.07,884.63,857.72,868.57,4982390000 +2008-12-12,871.79,883.24,851.35,879.73,5959590000 +2008-12-11,898.35,904.63,868.73,873.59,5513840000 +2008-12-10,892.17,908.27,885.45,899.24,5942130000 +2008-12-09,906.48,916.26,885.38,888.67,5693110000 +2008-12-08,882.71,918.57,882.71,909.7,6553600000 +2008-12-05,844.43,879.42,818.41,876.07,6165370000 +2008-12-04,869.75,875.6,833.6,845.22,5860390000 +2008-12-03,843.6,873.12,827.6,870.74,6221880000 +2008-12-02,817.94,850.54,817.94,848.81,6170100000 +2008-12-01,888.61,888.61,815.69,816.21,6052010000 +2008-11-28,886.89,896.25,881.21,896.24,2740860000 +2008-11-26,852.9,887.68,841.37,887.68,5793260000 +2008-11-25,853.4,868.94,834.99,857.39,6952700000 +2008-11-24,801.2,865.6,801.2,851.81,7879440000 +2008-11-21,755.84,801.2,741.02,800.03,9495900000 +2008-11-20,805.87,820.52,747.78,752.44,9093740000 +2008-11-19,859.03,864.57,806.18,806.58,6548600000 +2008-11-18,852.34,865.9,826.84,859.12,6679470000 +2008-11-17,873.23,882.29,848.98,850.75,4927490000 +2008-11-14,904.36,916.88,869.88,873.29,5881030000 +2008-11-13,853.13,913.01,818.69,911.29,7849120000 +2008-11-12,893.39,893.39,850.48,852.3,5764180000 +2008-11-11,917.15,917.15,884.9,898.95,4998340000 +2008-11-10,936.75,951.95,907.47,919.21,4572000000 +2008-11-07,907.44,931.46,906.9,930.99,4931640000 +2008-11-06,952.4,952.4,899.73,904.88,6102230000 +2008-11-05,1001.84,1001.84,949.86,952.77,5426640000 +2008-11-04,971.31,1007.51,971.31,1005.75,5531290000 +2008-11-03,968.67,975.57,958.82,966.3,4492280000 +2008-10-31,953.11,984.38,944.59,968.75,6394350000 +2008-10-30,939.38,963.23,928.5,954.09,6175830000 +2008-10-29,939.51,969.97,922.26,930.09,7077800000 +2008-10-28,848.92,940.51,845.27,940.51,7096950400 +2008-10-27,874.28,893.78,846.75,848.92,5558050000 +2008-10-24,895.22,896.3,852.85,876.77,6550050000 +2008-10-23,899.08,922.83,858.44,908.11,7184180000 +2008-10-22,951.67,951.67,875.81,896.78,6147980000 +2008-10-21,980.4,985.44,952.47,955.05,5121830000 +2008-10-20,943.51,985.4,943.51,985.4,5175640000 +2008-10-17,942.29,984.64,918.74,940.55,6581780000 +2008-10-16,909.53,947.71,865.83,946.43,7984500000 +2008-10-15,994.6,994.6,903.99,907.84,6542330000 +2008-10-14,1009.97,1044.31,972.07,998.01,8161990400 +2008-10-13,912.75,1006.93,912.75,1003.35,7263369600 +2008-10-10,902.31,936.36,839.8,899.22,11456230400 +2008-10-09,988.42,1005.25,909.19,909.92,6819000000 +2008-10-08,988.91,1021.06,970.97,984.94,8716329600 +2008-10-07,1057.6,1072.91,996.23,996.23,7069209600 +2008-10-06,1097.56,1097.56,1007.97,1056.89,7956020000 +2008-10-03,1115.16,1153.82,1098.14,1099.23,6716120000 +2008-10-02,1160.64,1160.64,1111.43,1114.28,6285640000 +2008-10-01,1164.17,1167.03,1140.77,1161.06,5782130000 +2008-09-30,1113.78,1168.03,1113.78,1166.36,4937680000 +2008-09-29,1209.07,1209.07,1106.42,1106.42,7305060000 +2008-09-26,1204.47,1215.77,1187.54,1213.27,5383610000 +2008-09-25,1187.87,1220.03,1187.87,1209.18,5877640000 +2008-09-24,1188.79,1197.41,1179.79,1185.87,4820360000 +2008-09-23,1207.61,1221.15,1187.06,1188.22,5185730000 +2008-09-22,1255.37,1255.37,1205.61,1207.09,5332130000 +2008-09-19,1213.11,1265.12,1213.11,1255.08,9387169600 +2008-09-18,1157.08,1211.14,1133.5,1206.51,10082689600 +2008-09-17,1210.34,1210.34,1155.88,1156.39,9431870400 +2008-09-16,1188.31,1214.84,1169.28,1213.6,9459830400 +2008-09-15,1250.92,1250.92,1192.7,1192.7,8279510400 +2008-09-12,1245.88,1255.09,1233.81,1251.7,6273260000 +2008-09-11,1229.04,1249.98,1211.54,1249.05,6869249600 +2008-09-10,1227.5,1243.9,1221.6,1232.04,6543440000 +2008-09-09,1267.98,1268.66,1224.51,1224.51,7380630400 +2008-09-08,1249.5,1274.42,1247.12,1267.79,7351340000 +2008-09-05,1233.21,1244.94,1217.23,1242.31,5017080000 +2008-09-04,1271.8,1271.8,1232.83,1236.83,5212500000 +2008-09-03,1276.61,1280.6,1265.59,1274.98,5056980000 +2008-09-02,1287.83,1303.04,1272.2,1277.58,4783560000 +2008-08-29,1296.49,1297.59,1282.74,1282.83,3288120000 +2008-08-28,1283.79,1300.68,1283.79,1300.68,3854280000 +2008-08-27,1271.29,1285.05,1270.03,1281.66,3499610000 +2008-08-26,1267.03,1275.65,1263.21,1271.51,3587570000 +2008-08-25,1290.47,1290.47,1264.87,1266.84,3420600000 +2008-08-22,1277.59,1293.09,1277.59,1292.2,3741070000 +2008-08-21,1271.07,1281.4,1265.22,1277.72,4032590000 +2008-08-20,1267.34,1276.01,1261.16,1274.54,4555030000 +2008-08-19,1276.65,1276.65,1263.11,1266.69,4159760000 +2008-08-18,1298.14,1300.22,1274.51,1278.6,3829290000 +2008-08-15,1293.85,1302.05,1290.74,1298.2,4041820000 +2008-08-14,1282.11,1300.11,1276.84,1292.93,4064000000 +2008-08-13,1288.64,1294.03,1274.86,1285.83,4787600000 +2008-08-12,1304.79,1304.79,1285.64,1289.59,4711290000 +2008-08-11,1294.42,1313.15,1291.41,1305.32,5067310000 +2008-08-08,1266.29,1297.85,1262.11,1296.32,4966810000 +2008-08-07,1286.51,1286.51,1264.29,1266.07,5319380000 +2008-08-06,1283.99,1291.67,1276,1289.19,4873420000 +2008-08-05,1254.87,1284.88,1254.67,1284.88,1219310000 +2008-08-04,1253.27,1260.49,1247.45,1249.01,4562280000 +2008-08-01,1269.42,1270.52,1254.54,1260.31,4684870000 +2008-07-31,1281.37,1284.93,1265.97,1267.38,5346050000 +2008-07-30,1264.52,1284.33,1264.52,1284.26,5631330000 +2008-07-29,1236.38,1263.2,1236.38,1263.2,5414240000 +2008-07-28,1257.76,1260.09,1234.37,1234.37,4282960000 +2008-07-25,1253.51,1263.23,1251.75,1257.76,4672560000 +2008-07-24,1283.22,1283.22,1251.48,1252.54,6127980000 +2008-07-23,1278.87,1291.17,1276.06,1282.19,6705830000 +2008-07-22,1257.08,1277.42,1248.83,1277,6180230000 +2008-07-21,1261.82,1267.74,1255.7,1260,4630640000 +2008-07-18,1258.22,1262.23,1251.81,1260.68,5653280000 +2008-07-17,1246.31,1262.31,1241.49,1260.32,7365209600 +2008-07-16,1214.65,1245.52,1211.39,1245.36,6738630400 +2008-07-15,1226.83,1234.35,1200.44,1214.91,7363640000 +2008-07-14,1241.61,1253.5,1225.01,1228.3,5434860000 +2008-07-11,1248.66,1257.27,1225.35,1239.49,6742200000 +2008-07-10,1245.25,1257.65,1236.76,1253.39,5840430000 +2008-07-09,1273.38,1277.36,1244.57,1244.69,5181000000 +2008-07-08,1251.84,1274.17,1242.84,1273.7,6034110000 +2008-07-07,1262.9,1273.95,1240.68,1252.31,5265420000 +2008-07-03,1262.96,1271.48,1252.01,1262.9,3247590000 +2008-07-02,1285.82,1292.17,1261.51,1261.52,5276090000 +2008-07-01,1276.69,1285.31,1260.68,1284.91,5846290000 +2008-06-30,1278.06,1290.31,1274.86,1280,5032330000 +2008-06-27,1283.6,1289.45,1272,1278.38,6208260000 +2008-06-26,1316.29,1316.29,1283.15,1283.15,5231280000 +2008-06-25,1314.54,1335.63,1314.54,1321.97,4825640000 +2008-06-24,1317.23,1326.02,1304.42,1314.29,4705050000 +2008-06-23,1319.77,1323.78,1315.31,1318,4186370000 +2008-06-20,1341.02,1341.02,1314.46,1317.93,5324900000 +2008-06-19,1336.89,1347.66,1330.5,1342.83,4811670000 +2008-06-18,1349.59,1349.59,1333.4,1337.81,4573570000 +2008-06-17,1360.71,1366.59,1350.54,1350.93,3801960000 +2008-06-16,1358.85,1364.7,1352.07,1360.14,3706940000 +2008-06-13,1341.81,1360.03,1341.71,1360.03,4080420000 +2008-06-12,1335.78,1353.03,1331.29,1339.87,4734240000 +2008-06-11,1357.09,1357.09,1335.47,1335.49,4779980000 +2008-06-10,1358.98,1366.84,1351.56,1358.44,4635070000 +2008-06-09,1360.83,1370.63,1350.62,1361.76,4404570000 +2008-06-06,1400.06,1400.06,1359.9,1360.68,4771660000 +2008-06-05,1377.48,1404.05,1377.48,1404.05,4350790000 +2008-06-04,1376.26,1388.18,1371.74,1377.2,4338640000 +2008-06-03,1386.42,1393.12,1370.12,1377.65,4396380000 +2008-06-02,1399.62,1399.62,1377.79,1385.67,3714320000 +2008-05-30,1398.36,1404.46,1398.08,1400.38,3845630000 +2008-05-29,1390.5,1406.32,1388.59,1398.26,3894440000 +2008-05-28,1386.54,1391.25,1378.16,1390.84,3927240000 +2008-05-27,1375.97,1387.4,1373.07,1385.35,3588860000 +2008-05-23,1392.2,1392.2,1373.72,1375.93,3516380000 +2008-05-22,1390.83,1399.07,1390.23,1394.35,3955960000 +2008-05-21,1414.06,1419.12,1388.81,1390.71,4517990000 +2008-05-20,1424.49,1424.49,1409.09,1413.4,3854320000 +2008-05-19,1425.28,1440.24,1421.63,1426.63,3683970000 +2008-05-16,1423.89,1425.82,1414.35,1425.35,3842590000 +2008-05-15,1408.36,1424.4,1406.87,1423.57,3836480000 +2008-05-14,1405.65,1420.19,1405.65,1408.66,3979370000 +2008-05-13,1404.4,1406.3,1396.26,1403.04,4018590000 +2008-05-12,1389.4,1404.06,1386.2,1403.58,3370630000 +2008-05-09,1394.9,1394.9,1384.11,1388.28,3518620000 +2008-05-08,1394.29,1402.35,1389.39,1397.68,3827550000 +2008-05-07,1417.49,1419.54,1391.16,1392.57,4075860000 +2008-05-06,1405.6,1421.57,1397.1,1418.26,3924100000 +2008-05-05,1415.34,1415.34,1404.37,1407.49,3410090000 +2008-05-02,1409.16,1422.72,1406.25,1413.9,3953030000 +2008-05-01,1385.97,1410.07,1383.07,1409.34,4448780000 +2008-04-30,1391.22,1404.57,1384.25,1385.59,4508890000 +2008-04-29,1395.61,1397,1386.7,1390.94,3815320000 +2008-04-28,1397.96,1402.9,1394.4,1396.37,3607000000 +2008-04-25,1387.88,1399.11,1379.98,1397.84,3891150000 +2008-04-24,1380.52,1397.72,1371.09,1388.82,4461660000 +2008-04-23,1378.4,1387.87,1372.24,1379.93,4103610000 +2008-04-22,1386.43,1386.43,1369.84,1375.94,3821900000 +2008-04-21,1387.72,1390.23,1379.25,1388.17,3420570000 +2008-04-18,1369,1395.9,1369,1390.33,4222380000 +2008-04-17,1363.37,1368.6,1357.25,1365.56,3713880000 +2008-04-16,1337.02,1365.49,1337.02,1364.71,4260370000 +2008-04-15,1331.72,1337.72,1324.35,1334.43,3581230000 +2008-04-14,1332.2,1335.64,1326.16,1328.32,3565020000 +2008-04-11,1357.98,1357.98,1331.21,1332.83,3723790000 +2008-04-10,1355.37,1367.24,1350.11,1360.55,3686150000 +2008-04-09,1365.5,1368.39,1349.97,1354.49,3556670000 +2008-04-08,1370.16,1370.16,1360.62,1365.54,3602500000 +2008-04-07,1373.69,1386.74,1369.02,1372.54,3747780000 +2008-04-04,1369.85,1380.91,1362.83,1370.4,3703100000 +2008-04-03,1365.69,1375.66,1358.68,1369.31,3920100000 +2008-04-02,1369.96,1377.95,1361.55,1367.53,4320440000 +2008-04-01,1326.41,1370.18,1326.41,1370.18,4745120000 +2008-03-31,1315.92,1328.52,1312.81,1322.7,4188990000 +2008-03-28,1327.02,1334.87,1312.95,1315.22,3686980000 +2008-03-27,1340.34,1345.62,1325.66,1325.76,4037930000 +2008-03-26,1352.45,1352.45,1336.41,1341.13,4055670000 +2008-03-25,1349.07,1357.47,1341.21,1352.99,4145120000 +2008-03-24,1330.29,1359.68,1330.29,1349.88,4499000000 +2008-03-20,1299.67,1330.67,1295.22,1329.51,6145220000 +2008-03-19,1330.97,1341.51,1298.42,1298.42,1203830000 +2008-03-18,1277.16,1330.74,1277.16,1330.74,5335630000 +2008-03-17,1283.21,1287.5,1256.98,1276.6,5683010000 +2008-03-14,1316.05,1321.47,1274.86,1288.14,5153780000 +2008-03-13,1305.26,1321.68,1282.11,1315.48,5073360000 +2008-03-12,1321.13,1333.26,1307.86,1308.77,4414280000 +2008-03-11,1274.4,1320.65,1274.4,1320.65,5109080000 +2008-03-10,1293.16,1295.01,1272.66,1273.37,4261240000 +2008-03-07,1301.53,1313.24,1282.43,1293.37,4565410000 +2008-03-06,1332.2,1332.2,1303.42,1304.34,4323460000 +2008-03-05,1327.69,1344.19,1320.22,1333.7,4277710000 +2008-03-04,1329.58,1331.03,1307.39,1326.75,4757180000 +2008-03-03,1330.45,1335.13,1320.04,1331.34,4117570000 +2008-02-29,1364.07,1364.07,1325.42,1330.63,4426730000 +2008-02-28,1378.16,1378.16,1363.16,1367.68,3938580000 +2008-02-27,1378.95,1388.34,1372,1380.02,3904700000 +2008-02-26,1371.76,1387.34,1363.29,1381.29,4096060000 +2008-02-25,1352.75,1374.36,1346.03,1371.8,3866350000 +2008-02-22,1344.22,1354.3,1327.04,1353.11,3572660000 +2008-02-21,1362.21,1367.94,1339.34,1342.53,3696660000 +2008-02-20,1348.39,1363.71,1336.55,1360.03,3870520000 +2008-02-19,1355.86,1367.28,1345.05,1348.78,3613550000 +2008-02-15,1347.52,1350,1338.13,1349.99,3583300000 +2008-02-14,1367.33,1368.16,1347.31,1348.86,3644760000 +2008-02-13,1353.12,1369.23,1350.78,1367.21,3856420000 +2008-02-12,1340.55,1362.1,1339.36,1348.86,4044640000 +2008-02-11,1331.92,1341.4,1320.32,1339.13,3593140000 +2008-02-08,1336.88,1341.22,1321.06,1331.29,3768490000 +2008-02-07,1324.01,1347.16,1316.75,1336.91,4589160000 +2008-02-06,1339.48,1351.96,1324.34,1326.45,4008120000 +2008-02-05,1380.28,1380.28,1336.64,1336.64,4315740000 +2008-02-04,1395.38,1395.38,1379.69,1380.82,3495780000 +2008-02-01,1378.6,1396.02,1375.93,1395.42,4650770000 +2008-01-31,1351.98,1385.62,1334.08,1378.55,4970290000 +2008-01-30,1362.22,1385.86,1352.95,1355.81,4742760000 +2008-01-29,1355.94,1364.93,1350.19,1362.3,4232960000 +2008-01-28,1330.7,1353.97,1322.26,1353.96,4100930000 +2008-01-25,1357.32,1368.56,1327.5,1330.61,4882250000 +2008-01-24,1340.13,1355.15,1334.31,1352.07,5735300000 +2008-01-23,1310.41,1339.09,1270.05,1338.6,3241680000 +2008-01-22,1312.94,1322.09,1274.29,1310.5,6544690000 +2008-01-18,1333.9,1350.28,1312.51,1325.19,6004840000 +2008-01-17,1374.79,1377.72,1330.67,1333.25,5303130000 +2008-01-16,1377.41,1391.99,1364.27,1373.2,5440620000 +2008-01-15,1411.88,1411.88,1380.6,1380.95,4601640000 +2008-01-14,1402.91,1417.89,1402.91,1416.25,3682090000 +2008-01-11,1419.91,1419.91,1394.83,1401.02,4495840000 +2008-01-10,1406.78,1429.09,1395.31,1420.33,5170490000 +2008-01-09,1390.25,1409.19,1378.7,1409.13,5351030000 +2008-01-08,1415.71,1430.28,1388.3,1390.19,4705390000 +2008-01-07,1414.07,1423.87,1403.45,1416.18,4221260000 +2008-01-04,1444.01,1444.01,1411.19,1411.63,4166000000 +2008-01-03,1447.55,1456.8,1443.73,1447.16,3429500000 +2008-01-02,1467.97,1471.77,1442.07,1447.16,3452650000 +2007-12-31,1475.25,1475.83,1465.13,1468.36,2440880000 +2007-12-28,1479.83,1488.01,1471.7,1478.49,2420510000 +2007-12-27,1495.05,1495.05,1475.86,1476.27,2365770000 +2007-12-26,1495.12,1498.85,1488.2,1497.66,2010500000 +2007-12-24,1484.55,1497.63,1484.55,1496.45,1267420000 +2007-12-21,1463.19,1485.4,1463.19,1484.46,4508590000 +2007-12-20,1456.42,1461.53,1447.22,1460.12,3526890000 +2007-12-19,1454.7,1464.42,1445.31,1453,3401300000 +2007-12-18,1445.92,1460.16,1435.65,1454.98,3723690000 +2007-12-17,1465.05,1465.05,1445.43,1445.9,3569030000 +2007-12-14,1486.19,1486.67,1467.78,1467.95,3401050000 +2007-12-13,1483.27,1489.4,1469.21,1488.41,3635170000 +2007-12-12,1487.58,1511.96,1468.23,1486.59,4482120000 +2007-12-11,1516.68,1523.57,1475.99,1477.65,4080180000 +2007-12-10,1505.11,1518.27,1504.96,1515.96,2911760000 +2007-12-07,1508.6,1510.63,1502.66,1504.66,3177710000 +2007-12-06,1484.59,1508.02,1482.19,1507.34,3568570000 +2007-12-05,1465.22,1486.09,1465.22,1485.01,3663660000 +2007-12-04,1471.34,1471.34,1460.66,1462.79,3343620000 +2007-12-03,1479.63,1481.16,1470.08,1472.42,3323250000 +2007-11-30,1471.83,1488.94,1470.89,1481.14,4422200000 +2007-11-29,1467.41,1473.81,1458.36,1469.72,3524730000 +2007-11-28,1432.95,1471.62,1432.95,1469.02,4508020000 +2007-11-27,1409.59,1429.49,1407.43,1428.23,4320720000 +2007-11-26,1440.74,1446.09,1406.1,1407.22,3706470000 +2007-11-23,1417.62,1440.86,1417.62,1440.7,1612720000 +2007-11-21,1434.71,1436.4,1415.64,1416.77,4076230000 +2007-11-20,1434.51,1452.64,1419.28,1439.7,4875150000 +2007-11-19,1456.7,1456.7,1430.42,1433.27,4119650000 +2007-11-16,1453.09,1462.18,1443.99,1458.74,4168870000 +2007-11-15,1468.04,1472.67,1443.49,1451.15,3941010000 +2007-11-14,1483.4,1492.14,1466.47,1470.58,4031470000 +2007-11-13,1441.35,1481.37,1441.35,1481.05,4141310000 +2007-11-12,1453.66,1464.94,1438.53,1439.18,4192520000 +2007-11-09,1467.59,1474.09,1448.51,1453.7,4587050000 +2007-11-08,1475.27,1482.5,1450.31,1474.77,5439720000 +2007-11-07,1515.46,1515.46,1475.04,1475.62,4353160000 +2007-11-06,1505.33,1520.77,1499.07,1520.27,3879160000 +2007-11-05,1505.61,1510.84,1489.95,1502.17,3819330000 +2007-11-02,1511.07,1513.15,1492.53,1509.65,4285990000 +2007-11-01,1545.79,1545.79,1506.66,1508.44,4241470000 +2007-10-31,1532.15,1552.76,1529.4,1549.38,3953070000 +2007-10-30,1539.42,1539.42,1529.55,1531.02,3212520000 +2007-10-29,1536.92,1544.67,1536.43,1540.98,3124480000 +2007-10-26,1522.17,1535.53,1520.18,1535.28,3612120000 +2007-10-25,1516.15,1523.24,1500.46,1514.4,4183960000 +2007-10-24,1516.61,1517.23,1489.56,1515.88,4003300000 +2007-10-23,1509.3,1520.01,1503.61,1519.59,3309120000 +2007-10-22,1497.79,1508.06,1490.4,1506.33,3471830000 +2007-10-19,1540,1540,1500.26,1500.63,4160970000 +2007-10-18,1539.29,1542.79,1531.76,1540.08,3203210000 +2007-10-17,1544.44,1550.66,1526.01,1541.24,3638070000 +2007-10-16,1547.81,1547.81,1536.29,1538.53,3234560000 +2007-10-15,1562.25,1564.74,1540.81,1548.71,3139290000 +2007-10-12,1555.41,1563.03,1554.09,1561.8,2788690000 +2007-10-11,1564.72,1576.09,1546.72,1554.41,3911260000 +2007-10-10,1564.98,1565.42,1555.46,1562.47,3044760000 +2007-10-09,1553.18,1565.26,1551.82,1565.15,2932040000 +2007-10-08,1556.51,1556.51,1549,1552.58,2040650000 +2007-10-05,1543.84,1561.91,1543.84,1557.59,2919030000 +2007-10-04,1539.91,1544.02,1537.63,1542.84,2690430000 +2007-10-03,1545.8,1545.84,1536.34,1539.59,3065320000 +2007-10-02,1546.96,1548.01,1540.37,1546.63,3101910000 +2007-10-01,1527.29,1549.02,1527.25,1547.04,3281990000 +2007-09-28,1531.24,1533.74,1521.99,1526.75,2925350000 +2007-09-27,1527.32,1532.46,1525.81,1531.38,2872180000 +2007-09-26,1518.62,1529.39,1518.62,1525.42,3237390000 +2007-09-25,1516.34,1518.27,1507.13,1517.21,3187770000 +2007-09-24,1525.75,1530.18,1516.15,1517.73,3131310000 +2007-09-21,1518.75,1530.89,1518.75,1525.75,3679460000 +2007-09-20,1528.69,1529.14,1516.42,1518.75,2957700000 +2007-09-19,1519.75,1538.74,1519.75,1529.03,3846750000 +2007-09-18,1476.63,1519.89,1476.63,1519.78,3708940000 +2007-09-17,1484.24,1484.24,1471.82,1476.65,2598390000 +2007-09-14,1483.95,1485.99,1473.18,1484.25,2641740000 +2007-09-13,1471.47,1489.58,1471.47,1483.95,2877080000 +2007-09-12,1471.1,1479.5,1465.75,1471.56,2885720000 +2007-09-11,1451.69,1472.48,1451.69,1471.49,3015330000 +2007-09-10,1453.5,1462.25,1439.29,1451.7,2835720000 +2007-09-07,1478.55,1478.55,1449.07,1453.55,3191080000 +2007-09-06,1472.03,1481.49,1467.41,1478.55,2459590000 +2007-09-05,1488.76,1488.76,1466.34,1472.29,2991600000 +2007-09-04,1473.96,1496.4,1472.15,1489.42,2766600000 +2007-08-31,1457.61,1481.47,1457.61,1473.99,2731610000 +2007-08-30,1463.67,1468.43,1451.25,1457.64,2582960000 +2007-08-29,1432.01,1463.76,1432.01,1463.76,2824070000 +2007-08-28,1466.72,1466.72,1432.01,1432.36,3078090000 +2007-08-27,1479.36,1479.36,1465.98,1466.79,2406180000 +2007-08-24,1462.34,1479.4,1460.54,1479.37,2541400000 +2007-08-23,1464.05,1472.06,1453.88,1462.5,3084390000 +2007-08-22,1447.03,1464.86,1447.03,1464.07,3309120000 +2007-08-21,1445.55,1455.32,1439.76,1447.12,3012150000 +2007-08-20,1445.94,1451.75,1430.54,1445.55,3321340000 +2007-08-17,1411.26,1450.33,1411.26,1445.94,3570040000 +2007-08-16,1406.64,1415.97,1370.6,1411.27,6509300000 +2007-08-15,1426.15,1440.78,1404.36,1406.7,4290930000 +2007-08-14,1452.87,1456.74,1426.2,1426.54,3814630000 +2007-08-13,1453.42,1466.29,1451.54,1452.92,3696280000 +2007-08-10,1453.09,1462.02,1429.74,1453.64,5345780000 +2007-08-09,1497.21,1497.21,1453.09,1453.09,5889600000 +2007-08-08,1476.22,1503.89,1476.22,1497.49,5499560000 +2007-08-07,1467.62,1488.3,1455.8,1476.71,4909390000 +2007-08-06,1433.04,1467.67,1427.39,1467.67,5067200000 +2007-08-03,1472.18,1473.23,1432.8,1433.06,4272110000 +2007-08-02,1465.46,1476.43,1460.58,1472.2,4368850000 +2007-08-01,1455.18,1468.38,1439.59,1465.81,5256780000 +2007-07-31,1473.9,1488.3,1454.25,1455.27,4524520000 +2007-07-30,1458.93,1477.88,1454.32,1473.91,4128780000 +2007-07-27,1482.44,1488.53,1458.95,1458.95,4784650000 +2007-07-26,1518.09,1518.09,1465.3,1482.66,4472550000 +2007-07-25,1511.03,1524.31,1503.73,1518.09,4283200000 +2007-07-24,1541.57,1541.57,1508.62,1511.04,4115830000 +2007-07-23,1534.06,1547.23,1534.06,1541.57,3102700000 +2007-07-20,1553.19,1553.19,1529.2,1534.1,3745780000 +2007-07-19,1546.13,1555.2,1546.13,1553.08,3251450000 +2007-07-18,1549.2,1549.2,1533.67,1546.17,3609220000 +2007-07-17,1549.52,1555.32,1547.74,1549.37,3007140000 +2007-07-16,1552.5,1555.9,1546.69,1549.52,2704110000 +2007-07-13,1547.68,1555.1,1544.85,1552.5,2801120000 +2007-07-12,1518.74,1547.92,1518.74,1547.7,3489600000 +2007-07-11,1509.93,1519.34,1506.1,1518.76,3082920000 +2007-07-10,1531.85,1531.85,1510.01,1510.12,3244280000 +2007-07-09,1530.43,1534.26,1527.45,1531.85,2715330000 +2007-07-06,1524.96,1532.4,1520.47,1530.44,2441520000 +2007-07-05,1524.86,1526.57,1517.72,1525.4,2622950000 +2007-07-03,1519.12,1526.01,1519.12,1524.87,1560790000 +2007-07-02,1504.66,1519.45,1504.66,1519.43,2644990000 +2007-06-29,1505.7,1517.53,1493.61,1503.35,3165410000 +2007-06-28,1506.32,1514.84,1503.41,1505.71,3006710000 +2007-06-27,1492.62,1506.8,1484.18,1506.34,3398150000 +2007-06-26,1497.68,1506.12,1490.54,1492.89,3398530000 +2007-06-25,1502.56,1514.29,1492.68,1497.74,3287250000 +2007-06-22,1522.19,1522.19,1500.74,1502.56,4284320000 +2007-06-21,1512.5,1522.9,1504.75,1522.19,3161110000 +2007-06-20,1533.68,1537.32,1512.36,1512.84,3286900000 +2007-06-19,1531.02,1535.85,1525.67,1533.7,2873590000 +2007-06-18,1532.9,1535.44,1529.31,1531.05,2480240000 +2007-06-15,1522.97,1538.71,1522.97,1532.91,3406030000 +2007-06-14,1515.58,1526.45,1515.58,1522.97,2813630000 +2007-06-13,1492.65,1515.7,1492.65,1515.67,3077930000 +2007-06-12,1509.12,1511.33,1492.97,1493,3056200000 +2007-06-11,1507.64,1515.53,1503.35,1509.12,2525280000 +2007-06-08,1490.71,1507.76,1487.41,1507.67,2993460000 +2007-06-07,1517.36,1517.36,1490.37,1490.72,3538470000 +2007-06-06,1530.57,1530.57,1514.13,1517.38,2964190000 +2007-06-05,1539.12,1539.12,1525.62,1530.95,2939450000 +2007-06-04,1536.28,1540.53,1532.31,1539.18,2738930000 +2007-06-01,1530.62,1540.56,1530.62,1536.34,2927020000 +2007-05-31,1530.19,1535.56,1528.26,1530.62,3335530000 +2007-05-30,1517.6,1530.23,1510.06,1530.23,2980210000 +2007-05-29,1515.55,1521.8,1512.02,1518.11,2571790000 +2007-05-25,1507.5,1517.41,1507.5,1515.73,2316250000 +2007-05-24,1522.1,1529.31,1505.18,1507.51,3365530000 +2007-05-23,1524.09,1532.43,1521.9,1522.28,3084260000 +2007-05-22,1525.1,1529.24,1522.05,1524.12,2860500000 +2007-05-21,1522.75,1529.87,1522.71,1525.1,3465360000 +2007-05-18,1512.74,1522.75,1512.74,1522.75,2959050000 +2007-05-17,1514.01,1517.14,1509.29,1512.75,2868640000 +2007-05-16,1500.75,1514.15,1500.75,1514.14,2915350000 +2007-05-15,1503.11,1514.83,1500.43,1501.19,3071020000 +2007-05-14,1505.76,1510.9,1498.34,1503.15,2776130000 +2007-05-11,1491.47,1506.24,1491.47,1505.85,2720780000 +2007-05-10,1512.33,1512.33,1491.42,1491.47,3031240000 +2007-05-09,1507.32,1513.8,1503.77,1512.58,2935550000 +2007-05-08,1509.36,1509.36,1500.66,1507.72,2795720000 +2007-05-07,1505.57,1511,1505.54,1509.48,2545090000 +2007-05-04,1502.35,1510.34,1501.8,1505.62,2761930000 +2007-05-03,1495.56,1503.34,1495.56,1502.39,3007970000 +2007-05-02,1486.13,1499.1,1486.13,1495.92,3189800000 +2007-05-01,1482.37,1487.27,1476.7,1486.3,3400350000 +2007-04-30,1494.07,1497.16,1482.29,1482.37,3093420000 +2007-04-27,1494.21,1497.32,1488.67,1494.07,2732810000 +2007-04-26,1495.27,1498.02,1491.17,1494.25,3211800000 +2007-04-25,1480.28,1496.59,1480.28,1495.42,3252590000 +2007-04-24,1480.93,1483.82,1473.74,1480.41,3119750000 +2007-04-23,1484.33,1487.32,1480.19,1480.93,2575020000 +2007-04-20,1470.69,1484.74,1470.69,1484.35,3329940000 +2007-04-19,1472.48,1474.23,1464.47,1470.73,2913610000 +2007-04-18,1471.47,1476.57,1466.41,1472.5,2971330000 +2007-04-17,1468.47,1474.35,1467.15,1471.48,2920570000 +2007-04-16,1452.84,1468.62,1452.84,1468.33,2870140000 +2007-04-13,1447.8,1453.11,1444.15,1452.85,2690020000 +2007-04-12,1438.87,1448.02,1433.91,1447.8,2770570000 +2007-04-11,1448.23,1448.39,1436.15,1438.87,2950190000 +2007-04-10,1444.58,1448.73,1443.99,1448.39,2510110000 +2007-04-09,1443.77,1448.1,1443.28,1444.61,2349410000 +2007-04-05,1438.94,1444.88,1436.67,1443.76,2357230000 +2007-04-04,1437.75,1440.16,1435.08,1439.37,2616320000 +2007-04-03,1424.27,1440.57,1424.27,1437.77,2921760000 +2007-04-02,1420.83,1425.49,1416.37,1424.55,2875880000 +2007-03-30,1422.52,1429.22,1408.9,1420.86,2903960000 +2007-03-29,1417.17,1426.24,1413.27,1422.53,2854710000 +2007-03-28,1428.35,1428.35,1414.07,1417.23,3000440000 +2007-03-27,1437.49,1437.49,1425.54,1428.61,2673040000 +2007-03-26,1436.11,1437.65,1423.28,1437.5,2754660000 +2007-03-23,1434.54,1438.89,1433.21,1436.11,2619020000 +2007-03-22,1435.04,1437.66,1429.88,1434.54,3129970000 +2007-03-21,1410.92,1437.77,1409.75,1435.04,3184770000 +2007-03-20,1402.04,1411.53,1400.7,1410.94,2795940000 +2007-03-19,1386.95,1403.2,1386.95,1402.06,2777180000 +2007-03-16,1392.28,1397.51,1383.63,1386.95,3393640000 +2007-03-15,1387.11,1395.73,1385.16,1392.28,2821900000 +2007-03-14,1377.86,1388.09,1363.98,1387.17,3758350000 +2007-03-13,1406.23,1406.23,1377.71,1377.95,3485570000 +2007-03-12,1402.8,1409.34,1398.4,1406.6,2664000000 +2007-03-09,1401.89,1410.15,1397.3,1402.84,2623050000 +2007-03-08,1391.88,1407.93,1391.88,1401.89,3014850000 +2007-03-07,1395.02,1401.16,1390.64,1391.97,3141350000 +2007-03-06,1374.06,1397.9,1374.06,1395.41,3358160000 +2007-03-05,1387.11,1391.86,1373.97,1374.12,3480520000 +2007-03-02,1403.16,1403.4,1386.87,1387.17,3312260000 +2007-03-01,1406.8,1409.46,1380.87,1403.17,3874910000 +2007-02-28,1398.64,1415.89,1396.65,1406.82,3925250000 +2007-02-27,1449.25,1449.25,1389.42,1399.04,4065230000 +2007-02-26,1451.04,1456.95,1445.48,1449.37,2822170000 +2007-02-23,1456.22,1456.22,1448.36,1451.19,2579950000 +2007-02-22,1457.29,1461.57,1450.51,1456.38,1950770000 +2007-02-21,1459.6,1459.6,1452.02,1457.63,2606980000 +2007-02-20,1455.53,1460.53,1449.2,1459.68,2337860000 +2007-02-16,1456.77,1456.77,1451.57,1455.54,2399450000 +2007-02-15,1455.15,1457.97,1453.19,1456.81,2490920000 +2007-02-14,1443.91,1457.65,1443.91,1455.3,2699290000 +2007-02-13,1433.22,1444.41,1433.22,1444.26,2652150000 +2007-02-12,1438,1439.11,1431.44,1433.37,2395680000 +2007-02-09,1448.25,1452.45,1433.44,1438.06,2951810000 +2007-02-08,1449.99,1450.45,1442.81,1448.31,2816180000 +2007-02-07,1447.41,1452.99,1446.44,1450.02,2618820000 +2007-02-06,1446.98,1450.19,1443.4,1448,2608710000 +2007-02-05,1448.33,1449.38,1443.85,1446.99,2439430000 +2007-02-02,1445.94,1449.33,1444.49,1448.39,2569450000 +2007-02-01,1437.9,1446.64,1437.9,1445.94,2914890000 +2007-01-31,1428.65,1441.61,1424.78,1438.24,2976690000 +2007-01-30,1420.61,1428.82,1420.61,1428.82,2706250000 +2007-01-29,1422.03,1426.94,1418.46,1420.62,2730480000 +2007-01-26,1423.9,1427.27,1416.96,1422.18,2626620000 +2007-01-25,1440.12,1440.69,1422.34,1423.9,2994330000 +2007-01-24,1427.96,1440.14,1427.96,1440.13,2783180000 +2007-01-23,1422.95,1431.33,1421.66,1427.99,2975070000 +2007-01-22,1430.47,1431.39,1420.4,1422.95,2540120000 +2007-01-19,1426.35,1431.57,1425.19,1430.5,2777480000 +2007-01-18,1430.59,1432.96,1424.21,1426.37,2822430000 +2007-01-17,1431.77,1435.27,1428.57,1430.62,2690270000 +2007-01-16,1430.73,1433.93,1428.62,1431.9,2599530000 +2007-01-12,1423.82,1431.23,1422.58,1430.73,2686480000 +2007-01-11,1414.84,1427.12,1414.84,1423.82,2857870000 +2007-01-10,1408.7,1415.99,1405.32,1414.85,2764660000 +2007-01-09,1412.84,1415.61,1405.42,1412.11,3038380000 +2007-01-08,1409.26,1414.98,1403.97,1412.84,2763340000 +2007-01-05,1418.34,1418.34,1405.75,1409.71,2919400000 +2007-01-04,1416.6,1421.84,1408.43,1418.34,3004460000 +2007-01-03,1418.03,1429.42,1407.86,1416.6,3429160000 +2006-12-29,1424.71,1427,1416.84,1418.3,1678200000 +2006-12-28,1426.77,1427.26,1422.05,1424.73,1508570000 +2006-12-27,1416.63,1427.72,1416.63,1426.84,1667370000 +2006-12-26,1410.75,1417.91,1410.45,1416.9,1310310000 +2006-12-22,1418.1,1418.82,1410.28,1410.76,1647590000 +2006-12-21,1423.2,1426.4,1415.9,1418.3,2322410000 +2006-12-20,1425.51,1429.05,1423.51,1423.53,2387630000 +2006-12-19,1422.42,1428.3,1414.88,1425.55,2717060000 +2006-12-18,1427.08,1431.81,1420.65,1422.48,2568140000 +2006-12-15,1425.48,1431.63,1425.48,1427.09,3229580000 +2006-12-14,1413.16,1427.23,1413.16,1425.49,2729700000 +2006-12-13,1411.32,1416.64,1411.05,1413.21,2552260000 +2006-12-12,1413,1413.78,1404.75,1411.56,2738170000 +2006-12-11,1409.81,1415.6,1408.56,1413.04,2289900000 +2006-12-08,1407.27,1414.09,1403.67,1409.84,2440460000 +2006-12-07,1412.86,1418.27,1406.8,1407.29,2743150000 +2006-12-06,1414.4,1415.93,1411.05,1412.9,2725280000 +2006-12-05,1409.1,1415.27,1408.78,1414.76,2755700000 +2006-12-04,1396.67,1411.23,1396.67,1409.12,2766320000 +2006-12-01,1400.63,1402.46,1385.93,1396.71,2800980000 +2006-11-30,1399.47,1406.3,1393.83,1400.63,4006230000 +2006-11-29,1386.11,1401.14,1386.11,1399.48,2790970000 +2006-11-28,1381.61,1387.91,1377.83,1386.72,2639750000 +2006-11-27,1400.95,1400.95,1381.44,1381.96,2711210000 +2006-11-24,1405.94,1405.94,1399.25,1400.95,832550000 +2006-11-22,1402.69,1407.89,1402.26,1406.09,2237710000 +2006-11-21,1400.43,1403.49,1399.99,1402.81,2597940000 +2006-11-20,1401.17,1404.37,1397.85,1400.5,2546710000 +2006-11-17,1399.76,1401.21,1394.55,1401.2,2726100000 +2006-11-16,1396.53,1403.76,1396.53,1399.76,2835730000 +2006-11-15,1392.91,1401.35,1392.13,1396.57,2831130000 +2006-11-14,1384.36,1394.49,1379.07,1393.22,3027480000 +2006-11-13,1380.58,1387.61,1378.8,1384.42,2386340000 +2006-11-10,1378.33,1381.04,1375.6,1380.9,2290200000 +2006-11-09,1385.43,1388.92,1377.31,1378.33,3012050000 +2006-11-08,1382.5,1388.61,1379.33,1385.72,2814820000 +2006-11-07,1379.75,1388.19,1379.19,1382.84,2636390000 +2006-11-06,1364.27,1381.4,1364.27,1379.78,2533550000 +2006-11-03,1367.31,1371.68,1360.98,1364.3,2419730000 +2006-11-02,1367.44,1368.39,1362.21,1367.34,2646180000 +2006-11-01,1377.76,1381.95,1366.26,1367.81,2821160000 +2006-10-31,1377.93,1381.21,1372.19,1377.94,2803030000 +2006-10-30,1377.3,1381.22,1373.46,1377.93,2770440000 +2006-10-27,1388.89,1388.89,1375.85,1377.34,2458450000 +2006-10-26,1382.21,1389.45,1379.47,1389.08,2793350000 +2006-10-25,1377.36,1383.61,1376,1382.22,2953540000 +2006-10-24,1377.02,1377.78,1372.42,1377.38,2876890000 +2006-10-23,1368.58,1377.4,1363.94,1377.02,2480430000 +2006-10-20,1366.94,1368.66,1362.1,1368.6,2526410000 +2006-10-19,1365.95,1368.09,1362.06,1366.96,2619830000 +2006-10-18,1363.93,1372.87,1360.95,1365.8,2658840000 +2006-10-17,1369.05,1369.05,1356.87,1364.05,2519620000 +2006-10-16,1365.61,1370.2,1364.48,1369.06,2305920000 +2006-10-13,1362.82,1366.63,1360.5,1365.62,2482920000 +2006-10-12,1349.94,1363.76,1349.94,1362.83,2514350000 +2006-10-11,1353.28,1353.97,1343.57,1349.95,2521000000 +2006-10-10,1350.62,1354.23,1348.6,1353.42,2376140000 +2006-10-09,1349.58,1352.69,1346.55,1350.66,1935170000 +2006-10-06,1353.22,1353.22,1344.21,1349.59,2523000000 +2006-10-05,1349.84,1353.79,1347.75,1353.22,2817240000 +2006-10-04,1333.81,1350.2,1331.48,1350.2,3019880000 +2006-10-03,1331.32,1338.31,1327.1,1334.11,2682690000 +2006-10-02,1335.82,1338.54,1330.28,1331.32,2154480000 +2006-09-29,1339.15,1339.88,1335.64,1335.85,2273430000 +2006-09-28,1336.56,1340.28,1333.75,1338.88,2397820000 +2006-09-27,1336.12,1340.08,1333.54,1336.59,2749190000 +2006-09-26,1326.35,1336.6,1325.3,1336.35,2673350000 +2006-09-25,1314.78,1329.35,1311.58,1326.37,2710240000 +2006-09-22,1318.03,1318.03,1310.94,1314.78,2162880000 +2006-09-21,1324.89,1328.19,1315.45,1318.03,2627440000 +2006-09-20,1318.28,1328.53,1318.28,1325.18,2543070000 +2006-09-19,1321.17,1322.04,1312.17,1317.64,2390850000 +2006-09-18,1319.85,1324.87,1318.16,1321.18,2325080000 +2006-09-15,1316.28,1324.65,1316.28,1319.66,3198030000 +2006-09-14,1318,1318,1313.25,1316.28,2351220000 +2006-09-13,1312.74,1319.92,1311.12,1318.07,2597220000 +2006-09-12,1299.53,1314.28,1299.53,1313,2791580000 +2006-09-11,1298.86,1302.36,1290.93,1299.54,2506430000 +2006-09-08,1294.02,1300.14,1294.02,1298.92,2132890000 +2006-09-07,1300.21,1301.25,1292.13,1294.02,2325850000 +2006-09-06,1313.04,1313.04,1299.28,1300.26,2329870000 +2006-09-05,1310.94,1314.67,1308.82,1313.25,2114480000 +2006-09-01,1303.8,1312.03,1303.8,1311.01,1800520000 +2006-08-31,1304.25,1306.11,1302.45,1303.82,1974540000 +2006-08-30,1303.7,1306.74,1302.15,1305.37,2060690000 +2006-08-29,1301.57,1305.02,1295.29,1304.28,2093720000 +2006-08-28,1295.09,1305.02,1293.97,1301.78,1834920000 +2006-08-25,1295.92,1298.88,1292.39,1295.09,1667580000 +2006-08-24,1292.97,1297.23,1291.4,1296.06,1930320000 +2006-08-23,1298.73,1301.5,1289.82,1292.99,1893670000 +2006-08-22,1297.52,1302.49,1294.44,1298.82,1908740000 +2006-08-21,1302.3,1302.3,1295.51,1297.52,1759240000 +2006-08-18,1297.48,1302.3,1293.57,1302.3,2033910000 +2006-08-17,1295.37,1300.78,1292.71,1297.48,2458340000 +2006-08-16,1285.27,1296.21,1285.27,1295.43,2554570000 +2006-08-15,1268.19,1286.23,1268.19,1285.58,2334100000 +2006-08-14,1266.67,1278.9,1266.67,1268.21,2118020000 +2006-08-11,1271.64,1271.64,1262.08,1266.74,2004540000 +2006-08-10,1265.72,1272.55,1261.3,1271.81,2402190000 +2006-08-09,1271.13,1283.74,1264.73,1265.95,2555180000 +2006-08-08,1275.67,1282.75,1268.37,1271.48,2457840000 +2006-08-07,1279.31,1279.31,1273,1275.77,2045660000 +2006-08-04,1280.26,1292.92,1273.82,1279.36,2530970000 +2006-08-03,1278.22,1283.96,1271.25,1280.27,2728440000 +2006-08-02,1270.73,1283.42,1270.73,1277.41,2610750000 +2006-08-01,1278.53,1278.66,1265.71,1270.92,2527690000 +2006-07-31,1278.53,1278.66,1274.31,1276.66,2461300000 +2006-07-28,1263.15,1280.42,1263.15,1278.55,2480420000 +2006-07-27,1268.2,1275.85,1261.92,1263.2,2776710000 +2006-07-26,1268.87,1273.89,1261.94,1268.4,2667710000 +2006-07-25,1260.91,1272.39,1257.19,1268.88,2563930000 +2006-07-24,1240.25,1262.5,1240.25,1260.91,2312720000 +2006-07-21,1249.12,1250.96,1238.72,1240.29,2704090000 +2006-07-20,1259.81,1262.56,1249.13,1249.13,2345580000 +2006-07-19,1236.74,1261.81,1236.74,1259.81,2701980000 +2006-07-18,1234.48,1239.86,1224.54,1236.86,2481750000 +2006-07-17,1236.2,1240.07,1231.49,1234.49,2146410000 +2006-07-14,1242.29,1242.7,1228.45,1236.2,2467120000 +2006-07-13,1258.58,1258.58,1241.43,1242.28,2545760000 +2006-07-12,1272.39,1273.31,1257.29,1258.6,2250450000 +2006-07-11,1267.26,1273.64,1259.65,1272.43,2310850000 +2006-07-10,1265.46,1274.06,1264.46,1267.34,1854590000 +2006-07-07,1274.08,1275.38,1263.13,1265.48,1988150000 +2006-07-06,1270.58,1278.32,1270.58,1274.08,2009160000 +2006-07-05,1280.05,1280.05,1265.91,1270.91,2165070000 +2006-07-03,1270.06,1280.38,1270.06,1280.19,1114470000 +2006-06-30,1272.86,1276.3,1270.2,1270.2,3049560000 +2006-06-29,1245.94,1272.88,1245.94,1272.87,2621250000 +2006-06-28,1238.99,1247.06,1237.59,1246,2085490000 +2006-06-27,1250.55,1253.37,1238.94,1239.2,2203130000 +2006-06-26,1244.5,1250.92,1243.68,1250.56,1878580000 +2006-06-23,1245.59,1253.13,1241.43,1244.5,2017270000 +2006-06-22,1251.92,1251.92,1241.53,1245.6,2148180000 +2006-06-21,1240.09,1257.96,1240.09,1252.2,2361230000 +2006-06-20,1240.12,1249.01,1238.87,1240.12,2232950000 +2006-06-19,1251.54,1255.93,1237.17,1240.13,2517200000 +2006-06-16,1256.16,1256.27,1246.33,1251.54,2783390000 +2006-06-15,1230.01,1258.64,1230.01,1256.16,2775480000 +2006-06-14,1223.66,1231.46,1219.29,1230.04,2667990000 +2006-06-13,1236.08,1243.37,1222.52,1223.69,3215770000 +2006-06-12,1252.27,1255.22,1236.43,1237.44,2247010000 +2006-06-09,1257.93,1262.58,1250.03,1252.3,2214000000 +2006-06-08,1256.08,1259.85,1235.18,1257.93,3543790000 +2006-06-07,1263.61,1272.47,1255.77,1256.15,2644170000 +2006-06-06,1265.23,1269.88,1254.46,1263.85,2697650000 +2006-06-05,1288.16,1288.16,1264.66,1265.29,2313470000 +2006-06-02,1285.71,1290.68,1280.22,1288.22,2295540000 +2006-06-01,1270.05,1285.71,1269.19,1285.71,2360160000 +2006-05-31,1259.38,1270.09,1259.38,1270.09,2692160000 +2006-05-30,1280.04,1280.04,1259.87,1259.87,2176190000 +2006-05-26,1272.71,1280.54,1272.5,1280.16,1814020000 +2006-05-25,1258.41,1273.26,1258.41,1272.88,2372730000 +2006-05-24,1256.56,1264.53,1245.34,1258.57,2999030000 +2006-05-23,1262.06,1273.67,1256.15,1256.58,2605250000 +2006-05-22,1267.03,1268.77,1252.98,1262.07,2773010000 +2006-05-19,1261.81,1272.15,1256.28,1267.03,2982300000 +2006-05-18,1270.25,1274.89,1261.75,1261.81,2537490000 +2006-05-17,1291.73,1291.73,1267.31,1270.32,2830200000 +2006-05-16,1294.5,1297.88,1288.51,1292.08,2386210000 +2006-05-15,1291.19,1294.81,1284.51,1294.5,2505660000 +2006-05-12,1305.88,1305.88,1290.38,1291.24,2567970000 +2006-05-11,1322.63,1322.63,1303.45,1305.92,2531520000 +2006-05-10,1324.57,1325.51,1317.44,1322.85,2268550000 +2006-05-09,1324.66,1326.6,1322.48,1325.14,2157290000 +2006-05-08,1325.76,1326.7,1322.87,1324.66,2151300000 +2006-05-05,1312.25,1326.53,1312.25,1325.76,2294760000 +2006-05-04,1307.85,1315.14,1307.85,1312.25,2431450000 +2006-05-03,1313.21,1313.47,1303.92,1308.12,2395230000 +2006-05-02,1305.19,1313.66,1305.19,1313.21,2403470000 +2006-05-01,1310.61,1317.21,1303.46,1305.19,2437040000 +2006-04-28,1309.72,1316.04,1306.16,1310.61,2419920000 +2006-04-27,1305.41,1315,1295.57,1309.72,2772010000 +2006-04-26,1301.74,1310.97,1301.74,1305.41,2502690000 +2006-04-25,1308.11,1310.79,1299.17,1301.74,2366380000 +2006-04-24,1311.28,1311.28,1303.79,1308.11,2117330000 +2006-04-21,1311.46,1317.67,1306.59,1311.28,2392630000 +2006-04-20,1309.93,1318.16,1306.38,1311.46,2512920000 +2006-04-19,1307.65,1310.39,1302.79,1309.93,2447310000 +2006-04-18,1285.33,1309.02,1285.33,1307.28,2595440000 +2006-04-17,1289.12,1292.45,1280.74,1285.33,1794650000 +2006-04-13,1288.12,1292.09,1283.37,1289.12,1891940000 +2006-04-12,1286.57,1290.93,1286.45,1288.12,1938100000 +2006-04-11,1296.6,1300.71,1282.96,1286.57,2232880000 +2006-04-10,1295.51,1300.74,1293.17,1296.62,1898320000 +2006-04-07,1309.04,1314.07,1294.18,1295.5,2082470000 +2006-04-06,1311.56,1311.99,1302.44,1309.04,2281680000 +2006-04-05,1305.93,1312.81,1304.82,1311.56,2420020000 +2006-04-04,1297.81,1307.55,1294.71,1305.93,2147660000 +2006-04-03,1302.88,1309.19,1296.65,1297.81,2494080000 +2006-03-31,1300.25,1303,1294.87,1294.87,2236710000 +2006-03-30,1302.89,1310.15,1296.72,1300.25,2294560000 +2006-03-29,1293.23,1305.6,1293.23,1302.89,2143540000 +2006-03-28,1301.61,1306.24,1291.84,1293.23,2148580000 +2006-03-27,1302.95,1303.74,1299.09,1301.61,2029700000 +2006-03-24,1301.67,1306.53,1298.89,1302.95,2326070000 +2006-03-23,1305.04,1305.04,1298.11,1301.67,1980940000 +2006-03-22,1297.23,1305.97,1295.81,1305.04,2039810000 +2006-03-21,1305.08,1310.88,1295.82,1297.23,2147370000 +2006-03-20,1307.25,1310,1303.59,1305.08,1976830000 +2006-03-17,1305.33,1309.79,1305.32,1307.25,2549620000 +2006-03-16,1303.02,1310.45,1303.02,1305.33,2292180000 +2006-03-15,1297.48,1304.4,1294.97,1303.02,2293000000 +2006-03-14,1284.13,1298.14,1282.67,1297.48,2165270000 +2006-03-13,1281.58,1287.37,1281.58,1284.13,2070330000 +2006-03-10,1272.23,1284.37,1271.11,1281.42,2123450000 +2006-03-09,1278.47,1282.74,1272.23,1272.23,2140110000 +2006-03-08,1275.88,1280.33,1268.42,1278.47,2442870000 +2006-03-07,1278.26,1278.26,1271.11,1275.88,2268050000 +2006-03-06,1287.23,1288.23,1275.67,1278.26,2280190000 +2006-03-03,1289.14,1297.33,1284.2,1287.23,2152950000 +2006-03-02,1291.24,1291.24,1283.21,1289.14,2494590000 +2006-03-01,1280.66,1291.8,1280.66,1291.24,2308320000 +2006-02-28,1294.12,1294.12,1278.66,1280.66,2370860000 +2006-02-27,1289.43,1297.57,1289.43,1294.12,1975320000 +2006-02-24,1287.79,1292.11,1285.62,1289.43,1933010000 +2006-02-23,1292.67,1293.84,1285.14,1287.79,2144210000 +2006-02-22,1283.03,1294.17,1283.03,1292.67,2222380000 +2006-02-21,1287.24,1291.92,1281.33,1283.03,2104320000 +2006-02-17,1289.38,1289.47,1284.07,1287.24,2128260000 +2006-02-16,1280,1289.39,1280,1289.38,2251490000 +2006-02-15,1275.53,1281,1271.06,1280,2317590000 +2006-02-14,1262.86,1278.21,1260.8,1275.53,2437940000 +2006-02-13,1266.99,1266.99,1258.34,1262.86,1850080000 +2006-02-10,1263.82,1269.89,1254.98,1266.99,2290050000 +2006-02-09,1265.65,1274.56,1262.8,1263.78,2441920000 +2006-02-08,1254.78,1266.47,1254.78,1265.65,2456860000 +2006-02-07,1265.02,1265.78,1253.61,1254.78,2366370000 +2006-02-06,1264.03,1267.04,1261.62,1265.02,2132360000 +2006-02-03,1270.84,1270.87,1261.02,1264.03,2282210000 +2006-02-02,1282.46,1282.46,1267.72,1270.84,2565300000 +2006-02-01,1280.08,1283.33,1277.57,1282.46,2589410000 +2006-01-31,1285.2,1285.2,1276.85,1280.08,2708310000 +2006-01-30,1283.72,1287.94,1283.51,1285.19,2282730000 +2006-01-27,1273.83,1286.38,1273.83,1283.72,2623620000 +2006-01-26,1264.68,1276.44,1264.68,1273.83,2856780000 +2006-01-25,1266.86,1271.87,1259.42,1264.68,2617060000 +2006-01-24,1263.82,1271.47,1263.82,1266.86,2608720000 +2006-01-23,1261.49,1268.19,1261.49,1263.82,2256070000 +2006-01-20,1285.04,1285.04,1260.92,1261.49,2845810000 +2006-01-19,1277.93,1287.79,1277.93,1285.04,2444020000 +2006-01-18,1282.93,1282.93,1272.08,1277.93,2233200000 +2006-01-17,1287.61,1287.61,1278.61,1283.03,2179970000 +2006-01-13,1286.06,1288.96,1282.78,1287.61,2206510000 +2006-01-12,1294.18,1294.18,1285.04,1286.06,2318350000 +2006-01-11,1289.72,1294.9,1288.12,1294.18,2406130000 +2006-01-10,1290.15,1290.15,1283.76,1289.69,2373080000 +2006-01-09,1285.45,1290.78,1284.82,1290.15,2301490000 +2006-01-06,1273.48,1286.09,1273.48,1285.45,2446560000 +2006-01-05,1273.46,1276.91,1270.3,1273.48,2433340000 +2006-01-04,1268.8,1275.37,1267.74,1273.46,2515330000 +2006-01-03,1248.29,1270.22,1245.74,1268.8,2554570000 +2005-12-30,1254.42,1254.42,1246.59,1248.29,1443500000 +2005-12-29,1258.17,1260.61,1254.18,1254.42,1382540000 +2005-12-28,1256.54,1261.1,1256.54,1258.17,1422360000 +2005-12-27,1268.66,1271.83,1256.54,1256.54,1540470000 +2005-12-23,1268.12,1269.76,1265.92,1268.66,1285810000 +2005-12-22,1262.79,1268.19,1262.5,1268.12,1888500000 +2005-12-21,1259.62,1269.37,1259.62,1262.79,2065170000 +2005-12-20,1259.92,1263.86,1257.21,1259.62,1996690000 +2005-12-19,1267.32,1270.51,1259.28,1259.92,2208810000 +2005-12-16,1270.94,1275.24,1267.32,1267.32,2584190000 +2005-12-15,1272.74,1275.17,1267.74,1270.94,2180590000 +2005-12-14,1267.43,1275.8,1267.07,1272.74,2145520000 +2005-12-13,1260.43,1272.11,1258.56,1267.43,2390020000 +2005-12-12,1259.37,1263.86,1255.52,1260.43,1876550000 +2005-12-09,1255.84,1263.08,1254.24,1259.37,1896290000 +2005-12-08,1257.37,1263.36,1250.91,1255.84,2178300000 +2005-12-07,1263.7,1264.85,1253.02,1257.37,2093830000 +2005-12-06,1262.09,1272.89,1262.09,1263.7,2110740000 +2005-12-05,1265.08,1265.08,1258.12,1262.09,2325840000 +2005-12-02,1264.67,1266.85,1261.42,1265.08,2125580000 +2005-12-01,1249.48,1266.17,1249.48,1264.67,2614830000 +2005-11-30,1257.48,1260.93,1249.39,1249.48,2374690000 +2005-11-29,1257.46,1266.18,1257.46,1257.48,2268340000 +2005-11-28,1268.25,1268.44,1257.17,1257.46,2016900000 +2005-11-25,1265.61,1268.78,1265.54,1268.25,724940000 +2005-11-23,1261.23,1270.64,1259.51,1265.61,1985400000 +2005-11-22,1254.85,1261.9,1251.4,1261.23,2291420000 +2005-11-21,1248.27,1255.89,1246.9,1254.85,2117350000 +2005-11-18,1242.8,1249.58,1240.71,1248.27,2453290000 +2005-11-17,1231.21,1242.96,1231.21,1242.8,2298040000 +2005-11-16,1229.01,1232.24,1227.18,1231.21,2121580000 +2005-11-15,1233.76,1237.94,1226.41,1229.01,2359370000 +2005-11-14,1234.72,1237.2,1231.78,1233.76,1899780000 +2005-11-11,1230.96,1235.7,1230.72,1234.72,1773140000 +2005-11-10,1220.65,1232.41,1215.05,1230.96,2378460000 +2005-11-09,1218.59,1226.59,1216.53,1220.65,2214460000 +2005-11-08,1222.81,1222.81,1216.08,1218.59,1965050000 +2005-11-07,1220.14,1224.18,1217.29,1222.81,1987580000 +2005-11-04,1219.94,1222.52,1214.45,1220.14,2050510000 +2005-11-03,1214.76,1224.7,1214.76,1219.94,2716630000 +2005-11-02,1202.76,1215.17,1201.07,1214.76,2648090000 +2005-11-01,1207.01,1207.34,1201.66,1202.76,2457850000 +2005-10-31,1198.41,1211.43,1198.41,1207.01,2567470000 +2005-10-28,1178.9,1198.41,1178.9,1198.41,2379400000 +2005-10-27,1191.38,1192.65,1178.89,1178.9,2395370000 +2005-10-26,1196.54,1204.01,1191.38,1191.38,2467750000 +2005-10-25,1199.38,1201.3,1189.29,1196.54,2312470000 +2005-10-24,1179.59,1199.39,1179.59,1199.38,2197790000 +2005-10-21,1177.8,1186.46,1174.92,1179.59,2470920000 +2005-10-20,1195.76,1197.3,1173.3,1177.8,2617250000 +2005-10-19,1178.14,1195.76,1170.55,1195.76,2703590000 +2005-10-18,1190.1,1190.1,1178.13,1178.14,2197010000 +2005-10-17,1186.57,1191.21,1184.48,1190.1,2054570000 +2005-10-14,1176.84,1187.13,1175.44,1186.57,2188940000 +2005-10-13,1177.68,1179.56,1168.2,1176.84,2351150000 +2005-10-12,1184.87,1190.02,1173.65,1177.68,2491280000 +2005-10-11,1187.33,1193.1,1183.16,1184.87,2299040000 +2005-10-10,1195.9,1196.52,1186.12,1187.33,2195990000 +2005-10-07,1191.49,1199.71,1191.46,1195.9,2126080000 +2005-10-06,1196.39,1202.14,1181.92,1191.49,2792030000 +2005-10-05,1214.47,1214.47,1196.25,1196.39,2546780000 +2005-10-04,1226.7,1229.88,1214.02,1214.47,2341420000 +2005-10-03,1228.81,1233.34,1225.15,1226.7,2097490000 +2005-09-30,1227.68,1229.57,1225.22,1228.81,2097520000 +2005-09-29,1216.89,1228.7,1211.54,1227.68,2176120000 +2005-09-28,1215.66,1220.98,1212.72,1216.89,2106980000 +2005-09-27,1215.63,1220.17,1211.11,1215.66,1976270000 +2005-09-26,1215.29,1222.56,1211.84,1215.63,2022220000 +2005-09-23,1214.62,1218.83,1209.8,1215.29,1973020000 +2005-09-22,1210.2,1216.64,1205.35,1214.62,2424720000 +2005-09-21,1221.34,1221.52,1209.89,1210.2,2548150000 +2005-09-20,1231.02,1236.49,1220.07,1221.34,2319250000 +2005-09-19,1237.91,1237.91,1227.65,1231.02,2076540000 +2005-09-16,1228.42,1237.95,1228.42,1237.91,3152470000 +2005-09-15,1227.16,1231.88,1224.85,1227.73,2079340000 +2005-09-14,1231.2,1234.74,1226.16,1227.16,1986750000 +2005-09-13,1240.57,1240.57,1231.2,1231.2,2082360000 +2005-09-12,1241.48,1242.6,1239.15,1240.56,1938050000 +2005-09-09,1231.67,1243.13,1231.67,1241.48,1992560000 +2005-09-08,1236.36,1236.36,1229.51,1231.67,1955380000 +2005-09-07,1233.39,1237.06,1230.93,1236.36,2067700000 +2005-09-06,1218.02,1233.61,1218.02,1233.39,1932090000 +2005-09-02,1221.59,1224.45,1217.75,1218.02,1640160000 +2005-09-01,1220.33,1227.29,1216.18,1221.59,2229860000 +2005-08-31,1208.41,1220.36,1204.4,1220.33,2365510000 +2005-08-30,1212.28,1212.28,1201.07,1208.41,1916470000 +2005-08-29,1205.1,1214.28,1201.53,1212.28,1599450000 +2005-08-26,1212.4,1212.4,1204.23,1205.1,1541090000 +2005-08-25,1209.59,1213.73,1209.57,1212.37,1571110000 +2005-08-24,1217.57,1224.15,1209.37,1209.59,1930800000 +2005-08-23,1221.73,1223.04,1214.44,1217.59,1678620000 +2005-08-22,1219.71,1228.96,1216.47,1221.73,1621330000 +2005-08-19,1219.02,1225.08,1219.02,1219.71,1558790000 +2005-08-18,1220.24,1222.64,1215.93,1219.02,1808170000 +2005-08-17,1219.34,1225.63,1218.07,1220.24,1859150000 +2005-08-16,1233.87,1233.87,1219.05,1219.34,1820410000 +2005-08-15,1230.4,1236.24,1226.2,1233.87,1562880000 +2005-08-12,1237.81,1237.81,1225.87,1230.39,1709300000 +2005-08-11,1229.13,1237.81,1228.33,1237.81,1941560000 +2005-08-10,1231.38,1242.69,1226.58,1229.13,2172320000 +2005-08-09,1223.13,1234.11,1223.13,1231.38,1897520000 +2005-08-08,1226.42,1232.28,1222.67,1223.13,1804140000 +2005-08-05,1235.86,1235.86,1225.62,1226.42,1930280000 +2005-08-04,1245.04,1245.04,1235.15,1235.86,1981220000 +2005-08-03,1244.12,1245.86,1240.57,1245.04,1999980000 +2005-08-02,1235.35,1244.69,1235.35,1244.12,2043120000 +2005-08-01,1234.18,1239.1,1233.8,1235.35,1716870000 +2005-07-29,1243.72,1245.04,1234.18,1234.18,1789600000 +2005-07-28,1236.79,1245.15,1235.81,1243.72,2001680000 +2005-07-27,1231.16,1237.64,1230.15,1236.79,1945800000 +2005-07-26,1229.03,1234.42,1229.03,1231.16,1934180000 +2005-07-25,1233.68,1238.36,1228.15,1229.03,1717580000 +2005-07-22,1227.04,1234.19,1226.15,1233.68,1766990000 +2005-07-21,1235.2,1235.83,1224.7,1227.04,2129840000 +2005-07-20,1229.35,1236.56,1222.91,1235.2,2063340000 +2005-07-19,1221.13,1230.34,1221.13,1229.35,2041280000 +2005-07-18,1227.92,1227.92,1221.13,1221.13,1582100000 +2005-07-15,1226.5,1229.53,1223.5,1227.92,1716400000 +2005-07-14,1223.29,1233.16,1223.29,1226.5,2048710000 +2005-07-13,1222.21,1224.46,1219.64,1223.29,1812500000 +2005-07-12,1219.44,1225.54,1216.6,1222.21,1932010000 +2005-07-11,1211.86,1220.03,1211.86,1219.44,1846300000 +2005-07-08,1197.87,1212.73,1197.2,1211.86,1900810000 +2005-07-07,1194.94,1198.46,1183.55,1197.87,1952440000 +2005-07-06,1204.99,1206.11,1194.78,1194.94,1883470000 +2005-07-05,1194.44,1206.34,1192.49,1204.99,1805820000 +2005-07-01,1191.33,1197.89,1191.33,1194.44,1593820000 +2005-06-30,1199.85,1203.27,1190.51,1191.33,2109490000 +2005-06-29,1201.57,1204.07,1198.7,1199.85,1769280000 +2005-06-28,1190.69,1202.54,1190.69,1201.57,1772410000 +2005-06-27,1191.57,1194.33,1188.3,1190.69,1738620000 +2005-06-24,1200.73,1200.9,1191.45,1191.57,2418800000 +2005-06-23,1213.88,1216.45,1200.72,1200.73,2029920000 +2005-06-22,1213.61,1219.59,1211.69,1213.88,1823250000 +2005-06-21,1216.1,1217.13,1211.86,1213.61,1720700000 +2005-06-20,1216.96,1219.1,1210.65,1216.1,1714530000 +2005-06-17,1210.93,1219.55,1210.93,1216.96,2407370000 +2005-06-16,1206.55,1212.1,1205.47,1210.96,1776040000 +2005-06-15,1203.91,1208.08,1198.66,1206.58,1840440000 +2005-06-14,1200.82,1207.53,1200.18,1203.91,1698150000 +2005-06-13,1198.11,1206.03,1194.51,1200.82,1661350000 +2005-06-10,1200.93,1202.79,1192.64,1198.11,1664180000 +2005-06-09,1194.67,1201.86,1191.09,1200.93,1824120000 +2005-06-08,1197.26,1201.97,1193.33,1194.67,1715490000 +2005-06-07,1197.51,1208.85,1197.26,1197.26,1851370000 +2005-06-06,1196.02,1198.78,1192.75,1197.51,1547120000 +2005-06-03,1204.29,1205.09,1194.55,1196.02,1627520000 +2005-06-02,1202.27,1204.67,1198.42,1204.29,1813790000 +2005-06-01,1191.5,1205.64,1191.03,1202.22,1810100000 +2005-05-31,1198.78,1198.78,1191.5,1191.5,1840680000 +2005-05-27,1197.62,1199.56,1195.28,1198.78,1381430000 +2005-05-26,1190.01,1198.95,1190.01,1197.62,1654110000 +2005-05-25,1194.07,1194.07,1185.96,1190.01,1742180000 +2005-05-24,1193.86,1195.29,1189.87,1194.07,1681000000 +2005-05-23,1189.28,1197.44,1188.76,1193.86,1681170000 +2005-05-20,1191.08,1191.22,1185.19,1189.28,1631750000 +2005-05-19,1185.56,1191.09,1184.49,1191.08,1775860000 +2005-05-18,1173.8,1187.9,1173.8,1185.56,2266320000 +2005-05-17,1165.69,1174.35,1159.86,1173.8,1887260000 +2005-05-16,1154.05,1165.75,1153.64,1165.69,1856860000 +2005-05-13,1159.36,1163.75,1146.18,1154.05,2188590000 +2005-05-12,1171.11,1173.37,1157.76,1159.36,1995290000 +2005-05-11,1166.22,1171.77,1157.71,1171.11,1834970000 +2005-05-10,1178.84,1178.84,1162.98,1166.22,1889660000 +2005-05-09,1171.35,1178.87,1169.38,1178.84,1857020000 +2005-05-06,1172.63,1177.75,1170.5,1171.35,1707200000 +2005-05-05,1175.65,1178.62,1166.77,1172.63,1997100000 +2005-05-04,1161.17,1176.01,1161.17,1175.65,2306480000 +2005-05-03,1162.16,1166.89,1156.71,1161.17,2167020000 +2005-05-02,1156.85,1162.87,1154.71,1162.16,1980040000 +2005-04-29,1143.22,1156.97,1139.19,1156.85,2362360000 +2005-04-28,1156.38,1156.38,1143.22,1143.22,2182270000 +2005-04-27,1151.74,1159.87,1144.42,1156.38,2151520000 +2005-04-26,1162.1,1164.8,1151.83,1151.83,1959740000 +2005-04-25,1152.12,1164.05,1152.12,1162.1,1795030000 +2005-04-22,1159.95,1159.95,1142.95,1152.12,2045880000 +2005-04-21,1137.5,1159.95,1137.5,1159.95,2308560000 +2005-04-20,1152.78,1155.5,1136.15,1137.5,2217050000 +2005-04-19,1145.98,1154.67,1145.98,1152.78,2142700000 +2005-04-18,1142.62,1148.92,1139.8,1145.98,2180670000 +2005-04-15,1162.05,1162.05,1141.92,1142.62,2689960000 +2005-04-14,1173.79,1174.67,1161.7,1162.05,2355040000 +2005-04-13,1187.76,1187.76,1171.4,1173.79,2049740000 +2005-04-12,1181.21,1190.17,1170.85,1187.76,1979830000 +2005-04-11,1181.2,1184.07,1178.69,1181.21,1525310000 +2005-04-08,1191.14,1191.75,1181.13,1181.2,1661330000 +2005-04-07,1184.07,1191.88,1183.81,1191.14,1900620000 +2005-04-06,1181.39,1189.34,1181.39,1184.07,1797400000 +2005-04-05,1176.12,1183.56,1176.12,1181.39,1870800000 +2005-04-04,1172.79,1178.61,1167.72,1176.12,2079770000 +2005-04-01,1180.59,1189.8,1169.91,1172.92,2168690000 +2005-03-31,1181.41,1184.53,1179.49,1180.59,2214230000 +2005-03-30,1165.36,1181.54,1165.36,1181.41,2097110000 +2005-03-29,1174.28,1179.39,1163.69,1165.36,2223250000 +2005-03-28,1171.42,1179.91,1171.42,1174.28,1746220000 +2005-03-24,1172.53,1180.11,1171.42,1171.42,1721720000 +2005-03-23,1171.71,1176.26,1168.7,1172.53,2246870000 +2005-03-22,1183.78,1189.59,1171.63,1171.71,2114470000 +2005-03-21,1189.65,1189.65,1178.82,1183.78,1819440000 +2005-03-18,1190.21,1191.98,1182.78,1189.65,2344370000 +2005-03-17,1188.07,1193.28,1186.34,1190.21,1581930000 +2005-03-16,1197.75,1197.75,1185.61,1188.07,1653190000 +2005-03-15,1206.83,1210.54,1197.75,1197.75,1513530000 +2005-03-14,1200.08,1206.83,1199.51,1206.83,1437430000 +2005-03-11,1209.25,1213.04,1198.15,1200.08,1449820000 +2005-03-10,1207.01,1211.23,1201.41,1209.25,1604020000 +2005-03-09,1219.43,1219.43,1206.66,1207.01,1704970000 +2005-03-08,1225.31,1225.69,1218.57,1219.43,1523090000 +2005-03-07,1222.12,1229.11,1222.12,1225.31,1488830000 +2005-03-04,1210.47,1224.76,1210.47,1222.12,1636820000 +2005-03-03,1210.08,1215.72,1204.45,1210.47,1616240000 +2005-03-02,1210.41,1215.79,1204.22,1210.08,1568540000 +2005-03-01,1203.6,1212.25,1203.6,1210.41,1708060000 +2005-02-28,1211.37,1211.37,1198.13,1203.6,1795480000 +2005-02-25,1200.2,1212.15,1199.61,1211.37,1523680000 +2005-02-24,1190.8,1200.42,1187.8,1200.2,1518750000 +2005-02-23,1184.16,1193.52,1184.16,1190.8,1501090000 +2005-02-22,1201.59,1202.48,1184.16,1184.16,1744940000 +2005-02-18,1200.75,1202.92,1197.35,1201.59,1551200000 +2005-02-17,1210.34,1211.33,1200.74,1200.75,1580120000 +2005-02-16,1210.12,1212.44,1205.06,1210.34,1490100000 +2005-02-15,1206.14,1212.44,1205.52,1210.12,1527080000 +2005-02-14,1205.3,1206.93,1203.59,1206.14,1290180000 +2005-02-11,1197.01,1208.38,1193.28,1205.3,1562300000 +2005-02-10,1191.99,1198.75,1191.54,1197.01,1491670000 +2005-02-09,1202.3,1203.83,1191.54,1191.99,1511040000 +2005-02-08,1201.72,1205.11,1200.16,1202.3,1416170000 +2005-02-07,1203.03,1204.15,1199.27,1201.72,1347270000 +2005-02-04,1189.89,1203.47,1189.67,1203.03,1648160000 +2005-02-03,1193.19,1193.19,1185.64,1189.89,1554460000 +2005-02-02,1189.41,1195.25,1188.92,1193.19,1561740000 +2005-02-01,1181.27,1190.39,1180.95,1189.41,1681980000 +2005-01-31,1171.36,1182.07,1171.36,1181.27,1679800000 +2005-01-28,1174.55,1175.61,1166.25,1171.36,1641800000 +2005-01-27,1174.07,1177.5,1170.15,1174.55,1600600000 +2005-01-26,1168.41,1175.96,1168.41,1174.07,1635900000 +2005-01-25,1163.75,1174.3,1163.75,1168.41,1610400000 +2005-01-24,1167.87,1173.03,1163.75,1163.75,1494600000 +2005-01-21,1175.41,1179.45,1167.82,1167.87,1643500000 +2005-01-20,1184.63,1184.63,1173.42,1175.41,1692000000 +2005-01-19,1195.98,1195.98,1184.41,1184.63,1498700000 +2005-01-18,1184.52,1195.98,1180.1,1195.98,1596800000 +2005-01-14,1177.45,1185.21,1177.45,1184.52,1335400000 +2005-01-13,1187.7,1187.7,1175.81,1177.45,1510300000 +2005-01-12,1182.99,1187.92,1175.64,1187.7,1562100000 +2005-01-11,1190.25,1190.25,1180.43,1182.99,1488800000 +2005-01-10,1186.19,1194.78,1184.8,1190.25,1490400000 +2005-01-07,1187.89,1192.2,1182.16,1186.19,1477900000 +2005-01-06,1183.74,1191.63,1183.27,1187.89,1569100000 +2005-01-05,1188.05,1192.73,1183.72,1183.74,1738900000 +2005-01-04,1202.08,1205.84,1185.39,1188.05,1721000000 +2005-01-03,1211.92,1217.8,1200.32,1202.08,1510800000 +2004-12-31,1213.55,1217.33,1211.65,1211.92,786900000 +2004-12-30,1213.45,1216.47,1213.41,1213.55,829800000 +2004-12-29,1213.54,1213.85,1210.95,1213.45,925900000 +2004-12-28,1204.92,1213.54,1204.92,1213.54,983000000 +2004-12-27,1210.13,1214.13,1204.92,1204.92,922000000 +2004-12-23,1209.57,1213.66,1208.71,1210.13,956100000 +2004-12-22,1205.45,1211.42,1203.85,1209.57,1390800000 +2004-12-21,1194.65,1205.93,1194.65,1205.45,1483700000 +2004-12-20,1194.2,1203.43,1193.36,1194.65,1422800000 +2004-12-17,1203.21,1203.21,1193.49,1194.2,2335000000 +2004-12-16,1205.72,1207.97,1198.41,1203.21,1793900000 +2004-12-15,1203.38,1206.61,1199.44,1205.72,1695800000 +2004-12-14,1198.68,1205.29,1197.84,1203.38,1544400000 +2004-12-13,1188,1198.74,1188,1198.68,1436100000 +2004-12-10,1189.24,1191.45,1185.24,1188,1443700000 +2004-12-09,1182.81,1190.51,1173.79,1189.24,1624700000 +2004-12-08,1177.07,1184.05,1177.07,1182.81,1525200000 +2004-12-07,1190.25,1192.17,1177.07,1177.07,1533900000 +2004-12-06,1191.17,1192.41,1185.18,1190.25,1354400000 +2004-12-03,1190.33,1197.46,1187.71,1191.17,1566700000 +2004-12-02,1191.37,1194.8,1186.72,1190.33,1774900000 +2004-12-01,1173.78,1191.37,1173.78,1191.37,1772800000 +2004-11-30,1178.57,1178.66,1173.81,1173.82,1553500000 +2004-11-29,1182.65,1186.94,1172.37,1178.57,1378500000 +2004-11-26,1181.76,1186.62,1181.08,1182.65,504580000 +2004-11-24,1176.94,1182.46,1176.94,1181.76,1149600000 +2004-11-23,1177.24,1179.52,1171.41,1176.94,1428300000 +2004-11-22,1170.34,1178.18,1167.89,1177.24,1392700000 +2004-11-19,1183.55,1184,1169.19,1170.34,1526600000 +2004-11-18,1181.94,1184.9,1180.15,1183.55,1456700000 +2004-11-17,1175.43,1188.46,1175.43,1181.94,1684200000 +2004-11-16,1183.81,1183.81,1175.32,1175.43,1364400000 +2004-11-15,1184.17,1184.48,1179.85,1183.81,1453300000 +2004-11-12,1173.48,1184.17,1171.43,1184.17,1531600000 +2004-11-11,1162.91,1174.8,1162.91,1173.48,1393000000 +2004-11-10,1164.08,1169.25,1162.51,1162.91,1504300000 +2004-11-09,1164.89,1168.96,1162.48,1164.08,1450800000 +2004-11-08,1166.17,1166.77,1162.32,1164.89,1358700000 +2004-11-05,1161.67,1170.87,1160.66,1166.17,1724400000 +2004-11-04,1143.2,1161.67,1142.34,1161.67,1782700000 +2004-11-03,1130.54,1147.57,1130.54,1143.2,1767500000 +2004-11-02,1130.51,1140.48,1128.12,1130.56,1659000000 +2004-11-01,1130.2,1133.41,1127.6,1130.51,1395900000 +2004-10-29,1127.44,1131.4,1124.62,1130.2,1500800000 +2004-10-28,1125.34,1130.67,1120.6,1127.44,1628200000 +2004-10-27,1111.09,1126.29,1107.43,1125.4,1741900000 +2004-10-26,1094.81,1111.1,1094.81,1111.09,1685400000 +2004-10-25,1095.74,1096.81,1090.29,1094.8,1380500000 +2004-10-22,1106.49,1108.14,1095.47,1095.74,1469600000 +2004-10-21,1103.66,1108.87,1098.47,1106.49,1673000000 +2004-10-20,1103.23,1104.09,1094.25,1103.66,1685700000 +2004-10-19,1114.02,1117.96,1103.15,1103.23,1737500000 +2004-10-18,1108.2,1114.46,1103.33,1114.02,1373300000 +2004-10-15,1103.29,1113.17,1102.14,1108.2,1645100000 +2004-10-14,1113.65,1114.96,1102.06,1103.29,1489500000 +2004-10-13,1121.84,1127.01,1109.63,1113.65,1546200000 +2004-10-12,1124.39,1124.39,1115.77,1121.84,1320100000 +2004-10-11,1122.14,1126.2,1122.14,1124.39,943800000 +2004-10-08,1130.65,1132.92,1120.19,1122.14,1291600000 +2004-10-07,1142.05,1142.05,1130.5,1130.65,1447500000 +2004-10-06,1134.48,1142.05,1132.94,1142.05,1416700000 +2004-10-05,1135.17,1137.87,1132.03,1134.48,1418400000 +2004-10-04,1131.5,1140.13,1131.5,1135.17,1534000000 +2004-10-01,1114.58,1131.64,1114.58,1131.5,1582200000 +2004-09-30,1114.8,1116.31,1109.68,1114.58,1748000000 +2004-09-29,1110.06,1114.8,1107.42,1114.8,1402900000 +2004-09-28,1103.52,1111.77,1101.29,1110.06,1396600000 +2004-09-27,1110.11,1110.11,1103.24,1103.52,1263500000 +2004-09-24,1108.36,1113.81,1108.36,1110.11,1255400000 +2004-09-23,1113.56,1113.61,1108.05,1108.36,1286300000 +2004-09-22,1129.3,1129.3,1112.67,1113.56,1379900000 +2004-09-21,1122.2,1131.54,1122.2,1129.3,1325000000 +2004-09-20,1128.55,1128.55,1120.34,1122.2,1197600000 +2004-09-17,1123.5,1130.14,1123.5,1128.55,1422600000 +2004-09-16,1120.37,1126.06,1120.37,1123.5,1113900000 +2004-09-15,1128.33,1128.33,1119.82,1120.37,1256000000 +2004-09-14,1125.82,1129.46,1124.72,1128.33,1204500000 +2004-09-13,1123.92,1129.78,1123.35,1125.82,1299800000 +2004-09-10,1118.38,1125.26,1114.39,1123.92,1261200000 +2004-09-09,1116.27,1121.3,1113.62,1118.38,1371300000 +2004-09-08,1121.3,1123.05,1116.27,1116.27,1246300000 +2004-09-07,1113.63,1124.08,1113.63,1121.3,1214400000 +2004-09-03,1118.31,1120.8,1113.57,1113.63,924170000 +2004-09-02,1105.91,1119.11,1105.6,1118.31,1118400000 +2004-09-01,1104.24,1109.24,1099.18,1105.91,1142100000 +2004-08-31,1099.15,1104.24,1094.72,1104.24,1138200000 +2004-08-30,1107.77,1107.77,1099.15,1099.15,843100000 +2004-08-27,1105.09,1109.68,1104.62,1107.77,845400000 +2004-08-26,1104.96,1106.78,1102.46,1105.09,1023600000 +2004-08-25,1096.19,1106.29,1093.24,1104.96,1192200000 +2004-08-24,1095.68,1100.94,1092.82,1096.19,1092500000 +2004-08-23,1098.35,1101.4,1094.73,1095.68,1021900000 +2004-08-20,1091.23,1100.26,1089.57,1098.35,1199900000 +2004-08-19,1095.17,1095.17,1086.28,1091.23,1249400000 +2004-08-18,1081.71,1095.17,1078.93,1095.17,1282500000 +2004-08-17,1079.34,1086.78,1079.34,1081.71,1267800000 +2004-08-16,1064.8,1080.66,1064.8,1079.34,1206200000 +2004-08-13,1063.23,1067.58,1060.72,1064.8,1175100000 +2004-08-12,1075.79,1075.79,1062.82,1063.23,1405100000 +2004-08-11,1079.04,1079.04,1065.92,1075.79,1410400000 +2004-08-10,1065.22,1079.04,1065.22,1079.04,1245600000 +2004-08-09,1063.97,1069.46,1063.97,1065.22,1086000000 +2004-08-06,1080.7,1080.7,1062.23,1063.97,1521000000 +2004-08-05,1098.63,1098.79,1079.98,1080.7,1397400000 +2004-08-04,1099.69,1102.45,1092.4,1098.63,1369200000 +2004-08-03,1106.62,1106.62,1099.26,1099.69,1338300000 +2004-08-02,1101.72,1108.6,1097.34,1106.62,1276000000 +2004-07-30,1100.43,1103.73,1096.96,1101.72,1298200000 +2004-07-29,1095.42,1103.51,1095.42,1100.43,1530100000 +2004-07-28,1094.83,1098.84,1082.17,1095.42,1554300000 +2004-07-27,1084.07,1096.65,1084.07,1094.83,1610800000 +2004-07-26,1086.2,1089.82,1078.78,1084.07,1413400000 +2004-07-23,1096.84,1096.84,1083.56,1086.2,1337500000 +2004-07-22,1093.88,1099.66,1084.16,1096.84,1680800000 +2004-07-21,1108.67,1116.27,1093.88,1093.88,1679500000 +2004-07-20,1100.9,1108.88,1099.1,1108.67,1445800000 +2004-07-19,1101.39,1105.52,1096.55,1100.9,1319900000 +2004-07-16,1106.69,1112.17,1101.07,1101.39,1450300000 +2004-07-15,1111.47,1114.63,1106.67,1106.69,1408700000 +2004-07-14,1115.14,1119.6,1107.83,1111.47,1462000000 +2004-07-13,1114.35,1116.3,1112.99,1115.14,1199700000 +2004-07-12,1112.81,1116.11,1106.71,1114.35,1114600000 +2004-07-09,1109.11,1115.57,1109.11,1112.81,1186300000 +2004-07-08,1118.33,1119.12,1108.72,1109.11,1401100000 +2004-07-07,1116.21,1122.37,1114.92,1118.33,1328600000 +2004-07-06,1125.38,1125.38,1113.21,1116.21,1283300000 +2004-07-02,1128.94,1129.15,1123.26,1125.38,1085000000 +2004-07-01,1140.84,1140.84,1123.06,1128.94,1495700000 +2004-06-30,1136.2,1144.2,1133.62,1140.84,1473800000 +2004-06-29,1133.35,1138.26,1131.81,1136.2,1375000000 +2004-06-28,1134.43,1142.6,1131.72,1133.35,1354600000 +2004-06-25,1140.65,1145.97,1134.24,1134.43,1812900000 +2004-06-24,1144.06,1146.34,1139.94,1140.65,1394900000 +2004-06-23,1134.41,1145.15,1131.73,1144.06,1444200000 +2004-06-22,1130.3,1135.05,1124.37,1134.41,1382300000 +2004-06-21,1135.02,1138.05,1129.64,1130.3,1123900000 +2004-06-18,1132.05,1138.96,1129.83,1135.02,1500600000 +2004-06-17,1133.56,1133.56,1126.89,1132.05,1296700000 +2004-06-16,1132.01,1135.28,1130.55,1133.56,1168400000 +2004-06-15,1125.29,1137.36,1125.29,1132.01,1345900000 +2004-06-14,1136.47,1136.47,1122.16,1125.29,1179400000 +2004-06-10,1131.33,1136.47,1131.33,1136.47,1160600000 +2004-06-09,1142.18,1142.18,1131.17,1131.33,1276800000 +2004-06-08,1140.42,1142.18,1135.45,1142.18,1190300000 +2004-06-07,1122.5,1140.54,1122.5,1140.42,1211800000 +2004-06-04,1116.64,1129.17,1116.64,1122.5,1115300000 +2004-06-03,1124.99,1125.31,1116.57,1116.64,1232400000 +2004-06-02,1121.2,1128.1,1118.64,1124.99,1251700000 +2004-06-01,1120.68,1122.7,1113.32,1121.2,1238000000 +2004-05-28,1121.28,1122.69,1118.1,1120.68,1172600000 +2004-05-27,1114.94,1123.95,1114.86,1121.28,1447500000 +2004-05-26,1113.05,1116.71,1109.91,1114.94,1369400000 +2004-05-25,1095.41,1113.8,1090.74,1113.05,1545700000 +2004-05-24,1093.56,1101.28,1091.77,1095.41,1227500000 +2004-05-21,1089.19,1099.64,1089.19,1093.56,1258600000 +2004-05-20,1088.68,1092.62,1085.43,1089.19,1211000000 +2004-05-19,1091.49,1105.93,1088.49,1088.68,1548600000 +2004-05-18,1084.1,1094.1,1084.1,1091.49,1353000000 +2004-05-17,1095.7,1095.7,1079.36,1084.1,1430100000 +2004-05-14,1096.44,1102.1,1088.24,1095.7,1335900000 +2004-05-13,1097.28,1102.77,1091.76,1096.44,1411100000 +2004-05-12,1095.45,1097.55,1076.32,1097.28,1697600000 +2004-05-11,1087.12,1095.69,1087.12,1095.45,1533800000 +2004-05-10,1098.7,1098.7,1079.63,1087.12,1918400000 +2004-05-07,1113.99,1117.3,1098.63,1098.7,1653600000 +2004-05-06,1121.53,1121.53,1106.3,1113.99,1509300000 +2004-05-05,1119.55,1125.07,1117.9,1121.53,1469000000 +2004-05-04,1117.49,1127.74,1112.89,1119.55,1662100000 +2004-05-03,1107.3,1118.72,1107.3,1117.49,1571600000 +2004-04-30,1113.89,1119.26,1107.23,1107.3,1634700000 +2004-04-29,1122.41,1128.8,1108.04,1113.89,1859000000 +2004-04-28,1138.11,1138.11,1121.7,1122.41,1855600000 +2004-04-27,1135.53,1146.56,1135.53,1138.11,1518000000 +2004-04-26,1140.6,1145.08,1132.91,1135.53,1290600000 +2004-04-23,1139.93,1141.92,1134.81,1140.6,1396100000 +2004-04-22,1124.09,1142.77,1121.95,1139.93,1826700000 +2004-04-21,1118.15,1125.72,1116.03,1124.09,1738100000 +2004-04-20,1135.82,1139.26,1118.09,1118.15,1508500000 +2004-04-19,1134.56,1136.18,1129.84,1135.82,1194900000 +2004-04-16,1128.84,1136.8,1126.9,1134.61,1487800000 +2004-04-15,1128.17,1134.08,1120.75,1128.84,1568700000 +2004-04-14,1129.44,1132.52,1122.15,1128.17,1547700000 +2004-04-13,1145.2,1147.78,1127.7,1129.44,1423200000 +2004-04-12,1139.32,1147.29,1139.32,1145.2,1102400000 +2004-04-08,1140.53,1148.97,1134.52,1139.32,1199800000 +2004-04-07,1148.16,1148.16,1138.41,1140.53,1458800000 +2004-04-06,1150.57,1150.57,1143.3,1148.16,1397700000 +2004-04-05,1141.81,1150.57,1141.64,1150.57,1413700000 +2004-04-02,1132.17,1144.81,1132.17,1141.81,1629200000 +2004-04-01,1126.21,1135.67,1126.2,1132.17,1560700000 +2004-03-31,1127,1130.83,1121.46,1126.21,1560700000 +2004-03-30,1122.47,1127.6,1119.66,1127,1332400000 +2004-03-29,1108.06,1124.37,1108.06,1122.47,1405500000 +2004-03-26,1109.19,1115.27,1106.13,1108.06,1319100000 +2004-03-25,1091.33,1110.38,1091.33,1109.19,1471700000 +2004-03-24,1093.95,1098.32,1087.16,1091.33,1527800000 +2004-03-23,1095.4,1101.52,1091.57,1093.95,1458200000 +2004-03-22,1109.78,1109.78,1089.54,1095.4,1452300000 +2004-03-19,1122.32,1122.72,1109.69,1109.78,1457400000 +2004-03-18,1123.75,1125.5,1113.25,1122.32,1369200000 +2004-03-17,1110.7,1125.76,1110.7,1123.75,1490100000 +2004-03-16,1104.49,1113.76,1102.61,1110.7,1500700000 +2004-03-15,1120.57,1120.57,1103.36,1104.49,1600600000 +2004-03-12,1106.78,1120.63,1106.78,1120.57,1388500000 +2004-03-11,1123.89,1125.96,1105.87,1106.78,1889900000 +2004-03-10,1140.58,1141.45,1122.53,1123.89,1648400000 +2004-03-09,1147.2,1147.32,1136.84,1140.58,1499400000 +2004-03-08,1156.86,1159.94,1146.97,1147.2,1254400000 +2004-03-05,1154.87,1163.23,1148.77,1156.86,1398200000 +2004-03-04,1151.03,1154.97,1149.81,1154.87,1265800000 +2004-03-03,1149.1,1152.44,1143.78,1151.03,1334500000 +2004-03-02,1155.97,1156.54,1147.31,1149.1,1476000000 +2004-03-01,1144.94,1157.45,1144.94,1155.97,1497100000 +2004-02-27,1145.8,1151.68,1141.8,1144.94,1540400000 +2004-02-26,1143.67,1147.23,1138.62,1144.91,1383900000 +2004-02-25,1139.09,1145.24,1138.96,1143.67,1360700000 +2004-02-24,1140.99,1144.54,1134.43,1139.09,1543600000 +2004-02-23,1144.11,1146.69,1136.98,1140.99,1380400000 +2004-02-20,1147.06,1149.81,1139,1144.11,1479600000 +2004-02-19,1151.82,1158.57,1146.85,1147.06,1562800000 +2004-02-18,1156.99,1157.4,1149.54,1151.82,1382400000 +2004-02-17,1145.81,1158.98,1145.81,1156.99,1396500000 +2004-02-13,1152.11,1156.88,1143.24,1145.81,1329200000 +2004-02-12,1157.76,1157.76,1151.44,1152.11,1464300000 +2004-02-11,1145.54,1158.89,1142.33,1157.76,1699300000 +2004-02-10,1139.81,1147.02,1138.7,1145.54,1403900000 +2004-02-09,1142.76,1144.46,1139.21,1139.81,1303500000 +2004-02-06,1128.59,1142.79,1128.39,1142.76,1477600000 +2004-02-05,1126.52,1131.17,1124.44,1128.59,1566600000 +2004-02-04,1136.03,1136.03,1124.74,1126.52,1634800000 +2004-02-03,1135.26,1137.44,1131.33,1136.03,1476900000 +2004-02-02,1131.13,1142.45,1127.87,1135.26,1599200000 +2004-01-30,1134.11,1134.17,1127.73,1131.13,1635000000 +2004-01-29,1128.48,1134.39,1122.38,1134.11,1921900000 +2004-01-28,1144.05,1149.14,1126.5,1128.48,1842000000 +2004-01-27,1155.37,1155.37,1144.05,1144.05,1673100000 +2004-01-26,1141.55,1155.38,1141,1155.37,1480600000 +2004-01-23,1143.94,1150.31,1136.85,1141.55,1561200000 +2004-01-22,1147.62,1150.51,1143.01,1143.94,1693700000 +2004-01-21,1138.77,1149.21,1134.62,1147.62,1757600000 +2004-01-20,1139.83,1142.93,1135.4,1138.77,1698200000 +2004-01-16,1132.05,1139.83,1132.05,1139.83,1721100000 +2004-01-15,1130.52,1137.11,1124.54,1132.05,1695000000 +2004-01-14,1121.22,1130.75,1121.22,1130.52,1514600000 +2004-01-13,1127.23,1129.07,1115.19,1121.22,1595900000 +2004-01-12,1121.86,1127.85,1120.9,1127.23,1510200000 +2004-01-09,1131.92,1131.92,1120.9,1121.86,1720700000 +2004-01-08,1126.33,1131.92,1124.91,1131.92,1868400000 +2004-01-07,1123.67,1126.33,1116.45,1126.33,1704900000 +2004-01-06,1122.22,1124.46,1118.44,1123.67,1494500000 +2004-01-05,1108.48,1122.22,1108.48,1122.22,1578200000 +2004-01-02,1111.92,1118.85,1105.08,1108.48,1153200000 +2003-12-31,1109.64,1112.56,1106.21,1111.92,1027500000 +2003-12-30,1109.48,1109.75,1106.41,1109.64,1012600000 +2003-12-29,1095.89,1109.48,1095.89,1109.48,1058800000 +2003-12-26,1094.04,1098.47,1094.04,1095.89,356070000 +2003-12-24,1096.02,1096.4,1092.73,1094.04,518060000 +2003-12-23,1092.94,1096.95,1091.73,1096.02,1145300000 +2003-12-22,1088.66,1092.94,1086.14,1092.94,1251700000 +2003-12-19,1089.18,1091.06,1084.19,1088.66,1657300000 +2003-12-18,1076.48,1089.5,1076.48,1089.18,1579900000 +2003-12-17,1075.13,1076.54,1071.14,1076.48,1441700000 +2003-12-16,1068.04,1075.94,1068.04,1075.13,1547900000 +2003-12-15,1074.14,1082.79,1068,1068.04,1520800000 +2003-12-12,1071.21,1074.76,1067.64,1074.14,1223100000 +2003-12-11,1059.05,1073.63,1059.05,1071.21,1441100000 +2003-12-10,1060.18,1063.02,1053.41,1059.05,1444000000 +2003-12-09,1069.3,1071.94,1059.16,1060.18,1465500000 +2003-12-08,1061.5,1069.59,1060.93,1069.3,1218900000 +2003-12-05,1069.72,1069.72,1060.09,1061.5,1265900000 +2003-12-04,1064.73,1070.37,1063.15,1069.72,1463100000 +2003-12-03,1066.62,1074.3,1064.63,1064.73,1441700000 +2003-12-02,1070.12,1071.22,1065.22,1066.62,1383200000 +2003-12-01,1058.2,1070.47,1058.2,1070.12,1375000000 +2003-11-28,1058.45,1060.63,1056.77,1058.2,487220000 +2003-11-26,1053.89,1058.45,1048.28,1058.45,1097700000 +2003-11-25,1052.08,1058.05,1049.31,1053.89,1333700000 +2003-11-24,1035.28,1052.08,1035.28,1052.08,1302800000 +2003-11-21,1033.65,1037.57,1031.2,1035.28,1273800000 +2003-11-20,1042.44,1046.48,1033.42,1033.65,1326700000 +2003-11-19,1034.15,1043.95,1034.15,1042.44,1326200000 +2003-11-18,1043.63,1048.77,1034,1034.15,1354300000 +2003-11-17,1050.35,1050.35,1035.28,1043.63,1374300000 +2003-11-14,1058.41,1063.65,1048.11,1050.35,1356100000 +2003-11-13,1058.56,1059.62,1052.96,1058.41,1383000000 +2003-11-12,1046.57,1059.1,1046.57,1058.53,1349300000 +2003-11-11,1047.11,1048.23,1043.46,1046.57,1162500000 +2003-11-10,1053.21,1053.65,1045.58,1047.11,1243600000 +2003-11-07,1058.05,1062.39,1052.17,1053.21,1440500000 +2003-11-06,1051.81,1058.94,1046.93,1058.05,1453900000 +2003-11-05,1053.25,1054.54,1044.88,1051.81,1401800000 +2003-11-04,1059.02,1059.02,1051.7,1053.25,1417600000 +2003-11-03,1050.71,1061.44,1050.71,1059.02,1378200000 +2003-10-31,1046.94,1053.09,1046.94,1050.71,1498900000 +2003-10-30,1048.11,1052.81,1043.82,1046.94,1629700000 +2003-10-29,1046.79,1049.83,1043.35,1048.11,1562600000 +2003-10-28,1031.13,1046.79,1031.13,1046.79,1629200000 +2003-10-27,1028.91,1037.75,1028.91,1031.13,1371800000 +2003-10-24,1033.77,1033.77,1018.32,1028.91,1420300000 +2003-10-23,1030.36,1035.44,1025.89,1033.77,1604300000 +2003-10-22,1046.03,1046.03,1028.39,1030.36,1647200000 +2003-10-21,1044.68,1048.57,1042.59,1046.03,1498000000 +2003-10-20,1039.32,1044.69,1036.13,1044.68,1172600000 +2003-10-17,1050.07,1051.89,1036.57,1039.32,1352000000 +2003-10-16,1046.76,1052.94,1044.04,1050.07,1417700000 +2003-10-15,1049.48,1053.79,1043.15,1046.76,1521100000 +2003-10-14,1045.35,1049.49,1040.84,1049.48,1271900000 +2003-10-13,1038.06,1048.9,1038.06,1045.35,1040500000 +2003-10-10,1038.73,1040.84,1035.74,1038.06,1108100000 +2003-10-09,1033.78,1048.28,1033.78,1038.73,1578700000 +2003-10-08,1039.25,1040.06,1030.96,1033.78,1262500000 +2003-10-07,1034.35,1039.25,1026.27,1039.25,1279500000 +2003-10-06,1029.85,1036.48,1029.15,1034.35,1025800000 +2003-10-03,1020.24,1039.31,1020.24,1029.85,1570500000 +2003-10-02,1018.22,1021.87,1013.38,1020.24,1269300000 +2003-10-01,995.97,1018.22,995.97,1018.22,1566300000 +2003-09-30,1006.58,1006.58,990.36,995.97,1590500000 +2003-09-29,996.85,1006.89,995.31,1006.58,1366500000 +2003-09-26,1003.27,1003.45,996.08,996.85,1472500000 +2003-09-25,1009.38,1015.97,1003.26,1003.27,1530000000 +2003-09-24,1029.03,1029.83,1008.93,1009.38,1556000000 +2003-09-23,1022.82,1030.12,1021.54,1029.03,1301700000 +2003-09-22,1036.3,1036.3,1018.3,1022.82,1278800000 +2003-09-19,1039.58,1040.29,1031.89,1036.3,1518600000 +2003-09-18,1025.97,1040.16,1025.75,1039.58,1498800000 +2003-09-17,1029.32,1031.34,1024.53,1025.97,1338210000 +2003-09-16,1014.81,1029.66,1014.81,1029.32,1403200000 +2003-09-15,1018.63,1019.79,1013.59,1014.81,1151300000 +2003-09-12,1016.42,1019.65,1007.71,1018.63,1236700000 +2003-09-11,1010.92,1020.88,1010.92,1016.42,1335900000 +2003-09-10,1023.17,1023.17,1009.74,1010.92,1582100000 +2003-09-09,1031.64,1031.64,1021.14,1023.17,1414800000 +2003-09-08,1021.39,1032.41,1021.39,1031.64,1299300000 +2003-09-05,1027.97,1029.21,1018.19,1021.39,1465200000 +2003-09-04,1026.27,1029.17,1022.19,1027.97,1453900000 +2003-09-03,1021.99,1029.34,1021.99,1026.27,1675600000 +2003-09-02,1008.01,1022.59,1005.73,1021.99,1470500000 +2003-08-29,1002.84,1008.85,999.52,1008.01,945100000 +2003-08-28,996.79,1004.12,991.42,1002.84,1165200000 +2003-08-27,996.73,998.05,993.33,996.79,1051400000 +2003-08-26,993.71,997.93,983.57,996.73,1178700000 +2003-08-25,993.06,993.71,987.91,993.71,971700000 +2003-08-22,1003.27,1011.01,992.62,993.06,1308900000 +2003-08-21,1000.3,1009.53,999.33,1003.27,1407100000 +2003-08-20,1002.35,1003.54,996.62,1000.3,1210800000 +2003-08-19,999.74,1003.3,995.3,1002.35,1300600000 +2003-08-18,990.67,1000.35,990.67,999.74,1127600000 +2003-08-15,990.51,992.39,987.1,990.67,636370000 +2003-08-14,984.03,991.91,980.36,990.51,1186800000 +2003-08-13,990.35,992.5,980.85,984.03,1208800000 +2003-08-12,980.59,990.41,979.9,990.35,1132300000 +2003-08-11,977.59,985.46,974.21,980.59,1022200000 +2003-08-08,974.12,980.57,973.83,977.59,1086600000 +2003-08-07,967.08,974.89,963.82,974.12,1389300000 +2003-08-06,965.46,975.74,960.84,967.08,1491000000 +2003-08-05,982.82,982.82,964.97,965.46,1351700000 +2003-08-04,980.15,985.75,966.79,982.82,1318700000 +2003-08-01,990.31,990.31,978.86,980.15,1390600000 +2003-07-31,987.49,1004.59,987.49,990.31,1608000000 +2003-07-30,989.28,992.62,985.96,987.49,1391900000 +2003-07-29,996.52,998.64,984.15,989.28,1508900000 +2003-07-28,998.68,1000.68,993.59,996.52,1328600000 +2003-07-25,981.6,998.71,977.49,998.68,1397500000 +2003-07-24,988.61,998.89,981.07,981.6,1559000000 +2003-07-23,988.11,989.86,979.79,988.61,1362700000 +2003-07-22,978.8,990.29,976.08,988.11,1439700000 +2003-07-21,993.32,993.32,975.63,978.8,1254200000 +2003-07-18,981.73,994.25,981.71,993.32,1365200000 +2003-07-17,994,994,978.6,981.73,1661400000 +2003-07-16,1000.42,1003.47,989.3,994.09,1662000000 +2003-07-15,1003.86,1009.61,996.67,1000.42,1518600000 +2003-07-14,998.14,1015.41,998.14,1003.86,1448900000 +2003-07-11,988.7,1000.86,988.7,998.14,1212700000 +2003-07-10,1002.21,1002.21,983.63,988.7,1465700000 +2003-07-09,1007.84,1010.43,998.17,1002.21,1618000000 +2003-07-08,1004.42,1008.92,998.73,1007.84,1565700000 +2003-07-07,985.7,1005.56,985.7,1004.42,1429100000 +2003-07-03,993.75,995,983.34,985.7,775900000 +2003-07-02,982.32,993.78,982.32,993.75,1519300000 +2003-07-01,974.5,983.26,962.1,982.32,1460200000 +2003-06-30,976.22,983.61,973.6,974.5,1587200000 +2003-06-27,985.82,988.88,974.29,976.22,1267800000 +2003-06-26,975.32,986.53,973.8,985.82,1387400000 +2003-06-25,983.45,991.64,974.86,975.32,1459200000 +2003-06-24,981.64,987.84,979.08,983.45,1388300000 +2003-06-23,995.69,995.69,977.4,981.64,1398100000 +2003-06-20,994.7,1002.09,993.36,995.69,1698000000 +2003-06-19,1010.09,1011.22,993.08,994.7,1530100000 +2003-06-18,1011.66,1015.12,1004.61,1010.09,1488900000 +2003-06-17,1010.74,1015.33,1007.04,1011.66,1479700000 +2003-06-16,988.61,1010.86,988.61,1010.74,1345900000 +2003-06-13,998.51,1000.92,984.27,988.61,1271600000 +2003-06-12,997.48,1002.74,991.27,998.51,1553100000 +2003-06-11,984.84,997.48,981.61,997.48,1520000000 +2003-06-10,975.93,984.84,975.93,984.84,1275400000 +2003-06-09,987.76,987.76,972.59,975.93,1307000000 +2003-06-06,990.14,1007.69,986.01,987.76,1837200000 +2003-06-05,986.24,990.14,978.13,990.14,1693100000 +2003-06-04,971.56,987.85,970.72,986.24,1618700000 +2003-06-03,967,973.02,964.47,971.56,1450200000 +2003-06-02,963.59,979.11,963.59,967,1662500000 +2003-05-30,949.64,965.38,949.64,963.59,1688800000 +2003-05-29,953.22,962.08,946.23,949.64,1685800000 +2003-05-28,951.48,959.39,950.12,953.22,1559000000 +2003-05-27,933.22,952.76,927.33,951.48,1532000000 +2003-05-23,931.87,935.2,927.42,933.22,1201000000 +2003-05-22,923.42,935.3,922.54,931.87,1448500000 +2003-05-21,919.73,923.85,914.91,923.42,1457800000 +2003-05-20,920.77,925.34,912.05,919.73,1505300000 +2003-05-19,944.3,944.3,920.23,920.77,1375700000 +2003-05-16,946.67,948.65,938.6,944.3,1505500000 +2003-05-15,939.28,948.23,938.79,946.67,1508700000 +2003-05-14,942.3,947.29,935.24,939.28,1401800000 +2003-05-13,945.11,947.51,938.91,942.3,1418100000 +2003-05-12,933.41,946.84,929.3,945.11,1378800000 +2003-05-09,920.27,933.77,920.27,933.41,1326100000 +2003-05-08,929.62,929.62,919.72,920.27,1379600000 +2003-05-07,934.39,937.22,926.41,929.62,1531900000 +2003-05-06,926.55,939.61,926.38,934.39,1649600000 +2003-05-05,930.08,933.88,924.55,926.55,1446300000 +2003-05-02,916.3,930.56,912.35,930.08,1554300000 +2003-05-01,916.92,919.68,902.83,916.3,1397500000 +2003-04-30,917.84,922.01,911.7,916.92,1788510000 +2003-04-29,914.84,924.24,911.1,917.84,1525600000 +2003-04-28,898.81,918.15,898.81,914.84,1273000000 +2003-04-25,911.43,911.43,897.52,898.81,1335800000 +2003-04-24,919.02,919.02,906.69,911.43,1648100000 +2003-04-23,911.37,919.74,909.89,919.02,1667200000 +2003-04-22,892.01,911.74,886.7,911.37,1631200000 +2003-04-21,893.58,898.01,888.17,892.01,1118700000 +2003-04-17,879.91,893.83,879.2,893.58,1430600000 +2003-04-16,890.81,896.77,877.93,879.91,1587600000 +2003-04-15,885.23,891.27,881.85,890.81,1460200000 +2003-04-14,868.3,885.26,868.3,885.23,1131000000 +2003-04-11,871.58,883.34,865.92,868.3,1141600000 +2003-04-10,865.99,871.78,862.76,871.58,1275300000 +2003-04-09,878.29,887.35,865.72,865.99,1293700000 +2003-04-08,879.93,883.11,874.68,878.29,1235400000 +2003-04-07,878.85,904.89,878.85,879.93,1494000000 +2003-04-04,876.45,882.73,874.23,878.85,1241200000 +2003-04-03,880.9,885.89,876.12,876.45,1339500000 +2003-04-02,858.48,884.57,858.48,880.9,1589800000 +2003-04-01,848.18,861.28,847.85,858.48,1461600000 +2003-03-31,863.5,863.5,843.68,848.18,1495500000 +2003-03-28,868.52,869.88,860.83,863.5,1227000000 +2003-03-27,869.95,874.15,858.09,868.52,1232900000 +2003-03-26,874.74,875.8,866.47,869.95,1319700000 +2003-03-25,864.23,879.87,862.59,874.74,1333400000 +2003-03-24,895.79,895.79,862.02,864.23,1293000000 +2003-03-21,875.84,895.9,875.84,895.79,1883710000 +2003-03-20,874.02,879.6,859.01,875.67,1439100000 +2003-03-19,866.45,874.99,861.21,874.02,1473400000 +2003-03-18,862.79,866.94,857.36,866.45,1555100000 +2003-03-17,833.27,862.79,827.17,862.79,1700420000 +2003-03-14,831.89,841.39,828.26,833.27,1541900000 +2003-03-13,804.19,832.02,804.19,831.9,1816300000 +2003-03-12,800.73,804.19,788.9,804.19,1620000000 +2003-03-11,807.48,814.25,800.3,800.73,1427700000 +2003-03-10,828.89,828.89,806.57,807.48,1255000000 +2003-03-07,822.1,829.55,811.23,828.89,1368500000 +2003-03-06,829.85,829.85,819.85,822.1,1299200000 +2003-03-05,821.99,829.87,819,829.85,1332700000 +2003-03-04,834.81,835.43,821.96,821.99,1256600000 +2003-03-03,841.15,852.34,832.74,834.81,1208900000 +2003-02-28,837.28,847,837.28,841.15,1373300000 +2003-02-27,827.55,842.19,827.55,837.28,1287800000 +2003-02-26,838.57,840.1,826.68,827.55,1374400000 +2003-02-25,832.58,839.55,818.54,838.57,1483700000 +2003-02-24,848.17,848.17,832.16,832.58,1229200000 +2003-02-21,837.1,852.28,831.48,848.17,1398200000 +2003-02-20,845.13,849.37,836.56,837.1,1194100000 +2003-02-19,851.17,851.17,838.79,845.13,1075600000 +2003-02-18,834.89,852.87,834.89,851.17,1250800000 +2003-02-14,817.37,834.89,815.03,834.89,1404600000 +2003-02-13,818.68,821.25,806.29,817.37,1489300000 +2003-02-12,829.2,832.12,818.49,818.68,1260500000 +2003-02-11,835.97,843.02,825.09,829.2,1307000000 +2003-02-10,829.69,837.16,823.53,835.97,1238200000 +2003-02-07,838.15,845.73,826.7,829.69,1276800000 +2003-02-06,843.59,844.23,833.25,838.15,1430900000 +2003-02-05,848.2,861.63,842.11,843.59,1450800000 +2003-02-04,860.32,860.32,840.19,848.2,1451600000 +2003-02-03,855.7,864.64,855.7,860.32,1258500000 +2003-01-31,844.61,858.33,840.34,855.7,1578530000 +2003-01-30,864.36,865.48,843.74,844.61,1510300000 +2003-01-29,858.54,868.72,845.86,864.36,1595400000 +2003-01-28,847.48,860.76,847.48,858.54,1459100000 +2003-01-27,861.4,863.95,844.25,847.48,1435900000 +2003-01-24,887.34,887.34,859.71,861.4,1574800000 +2003-01-23,878.36,890.25,876.89,887.34,1744550000 +2003-01-22,887.62,889.74,877.64,878.36,1560800000 +2003-01-21,901.78,906,887.62,887.62,1335200000 +2003-01-17,914.6,914.6,899.02,901.78,1358200000 +2003-01-16,918.22,926.03,911.98,914.6,1534600000 +2003-01-15,931.66,932.59,916.7,918.22,1432100000 +2003-01-14,926.26,931.66,921.72,931.66,1379400000 +2003-01-13,927.57,935.05,922.05,926.26,1396300000 +2003-01-10,927.58,932.89,917.66,927.57,1485400000 +2003-01-09,909.93,928.31,909.93,927.57,1560300000 +2003-01-08,922.93,922.93,908.32,909.93,1467600000 +2003-01-07,929.01,930.81,919.93,922.93,1545200000 +2003-01-06,908.59,931.77,908.59,929.01,1435900000 +2003-01-03,909.03,911.25,903.07,908.59,1130800000 +2003-01-02,879.82,909.03,879.82,909.03,1229200000 +2002-12-31,879.39,881.93,869.45,879.82,1088500000 +2002-12-30,875.4,882.1,870.23,879.39,1057800000 +2002-12-27,889.66,890.46,873.62,875.4,758400000 +2002-12-26,892.47,903.89,887.48,889.66,721100000 +2002-12-24,897.38,897.38,892.29,892.47,458310000 +2002-12-23,895.74,902.43,892.26,897.38,1112100000 +2002-12-20,884.25,897.79,884.25,895.76,1782730000 +2002-12-19,890.02,899.19,880.32,884.25,1385900000 +2002-12-18,902.99,902.99,887.82,891.12,1446200000 +2002-12-17,910.4,911.22,901.74,902.99,1251800000 +2002-12-16,889.48,910.42,889.48,910.4,1271600000 +2002-12-13,901.58,901.58,888.48,889.48,1330800000 +2002-12-12,904.96,908.37,897,901.58,1255300000 +2002-12-11,904.45,909.94,896.48,904.96,1285100000 +2002-12-10,892,904.95,892,904.45,1286600000 +2002-12-09,912.23,912.23,891.97,892,1320800000 +2002-12-06,906.55,915.48,895.96,912.23,1241100000 +2002-12-05,917.58,921.49,905.9,906.55,1250200000 +2002-12-04,920.75,925.25,909.51,917.58,1588900000 +2002-12-03,934.53,934.53,918.73,920.75,1488400000 +2002-12-02,936.31,954.28,927.72,934.53,1612000000 +2002-11-29,938.87,941.82,935.58,936.31,643460000 +2002-11-27,913.31,940.41,913.31,938.87,1350300000 +2002-11-26,932.87,932.87,912.1,913.31,1543600000 +2002-11-25,930.55,937.15,923.31,932.87,1574000000 +2002-11-22,933.76,937.28,928.41,930.55,1626800000 +2002-11-21,914.15,935.13,914.15,933.76,2415100000 +2002-11-20,896.74,915.01,894.93,914.15,1517300000 +2002-11-19,900.36,905.45,893.09,896.74,1337400000 +2002-11-18,909.83,915.91,899.48,900.36,1282600000 +2002-11-15,904.27,910.21,895.35,909.83,1400100000 +2002-11-14,882.53,904.27,882.53,904.27,1519000000 +2002-11-13,882.95,892.51,872.05,882.53,1463400000 +2002-11-12,876.19,894.3,876.19,882.95,1377100000 +2002-11-11,894.74,894.74,874.63,876.19,1113000000 +2002-11-08,902.65,910.11,891.62,894.74,1446500000 +2002-11-07,923.76,923.76,898.68,902.65,1466900000 +2002-11-06,915.39,925.66,905,923.76,1674000000 +2002-11-05,908.35,915.83,904.91,915.39,1354100000 +2002-11-04,900.96,924.58,900.96,908.35,1645900000 +2002-11-01,885.76,903.42,877.71,900.96,1450400000 +2002-10-31,890.71,898.83,879.75,885.76,1641300000 +2002-10-30,882.15,895.28,879.19,890.71,1422300000 +2002-10-29,890.23,890.64,867.91,882.15,1529700000 +2002-10-28,897.65,907.44,886.15,890.23,1382600000 +2002-10-25,882.5,897.71,877.03,897.65,1340400000 +2002-10-24,896.14,902.94,879,882.5,1700570000 +2002-10-23,890.16,896.14,873.82,896.14,1593900000 +2002-10-22,899.72,899.72,882.4,890.16,1549200000 +2002-10-21,884.39,900.69,873.06,899.72,1447000000 +2002-10-18,879.2,886.68,866.58,884.39,1423100000 +2002-10-17,860.02,885.35,860.02,879.2,1780390000 +2002-10-16,881.27,881.27,856.28,860.02,1585000000 +2002-10-15,841.44,881.27,841.44,881.27,1956000000 +2002-10-14,835.32,844.39,828.37,841.44,1200300000 +2002-10-11,803.92,843.27,803.92,835.32,1854130000 +2002-10-10,776.76,806.51,768.63,803.92,2090230000 +2002-10-09,798.55,798.55,775.8,776.76,1885030000 +2002-10-08,785.28,808.86,779.5,798.55,1938430000 +2002-10-07,800.58,808.21,782.96,785.28,1576500000 +2002-10-04,818.95,825.9,794.1,800.58,1835930000 +2002-10-03,827.91,840.02,817.25,818.95,1674500000 +2002-10-02,843.77,851.93,826.5,827.91,1668900000 +2002-10-01,815.28,847.93,812.82,847.91,1780900000 +2002-09-30,827.37,827.37,800.2,815.28,1721870000 +2002-09-27,854.95,854.95,826.84,827.37,1507300000 +2002-09-26,839.66,856.6,839.66,854.95,1650000000 +2002-09-25,819.27,844.22,818.46,839.66,1651500000 +2002-09-24,833.7,833.7,817.38,819.29,1670240000 +2002-09-23,845.39,845.39,825.76,833.7,1381100000 +2002-09-20,843.32,849.32,839.09,845.39,1792800000 +2002-09-19,869.46,869.46,843.09,843.32,1524000000 +2002-09-18,873.52,878.45,857.39,869.46,1501000000 +2002-09-17,891.1,902.68,872.38,873.52,1448600000 +2002-09-16,889.81,891.84,878.91,891.1,1001400000 +2002-09-13,886.91,892.75,877.05,889.81,1271000000 +2002-09-12,909.45,909.45,884.84,886.91,1191600000 +2002-09-11,910.63,924.02,908.47,909.45,846600000 +2002-09-10,902.96,909.89,900.5,909.58,1186400000 +2002-09-09,893.92,907.34,882.92,902.96,1130600000 +2002-09-06,879.15,899.07,879.15,893.92,1184500000 +2002-09-05,893.4,893.4,870.5,879.15,1401300000 +2002-09-04,878.02,896.1,875.73,893.4,1372100000 +2002-09-03,916.07,916.07,877.51,878.02,1289800000 +2002-08-30,917.8,928.15,910.17,916.07,929900000 +2002-08-29,917.87,924.59,903.33,917.8,1271100000 +2002-08-28,934.82,934.82,913.21,917.87,1146600000 +2002-08-27,947.95,955.82,930.36,934.82,1307700000 +2002-08-26,940.86,950.8,930.42,947.95,1016900000 +2002-08-23,962.7,962.7,937.17,940.86,1071500000 +2002-08-22,949.36,965,946.43,962.7,1373000000 +2002-08-21,937.43,951.59,931.32,949.36,1353100000 +2002-08-20,950.7,950.7,931.86,937.43,1308500000 +2002-08-19,928.77,951.17,927.21,950.7,1299800000 +2002-08-16,930.25,935.38,916.21,928.77,1265300000 +2002-08-15,919.62,933.29,918.17,930.25,1505100000 +2002-08-14,884.21,920.21,876.2,919.62,1533800000 +2002-08-13,903.8,911.71,883.62,884.21,1297700000 +2002-08-12,908.64,908.64,892.38,903.8,1036500000 +2002-08-09,898.73,913.95,890.77,908.64,1294900000 +2002-08-08,876.77,905.84,875.17,905.46,1646700000 +2002-08-07,859.57,878.74,854.15,876.77,1490400000 +2002-08-06,834.6,874.44,834.6,859.57,1514100000 +2002-08-05,864.24,864.24,833.44,834.6,1425500000 +2002-08-02,884.4,884.72,853.95,864.24,1538100000 +2002-08-01,911.62,911.62,882.48,884.66,1672200000 +2002-07-31,902.78,911.64,889.88,911.62,2049360000 +2002-07-30,898.96,909.81,884.7,902.78,1826090000 +2002-07-29,852.84,898.96,852.84,898.96,1778650000 +2002-07-26,838.68,852.85,835.92,852.84,1796100000 +2002-07-25,843.42,853.83,816.11,838.68,2424700000 +2002-07-24,797.71,844.32,775.68,843.43,2775560000 +2002-07-23,819.85,827.69,796.13,797.7,2441020000 +2002-07-22,847.76,854.13,813.26,819.85,2248060000 +2002-07-19,881.56,881.56,842.07,847.75,2654100000 +2002-07-18,905.45,907.8,880.6,881.56,1736300000 +2002-07-17,901.05,926.52,895.03,906.04,2566500000 +2002-07-16,917.93,918.65,897.13,900.94,1843700000 +2002-07-15,921.39,921.39,876.46,917.93,2574800000 +2002-07-12,927.37,934.31,913.71,921.39,1607400000 +2002-07-11,920.47,929.16,900.94,927.37,2080480000 +2002-07-10,952.83,956.34,920.29,920.47,1816900000 +2002-07-09,976.98,979.63,951.71,952.83,1348900000 +2002-07-08,989.03,993.56,972.91,976.98,1184400000 +2002-07-05,953.99,989.07,953.99,989.03,699400000 +2002-07-03,948.09,954.3,934.87,953.99,1527800000 +2002-07-02,968.65,968.65,945.54,948.09,1823000000 +2002-07-01,989.82,994.46,967.43,968.65,1425500000 +2002-06-28,990.64,1001.79,988.31,989.82,2117000000 +2002-06-27,973.53,990.67,963.74,990.64,1908600000 +2002-06-26,976.14,977.43,952.92,973.53,2014290000 +2002-06-25,992.72,1005.88,974.21,976.14,1513700000 +2002-06-24,989.14,1002.11,970.85,992.72,1552600000 +2002-06-21,1006.29,1006.29,985.65,989.14,1497200000 +2002-06-20,1019.99,1023.33,1004.59,1006.29,1389700000 +2002-06-19,1037.14,1037.61,1017.88,1019.99,1336100000 +2002-06-18,1036.17,1040.83,1030.92,1037.14,1193100000 +2002-06-17,1007.27,1036.17,1007.27,1036.17,1236600000 +2002-06-14,1009.56,1009.56,981.63,1007.27,1549000000 +2002-06-13,1020.26,1023.47,1008.12,1009.56,1405500000 +2002-06-12,1013.26,1021.85,1002.58,1020.26,1795720000 +2002-06-11,1030.74,1039.04,1012.94,1013.6,1212400000 +2002-06-10,1027.53,1038.18,1025.45,1030.74,1226200000 +2002-06-07,1029.15,1033.02,1012.49,1027.53,1341300000 +2002-06-06,1049.9,1049.9,1026.91,1029.15,1601500000 +2002-06-05,1040.69,1050.11,1038.84,1049.9,1300100000 +2002-06-04,1040.68,1046.06,1030.52,1040.69,1466600000 +2002-06-03,1067.14,1070.74,1039.9,1040.68,1324300000 +2002-05-31,1064.66,1079.93,1064.66,1067.14,1277300000 +2002-05-30,1067.66,1069.5,1054.26,1064.66,1286600000 +2002-05-29,1074.55,1074.83,1067.66,1067.66,1081800000 +2002-05-28,1083.82,1085.98,1070.31,1074.55,996500000 +2002-05-24,1097.08,1097.08,1082.19,1083.82,885400000 +2002-05-23,1086.02,1097.1,1080.55,1097.08,1192900000 +2002-05-22,1079.88,1086.02,1075.64,1086.02,1136300000 +2002-05-21,1091.88,1099.55,1079.08,1079.88,1200500000 +2002-05-20,1106.59,1106.59,1090.61,1091.88,989800000 +2002-05-17,1098.23,1106.59,1096.77,1106.59,1274400000 +2002-05-16,1091.07,1099.29,1089.17,1098.23,1256600000 +2002-05-15,1097.28,1104.23,1088.94,1091.07,1420200000 +2002-05-14,1074.56,1097.71,1074.56,1097.28,1414500000 +2002-05-13,1054.99,1074.84,1053.9,1074.56,1088600000 +2002-05-10,1073.01,1075.43,1053.93,1054.99,1171900000 +2002-05-09,1088.85,1088.85,1072.23,1073.01,1153000000 +2002-05-08,1049.49,1088.92,1049.49,1088.85,1502000000 +2002-05-07,1052.67,1058.67,1048.96,1049.49,1354700000 +2002-05-06,1073.43,1075.96,1052.65,1052.67,1122600000 +2002-05-03,1084.56,1084.56,1068.89,1073.43,1284500000 +2002-05-02,1086.46,1091.42,1079.46,1084.56,1364000000 +2002-05-01,1076.92,1088.32,1065.29,1086.46,1451400000 +2002-04-30,1065.45,1082.62,1063.46,1076.92,1628600000 +2002-04-29,1076.32,1078.95,1063.62,1065.45,1314700000 +2002-04-26,1091.48,1096.77,1076.31,1076.32,1374200000 +2002-04-25,1093.14,1094.36,1084.81,1091.48,1517400000 +2002-04-24,1100.96,1108.46,1092.51,1093.14,1373200000 +2002-04-23,1107.83,1111.17,1098.94,1100.96,1388500000 +2002-04-22,1125.17,1125.17,1105.62,1107.83,1181800000 +2002-04-19,1124.47,1128.82,1122.59,1125.17,1185000000 +2002-04-18,1126.07,1130.49,1109.29,1124.47,1359300000 +2002-04-17,1128.37,1133,1123.37,1126.07,1376900000 +2002-04-16,1102.55,1129.4,1102.55,1128.37,1341300000 +2002-04-15,1111.01,1114.86,1099.41,1102.55,1120400000 +2002-04-12,1103.69,1112.77,1102.74,1111.01,1282100000 +2002-04-11,1130.47,1130.47,1102.42,1103.69,1505600000 +2002-04-10,1117.8,1131.76,1117.8,1130.47,1447900000 +2002-04-09,1125.29,1128.29,1116.73,1117.8,1235400000 +2002-04-08,1122.73,1125.41,1111.79,1125.29,1095300000 +2002-04-05,1126.34,1133.31,1119.49,1122.73,1110200000 +2002-04-04,1125.4,1130.45,1120.06,1126.34,1283800000 +2002-04-03,1136.76,1138.85,1119.68,1125.4,1219700000 +2002-04-02,1146.54,1146.54,1135.71,1136.76,1176700000 +2002-04-01,1147.39,1147.84,1132.87,1146.54,1050900000 +2002-03-28,1144.58,1154.45,1144.58,1147.39,1147600000 +2002-03-27,1138.49,1146.95,1135.33,1144.58,1180100000 +2002-03-26,1131.87,1147,1131.61,1138.49,1223600000 +2002-03-25,1148.7,1151.04,1131.87,1131.87,1057900000 +2002-03-22,1153.59,1156.49,1144.6,1148.7,1243300000 +2002-03-21,1151.85,1155.1,1139.48,1153.59,1339200000 +2002-03-20,1170.29,1170.29,1151.61,1151.85,1304900000 +2002-03-19,1165.55,1173.94,1165.55,1170.29,1255000000 +2002-03-18,1166.16,1172.73,1159.14,1165.55,1169500000 +2002-03-15,1153.04,1166.48,1153.04,1166.16,1493900000 +2002-03-14,1154.09,1157.83,1151.08,1153.04,1208800000 +2002-03-13,1165.58,1165.58,1151.01,1154.09,1354000000 +2002-03-12,1168.26,1168.26,1154.34,1165.58,1304400000 +2002-03-11,1164.31,1173.03,1159.58,1168.26,1210200000 +2002-03-08,1157.54,1172.76,1157.54,1164.31,1412000000 +2002-03-07,1162.77,1167.94,1150.69,1157.54,1517400000 +2002-03-06,1146.14,1165.29,1145.11,1162.77,1541300000 +2002-03-05,1153.84,1157.74,1144.78,1146.14,1549300000 +2002-03-04,1131.78,1153.84,1130.93,1153.84,1594300000 +2002-03-01,1106.73,1131.79,1106.73,1131.78,1456500000 +2002-02-28,1109.89,1121.57,1106.73,1106.73,1392200000 +2002-02-27,1109.38,1123.06,1102.26,1109.89,1393800000 +2002-02-26,1109.43,1115.05,1101.72,1109.38,1309200000 +2002-02-25,1089.84,1112.71,1089.84,1109.43,1367400000 +2002-02-22,1080.95,1093.93,1074.39,1089.84,1411000000 +2002-02-21,1097.98,1101.5,1080.24,1080.95,1381600000 +2002-02-20,1083.34,1098.32,1074.36,1097.98,1438900000 +2002-02-19,1104.18,1104.18,1082.24,1083.34,1189900000 +2002-02-15,1116.48,1117.09,1103.23,1104.18,1359200000 +2002-02-14,1118.51,1124.72,1112.3,1116.48,1272500000 +2002-02-13,1107.5,1120.56,1107.5,1118.51,1215900000 +2002-02-12,1111.94,1112.68,1102.98,1107.5,1094200000 +2002-02-11,1096.22,1112.01,1094.68,1111.94,1159400000 +2002-02-08,1080.17,1096.3,1079.91,1096.22,1371900000 +2002-02-07,1083.51,1094.03,1078.44,1080.17,1441600000 +2002-02-06,1090.02,1093.58,1077.78,1083.51,1665800000 +2002-02-05,1094.44,1100.96,1082.58,1090.02,1778300000 +2002-02-04,1122.2,1122.2,1092.25,1094.44,1437600000 +2002-02-01,1130.2,1130.2,1118.51,1122.2,1367200000 +2002-01-31,1113.57,1130.21,1113.3,1130.2,1557000000 +2002-01-30,1100.64,1113.79,1081.66,1113.57,2019600000 +2002-01-29,1133.06,1137.47,1098.74,1100.64,1812000000 +2002-01-28,1133.28,1138.63,1126.66,1133.06,1186800000 +2002-01-25,1132.15,1138.31,1127.82,1133.28,1345100000 +2002-01-24,1128.18,1139.5,1128.18,1132.15,1552800000 +2002-01-23,1119.31,1131.94,1117.43,1128.18,1479200000 +2002-01-22,1127.58,1135.26,1117.91,1119.31,1311600000 +2002-01-18,1138.88,1138.88,1124.45,1127.58,1333300000 +2002-01-17,1127.57,1139.27,1127.57,1138.88,1380100000 +2002-01-16,1146.19,1146.19,1127.49,1127.57,1482500000 +2002-01-15,1138.41,1148.81,1136.88,1146.19,1386900000 +2002-01-14,1145.6,1145.6,1138.15,1138.41,1286400000 +2002-01-11,1156.55,1159.41,1145.45,1145.6,1211900000 +2002-01-10,1155.14,1159.93,1150.85,1156.55,1299000000 +2002-01-09,1160.71,1174.26,1151.89,1155.14,1452000000 +2002-01-08,1164.89,1167.6,1157.46,1160.71,1258800000 +2002-01-07,1172.51,1176.97,1163.55,1164.89,1308300000 +2002-01-04,1165.27,1176.55,1163.42,1172.51,1513000000 +2002-01-03,1154.67,1165.27,1154.01,1165.27,1398900000 +2002-01-02,1148.08,1154.67,1136.23,1154.67,1171000000 +2001-12-31,1161.02,1161.16,1148.04,1148.08,943600000 +2001-12-28,1157.13,1164.64,1157.13,1161.02,917400000 +2001-12-27,1149.37,1157.13,1149.37,1157.13,876300000 +2001-12-26,1144.65,1159.18,1144.65,1149.37,791100000 +2001-12-24,1144.89,1147.83,1144.62,1144.65,439670000 +2001-12-21,1139.93,1147.46,1139.93,1144.89,1694000000 +2001-12-20,1149.56,1151.42,1139.93,1139.93,1490500000 +2001-12-19,1142.92,1152.44,1134.75,1149.56,1484900000 +2001-12-18,1134.36,1145.1,1134.36,1142.92,1354000000 +2001-12-17,1123.09,1137.3,1122.66,1134.36,1260400000 +2001-12-14,1119.38,1128.28,1114.53,1123.09,1306800000 +2001-12-13,1137.07,1137.07,1117.85,1119.38,1511500000 +2001-12-12,1136.76,1141.58,1126.01,1137.07,1449700000 +2001-12-11,1139.93,1150.89,1134.32,1136.76,1367200000 +2001-12-10,1158.31,1158.31,1139.66,1139.93,1218700000 +2001-12-07,1167.1,1167.1,1152.66,1158.31,1248200000 +2001-12-06,1170.35,1173.35,1164.43,1167.1,1487900000 +2001-12-05,1143.77,1173.62,1143.77,1170.35,1765300000 +2001-12-04,1129.9,1144.8,1128.86,1144.8,1318500000 +2001-12-03,1139.45,1139.45,1125.78,1129.9,1202900000 +2001-11-30,1140.2,1143.57,1135.89,1139.45,1343600000 +2001-11-29,1128.52,1140.4,1125.51,1140.2,1375700000 +2001-11-28,1149.5,1149.5,1128.29,1128.52,1423700000 +2001-11-27,1157.42,1163.38,1140.81,1149.5,1288000000 +2001-11-26,1150.34,1157.88,1146.17,1157.42,1129800000 +2001-11-23,1137.03,1151.05,1135.9,1150.34,410300000 +2001-11-21,1142.66,1142.66,1129.78,1137.03,1029300000 +2001-11-20,1151.06,1152.45,1142.17,1142.66,1330200000 +2001-11-19,1138.65,1151.06,1138.65,1151.06,1316800000 +2001-11-16,1142.24,1143.52,1129.92,1138.65,1337400000 +2001-11-15,1141.21,1146.46,1135.06,1142.24,1454500000 +2001-11-14,1139.09,1148.28,1132.87,1141.21,1443400000 +2001-11-13,1118.33,1139.14,1118.33,1139.09,1370100000 +2001-11-12,1120.31,1121.71,1098.32,1118.33,991600000 +2001-11-09,1118.54,1123.02,1111.13,1120.31,1093800000 +2001-11-08,1115.8,1135.75,1115.42,1118.54,1517500000 +2001-11-07,1118.86,1126.62,1112.98,1115.8,1411300000 +2001-11-06,1102.84,1119.73,1095.36,1118.86,1356000000 +2001-11-05,1087.2,1106.72,1087.2,1102.84,1267700000 +2001-11-02,1084.1,1089.63,1075.58,1087.2,1121900000 +2001-11-01,1059.78,1085.61,1054.31,1084.1,1317400000 +2001-10-31,1059.79,1074.79,1057.55,1059.78,1352500000 +2001-10-30,1078.3,1078.3,1053.61,1059.79,1297400000 +2001-10-29,1104.61,1104.61,1078.3,1078.3,1106100000 +2001-10-26,1100.09,1110.61,1094.24,1104.61,1244500000 +2001-10-25,1085.2,1100.09,1065.64,1100.09,1364400000 +2001-10-24,1084.78,1090.26,1079.98,1085.2,1336200000 +2001-10-23,1089.9,1098.99,1081.53,1084.78,1317300000 +2001-10-22,1073.48,1090.57,1070.79,1089.9,1105700000 +2001-10-19,1068.61,1075.52,1057.24,1073.48,1294900000 +2001-10-18,1077.09,1077.94,1064.54,1068.61,1262900000 +2001-10-17,1097.54,1107.12,1076.57,1077.09,1452200000 +2001-10-16,1089.98,1101.66,1087.13,1097.54,1210500000 +2001-10-15,1091.65,1091.65,1078.19,1089.98,1024700000 +2001-10-12,1097.43,1097.43,1072.15,1091.65,1331400000 +2001-10-11,1080.99,1099.16,1080.99,1097.43,1704580000 +2001-10-10,1056.75,1081.62,1052.76,1080.99,1312400000 +2001-10-09,1062.44,1063.37,1053.83,1056.75,1227800000 +2001-10-08,1071.37,1071.37,1056.88,1062.44,979000000 +2001-10-05,1069.62,1072.35,1053.5,1071.38,1301700000 +2001-10-04,1072.28,1084.12,1067.82,1069.63,1609100000 +2001-10-03,1051.33,1075.38,1041.48,1072.28,1650600000 +2001-10-02,1038.55,1051.33,1034.47,1051.33,1289800000 +2001-10-01,1040.94,1040.94,1026.76,1038.55,1175600000 +2001-09-28,1018.61,1040.94,1018.61,1040.94,1631500000 +2001-09-27,1007.04,1018.92,998.24,1018.61,1467000000 +2001-09-26,1012.27,1020.29,1002.62,1007.04,1519100000 +2001-09-25,1003.45,1017.14,998.33,1012.27,1613800000 +2001-09-24,965.8,1008.44,965.8,1003.45,1746600000 +2001-09-21,984.54,984.54,944.75,965.8,2317300000 +2001-09-20,1016.1,1016.1,984.49,984.54,2004800000 +2001-09-19,1032.74,1038.91,984.62,1016.1,2120550000 +2001-09-18,1038.77,1046.42,1029.25,1032.74,1650410000 +2001-09-17,1092.54,1092.54,1037.46,1038.77,2330830000 +2001-09-10,1085.78,1096.94,1073.15,1092.54,1276600000 +2001-09-07,1106.4,1106.4,1082.12,1085.78,1424300000 +2001-09-06,1131.74,1131.74,1105.83,1106.4,1359700000 +2001-09-05,1132.94,1135.52,1114.86,1131.74,1384500000 +2001-09-04,1133.58,1155.4,1129.06,1132.94,1178300000 +2001-08-31,1129.03,1141.83,1126.38,1133.58,920100000 +2001-08-30,1148.6,1151.75,1124.87,1129.03,1157000000 +2001-08-29,1161.51,1166.97,1147.38,1148.56,963700000 +2001-08-28,1179.21,1179.66,1161.17,1161.51,987100000 +2001-08-27,1184.93,1186.85,1178.07,1179.21,842600000 +2001-08-24,1162.09,1185.15,1162.09,1184.93,1043600000 +2001-08-23,1165.31,1169.86,1160.96,1162.09,986200000 +2001-08-22,1157.26,1168.56,1153.34,1165.31,1110800000 +2001-08-21,1171.41,1179.85,1156.56,1157.26,1041600000 +2001-08-20,1161.97,1171.41,1160.94,1171.41,897100000 +2001-08-17,1181.66,1181.66,1156.07,1161.97,974300000 +2001-08-16,1178.02,1181.8,1166.08,1181.66,1055400000 +2001-08-15,1186.73,1191.21,1177.61,1178.02,1065600000 +2001-08-14,1191.29,1198.79,1184.26,1186.73,964600000 +2001-08-13,1190.16,1193.82,1185.12,1191.29,837600000 +2001-08-10,1183.43,1193.33,1169.55,1190.16,960900000 +2001-08-09,1183.53,1184.71,1174.68,1183.43,1104200000 +2001-08-08,1204.4,1206.79,1181.27,1183.53,1124600000 +2001-08-07,1200.47,1207.56,1195.64,1204.4,1012000000 +2001-08-06,1214.35,1214.35,1197.35,1200.48,811700000 +2001-08-03,1220.75,1220.75,1205.31,1214.35,939900000 +2001-08-02,1215.93,1226.27,1215.31,1220.75,1218300000 +2001-08-01,1211.23,1223.04,1211.23,1215.93,1340300000 +2001-07-31,1204.52,1222.74,1204.52,1211.23,1129200000 +2001-07-30,1205.82,1209.05,1200.41,1204.52,909100000 +2001-07-27,1202.93,1209.26,1195.99,1205.82,1015300000 +2001-07-26,1190.49,1204.18,1182.65,1202.93,1213900000 +2001-07-25,1171.65,1190.52,1171.28,1190.49,1280700000 +2001-07-24,1191.03,1191.03,1165.54,1171.65,1198700000 +2001-07-23,1210.85,1215.22,1190.5,1191.03,986900000 +2001-07-20,1215.02,1215.69,1207.04,1210.85,1170900000 +2001-07-19,1207.71,1225.04,1205.8,1215.02,1343500000 +2001-07-18,1214.44,1214.44,1198.33,1207.71,1316300000 +2001-07-17,1202.45,1215.36,1196.14,1214.44,1238100000 +2001-07-16,1215.68,1219.63,1200.05,1202.45,1039800000 +2001-07-13,1208.14,1218.54,1203.61,1215.68,1121700000 +2001-07-12,1180.18,1210.25,1180.18,1208.14,1394000000 +2001-07-11,1181.52,1184.93,1168.46,1180.18,1384100000 +2001-07-10,1198.78,1203.43,1179.93,1181.52,1263800000 +2001-07-09,1190.59,1201.76,1189.75,1198.78,1045700000 +2001-07-06,1219.24,1219.24,1188.74,1190.59,1056700000 +2001-07-05,1234.45,1234.45,1219.15,1219.24,934900000 +2001-07-03,1236.71,1236.71,1229.43,1234.45,622110000 +2001-07-02,1224.42,1239.78,1224.03,1236.72,1128300000 +2001-06-29,1226.2,1237.29,1221.14,1224.38,1832360000 +2001-06-28,1211.07,1234.44,1211.07,1226.2,1327300000 +2001-06-27,1216.76,1219.92,1207.29,1211.07,1162100000 +2001-06-26,1218.6,1220.7,1204.64,1216.76,1198900000 +2001-06-25,1225.35,1231.5,1213.6,1218.6,1050100000 +2001-06-22,1237.04,1237.73,1221.41,1225.35,1189200000 +2001-06-21,1223.14,1240.24,1220.25,1237.04,1546820000 +2001-06-20,1212.58,1225.61,1210.07,1223.14,1350100000 +2001-06-19,1208.43,1226.11,1207.71,1212.58,1184900000 +2001-06-18,1214.36,1221.23,1208.33,1208.43,1111600000 +2001-06-15,1219.87,1221.5,1203.03,1214.36,1635550000 +2001-06-14,1241.6,1241.6,1218.9,1219.87,1242900000 +2001-06-13,1255.85,1259.75,1241.59,1241.6,1063600000 +2001-06-12,1254.39,1261,1235.75,1255.85,1136500000 +2001-06-11,1264.96,1264.96,1249.23,1254.39,870100000 +2001-06-08,1276.96,1277.11,1259.99,1264.96,726200000 +2001-06-07,1270.03,1277.08,1265.08,1276.96,1089600000 +2001-06-06,1283.57,1283.85,1269.01,1270.03,1061900000 +2001-06-05,1267.11,1286.62,1267.11,1283.57,1116800000 +2001-06-04,1260.67,1267.17,1256.36,1267.11,836500000 +2001-06-01,1255.82,1265.34,1246.88,1260.67,1015000000 +2001-05-31,1248.08,1261.91,1248.07,1255.82,1226600000 +2001-05-30,1267.93,1267.93,1245.96,1248.08,1158600000 +2001-05-29,1277.89,1278.42,1265.41,1267.93,1026000000 +2001-05-25,1293.17,1293.17,1276.42,1277.89,828100000 +2001-05-24,1289.05,1295.04,1281.22,1293.17,1100700000 +2001-05-23,1309.38,1309.38,1288.7,1289.05,1134800000 +2001-05-22,1312.83,1315.93,1306.89,1309.38,1260400000 +2001-05-21,1291.96,1312.95,1287.87,1312.83,1174900000 +2001-05-18,1288.49,1292.06,1281.15,1291.96,1130800000 +2001-05-17,1284.99,1296.48,1282.65,1288.49,1355600000 +2001-05-16,1249.44,1286.39,1243.02,1284.99,1405300000 +2001-05-15,1248.92,1257.45,1245.36,1249.44,1071800000 +2001-05-14,1245.67,1249.68,1241.02,1248.92,858200000 +2001-05-11,1255.18,1259.84,1240.79,1245.67,906200000 +2001-05-10,1255.54,1268.14,1254.56,1255.18,1056700000 +2001-05-09,1261.2,1261.65,1247.83,1255.54,1132400000 +2001-05-08,1266.71,1267.01,1253,1261.2,1006300000 +2001-05-07,1266.61,1270,1259.19,1263.51,949000000 +2001-05-04,1248.58,1267.51,1232,1266.61,1082100000 +2001-05-03,1267.43,1267.43,1239.88,1248.58,1137900000 +2001-05-02,1266.44,1272.93,1257.7,1267.43,1342200000 +2001-05-01,1249.46,1266.47,1243.55,1266.44,1181300000 +2001-04-30,1253.05,1269.3,1243.99,1249.46,1266800000 +2001-04-27,1234.52,1253.07,1234.52,1253.05,1091300000 +2001-04-26,1228.75,1248.3,1228.75,1234.52,1345200000 +2001-04-25,1209.47,1232.36,1207.38,1228.75,1203600000 +2001-04-24,1224.36,1233.54,1208.89,1209.47,1216500000 +2001-04-23,1242.98,1242.98,1217.47,1224.36,1012600000 +2001-04-20,1253.7,1253.7,1234.41,1242.98,1338700000 +2001-04-19,1238.16,1253.71,1233.39,1253.69,1486800000 +2001-04-18,1191.81,1248.42,1191.81,1238.16,1918900000 +2001-04-17,1179.68,1192.25,1168.9,1191.81,1109600000 +2001-04-16,1183.5,1184.64,1167.38,1179.68,913900000 +2001-04-12,1165.89,1183.51,1157.73,1183.5,1102000000 +2001-04-11,1168.38,1182.24,1160.26,1165.89,1290300000 +2001-04-10,1137.59,1173.92,1137.59,1168.38,1349600000 +2001-04-09,1128.43,1146.13,1126.38,1137.59,1062800000 +2001-04-06,1151.44,1151.44,1119.29,1128.43,1266800000 +2001-04-05,1103.25,1151.47,1103.25,1151.44,1368000000 +2001-04-04,1106.46,1117.5,1091.99,1103.25,1425590000 +2001-04-03,1145.87,1145.87,1100.19,1106.46,1386100000 +2001-04-02,1160.33,1169.51,1137.51,1145.87,1254900000 +2001-03-30,1147.95,1162.8,1143.83,1160.33,1280800000 +2001-03-29,1153.29,1161.69,1136.26,1147.95,1234500000 +2001-03-28,1182.17,1182.17,1147.83,1153.29,1333400000 +2001-03-27,1152.69,1183.35,1150.96,1182.17,1314200000 +2001-03-26,1139.83,1160.02,1139.83,1152.69,1114000000 +2001-03-23,1117.58,1141.83,1117.58,1139.83,1364900000 +2001-03-22,1122.14,1124.27,1081.19,1117.58,1723950000 +2001-03-21,1142.62,1149.39,1118.74,1122.14,1346300000 +2001-03-20,1170.81,1180.56,1142.19,1142.62,1235900000 +2001-03-19,1150.53,1173.5,1147.18,1170.81,1126200000 +2001-03-16,1173.56,1173.56,1148.64,1150.53,1543560000 +2001-03-15,1166.71,1182.04,1166.71,1173.56,1259500000 +2001-03-14,1197.66,1197.66,1155.35,1166.71,1397400000 +2001-03-13,1180.16,1197.83,1171.5,1197.66,1360900000 +2001-03-12,1233.42,1233.42,1176.78,1180.16,1229000000 +2001-03-09,1264.74,1264.74,1228.42,1233.42,1085900000 +2001-03-08,1261.89,1266.5,1257.6,1264.74,1114100000 +2001-03-07,1253.8,1263.86,1253.8,1261.89,1132200000 +2001-03-06,1241.41,1267.42,1241.41,1253.8,1091800000 +2001-03-05,1234.18,1242.55,1234.04,1241.41,929200000 +2001-03-02,1241.23,1251.01,1219.74,1234.18,1294000000 +2001-03-01,1239.94,1241.36,1214.5,1241.23,1294900000 +2001-02-28,1257.94,1263.47,1229.65,1239.94,1225300000 +2001-02-27,1267.65,1272.76,1252.26,1257.94,1114100000 +2001-02-26,1245.86,1267.69,1241.71,1267.65,1130800000 +2001-02-23,1252.82,1252.82,1215.44,1245.86,1231300000 +2001-02-22,1255.27,1259.94,1228.33,1252.82,1365900000 +2001-02-21,1278.94,1282.97,1253.16,1255.27,1208500000 +2001-02-20,1301.53,1307.16,1278.44,1278.94,1112200000 +2001-02-16,1326.61,1326.61,1293.18,1301.53,1257200000 +2001-02-15,1315.92,1331.29,1315.92,1326.61,1153700000 +2001-02-14,1318.8,1320.73,1304.72,1315.92,1150300000 +2001-02-13,1330.31,1336.62,1317.51,1318.8,1075200000 +2001-02-12,1314.76,1330.96,1313.64,1330.31,1039100000 +2001-02-09,1332.53,1332.53,1309.98,1314.76,1075500000 +2001-02-08,1341.1,1350.32,1332.42,1332.53,1107200000 +2001-02-07,1352.26,1352.26,1334.26,1340.89,1158300000 +2001-02-06,1354.31,1363.55,1350.04,1352.26,1059600000 +2001-02-05,1349.47,1354.56,1344.48,1354.31,1013000000 +2001-02-02,1373.47,1376.38,1348.72,1349.47,1048400000 +2001-02-01,1366.01,1373.5,1359.34,1373.47,1118800000 +2001-01-31,1373.73,1383.37,1364.66,1366.01,1295300000 +2001-01-30,1364.17,1375.68,1356.2,1373.73,1149800000 +2001-01-29,1354.92,1365.54,1350.36,1364.17,1053100000 +2001-01-26,1357.51,1357.51,1342.75,1354.95,1098000000 +2001-01-25,1364.3,1367.35,1354.63,1357.51,1258000000 +2001-01-24,1360.4,1369.75,1357.28,1364.3,1309000000 +2001-01-23,1342.9,1362.9,1339.63,1360.4,1232600000 +2001-01-22,1342.54,1353.62,1333.84,1342.9,1164000000 +2001-01-19,1347.97,1354.55,1336.74,1342.54,1407800000 +2001-01-18,1329.89,1352.71,1327.41,1347.97,1445000000 +2001-01-17,1326.65,1346.92,1325.41,1329.47,1349100000 +2001-01-16,1318.32,1327.81,1313.33,1326.65,1205700000 +2001-01-12,1326.82,1333.21,1311.59,1318.55,1276000000 +2001-01-11,1313.27,1332.19,1309.72,1326.82,1411200000 +2001-01-10,1300.8,1313.76,1287.28,1313.27,1296500000 +2001-01-09,1295.86,1311.72,1295.14,1300.8,1191300000 +2001-01-08,1298.35,1298.35,1276.29,1295.86,1115500000 +2001-01-05,1333.34,1334.77,1294.95,1298.35,1430800000 +2001-01-04,1347.56,1350.24,1329.14,1333.34,2131000000 +2001-01-03,1283.27,1347.76,1274.62,1347.56,1880700000 +2001-01-02,1320.28,1320.28,1276.05,1283.27,1129400000 +2000-12-29,1334.22,1340.1,1317.51,1320.28,1035500000 +2000-12-28,1328.92,1335.93,1325.78,1334.22,1015300000 +2000-12-27,1315.19,1332.03,1310.96,1328.92,1092700000 +2000-12-26,1305.97,1315.94,1301.64,1315.19,806500000 +2000-12-22,1274.86,1305.97,1274.86,1305.95,1087100000 +2000-12-21,1264.74,1285.31,1254.07,1274.86,1449900000 +2000-12-20,1305.6,1305.6,1261.16,1264.74,1421600000 +2000-12-19,1322.96,1346.44,1305.2,1305.6,1324900000 +2000-12-18,1312.15,1332.32,1312.15,1322.74,1189900000 +2000-12-15,1340.93,1340.93,1305.38,1312.15,1561100000 +2000-12-14,1359.99,1359.99,1340.48,1340.93,1061300000 +2000-12-13,1371.18,1385.82,1358.48,1359.99,1195100000 +2000-12-12,1380.2,1380.27,1370.27,1371.18,1083400000 +2000-12-11,1369.89,1389.05,1364.14,1380.2,1202400000 +2000-12-08,1343.55,1380.33,1343.55,1369.89,1358300000 +2000-12-07,1351.46,1353.5,1339.26,1343.55,1128000000 +2000-12-06,1376.54,1376.54,1346.15,1351.46,1399300000 +2000-12-05,1324.97,1376.56,1324.97,1376.54,900300000 +2000-12-04,1315.18,1332.06,1310.23,1324.97,1103000000 +2000-12-01,1314.95,1334.67,1307.02,1315.23,1195200000 +2000-11-30,1341.91,1341.91,1294.9,1314.95,1186530000 +2000-11-29,1336.09,1352.38,1329.28,1341.93,402100000 +2000-11-28,1348.97,1358.81,1334.97,1336.09,1028200000 +2000-11-27,1341.77,1362.5,1341.77,1348.97,946100000 +2000-11-24,1322.36,1343.83,1322.36,1341.77,404870000 +2000-11-22,1347.35,1347.35,1321.89,1322.36,963200000 +2000-11-21,1342.62,1355.87,1333.62,1347.35,1137100000 +2000-11-20,1367.72,1367.72,1341.67,1342.62,955800000 +2000-11-17,1372.32,1384.85,1355.55,1367.72,1070400000 +2000-11-16,1389.81,1394.76,1370.39,1372.32,956300000 +2000-11-15,1382.95,1395.96,1374.75,1389.81,1066800000 +2000-11-14,1351.26,1390.06,1351.26,1382.95,1118800000 +2000-11-13,1365.98,1365.98,1328.62,1351.26,1129300000 +2000-11-10,1400.14,1400.14,1365.97,1365.98,962500000 +2000-11-09,1409.28,1409.28,1369.68,1400.14,1111000000 +2000-11-08,1431.87,1437.28,1408.78,1409.28,909300000 +2000-11-07,1432.19,1436.22,1423.26,1431.87,880900000 +2000-11-06,1428.76,1438.46,1427.72,1432.19,930900000 +2000-11-03,1428.32,1433.21,1420.92,1426.69,997700000 +2000-11-02,1421.22,1433.4,1421.22,1428.32,1167700000 +2000-11-01,1429.4,1429.6,1410.45,1421.22,1206800000 +2000-10-31,1398.66,1432.22,1398.66,1429.4,1366400000 +2000-10-30,1379.58,1406.36,1376.86,1398.66,1186500000 +2000-10-27,1364.44,1384.57,1364.13,1379.58,1086300000 +2000-10-26,1364.9,1372.72,1337.81,1364.44,1303800000 +2000-10-25,1398.13,1398.13,1362.21,1364.9,1315600000 +2000-10-24,1395.78,1415.64,1388.13,1398.13,1158600000 +2000-10-23,1396.93,1406.96,1387.75,1395.78,1046800000 +2000-10-20,1388.76,1408.47,1382.19,1396.93,1177400000 +2000-10-19,1342.13,1389.93,1342.13,1388.76,1297900000 +2000-10-18,1349.97,1356.65,1305.79,1342.13,1441700000 +2000-10-17,1374.62,1380.99,1342.34,1349.97,1161500000 +2000-10-16,1374.17,1379.48,1365.06,1374.62,1005400000 +2000-10-13,1329.78,1374.17,1327.08,1374.17,1223900000 +2000-10-12,1364.59,1374.93,1328.06,1329.78,1388600000 +2000-10-11,1387.02,1387.02,1349.67,1364.59,1387500000 +2000-10-10,1402.03,1408.83,1383.85,1387.02,1044000000 +2000-10-09,1408.99,1409.69,1392.48,1402.03,716600000 +2000-10-06,1436.28,1443.3,1397.06,1408.99,1150100000 +2000-10-05,1434.32,1444.17,1431.8,1436.28,1176100000 +2000-10-04,1426.46,1439.99,1416.31,1434.32,1167400000 +2000-10-03,1436.23,1454.82,1425.28,1426.46,1098100000 +2000-10-02,1436.52,1445.6,1429.83,1436.23,1051200000 +2000-09-29,1458.29,1458.29,1436.29,1436.51,1197100000 +2000-09-28,1426.57,1461.69,1425.78,1458.29,1206200000 +2000-09-27,1427.21,1437.22,1419.44,1426.57,1174700000 +2000-09-26,1439.03,1448.04,1425.25,1427.21,1106600000 +2000-09-25,1448.72,1457.42,1435.93,1439.03,982400000 +2000-09-22,1449.05,1449.05,1421.88,1448.72,1185500000 +2000-09-21,1451.34,1452.77,1436.3,1449.05,1105400000 +2000-09-20,1459.9,1460.49,1430.95,1451.34,1104000000 +2000-09-19,1444.51,1461.16,1444.51,1459.9,1024900000 +2000-09-18,1465.81,1467.77,1441.92,1444.51,962500000 +2000-09-15,1480.87,1480.96,1460.22,1465.81,1268400000 +2000-09-14,1484.91,1494.16,1476.73,1480.87,1014000000 +2000-09-13,1481.99,1487.45,1473.61,1484.91,1068300000 +2000-09-12,1489.26,1496.93,1479.67,1481.99,991200000 +2000-09-11,1494.5,1506.76,1483.01,1489.26,899300000 +2000-09-08,1502.51,1502.51,1489.88,1494.5,961000000 +2000-09-07,1492.25,1505.34,1492.25,1502.51,985500000 +2000-09-06,1507.08,1512.61,1492.12,1492.25,995100000 +2000-09-05,1520.77,1520.77,1504.21,1507.08,838500000 +2000-09-01,1517.68,1530.09,1515.53,1520.77,767700000 +2000-08-31,1502.59,1525.21,1502.59,1517.68,1056600000 +2000-08-30,1509.84,1510.49,1500.09,1502.59,818400000 +2000-08-29,1514.09,1514.81,1505.46,1509.84,795600000 +2000-08-28,1506.45,1523.95,1506.45,1514.09,733600000 +2000-08-25,1508.31,1513.47,1505.09,1506.45,685600000 +2000-08-24,1505.97,1511.16,1501.25,1508.31,837100000 +2000-08-23,1498.13,1507.2,1489.52,1505.97,871000000 +2000-08-22,1499.48,1508.45,1497.42,1498.13,818800000 +2000-08-21,1491.72,1502.84,1491.13,1499.48,731600000 +2000-08-18,1496.07,1499.47,1488.99,1491.72,821400000 +2000-08-17,1479.85,1499.32,1479.85,1496.07,922400000 +2000-08-16,1484.43,1496.09,1475.74,1479.85,929800000 +2000-08-15,1491.56,1493.12,1482.74,1484.43,895900000 +2000-08-14,1471.84,1491.64,1468.56,1491.56,783800000 +2000-08-11,1460.25,1475.72,1453.06,1471.84,835500000 +2000-08-10,1472.87,1475.15,1459.89,1460.25,940800000 +2000-08-09,1482.8,1490.33,1471.16,1472.87,1054000000 +2000-08-08,1479.32,1484.52,1472.61,1482.8,992200000 +2000-08-07,1462.93,1480.8,1460.72,1479.32,854800000 +2000-08-04,1452.56,1462.93,1451.31,1462.93,956000000 +2000-08-03,1438.7,1454.19,1425.43,1452.56,1095600000 +2000-08-02,1438.1,1451.59,1433.49,1438.7,994500000 +2000-08-01,1430.83,1443.54,1428.96,1438.1,938700000 +2000-07-31,1419.89,1437.65,1418.71,1430.83,952600000 +2000-07-28,1449.62,1456.68,1413.89,1419.89,980000000 +2000-07-27,1452.42,1464.91,1445.33,1449.62,1156400000 +2000-07-26,1474.47,1474.47,1452.42,1452.42,1235800000 +2000-07-25,1464.29,1476.23,1464.29,1474.47,969400000 +2000-07-24,1480.19,1485.88,1463.8,1464.29,880300000 +2000-07-21,1495.57,1495.57,1477.91,1480.19,968300000 +2000-07-20,1481.96,1501.92,1481.96,1495.57,1064600000 +2000-07-19,1493.74,1495.63,1479.92,1481.96,909400000 +2000-07-18,1510.49,1510.49,1491.35,1493.74,908300000 +2000-07-17,1509.98,1517.32,1505.26,1510.49,906000000 +2000-07-14,1495.84,1509.99,1494.56,1509.98,960600000 +2000-07-13,1492.92,1501.39,1489.65,1495.84,1026800000 +2000-07-12,1480.88,1497.69,1480.88,1492.92,1001200000 +2000-07-11,1475.62,1488.77,1470.48,1480.88,980500000 +2000-07-10,1478.9,1486.56,1474.76,1475.62,838700000 +2000-07-07,1456.67,1484.12,1456.67,1478.9,931700000 +2000-07-06,1446.23,1461.65,1439.56,1456.67,947300000 +2000-07-05,1469.54,1469.54,1442.45,1446.23,1019300000 +2000-07-03,1454.6,1469.58,1450.85,1469.54,451900000 +2000-06-30,1442.39,1454.68,1438.71,1454.6,1459700000 +2000-06-29,1454.82,1455.14,1434.63,1442.39,1110900000 +2000-06-28,1450.55,1467.63,1450.55,1454.82,1095100000 +2000-06-27,1455.31,1463.35,1450.55,1450.55,1042500000 +2000-06-26,1441.48,1459.66,1441.48,1455.31,889000000 +2000-06-23,1452.18,1459.94,1438.31,1441.48,847600000 +2000-06-22,1479.13,1479.13,1448.03,1452.18,1022700000 +2000-06-21,1475.95,1482.19,1468,1479.13,1009600000 +2000-06-20,1486,1487.32,1470.18,1475.95,1031500000 +2000-06-19,1464.46,1488.93,1459.05,1486,921700000 +2000-06-16,1478.73,1480.77,1460.42,1464.46,1250800000 +2000-06-15,1470.54,1482.04,1464.62,1478.73,1011400000 +2000-06-14,1469.44,1483.62,1467.71,1470.54,929700000 +2000-06-13,1446,1470.42,1442.38,1469.44,935900000 +2000-06-12,1456.95,1462.93,1445.99,1446,774100000 +2000-06-09,1461.67,1472.67,1454.96,1456.95,786000000 +2000-06-08,1471.36,1475.65,1456.49,1461.67,854300000 +2000-06-07,1457.84,1474.64,1455.06,1471.36,854600000 +2000-06-06,1467.63,1471.36,1454.74,1457.84,950100000 +2000-06-05,1477.26,1477.28,1464.68,1467.63,838600000 +2000-06-02,1448.81,1483.23,1448.81,1477.26,1162400000 +2000-06-01,1420.6,1448.81,1420.6,1448.81,960100000 +2000-05-31,1422.44,1434.49,1415.5,1420.6,960500000 +2000-05-30,1378.02,1422.45,1378.02,1422.45,844200000 +2000-05-26,1381.52,1391.42,1369.75,1378.02,722600000 +2000-05-25,1399.05,1411.65,1373.93,1381.52,984500000 +2000-05-24,1373.86,1401.75,1361.09,1399.05,1152300000 +2000-05-23,1400.72,1403.77,1373.43,1373.86,869900000 +2000-05-22,1406.95,1410.55,1368.73,1400.72,869000000 +2000-05-19,1437.21,1437.21,1401.74,1406.95,853700000 +2000-05-18,1447.8,1458.04,1436.59,1437.21,807900000 +2000-05-17,1466.04,1466.04,1441.67,1447.8,820500000 +2000-05-16,1452.36,1470.4,1450.76,1466.04,955500000 +2000-05-15,1420.96,1452.39,1416.54,1452.36,854600000 +2000-05-12,1407.81,1430.13,1407.81,1420.96,858200000 +2000-05-11,1383.05,1410.26,1383.05,1407.81,953600000 +2000-05-10,1412.14,1412.14,1375.14,1383.05,1006400000 +2000-05-09,1424.17,1430.28,1401.85,1412.14,896600000 +2000-05-08,1432.63,1432.63,1417.05,1424.17,787600000 +2000-05-05,1409.57,1436.03,1405.08,1432.63,805500000 +2000-05-04,1415.1,1420.99,1404.94,1409.57,925800000 +2000-05-03,1446.29,1446.29,1398.36,1415.1,991600000 +2000-05-02,1468.25,1468.25,1445.22,1446.29,1011500000 +2000-05-01,1452.43,1481.51,1452.43,1468.25,966300000 +2000-04-28,1464.92,1473.62,1448.15,1452.43,984600000 +2000-04-27,1460.99,1469.21,1434.81,1464.92,1111000000 +2000-04-26,1477.44,1482.94,1456.98,1460.99,999600000 +2000-04-25,1429.86,1477.67,1429.86,1477.44,1071100000 +2000-04-24,1434.54,1434.54,1407.13,1429.86,868700000 +2000-04-20,1427.47,1435.49,1422.08,1434.54,896200000 +2000-04-19,1441.61,1447.69,1424.26,1427.47,1001400000 +2000-04-18,1401.44,1441.61,1397.81,1441.61,1109400000 +2000-04-17,1356.56,1401.53,1346.5,1401.44,1204700000 +2000-04-14,1440.51,1440.51,1339.4,1356.56,1279700000 +2000-04-13,1467.17,1477.52,1439.34,1440.51,1032000000 +2000-04-12,1500.59,1509.08,1466.15,1467.17,1175900000 +2000-04-11,1504.46,1512.8,1486.78,1500.59,971400000 +2000-04-10,1516.35,1527.19,1503.35,1504.46,853700000 +2000-04-07,1501.34,1518.68,1501.34,1516.35,891600000 +2000-04-06,1487.37,1511.76,1487.37,1501.34,1008000000 +2000-04-05,1494.73,1506.55,1478.05,1487.37,1110300000 +2000-04-04,1505.98,1526.45,1416.41,1494.73,1515460000 +2000-04-03,1498.58,1507.19,1486.96,1505.97,1021700000 +2000-03-31,1487.92,1519.81,1484.38,1498.58,1227400000 +2000-03-30,1508.52,1517.38,1474.63,1487.92,1193400000 +2000-03-29,1507.73,1521.45,1497.45,1508.52,1061900000 +2000-03-28,1523.86,1527.36,1507.09,1507.73,959100000 +2000-03-27,1527.46,1534.63,1518.46,1523.86,901000000 +2000-03-24,1527.35,1552.87,1516.83,1527.46,1052200000 +2000-03-23,1500.64,1532.5,1492.39,1527.35,1078300000 +2000-03-22,1493.87,1505.08,1487.33,1500.64,1075000000 +2000-03-21,1456.63,1493.92,1446.06,1493.87,1065900000 +2000-03-20,1464.47,1470.3,1448.49,1456.63,920800000 +2000-03-17,1458.47,1477.33,1453.32,1464.47,1295100000 +2000-03-16,1392.15,1458.47,1392.15,1458.47,1482300000 +2000-03-15,1359.15,1397.99,1356.99,1392.14,1302800000 +2000-03-14,1383.62,1395.15,1359.15,1359.15,1094000000 +2000-03-13,1395.07,1398.39,1364.84,1383.62,1016100000 +2000-03-10,1401.69,1413.46,1392.07,1395.07,1138800000 +2000-03-09,1366.7,1401.82,1357.88,1401.69,1123000000 +2000-03-08,1355.62,1373.79,1346.62,1366.7,1203000000 +2000-03-07,1391.28,1399.21,1349.99,1355.62,1314100000 +2000-03-06,1409.17,1409.74,1384.75,1391.28,1029000000 +2000-03-03,1381.76,1410.88,1381.76,1409.17,1150300000 +2000-03-02,1379.19,1386.56,1370.35,1381.76,1198600000 +2000-03-01,1366.42,1383.46,1366.42,1379.19,1274100000 +2000-02-29,1348.05,1369.63,1348.05,1366.42,1204300000 +2000-02-28,1333.36,1360.82,1325.07,1348.05,1026500000 +2000-02-25,1353.43,1362.14,1329.15,1333.36,1065200000 +2000-02-24,1360.69,1364.8,1329.88,1353.43,1215000000 +2000-02-23,1352.17,1370.11,1342.44,1360.69,993700000 +2000-02-22,1346.09,1358.11,1331.88,1352.17,980000000 +2000-02-18,1388.26,1388.59,1345.32,1346.09,1042300000 +2000-02-17,1387.67,1399.88,1380.07,1388.26,1034800000 +2000-02-16,1402.05,1404.55,1385.58,1387.67,1018800000 +2000-02-15,1389.94,1407.72,1376.25,1402.05,1092100000 +2000-02-14,1387.12,1394.93,1380.53,1389.94,927300000 +2000-02-11,1416.83,1416.83,1378.89,1387.12,1025700000 +2000-02-10,1411.7,1422.1,1406.43,1416.83,1058800000 +2000-02-09,1441.72,1444.55,1411.65,1411.71,1050500000 +2000-02-08,1424.24,1441.83,1424.24,1441.72,1047700000 +2000-02-07,1424.37,1427.15,1413.33,1424.24,918100000 +2000-02-04,1424.97,1435.91,1420.63,1424.37,1045100000 +2000-02-03,1409.12,1425.78,1398.52,1424.97,1146500000 +2000-02-02,1409.28,1420.61,1403.49,1409.12,1038600000 +2000-02-01,1394.46,1412.49,1384.79,1409.28,981000000 +2000-01-31,1360.16,1394.48,1350.14,1394.46,993800000 +2000-01-28,1398.56,1398.56,1356.2,1360.16,1095800000 +2000-01-27,1404.09,1418.86,1370.99,1398.56,1129500000 +2000-01-26,1410.03,1412.73,1400.16,1404.09,1117300000 +2000-01-25,1401.53,1414.26,1388.49,1410.03,1073700000 +2000-01-24,1441.36,1454.09,1395.42,1401.53,1115800000 +2000-01-21,1445.57,1453.18,1439.6,1441.36,1209800000 +2000-01-20,1455.9,1465.71,1438.54,1445.57,1100700000 +2000-01-19,1455.14,1461.39,1448.68,1455.9,1087800000 +2000-01-18,1465.15,1465.15,1451.3,1455.14,1056700000 +2000-01-14,1449.68,1473,1449.68,1465.15,1085900000 +2000-01-13,1432.25,1454.2,1432.25,1449.68,1030400000 +2000-01-12,1438.56,1442.6,1427.08,1432.25,974600000 +2000-01-11,1457.6,1458.66,1434.42,1438.56,1014000000 +2000-01-10,1441.47,1464.36,1441.47,1457.6,1064800000 +2000-01-07,1403.45,1441.47,1400.73,1441.47,1225200000 +2000-01-06,1402.11,1411.9,1392.1,1403.45,1092300000 +2000-01-05,1399.42,1413.27,1377.68,1402.11,1085500000 +2000-01-04,1455.22,1455.22,1397.43,1399.42,1009000000 +2000-01-03,1469.25,1478,1438.36,1455.22,931800000 +1999-12-31,1464.47,1472.42,1458.19,1469.25,374050000 +1999-12-30,1463.46,1473.1,1462.6,1464.47,554680000 +1999-12-29,1457.66,1467.47,1457.66,1463.46,567860000 +1999-12-28,1457.09,1462.68,1452.78,1457.66,655400000 +1999-12-27,1458.34,1463.19,1450.83,1457.1,722600000 +1999-12-23,1436.13,1461.44,1436.13,1458.34,728600000 +1999-12-22,1433.43,1440.02,1429.13,1436.13,850000000 +1999-12-21,1418.09,1436.47,1414.8,1433.43,963500000 +1999-12-20,1421.03,1429.16,1411.1,1418.09,904600000 +1999-12-17,1418.78,1431.77,1418.78,1421.03,1349800000 +1999-12-16,1413.32,1423.11,1408.35,1418.78,1070300000 +1999-12-15,1403.17,1417.4,1396.2,1413.33,1033900000 +1999-12-14,1415.22,1418.3,1401.59,1403.17,1027800000 +1999-12-13,1417.04,1421.58,1410.1,1415.22,977600000 +1999-12-10,1408.11,1421.58,1405.65,1417.04,987200000 +1999-12-09,1403.88,1418.43,1391.47,1408.11,1122100000 +1999-12-08,1409.17,1415.66,1403.88,1403.88,957000000 +1999-12-07,1423.33,1426.81,1409.17,1409.17,1085800000 +1999-12-06,1433.3,1434.15,1418.25,1423.33,916800000 +1999-12-03,1409.04,1447.42,1409.04,1433.3,1006400000 +1999-12-02,1397.72,1409.04,1397.72,1409.04,900700000 +1999-12-01,1388.91,1400.12,1387.38,1397.72,884000000 +1999-11-30,1407.83,1410.59,1386.95,1388.91,951500000 +1999-11-29,1416.62,1416.62,1404.15,1407.83,866100000 +1999-11-26,1417.08,1425.24,1416.14,1416.62,312120000 +1999-11-24,1404.64,1419.71,1399.17,1417.08,734800000 +1999-11-23,1420.94,1423.91,1402.2,1404.64,926100000 +1999-11-22,1422,1425,1412.4,1420.94,873500000 +1999-11-19,1424.94,1424.94,1417.54,1422,893800000 +1999-11-18,1410.71,1425.31,1410.71,1424.94,1022800000 +1999-11-17,1420.07,1423.44,1410.69,1410.71,960000000 +1999-11-16,1394.39,1420.36,1394.39,1420.07,942200000 +1999-11-15,1396.06,1398.58,1392.28,1394.39,795700000 +1999-11-12,1381.46,1396.12,1368.54,1396.06,900200000 +1999-11-11,1373.46,1382.12,1372.19,1381.46,891300000 +1999-11-10,1365.28,1379.18,1359.98,1373.46,984700000 +1999-11-09,1377.01,1383.81,1361.45,1365.28,854300000 +1999-11-08,1370.23,1380.78,1365.87,1377.01,806800000 +1999-11-05,1362.64,1387.48,1362.64,1370.23,1007300000 +1999-11-04,1354.93,1369.41,1354.93,1362.64,981700000 +1999-11-03,1347.74,1360.33,1347.74,1354.93,914400000 +1999-11-02,1354.12,1369.32,1346.41,1347.74,904500000 +1999-11-01,1362.93,1367.3,1354.05,1354.12,861000000 +1999-10-29,1342.44,1373.17,1342.44,1362.93,1120500000 +1999-10-28,1296.71,1342.47,1296.71,1342.44,1135100000 +1999-10-27,1281.91,1299.39,1280.48,1296.71,950100000 +1999-10-26,1293.63,1303.46,1281.86,1281.91,878300000 +1999-10-25,1301.65,1301.68,1286.07,1293.63,777000000 +1999-10-22,1283.61,1308.81,1283.61,1301.65,959200000 +1999-10-21,1289.43,1289.43,1265.61,1283.61,1012500000 +1999-10-20,1261.32,1289.44,1261.32,1289.43,928800000 +1999-10-19,1254.13,1279.32,1254.13,1261.32,905700000 +1999-10-18,1247.41,1254.13,1233.7,1254.13,818700000 +1999-10-15,1283.42,1283.42,1245.39,1247.41,912600000 +1999-10-14,1285.55,1289.63,1267.62,1283.42,892300000 +1999-10-13,1313.04,1313.04,1282.8,1285.55,821500000 +1999-10-12,1335.21,1335.21,1311.8,1313.04,778300000 +1999-10-11,1336.02,1339.23,1332.96,1335.21,655900000 +1999-10-08,1317.64,1336.61,1311.88,1336.02,897300000 +1999-10-07,1325.4,1328.05,1314.13,1317.64,827800000 +1999-10-06,1301.35,1325.46,1301.35,1325.4,895200000 +1999-10-05,1304.6,1316.41,1286.44,1301.35,965700000 +1999-10-04,1282.81,1304.6,1282.81,1304.6,803300000 +1999-10-01,1282.71,1283.17,1265.78,1282.81,896200000 +1999-09-30,1268.37,1291.31,1268.37,1282.71,1017600000 +1999-09-29,1282.2,1288.83,1268.16,1268.37,856000000 +1999-09-28,1283.31,1285.55,1256.26,1282.2,885400000 +1999-09-27,1277.36,1295.03,1277.36,1283.31,780600000 +1999-09-24,1280.41,1281.17,1263.84,1277.36,872800000 +1999-09-23,1310.51,1315.25,1277.3,1280.41,890800000 +1999-09-22,1307.58,1316.18,1297.81,1310.51,822200000 +1999-09-21,1335.52,1335.53,1301.97,1307.58,817300000 +1999-09-20,1335.42,1338.38,1330.61,1335.53,568000000 +1999-09-17,1318.48,1337.59,1318.48,1335.42,861900000 +1999-09-16,1317.97,1322.51,1299.97,1318.48,739000000 +1999-09-15,1336.29,1347.21,1317.97,1317.97,787300000 +1999-09-14,1344.13,1344.18,1330.61,1336.29,734500000 +1999-09-13,1351.66,1351.66,1341.7,1344.13,657900000 +1999-09-10,1347.66,1357.62,1346.2,1351.66,808500000 +1999-09-09,1344.15,1347.66,1333.91,1347.66,773900000 +1999-09-08,1350.45,1355.18,1337.36,1344.15,791200000 +1999-09-07,1357.24,1361.39,1349.59,1350.45,715300000 +1999-09-03,1319.11,1357.74,1319.11,1357.24,663200000 +1999-09-02,1331.07,1331.07,1304.88,1319.11,687100000 +1999-09-01,1320.41,1331.18,1320.39,1331.07,708200000 +1999-08-31,1324.02,1333.27,1306.96,1320.41,861700000 +1999-08-30,1348.27,1350.7,1322.8,1324.02,597900000 +1999-08-27,1362.01,1365.63,1347.35,1348.27,570050000 +1999-08-26,1381.79,1381.79,1361.53,1362.01,719000000 +1999-08-25,1363.5,1382.84,1359.2,1381.79,864600000 +1999-08-24,1360.22,1373.32,1353.63,1363.5,732700000 +1999-08-23,1336.61,1360.24,1336.61,1360.22,682600000 +1999-08-20,1323.59,1336.61,1323.59,1336.61,661200000 +1999-08-19,1332.84,1332.84,1315.35,1323.59,684200000 +1999-08-18,1344.16,1344.16,1332.13,1332.84,682800000 +1999-08-17,1330.77,1344.16,1328.76,1344.16,691500000 +1999-08-16,1327.68,1331.05,1320.51,1330.77,583550000 +1999-08-13,1298.16,1327.72,1298.16,1327.68,691700000 +1999-08-12,1301.93,1313.61,1298.06,1298.16,745600000 +1999-08-11,1281.43,1301.93,1281.43,1301.93,792300000 +1999-08-10,1297.8,1298.62,1267.73,1281.43,836200000 +1999-08-09,1300.29,1306.68,1295.99,1297.8,684300000 +1999-08-06,1313.71,1316.74,1293.19,1300.29,698900000 +1999-08-05,1305.33,1313.71,1287.23,1313.71,859300000 +1999-08-04,1322.18,1330.16,1304.5,1305.33,789300000 +1999-08-03,1328.05,1336.13,1314.91,1322.18,739600000 +1999-08-02,1328.72,1344.69,1325.21,1328.05,649550000 +1999-07-30,1341.03,1350.92,1328.49,1328.72,736800000 +1999-07-29,1365.4,1365.4,1332.82,1341.03,770100000 +1999-07-28,1362.84,1370.53,1355.54,1365.4,690900000 +1999-07-27,1347.75,1368.7,1347.75,1362.84,723800000 +1999-07-26,1356.94,1358.61,1346.2,1347.76,613450000 +1999-07-23,1360.97,1367.41,1349.91,1356.94,630580000 +1999-07-22,1379.29,1379.29,1353.98,1360.97,771700000 +1999-07-21,1377.1,1386.66,1372.63,1379.29,785500000 +1999-07-20,1407.65,1407.65,1375.15,1377.1,754800000 +1999-07-19,1418.78,1420.33,1404.56,1407.65,642330000 +1999-07-16,1409.62,1418.78,1407.07,1418.78,714100000 +1999-07-15,1398.17,1409.84,1398.17,1409.62,818800000 +1999-07-14,1393.56,1400.05,1386.51,1398.17,756100000 +1999-07-13,1399.1,1399.1,1386.84,1393.56,736000000 +1999-07-12,1403.28,1406.82,1394.7,1399.1,685300000 +1999-07-09,1394.42,1403.28,1394.42,1403.28,701000000 +1999-07-08,1395.86,1403.25,1386.69,1394.42,830600000 +1999-07-07,1388.12,1395.88,1384.95,1395.86,791200000 +1999-07-06,1391.22,1405.29,1387.08,1388.12,722900000 +1999-07-02,1380.96,1391.22,1379.57,1391.22,613570000 +1999-07-01,1372.71,1382.8,1360.8,1380.96,843400000 +1999-06-30,1351.45,1372.93,1338.78,1372.71,1117000000 +1999-06-29,1331.35,1351.51,1328.4,1351.45,820100000 +1999-06-28,1315.31,1333.68,1315.31,1331.35,652910000 +1999-06-25,1315.78,1329.13,1312.64,1315.31,623460000 +1999-06-24,1333.06,1333.06,1308.47,1315.78,690400000 +1999-06-23,1335.87,1335.88,1322.55,1333.06,731800000 +1999-06-22,1349,1351.12,1335.52,1335.88,716500000 +1999-06-21,1342.84,1349.06,1337.63,1349,686600000 +1999-06-18,1339.9,1344.48,1333.52,1342.84,914500000 +1999-06-17,1330.41,1343.54,1322.75,1339.9,700300000 +1999-06-16,1301.16,1332.83,1301.16,1330.41,806800000 +1999-06-15,1294,1310.76,1294,1301.16,696600000 +1999-06-14,1293.64,1301.99,1292.2,1294,669400000 +1999-06-11,1302.82,1311.97,1287.88,1293.64,698200000 +1999-06-10,1318.64,1318.64,1293.28,1302.82,716500000 +1999-06-09,1317.33,1326.01,1314.73,1318.64,662000000 +1999-06-08,1334.52,1334.52,1312.83,1317.33,685900000 +1999-06-07,1327.75,1336.42,1325.89,1334.52,664300000 +1999-06-04,1299.54,1327.75,1299.54,1327.75,694500000 +1999-06-03,1294.81,1304.15,1294.2,1299.54,719600000 +1999-06-02,1294.26,1297.1,1277.47,1294.81,728000000 +1999-06-01,1301.84,1301.84,1281.44,1294.26,683800000 +1999-05-28,1281.41,1304,1281.41,1301.84,649960000 +1999-05-27,1304.76,1304.76,1277.31,1281.41,811400000 +1999-05-26,1284.4,1304.85,1278.43,1304.76,870800000 +1999-05-25,1306.65,1317.52,1284.38,1284.4,826700000 +1999-05-24,1330.29,1333.02,1303.53,1306.65,754700000 +1999-05-21,1338.83,1340.88,1326.19,1330.29,686600000 +1999-05-20,1344.23,1350.49,1338.83,1338.83,752200000 +1999-05-19,1333.32,1344.23,1327.05,1344.23,801100000 +1999-05-18,1339.49,1345.44,1323.46,1333.32,753400000 +1999-05-17,1337.8,1339.95,1321.19,1339.49,665500000 +1999-05-14,1367.56,1367.56,1332.63,1337.8,727800000 +1999-05-13,1364,1375.98,1364,1367.56,796900000 +1999-05-12,1355.61,1367.36,1333.1,1364,825500000 +1999-05-11,1340.3,1360,1340.3,1355.61,836100000 +1999-05-10,1345,1352.01,1334,1340.3,773300000 +1999-05-07,1332.05,1345.99,1332.05,1345,814900000 +1999-05-06,1347.31,1348.36,1322.56,1332.05,875400000 +1999-05-05,1332,1347.32,1317.44,1347.31,913500000 +1999-05-04,1354.63,1354.64,1330.64,1332,933100000 +1999-05-03,1335.18,1354.63,1329.01,1354.63,811400000 +1999-04-30,1342.83,1351.83,1314.58,1335.18,936500000 +1999-04-29,1350.91,1356.75,1336.81,1342.83,1003600000 +1999-04-28,1362.8,1368.62,1348.29,1350.91,951700000 +1999-04-27,1360.04,1371.56,1356.55,1362.8,891700000 +1999-04-26,1356.85,1363.56,1353.72,1360.04,712000000 +1999-04-23,1358.83,1363.65,1348.45,1356.85,744900000 +1999-04-22,1336.12,1358.84,1336.12,1358.82,927900000 +1999-04-21,1306.17,1336.12,1301.84,1336.12,920000000 +1999-04-20,1289.48,1306.3,1284.21,1306.17,985400000 +1999-04-19,1319,1340.1,1284.48,1289.48,1214400000 +1999-04-16,1322.86,1325.03,1311.4,1319,1002300000 +1999-04-15,1328.44,1332.41,1308.38,1322.85,1089800000 +1999-04-14,1349.82,1357.24,1326.41,1328.44,952000000 +1999-04-13,1358.64,1362.38,1344.03,1349.82,810900000 +1999-04-12,1348.35,1358.69,1333.48,1358.63,810800000 +1999-04-09,1343.98,1351.22,1335.24,1348.35,716100000 +1999-04-08,1326.89,1344.08,1321.6,1343.98,850500000 +1999-04-07,1317.89,1329.58,1312.59,1326.89,816400000 +1999-04-06,1321.12,1326.76,1311.07,1317.89,787500000 +1999-04-05,1293.72,1321.12,1293.72,1321.12,695800000 +1999-04-01,1286.37,1294.54,1282.56,1293.72,703000000 +1999-03-31,1300.75,1313.6,1285.87,1286.37,924300000 +1999-03-30,1310.17,1310.17,1295.47,1300.75,729000000 +1999-03-29,1282.8,1311.76,1282.8,1310.17,747900000 +1999-03-26,1289.99,1289.99,1277.25,1282.8,707200000 +1999-03-25,1268.59,1289.99,1268.59,1289.99,784200000 +1999-03-24,1262.14,1269.02,1256.43,1268.59,761900000 +1999-03-23,1297.01,1297.01,1257.46,1262.14,811300000 +1999-03-22,1299.29,1303.84,1294.26,1297.01,658200000 +1999-03-19,1316.55,1323.82,1298.92,1299.29,914700000 +1999-03-18,1297.82,1317.62,1294.75,1316.55,831000000 +1999-03-17,1306.38,1306.55,1292.63,1297.82,752300000 +1999-03-16,1307.26,1311.11,1302.29,1306.38,751900000 +1999-03-15,1294.59,1307.47,1291.03,1307.26,727200000 +1999-03-12,1297.68,1304.42,1289.17,1294.59,825800000 +1999-03-11,1286.84,1306.43,1286.84,1297.68,904800000 +1999-03-10,1279.84,1287.02,1275.16,1286.84,841900000 +1999-03-09,1282.73,1293.74,1275.11,1279.84,803700000 +1999-03-08,1275.47,1282.74,1271.58,1282.73,714600000 +1999-03-05,1246.64,1275.73,1246.64,1275.47,834900000 +1999-03-04,1227.7,1247.74,1227.7,1246.64,770900000 +1999-03-03,1225.5,1231.63,1216.03,1227.7,751700000 +1999-03-02,1236.16,1248.31,1221.87,1225.5,753600000 +1999-03-01,1238.33,1238.7,1221.88,1236.16,699500000 +1999-02-26,1245.02,1246.73,1226.24,1238.33,784600000 +1999-02-25,1253.41,1253.41,1225.01,1245.02,740500000 +1999-02-24,1271.18,1283.84,1251.94,1253.41,782000000 +1999-02-23,1272.14,1280.38,1263.36,1271.18,781100000 +1999-02-22,1239.22,1272.22,1239.22,1272.14,718500000 +1999-02-19,1237.28,1247.91,1232.03,1239.22,700000000 +1999-02-18,1224.03,1239.13,1220.7,1237.28,742400000 +1999-02-17,1241.87,1249.31,1220.92,1224.03,735100000 +1999-02-16,1230.13,1252.17,1230.13,1241.87,653760000 +1999-02-12,1254.04,1254.04,1225.53,1230.13,691500000 +1999-02-11,1223.55,1254.05,1223.19,1254.04,815800000 +1999-02-10,1216.14,1226.78,1211.89,1223.55,721400000 +1999-02-09,1243.77,1243.97,1215.63,1216.14,736000000 +1999-02-08,1239.4,1246.93,1231.98,1243.77,705400000 +1999-02-05,1248.49,1251.86,1232.28,1239.4,872000000 +1999-02-04,1272.07,1272.23,1248.36,1248.49,854400000 +1999-02-03,1261.99,1276.04,1255.27,1272.07,876500000 +1999-02-02,1273,1273.49,1247.56,1261.99,845500000 +1999-02-01,1279.64,1283.75,1271.31,1273,799400000 +1999-01-29,1265.37,1280.37,1255.18,1279.64,917000000 +1999-01-28,1243.17,1266.4,1243.17,1265.37,848800000 +1999-01-27,1252.31,1262.61,1242.82,1243.17,893800000 +1999-01-26,1233.98,1253.25,1233.98,1252.31,896400000 +1999-01-25,1225.19,1233.98,1219.46,1233.98,723900000 +1999-01-22,1235.16,1236.41,1217.97,1225.19,785900000 +1999-01-21,1256.62,1256.94,1232.19,1235.16,871800000 +1999-01-20,1252,1274.07,1251.54,1256.62,905700000 +1999-01-19,1243.26,1253.27,1234.91,1252,785500000 +1999-01-15,1212.19,1243.26,1212.19,1243.26,798100000 +1999-01-14,1234.4,1236.81,1209.54,1212.19,797200000 +1999-01-13,1239.51,1247.75,1205.46,1234.4,931500000 +1999-01-12,1263.88,1264.45,1238.29,1239.51,800200000 +1999-01-11,1275.09,1276.22,1253.34,1263.88,818000000 +1999-01-08,1269.73,1278.24,1261.82,1275.09,937800000 +1999-01-07,1272.34,1272.34,1257.68,1269.73,863000000 +1999-01-06,1244.78,1272.5,1244.78,1272.34,986900000 +1999-01-05,1228.1,1246.11,1228.1,1244.78,775000000 +1999-01-04,1229.23,1248.81,1219.1,1228.1,877000000 +1998-12-31,1231.93,1237.18,1224.96,1229.23,719200000 +1998-12-30,1241.81,1244.93,1231.2,1231.93,594220000 +1998-12-29,1225.49,1241.86,1220.78,1241.81,586490000 +1998-12-28,1226.27,1231.52,1221.17,1225.49,531560000 +1998-12-24,1228.54,1229.72,1224.85,1226.27,246980000 +1998-12-23,1203.57,1229.89,1203.57,1228.54,697500000 +1998-12-22,1202.84,1209.22,1192.72,1203.57,680500000 +1998-12-21,1188.03,1210.88,1188.03,1202.84,744800000 +1998-12-18,1179.98,1188.89,1178.27,1188.03,839600000 +1998-12-17,1161.94,1180.03,1161.94,1179.98,739400000 +1998-12-16,1162.83,1166.29,1154.69,1161.94,725500000 +1998-12-15,1141.2,1162.83,1141.2,1162.83,777900000 +1998-12-14,1166.46,1166.46,1136.89,1141.2,741800000 +1998-12-11,1165.02,1167.89,1153.19,1166.46,688900000 +1998-12-10,1183.49,1183.77,1163.75,1165.02,748600000 +1998-12-09,1181.38,1185.22,1175.89,1183.49,694200000 +1998-12-08,1187.7,1193.53,1172.78,1181.38,727700000 +1998-12-07,1176.74,1188.96,1176.71,1187.7,671200000 +1998-12-04,1150.14,1176.74,1150.14,1176.74,709700000 +1998-12-03,1171.25,1176.99,1149.61,1150.14,799100000 +1998-12-02,1175.28,1175.28,1157.76,1171.25,727400000 +1998-12-01,1163.63,1175.89,1150.31,1175.28,789200000 +1998-11-30,1192.33,1192.72,1163.63,1163.63,687900000 +1998-11-27,1186.87,1192.97,1186.83,1192.33,256950000 +1998-11-25,1182.99,1187.16,1179.37,1186.87,583580000 +1998-11-24,1188.21,1191.3,1181.81,1182.99,766200000 +1998-11-23,1163.55,1188.21,1163.55,1188.21,774100000 +1998-11-20,1152.61,1163.55,1152.61,1163.55,721200000 +1998-11-19,1144.48,1155.1,1144.42,1152.61,671000000 +1998-11-18,1139.32,1144.52,1133.07,1144.48,652510000 +1998-11-17,1135.87,1151.71,1129.67,1139.32,705200000 +1998-11-16,1125.72,1138.72,1125.72,1135.87,615580000 +1998-11-13,1117.69,1126.34,1116.76,1125.72,602270000 +1998-11-12,1120.97,1126.57,1115.55,1117.69,662300000 +1998-11-11,1128.26,1136.25,1117.4,1120.97,715700000 +1998-11-10,1130.2,1135.37,1122.8,1128.26,671300000 +1998-11-09,1141.01,1141.01,1123.17,1130.2,592990000 +1998-11-06,1133.85,1141.3,1131.18,1141.01,683100000 +1998-11-05,1118.67,1133.88,1109.55,1133.85,770200000 +1998-11-04,1110.84,1127.18,1110.59,1118.67,861100000 +1998-11-03,1111.6,1115.02,1106.42,1110.84,704300000 +1998-11-02,1098.67,1114.44,1098.67,1111.6,753800000 +1998-10-30,1085.93,1103.78,1085.93,1098.67,785000000 +1998-10-29,1068.09,1086.11,1065.95,1085.93,699400000 +1998-10-28,1065.34,1072.79,1059.65,1068.09,677500000 +1998-10-27,1072.32,1087.08,1063.06,1065.34,764500000 +1998-10-26,1070.67,1081.23,1068.17,1072.32,609910000 +1998-10-23,1078.48,1078.48,1067.43,1070.67,637640000 +1998-10-22,1069.92,1080.43,1061.47,1078.48,754900000 +1998-10-21,1063.93,1073.61,1058.08,1069.92,745100000 +1998-10-20,1062.39,1084.06,1060.61,1063.93,958200000 +1998-10-19,1056.42,1065.21,1054.23,1062.39,738600000 +1998-10-16,1047.49,1062.65,1047.49,1056.42,1042200000 +1998-10-15,1005.53,1053.09,1000.12,1047.49,937600000 +1998-10-14,994.8,1014.42,987.8,1005.53,791200000 +1998-10-13,997.71,1000.78,987.55,994.8,733300000 +1998-10-12,984.39,1010.71,984.39,997.71,691100000 +1998-10-09,959.44,984.42,953.04,984.39,878100000 +1998-10-08,970.68,970.68,923.32,959.44,1114600000 +1998-10-07,984.59,995.66,957.15,970.68,977000000 +1998-10-06,988.56,1008.77,974.81,984.59,845700000 +1998-10-05,1002.6,1002.6,964.72,988.56,817500000 +1998-10-02,986.39,1005.45,971.69,1002.6,902900000 +1998-10-01,1017.01,1017.01,981.29,986.39,899700000 +1998-09-30,1049.02,1049.02,1015.73,1017.01,800100000 +1998-09-29,1048.69,1056.31,1039.88,1049.02,760100000 +1998-09-28,1044.75,1061.46,1042.23,1048.69,690500000 +1998-09-25,1042.72,1051.89,1028.49,1044.75,736800000 +1998-09-24,1066.09,1066.11,1033.04,1042.72,805900000 +1998-09-23,1029.63,1066.09,1029.63,1066.09,899700000 +1998-09-22,1023.89,1033.89,1021.96,1029.63,694900000 +1998-09-21,1020.09,1026.02,993.82,1023.89,609880000 +1998-09-18,1018.87,1022.01,1011.86,1020.09,794700000 +1998-09-17,1045.48,1045.48,1016.05,1018.87,694500000 +1998-09-16,1037.68,1046.07,1029.31,1045.48,797500000 +1998-09-15,1029.72,1037.9,1021.42,1037.68,724600000 +1998-09-14,1009.06,1038.38,1009.06,1029.72,714400000 +1998-09-11,980.19,1009.06,969.71,1009.06,819100000 +1998-09-10,1006.2,1006.2,968.64,980.19,880300000 +1998-09-09,1023.46,1027.72,1004.56,1006.2,704300000 +1998-09-08,973.89,1023.46,973.89,1023.46,814800000 +1998-09-04,982.26,991.41,956.51,973.89,780300000 +1998-09-03,990.47,990.47,969.32,982.26,880500000 +1998-09-02,994.26,1013.19,988.4,990.48,894600000 +1998-09-01,957.28,1000.71,939.98,994.26,1216600000 +1998-08-31,1027.14,1033.47,957.28,957.28,917500000 +1998-08-28,1042.59,1051.8,1021.04,1027.14,840300000 +1998-08-27,1084.19,1084.19,1037.61,1042.59,938600000 +1998-08-26,1092.85,1092.85,1075.91,1084.19,674100000 +1998-08-25,1088.14,1106.64,1085.53,1092.85,664900000 +1998-08-24,1081.24,1093.82,1081.24,1088.14,558100000 +1998-08-21,1091.6,1091.6,1054.92,1081.24,725700000 +1998-08-20,1098.06,1098.79,1089.55,1091.6,621630000 +1998-08-19,1101.2,1106.32,1094.93,1098.06,633630000 +1998-08-18,1083.67,1101.72,1083.67,1101.2,690600000 +1998-08-17,1062.75,1083.67,1055.08,1083.67,584380000 +1998-08-14,1074.91,1083.92,1057.22,1062.75,644030000 +1998-08-13,1084.22,1091.5,1074.91,1074.91,660700000 +1998-08-12,1068.98,1084.7,1068.98,1084.22,711700000 +1998-08-11,1083.14,1083.14,1054,1068.98,774400000 +1998-08-10,1089.45,1092.82,1081.76,1083.14,579180000 +1998-08-07,1089.63,1102.54,1084.72,1089.45,759100000 +1998-08-06,1081.43,1090.95,1074.94,1089.63,768400000 +1998-08-05,1072.12,1084.8,1057.35,1081.43,851600000 +1998-08-04,1112.44,1119.73,1071.82,1072.12,852600000 +1998-08-03,1120.67,1121.79,1110.39,1112.44,620400000 +1998-07-31,1142.95,1142.97,1114.3,1120.67,645910000 +1998-07-30,1125.21,1143.07,1125.21,1142.95,687400000 +1998-07-29,1130.24,1138.56,1121.98,1125.21,644350000 +1998-07-28,1147.27,1147.27,1119.44,1130.24,703600000 +1998-07-27,1140.8,1147.27,1128.19,1147.27,619990000 +1998-07-24,1139.75,1150.14,1129.11,1140.8,698600000 +1998-07-23,1164.08,1164.35,1139.75,1139.75,741600000 +1998-07-22,1165.07,1167.67,1155.2,1164.08,739800000 +1998-07-21,1184.1,1187.37,1163.05,1165.07,659700000 +1998-07-20,1186.75,1190.58,1179.19,1184.1,560580000 +1998-07-17,1183.99,1188.1,1182.42,1186.75,618030000 +1998-07-16,1174.81,1184.02,1170.4,1183.99,677800000 +1998-07-15,1177.58,1181.48,1174.73,1174.81,723900000 +1998-07-14,1165.19,1179.76,1165.19,1177.58,700300000 +1998-07-13,1164.33,1166.98,1160.21,1165.19,574880000 +1998-07-10,1158.57,1166.93,1150.88,1164.33,576080000 +1998-07-09,1166.38,1166.38,1156.03,1158.56,663600000 +1998-07-08,1154.66,1166.89,1154.66,1166.38,607230000 +1998-07-07,1157.33,1159.81,1152.85,1154.66,624890000 +1998-07-06,1146.42,1157.33,1145.03,1157.33,514750000 +1998-07-02,1148.56,1148.56,1142.99,1146.42,510210000 +1998-07-01,1133.84,1148.56,1133.84,1148.56,701600000 +1998-06-30,1138.49,1140.8,1131.98,1133.84,757200000 +1998-06-29,1133.2,1145.15,1133.2,1138.49,564350000 +1998-06-26,1129.28,1136.83,1129.28,1133.2,520050000 +1998-06-25,1132.88,1142.04,1127.6,1129.28,669900000 +1998-06-24,1119.49,1134.4,1115.1,1132.88,714900000 +1998-06-23,1103.21,1119.49,1103.21,1119.49,657100000 +1998-06-22,1100.65,1109.01,1099.42,1103.21,531550000 +1998-06-19,1106.37,1111.25,1097.1,1100.65,715500000 +1998-06-18,1107.11,1109.36,1103.71,1106.37,590440000 +1998-06-17,1087.59,1112.87,1087.58,1107.11,744400000 +1998-06-16,1077.01,1087.59,1074.67,1087.59,664600000 +1998-06-15,1098.84,1098.84,1077.01,1077.01,595820000 +1998-06-12,1094.58,1098.84,1080.83,1098.84,633300000 +1998-06-11,1112.28,1114.2,1094.28,1094.58,627470000 +1998-06-10,1118.41,1126,1110.27,1112.28,609410000 +1998-06-09,1115.72,1119.92,1111.31,1118.41,563610000 +1998-06-08,1113.86,1119.7,1113.31,1115.72,543390000 +1998-06-05,1095.1,1113.88,1094.83,1113.86,558440000 +1998-06-04,1082.73,1095.93,1078.1,1094.83,577470000 +1998-06-03,1093.22,1097.43,1081.09,1082.73,584480000 +1998-06-02,1090.98,1098.71,1089.67,1093.22,590930000 +1998-06-01,1090.82,1097.85,1084.22,1090.98,537660000 +1998-05-29,1097.6,1104.16,1090.82,1090.82,556780000 +1998-05-28,1092.23,1099.73,1089.06,1097.6,588900000 +1998-05-27,1094.02,1094.44,1074.39,1092.23,682040000 +1998-05-26,1110.47,1116.79,1094.01,1094.02,541410000 +1998-05-22,1114.64,1116.89,1107.99,1110.47,444070000 +1998-05-21,1119.06,1124.45,1111.94,1114.64,551970000 +1998-05-20,1109.52,1119.08,1107.51,1119.06,587240000 +1998-05-19,1105.82,1113.5,1105.82,1109.52,566020000 +1998-05-18,1108.73,1112.44,1097.99,1105.82,519100000 +1998-05-15,1117.37,1118.66,1107.11,1108.73,621990000 +1998-05-14,1118.86,1124.03,1112.43,1117.37,578380000 +1998-05-13,1115.79,1122.22,1114.93,1118.86,600010000 +1998-05-12,1106.64,1115.96,1102.78,1115.79,604420000 +1998-05-11,1108.14,1119.13,1103.72,1106.64,560840000 +1998-05-08,1095.14,1111.42,1094.53,1108.14,567890000 +1998-05-07,1104.92,1105.58,1094.59,1095.14,582240000 +1998-05-06,1115.5,1118.39,1104.64,1104.92,606540000 +1998-05-05,1122.07,1122.07,1111.16,1115.5,583630000 +1998-05-04,1121,1130.52,1121,1122.07,551700000 +1998-05-01,1111.75,1121.02,1111.75,1121,581970000 +1998-04-30,1094.63,1116.97,1094.63,1111.75,695600000 +1998-04-29,1085.11,1098.24,1084.65,1094.62,638790000 +1998-04-28,1086.54,1095.94,1081.49,1085.11,678600000 +1998-04-27,1107.9,1107.9,1076.7,1086.54,685960000 +1998-04-24,1119.58,1122.81,1104.77,1107.9,633890000 +1998-04-23,1130.54,1130.54,1117.49,1119.58,653190000 +1998-04-22,1126.67,1132.98,1126.29,1130.54,696740000 +1998-04-21,1123.65,1129.65,1119.54,1126.67,675640000 +1998-04-20,1122.72,1124.88,1118.43,1123.65,595190000 +1998-04-17,1108.17,1122.72,1104.95,1122.72,672290000 +1998-04-16,1119.32,1119.32,1105.27,1108.17,699570000 +1998-04-15,1115.75,1119.9,1112.24,1119.32,685020000 +1998-04-14,1109.69,1115.95,1109.48,1115.75,613730000 +1998-04-13,1110.67,1110.75,1100.6,1109.69,564480000 +1998-04-09,1101.65,1111.45,1101.65,1110.67,548940000 +1998-04-08,1109.55,1111.6,1098.21,1101.65,616330000 +1998-04-07,1121.38,1121.38,1102.44,1109.55,670760000 +1998-04-06,1122.7,1131.99,1121.37,1121.38,625810000 +1998-04-03,1120.01,1126.36,1118.12,1122.7,653880000 +1998-04-02,1108.15,1121.01,1107.89,1120.01,674340000 +1998-04-01,1101.75,1109.19,1095.29,1108.15,677310000 +1998-03-31,1093.55,1110.13,1093.55,1101.75,674930000 +1998-03-30,1095.44,1099.1,1090.02,1093.6,497400000 +1998-03-27,1100.8,1107.18,1091.14,1095.44,582190000 +1998-03-26,1101.93,1106.28,1097,1100.8,606770000 +1998-03-25,1105.65,1113.07,1092.84,1101.93,676550000 +1998-03-24,1095.55,1106.75,1095.55,1105.65,605720000 +1998-03-23,1099.16,1101.16,1094.25,1095.55,631350000 +1998-03-20,1089.74,1101.04,1089.39,1099.16,717310000 +1998-03-19,1085.52,1089.74,1084.3,1089.74,598240000 +1998-03-18,1080.45,1085.52,1077.77,1085.52,632690000 +1998-03-17,1079.27,1080.52,1073.29,1080.45,680960000 +1998-03-16,1068.61,1079.46,1068.61,1079.27,548980000 +1998-03-13,1069.92,1075.86,1066.57,1068.61,597800000 +1998-03-12,1068.47,1071.87,1063.54,1069.92,594940000 +1998-03-11,1064.25,1069.18,1064.22,1068.47,655260000 +1998-03-10,1052.31,1064.59,1052.31,1064.25,631920000 +1998-03-09,1055.69,1058.55,1050.02,1052.31,624700000 +1998-03-06,1035.05,1055.69,1035.05,1055.69,665500000 +1998-03-05,1047.33,1047.33,1030.87,1035.05,648270000 +1998-03-04,1052.02,1052.02,1042.74,1047.33,644280000 +1998-03-03,1047.7,1052.02,1043.41,1052.02,612360000 +1998-03-02,1049.34,1053.98,1044.7,1047.7,591470000 +1998-02-27,1048.67,1051.66,1044.4,1049.34,574480000 +1998-02-26,1042.9,1048.68,1039.85,1048.67,646280000 +1998-02-25,1030.56,1045.79,1030.56,1042.9,611350000 +1998-02-24,1038.14,1038.73,1028.89,1030.56,589880000 +1998-02-23,1034.21,1038.68,1031.76,1038.14,550730000 +1998-02-20,1028.28,1034.21,1022.69,1034.21,594300000 +1998-02-19,1032.08,1032.93,1026.62,1028.28,581820000 +1998-02-18,1022.76,1032.08,1021.7,1032.08,606000000 +1998-02-17,1020.09,1028.02,1020.09,1022.76,605890000 +1998-02-13,1024.14,1024.14,1017.71,1020.09,531940000 +1998-02-12,1020.01,1026.3,1008.55,1024.14,611480000 +1998-02-11,1019.01,1020.71,1016.38,1020.01,599300000 +1998-02-10,1010.74,1022.15,1010.71,1019.01,642800000 +1998-02-09,1012.46,1015.33,1006.28,1010.74,524810000 +1998-02-06,1003.54,1013.07,1003.36,1012.46,569650000 +1998-02-05,1006.9,1013.51,1000.27,1003.54,703980000 +1998-02-04,1006,1009.52,999.43,1006.9,695420000 +1998-02-03,1001.27,1006.13,996.9,1006,692120000 +1998-02-02,980.28,1002.48,980.28,1001.27,724320000 +1998-01-30,985.49,987.41,979.63,980.28,613380000 +1998-01-29,977.46,992.65,975.21,985.49,750760000 +1998-01-28,969.02,978.63,969.02,977.46,708470000 +1998-01-27,956.95,973.23,956.26,969.02,679140000 +1998-01-26,957.59,963.04,954.24,956.95,555080000 +1998-01-23,963.04,966.44,950.86,957.59,635770000 +1998-01-22,970.81,970.81,959.49,963.04,646570000 +1998-01-21,978.6,978.6,963.29,970.81,626160000 +1998-01-20,961.51,978.6,961.48,978.6,644790000 +1998-01-16,950.73,965.12,950.73,961.51,670080000 +1998-01-15,957.94,957.94,950.27,950.73,569050000 +1998-01-14,952.12,958.12,948,957.94,603280000 +1998-01-13,939.21,952.14,939.21,952.12,646740000 +1998-01-12,927.69,939.25,912.83,939.21,705450000 +1998-01-09,956.05,956.05,921.72,927.69,746420000 +1998-01-08,964,964,955.04,956.05,652140000 +1998-01-07,966.58,966.58,952.67,964,667390000 +1998-01-06,977.07,977.07,962.68,966.58,618360000 +1998-01-05,975.04,982.63,969,977.07,628070000 +1998-01-02,970.43,975.04,965.73,975.04,366730000 +1997-12-31,970.84,975.02,967.41,970.43,467280000 +1997-12-30,953.35,970.84,953.35,970.84,499500000 +1997-12-29,936.46,953.95,936.46,953.35,443160000 +1997-12-26,932.7,939.99,932.7,936.46,154900000 +1997-12-24,939.13,942.88,932.7,932.7,265980000 +1997-12-23,953.7,954.51,938.91,939.13,515070000 +1997-12-22,946.78,956.73,946.25,953.7,530670000 +1997-12-19,955.3,955.3,924.92,946.78,793200000 +1997-12-18,965.54,965.54,950.55,955.3,618870000 +1997-12-17,968.04,974.3,964.25,965.54,618900000 +1997-12-16,963.39,973,963.39,968.04,623320000 +1997-12-15,953.39,965.96,953.39,963.39,597150000 +1997-12-12,954.94,961.32,947,953.39,579280000 +1997-12-11,969.79,969.79,951.89,954.94,631770000 +1997-12-10,975.78,975.78,962.68,969.79,602290000 +1997-12-09,982.37,982.37,973.81,975.78,539130000 +1997-12-08,983.79,985.67,979.57,982.37,490320000 +1997-12-05,973.1,986.25,969.1,983.79,563590000 +1997-12-04,976.77,983.36,971.37,973.1,633470000 +1997-12-03,971.68,980.81,966.16,976.77,624610000 +1997-12-02,974.78,976.2,969.83,971.68,576120000 +1997-12-01,955.4,974.77,955.4,974.77,590300000 +1997-11-28,951.64,959.13,951.64,955.4,189070000 +1997-11-26,950.82,956.47,950.82,951.64,487750000 +1997-11-25,946.67,954.47,944.71,950.82,587890000 +1997-11-24,963.09,963.09,945.22,946.67,514920000 +1997-11-21,958.98,964.55,954.6,963.09,611000000 +1997-11-20,944.59,961.83,944.59,958.98,602610000 +1997-11-19,938.23,947.28,934.83,944.59,542720000 +1997-11-18,946.2,947.65,937.43,938.23,521380000 +1997-11-17,928.35,949.66,928.35,946.2,576540000 +1997-11-14,916.66,930.44,915.34,928.35,635760000 +1997-11-13,905.96,917.79,900.61,916.66,653960000 +1997-11-12,923.78,923.88,905.34,905.96,585340000 +1997-11-11,921.13,928.29,919.63,923.78,435660000 +1997-11-10,927.51,935.9,920.26,921.13,464140000 +1997-11-07,938.03,938.03,915.39,927.51,569980000 +1997-11-06,942.76,942.85,934.16,938.03,522890000 +1997-11-05,940.76,949.62,938.16,942.76,565680000 +1997-11-04,938.99,941.4,932.66,940.76,541590000 +1997-11-03,914.62,939.02,914.62,938.99,564740000 +1997-10-31,903.68,919.93,903.68,914.62,638070000 +1997-10-30,919.16,923.28,903.68,903.68,712230000 +1997-10-29,921.85,935.24,913.88,919.16,777660000 +1997-10-28,876.99,923.09,855.27,921.85,1202550000 +1997-10-27,941.64,941.64,876.73,876.99,693730000 +1997-10-24,950.69,960.04,937.55,941.64,677630000 +1997-10-23,968.49,968.49,944.16,950.69,673270000 +1997-10-22,972.28,972.61,965.66,968.49,613490000 +1997-10-21,955.61,972.56,955.61,972.28,582310000 +1997-10-20,944.16,955.72,941.43,955.61,483880000 +1997-10-17,955.23,955.23,931.58,944.16,624980000 +1997-10-16,965.72,973.38,950.77,955.25,597010000 +1997-10-15,970.28,970.28,962.75,965.72,505310000 +1997-10-14,968.1,972.86,961.87,970.28,510330000 +1997-10-13,966.98,973.46,966.95,968.1,354800000 +1997-10-10,970.62,970.62,963.42,966.98,500680000 +1997-10-09,973.84,974.72,963.34,970.62,551840000 +1997-10-08,983.12,983.12,968.65,973.84,573110000 +1997-10-07,972.69,983.12,971.95,983.12,551970000 +1997-10-06,965.03,974.16,965.03,972.69,495620000 +1997-10-03,960.46,975.47,955.13,965.03,623370000 +1997-10-02,955.41,960.46,952.94,960.46,474760000 +1997-10-01,947.28,956.71,947.28,955.41,598660000 +1997-09-30,953.34,955.17,947.28,947.28,587500000 +1997-09-29,945.22,953.96,941.94,953.34,477100000 +1997-09-26,937.91,946.44,937.91,945.22,505340000 +1997-09-25,944.48,947,937.38,937.91,524880000 +1997-09-24,951.93,959.78,944.07,944.48,639460000 +1997-09-23,955.43,955.78,948.07,951.93,522930000 +1997-09-22,950.51,960.59,950.51,955.43,490900000 +1997-09-19,947.29,952.35,943.9,950.51,631040000 +1997-09-18,943,958.19,943,947.29,566830000 +1997-09-17,945.64,950.29,941.99,943,590550000 +1997-09-16,919.77,947.66,919.77,945.64,636380000 +1997-09-15,923.91,928.9,919.41,919.77,468030000 +1997-09-12,912.59,925.05,906.7,923.91,544150000 +1997-09-11,919.03,919.03,902.56,912.59,575020000 +1997-09-10,933.62,933.62,918.76,919.03,517620000 +1997-09-09,931.2,938.9,927.28,933.62,502200000 +1997-09-08,929.05,936.5,929.05,931.2,466430000 +1997-09-05,930.87,940.37,924.05,929.05,536400000 +1997-09-04,927.86,933.36,925.59,930.87,559310000 +1997-09-03,927.58,935.9,926.87,927.86,549060000 +1997-09-02,899.47,927.58,899.47,927.58,491870000 +1997-08-29,903.67,907.28,896.82,899.47,413910000 +1997-08-28,913.7,915.9,898.65,903.67,486300000 +1997-08-27,913.02,916.23,903.83,913.7,492150000 +1997-08-26,920.16,922.47,911.72,913.02,449110000 +1997-08-25,923.55,930.93,917.29,920.16,388990000 +1997-08-22,925.05,925.05,905.42,923.54,460160000 +1997-08-21,939.35,939.47,921.35,925.05,499000000 +1997-08-20,926.01,939.35,924.58,939.35,521270000 +1997-08-19,912.49,926.01,912.49,926.01,545630000 +1997-08-18,900.81,912.57,893.34,912.49,514330000 +1997-08-15,924.77,924.77,900.81,900.81,537820000 +1997-08-14,922.02,930.07,916.92,924.77,530460000 +1997-08-13,926.53,935.77,916.54,922.02,587210000 +1997-08-12,937,942.99,925.66,926.53,499310000 +1997-08-11,933.54,938.5,925.39,937,480340000 +1997-08-08,951.19,951.19,925.74,933.54,563420000 +1997-08-07,960.32,964.17,950.87,951.19,576030000 +1997-08-06,952.37,962.43,949.45,960.32,565200000 +1997-08-05,950.3,954.21,948.92,952.37,525710000 +1997-08-04,947.14,953.18,943.6,950.3,456000000 +1997-08-01,954.29,955.35,939.04,947.14,513750000 +1997-07-31,952.29,957.73,948.89,954.31,547830000 +1997-07-30,942.29,953.98,941.98,952.29,568470000 +1997-07-29,936.45,942.96,932.56,942.29,544540000 +1997-07-28,938.79,942.97,935.19,936.45,466920000 +1997-07-25,940.3,945.65,936.09,938.79,521510000 +1997-07-24,936.56,941.51,926.91,940.3,571020000 +1997-07-23,933.98,941.8,933.98,936.56,616930000 +1997-07-22,912.94,934.38,912.94,933.98,579590000 +1997-07-21,915.3,915.38,907.12,912.94,459500000 +1997-07-18,931.61,931.61,912.9,915.3,589710000 +1997-07-17,936.59,936.96,927.9,931.61,629250000 +1997-07-16,925.76,939.32,925.76,936.59,647390000 +1997-07-15,918.38,926.15,914.52,925.76,598370000 +1997-07-14,916.68,921.78,912.02,918.38,485960000 +1997-07-11,913.78,919.74,913.11,916.68,500050000 +1997-07-10,907.54,916.54,904.31,913.78,551340000 +1997-07-09,918.75,922.03,902.48,907.54,589110000 +1997-07-08,912.2,918.76,911.56,918.75,526010000 +1997-07-07,916.92,923.26,909.69,912.2,518780000 +1997-07-03,904.03,917.82,904.03,916.92,374680000 +1997-07-02,891.03,904.05,891.03,904.03,526970000 +1997-07-01,885.14,893.88,884.54,891.03,544190000 +1997-06-30,887.3,892.62,879.82,885.14,561540000 +1997-06-27,883.68,894.7,883.68,887.3,472540000 +1997-06-26,888.99,893.21,879.32,883.68,499780000 +1997-06-25,896.34,902.09,882.24,888.99,603040000 +1997-06-24,878.62,896.75,878.62,896.34,542650000 +1997-06-23,898.7,898.7,878.43,878.62,492940000 +1997-06-20,897.99,901.77,897.77,898.7,653110000 +1997-06-19,889.06,900.09,888.99,897.99,536940000 +1997-06-18,894.42,894.42,887.03,889.06,491740000 +1997-06-17,893.9,897.6,886.19,894.42,543010000 +1997-06-16,893.27,895.17,891.21,893.9,414280000 +1997-06-13,883.48,894.69,883.48,893.27,575810000 +1997-06-12,869.57,884.34,869.01,883.46,592730000 +1997-06-11,865.27,870.66,865.15,869.57,513740000 +1997-06-10,862.91,870.05,862.18,865.27,526980000 +1997-06-09,858.01,865.14,858.01,862.91,465810000 +1997-06-06,843.43,859.24,843.36,858.01,488940000 +1997-06-05,840.11,848.89,840.11,843.43,452610000 +1997-06-04,845.48,845.55,838.82,840.11,466690000 +1997-06-03,846.36,850.56,841.51,845.48,527120000 +1997-06-02,848.28,851.34,844.61,846.36,435950000 +1997-05-30,844.08,851.87,831.87,848.28,537200000 +1997-05-29,847.21,848.96,842.61,844.08,462600000 +1997-05-28,849.71,850.95,843.21,847.21,487340000 +1997-05-27,847.03,851.53,840.96,849.71,436150000 +1997-05-23,835.66,848.49,835.66,847.03,417030000 +1997-05-22,839.35,841.91,833.86,835.66,426940000 +1997-05-21,841.66,846.87,835.22,839.35,540730000 +1997-05-20,833.27,841.96,826.41,841.66,450850000 +1997-05-19,829.75,835.92,828.87,833.27,345140000 +1997-05-16,841.88,841.88,829.15,829.75,486780000 +1997-05-15,836.04,842.45,833.34,841.88,458170000 +1997-05-14,833.13,841.29,833.13,836.04,504960000 +1997-05-13,837.66,838.49,829.12,833.13,489760000 +1997-05-12,824.78,838.56,824.78,837.66,459370000 +1997-05-09,820.26,827.69,815.78,824.78,455690000 +1997-05-08,815.62,829.09,811.84,820.26,534120000 +1997-05-07,827.76,827.76,814.7,815.62,500580000 +1997-05-06,830.24,832.29,824.7,827.76,603680000 +1997-05-05,812.97,830.29,811.8,830.29,549410000 +1997-05-02,798.53,812.99,798.53,812.97,499770000 +1997-05-01,801.34,802.95,793.21,798.53,460380000 +1997-04-30,794.05,804.13,791.21,801.34,556070000 +1997-04-29,772.96,794.44,772.96,794.05,547690000 +1997-04-28,765.37,773.89,763.3,772.96,404470000 +1997-04-25,771.18,771.18,764.63,765.37,414350000 +1997-04-24,773.64,779.89,769.72,771.18,493640000 +1997-04-23,774.61,778.19,771.9,773.64,489350000 +1997-04-22,760.37,774.64,759.9,774.61,507500000 +1997-04-21,766.34,767.39,756.38,760.37,397300000 +1997-04-18,761.77,767.93,761.77,766.34,468940000 +1997-04-17,763.53,768.55,760.49,761.77,503760000 +1997-04-16,754.72,763.53,751.99,763.53,498820000 +1997-04-15,743.73,754.72,743.73,754.72,507370000 +1997-04-14,737.65,743.73,733.54,743.73,406800000 +1997-04-11,758.34,758.34,737.64,737.65,444380000 +1997-04-10,760.6,763.73,757.65,758.34,421790000 +1997-04-09,766.12,769.53,759.15,760.6,451500000 +1997-04-08,762.13,766.25,758.36,766.12,450790000 +1997-04-07,757.9,764.82,757.9,762.13,453790000 +1997-04-04,750.32,757.9,744.04,757.9,544580000 +1997-04-03,750.11,751.04,744.4,750.32,498010000 +1997-04-02,759.64,759.65,747.59,750.11,478210000 +1997-04-01,757.12,761.49,751.26,759.64,515770000 +1997-03-31,773.88,773.88,756.13,757.12,555880000 +1997-03-27,790.5,792.58,767.32,773.88,476790000 +1997-03-26,789.07,794.89,786.77,790.5,506670000 +1997-03-25,790.89,798.11,788.39,789.07,487520000 +1997-03-24,784.1,791.01,780.79,790.89,451970000 +1997-03-21,782.65,786.44,782.65,784.1,638760000 +1997-03-20,785.77,786.29,778.04,782.65,497480000 +1997-03-19,789.66,791.59,780.03,785.77,535580000 +1997-03-18,795.71,797.18,785.47,789.66,467330000 +1997-03-17,793.17,796.28,782.98,795.71,495260000 +1997-03-14,789.56,796.88,789.56,793.17,491540000 +1997-03-13,804.26,804.26,789.44,789.56,507560000 +1997-03-12,811.34,811.34,801.07,804.26,490200000 +1997-03-11,813.65,814.9,810.77,811.34,493250000 +1997-03-10,804.97,813.66,803.66,813.65,468780000 +1997-03-07,798.56,808.19,798.56,804.97,508270000 +1997-03-06,801.99,804.11,797.5,798.56,540310000 +1997-03-05,790.95,801.99,790.95,801.99,532500000 +1997-03-04,795.31,798.93,789.98,790.95,537890000 +1997-03-03,790.82,795.31,785.66,795.31,437220000 +1997-02-28,795.07,795.7,788.5,790.82,508280000 +1997-02-27,805.68,805.68,795.06,795.07,464660000 +1997-02-26,812.1,812.7,798.13,805.68,573920000 +1997-02-25,810.28,812.85,807.65,812.03,527450000 +1997-02-24,801.77,810.64,798.42,810.28,462450000 +1997-02-21,802.8,804.94,799.99,801.77,478450000 +1997-02-20,812.49,812.49,800.35,802.8,492220000 +1997-02-19,816.29,817.68,811.2,812.49,519350000 +1997-02-18,808.48,816.29,806.34,816.29,474110000 +1997-02-14,811.82,812.2,808.15,808.48,491540000 +1997-02-13,802.77,812.93,802.77,811.82,593710000 +1997-02-12,789.59,802.77,789.59,802.77,563890000 +1997-02-11,785.43,789.6,780.95,789.59,483090000 +1997-02-10,789.56,793.46,784.69,785.43,471590000 +1997-02-07,780.15,789.72,778.19,789.56,540910000 +1997-02-06,778.28,780.35,774.45,780.15,519660000 +1997-02-05,789.26,792.71,773.43,778.28,580520000 +1997-02-04,786.73,789.28,783.68,789.26,506530000 +1997-02-03,786.16,787.14,783.12,786.73,463600000 +1997-01-31,784.17,791.86,784.17,786.16,578550000 +1997-01-30,772.5,784.17,772.5,784.17,524160000 +1997-01-29,765.02,772.7,765.02,772.5,498390000 +1997-01-28,765.02,776.32,761.75,765.02,541580000 +1997-01-27,770.52,771.43,764.18,765.02,445760000 +1997-01-24,777.56,778.21,768.17,770.52,542920000 +1997-01-23,786.23,794.67,776.64,777.56,685070000 +1997-01-22,782.72,786.23,779.56,786.23,589230000 +1997-01-21,776.7,783.72,772,782.72,571280000 +1997-01-20,776.17,780.08,774.19,776.7,440470000 +1997-01-17,769.75,776.37,769.72,776.17,534640000 +1997-01-16,767.2,772.05,765.25,769.75,537290000 +1997-01-15,768.86,770.95,763.72,767.2,524990000 +1997-01-14,759.51,772.04,759.51,768.86,531600000 +1997-01-13,759.5,762.85,756.69,759.51,445400000 +1997-01-10,754.85,759.65,746.92,759.5,545850000 +1997-01-09,748.41,757.68,748.41,754.85,555370000 +1997-01-08,753.23,755.72,747.71,748.41,557510000 +1997-01-07,747.65,753.26,742.18,753.23,538220000 +1997-01-06,748.03,753.31,743.82,747.65,531350000 +1997-01-03,737.01,748.24,737.01,748.03,452970000 +1997-01-02,740.74,742.81,729.55,737.01,463230000 +1996-12-31,753.85,753.95,740.74,740.74,399760000 +1996-12-30,756.79,759.2,752.73,753.85,339060000 +1996-12-27,755.82,758.75,754.82,756.79,253810000 +1996-12-26,751.03,757.07,751.02,755.82,254630000 +1996-12-24,746.92,751.03,746.92,751.03,165140000 +1996-12-23,748.87,750.4,743.28,746.92,343280000 +1996-12-20,745.76,755.41,745.76,748.87,654340000 +1996-12-19,731.54,746.06,731.54,745.76,526410000 +1996-12-18,726.04,732.76,726.04,731.54,500490000 +1996-12-17,720.98,727.67,716.69,726.04,519840000 +1996-12-16,728.64,732.68,719.4,720.98,447560000 +1996-12-13,729.33,731.4,721.97,728.64,458540000 +1996-12-12,740.73,744.86,729.3,729.3,492920000 +1996-12-11,747.54,747.54,732.75,740.73,494210000 +1996-12-10,749.76,753.43,747.02,747.54,446120000 +1996-12-09,739.6,749.76,739.6,749.76,381570000 +1996-12-06,744.38,744.38,726.89,739.6,500860000 +1996-12-05,745.1,747.65,742.61,744.38,483710000 +1996-12-04,748.28,748.4,738.46,745.1,498240000 +1996-12-03,756.56,761.75,747.58,748.28,516160000 +1996-12-02,757.02,757.03,751.49,756.56,412520000 +1996-11-29,755,758.27,755,757.02,14990000 +1996-11-27,755.96,757.3,753.18,755,377780000 +1996-11-26,757.03,762.12,752.83,755.96,527380000 +1996-11-25,748.73,757.05,747.99,757.03,475260000 +1996-11-22,742.75,748.73,742.75,748.73,525210000 +1996-11-21,743.95,745.2,741.08,742.75,464430000 +1996-11-20,742.16,746.99,740.4,743.95,497900000 +1996-11-19,737.02,742.18,736.87,742.16,461980000 +1996-11-18,737.62,739.24,734.39,737.02,388520000 +1996-11-15,735.88,741.92,735.15,737.62,529100000 +1996-11-14,731.13,735.99,729.2,735.88,480350000 +1996-11-13,729.56,732.11,728.03,731.13,429840000 +1996-11-12,731.87,733.04,728.2,729.56,471740000 +1996-11-11,730.82,732.6,729.94,731.87,353960000 +1996-11-08,727.65,730.82,725.22,730.82,402320000 +1996-11-07,724.59,729.49,722.23,727.65,502530000 +1996-11-06,714.14,724.6,712.83,724.59,509600000 +1996-11-05,706.73,714.56,706.73,714.14,486660000 +1996-11-04,703.77,707.02,702.84,706.73,398790000 +1996-11-01,705.27,708.6,701.3,703.77,465510000 +1996-10-31,700.9,706.61,700.35,705.27,482840000 +1996-10-30,701.5,703.44,700.05,700.9,437770000 +1996-10-29,697.26,703.25,696.22,701.5,443890000 +1996-10-28,700.92,705.4,697.25,697.26,383620000 +1996-10-25,702.29,704.11,700.53,700.92,367640000 +1996-10-24,707.27,708.25,702.11,702.29,418970000 +1996-10-23,706.57,707.31,700.98,707.27,442170000 +1996-10-22,709.85,709.85,704.55,706.57,410790000 +1996-10-21,710.82,714.1,707.71,709.85,414630000 +1996-10-18,706.99,711.04,706.11,710.82,473020000 +1996-10-17,705,708.52,704.76,706.99,478550000 +1996-10-16,702.57,704.42,699.15,704.41,441410000 +1996-10-15,703.54,708.07,699.07,702.57,458980000 +1996-10-14,700.66,705.16,700.66,703.54,322000000 +1996-10-11,694.61,700.67,694.61,700.66,396050000 +1996-10-10,696.74,696.82,693.34,694.61,394950000 +1996-10-09,700.64,702.36,694.42,696.74,408450000 +1996-10-08,703.34,705.76,699.88,700.64,435070000 +1996-10-07,701.46,704.17,701.39,703.34,380750000 +1996-10-04,692.78,701.74,692.78,701.46,463940000 +1996-10-03,694.01,694.81,691.78,692.78,386500000 +1996-10-02,689.08,694.82,689.08,694.01,440130000 +1996-10-01,687.31,689.54,684.44,689.08,421550000 +1996-09-30,686.19,690.11,686.03,687.33,388570000 +1996-09-27,685.86,687.11,683.73,686.19,414760000 +1996-09-26,685.83,690.15,683.77,685.86,500870000 +1996-09-25,685.61,688.26,684.92,685.83,451710000 +1996-09-24,686.48,690.88,683.54,685.61,460150000 +1996-09-23,687.03,687.03,681.01,686.48,297760000 +1996-09-20,683,687.07,683,687.03,519420000 +1996-09-19,681.47,684.07,679.06,683,398580000 +1996-09-18,682.94,683.77,679.75,681.47,396600000 +1996-09-17,683.98,685.8,679.96,682.94,449850000 +1996-09-16,680.54,686.48,680.53,683.98,430080000 +1996-09-13,671.15,681.39,671.15,680.54,488360000 +1996-09-12,667.28,673.07,667.28,671.15,398820000 +1996-09-11,663.81,667.73,661.79,667.28,376880000 +1996-09-10,663.76,665.57,661.55,663.81,372960000 +1996-09-09,655.68,663.77,655.68,663.76,311530000 +1996-09-06,649.44,658.21,649.44,655.68,348710000 +1996-09-05,655.61,655.61,648.89,649.44,361430000 +1996-09-04,654.72,655.82,652.93,655.61,351290000 +1996-09-03,651.99,655.13,643.97,654.72,345740000 +1996-08-30,657.4,657.71,650.52,651.99,258380000 +1996-08-29,664.81,664.81,655.35,657.4,321120000 +1996-08-28,666.4,667.41,664.39,664.81,296440000 +1996-08-27,663.88,666.4,663.5,666.4,310520000 +1996-08-26,667.03,667.03,662.36,663.88,281430000 +1996-08-23,670.68,670.68,664.93,667.03,308010000 +1996-08-22,665.07,670.68,664.88,670.68,354950000 +1996-08-21,665.69,665.69,662.16,665.07,348820000 +1996-08-20,666.58,666.99,665.15,665.69,334960000 +1996-08-19,665.21,667.12,665,666.58,294080000 +1996-08-16,662.28,666.34,662.26,665.21,337650000 +1996-08-15,662.05,664.18,660.64,662.28,323950000 +1996-08-14,660.2,662.42,658.47,662.05,343460000 +1996-08-13,665.77,665.77,659.13,660.2,362470000 +1996-08-12,662.1,665.77,658.95,665.77,312170000 +1996-08-09,662.59,665.37,660.31,662.1,327280000 +1996-08-08,664.16,664.17,661.28,662.59,334570000 +1996-08-07,662.38,664.61,660,664.16,394340000 +1996-08-06,660.23,662.75,656.83,662.38,347290000 +1996-08-05,662.49,663.64,659.03,660.23,307240000 +1996-08-02,650.02,662.49,650.02,662.49,442080000 +1996-08-01,639.95,650.66,639.49,650.02,439110000 +1996-07-31,635.26,640.54,633.74,639.95,403560000 +1996-07-30,630.91,635.26,629.22,635.26,341090000 +1996-07-29,635.9,635.9,630.9,630.91,281560000 +1996-07-26,631.17,636.23,631.17,635.9,349900000 +1996-07-25,626.65,633.57,626.65,631.17,405390000 +1996-07-24,626.19,629.1,616.43,626.65,463030000 +1996-07-23,633.79,637.7,625.65,626.87,421900000 +1996-07-22,638.73,638.73,630.38,633.77,327300000 +1996-07-19,643.51,643.51,635.5,638.73,408070000 +1996-07-18,634.07,644.44,633.29,643.56,474460000 +1996-07-17,628.37,636.61,628.37,634.07,513830000 +1996-07-16,629.8,631.99,605.88,628.37,682980000 +1996-07-15,646.19,646.19,629.69,629.8,419020000 +1996-07-12,645.67,647.64,640.21,646.19,396740000 +1996-07-11,656.06,656.06,639.52,645.67,520470000 +1996-07-10,654.75,656.27,648.39,656.06,421350000 +1996-07-09,652.54,656.6,652.54,654.75,400170000 +1996-07-08,657.44,657.65,651.13,652.54,367560000 +1996-07-05,672.4,672.4,657.41,657.44,181470000 +1996-07-03,673.61,673.64,670.21,672.4,336260000 +1996-07-02,675.88,675.88,672.55,673.61,388000000 +1996-07-01,670.63,675.88,670.63,675.88,345750000 +1996-06-28,668.55,672.68,668.55,670.63,470460000 +1996-06-27,664.39,668.9,661.56,668.55,405580000 +1996-06-26,668.48,668.49,663.67,664.39,386520000 +1996-06-25,668.85,670.65,667.29,668.48,391900000 +1996-06-24,666.84,671.07,666.84,668.85,333840000 +1996-06-21,662.1,666.84,662.1,666.84,520340000 +1996-06-20,661.96,664.96,658.75,662.1,441060000 +1996-06-19,662.06,665.62,661.21,661.96,383610000 +1996-06-18,665.16,666.36,661.34,662.06,373290000 +1996-06-17,665.85,668.27,664.09,665.16,298410000 +1996-06-14,667.92,668.4,664.35,665.85,390630000 +1996-06-13,669.04,670.54,665.49,667.92,397620000 +1996-06-12,670.97,673.67,668.77,669.04,397190000 +1996-06-11,672.16,676.72,669.94,670.97,405390000 +1996-06-10,673.31,673.61,670.15,672.16,337480000 +1996-06-07,673.03,673.31,662.48,673.31,445710000 +1996-06-06,678.44,680.32,673.02,673.03,466940000 +1996-06-05,672.56,678.45,672.09,678.44,380360000 +1996-06-04,667.68,672.6,667.68,672.56,386040000 +1996-06-03,669.12,669.12,665.19,667.68,318470000 +1996-05-31,671.7,673.46,667,669.12,351750000 +1996-05-30,667.93,673.51,664.56,671.7,381960000 +1996-05-29,672.23,673.73,666.09,667.93,346730000 +1996-05-28,678.51,679.98,671.52,672.23,341480000 +1996-05-24,676,679.72,676,678.51,329150000 +1996-05-23,678.42,681.1,673.45,676,431850000 +1996-05-22,672.76,678.42,671.23,678.42,423670000 +1996-05-21,673.15,675.56,672.26,672.76,409610000 +1996-05-20,668.91,673.66,667.64,673.15,385000000 +1996-05-17,664.85,669.84,664.85,668.91,429140000 +1996-05-16,665.42,667.11,662.79,664.85,392070000 +1996-05-15,665.6,669.82,664.46,665.42,447790000 +1996-05-14,661.51,666.96,661.51,665.6,460440000 +1996-05-13,652.09,662.16,652.09,661.51,394180000 +1996-05-10,645.44,653,645.44,652.09,428370000 +1996-05-09,644.77,647.95,643.18,645.44,404310000 +1996-05-08,638.26,644.79,630.07,644.77,495460000 +1996-05-07,640.81,641.4,636.96,638.26,410770000 +1996-05-06,641.63,644.64,636.19,640.81,375820000 +1996-05-03,643.38,648.45,640.23,641.63,434010000 +1996-05-02,654.58,654.58,642.13,643.38,442960000 +1996-05-01,654.17,656.44,652.26,654.58,404620000 +1996-04-30,654.16,654.59,651.05,654.17,393390000 +1996-04-29,653.46,654.71,651.6,654.16,344030000 +1996-04-26,652.87,656.43,651.96,653.46,402530000 +1996-04-25,650.17,654.18,647.06,652.87,462120000 +1996-04-24,651.58,653.37,648.25,650.17,494220000 +1996-04-23,647.89,651.59,647.7,651.58,452690000 +1996-04-22,645.07,650.91,645.07,647.89,395370000 +1996-04-19,643.61,647.32,643.61,645.07,435690000 +1996-04-18,641.61,644.66,640.76,643.61,415150000 +1996-04-17,645,645,638.71,641.61,465200000 +1996-04-16,642.49,645.57,642.15,645,453310000 +1996-04-15,636.71,642.49,636.71,642.49,346370000 +1996-04-12,631.18,637.14,631.18,636.71,413270000 +1996-04-11,633.5,635.26,624.14,631.18,519710000 +1996-04-10,642.19,642.78,631.76,633.5,475150000 +1996-04-09,644.24,646.33,640.84,642.19,426790000 +1996-04-08,655.86,655.86,638.04,644.24,411810000 +1996-04-04,655.88,656.68,654.89,655.86,383400000 +1996-04-03,655.26,655.89,651.81,655.88,386620000 +1996-04-02,653.73,655.27,652.81,655.26,406640000 +1996-04-01,645.5,653.87,645.5,653.73,392120000 +1996-03-29,648.94,650.96,644.89,645.5,413510000 +1996-03-28,648.91,649.58,646.36,648.94,370750000 +1996-03-27,652.97,653.94,647.6,648.91,406280000 +1996-03-26,650.04,654.31,648.15,652.97,400090000 +1996-03-25,650.62,655.5,648.82,650.04,336700000 +1996-03-22,649.19,652.08,649.19,650.62,329390000 +1996-03-21,649.98,651.54,648.1,649.19,367180000 +1996-03-20,651.69,653.13,645.57,649.98,409780000 +1996-03-19,652.65,656.18,649.8,651.69,438300000 +1996-03-18,641.43,652.65,641.43,652.65,437100000 +1996-03-15,640.87,642.87,638.35,641.43,529970000 +1996-03-14,638.55,644.17,638.55,640.87,492630000 +1996-03-13,637.09,640.52,635.19,638.55,413030000 +1996-03-12,640.02,640.02,628.82,637.09,454980000 +1996-03-11,633.5,640.41,629.95,640.02,449500000 +1996-03-08,653.65,653.65,627.63,633.5,546550000 +1996-03-07,652,653.65,649.54,653.65,425790000 +1996-03-06,655.79,656.97,651.61,652,428220000 +1996-03-05,650.81,655.8,648.77,655.79,445700000 +1996-03-04,644.37,653.54,644.37,650.81,417270000 +1996-03-01,640.43,644.38,635,644.37,471480000 +1996-02-29,644.75,646.95,639.01,640.43,453170000 +1996-02-28,647.24,654.39,643.99,644.75,447790000 +1996-02-27,650.46,650.62,643.87,647.24,431340000 +1996-02-26,659.08,659.08,650.16,650.46,399330000 +1996-02-23,658.86,663,652.25,659.08,443130000 +1996-02-22,648.1,659.75,648.1,658.86,485470000 +1996-02-21,640.65,648.11,640.65,648.1,431220000 +1996-02-20,647.98,647.98,638.79,640.65,395910000 +1996-02-16,651.32,651.42,646.99,647.98,445570000 +1996-02-15,655.58,656.84,651.15,651.32,415320000 +1996-02-14,660.51,661.53,654.36,655.58,421790000 +1996-02-13,661.45,664.23,657.92,660.51,441540000 +1996-02-12,656.37,662.95,656.34,661.45,397890000 +1996-02-09,656.07,661.08,653.64,656.37,477640000 +1996-02-08,649.93,656.54,647.93,656.07,474970000 +1996-02-07,646.33,649.93,645.59,649.93,462730000 +1996-02-06,641.43,646.67,639.68,646.33,465940000 +1996-02-05,635.84,641.43,633.71,641.43,377760000 +1996-02-02,638.46,639.26,634.29,635.84,420020000 +1996-02-01,636.02,638.46,634.54,638.46,461430000 +1996-01-31,630.15,636.18,629.48,636.02,472210000 +1996-01-30,624.22,630.29,624.22,630.15,464350000 +1996-01-29,621.62,624.22,621.42,624.22,363330000 +1996-01-26,617.03,621.7,615.26,621.62,385700000 +1996-01-25,619.96,620.15,616.62,617.03,453270000 +1996-01-24,612.79,619.96,612.79,619.96,476380000 +1996-01-23,613.4,613.4,610.65,612.79,416910000 +1996-01-22,611.83,613.45,610.95,613.4,398040000 +1996-01-19,608.24,612.92,606.76,611.83,497720000 +1996-01-18,606.37,608.27,604.12,608.24,450410000 +1996-01-17,608.44,609.93,604.7,606.37,458720000 +1996-01-16,599.82,608.44,599.05,608.44,425220000 +1996-01-15,601.81,603.43,598.47,599.82,306180000 +1996-01-12,602.69,604.8,597.46,601.81,383400000 +1996-01-11,598.48,602.71,597.54,602.69,408800000 +1996-01-10,609.45,609.45,597.29,598.48,496830000 +1996-01-09,618.46,619.15,608.21,609.45,417400000 +1996-01-08,616.71,618.46,616.49,618.46,130360000 +1996-01-05,617.7,617.7,612.02,616.71,437110000 +1996-01-04,621.32,624.49,613.96,617.7,512580000 +1996-01-03,620.73,623.25,619.56,621.32,468950000 +1996-01-02,615.93,620.74,613.17,620.73,364180000 +1995-12-29,614.12,615.93,612.36,615.93,321250000 +1995-12-28,614.53,615.5,612.4,614.12,288660000 +1995-12-27,614.3,615.73,613.75,614.53,252300000 +1995-12-26,611.96,614.5,611.96,614.3,217280000 +1995-12-22,610.49,613.5,610.45,611.95,289600000 +1995-12-21,605.94,610.52,605.94,610.49,415810000 +1995-12-20,611.93,614.27,605.93,605.94,437680000 +1995-12-19,606.81,611.94,605.05,611.93,478280000 +1995-12-18,616.34,616.34,606.13,606.81,426270000 +1995-12-15,616.92,617.72,614.46,616.34,636800000 +1995-12-14,621.69,622.88,616.13,616.92,465300000 +1995-12-13,618.78,622.02,618.27,621.69,415290000 +1995-12-12,619.52,619.55,617.68,618.78,349860000 +1995-12-11,617.48,620.9,617.14,619.52,342070000 +1995-12-08,616.17,617.82,614.32,617.48,327900000 +1995-12-07,620.18,620.19,615.21,616.17,379260000 +1995-12-06,617.68,621.11,616.69,620.18,417780000 +1995-12-05,613.68,618.48,613.14,617.68,437360000 +1995-12-04,606.98,613.83,606.84,613.68,405480000 +1995-12-01,605.37,608.11,605.37,606.98,393310000 +1995-11-30,607.64,608.69,605.37,605.37,440050000 +1995-11-29,606.45,607.66,605.47,607.64,398280000 +1995-11-28,601.32,606.45,599.02,606.45,408860000 +1995-11-27,599.97,603.35,599.97,601.32,359130000 +1995-11-24,598.4,600.24,598.4,599.97,125870000 +1995-11-22,600.24,600.71,598.4,598.4,404980000 +1995-11-21,596.85,600.28,595.42,600.24,408320000 +1995-11-20,600.07,600.4,596.17,596.85,333150000 +1995-11-17,597.34,600.14,597.3,600.07,437200000 +1995-11-16,593.96,597.91,593.52,597.34,423280000 +1995-11-15,589.29,593.97,588.36,593.96,376100000 +1995-11-14,592.3,592.3,588.98,589.29,354420000 +1995-11-13,592.72,593.72,590.58,592.3,295840000 +1995-11-10,593.26,593.26,590.39,592.72,298690000 +1995-11-09,591.71,593.9,590.89,593.26,380760000 +1995-11-08,586.32,591.71,586.32,591.71,359780000 +1995-11-07,588.46,588.46,584.24,586.32,364680000 +1995-11-06,590.57,590.64,588.31,588.46,309100000 +1995-11-03,589.72,590.57,588.65,590.57,348500000 +1995-11-02,584.22,589.72,584.22,589.72,397070000 +1995-11-01,581.5,584.24,581.04,584.22,378090000 +1995-10-31,583.25,586.71,581.5,581.5,377390000 +1995-10-30,579.7,583.79,579.7,583.25,319160000 +1995-10-27,576.72,579.71,573.21,579.7,379230000 +1995-10-26,582.47,582.63,572.53,576.72,464270000 +1995-10-25,586.54,587.19,581.41,582.47,433620000 +1995-10-24,585.06,587.31,584.75,586.54,415540000 +1995-10-23,587.46,587.46,583.73,585.06,330750000 +1995-10-20,590.65,590.66,586.78,587.46,389360000 +1995-10-19,587.44,590.66,586.34,590.65,406620000 +1995-10-18,586.78,589.77,586.27,587.44,411270000 +1995-10-17,583.03,586.78,581.9,586.78,356380000 +1995-10-16,584.5,584.86,582.63,583.03,300750000 +1995-10-13,583.1,587.39,583.1,584.5,374680000 +1995-10-12,579.46,583.12,579.46,583.1,344060000 +1995-10-11,577.52,579.52,577.08,579.46,340740000 +1995-10-10,578.37,578.37,571.55,577.52,412710000 +1995-10-09,582.49,582.49,576.35,578.37,275320000 +1995-10-06,582.63,584.54,582.1,582.49,313680000 +1995-10-05,581.47,582.63,579.58,582.63,367480000 +1995-10-04,582.34,582.34,579.91,581.47,339380000 +1995-10-03,581.72,582.34,578.48,582.34,385940000 +1995-10-02,584.41,585.05,580.54,581.72,304990000 +1995-09-29,585.87,587.61,584,584.41,335250000 +1995-09-28,581.04,585.88,580.69,585.87,367720000 +1995-09-27,581.41,581.42,574.68,581.04,411300000 +1995-09-26,581.81,584.66,580.65,581.41,363630000 +1995-09-25,581.73,582.14,579.5,581.81,273120000 +1995-09-22,583,583,578.25,581.73,370790000 +1995-09-21,586.77,586.79,580.91,583,367100000 +1995-09-20,584.2,586.77,584.18,586.77,400050000 +1995-09-19,582.78,584.24,580.75,584.2,371170000 +1995-09-18,583.35,583.37,579.36,582.77,326090000 +1995-09-15,583.61,585.07,581.79,583.35,459370000 +1995-09-14,578.77,583.99,578.77,583.61,382880000 +1995-09-13,576.51,579.72,575.47,578.77,384380000 +1995-09-12,573.91,576.51,573.11,576.51,344540000 +1995-09-11,572.68,575.15,572.68,573.91,296840000 +1995-09-08,570.29,572.68,569.27,572.68,317940000 +1995-09-07,570.17,571.11,569.23,570.29,321720000 +1995-09-06,569.17,570.53,569,570.17,369540000 +1995-09-05,563.86,569.2,563.84,569.17,332670000 +1995-09-01,561.88,564.62,561.01,563.84,256730000 +1995-08-31,561.09,562.36,560.49,561.88,300920000 +1995-08-30,560,561.52,559.49,560.92,329840000 +1995-08-29,559.05,560.01,555.71,560,311290000 +1995-08-28,560.1,562.22,557.99,559.05,267860000 +1995-08-25,557.46,561.31,557.46,560.1,255990000 +1995-08-24,557.14,558.63,555.2,557.46,299200000 +1995-08-23,559.52,560,557.08,557.14,291890000 +1995-08-22,558.11,559.52,555.87,559.52,290890000 +1995-08-21,559.21,563.34,557.89,558.11,303200000 +1995-08-18,559.04,561.24,558.34,559.21,320490000 +1995-08-17,559.97,559.97,557.42,559.04,354460000 +1995-08-16,558.57,559.98,557.37,559.97,390170000 +1995-08-15,559.74,559.98,555.22,558.57,330070000 +1995-08-14,555.11,559.74,554.76,559.74,264920000 +1995-08-11,557.45,558.5,553.04,555.11,267850000 +1995-08-10,559.71,560.63,556.05,557.45,306660000 +1995-08-09,560.39,561.59,559.29,559.71,303390000 +1995-08-08,560.03,561.53,558.32,560.39,306090000 +1995-08-07,558.94,561.24,558.94,560.03,277050000 +1995-08-04,558.75,559.57,557.91,558.94,314740000 +1995-08-03,558.8,558.8,554.1,558.75,353110000 +1995-08-02,559.64,565.62,557.87,558.8,374330000 +1995-08-01,562.06,562.11,556.67,559.64,332210000 +1995-07-31,562.93,563.49,560.06,562.06,291950000 +1995-07-28,565.22,565.4,562.04,562.93,311590000 +1995-07-27,561.61,565.33,561.61,565.22,356570000 +1995-07-26,561.1,563.78,560.85,561.61,393470000 +1995-07-25,556.63,561.75,556.34,561.1,373200000 +1995-07-24,553.62,557.21,553.62,556.63,315300000 +1995-07-21,553.34,554.73,550.91,553.62,431830000 +1995-07-20,550.98,554.43,549.1,553.54,383380000 +1995-07-19,556.58,558.46,542.51,550.98,489850000 +1995-07-18,562.55,562.72,556.86,558.46,372230000 +1995-07-17,560.34,562.94,559.45,562.72,322540000 +1995-07-14,561,561,556.41,559.89,312930000 +1995-07-13,560.89,562,559.07,561,387500000 +1995-07-12,555.27,561.56,554.27,560.89,416360000 +1995-07-11,556.78,557.19,553.8,554.78,376770000 +1995-07-10,556.37,558.48,555.77,557.19,409700000 +1995-07-07,553.9,556.57,553.05,556.37,466540000 +1995-07-06,547.26,553.99,546.59,553.99,420500000 +1995-07-05,547.09,549.98,546.28,547.26,357850000 +1995-07-03,544.75,547.1,544.43,547.09,117900000 +1995-06-30,543.87,546.82,543.51,544.75,311650000 +1995-06-29,544.73,546.25,540.79,543.87,313080000 +1995-06-28,542.43,546.33,540.72,544.73,368060000 +1995-06-27,544.11,547.07,542.19,542.43,346950000 +1995-06-26,549.71,549.79,544.06,544.13,296720000 +1995-06-23,551.07,551.07,548.23,549.71,321660000 +1995-06-22,543.98,551.07,543.98,551.07,421000000 +1995-06-21,544.98,545.93,543.9,543.98,398210000 +1995-06-20,545.22,545.44,543.43,544.98,382370000 +1995-06-19,539.83,545.22,539.83,545.22,322990000 +1995-06-16,537.51,539.98,537.12,539.83,442740000 +1995-06-15,536.48,539.07,535.56,537.12,334700000 +1995-06-14,536.05,536.48,533.83,536.47,330770000 +1995-06-13,530.88,536.23,530.88,536.05,339660000 +1995-06-12,527.94,532.54,527.94,530.88,289920000 +1995-06-09,532.35,532.35,526,527.94,327570000 +1995-06-08,533.13,533.56,531.65,532.35,289880000 +1995-06-07,535.55,535.55,531.66,533.13,327790000 +1995-06-06,535.6,537.09,535.14,535.55,340490000 +1995-06-05,532.51,537.73,532.47,535.6,337520000 +1995-06-02,533.49,536.91,529.55,532.51,366000000 +1995-06-01,533.4,534.21,530.05,533.49,345920000 +1995-05-31,523.7,533.41,522.17,533.4,358180000 +1995-05-30,523.65,525.58,521.38,523.58,283020000 +1995-05-26,528.59,528.59,522.51,523.65,291220000 +1995-05-25,528.37,529.04,524.89,528.59,341820000 +1995-05-24,528.59,531.91,525.57,528.61,391770000 +1995-05-23,523.65,528.59,523.65,528.59,362690000 +1995-05-22,519.19,524.34,519.19,523.65,285600000 +1995-05-19,519.58,519.58,517.07,519.19,354010000 +1995-05-18,526.88,526.88,519.58,519.58,351900000 +1995-05-17,528.19,528.42,525.38,527.07,347930000 +1995-05-16,527.74,529.08,526.45,528.19,366180000 +1995-05-15,525.55,527.74,525,527.74,316240000 +1995-05-12,524.37,527.05,523.3,525.55,361000000 +1995-05-11,524.33,524.89,522.7,524.37,339900000 +1995-05-10,523.74,524.4,521.53,524.36,381990000 +1995-05-09,523.96,525.99,521.79,523.56,361300000 +1995-05-08,520.09,525.15,519.14,523.96,291810000 +1995-05-05,520.75,522.35,518.28,520.12,342380000 +1995-05-04,520.48,525.4,519.44,520.54,434990000 +1995-05-03,514.93,520.54,514.86,520.48,392370000 +1995-05-02,514.23,515.18,513.03,514.86,302560000 +1995-05-01,514.76,515.6,513.42,514.26,296830000 +1995-04-28,513.64,515.29,510.9,514.71,320440000 +1995-04-27,512.7,513.62,511.63,513.55,350850000 +1995-04-26,511.99,513.04,510.47,512.66,350810000 +1995-04-25,512.8,513.54,511.32,512.1,351790000 +1995-04-24,508.49,513.02,507.44,512.89,326280000 +1995-04-21,505.63,508.49,505.63,508.49,403250000 +1995-04-20,504.92,506.5,503.44,505.29,368450000 +1995-04-19,505.37,505.89,501.19,504.92,378050000 +1995-04-18,506.43,507.65,504.12,505.37,344680000 +1995-04-17,509.23,512.03,505.43,506.13,333930000 +1995-04-13,507.19,509.83,507.17,509.23,301580000 +1995-04-12,505.59,507.17,505.07,507.17,327880000 +1995-04-11,507.24,508.85,505.29,505.53,310660000 +1995-04-10,506.3,507.01,504.61,507.01,260980000 +1995-04-07,506.13,507.19,503.59,506.42,314760000 +1995-04-06,505.63,507.1,505,506.08,320460000 +1995-04-05,505.27,505.57,503.17,505.57,315170000 +1995-04-04,501.85,505.26,501.85,505.24,330580000 +1995-04-03,500.7,501.91,500.2,501.85,296430000 +1995-03-31,501.94,502.22,495.7,500.71,353060000 +1995-03-30,503.17,504.66,501,502.22,362940000 +1995-03-29,503.92,508.15,500.96,503.12,385940000 +1995-03-28,503.19,503.91,501.83,503.9,320360000 +1995-03-27,500.97,503.2,500.93,503.2,296270000 +1995-03-24,496.07,500.97,496.07,500.97,358370000 +1995-03-23,495.67,496.77,494.19,495.95,318530000 +1995-03-22,495.07,495.67,493.67,495.67,313120000 +1995-03-21,496.15,499.19,494.04,495.07,367110000 +1995-03-20,495.52,496.61,495.27,496.14,301740000 +1995-03-17,495.43,496.67,494.95,495.52,417380000 +1995-03-16,491.87,495.74,491.78,495.41,336670000 +1995-03-15,492.89,492.89,490.83,491.88,309540000 +1995-03-14,490.05,493.69,490.05,492.89,346160000 +1995-03-13,489.57,491.28,489.35,490.05,275280000 +1995-03-10,483.16,490.37,483.16,489.57,382940000 +1995-03-09,483.14,483.74,482.05,483.16,319320000 +1995-03-08,482.12,484.08,481.57,483.14,349780000 +1995-03-07,485.63,485.63,479.7,482.12,355550000 +1995-03-06,485.42,485.7,481.52,485.63,298870000 +1995-03-03,485.13,485.42,483.07,485.42,330840000 +1995-03-02,485.65,485.71,483.19,485.13,330030000 +1995-03-01,487.39,487.83,484.92,485.65,362600000 +1995-02-28,483.81,487.44,483.77,487.39,317220000 +1995-02-27,488.26,488.26,483.18,483.81,285790000 +1995-02-24,486.82,488.28,485.7,488.11,302930000 +1995-02-23,485.07,489.19,485.07,486.91,394280000 +1995-02-22,482.74,486.15,482.45,485.07,339460000 +1995-02-21,481.95,483.26,481.94,482.72,308090000 +1995-02-17,485.15,485.22,481.97,481.97,347970000 +1995-02-16,484.56,485.22,483.05,485.22,360990000 +1995-02-15,482.55,485.54,481.77,484.54,378040000 +1995-02-14,481.65,482.94,480.89,482.55,300720000 +1995-02-13,481.46,482.86,481.07,481.65,256270000 +1995-02-10,480.19,481.96,479.53,481.46,295600000 +1995-02-09,481.19,482,479.91,480.19,325570000 +1995-02-08,480.81,482.6,480.4,481.19,318430000 +1995-02-07,481.14,481.32,479.69,480.81,314660000 +1995-02-06,478.64,481.95,478.36,481.14,325660000 +1995-02-03,472.78,479.91,472.78,478.65,441000000 +1995-02-02,470.4,472.79,469.95,472.79,322110000 +1995-02-01,470.42,472.75,469.29,470.4,395310000 +1995-01-31,468.51,471.03,468.18,470.42,411590000 +1995-01-30,470.39,470.52,467.49,468.51,318550000 +1995-01-27,468.32,471.36,468.32,470.39,339510000 +1995-01-26,467.44,468.62,466.9,468.32,304730000 +1995-01-25,465.86,469.51,464.4,467.44,342610000 +1995-01-24,465.81,466.88,465.47,465.86,315430000 +1995-01-23,464.78,466.23,461.14,465.82,325830000 +1995-01-20,466.95,466.99,463.99,464.78,378190000 +1995-01-19,469.72,469.72,466.4,466.95,297220000 +1995-01-18,470.05,470.43,468.03,469.71,344660000 +1995-01-17,469.38,470.15,468.19,470.05,331520000 +1995-01-16,465.97,470.39,465.97,469.38,315810000 +1995-01-13,461.64,466.43,461.64,465.97,336740000 +1995-01-12,461.64,461.93,460.63,461.64,313040000 +1995-01-11,461.68,463.61,458.65,461.66,346310000 +1995-01-10,460.9,464.59,460.9,461.68,352450000 +1995-01-09,460.67,461.77,459.74,460.83,278790000 +1995-01-06,460.38,462.49,459.47,460.68,308070000 +1995-01-05,460.73,461.3,459.75,460.34,309050000 +1995-01-04,459.13,460.72,457.56,460.71,319510000 +1995-01-03,459.21,459.27,457.2,459.11,262450000 +1994-12-30,461.17,462.12,459.24,459.27,256260000 +1994-12-29,460.92,461.81,460.36,461.17,250650000 +1994-12-28,462.47,462.49,459,460.86,246260000 +1994-12-27,459.85,462.73,459.85,462.47,211180000 +1994-12-23,459.7,461.32,459.39,459.83,196540000 +1994-12-22,459.62,461.21,459.33,459.68,340330000 +1994-12-21,457.24,461.7,457.17,459.61,379130000 +1994-12-20,458.08,458.45,456.37,457.1,326530000 +1994-12-19,458.78,458.78,456.64,457.91,271850000 +1994-12-16,455.35,458.8,455.35,458.8,481860000 +1994-12-15,454.97,456.84,454.5,455.34,332790000 +1994-12-14,450.05,456.16,450.05,454.97,355000000 +1994-12-13,449.52,451.69,449.43,450.15,307110000 +1994-12-12,446.95,449.48,445.62,449.47,285730000 +1994-12-09,445.45,446.98,442.88,446.96,336440000 +1994-12-08,451.23,452.06,444.59,445.45,362290000 +1994-12-07,453.11,453.11,450.01,451.23,283490000 +1994-12-06,453.29,453.93,450.35,453.11,298930000 +1994-12-05,453.3,455.04,452.06,453.32,258490000 +1994-12-02,448.92,453.31,448,453.3,284750000 +1994-12-01,453.55,453.91,447.97,448.92,285920000 +1994-11-30,455.17,457.13,453.27,453.69,298650000 +1994-11-29,454.23,455.17,452.14,455.17,286620000 +1994-11-28,452.26,454.19,451.04,454.16,265480000 +1994-11-25,449.94,452.87,449.94,452.29,118290000 +1994-11-23,450.01,450.61,444.18,449.93,430760000 +1994-11-22,457.95,458.03,450.08,450.09,387270000 +1994-11-21,461.69,463.41,457.55,458.3,293030000 +1994-11-18,463.6,463.84,460.25,461.47,356730000 +1994-11-17,465.71,465.83,461.47,463.57,323190000 +1994-11-16,465.06,466.25,464.28,465.62,296980000 +1994-11-15,466.04,468.51,462.95,465.03,336450000 +1994-11-14,462.44,466.29,462.35,466.04,260380000 +1994-11-11,464.17,464.17,461.45,462.35,220800000 +1994-11-10,465.4,467.79,463.73,464.37,280910000 +1994-11-09,465.65,469.95,463.46,465.4,337780000 +1994-11-08,463.08,467.54,463.07,465.65,290860000 +1994-11-07,462.31,463.56,461.25,463.07,255030000 +1994-11-04,467.96,469.28,462.28,462.28,280560000 +1994-11-03,466.5,468.64,466.4,467.91,285170000 +1994-11-02,468.41,470.92,466.36,466.51,331360000 +1994-11-01,472.26,472.26,467.64,468.42,314940000 +1994-10-31,473.76,474.74,472.33,472.35,302820000 +1994-10-28,465.84,473.78,465.8,473.77,381450000 +1994-10-27,462.68,465.85,462.62,465.85,327790000 +1994-10-26,461.55,463.77,461.22,462.62,322570000 +1994-10-25,460.83,461.95,458.26,461.53,326110000 +1994-10-24,464.89,466.37,460.8,460.83,282800000 +1994-10-21,466.69,466.69,463.83,464.89,315310000 +1994-10-20,470.37,470.37,465.39,466.85,331460000 +1994-10-19,467.69,471.43,465.96,470.28,317030000 +1994-10-18,469.02,469.19,466.54,467.66,259730000 +1994-10-17,469.11,469.88,468.16,468.96,238490000 +1994-10-14,467.78,469.53,466.11,469.1,251770000 +1994-10-13,465.56,471.3,465.56,467.79,337900000 +1994-10-12,465.78,466.7,464.79,465.47,269550000 +1994-10-11,459.04,466.34,459.04,465.79,355540000 +1994-10-10,455.12,459.29,455.12,459.04,213110000 +1994-10-07,452.37,455.67,452.13,455.1,284230000 +1994-10-06,453.52,454.49,452.13,452.36,272620000 +1994-10-05,454.59,454.59,449.27,453.52,359670000 +1994-10-04,461.77,462.46,454.03,454.59,325620000 +1994-10-03,462.69,463.31,460.33,461.74,269130000 +1994-09-30,462.27,465.3,461.91,462.71,291900000 +1994-09-29,464.84,464.84,461.51,462.24,302280000 +1994-09-28,462.1,465.55,462.1,464.84,330020000 +1994-09-27,460.82,462.75,459.83,462.05,290330000 +1994-09-26,459.65,460.87,459.31,460.82,272530000 +1994-09-23,461.27,462.14,459.01,459.67,300060000 +1994-09-22,461.45,463.22,460.96,461.27,305210000 +1994-09-21,463.42,464.01,458.47,461.46,351830000 +1994-09-20,470.83,470.83,463.36,463.36,326050000 +1994-09-19,471.21,473.15,470.68,470.85,277110000 +1994-09-16,474.81,474.81,470.06,471.19,410750000 +1994-09-15,468.8,474.81,468.79,474.81,281920000 +1994-09-14,467.55,468.86,466.82,468.8,297480000 +1994-09-13,466.27,468.76,466.27,467.51,293370000 +1994-09-12,468.18,468.42,466.15,466.21,244680000 +1994-09-09,473.13,473.13,466.55,468.18,293360000 +1994-09-08,470.96,473.4,470.86,473.14,295010000 +1994-09-07,471.86,472.41,470.2,470.99,290330000 +1994-09-06,471,471.92,469.64,471.86,199670000 +1994-09-02,473.2,474.89,470.67,470.99,216150000 +1994-09-01,475.49,475.49,471.74,473.17,282830000 +1994-08-31,476.07,477.59,474.43,475.49,354650000 +1994-08-30,474.59,476.61,473.56,476.07,294520000 +1994-08-29,473.89,477.14,473.89,474.59,266080000 +1994-08-26,468.08,474.65,468.08,473.8,305120000 +1994-08-25,469.07,470.12,467.64,468.08,284230000 +1994-08-24,464.51,469.05,464.51,469.03,310510000 +1994-08-23,462.39,466.58,462.39,464.51,307240000 +1994-08-22,463.61,463.61,461.46,462.32,235870000 +1994-08-19,463.25,464.37,461.81,463.68,276630000 +1994-08-18,465.1,465.1,462.3,463.17,287330000 +1994-08-17,465.11,465.91,464.57,465.17,309250000 +1994-08-16,461.22,465.2,459.89,465.01,306640000 +1994-08-15,461.97,463.34,461.21,461.23,223210000 +1994-08-12,458.88,462.27,458.88,461.94,249280000 +1994-08-11,460.31,461.41,456.88,458.88,275690000 +1994-08-10,457.98,460.48,457.98,460.3,279500000 +1994-08-09,457.89,458.16,456.66,457.92,259140000 +1994-08-08,457.08,458.3,457.01,457.89,217680000 +1994-08-05,458.34,458.34,456.08,457.09,230270000 +1994-08-04,461.45,461.49,458.4,458.4,289150000 +1994-08-03,460.65,461.46,459.51,461.45,283840000 +1994-08-02,461.01,462.77,459.7,460.56,294740000 +1994-08-01,458.28,461.01,458.08,461.01,258180000 +1994-07-29,454.25,459.33,454.25,458.26,269560000 +1994-07-28,452.57,454.93,452.3,454.24,245990000 +1994-07-27,453.36,453.38,451.36,452.57,251680000 +1994-07-26,454.25,454.25,452.78,453.36,232670000 +1994-07-25,453.1,454.32,452.76,454.25,213470000 +1994-07-22,452.61,454.03,452.33,453.11,261600000 +1994-07-21,451.6,453.22,451,452.61,292120000 +1994-07-20,453.89,454.16,450.69,451.6,267840000 +1994-07-19,455.22,455.3,453.86,453.86,251530000 +1994-07-18,454.41,455.71,453.26,455.22,227460000 +1994-07-15,453.28,454.33,452.8,454.16,275860000 +1994-07-14,448.73,454.33,448.73,453.41,322330000 +1994-07-13,448.03,450.06,447.97,448.73,265840000 +1994-07-12,448.02,448.16,444.65,447.95,252250000 +1994-07-11,449.56,450.24,445.27,448.06,222970000 +1994-07-08,448.38,449.75,446.53,449.55,236520000 +1994-07-07,446.15,448.64,446.15,448.38,259740000 +1994-07-06,446.29,447.28,444.18,446.13,236230000 +1994-07-05,446.2,447.62,445.14,446.37,195410000 +1994-07-01,444.27,446.45,443.58,446.2,199030000 +1994-06-30,447.63,448.61,443.66,444.27,293410000 +1994-06-29,446.05,449.83,446.04,447.63,264430000 +1994-06-28,447.36,448.47,443.08,446.07,267740000 +1994-06-27,442.78,447.76,439.83,447.31,250080000 +1994-06-24,449.63,449.63,442.51,442.8,261260000 +1994-06-23,453.09,454.16,449.43,449.63,256480000 +1994-06-22,451.4,453.91,451.4,453.09,251110000 +1994-06-21,455.48,455.48,449.45,451.34,298730000 +1994-06-20,458.45,458.45,454.46,455.48,229520000 +1994-06-17,461.93,462.16,458.44,458.45,373450000 +1994-06-16,460.61,461.93,459.8,461.93,256390000 +1994-06-15,462.38,463.23,459.95,460.61,269740000 +1994-06-14,459.1,462.52,459.1,462.37,288550000 +1994-06-13,458.67,459.36,457.18,459.1,243640000 +1994-06-10,457.86,459.48,457.36,458.67,222480000 +1994-06-09,457.06,457.87,455.86,457.86,252870000 +1994-06-08,458.21,459.74,455.43,457.06,256000000 +1994-06-07,458.88,459.46,457.65,458.21,234680000 +1994-06-06,460.13,461.87,458.85,458.88,259080000 +1994-06-03,457.65,460.86,456.27,460.13,271490000 +1994-06-02,457.62,458.5,457.26,457.65,271630000 +1994-06-01,456.5,458.29,453.99,457.63,279910000 +1994-05-31,457.32,457.61,455.16,456.5,216700000 +1994-05-27,457.03,457.33,454.67,457.33,186430000 +1994-05-26,456.33,457.77,455.79,457.06,255740000 +1994-05-25,454.84,456.34,452.2,456.34,254420000 +1994-05-24,453.21,456.77,453.21,454.81,280040000 +1994-05-23,454.92,454.92,451.79,453.2,249420000 +1994-05-20,456.48,456.48,454.22,454.92,295180000 +1994-05-19,453.69,456.88,453,456.48,303680000 +1994-05-18,449.39,454.45,448.87,453.69,337670000 +1994-05-17,444.49,449.37,443.7,449.37,311280000 +1994-05-16,444.15,445.82,443.62,444.49,234700000 +1994-05-13,443.62,444.72,441.21,444.14,252070000 +1994-05-12,441.5,444.8,441.5,443.75,272770000 +1994-05-11,446.03,446.03,440.78,441.49,277400000 +1994-05-10,442.37,446.84,442.37,446.01,297660000 +1994-05-09,447.82,447.82,441.84,442.32,250870000 +1994-05-06,451.37,451.37,445.64,447.82,291910000 +1994-05-05,451.72,452.82,450.72,451.38,255690000 +1994-05-04,453.04,453.11,449.87,451.72,267940000 +1994-05-03,453.06,453.98,450.51,453.03,288270000 +1994-05-02,450.91,453.57,449.05,453.02,296130000 +1994-04-29,449.07,451.35,447.91,450.91,293970000 +1994-04-28,451.84,452.23,447.97,449.1,325200000 +1994-04-26,452.71,452.79,450.66,451.87,288120000 +1994-04-25,447.64,452.71,447.58,452.71,262320000 +1994-04-22,448.73,449.96,447.16,447.63,295710000 +1994-04-21,441.96,449.14,441.96,448.73,378770000 +1994-04-20,442.54,445.01,439.4,441.96,366540000 +1994-04-19,442.54,444.82,438.83,442.54,323280000 +1994-04-18,446.27,447.87,441.48,442.46,271470000 +1994-04-15,446.38,447.85,445.81,446.18,309550000 +1994-04-14,446.26,447.55,443.57,446.38,275130000 +1994-04-13,447.63,448.57,442.62,446.26,278030000 +1994-04-12,449.83,450.8,447.33,447.57,257990000 +1994-04-11,447.12,450.34,447.1,449.87,243180000 +1994-04-08,450.89,450.89,445.51,447.1,264090000 +1994-04-07,448.11,451.1,446.38,450.88,289280000 +1994-04-06,448.29,449.63,444.98,448.05,302000000 +1994-04-05,439.14,448.29,439.14,448.29,365990000 +1994-04-04,445.66,445.66,435.86,438.92,344390000 +1994-03-31,445.55,447.16,436.16,445.77,403580000 +1994-03-30,452.48,452.49,445.55,445.55,390520000 +1994-03-29,460,460.32,452.43,452.48,305360000 +1994-03-28,460.58,461.12,456.1,460,287350000 +1994-03-25,464.35,465.29,460.58,460.58,249640000 +1994-03-24,468.57,468.57,462.41,464.35,303740000 +1994-03-23,468.89,470.38,468.52,468.54,281500000 +1994-03-22,468.4,470.47,467.88,468.8,282240000 +1994-03-21,471.06,471.06,467.23,468.54,247380000 +1994-03-18,470.89,471.09,467.83,471.06,462240000 +1994-03-17,469.42,471.05,468.62,470.9,303930000 +1994-03-16,467.04,469.85,465.48,469.42,307640000 +1994-03-15,467.39,468.99,466.04,467.01,303750000 +1994-03-14,466.44,467.6,466.08,467.39,260150000 +1994-03-11,463.86,466.61,462.54,466.44,303890000 +1994-03-10,467.08,467.29,462.46,463.9,369370000 +1994-03-09,465.94,467.42,463.4,467.06,309810000 +1994-03-08,466.92,467.79,465.02,465.88,298110000 +1994-03-07,464.74,468.07,464.74,466.91,285590000 +1994-03-04,463.03,466.16,462.41,464.74,311850000 +1994-03-03,464.81,464.83,462.5,463.01,291790000 +1994-03-02,464.4,464.87,457.49,464.81,361130000 +1994-03-01,467.19,467.43,462.02,464.44,304450000 +1994-02-28,466.07,469.16,466.07,467.14,268690000 +1994-02-25,464.33,466.48,464.33,466.07,273680000 +1994-02-24,470.65,470.65,464.26,464.26,342940000 +1994-02-23,471.48,472.41,469.47,470.69,309910000 +1994-02-22,467.69,471.65,467.58,471.46,270900000 +1994-02-18,470.29,471.09,466.07,467.69,293210000 +1994-02-17,472.79,475.12,468.44,470.34,340030000 +1994-02-16,472.53,474.16,471.94,472.79,295450000 +1994-02-15,470.23,473.41,470.23,472.52,306790000 +1994-02-14,470.18,471.99,469.05,470.23,263190000 +1994-02-11,468.93,471.13,466.89,470.18,213740000 +1994-02-10,472.81,473.13,468.91,468.93,327250000 +1994-02-09,471.05,473.41,471.05,472.77,332670000 +1994-02-08,471.76,472.33,469.5,471.05,318180000 +1994-02-07,469.81,472.09,467.57,471.76,348270000 +1994-02-04,480.68,481.02,469.28,469.81,378380000 +1994-02-03,481.96,481.96,478.71,480.71,318350000 +1994-02-02,479.62,482.23,479.57,482,328960000 +1994-02-01,481.6,481.64,479.18,479.62,322510000 +1994-01-31,478.7,482.85,478.7,481.61,322870000 +1994-01-28,477.05,479.75,477.05,478.7,313140000 +1994-01-27,473.2,477.52,473.2,477.05,346500000 +1994-01-26,470.92,473.44,470.72,473.2,304660000 +1994-01-25,471.97,472.56,470.27,470.92,326120000 +1994-01-24,474.72,475.2,471.49,471.97,296900000 +1994-01-21,474.98,475.56,473.72,474.72,346350000 +1994-01-20,474.3,475,473.42,474.98,310450000 +1994-01-19,474.25,474.7,472.21,474.3,311370000 +1994-01-18,473.3,475.19,473.29,474.25,308840000 +1994-01-17,474.91,474.91,472.84,473.3,233980000 +1994-01-14,472.5,475.32,472.5,474.91,304920000 +1994-01-13,474.17,474.17,471.8,472.47,277970000 +1994-01-12,474.13,475.06,472.14,474.17,310690000 +1994-01-11,475.27,475.28,473.27,474.13,305490000 +1994-01-10,469.9,475.27,469.55,475.27,319490000 +1994-01-07,467.09,470.26,467.03,469.9,324920000 +1994-01-06,467.55,469,467.02,467.12,365960000 +1994-01-05,466.89,467.82,465.92,467.55,400030000 +1994-01-04,465.44,466.89,464.44,466.89,326600000 +1994-01-03,466.51,466.94,464.36,465.44,270140000 +1993-12-31,468.66,470.75,466.45,466.45,168590000 +1993-12-30,470.58,470.58,468.09,468.64,195860000 +1993-12-29,470.88,471.29,469.87,470.58,269570000 +1993-12-28,470.61,471.05,469.43,470.94,200960000 +1993-12-27,467.4,470.55,467.35,470.54,171200000 +1993-12-23,467.3,468.97,467.3,467.38,227240000 +1993-12-22,465.08,467.38,465.08,467.32,272440000 +1993-12-21,465.84,465.92,464.03,465.3,273370000 +1993-12-20,466.38,466.9,465.53,465.85,255900000 +1993-12-17,463.34,466.38,463.34,466.38,363750000 +1993-12-16,461.86,463.98,461.86,463.34,284620000 +1993-12-15,463.06,463.69,461.84,461.84,331770000 +1993-12-14,465.73,466.12,462.46,463.06,275050000 +1993-12-13,463.93,465.71,462.71,465.7,256580000 +1993-12-10,464.18,464.87,462.66,463.93,245620000 +1993-12-09,466.29,466.54,463.87,464.18,287570000 +1993-12-08,465.88,466.73,465.42,466.29,314460000 +1993-12-07,466.43,466.77,465.44,466.76,285690000 +1993-12-06,464.89,466.89,464.4,466.43,292370000 +1993-12-03,463.13,464.89,462.67,464.89,268360000 +1993-12-02,461.89,463.22,461.45,463.11,256370000 +1993-12-01,461.93,464.47,461.63,461.89,293870000 +1993-11-30,461.9,463.62,460.45,461.79,286660000 +1993-11-29,463.06,464.83,461.83,461.9,272710000 +1993-11-26,462.36,463.63,462.36,463.06,90220000 +1993-11-24,461.03,462.9,461.03,462.36,230630000 +1993-11-23,459.13,461.77,458.47,461.03,260400000 +1993-11-22,462.6,462.6,457.08,459.13,280130000 +1993-11-19,463.59,463.6,460.03,462.6,302970000 +1993-11-18,464.83,464.88,461.73,463.62,313490000 +1993-11-17,466.74,467.24,462.73,464.81,316940000 +1993-11-16,463.75,466.74,462.97,466.74,303980000 +1993-11-15,465.39,466.13,463.01,463.75,251030000 +1993-11-12,462.64,465.84,462.64,465.39,326240000 +1993-11-11,463.72,464.96,462.49,462.64,283820000 +1993-11-10,460.4,463.72,459.57,463.72,283450000 +1993-11-09,460.21,463.42,460.21,460.33,276360000 +1993-11-08,459.57,461.54,458.78,460.21,234340000 +1993-11-05,457.49,459.63,454.36,459.57,336890000 +1993-11-04,463.02,463.16,457.26,457.49,323430000 +1993-11-03,468.44,468.61,460.95,463.02,342110000 +1993-11-02,469.1,469.1,466.2,468.44,304780000 +1993-11-01,467.83,469.11,467.33,469.1,256030000 +1993-10-29,467.72,468.2,467.37,467.83,270570000 +1993-10-28,464.52,468.76,464.52,467.73,301220000 +1993-10-27,464.3,464.61,463.36,464.61,279830000 +1993-10-26,464.2,464.32,462.65,464.3,284530000 +1993-10-25,463.27,464.49,462.05,464.2,260310000 +1993-10-22,465.36,467.82,463.27,463.27,301440000 +1993-10-21,466.06,466.64,464.38,465.36,289600000 +1993-10-20,466.21,466.87,464.54,466.07,305670000 +1993-10-19,468.41,468.64,464.8,466.21,304400000 +1993-10-18,469.5,470.04,468.02,468.45,329580000 +1993-10-15,466.83,471.1,466.83,469.5,366110000 +1993-10-14,461.55,466.83,461.55,466.83,352530000 +1993-10-13,461.12,461.98,460.76,461.49,290930000 +1993-10-12,461.04,462.47,460.73,461.12,263970000 +1993-10-11,460.31,461.87,460.31,460.88,183060000 +1993-10-08,459.18,460.99,456.4,460.31,243600000 +1993-10-07,460.71,461.13,459.08,459.18,255210000 +1993-10-06,461.24,462.6,460.26,460.74,277070000 +1993-10-05,461.34,463.15,459.45,461.2,294570000 +1993-10-04,461.28,461.8,460.02,461.34,229380000 +1993-10-01,458.93,461.48,458.35,461.28,256880000 +1993-09-30,460.11,460.56,458.28,458.93,280980000 +1993-09-29,461.6,462.17,459.51,460.11,277690000 +1993-09-28,461.84,462.08,460.91,461.53,243320000 +1993-09-27,457.63,461.81,457.63,461.8,244920000 +1993-09-24,457.74,458.56,456.92,457.63,248270000 +1993-09-23,456.25,458.69,456.25,457.74,275350000 +1993-09-22,452.94,456.92,452.94,456.2,298960000 +1993-09-21,455.05,455.8,449.64,452.95,300310000 +1993-09-20,458.84,459.91,455,455.05,231130000 +1993-09-17,459.43,459.43,457.09,458.83,381370000 +1993-09-16,461.54,461.54,459,459.43,229700000 +1993-09-15,459.9,461.96,456.31,461.6,294410000 +1993-09-14,461.93,461.93,458.15,459.9,258650000 +1993-09-13,461.7,463.38,461.41,462.06,244970000 +1993-09-10,457.49,461.86,457.49,461.72,269950000 +1993-09-09,456.65,458.11,455.17,457.5,258070000 +1993-09-08,458.52,458.53,453.75,456.65,283100000 +1993-09-07,461.34,462.07,457.95,458.52,229500000 +1993-09-03,461.3,462.05,459.91,461.34,197160000 +1993-09-02,463.13,463.54,461.07,461.3,259870000 +1993-09-01,463.55,463.8,461.77,463.15,245040000 +1993-08-31,461.9,463.56,461.29,463.56,252830000 +1993-08-30,460.54,462.58,460.28,461.9,194180000 +1993-08-27,461.05,461.05,459.19,460.54,196140000 +1993-08-26,460.04,462.87,458.82,461.04,254070000 +1993-08-25,459.75,462.04,459.3,460.13,301650000 +1993-08-24,455.23,459.77,455.04,459.77,270700000 +1993-08-23,456.12,456.12,454.29,455.23,212500000 +1993-08-20,456.51,456.68,454.6,456.16,276800000 +1993-08-19,456.01,456.76,455.2,456.43,293330000 +1993-08-18,453.21,456.99,453.21,456.04,312940000 +1993-08-17,452.38,453.7,451.96,453.13,261320000 +1993-08-16,450.25,453.41,450.25,452.38,233640000 +1993-08-13,448.97,450.25,448.97,450.14,214370000 +1993-08-12,450.47,451.63,447.53,448.96,278530000 +1993-08-11,449.6,451,449.6,450.46,268330000 +1993-08-10,450.71,450.71,449.1,449.45,255520000 +1993-08-09,448.68,451.51,448.31,450.72,232750000 +1993-08-06,448.13,449.26,447.87,448.68,221170000 +1993-08-05,448.55,449.61,446.94,448.13,261900000 +1993-08-04,449.27,449.72,447.93,448.54,230040000 +1993-08-03,450.15,450.43,447.59,449.27,253110000 +1993-08-02,448.13,450.15,448.03,450.15,230380000 +1993-07-30,450.19,450.22,446.98,448.13,254420000 +1993-07-29,447.19,450.77,447.19,450.24,261240000 +1993-07-28,448.25,448.61,446.59,447.19,273100000 +1993-07-27,449,449.44,446.76,448.24,256750000 +1993-07-26,447.06,449.5,447.04,449.09,222580000 +1993-07-23,444.54,447.1,444.54,447.1,222170000 +1993-07-22,447.18,447.23,443.72,444.51,249630000 +1993-07-21,447.28,447.5,445.84,447.18,278590000 +1993-07-20,446.03,447.63,443.71,447.31,277420000 +1993-07-19,445.75,446.78,444.83,446.03,216370000 +1993-07-16,449.07,449.08,445.66,445.75,263100000 +1993-07-15,450.09,450.12,447.26,449.22,277810000 +1993-07-14,448.08,451.12,448.08,450.08,297430000 +1993-07-13,449,450.7,448.07,448.09,236720000 +1993-07-12,448.13,449.11,447.71,448.98,202310000 +1993-07-09,448.64,448.94,446.74,448.11,235210000 +1993-07-08,442.84,448.64,442.84,448.64,282910000 +1993-07-07,441.4,443.63,441.4,442.83,253170000 +1993-07-06,445.86,446.87,441.42,441.43,234810000 +1993-07-02,449.02,449.02,445.2,445.84,220750000 +1993-07-01,450.54,451.15,448.71,449.02,292040000 +1993-06-30,450.69,451.47,450.15,450.53,281120000 +1993-06-29,451.89,451.9,449.67,450.69,276310000 +1993-06-28,447.6,451.9,447.6,451.85,242090000 +1993-06-25,446.62,448.64,446.62,447.6,210430000 +1993-06-24,443.04,447.21,442.5,446.62,267450000 +1993-06-23,445.96,445.96,443.19,443.19,278260000 +1993-06-22,446.25,446.29,444.94,445.93,259530000 +1993-06-21,443.68,446.22,443.68,446.22,223650000 +1993-06-18,448.54,448.59,443.68,443.68,300500000 +1993-06-17,447.43,448.98,446.91,448.54,239810000 +1993-06-16,446.27,447.43,443.61,447.43,267500000 +1993-06-15,447.73,448.28,446.18,446.27,234110000 +1993-06-14,447.26,448.64,447.23,447.71,210440000 +1993-06-11,445.38,448.19,445.38,447.26,256750000 +1993-06-10,445.78,446.22,444.09,445.38,232600000 +1993-06-09,444.71,447.39,444.66,445.78,249030000 +1993-06-08,447.65,447.65,444.31,444.71,240640000 +1993-06-07,450.07,450.75,447.32,447.69,236920000 +1993-06-04,452.43,452.43,448.92,450.06,226440000 +1993-06-03,453.84,453.85,451.12,452.49,285570000 +1993-06-02,453.83,454.53,452.68,453.85,295560000 +1993-06-01,450.23,455.63,450.23,453.83,229690000 +1993-05-28,452.41,452.41,447.67,450.19,207820000 +1993-05-27,453.44,454.55,451.14,452.41,300810000 +1993-05-26,448.85,453.51,448.82,453.44,274230000 +1993-05-25,448,449.04,447.7,448.85,222090000 +1993-05-24,445.84,448.44,445.26,448,197990000 +1993-05-21,450.59,450.59,444.89,445.84,279120000 +1993-05-20,447.57,450.59,447.36,450.59,289160000 +1993-05-19,440.32,447.86,436.86,447.57,342420000 +1993-05-18,440.39,441.26,437.95,440.32,264300000 +1993-05-17,439.56,440.38,437.83,440.37,227580000 +1993-05-14,439.22,439.82,438.1,439.56,252910000 +1993-05-13,444.75,444.75,439.23,439.23,293920000 +1993-05-12,444.32,445.16,442.87,444.8,255680000 +1993-05-11,442.8,444.57,441.52,444.36,218480000 +1993-05-10,442.34,445.42,442.05,442.8,235580000 +1993-05-07,443.28,443.7,441.69,442.31,223570000 +1993-05-06,444.6,444.81,442.9,443.26,255460000 +1993-05-05,443.98,446.09,443.76,444.52,274240000 +1993-05-04,442.58,445.19,442.45,444.05,268310000 +1993-05-03,440.19,442.59,438.25,442.46,224970000 +1993-04-30,438.89,442.29,438.89,440.19,247460000 +1993-04-29,438.02,438.96,435.59,438.89,249760000 +1993-04-28,438.01,438.8,436.68,438.02,267980000 +1993-04-27,433.52,438.02,433.14,438.01,284140000 +1993-04-26,437.03,438.35,432.3,433.54,283260000 +1993-04-23,439.49,439.49,436.82,437.03,259810000 +1993-04-22,443.55,445.73,439.46,439.46,310390000 +1993-04-21,445.09,445.77,443.08,443.63,287300000 +1993-04-20,447.46,447.46,441.81,445.1,317990000 +1993-04-19,448.94,449.14,445.85,447.46,244710000 +1993-04-16,448.41,449.39,447.67,448.94,305160000 +1993-04-15,448.6,449.11,446.39,448.4,259500000 +1993-04-14,449.22,450,448.02,448.66,257340000 +1993-04-13,448.41,450.4,447.66,449.22,286690000 +1993-04-12,441.84,448.37,441.84,448.37,259690000 +1993-04-08,442.71,443.77,440.02,441.84,284370000 +1993-04-07,441.16,442.73,440.5,442.73,300000000 +1993-04-06,442.29,443.38,439.48,441.16,293680000 +1993-04-05,441.42,442.43,440.53,442.29,296080000 +1993-04-02,450.28,450.28,440.71,441.39,323330000 +1993-04-01,451.67,452.63,449.6,450.3,234530000 +1993-03-31,451.97,454.88,451.67,451.67,279190000 +1993-03-30,450.79,452.06,449.63,451.97,231190000 +1993-03-29,447.76,452.81,447.75,450.77,199970000 +1993-03-26,450.91,452.09,447.69,447.78,226650000 +1993-03-25,448.09,451.75,447.93,450.88,251530000 +1993-03-24,448.71,450.9,446.1,448.07,274300000 +1993-03-23,448.88,449.8,448.3,448.76,232730000 +1993-03-22,450.17,450.17,446.08,448.88,233190000 +1993-03-19,451.9,453.32,449.91,450.18,339660000 +1993-03-18,448.36,452.39,448.36,451.89,241180000 +1993-03-17,451.36,451.36,447.99,448.31,241270000 +1993-03-16,451.43,452.36,451.01,451.37,218820000 +1993-03-15,449.83,451.43,449.4,451.43,195930000 +1993-03-12,453.7,453.7,447.04,449.83,255420000 +1993-03-11,456.35,456.76,453.48,453.72,257060000 +1993-03-10,454.4,456.34,452.7,456.33,255610000 +1993-03-09,454.67,455.52,453.68,454.4,290670000 +1993-03-08,446.12,454.71,446.12,454.71,275290000 +1993-03-05,447.34,449.59,445.56,446.11,253480000 +1993-03-04,449.26,449.52,446.72,447.34,234220000 +1993-03-03,447.9,450,447.73,449.26,277380000 +1993-03-02,442,447.91,441.07,447.9,269750000 +1993-03-01,443.38,444.18,441.34,442.01,232460000 +1993-02-26,442.34,443.77,440.98,443.38,234160000 +1993-02-25,440.7,442.34,439.67,442.34,252860000 +1993-02-24,434.76,440.87,434.68,440.87,316750000 +1993-02-23,435.34,436.84,432.41,434.8,329060000 +1993-02-22,434.21,436.49,433.53,435.24,311570000 +1993-02-19,431.93,434.26,431.68,434.22,310700000 +1993-02-18,433.3,437.79,428.25,431.9,311180000 +1993-02-17,433.93,433.97,430.92,433.3,302210000 +1993-02-16,444.53,444.53,433.47,433.91,332850000 +1993-02-12,447.66,447.7,444.58,444.58,216810000 +1993-02-11,446.21,449.36,446.21,447.66,257190000 +1993-02-10,445.33,446.37,444.24,446.23,251910000 +1993-02-09,448.04,448.04,444.52,445.33,240410000 +1993-02-08,448.94,450.04,447.7,447.85,243400000 +1993-02-05,449.56,449.56,446.95,448.93,324710000 +1993-02-04,447.2,449.86,447.2,449.56,351140000 +1993-02-03,442.56,447.35,442.56,447.2,345410000 +1993-02-02,442.52,442.87,440.76,442.55,271560000 +1993-02-01,438.78,442.52,438.78,442.52,238570000 +1993-01-29,438.67,438.93,436.91,438.78,247200000 +1993-01-28,438.13,439.14,437.3,438.66,256980000 +1993-01-27,439.95,440.04,436.82,438.11,277020000 +1993-01-26,440.05,442.66,439.54,439.95,314110000 +1993-01-25,436.11,440.53,436.11,440.01,288740000 +1993-01-22,435.49,437.81,435.49,436.11,293320000 +1993-01-21,433.37,435.75,432.48,435.49,257620000 +1993-01-20,435.14,436.23,433.37,433.37,268790000 +1993-01-19,436.84,437.7,434.59,435.13,283240000 +1993-01-18,437.13,437.13,435.92,436.84,196030000 +1993-01-15,435.87,439.49,435.84,437.15,309720000 +1993-01-14,433.08,435.96,433.08,435.94,281040000 +1993-01-13,431.03,433.44,429.99,433.03,245360000 +1993-01-12,430.95,431.39,428.19,431.04,239410000 +1993-01-11,429.04,431.04,429.01,430.95,217150000 +1993-01-08,430.73,430.73,426.88,429.05,263470000 +1993-01-07,434.52,435.46,429.76,430.73,304850000 +1993-01-06,434.34,435.17,432.52,434.52,295240000 +1993-01-05,435.38,435.4,433.55,434.34,240350000 +1993-01-04,435.7,437.32,434.48,435.38,201210000 +1992-12-31,438.82,439.59,435.71,435.71,165910000 +1992-12-30,437.98,439.37,437.12,438.82,183930000 +1992-12-29,439.15,442.65,437.6,437.98,213660000 +1992-12-28,439.77,439.77,437.26,439.15,143970000 +1992-12-24,439.03,439.81,439.03,439.77,95240000 +1992-12-23,440.29,441.11,439.03,439.03,234140000 +1992-12-22,440.7,441.64,438.25,440.31,250430000 +1992-12-21,441.26,441.26,439.65,440.7,224680000 +1992-12-18,435.46,441.29,435.46,441.28,389300000 +1992-12-17,431.52,435.44,431.46,435.43,251640000 +1992-12-16,432.58,434.22,430.88,431.52,242130000 +1992-12-15,432.82,433.66,431.92,432.57,227770000 +1992-12-14,433.73,435.26,432.83,432.84,187040000 +1992-12-11,434.64,434.64,433.34,433.73,164510000 +1992-12-10,435.66,435.66,432.65,434.64,240640000 +1992-12-09,436.99,436.99,433.98,435.65,230060000 +1992-12-08,435.31,436.99,434.68,436.99,234330000 +1992-12-07,432.06,435.31,432.06,435.31,217700000 +1992-12-04,429.93,432.89,429.74,432.06,234960000 +1992-12-03,429.98,430.99,428.8,429.91,238050000 +1992-12-02,430.78,430.87,428.61,429.89,247010000 +1992-12-01,431.35,431.47,429.2,430.78,259050000 +1992-11-30,430.19,431.53,429.36,431.35,230150000 +1992-11-27,429.19,431.93,429.17,430.16,106020000 +1992-11-25,427.59,429.41,427.58,429.19,207700000 +1992-11-24,425.14,429.31,424.83,427.59,241540000 +1992-11-23,426.65,426.65,424.95,425.12,192530000 +1992-11-20,423.61,426.98,423.61,426.65,257460000 +1992-11-19,422.86,423.61,422.5,423.61,218720000 +1992-11-18,419.27,423.49,419.24,422.85,219080000 +1992-11-17,420.63,420.97,418.31,419.27,187660000 +1992-11-16,422.44,422.44,420.35,420.68,173600000 +1992-11-13,422.89,422.91,421.04,422.43,192950000 +1992-11-12,422.2,423.1,421.7,422.87,226010000 +1992-11-11,418.62,422.33,418.4,422.2,243750000 +1992-11-10,418.59,419.71,417.98,418.62,223180000 +1992-11-09,417.58,420.13,416.79,418.59,197560000 +1992-11-06,418.35,418.35,417.01,417.58,205310000 +1992-11-05,417.08,418.4,415.58,418.34,219730000 +1992-11-04,419.91,421.07,416.61,417.11,194400000 +1992-11-03,422.75,422.81,418.59,419.92,208140000 +1992-11-02,418.66,422.75,418.12,422.75,203280000 +1992-10-30,420.86,421.13,418.54,418.68,201930000 +1992-10-29,420.15,421.16,419.83,420.86,206550000 +1992-10-28,418.49,420.13,417.56,420.13,203910000 +1992-10-27,418.18,419.2,416.97,418.49,201730000 +1992-10-26,414.09,418.17,413.71,418.16,188060000 +1992-10-23,414.9,416.23,413.68,414.1,199060000 +1992-10-22,415.67,416.81,413.1,414.9,216400000 +1992-10-21,415.53,416.15,414.54,415.67,219100000 +1992-10-20,414.98,417.98,414.49,415.48,258210000 +1992-10-19,411.73,414.98,410.66,414.98,222150000 +1992-10-16,409.6,411.73,407.43,411.73,235920000 +1992-10-15,409.34,411.03,407.92,409.6,213590000 +1992-10-14,409.3,411.52,407.86,409.37,175900000 +1992-10-13,407.44,410.64,406.83,409.3,186650000 +1992-10-12,402.66,407.44,402.66,407.44,126670000 +1992-10-09,407.75,407.75,402.42,402.66,178940000 +1992-10-08,404.29,408.04,404.29,407.75,205000000 +1992-10-07,407.17,408.6,403.91,404.25,184380000 +1992-10-06,407.57,408.56,404.84,407.18,203500000 +1992-10-05,410.47,410.47,396.8,407.57,286550000 +1992-10-02,416.29,416.35,410.45,410.47,188030000 +1992-10-01,417.8,418.67,415.46,416.29,204780000 +1992-09-30,416.79,418.58,416.67,417.8,184470000 +1992-09-29,416.62,417.38,415.34,416.8,170750000 +1992-09-28,414.35,416.62,413,416.62,158760000 +1992-09-25,418.47,418.63,412.71,414.35,213670000 +1992-09-24,417.46,419.01,417.46,418.47,187960000 +1992-09-23,417.14,417.88,416,417.44,205700000 +1992-09-22,422.14,422.14,417.13,417.14,188810000 +1992-09-21,422.9,422.9,421.18,422.14,153940000 +1992-09-18,419.92,422.93,419.92,422.93,237440000 +1992-09-17,419.92,421.43,419.62,419.93,188270000 +1992-09-16,419.71,422.44,417.77,419.92,231450000 +1992-09-15,425.22,425.22,419.54,419.77,211860000 +1992-09-14,419.65,425.27,419.65,425.27,250940000 +1992-09-11,419.95,420.58,419.13,419.58,180560000 +1992-09-10,416.34,420.52,416.34,419.95,221990000 +1992-09-09,414.44,416.44,414.44,416.36,178800000 +1992-09-08,417.08,417.18,414.3,414.44,161440000 +1992-09-04,417.98,418.62,416.76,417.08,124380000 +1992-09-03,417.98,420.31,417.49,417.98,212500000 +1992-09-02,416.07,418.28,415.31,417.98,187480000 +1992-09-01,414.03,416.07,413.35,416.07,172680000 +1992-08-31,414.87,415.29,413.76,414.03,161480000 +1992-08-28,413.54,414.95,413.38,414.84,152260000 +1992-08-27,413.51,415.83,413.51,413.53,178600000 +1992-08-26,411.65,413.61,410.53,413.51,171860000 +1992-08-25,410.73,411.64,408.3,411.61,202760000 +1992-08-24,414.8,414.8,410.07,410.72,165690000 +1992-08-21,418.27,420.35,413.58,414.85,204800000 +1992-08-20,418.19,418.85,416.93,418.26,183420000 +1992-08-19,421.34,421.62,418.19,418.19,187070000 +1992-08-18,420.74,421.4,419.78,421.34,171750000 +1992-08-17,419.89,421.89,419.44,420.74,152830000 +1992-08-14,417.74,420.4,417.74,419.91,166820000 +1992-08-13,417.78,419.88,416.4,417.73,185750000 +1992-08-12,418.89,419.75,416.43,417.78,176560000 +1992-08-11,419.45,419.72,416.53,418.9,173940000 +1992-08-10,418.87,419.42,417.04,419.42,142480000 +1992-08-07,420.59,423.45,418.51,418.88,190640000 +1992-08-06,422.19,422.36,420.26,420.59,181440000 +1992-08-05,424.35,424.35,421.92,422.19,172450000 +1992-08-04,425.09,425.14,423.1,424.36,166760000 +1992-08-03,424.19,425.09,422.84,425.09,164460000 +1992-07-31,423.92,424.8,422.46,424.21,172920000 +1992-07-30,422.2,423.94,421.57,423.92,193410000 +1992-07-29,417.52,423.02,417.52,422.23,275850000 +1992-07-28,411.55,417.55,411.55,417.52,218060000 +1992-07-27,411.6,412.67,411.27,411.54,164700000 +1992-07-24,412.07,412.07,409.93,411.6,163890000 +1992-07-23,410.93,412.08,409.81,412.08,175490000 +1992-07-22,413.74,413.74,409.95,410.93,190160000 +1992-07-21,413.75,414.92,413.1,413.76,173760000 +1992-07-20,415.62,415.62,410.72,413.75,165760000 +1992-07-17,417.54,417.54,412.96,415.62,192120000 +1992-07-16,417.04,417.93,414.79,417.54,206900000 +1992-07-15,417.68,417.81,416.29,417.1,206560000 +1992-07-14,414.86,417.69,414.33,417.68,195570000 +1992-07-13,414.62,415.86,413.93,414.87,148870000 +1992-07-10,414.23,415.88,413.34,414.62,164770000 +1992-07-09,410.28,414.69,410.26,414.23,207980000 +1992-07-08,409.15,410.28,407.2,410.28,201030000 +1992-07-07,413.83,415.33,408.58,409.16,226050000 +1992-07-06,411.77,413.84,410.46,413.84,186920000 +1992-07-02,412.88,415.71,410.07,411.77,220200000 +1992-07-01,408.2,412.88,408.2,412.88,214250000 +1992-06-30,408.94,409.63,407.85,408.14,195530000 +1992-06-29,403.47,408.96,403.47,408.94,176750000 +1992-06-26,403.12,403.51,401.94,403.45,154430000 +1992-06-25,403.83,405.53,402.01,403.12,182960000 +1992-06-24,404.05,404.76,403.26,403.84,193870000 +1992-06-23,403.4,405.41,403.4,404.04,189190000 +1992-06-22,403.64,403.64,399.92,403.4,169370000 +1992-06-19,400.96,404.23,400.96,403.67,233460000 +1992-06-18,402.26,402.68,400.51,400.96,225600000 +1992-06-17,408.33,408.33,401.98,402.26,227760000 +1992-06-16,410.29,411.4,408.32,408.32,194400000 +1992-06-15,409.76,411.68,408.13,410.29,164080000 +1992-06-12,409.08,411.86,409.08,409.76,181860000 +1992-06-11,407.25,409.05,406.11,409.05,204780000 +1992-06-10,410.06,410.1,406.81,407.25,210750000 +1992-06-09,413.4,413.56,409.3,410.06,191170000 +1992-06-08,413.48,413.95,412.03,413.36,161240000 +1992-06-05,413.26,413.85,410.97,413.48,199050000 +1992-06-04,414.6,414.98,412.97,413.26,204450000 +1992-06-03,413.5,416.54,413.04,414.59,215770000 +1992-06-02,417.3,417.3,413.5,413.5,202560000 +1992-06-01,415.35,417.3,412.44,417.3,180800000 +1992-05-29,416.74,418.36,415.35,415.35,204010000 +1992-05-28,412.17,416.77,411.81,416.74,195300000 +1992-05-27,411.41,412.68,411.06,412.17,182240000 +1992-05-26,414.02,414.02,410.23,411.41,197700000 +1992-05-22,412.61,414.82,412.6,414.02,146710000 +1992-05-21,415.4,415.41,411.57,412.6,184860000 +1992-05-20,416.37,416.83,415.37,415.39,198180000 +1992-05-19,412.82,416.51,412.26,416.37,187130000 +1992-05-18,410.13,413.34,410.13,412.81,151380000 +1992-05-15,413.14,413.14,409.85,410.09,192740000 +1992-05-14,416.45,416.52,411.82,413.14,189150000 +1992-05-13,416.29,417.04,415.86,416.45,175850000 +1992-05-12,418.49,418.68,414.69,416.29,192870000 +1992-05-11,416.05,418.75,416.05,418.49,155730000 +1992-05-08,415.87,416.85,414.41,416.05,168720000 +1992-05-07,416.79,416.84,415.38,415.85,168980000 +1992-05-06,416.84,418.48,416.4,416.79,199950000 +1992-05-05,416.91,418.53,415.77,416.84,200550000 +1992-05-04,412.54,417.84,412.54,416.91,174540000 +1992-05-01,414.95,415.21,409.87,412.53,177390000 +1992-04-30,412.02,414.95,412.02,414.95,223590000 +1992-04-29,409.11,412.31,409.11,412.02,206780000 +1992-04-28,408.45,409.69,406.33,409.11,189220000 +1992-04-27,409.03,409.6,407.64,408.45,172900000 +1992-04-24,411.6,412.48,408.74,409.02,199310000 +1992-04-23,409.81,411.6,406.86,411.6,235860000 +1992-04-22,410.26,411.3,409.23,409.81,218850000 +1992-04-21,410.16,411.09,408.2,410.26,214460000 +1992-04-20,416.05,416.05,407.93,410.18,191980000 +1992-04-16,416.28,416.28,413.4,416.04,233230000 +1992-04-15,412.39,416.28,412.39,416.28,229710000 +1992-04-14,406.08,413.86,406.08,412.39,231130000 +1992-04-13,404.28,406.08,403.9,406.08,143140000 +1992-04-10,400.59,405.12,400.59,404.29,199530000 +1992-04-09,394.5,401.04,394.5,400.64,231430000 +1992-04-08,398.05,398.05,392.41,394.5,249280000 +1992-04-07,405.59,405.75,397.97,398.06,205210000 +1992-04-06,401.54,405.93,401.52,405.59,179910000 +1992-04-03,400.5,401.59,398.21,401.55,188580000 +1992-04-02,404.17,404.63,399.28,400.5,185210000 +1992-04-01,403.67,404.5,400.75,404.23,186530000 +1992-03-31,403,405.21,402.22,403.69,182360000 +1992-03-30,403.5,404.3,402.97,403,133990000 +1992-03-27,407.86,407.86,402.87,403.5,166140000 +1992-03-26,407.52,409.44,406.75,407.86,176720000 +1992-03-25,408.88,409.87,407.52,407.52,192650000 +1992-03-24,409.91,411.43,407.99,408.88,191610000 +1992-03-23,411.29,411.29,408.87,409.91,157050000 +1992-03-20,409.8,411.3,408.53,411.3,246210000 +1992-03-19,409.15,410.57,409.12,409.8,197310000 +1992-03-18,409.58,410.84,408.23,409.15,191720000 +1992-03-17,406.39,409.72,406.39,409.58,187250000 +1992-03-16,405.85,406.4,403.55,406.39,155950000 +1992-03-13,403.92,406.69,403.92,405.84,177900000 +1992-03-12,404.03,404.72,401.94,403.89,180310000 +1992-03-11,406.88,407.02,402.64,404.03,186330000 +1992-03-10,405.21,409.16,405.21,406.89,203000000 +1992-03-09,404.45,405.64,404.25,405.21,160650000 +1992-03-06,406.51,407.51,403.65,404.44,185190000 +1992-03-05,409.33,409.33,405.42,406.51,205770000 +1992-03-04,412.86,413.27,409.33,409.33,206860000 +1992-03-03,412.45,413.78,411.88,412.85,200890000 +1992-03-02,412.68,413.74,411.52,412.45,180380000 +1992-02-28,413.86,416.07,411.8,412.7,202320000 +1992-02-27,415.35,415.99,413.47,413.86,215110000 +1992-02-26,410.48,415.35,410.48,415.35,241500000 +1992-02-25,412.27,412.27,408.02,410.45,210350000 +1992-02-24,411.46,412.94,410.34,412.27,177540000 +1992-02-21,413.9,414.26,409.72,411.43,261650000 +1992-02-20,408.26,413.9,408.26,413.9,270650000 +1992-02-19,407.38,408.7,406.54,408.26,232970000 +1992-02-18,412.48,413.27,406.34,407.38,234300000 +1992-02-14,413.69,413.84,411.2,412.48,215110000 +1992-02-13,417.13,417.77,412.07,413.69,229360000 +1992-02-12,413.77,418.08,413.36,417.13,237630000 +1992-02-11,413.77,414.38,412.24,413.76,200130000 +1992-02-10,411.07,413.77,411.07,413.77,184410000 +1992-02-07,413.82,415.29,408.04,411.09,231120000 +1992-02-06,413.87,414.55,411.93,413.82,242050000 +1992-02-05,413.88,416.17,413.18,413.84,262440000 +1992-02-04,409.6,413.85,409.28,413.85,233680000 +1992-02-03,408.79,409.95,407.45,409.53,185290000 +1992-01-31,411.65,412.63,408.64,408.78,197620000 +1992-01-30,410.34,412.17,409.26,411.62,194680000 +1992-01-29,414.96,417.83,409.17,410.34,248940000 +1992-01-28,414.98,416.41,414.54,414.96,218400000 +1992-01-27,415.44,416.84,414.48,414.99,190970000 +1992-01-24,414.96,417.27,414.29,415.48,213630000 +1992-01-23,418.13,419.78,414.36,414.96,234580000 +1992-01-22,412.65,418.13,412.49,418.13,228140000 +1992-01-21,416.36,416.39,411.32,412.64,218750000 +1992-01-20,418.86,418.86,415.8,416.36,180900000 +1992-01-17,418.2,419.45,416,418.86,287370000 +1992-01-16,420.77,420.85,415.37,418.21,336240000 +1992-01-15,420.45,421.18,418.79,420.77,314830000 +1992-01-14,414.34,420.44,414.32,420.44,265900000 +1992-01-13,415.05,415.36,413.54,414.34,200270000 +1992-01-10,417.62,417.62,413.31,415.1,236130000 +1992-01-09,418.09,420.5,415.85,417.61,292350000 +1992-01-08,417.36,420.23,415.02,418.1,290750000 +1992-01-07,417.96,417.96,415.2,417.4,252780000 +1992-01-06,419.31,419.44,416.92,417.96,251210000 +1992-01-03,417.27,419.79,416.16,419.34,224270000 +1992-01-02,417.03,417.27,411.04,417.26,207570000 +1991-12-31,415.14,418.32,412.73,417.09,247080000 +1991-12-30,406.49,415.14,406.49,415.14,245600000 +1991-12-27,404.84,406.58,404.59,406.46,157950000 +1991-12-26,399.33,404.92,399.31,404.84,149230000 +1991-12-24,396.82,401.79,396.82,399.33,162640000 +1991-12-23,387.05,397.44,386.96,396.82,228900000 +1991-12-20,382.52,388.24,382.52,387.04,316140000 +1991-12-19,383.46,383.46,380.64,382.52,199330000 +1991-12-18,382.74,383.51,380.88,383.48,192410000 +1991-12-17,384.46,385.05,382.6,382.74,191310000 +1991-12-16,384.48,385.84,384.37,384.46,173080000 +1991-12-13,381.55,385.04,381.55,384.47,194470000 +1991-12-12,377.7,381.62,377.7,381.55,192950000 +1991-12-11,377.9,379.42,374.78,377.7,207430000 +1991-12-10,378.26,379.57,376.64,377.9,192920000 +1991-12-09,379.09,381.42,377.67,378.26,174760000 +1991-12-06,377.39,382.39,375.41,379.1,199160000 +1991-12-05,380.07,380.07,376.58,377.39,166350000 +1991-12-04,380.96,381.51,378.07,380.07,187960000 +1991-12-03,381.4,381.48,379.92,380.96,187230000 +1991-12-02,375.11,381.4,371.36,381.4,188410000 +1991-11-29,376.55,376.55,374.65,375.22,76830000 +1991-11-27,377.96,378.11,375.98,376.55,167720000 +1991-11-26,375.34,378.29,371.63,377.96,213810000 +1991-11-25,376.14,377.07,374,375.34,175870000 +1991-11-22,380.05,380.05,374.52,376.14,188240000 +1991-11-21,378.53,381.12,377.41,380.06,195130000 +1991-11-20,379.42,381.51,377.84,378.53,192760000 +1991-11-19,385.24,385.24,374.9,379.42,243880000 +1991-11-18,382.62,385.4,379.7,385.24,241940000 +1991-11-15,397.15,397.16,382.62,382.62,239690000 +1991-11-14,397.41,398.22,395.85,397.15,200030000 +1991-11-13,396.74,397.42,394.01,397.41,184480000 +1991-11-12,393.12,397.13,393.12,396.74,198610000 +1991-11-11,392.9,393.57,392.32,393.12,128920000 +1991-11-08,393.72,396.43,392.42,392.89,183260000 +1991-11-07,389.97,393.72,389.97,393.72,205480000 +1991-11-06,388.71,389.97,387.58,389.97,167440000 +1991-11-05,390.28,392.17,388.19,388.71,172090000 +1991-11-04,391.29,391.29,388.09,390.28,155660000 +1991-11-01,392.46,395.1,389.67,391.32,205780000 +1991-10-31,392.96,392.96,391.58,392.45,179680000 +1991-10-30,391.48,393.11,390.78,392.96,195400000 +1991-10-29,389.52,391.7,386.88,391.48,192810000 +1991-10-28,384.2,389.52,384.2,389.52,161630000 +1991-10-25,385.07,386.13,382.97,384.2,167310000 +1991-10-24,387.94,388.32,383.45,385.07,179040000 +1991-10-23,387.83,389.08,386.52,387.94,185390000 +1991-10-22,390.02,391.2,387.4,387.83,194160000 +1991-10-21,392.49,392.49,388.96,390.02,154140000 +1991-10-18,391.92,392.8,391.77,392.5,204090000 +1991-10-17,392.79,393.81,390.32,391.92,206030000 +1991-10-16,391.01,393.29,390.14,392.8,225380000 +1991-10-15,386.47,391.5,385.95,391.01,213540000 +1991-10-14,381.45,386.47,381.45,386.47,130120000 +1991-10-11,380.55,381.46,379.9,381.45,148850000 +1991-10-10,376.8,380.55,376.11,380.55,164240000 +1991-10-09,380.57,380.57,376.35,376.8,186710000 +1991-10-08,379.5,381.23,379.18,380.67,177120000 +1991-10-07,381.22,381.27,379.07,379.5,148430000 +1991-10-04,384.47,385.19,381.24,381.25,164000000 +1991-10-03,388.23,388.23,384.47,384.47,174360000 +1991-10-02,389.2,390.03,387.62,388.26,166380000 +1991-10-01,387.86,389.56,387.86,389.2,163570000 +1991-09-30,385.91,388.29,384.32,387.86,146780000 +1991-09-27,386.49,389.09,384.87,385.9,160660000 +1991-09-26,386.87,388.39,385.3,386.49,158980000 +1991-09-25,387.72,388.25,385.99,386.88,153910000 +1991-09-24,385.92,388.13,384.46,387.71,170350000 +1991-09-23,387.9,388.55,385.76,385.92,145940000 +1991-09-20,387.56,388.82,386.49,387.92,254520000 +1991-09-19,386.94,389.42,386.27,387.56,211010000 +1991-09-18,385.49,386.94,384.28,386.94,141340000 +1991-09-17,385.78,387.13,384.97,385.5,168340000 +1991-09-16,383.59,385.79,382.77,385.78,172560000 +1991-09-13,387.16,387.95,382.85,383.59,169630000 +1991-09-12,385.09,387.34,385.09,387.34,160420000 +1991-09-11,384.56,385.6,383.59,385.09,148000000 +1991-09-10,388.57,388.63,383.78,384.56,143390000 +1991-09-09,389.11,389.34,387.88,388.57,115100000 +1991-09-06,389.14,390.71,387.36,389.1,166560000 +1991-09-05,389.97,390.97,388.49,389.14,162380000 +1991-09-04,392.15,392.62,388.68,389.97,157520000 +1991-09-03,395.43,397.62,392.1,392.15,153600000 +1991-08-30,396.47,396.47,393.6,395.43,143440000 +1991-08-29,396.65,396.82,395.14,396.47,154150000 +1991-08-28,393.06,396.64,393.05,396.64,169890000 +1991-08-27,393.85,393.87,391.77,393.06,144670000 +1991-08-26,394.17,394.39,392.75,393.85,130570000 +1991-08-23,391.33,395.34,390.69,394.17,188870000 +1991-08-22,390.59,391.98,390.21,391.33,173090000 +1991-08-21,379.55,390.59,379.55,390.59,232690000 +1991-08-20,376.47,380.35,376.47,379.43,184260000 +1991-08-19,385.58,385.58,374.09,376.47,230350000 +1991-08-16,389.33,390.41,383.16,385.58,189480000 +1991-08-15,389.91,391.92,389.29,389.33,174690000 +1991-08-14,389.62,391.85,389.13,389.9,124230000 +1991-08-13,388.02,392.12,388.02,389.62,212760000 +1991-08-12,387.11,388.17,385.9,388.02,145440000 +1991-08-09,389.32,389.89,387.04,387.12,143740000 +1991-08-08,390.56,391.8,388.15,389.32,163890000 +1991-08-07,390.62,391.59,389.86,390.56,172220000 +1991-08-06,385.06,390.8,384.29,390.62,174460000 +1991-08-05,387.17,387.17,384.48,385.06,128050000 +1991-08-02,387.12,389.56,386.05,387.18,162270000 +1991-08-01,387.81,387.95,386.48,387.12,170610000 +1991-07-31,386.69,387.81,386.19,387.81,166830000 +1991-07-30,383.15,386.92,383.15,386.69,169010000 +1991-07-29,380.93,383.15,380.45,383.15,136000000 +1991-07-26,380.96,381.76,379.81,380.93,127760000 +1991-07-25,378.64,381.13,378.15,380.96,145800000 +1991-07-24,379.42,380.46,378.29,378.64,158700000 +1991-07-23,382.88,384.86,379.39,379.42,160190000 +1991-07-22,384.21,384.55,381.84,382.88,149050000 +1991-07-19,385.38,385.83,383.65,384.22,190700000 +1991-07-18,381.18,385.37,381.18,385.37,200930000 +1991-07-17,381.5,382.86,381.13,381.18,195460000 +1991-07-16,382.39,382.94,380.8,381.54,182990000 +1991-07-15,380.28,383,380.24,382.39,161750000 +1991-07-12,376.97,381.41,375.79,380.25,174770000 +1991-07-11,375.73,377.68,375.51,376.97,157930000 +1991-07-10,376.11,380.35,375.2,375.74,178290000 +1991-07-09,377.94,378.58,375.37,376.11,151820000 +1991-07-08,374.09,377.94,370.92,377.94,138330000 +1991-07-05,373.34,375.51,372.17,374.08,69910000 +1991-07-03,377.47,377.47,372.08,373.33,140580000 +1991-07-02,377.92,377.93,376.62,377.47,157290000 +1991-07-01,371.18,377.92,371.18,377.92,167480000 +1991-06-28,374.4,374.4,367.98,371.16,163770000 +1991-06-27,371.59,374.4,371.59,374.4,163080000 +1991-06-26,370.65,372.73,368.34,371.59,187170000 +1991-06-25,370.94,372.62,369.56,370.65,155710000 +1991-06-24,377.74,377.74,370.73,370.94,137940000 +1991-06-21,375.42,377.75,375.33,377.75,193310000 +1991-06-20,375.09,376.29,373.87,375.42,163980000 +1991-06-19,378.57,378.57,374.36,375.09,156440000 +1991-06-18,380.13,381.83,377.99,378.59,155200000 +1991-06-17,382.3,382.31,380.13,380.13,134230000 +1991-06-14,377.63,382.3,377.63,382.29,167950000 +1991-06-13,376.65,377.9,376.08,377.63,145650000 +1991-06-12,381.05,381.05,374.46,376.65,166140000 +1991-06-11,378.57,381.63,378.57,381.05,161610000 +1991-06-10,379.43,379.75,377.95,378.57,127720000 +1991-06-07,383.63,383.63,378.76,379.43,169570000 +1991-06-06,385.1,385.85,383.13,383.63,168260000 +1991-06-05,387.74,388.23,384.45,385.09,186560000 +1991-06-04,388.06,388.06,385.14,387.74,180450000 +1991-06-03,389.81,389.81,386.97,388.06,173990000 +1991-05-31,386.96,389.85,385.01,389.83,232040000 +1991-05-30,382.79,388.17,382.5,386.96,234440000 +1991-05-29,381.94,383.66,381.37,382.79,188450000 +1991-05-28,377.49,382.1,377.12,381.94,162350000 +1991-05-24,374.97,378.08,374.97,377.49,124640000 +1991-05-23,376.19,378.07,373.55,374.96,173080000 +1991-05-22,375.35,376.5,374.4,376.19,159310000 +1991-05-21,372.28,376.66,372.28,375.35,176620000 +1991-05-20,372.39,373.65,371.26,372.28,109510000 +1991-05-17,372.19,373.01,369.44,372.39,174210000 +1991-05-16,368.57,372.51,368.57,372.19,154460000 +1991-05-15,371.55,372.47,365.83,368.57,193110000 +1991-05-14,375.51,375.53,370.82,371.62,207890000 +1991-05-13,375.74,377.02,374.62,376.76,129620000 +1991-05-10,383.26,383.91,375.61,375.74,172730000 +1991-05-09,378.51,383.56,378.51,383.25,180460000 +1991-05-08,377.33,379.26,376.21,378.51,157240000 +1991-05-07,380.08,380.91,377.31,377.32,153290000 +1991-05-06,380.78,380.78,377.86,380.08,129110000 +1991-05-03,380.52,381,378.82,380.8,158150000 +1991-05-02,380.29,382.14,379.82,380.52,187090000 +1991-05-01,375.35,380.46,375.27,380.29,181900000 +1991-04-30,373.66,377.86,373.01,375.34,206230000 +1991-04-29,379.01,380.96,373.66,373.66,149860000 +1991-04-26,379.25,380.11,376.77,379.02,154550000 +1991-04-25,382.89,382.89,378.43,379.25,166940000 +1991-04-24,381.76,383.02,379.99,382.76,166800000 +1991-04-23,380.95,383.55,379.67,381.76,167840000 +1991-04-22,384.19,384.19,380.16,380.95,164410000 +1991-04-19,388.46,388.46,383.9,384.2,195520000 +1991-04-18,390.45,390.97,388.13,388.46,217410000 +1991-04-17,387.62,391.26,387.3,390.45,246930000 +1991-04-16,381.19,387.62,379.64,387.62,214480000 +1991-04-15,380.4,382.32,378.78,381.19,161800000 +1991-04-12,377.65,381.07,376.89,380.4,198610000 +1991-04-11,373.15,379.53,373.15,377.63,196570000 +1991-04-10,373.57,374.83,371.21,373.15,167940000 +1991-04-09,378.65,379.02,373.11,373.56,169940000 +1991-04-08,375.35,378.76,374.69,378.66,138580000 +1991-04-05,379.78,381.12,374.15,375.36,187410000 +1991-04-04,378.94,381.88,377.05,379.77,198120000 +1991-04-03,379.5,381.56,378.49,378.94,213720000 +1991-04-02,371.3,379.5,371.3,379.5,189530000 +1991-04-01,375.22,375.22,370.27,371.3,144010000 +1991-03-28,375.35,376.6,374.4,375.22,150750000 +1991-03-27,376.28,378.48,374.73,375.35,201830000 +1991-03-26,369.83,376.3,369.37,376.3,198720000 +1991-03-25,367.48,371.31,367.46,369.83,153920000 +1991-03-22,366.58,368.22,365.58,367.48,160890000 +1991-03-21,367.94,371.01,366.51,366.58,199830000 +1991-03-20,366.59,368.85,365.8,367.92,196810000 +1991-03-19,372.11,372.11,366.54,366.59,177070000 +1991-03-18,373.59,374.09,369.46,372.11,163100000 +1991-03-15,373.5,374.58,370.21,373.59,237650000 +1991-03-14,374.59,378.28,371.76,373.5,232070000 +1991-03-13,370.03,374.65,370.03,374.57,176000000 +1991-03-12,372.96,374.35,369.55,370.03,176440000 +1991-03-11,374.94,375.1,372.52,372.96,161600000 +1991-03-08,375.91,378.69,374.43,374.95,206850000 +1991-03-07,376.16,377.49,375.58,375.91,197060000 +1991-03-06,376.72,379.66,375.02,376.17,262290000 +1991-03-05,369.33,377.89,369.33,376.72,253700000 +1991-03-04,370.47,371.99,369.07,369.33,199830000 +1991-03-01,367.07,370.47,363.73,370.47,221510000 +1991-02-28,367.73,369.91,365.95,367.07,223010000 +1991-02-27,362.81,368.38,362.81,367.74,211410000 +1991-02-26,367.26,367.26,362.19,362.81,164170000 +1991-02-25,365.65,370.19,365.16,367.26,193820000 +1991-02-22,364.97,370.96,364.23,365.65,218760000 +1991-02-21,365.14,366.79,364.5,364.97,180770000 +1991-02-20,369.37,369.37,364.38,365.14,185680000 +1991-02-19,369.06,370.11,367.05,369.39,189900000 +1991-02-15,364.23,369.49,364.23,369.06,228480000 +1991-02-14,369.02,370.26,362.77,364.22,230750000 +1991-02-13,365.5,369.49,364.64,369.02,209960000 +1991-02-12,368.58,370.54,365.5,365.5,256160000 +1991-02-11,359.36,368.58,359.32,368.58,265350000 +1991-02-08,356.52,359.35,356.02,359.35,187830000 +1991-02-07,358.07,363.43,355.53,356.52,292190000 +1991-02-06,351.26,358.07,349.58,358.07,276940000 +1991-02-05,348.34,351.84,347.21,351.26,290570000 +1991-02-04,343.05,348.71,342.96,348.34,250750000 +1991-02-01,343.91,344.9,340.37,343.05,246670000 +1991-01-31,340.92,343.93,340.47,343.93,204520000 +1991-01-30,335.8,340.91,335.71,340.91,226790000 +1991-01-29,336.03,336.03,334.26,335.84,155740000 +1991-01-28,336.06,337.41,335.81,336.03,141270000 +1991-01-25,334.78,336.92,334.2,336.07,194350000 +1991-01-24,330.21,335.83,330.19,334.78,223150000 +1991-01-23,328.3,331.04,327.93,330.21,168620000 +1991-01-22,331.06,331.26,327.83,328.31,177060000 +1991-01-21,332.23,332.23,328.87,331.06,136290000 +1991-01-18,327.93,332.23,327.08,332.23,226770000 +1991-01-17,316.25,327.97,316.25,327.97,319080000 +1991-01-16,313.73,316.94,312.94,316.17,134560000 +1991-01-15,312.49,313.73,311.84,313.73,110000000 +1991-01-14,315.23,315.23,309.35,312.49,120830000 +1991-01-11,314.53,315.24,313.59,315.23,123050000 +1991-01-10,311.51,314.77,311.51,314.53,124510000 +1991-01-09,314.9,320.73,310.93,311.49,191100000 +1991-01-08,315.44,316.97,313.79,314.9,143390000 +1991-01-07,320.97,320.97,315.44,315.44,130610000 +1991-01-04,321.91,322.35,318.87,321,140820000 +1991-01-03,326.46,326.53,321.9,321.91,141450000 +1991-01-02,330.2,330.75,326.45,326.45,126280000 +1990-12-31,328.71,330.23,327.5,330.22,114130000 +1990-12-28,328.29,328.72,327.24,328.72,111030000 +1990-12-27,330.85,331.04,328.23,328.29,102900000 +1990-12-26,329.89,331.69,329.89,330.85,78730000 +1990-12-24,331.74,331.74,329.16,329.9,57200000 +1990-12-21,330.12,332.47,330.12,331.75,233400000 +1990-12-20,330.2,330.74,326.94,330.12,174700000 +1990-12-19,330.04,330.8,329.39,330.2,180380000 +1990-12-18,326.02,330.43,325.75,330.05,176460000 +1990-12-17,326.82,326.82,324.46,326.02,118560000 +1990-12-14,329.34,329.34,325.16,326.82,151010000 +1990-12-13,330.14,330.58,328.77,329.34,162110000 +1990-12-12,326.44,330.36,326.44,330.19,182270000 +1990-12-11,328.88,328.88,325.65,326.44,145330000 +1990-12-10,327.75,328.97,326.15,328.89,138650000 +1990-12-07,329.09,329.39,326.39,327.75,164950000 +1990-12-06,329.94,333.98,328.37,329.07,256380000 +1990-12-05,326.36,329.92,325.66,329.92,205820000 +1990-12-04,324.11,326.77,321.97,326.35,185820000 +1990-12-03,322.23,324.9,322.23,324.1,177000000 +1990-11-30,316.42,323.02,315.42,322.22,192350000 +1990-11-29,317.95,317.95,315.03,316.42,140920000 +1990-11-28,318.11,319.96,317.62,317.95,145490000 +1990-11-27,316.51,318.69,315.8,318.1,147590000 +1990-11-26,315.08,316.51,311.48,316.51,131540000 +1990-11-23,316.03,317.3,315.06,315.1,63350000 +1990-11-21,315.31,316.15,312.42,316.03,140660000 +1990-11-20,319.34,319.34,315.31,315.31,161170000 +1990-11-19,317.15,319.39,317.15,319.34,140950000 +1990-11-16,317.02,318.8,314.99,317.12,165440000 +1990-11-15,320.4,320.4,316.13,317.02,151370000 +1990-11-14,317.66,321.7,317.23,320.4,179310000 +1990-11-13,319.48,319.48,317.26,317.67,160240000 +1990-11-12,313.74,319.77,313.73,319.48,161390000 +1990-11-09,307.61,313.78,307.61,313.74,145160000 +1990-11-08,306.01,309.77,305.03,307.61,155570000 +1990-11-07,311.62,311.62,305.79,306.01,149130000 +1990-11-06,314.59,314.76,311.43,311.62,142660000 +1990-11-05,311.85,314.61,311.41,314.59,147510000 +1990-11-02,307.02,311.94,306.88,311.85,168700000 +1990-11-01,303.99,307.27,301.61,307.02,159270000 +1990-10-31,304.06,305.7,302.33,304,156060000 +1990-10-30,301.88,304.36,299.44,304.06,153450000 +1990-10-29,304.74,307.41,300.69,301.88,133980000 +1990-10-26,310.17,310.17,304.71,304.71,130190000 +1990-10-25,312.6,313.71,309.7,310.17,141460000 +1990-10-24,312.36,313.51,310.74,312.6,149290000 +1990-10-23,314.76,315.06,312.06,312.36,146300000 +1990-10-22,312.48,315.83,310.47,314.76,152650000 +1990-10-19,305.74,312.48,305.74,312.48,221480000 +1990-10-18,298.75,305.74,298.75,305.74,204110000 +1990-10-17,298.92,301.5,297.79,298.76,161260000 +1990-10-16,303.23,304.34,298.12,298.92,149570000 +1990-10-15,300.03,304.79,296.41,303.23,164980000 +1990-10-12,295.45,301.68,295.22,300.03,187940000 +1990-10-11,300.39,301.45,294.51,295.46,180060000 +1990-10-10,305.09,306.43,299.21,300.39,169190000 +1990-10-09,313.46,313.46,305.09,305.1,145610000 +1990-10-08,311.5,315.03,311.5,313.48,99470000 +1990-10-05,312.69,314.79,305.76,311.5,153380000 +1990-10-04,311.4,313.4,308.59,312.69,145410000 +1990-10-03,315.21,316.26,310.7,311.4,135490000 +1990-10-02,314.94,319.69,314.94,315.21,188360000 +1990-10-01,306.1,314.94,306.1,314.94,202210000 +1990-09-28,300.97,306.05,295.98,306.05,201010000 +1990-09-27,305.06,307.47,299.1,300.97,182690000 +1990-09-26,308.26,308.28,303.05,305.06,155570000 +1990-09-25,305.46,308.27,304.23,308.26,155940000 +1990-09-24,311.3,311.3,303.58,304.59,164070000 +1990-09-21,311.53,312.17,307.98,311.32,201050000 +1990-09-20,316.6,316.6,310.55,311.48,145100000 +1990-09-19,318.6,319.35,316.25,316.6,147530000 +1990-09-18,317.77,318.85,314.27,318.6,141130000 +1990-09-17,316.83,318.05,315.21,317.77,110600000 +1990-09-14,318.65,318.65,314.76,316.83,133390000 +1990-09-13,322.51,322.51,318.02,318.65,123390000 +1990-09-12,321.04,322.55,319.6,322.54,129890000 +1990-09-11,321.63,322.18,319.6,321.04,113220000 +1990-09-10,323.42,326.53,320.31,321.63,119730000 +1990-09-07,320.46,324.18,319.71,323.4,123800000 +1990-09-06,324.39,324.39,319.37,320.46,125620000 +1990-09-05,323.09,324.52,320.99,324.39,120610000 +1990-09-04,322.56,323.09,319.11,323.09,92940000 +1990-08-31,318.71,322.57,316.59,322.56,96480000 +1990-08-30,324.19,324.57,317.82,318.71,120890000 +1990-08-29,321.34,325.83,320.87,324.19,134240000 +1990-08-28,321.44,322.2,320.25,321.34,127660000 +1990-08-27,311.55,323.11,311.55,321.44,160150000 +1990-08-24,307.06,311.65,306.18,311.51,199040000 +1990-08-23,316.55,316.55,306.56,307.06,250440000 +1990-08-22,321.86,324.15,316.55,316.55,175550000 +1990-08-21,328.51,328.51,318.78,321.86,194630000 +1990-08-20,327.83,329.9,327.07,328.51,129630000 +1990-08-17,332.36,332.36,324.63,327.83,212560000 +1990-08-16,340.06,340.06,332.39,332.39,138850000 +1990-08-15,339.39,341.92,339.38,340.06,136710000 +1990-08-14,338.84,340.96,337.19,339.39,130320000 +1990-08-13,335.39,338.88,332.02,338.84,122820000 +1990-08-10,339.9,339.9,334.22,335.52,145340000 +1990-08-09,338.35,340.56,337.56,339.94,155810000 +1990-08-08,334.83,339.21,334.83,338.35,190400000 +1990-08-07,334.43,338.63,332.22,334.83,231580000 +1990-08-06,344.86,344.86,333.27,334.43,240400000 +1990-08-03,351.48,351.48,338.2,344.86,295880000 +1990-08-02,355.52,355.52,349.73,351.48,253090000 +1990-08-01,356.15,357.35,353.82,355.52,178260000 +1990-07-31,355.55,357.54,353.91,356.15,175380000 +1990-07-30,353.44,355.55,351.15,355.55,146470000 +1990-07-27,355.9,355.94,352.14,353.44,149070000 +1990-07-26,357.09,357.47,353.95,355.91,155040000 +1990-07-25,355.79,357.52,354.8,357.09,163530000 +1990-07-24,355.31,356.09,351.46,355.79,181920000 +1990-07-23,361.61,361.61,350.09,355.31,209030000 +1990-07-20,365.32,366.64,361.58,361.61,177810000 +1990-07-19,364.22,365.32,361.29,365.32,161990000 +1990-07-18,367.52,367.52,362.95,364.22,168760000 +1990-07-17,368.95,369.4,364.99,367.52,176790000 +1990-07-16,367.31,369.78,367.31,368.95,149430000 +1990-07-13,365.45,369.68,365.45,367.31,215600000 +1990-07-12,361.23,365.46,360.57,365.44,213180000 +1990-07-11,356.49,361.23,356.49,361.23,162220000 +1990-07-10,359.52,359.74,356.41,356.49,147630000 +1990-07-09,358.42,360.05,358.11,359.52,119390000 +1990-07-06,355.69,359.02,354.64,358.42,111730000 +1990-07-05,360.16,360.16,354.86,355.68,128320000 +1990-07-03,359.54,360.73,359.44,360.16,130050000 +1990-07-02,358.02,359.58,357.54,359.54,130200000 +1990-06-29,357.64,359.09,357.3,358.02,145510000 +1990-06-28,355.16,357.63,355.16,357.63,136120000 +1990-06-27,352.06,355.89,351.23,355.14,146620000 +1990-06-26,352.32,356.09,351.85,352.06,141420000 +1990-06-25,355.42,356.41,351.91,352.31,133100000 +1990-06-22,360.52,363.2,355.31,355.43,172570000 +1990-06-21,359.1,360.88,357.63,360.47,138570000 +1990-06-20,358.47,359.91,357,359.1,137420000 +1990-06-19,356.88,358.9,356.18,358.47,134930000 +1990-06-18,362.91,362.91,356.88,356.88,133470000 +1990-06-15,362.89,363.14,360.71,362.91,205130000 +1990-06-14,364.9,364.9,361.64,362.9,135770000 +1990-06-13,366.25,367.09,364.51,364.9,158910000 +1990-06-12,361.63,367.27,361.15,366.25,157100000 +1990-06-11,358.71,361.63,357.7,361.63,119550000 +1990-06-08,363.15,363.49,357.68,358.71,142600000 +1990-06-07,365.92,365.92,361.6,363.15,160360000 +1990-06-06,366.64,366.64,364.42,364.96,164030000 +1990-06-05,367.4,368.78,365.49,366.64,199720000 +1990-06-04,363.16,367.85,362.43,367.4,175520000 +1990-06-01,361.26,363.52,361.21,363.16,187860000 +1990-05-31,360.86,361.84,360.23,361.23,165690000 +1990-05-30,360.65,362.26,360,360.86,199540000 +1990-05-29,354.58,360.65,354.55,360.65,137410000 +1990-05-25,358.41,358.41,354.32,354.58,120250000 +1990-05-24,359.29,359.56,357.87,358.41,155140000 +1990-05-23,358.43,359.29,356.99,359.29,172330000 +1990-05-22,358,360.5,356.09,358.43,203350000 +1990-05-21,354.64,359.07,353.78,358,166280000 +1990-05-18,354.47,354.64,352.52,354.64,162520000 +1990-05-17,354,356.92,354,354.47,164770000 +1990-05-16,354.27,354.68,351.95,354,159810000 +1990-05-15,354.75,355.09,352.84,354.28,165730000 +1990-05-14,352,358.41,351.95,354.75,225410000 +1990-05-11,343.82,352.31,343.82,352,234040000 +1990-05-10,342.87,344.98,342.77,343.82,158460000 +1990-05-09,342.01,343.08,340.9,342.86,152220000 +1990-05-08,340.53,342.03,340.17,342.01,144230000 +1990-05-07,338.39,341.07,338.11,340.53,132760000 +1990-05-04,335.58,338.46,335.17,338.39,140550000 +1990-05-03,334.48,337.02,334.47,335.57,145560000 +1990-05-02,332.25,334.48,332.15,334.48,141610000 +1990-05-01,330.8,332.83,330.8,332.25,149020000 +1990-04-30,329.11,331.31,327.76,330.8,122750000 +1990-04-27,332.92,333.57,328.71,329.11,130630000 +1990-04-26,332.03,333.76,330.67,332.92,141330000 +1990-04-25,330.36,332.74,330.36,332.03,133480000 +1990-04-24,331.05,332.97,329.71,330.36,137360000 +1990-04-23,335.12,335.12,330.09,331.05,136150000 +1990-04-20,338.09,338.52,333.41,335.12,174260000 +1990-04-19,340.72,340.72,337.59,338.09,152930000 +1990-04-18,344.68,345.33,340.11,340.72,147130000 +1990-04-17,344.74,345.19,342.06,344.68,127990000 +1990-04-16,344.34,347.3,344.1,344.74,142810000 +1990-04-12,341.92,344.79,341.91,344.34,142470000 +1990-04-11,342.07,343,341.26,341.92,141080000 +1990-04-10,341.37,342.41,340.62,342.07,136020000 +1990-04-09,340.08,341.83,339.88,341.37,114970000 +1990-04-06,340.73,341.73,338.94,340.08,137490000 +1990-04-05,341.09,342.85,340.63,340.73,144170000 +1990-04-04,343.64,344.12,340.4,341.09,159530000 +1990-04-03,338.7,343.76,338.7,343.64,154310000 +1990-04-02,339.94,339.94,336.33,338.7,124360000 +1990-03-30,340.79,341.41,338.21,339.94,139340000 +1990-03-29,342,342.07,339.77,340.79,132190000 +1990-03-28,341.5,342.58,340.6,342,142300000 +1990-03-27,337.63,341.5,337.03,341.5,131610000 +1990-03-26,337.22,339.74,337.22,337.63,116110000 +1990-03-23,335.69,337.58,335.69,337.22,132070000 +1990-03-22,339.74,339.77,333.62,335.69,175930000 +1990-03-21,341.57,342.34,339.56,339.74,130990000 +1990-03-20,343.53,344.49,340.87,341.57,177320000 +1990-03-19,341.91,343.76,339.12,343.53,142300000 +1990-03-16,338.07,341.91,338.07,341.91,222520000 +1990-03-15,336.87,338.91,336.87,338.07,144410000 +1990-03-14,336,337.63,334.93,336.87,145060000 +1990-03-13,338.67,338.67,335.36,336,145440000 +1990-03-12,337.93,339.08,336.14,338.67,114790000 +1990-03-09,340.12,340.27,336.84,337.93,150410000 +1990-03-08,336.95,340.66,336.95,340.27,170900000 +1990-03-07,337.93,338.84,336.33,336.95,163580000 +1990-03-06,333.74,337.93,333.57,337.93,143640000 +1990-03-05,335.54,336.38,333.49,333.74,140110000 +1990-03-02,332.74,335.54,332.72,335.54,164330000 +1990-03-01,331.89,334.4,331.08,332.74,157930000 +1990-02-28,330.26,333.48,330.16,331.89,184400000 +1990-02-27,328.68,331.94,328.47,330.26,152590000 +1990-02-26,324.16,328.67,323.98,328.67,148900000 +1990-02-23,325.7,326.15,322.1,324.15,148490000 +1990-02-22,327.67,330.98,325.7,325.7,184320000 +1990-02-21,327.91,328.17,324.47,327.67,159240000 +1990-02-20,332.72,332.72,326.26,327.99,147300000 +1990-02-16,334.89,335.64,332.42,332.72,166840000 +1990-02-15,332.01,335.21,331.61,334.89,174620000 +1990-02-14,331.02,333.2,330.64,332.01,138530000 +1990-02-13,330.08,331.61,327.92,331.02,144490000 +1990-02-12,333.62,333.62,329.97,330.08,118390000 +1990-02-09,333.02,334.6,332.41,333.62,146910000 +1990-02-08,333.75,336.09,332,332.96,176240000 +1990-02-07,329.66,333.76,326.55,333.75,186710000 +1990-02-06,331.85,331.86,328.2,329.66,134070000 +1990-02-05,330.92,332.16,330.45,331.85,130950000 +1990-02-02,328.79,332.1,328.09,330.92,164400000 +1990-02-01,329.08,329.86,327.76,328.79,154580000 +1990-01-31,322.98,329.08,322.98,329.08,189660000 +1990-01-30,325.2,325.73,319.83,322.98,186030000 +1990-01-29,325.8,327.31,321.79,325.2,150770000 +1990-01-26,326.09,328.58,321.44,325.8,198190000 +1990-01-25,330.26,332.33,325.33,326.08,172270000 +1990-01-24,331.61,331.71,324.17,330.26,207830000 +1990-01-23,330.38,332.76,328.67,331.61,179300000 +1990-01-22,339.14,339.96,330.28,330.38,148380000 +1990-01-19,338.19,340.48,338.19,339.15,185590000 +1990-01-18,337.4,338.38,333.98,338.19,178590000 +1990-01-17,340.77,342.01,336.26,337.4,170470000 +1990-01-16,337,340.75,333.37,340.75,186070000 +1990-01-15,339.93,339.94,336.57,337,140590000 +1990-01-12,348.53,348.53,339.49,339.93,183880000 +1990-01-11,347.31,350.14,347.31,348.53,154390000 +1990-01-10,349.62,349.62,344.32,347.31,175990000 +1990-01-09,353.83,354.17,349.61,349.62,155210000 +1990-01-08,352.2,354.24,350.54,353.79,140110000 +1990-01-05,355.67,355.67,351.35,352.2,158530000 +1990-01-04,358.76,358.76,352.89,355.67,177000000 +1990-01-03,359.69,360.59,357.89,358.76,192330000 +1990-01-02,353.4,359.69,351.98,359.69,162070000 +1989-12-29,350.68,353.41,350.67,353.4,145940000 +1989-12-28,348.8,350.68,348.76,350.67,128030000 +1989-12-27,346.84,349.12,346.81,348.81,133740000 +1989-12-26,347.42,347.87,346.53,346.81,77610000 +1989-12-22,344.78,347.53,344.76,347.42,120980000 +1989-12-21,342.84,345.03,342.84,344.78,175150000 +1989-12-20,342.5,343.7,341.79,342.84,176520000 +1989-12-19,343.69,343.74,339.63,342.46,186060000 +1989-12-18,350.14,350.88,342.19,343.69,184750000 +1989-12-15,350.97,351.86,346.08,350.14,240390000 +1989-12-14,352.74,352.75,350.08,350.93,178700000 +1989-12-13,351.7,354.1,351.65,352.75,184660000 +1989-12-12,348.56,352.21,348.41,351.73,176820000 +1989-12-11,348.68,348.74,346.39,348.56,147130000 +1989-12-08,347.6,349.6,347.59,348.69,144910000 +1989-12-07,348.55,349.84,346,347.59,161980000 +1989-12-06,349.58,349.94,347.91,348.55,145850000 +1989-12-05,351.41,352.24,349.58,349.58,154640000 +1989-12-04,350.63,351.51,350.32,351.41,150360000 +1989-12-01,346.01,351.88,345.99,350.63,199200000 +1989-11-30,343.6,346.5,343.57,345.99,153200000 +1989-11-29,345.77,345.77,343.36,343.6,147270000 +1989-11-28,345.61,346.33,344.41,345.77,153770000 +1989-11-27,343.98,346.24,343.97,345.61,149390000 +1989-11-24,341.92,344.24,341.91,343.97,86290000 +1989-11-22,339.59,341.92,339.59,341.91,145730000 +1989-11-21,339.35,340.21,337.53,339.59,147900000 +1989-11-20,341.61,341.9,338.29,339.35,128170000 +1989-11-17,340.58,342.24,339.85,341.61,151020000 +1989-11-16,340.54,341.02,338.93,340.58,148370000 +1989-11-15,338,340.54,337.14,340.54,155130000 +1989-11-14,339.55,340.41,337.06,337.99,143170000 +1989-11-13,339.08,340.51,337.93,339.55,140750000 +1989-11-10,336.57,339.1,336.57,339.1,131800000 +1989-11-09,338.15,338.73,336.21,336.57,143390000 +1989-11-08,334.81,339.41,334.81,338.15,170150000 +1989-11-07,332.61,334.82,330.91,334.81,163000000 +1989-11-06,337.61,337.62,332.33,332.61,135480000 +1989-11-03,338.48,339.67,337.37,337.62,131500000 +1989-11-02,341.2,341.2,336.61,338.48,152440000 +1989-11-01,340.36,341.74,339.79,341.2,154240000 +1989-10-31,335.08,340.86,335.07,340.36,176100000 +1989-10-30,335.06,337.04,334.48,335.07,126630000 +1989-10-27,337.93,337.97,333.26,335.06,170330000 +1989-10-26,342.5,342.5,337.2,337.93,175240000 +1989-10-25,343.7,344.51,341.96,342.5,155650000 +1989-10-24,344.83,344.83,335.13,343.7,237960000 +1989-10-23,347.11,348.19,344.22,344.83,135860000 +1989-10-20,347.04,347.57,344.47,347.16,164830000 +1989-10-19,341.76,348.82,341.76,347.13,198120000 +1989-10-18,341.16,343.39,339.03,341.76,166900000 +1989-10-17,342.84,342.85,335.69,341.16,224070000 +1989-10-16,333.65,342.87,327.12,342.85,416290000 +1989-10-13,355.39,355.53,332.81,333.65,251170000 +1989-10-12,356.99,356.99,354.91,355.39,160120000 +1989-10-11,359.13,359.13,356.08,356.99,164070000 +1989-10-10,359.8,360.44,358.11,359.13,147560000 +1989-10-09,358.76,359.86,358.06,359.8,86810000 +1989-10-06,356.97,359.05,356.97,358.78,172520000 +1989-10-05,356.94,357.63,356.28,356.97,177890000 +1989-10-04,354.71,357.49,354.71,356.94,194590000 +1989-10-03,350.87,354.73,350.85,354.71,182550000 +1989-10-02,349.15,350.99,348.35,350.87,127410000 +1989-09-29,348.6,350.31,348.12,349.15,155300000 +1989-09-28,345.1,348.61,345.1,348.6,164240000 +1989-09-27,344.33,345.47,342.85,345.1,158400000 +1989-09-26,344.23,347.02,344.13,344.33,158350000 +1989-09-25,347.05,347.05,343.7,344.23,121130000 +1989-09-22,345.7,347.57,345.69,347.05,133350000 +1989-09-21,346.47,348.46,344.96,345.7,146930000 +1989-09-20,346.55,347.27,346.18,346.47,136640000 +1989-09-19,346.73,348.17,346.44,346.55,141610000 +1989-09-18,345.06,346.84,344.6,346.73,136940000 +1989-09-15,343.16,345.06,341.37,345.06,234860000 +1989-09-14,345.46,345.61,342.55,343.16,149250000 +1989-09-13,348.7,350.1,345.46,345.46,175330000 +1989-09-12,347.66,349.46,347.5,348.7,142140000 +1989-09-11,348.76,348.76,345.91,347.66,126020000 +1989-09-08,348.35,349.18,345.74,348.76,154090000 +1989-09-07,349.24,350.31,348.15,348.35,160160000 +1989-09-06,352.56,352.56,347.98,349.24,161800000 +1989-09-05,353.73,354.13,351.82,352.56,145180000 +1989-09-01,351.45,353.9,350.88,353.73,133300000 +1989-08-31,350.65,351.45,350.21,351.45,144820000 +1989-08-30,349.84,352.27,348.66,350.65,174350000 +1989-08-29,352.09,352.12,348.86,349.84,175210000 +1989-08-28,350.52,352.09,349.08,352.09,131180000 +1989-08-25,351.52,352.73,350.09,350.52,165930000 +1989-08-24,344.7,351.52,344.7,351.52,225520000 +1989-08-23,341.19,344.8,341.19,344.7,159640000 +1989-08-22,340.67,341.25,339,341.19,141930000 +1989-08-21,346.03,346.25,340.55,340.67,136800000 +1989-08-18,344.45,346.03,343.89,346.03,145810000 +1989-08-17,345.66,346.39,342.97,344.45,157560000 +1989-08-16,344.71,346.37,344.71,345.66,150060000 +1989-08-15,343.06,345.03,343.05,344.71,148770000 +1989-08-14,344.71,345.44,341.96,343.06,142010000 +1989-08-11,348.28,351.18,344.01,344.74,197550000 +1989-08-10,346.94,349.78,345.31,348.25,198660000 +1989-08-09,349.3,351,346.86,346.94,209900000 +1989-08-08,349.41,349.84,348.28,349.35,200340000 +1989-08-07,343.92,349.42,343.91,349.41,197580000 +1989-08-04,344.74,345.42,342.6,343.92,169750000 +1989-08-03,344.34,345.22,343.81,344.74,168690000 +1989-08-02,343.75,344.34,342.47,344.34,181760000 +1989-08-01,346.08,347.99,342.93,343.75,225280000 +1989-07-31,342.13,346.08,342.02,346.08,166650000 +1989-07-28,341.94,342.96,341.3,342.15,180610000 +1989-07-27,338.05,342,338.05,341.99,213680000 +1989-07-26,333.88,338.05,333.19,338.05,188270000 +1989-07-25,333.67,336.29,332.6,333.88,179270000 +1989-07-24,335.9,335.9,333.44,333.67,136260000 +1989-07-21,333.5,335.91,332.46,335.9,174880000 +1989-07-20,335.74,337.4,333.22,333.51,204590000 +1989-07-19,331.37,335.73,331.35,335.73,215740000 +1989-07-18,332.42,332.44,330.75,331.35,152350000 +1989-07-17,331.78,333.02,331.02,332.44,131960000 +1989-07-14,329.96,331.89,327.13,331.84,183480000 +1989-07-13,329.81,330.37,329.08,329.95,153820000 +1989-07-12,328.78,330.39,327.92,329.81,160550000 +1989-07-11,327.07,330.42,327.07,328.78,171590000 +1989-07-10,324.93,327.07,324.91,327.07,131870000 +1989-07-07,321.55,325.87,321.08,324.91,166430000 +1989-07-06,320.64,321.55,320.45,321.55,140450000 +1989-07-05,319.23,321.22,317.26,320.64,127710000 +1989-07-03,317.98,319.27,317.27,319.23,68870000 +1989-06-30,319.67,319.97,314.38,317.98,170490000 +1989-06-29,325.81,325.81,319.54,319.68,167100000 +1989-06-28,328.44,328.44,324.3,325.81,158470000 +1989-06-27,326.6,329.19,326.59,328.44,171090000 +1989-06-26,328,328.15,326.31,326.6,143600000 +1989-06-23,322.32,328,322.32,328,198720000 +1989-06-22,320.48,322.34,320.2,322.32,176510000 +1989-06-21,321.25,321.87,319.25,320.48,168830000 +1989-06-20,321.89,322.78,321.03,321.25,167650000 +1989-06-19,321.35,321.89,320.4,321.89,130720000 +1989-06-16,319.96,321.36,318.69,321.35,244510000 +1989-06-15,323.83,323.83,319.21,320.08,179480000 +1989-06-14,323.91,324.89,322.8,323.83,170540000 +1989-06-13,326.24,326.24,322.96,323.91,164870000 +1989-06-12,326.69,326.69,323.73,326.24,151460000 +1989-06-09,326.75,327.32,325.16,326.69,173240000 +1989-06-08,326.95,327.37,325.92,326.75,212310000 +1989-06-07,324.24,327.39,324.24,326.95,213710000 +1989-06-06,322.03,324.48,321.27,324.24,187570000 +1989-06-05,325.52,325.93,322.02,322.03,163420000 +1989-06-02,321.97,325.63,321.97,325.52,229140000 +1989-06-01,320.51,322.57,320.01,321.97,223160000 +1989-05-31,319.05,321.3,318.68,320.52,162530000 +1989-05-30,321.59,322.53,317.83,319.05,151780000 +1989-05-26,319.17,321.59,319.14,321.59,143120000 +1989-05-25,319.14,319.6,318.42,319.17,154470000 +1989-05-24,318.32,319.14,317.58,319.14,178600000 +1989-05-23,321.98,321.98,318.2,318.32,187690000 +1989-05-22,321.24,323.06,320.45,321.98,185010000 +1989-05-19,317.97,321.38,317.97,321.24,242410000 +1989-05-18,317.48,318.52,316.54,317.97,177480000 +1989-05-17,315.28,317.94,315.11,317.48,191210000 +1989-05-16,316.16,316.16,314.99,315.28,173100000 +1989-05-15,313.84,316.16,313.84,316.16,179350000 +1989-05-12,306.95,313.84,306.95,313.84,221490000 +1989-05-11,305.8,307.34,305.8,306.95,151620000 +1989-05-10,305.19,306.25,304.85,305.8,146000000 +1989-05-09,306,306.99,304.06,305.19,150090000 +1989-05-08,307.61,307.61,304.74,306,135130000 +1989-05-05,307.77,310.69,306.98,307.61,180810000 +1989-05-04,308.16,308.4,307.32,307.77,153130000 +1989-05-03,308.12,308.52,307.11,308.16,171690000 +1989-05-02,309.13,310.45,308.12,308.12,172560000 +1989-05-01,309.64,309.64,307.4,309.12,138050000 +1989-04-28,309.58,309.65,308.48,309.64,158390000 +1989-04-27,306.93,310.45,306.93,309.58,191170000 +1989-04-26,306.78,307.3,306.07,306.93,146090000 +1989-04-25,308.69,309.65,306.74,306.75,165430000 +1989-04-24,309.61,309.61,307.83,308.69,142100000 +1989-04-21,306.19,309.61,306.19,309.61,187310000 +1989-04-20,307.15,307.96,304.53,306.19,175970000 +1989-04-19,306.02,307.68,305.36,307.15,191510000 +1989-04-18,301.72,306.25,301.72,306.02,208650000 +1989-04-17,301.36,302.01,300.71,301.72,128540000 +1989-04-14,296.4,301.38,296.4,301.36,169780000 +1989-04-13,298.99,299,296.27,296.4,141590000 +1989-04-12,298.49,299.81,298.49,298.99,165200000 +1989-04-11,297.11,298.87,297.11,298.49,146830000 +1989-04-10,297.16,297.94,296.85,297.11,123990000 +1989-04-07,295.29,297.62,294.35,297.16,156950000 +1989-04-06,296.22,296.24,294.52,295.29,146530000 +1989-04-05,295.31,296.43,295.28,296.24,165880000 +1989-04-04,296.4,296.4,294.72,295.31,160680000 +1989-04-03,294.87,297.04,294.62,296.39,164660000 +1989-03-31,292.52,294.96,292.52,294.87,170960000 +1989-03-30,292.35,293.8,291.5,292.52,159950000 +1989-03-29,291.59,292.75,291.42,292.35,144240000 +1989-03-28,290.57,292.32,290.57,291.59,146420000 +1989-03-27,288.98,290.57,288.07,290.57,112960000 +1989-03-23,290.49,291.51,288.56,288.98,153750000 +1989-03-22,291.33,291.46,289.9,290.49,146570000 +1989-03-21,289.92,292.38,289.92,291.33,142010000 +1989-03-20,292.69,292.69,288.56,289.92,151260000 +1989-03-17,299.44,299.44,291.08,292.69,242900000 +1989-03-16,296.67,299.99,296.66,299.44,196040000 +1989-03-15,295.14,296.78,295.14,296.67,167070000 +1989-03-14,295.32,296.29,294.63,295.14,139970000 +1989-03-13,292.88,296.18,292.88,295.32,140460000 +1989-03-10,293.93,293.93,291.6,292.88,146830000 +1989-03-09,294.08,294.69,293.85,293.93,143160000 +1989-03-08,293.87,295.62,293.51,294.08,167620000 +1989-03-07,294.81,295.16,293.5,293.87,172500000 +1989-03-06,291.2,294.81,291.18,294.81,168880000 +1989-03-03,289.94,291.18,289.44,291.18,151790000 +1989-03-02,287.11,290.32,287.11,289.95,161980000 +1989-03-01,288.86,290.28,286.46,287.11,177210000 +1989-02-28,287.82,289.42,287.63,288.86,147430000 +1989-02-27,287.13,288.12,286.26,287.82,139900000 +1989-02-24,292.05,292.05,287.13,287.13,160680000 +1989-02-23,290.91,292.05,289.83,292.05,150370000 +1989-02-22,295.98,295.98,290.76,290.91,163140000 +1989-02-21,296.76,297.04,295.16,295.98,141950000 +1989-02-17,294.81,297.12,294.69,296.76,159520000 +1989-02-16,294.24,295.15,294.22,294.81,177450000 +1989-02-15,291.81,294.42,291.49,294.24,154220000 +1989-02-14,292.54,294.37,291.41,291.81,150610000 +1989-02-13,292.02,293.07,290.88,292.54,143520000 +1989-02-10,296.06,296.06,291.96,292.02,173560000 +1989-02-09,298.65,298.79,295.16,296.06,224220000 +1989-02-08,299.62,300.57,298.41,298.65,189420000 +1989-02-07,296.04,300.34,295.78,299.63,217260000 +1989-02-06,296.97,296.99,294.96,296.04,150980000 +1989-02-03,296.84,297.66,296.15,296.97,172980000 +1989-02-02,297.09,297.92,295.81,296.84,183430000 +1989-02-01,297.47,298.33,296.22,297.09,215640000 +1989-01-31,294.99,297.51,293.57,297.47,194050000 +1989-01-30,293.82,295.13,293.54,294.99,167830000 +1989-01-27,291.69,296.08,291.69,293.82,254870000 +1989-01-26,289.14,292.62,288.13,291.69,212250000 +1989-01-25,288.49,289.15,287.97,289.14,183610000 +1989-01-24,284.5,288.49,284.5,288.49,189620000 +1989-01-23,287.85,287.98,284.5,284.5,141640000 +1989-01-20,286.9,287.04,285.75,286.63,166120000 +1989-01-19,286.53,287.9,286.14,286.91,192030000 +1989-01-18,283.55,286.87,282.65,286.53,187450000 +1989-01-17,284.14,284.14,283.06,283.55,143930000 +1989-01-16,283.87,284.88,283.63,284.14,117380000 +1989-01-13,283.17,284.12,282.71,283.87,132320000 +1989-01-12,282.01,284.63,282.01,283.17,183000000 +1989-01-11,280.38,282.01,280.21,282.01,148950000 +1989-01-10,280.98,281.58,279.44,280.38,140420000 +1989-01-09,280.67,281.89,280.32,280.98,163180000 +1989-01-06,280.01,282.06,280.01,280.67,161330000 +1989-01-05,279.43,281.51,279.43,280.01,174040000 +1989-01-04,275.31,279.75,275.31,279.43,149700000 +1989-01-03,277.72,277.72,273.81,275.31,128500000 +1988-12-30,279.39,279.78,277.72,277.72,127210000 +1988-12-29,277.08,279.42,277.08,279.4,131290000 +1988-12-28,276.83,277.55,276.17,277.08,110630000 +1988-12-27,277.87,278.09,276.74,276.83,87490000 +1988-12-23,276.87,277.99,276.87,277.87,81760000 +1988-12-22,277.38,277.89,276.86,276.87,150510000 +1988-12-21,277.47,277.83,276.3,277.38,147250000 +1988-12-20,278.91,280.45,277.47,277.47,161090000 +1988-12-19,276.29,279.31,275.61,278.91,162250000 +1988-12-16,274.28,276.29,274.28,276.29,196480000 +1988-12-15,275.32,275.62,274.01,274.28,136820000 +1988-12-14,276.31,276.31,274.58,275.31,132350000 +1988-12-13,276.52,276.52,274.58,276.31,132340000 +1988-12-12,277.03,278.82,276.52,276.52,124160000 +1988-12-09,276.57,277.82,276.34,277.03,133770000 +1988-12-08,278.13,278.13,276.55,276.59,124150000 +1988-12-07,277.59,279.01,277.34,278.13,148360000 +1988-12-06,274.93,277.89,274.62,277.59,158340000 +1988-12-05,274.93,275.62,271.81,274.93,144660000 +1988-12-02,272.49,272.49,270.47,271.81,124610000 +1988-12-01,273.68,273.7,272.27,272.49,129380000 +1988-11-30,270.91,274.36,270.9,273.7,157810000 +1988-11-29,268.6,271.31,268.13,270.91,127420000 +1988-11-28,267.22,268.98,266.97,268.64,123480000 +1988-11-25,268.99,269,266.47,267.23,72090000 +1988-11-23,267.22,269.56,267.21,269,112010000 +1988-11-22,266.19,267.85,265.42,267.21,127000000 +1988-11-21,266.35,266.47,263.41,266.22,120430000 +1988-11-18,264.6,266.62,264.6,266.47,119320000 +1988-11-17,264.61,265.63,263.45,264.6,141280000 +1988-11-16,268.41,268.41,262.85,263.82,161710000 +1988-11-15,267.73,268.75,267.72,268.34,115170000 +1988-11-14,267.93,269.25,266.79,267.72,142900000 +1988-11-11,273.65,273.69,267.92,267.92,135500000 +1988-11-10,273.32,274.37,272.98,273.69,128920000 +1988-11-09,275.14,275.15,272.15,273.33,153140000 +1988-11-08,273.95,275.8,273.93,275.15,141660000 +1988-11-07,276.3,276.31,273.62,273.93,133870000 +1988-11-04,279.11,279.2,276.31,276.31,143580000 +1988-11-03,279.04,280.37,279.04,279.2,152980000 +1988-11-02,279.07,279.45,277.08,279.06,161300000 +1988-11-01,278.97,279.57,278.01,279.06,151250000 +1988-10-31,278.54,279.39,277.14,278.97,143460000 +1988-10-28,277.29,279.48,277.28,278.53,146300000 +1988-10-27,281.35,281.38,276,277.28,196540000 +1988-10-26,282.37,282.52,280.54,281.38,181550000 +1988-10-25,282.28,282.84,281.87,282.38,155190000 +1988-10-24,283.63,283.95,282.28,282.28,170590000 +1988-10-21,282.88,283.66,281.16,283.66,195410000 +1988-10-20,276.97,282.88,276.93,282.88,189580000 +1988-10-19,279.4,280.53,274.41,276.97,186350000 +1988-10-18,276.43,279.39,276.41,279.38,162500000 +1988-10-17,275.48,276.65,275.01,276.41,119290000 +1988-10-14,275.27,277.01,274.08,275.5,160240000 +1988-10-13,273.95,275.83,273.39,275.22,154530000 +1988-10-12,277.91,277.93,273.05,273.98,154840000 +1988-10-11,278.15,278.24,276.33,277.93,140900000 +1988-10-10,278.06,278.69,277.1,278.24,124660000 +1988-10-07,272.38,278.07,272.37,278.07,216390000 +1988-10-06,271.87,272.39,271.3,272.39,153570000 +1988-10-05,270.63,272.45,270.08,271.86,175130000 +1988-10-04,271.37,271.79,270.34,270.62,157760000 +1988-10-03,271.89,271.91,268.84,271.38,130380000 +1988-09-30,272.55,274.87,271.66,271.91,175750000 +1988-09-29,269.09,273.02,269.08,272.59,155790000 +1988-09-28,268.22,269.08,267.77,269.08,113720000 +1988-09-27,268.89,269.36,268.01,268.26,113010000 +1988-09-26,269.77,269.8,268.61,268.88,116420000 +1988-09-23,269.16,270.31,268.28,269.76,145100000 +1988-09-22,270.19,270.58,268.26,269.18,150670000 +1988-09-21,269.76,270.64,269.48,270.16,127400000 +1988-09-20,268.83,270.07,268.5,269.73,142220000 +1988-09-19,270.64,270.65,267.41,268.82,135770000 +1988-09-16,268.13,270.81,267.33,270.65,211110000 +1988-09-15,269.3,269.78,268.03,268.13,161210000 +1988-09-14,267.5,269.47,267.41,269.31,177220000 +1988-09-13,266.45,267.43,265.22,267.43,162490000 +1988-09-12,266.85,267.64,266.22,266.47,114880000 +1988-09-09,265.88,268.26,263.66,266.84,141540000 +1988-09-08,265.87,266.54,264.88,265.88,149380000 +1988-09-07,265.62,266.98,264.93,265.87,139590000 +1988-09-06,264.42,265.94,264.4,265.59,122250000 +1988-09-02,258.35,264.9,258.35,264.48,159840000 +1988-09-01,261.52,261.52,256.98,258.35,144090000 +1988-08-31,262.51,263.8,261.21,261.52,130480000 +1988-08-30,262.33,263.18,261.53,262.51,108720000 +1988-08-29,259.68,262.56,259.68,262.33,99280000 +1988-08-26,259.18,260.15,258.87,259.68,89240000 +1988-08-25,261.1,261.13,257.56,259.18,127640000 +1988-08-24,257.16,261.13,257.09,261.13,127800000 +1988-08-23,256.99,257.86,256.53,257.09,119540000 +1988-08-22,260.24,260.71,256.94,256.98,122250000 +1988-08-19,261.05,262.27,260.23,260.24,122370000 +1988-08-18,260.76,262.76,260.75,261.03,139820000 +1988-08-17,260.57,261.84,259.33,260.77,169500000 +1988-08-16,258.68,262.61,257.5,260.56,162790000 +1988-08-15,262.49,262.55,258.68,258.69,128560000 +1988-08-12,262.7,262.94,261.37,262.55,176960000 +1988-08-11,261.92,262.77,260.34,262.75,173000000 +1988-08-10,266.43,266.49,261.03,261.9,200950000 +1988-08-09,270,270.2,265.06,266.49,200710000 +1988-08-08,271.13,272.47,269.93,269.98,148800000 +1988-08-05,271.7,271.93,270.08,271.15,113400000 +1988-08-04,273,274.2,271.77,271.93,157240000 +1988-08-03,272.03,273.42,271.15,272.98,203590000 +1988-08-02,272.19,273.68,270.37,272.06,166660000 +1988-08-01,272.03,272.8,271.21,272.21,138170000 +1988-07-29,266.04,272.02,266.02,272.02,192340000 +1988-07-28,262.52,266.55,262.5,266.02,154570000 +1988-07-27,265.18,265.83,262.48,262.5,135890000 +1988-07-26,264.7,266.09,264.32,265.19,121960000 +1988-07-25,263.49,265.17,263.03,264.68,215140000 +1988-07-22,266.65,266.66,263.29,263.5,148880000 +1988-07-21,269.99,270,266.66,266.66,149460000 +1988-07-20,268.52,270.24,268.47,270,151990000 +1988-07-19,270.49,271.21,267.01,268.47,144110000 +1988-07-18,271.99,272.05,268.66,270.51,156210000 +1988-07-15,270.23,272.06,269.53,272.05,199710000 +1988-07-14,269.33,270.69,268.58,270.26,172410000 +1988-07-13,267.87,269.46,266.12,269.32,218930000 +1988-07-12,270.54,270.7,266.96,267.85,161650000 +1988-07-11,270.03,271.64,270.02,270.55,123300000 +1988-07-08,271.76,272.31,269.86,270.02,136070000 +1988-07-07,272,272.05,269.31,271.78,156100000 +1988-07-06,275.8,276.36,269.92,272.02,189630000 +1988-07-05,271.78,275.81,270.51,275.81,171790000 +1988-07-01,273.5,273.8,270.78,271.78,238330000 +1988-06-30,271,273.51,270.97,273.5,227410000 +1988-06-29,272.32,273.01,269.49,270.98,159590000 +1988-06-28,269.07,272.8,269.06,272.31,152370000 +1988-06-27,273.78,273.79,268.85,269.06,264410000 +1988-06-24,274.81,275.19,273.53,273.78,179880000 +1988-06-23,275.62,275.89,274.26,274.82,185770000 +1988-06-22,271.69,276.88,271.67,275.66,217510000 +1988-06-21,268.95,271.67,267.52,271.67,155060000 +1988-06-20,270.67,270.68,268.59,268.94,116750000 +1988-06-17,269.79,270.77,268.09,270.68,343920000 +1988-06-16,274.44,274.45,268.76,269.77,161550000 +1988-06-15,274.29,274.45,272.75,274.45,150260000 +1988-06-14,271.58,276.14,271.44,274.3,227150000 +1988-06-13,271.28,271.94,270.53,271.43,125310000 +1988-06-10,270.22,273.21,270.2,271.26,155710000 +1988-06-09,271.5,272.29,270.19,270.2,235160000 +1988-06-08,265.32,272.01,265.17,271.52,310030000 +1988-06-07,267.02,267.28,264.5,265.17,168710000 +1988-06-06,266.46,267.05,264.97,267.05,152460000 +1988-06-03,265.34,267.11,264.42,266.45,189600000 +1988-06-02,266.65,266.71,264.12,265.33,193540000 +1988-06-01,262.16,267.43,262.1,266.69,234560000 +1988-05-31,253.44,262.16,253.42,262.16,247610000 +1988-05-27,254.62,254.63,252.74,253.42,133590000 +1988-05-26,253.75,254.98,253.52,254.63,164260000 +1988-05-25,253.52,255.34,253.51,253.76,138310000 +1988-05-24,250.84,253.51,250.83,253.51,139930000 +1988-05-23,253,253.02,249.82,250.83,102640000 +1988-05-20,252.61,253.7,251.79,253.02,120600000 +1988-05-19,251.36,252.57,248.85,252.57,165160000 +1988-05-18,255.4,255.67,250.73,251.35,209420000 +1988-05-17,258.72,260.2,255.35,255.39,133850000 +1988-05-16,256.75,258.71,256.28,258.71,155010000 +1988-05-13,253.88,256.83,253.85,256.78,147240000 +1988-05-12,253.32,254.87,253.31,253.85,143880000 +1988-05-11,257.6,257.62,252.32,253.31,176720000 +1988-05-10,256.53,258.3,255.93,257.62,131200000 +1988-05-09,257.47,258.22,255.45,256.54,166320000 +1988-05-06,258.8,260.31,257.03,257.48,129080000 +1988-05-05,260.3,260.32,258.13,258.79,171840000 +1988-05-04,263.05,263.23,260.31,260.32,141320000 +1988-05-03,261.55,263.7,261.55,263,176920000 +1988-05-02,261.36,261.56,259.99,261.56,136470000 +1988-04-29,262.59,262.61,259.97,261.33,135620000 +1988-04-28,263.79,263.8,262.22,262.61,128680000 +1988-04-27,263.94,265.09,263.45,263.8,133810000 +1988-04-26,262.45,265.06,262.18,263.93,152300000 +1988-04-25,260.15,263.29,260.14,262.51,156950000 +1988-04-22,256.45,261.16,256.42,260.14,152520000 +1988-04-21,256.15,260.44,254.71,256.42,168440000 +1988-04-20,257.91,258.54,256.12,256.13,147590000 +1988-04-19,259.24,262.38,257.91,257.92,161910000 +1988-04-18,259.75,259.81,258.03,259.21,144650000 +1988-04-15,259.74,260.39,255.97,259.77,234160000 +1988-04-14,271.55,271.57,259.37,259.75,211810000 +1988-04-13,271.33,271.7,269.23,271.58,185120000 +1988-04-12,269.88,272.05,269.66,271.37,146400000 +1988-04-11,269.43,270.41,268.61,270.16,146370000 +1988-04-08,266.15,270.22,266.11,269.43,169300000 +1988-04-07,265.51,267.32,265.22,266.16,177840000 +1988-04-06,258.52,265.5,258.22,265.49,189760000 +1988-04-05,256.1,258.52,256.03,258.51,135290000 +1988-04-04,258.89,259.06,255.68,256.09,182240000 +1988-03-31,258.03,259.03,256.16,258.89,139870000 +1988-03-30,260.06,261.59,257.92,258.07,151810000 +1988-03-29,258.11,260.86,258.06,260.07,152690000 +1988-03-28,258.5,258.51,256.07,258.06,142820000 +1988-03-25,263.34,263.44,258.12,258.51,163170000 +1988-03-24,268.91,268.91,262.48,263.35,184910000 +1988-03-23,268.81,269.79,268.01,268.91,167370000 +1988-03-22,268.73,269.61,267.9,268.84,142000000 +1988-03-21,271.1,271.12,267.42,268.74,128830000 +1988-03-18,271.22,272.64,269.76,271.12,245750000 +1988-03-17,268.66,271.22,268.65,271.22,211920000 +1988-03-16,266.11,268.68,264.81,268.65,153590000 +1988-03-15,266.34,266.41,264.92,266.13,133170000 +1988-03-14,264.93,266.55,264.52,266.37,131890000 +1988-03-11,263.85,264.94,261.27,264.94,200020000 +1988-03-10,269.07,269.35,263.8,263.84,197260000 +1988-03-09,269.46,270.76,268.65,269.06,210900000 +1988-03-08,267.38,270.06,267.38,269.43,237680000 +1988-03-07,267.28,267.69,265.94,267.38,152980000 +1988-03-04,267.87,268.4,264.72,267.3,201410000 +1988-03-03,267.98,268.4,266.82,267.88,203310000 +1988-03-02,267.23,268.75,267,267.98,199630000 +1988-03-01,267.82,267.95,265.39,267.22,199990000 +1988-02-29,262.46,267.82,262.46,267.82,236050000 +1988-02-26,261.56,263,261.38,262.46,158060000 +1988-02-25,264.39,267.75,261.05,261.58,213490000 +1988-02-24,265.01,266.25,263.87,264.43,212730000 +1988-02-23,265.62,266.12,263.11,265.02,192260000 +1988-02-22,261.6,266.06,260.88,265.64,178930000 +1988-02-19,257.9,261.61,257.62,261.61,180300000 +1988-02-18,258.82,259.6,256.9,257.91,151430000 +1988-02-17,259.94,261.47,257.83,259.21,176830000 +1988-02-16,257.61,259.84,256.57,259.83,135380000 +1988-02-12,255.95,258.86,255.85,257.63,177190000 +1988-02-11,256.63,257.77,255.12,255.95,200760000 +1988-02-10,251.74,256.92,251.72,256.66,187980000 +1988-02-09,249.11,251.72,248.66,251.72,162350000 +1988-02-08,250.95,250.96,247.82,249.1,168850000 +1988-02-05,252.22,253.85,250.9,250.96,161310000 +1988-02-04,252.2,253.03,250.34,252.21,186490000 +1988-02-03,255.56,256.98,250.56,252.21,237270000 +1988-02-02,255.05,256.08,252.8,255.57,164920000 +1988-02-01,257.05,258.27,254.93,255.04,210660000 +1988-01-29,253.31,257.07,252.7,257.07,211880000 +1988-01-28,249.39,253.66,249.38,253.29,166430000 +1988-01-27,249.58,253.02,248.5,249.38,176360000 +1988-01-26,252.13,252.17,249.1,249.57,138380000 +1988-01-25,246.53,252.87,246.5,252.17,275250000 +1988-01-22,243.14,246.5,243.14,246.5,147050000 +1988-01-21,242.65,244.25,240.17,243.14,158080000 +1988-01-20,249.31,249.32,241.14,242.63,181660000 +1988-01-19,251.84,253.33,248.75,249.32,153550000 +1988-01-18,252.05,252.86,249.98,251.88,135100000 +1988-01-15,246.02,253.65,245.88,252.05,197940000 +1988-01-14,245.83,247,243.97,245.88,140570000 +1988-01-13,245.41,249.25,241.41,245.81,154020000 +1988-01-12,247.44,247.49,240.46,245.42,165730000 +1988-01-11,243.38,247.51,241.07,247.49,158980000 +1988-01-08,261.05,261.07,242.95,243.4,197300000 +1988-01-07,258.87,261.32,256.18,261.07,175360000 +1988-01-06,258.64,259.79,257.18,258.89,169730000 +1988-01-05,255.95,261.78,255.95,258.63,209520000 +1988-01-04,247.1,256.44,247.08,255.94,181810000 +1987-12-31,247.84,247.86,245.22,247.08,170140000 +1987-12-30,244.63,248.06,244.59,247.86,149230000 +1987-12-29,245.58,245.88,244.28,244.59,111580000 +1987-12-28,252.01,252.02,244.19,245.57,131220000 +1987-12-24,253.13,253.16,251.68,252.03,108800000 +1987-12-23,249.96,253.35,249.95,253.16,203110000 +1987-12-22,249.56,249.97,247.01,249.95,192650000 +1987-12-21,249.14,250.25,248.3,249.54,161790000 +1987-12-18,243.01,249.18,243.01,249.16,276220000 +1987-12-17,248.08,248.6,242.96,242.98,191780000 +1987-12-16,242.81,248.11,242.8,248.08,193820000 +1987-12-15,242.19,245.59,241.31,242.81,214970000 +1987-12-14,235.3,242.34,235.04,242.19,187680000 +1987-12-11,233.6,235.48,233.35,235.32,151680000 +1987-12-10,238.89,240.05,233.4,233.57,188960000 +1987-12-09,234.91,240.09,233.83,238.89,231430000 +1987-12-08,228.77,234.92,228.69,234.91,227310000 +1987-12-07,223.98,228.77,223.92,228.76,146660000 +1987-12-04,225.2,225.77,221.24,223.92,184800000 +1987-12-03,233.46,233.9,225.21,225.21,204160000 +1987-12-02,232.01,234.56,230.31,233.45,148890000 +1987-12-01,230.32,234.02,230.3,232,149870000 +1987-11-30,240.27,240.34,225.75,230.3,268910000 +1987-11-27,244.11,244.12,240.34,240.34,86360000 +1987-11-25,246.42,246.54,244.08,244.1,139780000 +1987-11-24,242.98,247.9,242.98,246.39,199520000 +1987-11-23,242,242.99,240.5,242.99,143160000 +1987-11-20,240.04,242.01,235.89,242,189170000 +1987-11-19,245.54,245.55,239.7,240.05,157140000 +1987-11-18,243.09,245.55,240.67,245.55,158270000 +1987-11-17,246.73,246.76,240.81,243.04,148240000 +1987-11-16,245.69,249.54,244.98,246.76,164340000 +1987-11-13,248.54,249.42,245.64,245.64,174920000 +1987-11-12,241.93,249.9,241.9,248.52,206280000 +1987-11-11,239.01,243.86,239,241.9,147850000 +1987-11-10,243.14,243.17,237.64,239,184310000 +1987-11-09,250.41,250.41,243.01,243.17,160690000 +1987-11-06,254.49,257.21,249.68,250.41,228290000 +1987-11-05,248.93,256.09,247.72,254.48,226000000 +1987-11-04,250.81,251,246.34,248.96,202500000 +1987-11-03,255.75,255.75,242.78,250.82,227800000 +1987-11-02,251.73,255.75,249.15,255.75,176000000 +1987-10-30,244.77,254.04,244.77,251.79,303400000 +1987-10-29,233.31,246.69,233.28,244.77,258100000 +1987-10-28,233.19,238.58,226.26,233.28,279400000 +1987-10-27,227.67,237.81,227.67,233.19,260200000 +1987-10-26,248.2,248.22,227.26,227.67,308800000 +1987-10-23,248.29,250.7,242.76,248.22,245600000 +1987-10-22,258.24,258.38,242.99,248.25,392200000 +1987-10-21,236.83,259.27,236.83,258.38,449600000 +1987-10-20,225.06,245.62,216.46,236.83,608100000 +1987-10-19,282.7,282.7,224.83,224.84,604300000 +1987-10-16,298.08,298.92,281.52,282.7,338500000 +1987-10-15,305.21,305.23,298.07,298.08,263200000 +1987-10-14,314.52,314.52,304.78,305.23,207400000 +1987-10-13,309.39,314.53,309.39,314.52,172900000 +1987-10-12,311.07,311.07,306.76,309.39,141900000 +1987-10-09,314.16,315.04,310.97,311.07,158300000 +1987-10-08,318.54,319.34,312.02,314.16,198700000 +1987-10-07,319.22,319.39,315.78,318.54,186300000 +1987-10-06,328.08,328.08,319.17,319.22,175600000 +1987-10-05,328.07,328.57,326.09,328.08,159700000 +1987-10-02,327.33,328.94,327.22,328.07,189100000 +1987-10-01,321.83,327.34,321.83,327.33,193200000 +1987-09-30,321.69,322.53,320.16,321.83,183100000 +1987-09-29,323.2,324.63,320.27,321.69,173500000 +1987-09-28,320.16,325.33,320.16,323.2,188100000 +1987-09-25,319.72,320.55,318.1,320.16,138000000 +1987-09-24,321.09,322.01,319.12,319.72,162200000 +1987-09-23,319.49,321.83,319.12,321.19,220300000 +1987-09-22,310.54,319.51,308.69,319.5,209500000 +1987-09-21,314.92,317.66,310.12,310.54,170100000 +1987-09-18,314.98,316.99,314.86,314.86,188100000 +1987-09-17,314.94,316.08,313.45,314.93,150700000 +1987-09-16,317.75,319.5,314.61,314.86,195700000 +1987-09-15,323.07,323.08,317.63,317.74,136200000 +1987-09-14,322.02,323.81,320.4,323.08,154400000 +1987-09-11,317.14,322.45,317.13,321.98,178000000 +1987-09-10,313.92,317.59,313.92,317.13,179800000 +1987-09-09,313.6,315.41,312.29,313.92,164900000 +1987-09-08,316.68,316.7,308.56,313.56,242900000 +1987-09-04,320.21,322.03,316.53,316.7,129100000 +1987-09-03,321.47,324.29,317.39,320.21,165200000 +1987-09-02,323.4,324.53,318.76,321.68,199900000 +1987-09-01,329.81,332.18,322.83,323.4,193500000 +1987-08-31,327.03,330.09,326.99,329.8,165800000 +1987-08-28,331.37,331.38,327.03,327.04,156300000 +1987-08-27,334.56,334.57,331.1,331.38,163600000 +1987-08-26,336.77,337.39,334.46,334.57,196200000 +1987-08-25,333.37,337.89,333.33,336.77,213500000 +1987-08-24,335.89,335.9,331.92,333.33,149400000 +1987-08-21,334.85,336.37,334.3,335.9,189600000 +1987-08-20,331.49,335.19,329.83,334.84,196600000 +1987-08-19,329.26,329.89,326.54,329.83,180900000 +1987-08-18,334.1,334.11,326.43,329.25,198400000 +1987-08-17,333.98,335.43,332.88,334.11,166100000 +1987-08-14,334.63,336.08,332.63,333.99,196100000 +1987-08-13,332.38,335.52,332.38,334.65,217100000 +1987-08-12,333.32,334.57,331.06,332.39,235800000 +1987-08-11,328.02,333.4,328,333.33,278100000 +1987-08-10,322.98,328,322.95,328,187200000 +1987-08-07,322.1,324.15,321.82,323,212700000 +1987-08-06,318.49,322.09,317.5,322.09,192000000 +1987-08-05,316.25,319.74,316.23,318.45,192700000 +1987-08-04,317.59,318.25,314.51,316.23,166500000 +1987-08-03,318.62,320.26,316.52,317.57,207800000 +1987-07-31,318.05,318.85,317.56,318.66,181900000 +1987-07-30,315.69,318.53,315.65,318.05,208000000 +1987-07-29,312.34,315.65,311.73,315.65,196200000 +1987-07-28,310.65,312.33,310.28,312.33,172600000 +1987-07-27,309.3,310.7,308.61,310.65,152000000 +1987-07-24,307.82,309.28,307.78,309.27,158400000 +1987-07-23,308.5,309.63,306.1,307.81,163700000 +1987-07-22,308.56,309.12,307.22,308.47,174700000 +1987-07-21,311.36,312.41,307.51,308.55,186600000 +1987-07-20,314.56,314.59,311.24,311.39,168100000 +1987-07-17,312.71,314.59,312.38,314.59,210000000 +1987-07-16,311,312.83,310.42,312.7,210900000 +1987-07-15,310.67,312.08,309.07,310.42,202300000 +1987-07-14,307.67,310.69,307.46,310.68,185900000 +1987-07-13,308.41,308.41,305.49,307.63,152500000 +1987-07-10,307.55,308.4,306.96,308.37,172100000 +1987-07-09,308.3,309.56,307.42,307.52,195400000 +1987-07-08,307.41,308.48,306.01,308.29,207500000 +1987-07-07,304.91,308.63,304.73,307.4,200700000 +1987-07-06,305.64,306.75,304.23,304.92,155000000 +1987-07-02,302.96,306.34,302.94,305.63,154900000 +1987-07-01,303.99,304,302.53,302.94,157000000 +1987-06-30,307.89,308,303.01,304,165500000 +1987-06-29,307.15,308.15,306.75,307.9,142500000 +1987-06-26,308.94,308.96,306.36,307.16,150500000 +1987-06-25,306.87,309.44,306.86,308.96,173500000 +1987-06-24,308.44,308.91,306.32,306.86,153800000 +1987-06-23,309.66,310.27,307.48,308.43,194200000 +1987-06-22,306.98,310.2,306.97,309.65,178200000 +1987-06-19,305.71,306.97,305.55,306.97,220500000 +1987-06-18,304.78,306.13,303.38,305.69,168600000 +1987-06-17,304.77,305.74,304.03,304.81,184700000 +1987-06-16,303.12,304.86,302.6,304.76,157800000 +1987-06-15,301.62,304.11,301.62,303.14,156900000 +1987-06-12,298.77,302.26,298.73,301.62,175100000 +1987-06-11,297.5,298.94,297.47,298.73,138900000 +1987-06-10,297.28,300.81,295.66,297.47,197400000 +1987-06-09,296.72,297.59,295.9,297.28,164200000 +1987-06-08,293.46,297.03,291.55,296.72,136400000 +1987-06-05,295.11,295.11,292.8,293.45,129100000 +1987-06-04,293.46,295.09,292.76,295.09,140300000 +1987-06-03,288.56,293.47,288.56,293.47,164200000 +1987-06-02,289.82,290.94,286.93,288.46,153400000 +1987-06-01,290.12,291.96,289.23,289.83,149300000 +1987-05-29,290.77,292.87,289.7,290.1,153500000 +1987-05-28,288.73,291.5,286.33,290.76,153800000 +1987-05-27,289.07,290.78,288.19,288.73,171400000 +1987-05-26,282.16,289.11,282.16,289.11,152500000 +1987-05-22,280.17,283.33,280.17,282.16,135800000 +1987-05-21,278.23,282.31,278.21,280.17,164800000 +1987-05-20,279.62,280.89,277.01,278.21,206800000 +1987-05-19,286.66,287.39,278.83,279.62,175400000 +1987-05-18,287.43,287.43,282.57,286.65,174200000 +1987-05-15,294.23,294.24,287.11,287.43,180800000 +1987-05-14,293.98,295.1,292.95,294.24,152000000 +1987-05-13,293.31,294.54,290.74,293.98,171000000 +1987-05-12,291.57,293.3,290.18,293.3,155300000 +1987-05-11,293.37,298.69,291.55,291.57,203700000 +1987-05-08,294.73,296.18,291.73,293.37,161900000 +1987-05-07,295.45,296.8,294.07,294.71,215200000 +1987-05-06,295.35,296.19,293.6,295.47,196600000 +1987-05-05,289.36,295.4,289.34,295.34,192300000 +1987-05-04,288.02,289.99,286.39,289.36,140600000 +1987-05-01,286.99,289.71,286.52,288.03,160100000 +1987-04-30,284.58,290.08,284.57,288.36,183100000 +1987-04-29,282.58,286.42,282.58,284.57,173600000 +1987-04-28,281.83,285.95,281.83,282.51,180100000 +1987-04-27,281.52,284.45,276.22,281.83,222700000 +1987-04-24,286.81,286.82,281.18,281.52,178000000 +1987-04-23,287.19,289.12,284.28,286.82,173900000 +1987-04-22,293.05,293.46,286.98,287.19,185900000 +1987-04-21,285.88,293.07,282.89,293.07,191300000 +1987-04-20,286.91,288.36,284.55,286.09,139100000 +1987-04-16,284.45,289.57,284.44,286.91,189600000 +1987-04-15,279.17,285.14,279.16,284.44,198200000 +1987-04-14,285.61,285.62,275.67,279.16,266500000 +1987-04-13,292.48,293.36,285.62,285.62,181000000 +1987-04-10,292.82,293.74,290.94,292.49,169500000 +1987-04-09,297.25,297.71,291.5,292.86,180300000 +1987-04-08,296.72,299.2,295.18,297.26,179800000 +1987-04-07,301.94,303.65,296.67,296.69,186400000 +1987-04-06,300.46,302.21,300.41,301.95,173700000 +1987-04-03,293.64,301.3,292.3,300.41,213400000 +1987-04-02,292.41,294.47,292.02,293.63,183000000 +1987-04-01,291.59,292.38,288.34,292.38,182600000 +1987-03-31,289.21,291.87,289.07,291.7,171800000 +1987-03-30,296.1,296.13,286.69,289.2,208400000 +1987-03-27,300.96,301.41,296.06,296.13,184400000 +1987-03-26,300.39,302.72,300.38,300.93,196000000 +1987-03-25,301.52,301.85,299.36,300.38,171300000 +1987-03-24,301.17,301.92,300.14,301.64,189900000 +1987-03-23,298.16,301.17,297.5,301.16,189100000 +1987-03-20,294.08,298.17,294.08,298.17,234000000 +1987-03-19,292.73,294.46,292.26,294.08,166100000 +1987-03-18,292.49,294.58,290.87,292.78,198100000 +1987-03-17,288.09,292.47,287.96,292.47,177300000 +1987-03-16,289.88,289.89,286.64,288.23,134900000 +1987-03-13,291.22,291.79,289.88,289.89,150900000 +1987-03-12,290.33,291.91,289.66,291.22,174500000 +1987-03-11,290.87,292.51,289.33,290.31,186900000 +1987-03-10,288.3,290.87,287.89,290.86,174800000 +1987-03-09,290.66,290.66,287.12,288.3,165400000 +1987-03-06,290.52,290.67,288.77,290.66,181600000 +1987-03-05,288.62,291.24,288.6,290.52,205400000 +1987-03-04,284.12,288.62,284.12,288.62,198400000 +1987-03-03,283,284.19,282.92,284.12,149200000 +1987-03-02,284.17,284.83,282.3,283,156700000 +1987-02-27,282.96,284.55,282.77,284.2,142800000 +1987-02-26,284,284.4,280.73,282.96,165800000 +1987-02-25,282.88,285.35,282.14,284,184100000 +1987-02-24,282.38,283.33,281.45,282.88,151300000 +1987-02-23,285.48,285.5,279.37,282.38,170500000 +1987-02-20,285.57,285.98,284.31,285.48,175800000 +1987-02-19,285.42,286.24,283.84,285.57,181500000 +1987-02-18,285.49,287.55,282.97,285.42,218200000 +1987-02-17,279.7,285.49,279.7,285.49,187800000 +1987-02-13,275.62,280.91,275.01,279.7,184400000 +1987-02-12,277.54,278.04,273.89,275.62,200400000 +1987-02-11,275.07,277.71,274.71,277.54,172400000 +1987-02-10,278.16,278.16,273.49,275.07,168300000 +1987-02-09,280.04,280.04,277.24,278.16,143300000 +1987-02-06,281.16,281.79,279.87,280.04,184100000 +1987-02-05,279.64,282.26,278.66,281.16,256700000 +1987-02-04,275.99,279.65,275.35,279.64,222400000 +1987-02-03,276.45,277.83,275.84,275.99,198100000 +1987-02-02,274.08,277.35,273.16,276.45,177400000 +1987-01-30,274.24,274.24,271.38,274.08,163400000 +1987-01-29,275.4,276.85,272.54,274.24,205300000 +1987-01-28,273.75,275.71,273.03,275.4,195800000 +1987-01-27,269.61,274.31,269.61,273.75,192300000 +1987-01-26,270.1,270.4,267.73,269.61,138900000 +1987-01-23,273.91,280.96,268.41,270.1,302400000 +1987-01-22,267.84,274.05,267.32,273.91,188700000 +1987-01-21,269.04,270.87,267.35,267.84,184200000 +1987-01-20,269.34,271.03,267.65,269.04,224800000 +1987-01-19,266.26,269.34,264,269.34,162800000 +1987-01-16,265.46,267.24,264.31,266.28,218400000 +1987-01-15,262.65,266.68,262.64,265.49,253100000 +1987-01-14,259.95,262.72,259.62,262.64,214200000 +1987-01-13,260.3,260.45,259.21,259.95,170900000 +1987-01-12,258.72,261.36,257.92,260.3,184200000 +1987-01-09,257.26,259.2,256.11,258.73,193000000 +1987-01-08,255.36,257.28,254.97,257.28,194500000 +1987-01-07,252.78,255.72,252.65,255.33,190900000 +1987-01-06,252.2,253.99,252.14,252.78,189300000 +1987-01-05,246.45,252.57,246.45,252.19,181900000 +1987-01-02,242.17,246.45,242.17,246.45,91880000 +1986-12-31,243.37,244.03,241.28,242.17,139200000 +1986-12-30,244.66,244.67,243.04,243.37,126200000 +1986-12-29,246.9,246.92,244.31,244.67,99800000 +1986-12-26,246.75,247.09,246.73,246.92,48860000 +1986-12-24,246.34,247.22,246.02,246.75,95410000 +1986-12-23,248.75,248.75,245.85,246.34,188700000 +1986-12-22,249.73,249.73,247.45,248.75,157600000 +1986-12-19,246.79,249.96,245.89,249.73,244700000 +1986-12-18,247.56,247.81,246.45,246.78,155400000 +1986-12-17,250.01,250.04,247.19,247.56,148800000 +1986-12-16,248.21,250.04,247.4,250.04,157000000 +1986-12-15,247.31,248.23,244.92,248.21,148200000 +1986-12-12,248.17,248.31,247.02,247.35,126600000 +1986-12-11,250.97,250.98,247.15,248.17,136000000 +1986-12-10,249.28,251.53,248.94,250.96,139700000 +1986-12-09,251.16,251.27,249.25,249.28,128700000 +1986-12-08,251.16,252.36,248.82,251.16,159000000 +1986-12-05,253.05,253.89,250.71,251.17,139800000 +1986-12-04,253.85,254.42,252.88,253.04,156900000 +1986-12-03,254,254.87,253.24,253.85,200100000 +1986-12-02,249.06,254,249.05,254,230400000 +1986-12-01,249.22,249.22,245.72,249.05,133800000 +1986-11-28,248.82,249.22,248.07,249.22,93530000 +1986-11-26,248.14,248.9,247.73,248.77,152000000 +1986-11-25,247.44,248.18,246.3,248.17,154600000 +1986-11-24,245.86,248,245.21,247.45,150800000 +1986-11-21,242.03,246.38,241.97,245.86,200700000 +1986-11-20,237.66,242.05,237.66,242.05,158100000 +1986-11-19,236.77,237.94,235.51,237.66,183300000 +1986-11-18,243.2,243.23,236.65,236.78,185300000 +1986-11-17,244.5,244.8,242.29,243.21,133300000 +1986-11-14,243.01,244.51,241.96,244.5,172100000 +1986-11-13,246.63,246.66,242.98,243.02,164000000 +1986-11-12,247.06,247.67,245.68,246.64,162200000 +1986-11-11,246.15,247.1,246.12,247.08,118500000 +1986-11-10,245.75,246.22,244.68,246.13,120200000 +1986-11-07,245.85,246.13,244.92,245.77,142300000 +1986-11-06,246.54,246.9,244.3,245.87,165300000 +1986-11-05,246.09,247.05,245.21,246.58,183200000 +1986-11-04,245.8,246.43,244.42,246.2,163200000 +1986-11-03,243.97,245.8,243.93,245.8,138200000 +1986-10-31,243.7,244.51,242.95,243.98,147200000 +1986-10-30,240.97,244.08,240.94,243.71,194200000 +1986-10-29,239.23,241,238.98,240.94,164400000 +1986-10-28,238.81,240.58,238.77,239.26,145900000 +1986-10-27,238.22,238.77,236.72,238.77,133200000 +1986-10-24,239.3,239.65,238.25,238.26,137500000 +1986-10-23,236.28,239.76,236.26,239.28,150900000 +1986-10-22,235.89,236.64,235.82,236.26,114000000 +1986-10-21,236.03,236.49,234.95,235.88,110000000 +1986-10-20,238.84,238.84,234.78,235.97,109000000 +1986-10-17,239.5,239.53,237.71,238.84,124100000 +1986-10-16,238.83,240.18,238.8,239.53,156900000 +1986-10-15,235.36,239.03,235.27,238.8,144300000 +1986-10-14,235.9,236.37,234.37,235.37,116800000 +1986-10-13,235.52,235.91,235.02,235.91,54990000 +1986-10-10,235.84,236.27,235.31,235.48,105100000 +1986-10-09,236.67,238.2,235.72,235.85,153400000 +1986-10-08,234.41,236.84,233.68,236.68,141700000 +1986-10-07,234.74,235.18,233.46,234.41,125100000 +1986-10-06,233.71,235.34,233.17,234.78,88250000 +1986-10-03,233.92,236.16,232.79,233.71,128100000 +1986-10-02,233.6,234.33,232.77,233.92,128100000 +1986-10-01,231.32,234.62,231.32,233.6,143600000 +1986-09-30,229.91,233.01,229.91,231.32,124900000 +1986-09-29,232.23,232.23,228.08,229.91,115600000 +1986-09-26,231.83,233.68,230.64,232.23,115300000 +1986-09-25,231.83,236.28,230.67,231.83,134300000 +1986-09-24,235.66,237.06,235.53,236.28,134600000 +1986-09-23,234.96,235.88,234.5,235.67,132600000 +1986-09-22,232.2,234.93,232.2,234.93,126100000 +1986-09-19,232.3,232.31,230.69,232.21,153900000 +1986-09-18,231.67,232.87,230.57,232.31,132200000 +1986-09-17,231.73,233.81,231.38,231.68,141000000 +1986-09-16,231.93,231.94,228.32,231.72,131200000 +1986-09-15,230.67,232.82,229.44,231.94,155600000 +1986-09-12,235.18,235.45,228.74,230.67,240500000 +1986-09-11,247.06,247.06,234.67,235.18,237600000 +1986-09-10,247.67,247.76,246.11,247.06,140300000 +1986-09-09,248.14,250.21,246.94,247.67,137500000 +1986-09-08,250.47,250.47,247.02,248.14,153300000 +1986-09-05,253.83,254.13,250.33,250.47,180600000 +1986-09-04,250.08,254.01,250.03,253.83,189400000 +1986-09-03,248.52,250.08,247.59,250.08,154300000 +1986-09-02,252.93,253.3,248.14,248.52,135500000 +1986-08-29,252.84,254.07,251.73,252.93,125300000 +1986-08-28,253.3,253.67,251.91,252.84,125100000 +1986-08-27,252.84,254.24,252.66,253.3,143300000 +1986-08-26,247.81,252.91,247.81,252.84,156600000 +1986-08-25,250.19,250.26,247.76,247.81,104400000 +1986-08-22,249.67,250.61,249.27,250.19,118100000 +1986-08-21,249.77,250.45,249.11,249.67,135200000 +1986-08-20,246.53,249.77,246.51,249.77,156600000 +1986-08-19,247.38,247.42,245.82,246.51,109300000 +1986-08-18,247.15,247.83,245.48,247.38,112800000 +1986-08-15,246.25,247.15,245.7,247.15,123500000 +1986-08-14,245.67,246.79,245.53,246.25,123800000 +1986-08-13,243.34,246.51,243.06,245.67,156400000 +1986-08-12,240.68,243.37,240.35,243.34,131700000 +1986-08-11,236.88,241.2,236.87,240.68,125600000 +1986-08-08,237.04,238.06,236.37,236.88,106300000 +1986-08-07,236.84,238.02,236.31,237.04,122400000 +1986-08-06,237.03,237.35,235.48,236.84,127500000 +1986-08-05,235.99,238.31,235.97,237.03,153100000 +1986-08-04,234.91,236.86,231.92,235.99,130000000 +1986-08-01,236.12,236.89,234.59,234.91,114900000 +1986-07-31,236.59,236.92,235.89,236.12,112700000 +1986-07-30,234.57,237.38,233.07,236.59,146700000 +1986-07-29,235.72,236.01,234.4,234.55,115700000 +1986-07-28,240.2,240.25,235.23,236.01,128000000 +1986-07-25,237.99,240.36,237.95,240.22,132000000 +1986-07-24,238.69,239.05,237.32,237.95,134700000 +1986-07-23,238.19,239.25,238.17,238.67,133300000 +1986-07-22,236.24,238.42,235.92,238.18,138500000 +1986-07-21,236.36,236.45,235.53,236.24,106300000 +1986-07-18,236.07,238.22,233.94,236.36,149700000 +1986-07-17,235.01,236.65,235.01,236.07,132400000 +1986-07-16,233.66,236.19,233.66,235.01,160800000 +1986-07-15,238.09,238.12,233.6,233.66,184000000 +1986-07-14,242.22,242.22,238.04,238.11,123200000 +1986-07-11,243.01,243.48,241.68,242.22,124500000 +1986-07-10,242.82,243.44,239.66,243.01,146200000 +1986-07-09,241.59,243.07,241.46,242.82,142900000 +1986-07-08,244.05,244.06,239.07,241.59,174100000 +1986-07-07,251.79,251.81,243.63,244.05,138200000 +1986-07-03,252.7,252.94,251.23,251.79,108300000 +1986-07-02,252.04,253.2,251.79,252.7,150000000 +1986-07-01,250.67,252.04,250.53,252.04,147700000 +1986-06-30,249.6,251.81,249.6,250.84,135100000 +1986-06-27,248.74,249.74,248.74,249.6,123800000 +1986-06-26,248.93,249.43,247.72,248.74,134100000 +1986-06-25,247.03,250.13,247.03,248.93,161800000 +1986-06-24,245.26,248.26,244.53,247.03,140600000 +1986-06-23,247.58,247.58,244.45,245.26,123800000 +1986-06-20,244.06,247.6,243.98,247.58,149100000 +1986-06-19,244.99,245.8,244.05,244.06,129000000 +1986-06-18,244.35,245.25,242.57,244.99,117000000 +1986-06-17,246.13,246.26,243.6,244.35,123100000 +1986-06-16,245.73,246.5,245.17,246.13,112100000 +1986-06-13,241.71,245.91,241.71,245.73,141200000 +1986-06-12,241.24,241.64,240.7,241.49,109100000 +1986-06-11,239.58,241.13,239.21,241.13,127400000 +1986-06-10,239.96,240.08,238.23,239.58,125000000 +1986-06-09,245.67,245.67,239.68,239.96,123300000 +1986-06-06,245.65,246.07,244.43,245.67,110900000 +1986-06-05,243.94,245.66,243.41,245.65,110900000 +1986-06-04,245.51,246.3,242.59,243.94,117000000 +1986-06-03,245.04,245.51,243.67,245.51,114700000 +1986-06-02,246.04,247.74,243.83,245.04,120600000 +1986-05-30,247.98,249.19,246.43,247.35,151200000 +1986-05-29,246.63,248.32,245.29,247.98,135700000 +1986-05-28,244.75,247.4,244.75,246.63,159600000 +1986-05-27,241.35,244.76,241.35,244.75,121200000 +1986-05-23,240.12,242.16,240.12,241.35,130200000 +1986-05-22,235.45,240.25,235.45,240.12,144900000 +1986-05-21,236.11,236.83,235.45,235.45,117100000 +1986-05-20,233.2,236.12,232.58,236.11,113000000 +1986-05-19,232.76,233.54,232.41,233.2,85840000 +1986-05-16,234.43,234.43,232.26,232.76,113500000 +1986-05-15,237.54,237.54,233.93,234.43,131600000 +1986-05-14,236.41,237.54,235.85,237.54,132100000 +1986-05-13,237.58,237.87,236.02,236.41,119200000 +1986-05-12,237.85,238.53,237.02,237.58,125400000 +1986-05-09,237.13,238.01,235.85,237.85,137400000 +1986-05-08,236.08,237.96,236.08,237.13,136000000 +1986-05-07,236.56,237.24,233.98,236.08,129900000 +1986-05-06,237.73,238.28,236.26,237.24,121200000 +1986-05-05,234.79,237.73,234.79,237.73,102400000 +1986-05-02,235.16,236.52,234.15,234.79,126300000 +1986-05-01,235.52,236.01,234.21,235.16,146500000 +1986-04-30,240.52,240.52,235.26,235.52,147500000 +1986-04-29,243.08,243.57,239.23,240.51,148800000 +1986-04-28,242.29,243.08,241.23,243.08,123900000 +1986-04-25,242.02,242.8,240.91,242.29,142300000 +1986-04-24,241.75,243.13,241.65,242.02,146600000 +1986-04-23,242.42,242.42,240.08,241.75,149700000 +1986-04-22,244.74,245.47,241.3,242.42,161500000 +1986-04-21,242.38,244.78,241.88,244.74,136100000 +1986-04-18,243.03,243.47,241.74,242.38,153600000 +1986-04-17,242.22,243.36,241.89,243.03,161400000 +1986-04-16,237.73,242.57,237.73,242.22,173800000 +1986-04-15,237.28,238.09,236.64,237.73,123700000 +1986-04-14,235.97,237.48,235.43,237.28,106700000 +1986-04-11,236.44,237.85,235.13,235.97,139400000 +1986-04-10,233.75,236.54,233.75,236.44,184800000 +1986-04-09,233.52,235.57,232.13,233.75,156300000 +1986-04-08,228.63,233.7,228.63,233.52,146300000 +1986-04-07,228.69,228.83,226.3,228.63,129800000 +1986-04-04,232.47,232.56,228.32,228.69,147300000 +1986-04-03,235.71,236.42,232.07,232.47,148200000 +1986-04-02,235.14,235.71,233.4,235.71,145300000 +1986-04-01,238.9,239.1,234.57,235.14,167400000 +1986-03-31,238.97,239.86,238.08,238.9,134400000 +1986-03-27,237.3,240.11,237.3,238.97,178100000 +1986-03-26,234.72,237.79,234.71,237.3,161500000 +1986-03-25,235.33,235.33,233.62,234.72,139300000 +1986-03-24,233.34,235.33,232.92,235.33,143800000 +1986-03-21,236.54,237.35,233.29,233.34,199100000 +1986-03-20,235.6,237.09,235.6,236.54,148000000 +1986-03-19,235.78,236.52,235.13,235.6,150000000 +1986-03-18,234.67,236.52,234.14,235.78,148000000 +1986-03-17,236.55,236.55,233.69,234.67,137500000 +1986-03-14,233.19,236.55,232.58,236.55,181900000 +1986-03-13,232.54,233.89,231.27,233.19,171500000 +1986-03-12,231.69,234.7,231.68,232.54,210300000 +1986-03-11,226.58,231.81,226.58,231.69,187300000 +1986-03-10,225.57,226.98,225.36,226.58,129900000 +1986-03-07,225.13,226.33,224.44,225.57,163200000 +1986-03-06,224.39,225.5,224.13,225.13,159000000 +1986-03-05,224.14,224.37,222.18,224.34,154600000 +1986-03-04,225.42,227.33,223.94,224.38,174500000 +1986-03-03,226.92,226.92,224.41,225.42,142700000 +1986-02-28,226.77,227.92,225.42,226.92,191700000 +1986-02-27,224.04,226.88,223.41,226.77,181700000 +1986-02-26,223.72,224.59,223.15,224.04,158000000 +1986-02-25,224.34,224.4,222.63,223.79,148000000 +1986-02-24,224.58,225.29,223.31,224.34,144700000 +1986-02-21,222.22,224.62,222.22,224.62,177600000 +1986-02-20,219.76,222.22,219.22,222.22,139700000 +1986-02-19,222.45,222.96,219.73,219.76,152000000 +1986-02-18,219.76,222.45,219.26,222.45,160200000 +1986-02-14,217.4,219.76,217.22,219.76,155600000 +1986-02-13,215.97,217.41,215.38,217.4,136500000 +1986-02-12,215.92,216.28,215.13,215.97,136400000 +1986-02-11,216.24,216.67,215.54,215.92,141300000 +1986-02-10,214.56,216.24,214.47,216.24,129900000 +1986-02-07,213.47,215.27,211.13,214.56,144400000 +1986-02-06,212.96,214.51,212.6,213.47,146100000 +1986-02-05,212.84,213.03,211.21,212.96,134300000 +1986-02-04,213.96,214.57,210.82,212.79,175700000 +1986-02-03,211.78,214.18,211.6,213.96,145300000 +1986-01-31,209.33,212.42,209.19,211.78,143500000 +1986-01-30,210.29,211.54,209.15,209.33,125300000 +1986-01-29,209.81,212.36,209.81,210.29,193800000 +1986-01-28,207.42,209.82,207.4,209.81,145700000 +1986-01-27,206.43,207.69,206.43,207.39,122900000 +1986-01-24,204.25,206.43,204.25,206.43,128900000 +1986-01-23,203.49,204.43,202.6,204.25,130300000 +1986-01-22,205.79,206.03,203.41,203.49,131200000 +1986-01-21,207.53,207.78,205.05,205.79,128300000 +1986-01-20,208.43,208.43,206.62,207.53,85340000 +1986-01-17,209.17,209.4,207.59,208.43,132100000 +1986-01-16,208.26,209.18,207.61,209.17,130500000 +1986-01-15,206.64,208.27,206.64,208.26,122400000 +1986-01-14,206.72,207.37,206.06,206.64,113900000 +1986-01-13,205.96,206.83,205.52,206.72,108700000 +1986-01-10,206.11,207.33,205.52,205.96,122800000 +1986-01-09,207.97,207.97,204.51,206.11,176500000 +1986-01-08,213.8,214.57,207.49,207.97,180300000 +1986-01-07,210.65,213.8,210.65,213.8,153000000 +1986-01-06,210.88,210.98,209.93,210.65,99610000 +1986-01-03,209.59,210.88,209.51,210.88,105000000 +1986-01-02,211.28,211.28,208.93,209.59,98960000 +1985-12-31,210.68,211.61,210.68,211.28,112700000 +1985-12-30,209.61,210.7,209.17,210.68,91970000 +1985-12-27,207.65,209.62,207.65,209.61,81560000 +1985-12-26,207.14,207.76,207.05,207.65,62050000 +1985-12-24,208.57,208.57,206.44,207.14,78300000 +1985-12-23,210.57,210.94,208.44,208.57,107900000 +1985-12-20,210.02,211.77,210.02,210.94,170300000 +1985-12-19,209.81,210.13,209.25,210.02,130200000 +1985-12-18,210.65,211.23,209.24,209.81,137900000 +1985-12-17,212.02,212.45,210.58,210.65,155200000 +1985-12-16,209.94,213.08,209.91,212.02,176000000 +1985-12-13,206.73,210.31,206.73,209.94,177900000 +1985-12-12,206.31,207.65,205.83,206.73,170500000 +1985-12-11,204.39,206.68,204.17,206.31,178500000 +1985-12-10,204.25,205.16,203.68,204.39,156500000 +1985-12-09,202.99,204.65,202.98,204.25,144000000 +1985-12-06,203.88,203.88,202.45,202.99,125500000 +1985-12-05,204.23,205.86,203.79,203.88,181000000 +1985-12-04,200.86,204.23,200.86,204.23,153200000 +1985-12-03,200.46,200.98,200.1,200.86,109700000 +1985-12-02,202.17,202.19,200.2,200.46,103500000 +1985-11-29,202.54,203.4,201.92,202.17,84060000 +1985-11-27,200.67,202.65,200.67,202.54,143700000 +1985-11-26,200.35,201.16,200.11,200.67,123100000 +1985-11-25,201.52,201.52,200.08,200.35,91710000 +1985-11-22,201.41,202.01,201.05,201.52,133800000 +1985-11-21,198.99,201.43,198.99,201.41,150300000 +1985-11-20,198.67,199.2,198.52,198.99,105100000 +1985-11-19,198.71,199.52,198.01,198.67,126100000 +1985-11-18,198.11,198.71,197.51,198.71,108400000 +1985-11-15,199.06,199.58,197.9,198.11,130200000 +1985-11-14,197.1,199.19,196.88,199.06,124900000 +1985-11-13,198.08,198.11,196.91,197.1,109700000 +1985-11-12,197.28,198.66,196.97,198.08,170800000 +1985-11-11,193.72,197.29,193.7,197.28,126500000 +1985-11-08,192.62,193.97,192.53,193.72,115000000 +1985-11-07,192.78,192.96,192.16,192.62,119000000 +1985-11-06,192.37,193.01,191.83,192.76,129500000 +1985-11-05,191.25,192.43,190.99,192.37,119200000 +1985-11-04,191.45,191.96,190.66,191.25,104900000 +1985-11-01,189.82,191.53,189.37,191.53,129400000 +1985-10-31,190.07,190.15,189.35,189.82,121500000 +1985-10-30,189.23,190.09,189.14,190.07,120400000 +1985-10-29,187.76,189.78,187.76,189.23,110600000 +1985-10-28,187.52,187.76,186.93,187.76,97880000 +1985-10-25,188.5,188.51,187.32,187.52,101800000 +1985-10-24,189.09,189.45,188.41,188.5,123100000 +1985-10-23,188.04,189.09,188.04,189.09,121700000 +1985-10-22,186.96,188.56,186.96,188.04,111300000 +1985-10-21,187.04,187.3,186.79,186.96,95680000 +1985-10-18,187.66,188.11,186.89,187.04,107100000 +1985-10-17,187.98,188.52,187.42,187.66,140500000 +1985-10-16,186.08,187.98,186.08,187.98,117400000 +1985-10-15,186.37,187.16,185.66,186.08,110400000 +1985-10-14,184.31,186.37,184.28,186.37,78540000 +1985-10-11,182.78,184.28,182.61,184.28,96370000 +1985-10-10,182.52,182.79,182.05,182.78,90910000 +1985-10-09,181.87,183.27,181.87,182.52,99140000 +1985-10-08,181.87,182.3,181.16,181.87,97170000 +1985-10-07,183.22,183.22,181.3,181.87,95550000 +1985-10-04,184.36,184.36,182.65,183.22,101200000 +1985-10-03,184.06,185.17,183.59,184.36,127500000 +1985-10-02,185.07,185.94,184.06,184.06,147300000 +1985-10-01,182.06,185.08,182.02,185.07,130200000 +1985-09-30,181.3,182.08,181.22,182.08,103600000 +1985-09-26,180.66,181.29,179.45,181.29,106100000 +1985-09-25,182.62,182.62,180.62,180.66,92120000 +1985-09-24,184.3,184.3,182.42,182.62,97870000 +1985-09-23,182.05,184.65,182.05,184.3,104800000 +1985-09-20,183.39,183.99,182.04,182.05,101400000 +1985-09-19,181.71,183.4,181.71,183.39,100300000 +1985-09-18,181.36,181.83,180.81,181.71,105700000 +1985-09-17,182.88,182.88,180.78,181.36,111900000 +1985-09-16,182.91,182.91,182.45,182.88,66700000 +1985-09-13,183.69,184.19,182.05,182.91,111400000 +1985-09-12,185.03,185.21,183.49,183.69,107100000 +1985-09-11,186.9,186.9,184.79,185.03,100400000 +1985-09-10,188.25,188.26,186.5,186.9,104700000 +1985-09-09,188.24,188.8,187.9,188.25,89850000 +1985-09-06,187.27,188.43,187.27,188.24,95040000 +1985-09-05,187.37,187.52,186.89,187.27,94480000 +1985-09-04,187.91,187.92,186.97,187.37,85510000 +1985-09-03,188.63,188.63,187.38,187.91,81190000 +1985-08-30,188.93,189.13,188,188.63,81620000 +1985-08-29,188.73,188.94,188.38,188.93,85660000 +1985-08-28,188.1,188.83,187.9,188.83,88530000 +1985-08-27,187.31,188.1,187.31,188.1,82140000 +1985-08-26,187.17,187.44,186.46,187.31,70290000 +1985-08-23,187.22,187.35,186.59,187.17,75270000 +1985-08-22,189.11,189.23,187.2,187.36,90600000 +1985-08-21,188.08,189.16,188.08,189.16,94880000 +1985-08-20,186.38,188.27,186.38,188.08,91230000 +1985-08-19,186.1,186.82,186.1,186.38,67930000 +1985-08-16,187.26,187.26,186.1,186.1,87910000 +1985-08-15,187.41,187.74,186.62,187.26,86100000 +1985-08-14,187.3,187.87,187.3,187.41,85780000 +1985-08-13,187.63,188.15,186.51,187.3,80300000 +1985-08-12,188.32,188.32,187.43,187.63,77340000 +1985-08-09,188.95,189.05,188.11,188.32,81750000 +1985-08-08,187.68,188.96,187.68,188.95,102900000 +1985-08-07,187.93,187.93,187.39,187.68,100000000 +1985-08-06,190.62,190.72,187.87,187.93,104000000 +1985-08-05,191.48,191.48,189.95,190.62,79610000 +1985-08-02,192.11,192.11,191.27,191.48,87860000 +1985-08-01,190.92,192.17,190.91,192.11,121500000 +1985-07-31,189.93,191.33,189.93,190.92,124200000 +1985-07-30,189.62,190.05,189.3,189.93,102300000 +1985-07-29,192.4,192.42,189.53,189.6,95960000 +1985-07-26,192.06,192.78,191.58,192.4,107000000 +1985-07-25,191.58,192.23,191.17,192.06,123300000 +1985-07-24,192.55,192.55,190.66,191.58,128600000 +1985-07-23,194.35,194.98,192.28,192.55,143600000 +1985-07-22,195.13,195.13,193.58,194.35,93540000 +1985-07-19,194.38,195.13,194.28,195.13,114800000 +1985-07-18,195.65,195.65,194.34,194.38,131400000 +1985-07-17,194.86,196.07,194.72,195.65,159900000 +1985-07-16,192.72,194.72,192.72,194.72,132500000 +1985-07-15,193.29,193.84,192.55,192.72,103900000 +1985-07-12,192.94,193.32,192.64,193.29,120300000 +1985-07-11,192.37,192.95,192.28,192.94,122800000 +1985-07-10,191.05,192.37,190.99,192.37,108200000 +1985-07-09,191.93,191.93,190.81,191.05,99060000 +1985-07-08,192.47,192.52,191.26,191.93,83670000 +1985-07-05,191.45,192.67,191.45,192.52,62450000 +1985-07-03,192.01,192.08,191.37,191.45,98410000 +1985-07-02,192.43,192.63,191.84,192.01,111100000 +1985-07-01,191.85,192.43,191.17,192.43,96080000 +1985-06-28,191.23,191.85,191.04,191.85,105200000 +1985-06-27,190.06,191.36,190.06,191.23,106700000 +1985-06-26,189.74,190.26,189.44,190.06,94130000 +1985-06-25,189.15,190.96,189.15,189.74,115700000 +1985-06-24,188.77,189.61,187.84,189.15,96040000 +1985-06-21,186.73,189.66,186.43,189.61,125400000 +1985-06-20,186.63,186.74,185.97,186.73,87500000 +1985-06-19,187.34,187.98,186.63,186.63,108300000 +1985-06-18,186.53,187.65,186.51,187.34,106900000 +1985-06-17,187.1,187.1,185.98,186.53,82170000 +1985-06-14,185.33,187.1,185.33,187.1,93090000 +1985-06-13,187.61,187.61,185.03,185.33,107000000 +1985-06-12,189.04,189.04,187.59,187.61,97700000 +1985-06-11,189.51,189.61,188.78,189.04,102100000 +1985-06-10,189.68,189.68,188.82,189.51,87940000 +1985-06-07,191.06,191.29,189.55,189.68,99630000 +1985-06-06,189.75,191.06,189.13,191.06,117200000 +1985-06-05,190.04,191.02,190.04,190.16,143900000 +1985-06-04,189.32,190.27,188.88,190.04,115400000 +1985-06-03,189.55,190.36,188.93,189.32,125000000 +1985-05-31,187.75,189.59,187.45,189.55,134100000 +1985-05-30,187.68,188.04,187.09,187.75,108300000 +1985-05-29,187.86,187.86,187.11,187.68,96540000 +1985-05-28,188.29,188.94,187.38,187.86,90600000 +1985-05-24,187.6,188.29,187.29,188.29,85970000 +1985-05-23,188.56,188.56,187.45,187.6,101000000 +1985-05-22,189.64,189.64,187.71,188.56,101400000 +1985-05-21,189.72,189.81,188.78,189.64,130200000 +1985-05-20,187.42,189.98,187.42,189.72,146300000 +1985-05-17,185.66,187.94,185.47,187.42,124600000 +1985-05-16,184.54,185.74,184.54,185.66,99420000 +1985-05-15,183.87,185.43,183.86,184.54,106100000 +1985-05-14,184.61,185.17,183.65,183.87,97360000 +1985-05-13,184.28,184.61,184.19,184.61,85830000 +1985-05-10,181.92,184.74,181.92,184.28,140300000 +1985-05-09,180.62,181.97,180.62,181.92,111000000 +1985-05-08,180.76,180.76,179.96,180.62,101300000 +1985-05-07,179.99,181.09,179.87,180.76,100200000 +1985-05-06,180.08,180.56,179.82,179.99,85650000 +1985-05-03,179.01,180.3,179.01,180.08,94870000 +1985-05-02,178.37,179.01,178.37,179.01,107700000 +1985-05-01,179.83,180.04,178.35,178.37,101600000 +1985-04-30,180.63,180.63,178.86,179.83,111800000 +1985-04-29,182.18,182.34,180.62,180.63,88860000 +1985-04-26,183.43,183.61,182.11,182.18,86570000 +1985-04-25,182.26,183.43,182.12,183.43,108600000 +1985-04-24,181.88,182.27,181.74,182.26,99600000 +1985-04-23,180.7,181.97,180.34,181.88,108900000 +1985-04-22,181.11,181.23,180.25,180.7,79930000 +1985-04-19,180.84,181.25,180.42,181.11,81110000 +1985-04-18,181.68,182.56,180.75,180.84,100600000 +1985-04-17,181.2,181.91,181.14,181.68,96020000 +1985-04-16,180.92,181.78,180.19,181.2,98480000 +1985-04-15,180.54,181.15,180.45,180.92,80660000 +1985-04-12,180.19,180.55,180.06,180.54,86220000 +1985-04-11,179.42,180.91,179.42,180.19,108400000 +1985-04-10,178.21,179.9,178.21,179.42,108200000 +1985-04-09,178.03,178.67,177.97,178.21,83980000 +1985-04-08,179.03,179.46,177.86,178.03,79960000 +1985-04-04,179.11,179.13,178.29,179.03,86910000 +1985-04-03,180.53,180.53,178.64,179.11,95480000 +1985-04-02,181.27,181.86,180.28,180.53,101700000 +1985-04-01,180.66,181.27,180.43,181.27,89900000 +1985-03-29,179.54,180.66,179.54,180.66,101400000 +1985-03-28,179.54,180.6,179.43,179.54,99780000 +1985-03-27,178.43,179.8,178.43,179.54,101000000 +1985-03-26,177.97,178.86,177.88,178.43,89930000 +1985-03-25,179.04,179.04,177.85,177.97,74040000 +1985-03-22,179.35,179.92,178.86,179.04,99250000 +1985-03-21,179.08,180.22,178.89,179.35,95930000 +1985-03-20,179.54,179.78,178.79,179.08,107500000 +1985-03-19,176.88,179.56,176.87,179.54,119200000 +1985-03-18,176.53,177.66,176.53,176.88,94020000 +1985-03-15,177.84,178.41,176.53,176.53,105200000 +1985-03-14,178.19,178.53,177.61,177.84,103400000 +1985-03-13,179.66,179.96,178.02,178.19,101700000 +1985-03-12,178.79,180.14,178.7,179.66,92840000 +1985-03-11,179.1,179.46,178.15,178.79,84110000 +1985-03-08,179.51,179.97,179.07,179.1,96390000 +1985-03-07,180.65,180.65,179.44,179.51,112100000 +1985-03-06,182.23,182.25,180.59,180.65,116900000 +1985-03-05,182.06,182.65,181.42,182.23,116400000 +1985-03-04,183.23,183.41,181.4,182.06,102100000 +1985-03-01,181.18,183.89,181.16,183.23,139900000 +1985-02-28,180.71,181.21,180.33,181.18,100700000 +1985-02-27,181.17,181.87,180.5,180.71,107700000 +1985-02-26,179.23,181.58,179.16,181.17,114200000 +1985-02-25,179.36,179.36,178.13,179.23,89740000 +1985-02-22,180.19,180.41,179.23,179.36,93680000 +1985-02-21,181.18,181.18,180.02,180.19,104000000 +1985-02-20,181.33,182.1,180.64,181.18,118200000 +1985-02-19,181.6,181.61,180.95,181.33,90400000 +1985-02-15,182.41,182.65,181.23,181.6,106500000 +1985-02-14,183.35,183.95,182.39,182.41,139700000 +1985-02-13,180.56,183.86,180.5,183.35,142500000 +1985-02-12,180.51,180.75,179.45,180.56,111100000 +1985-02-11,182.19,182.19,180.11,180.51,104000000 +1985-02-08,181.82,182.39,181.67,182.19,116500000 +1985-02-07,180.43,181.96,180.43,181.82,151700000 +1985-02-06,180.61,181.5,180.32,180.43,141000000 +1985-02-05,180.35,181.53,180.07,180.61,143900000 +1985-02-04,178.63,180.35,177.75,180.35,113700000 +1985-02-01,179.63,179.63,178.44,178.63,105400000 +1985-01-31,179.39,179.83,178.56,179.63,132500000 +1985-01-30,179.18,180.27,179.05,179.39,170000000 +1985-01-29,177.4,179.19,176.58,179.18,115700000 +1985-01-28,177.35,178.19,176.56,177.4,128400000 +1985-01-25,176.71,177.75,176.54,177.35,122400000 +1985-01-24,177.3,178.16,176.56,176.71,160700000 +1985-01-23,175.48,177.3,175.15,177.3,144400000 +1985-01-22,175.23,176.63,175.14,175.48,174800000 +1985-01-21,171.32,175.45,171.31,175.23,146800000 +1985-01-18,170.73,171.42,170.66,171.32,104700000 +1985-01-17,171.19,171.34,170.22,170.73,113600000 +1985-01-16,170.81,171.94,170.41,171.19,135500000 +1985-01-15,170.51,171.82,170.4,170.81,155300000 +1985-01-14,167.91,170.55,167.58,170.51,124900000 +1985-01-11,168.31,168.72,167.58,167.91,107600000 +1985-01-10,165.18,168.31,164.99,168.31,124700000 +1985-01-09,163.99,165.57,163.99,165.18,99230000 +1985-01-08,164.24,164.59,163.91,163.99,92110000 +1985-01-07,163.68,164.71,163.68,164.24,86190000 +1985-01-04,164.55,164.55,163.36,163.68,77480000 +1985-01-03,165.37,166.11,164.38,164.57,88880000 +1985-01-02,167.2,167.2,165.19,165.37,67820000 +1984-12-31,166.26,167.34,166.06,167.24,80260000 +1984-12-28,165.75,166.32,165.67,166.26,77070000 +1984-12-27,166.47,166.5,165.62,165.75,70100000 +1984-12-26,166.76,166.76,166.29,166.47,46700000 +1984-12-24,165.51,166.93,165.5,166.76,55550000 +1984-12-21,166.34,166.38,164.62,165.51,101200000 +1984-12-20,167.16,167.58,166.29,166.38,93220000 +1984-12-19,168.11,169.03,166.84,167.16,139600000 +1984-12-18,163.61,168.11,163.61,168.11,169000000 +1984-12-17,162.69,163.63,162.44,163.61,89490000 +1984-12-14,161.81,163.53,161.63,162.69,95060000 +1984-12-13,162.63,162.92,161.54,161.81,80850000 +1984-12-12,163.07,163.18,162.55,162.63,78710000 +1984-12-11,162.83,163.18,162.56,163.07,80240000 +1984-12-10,162.26,163.32,161.54,162.83,81140000 +1984-12-07,162.76,163.31,162.26,162.26,81000000 +1984-12-06,162.1,163.11,161.76,162.76,96560000 +1984-12-05,163.38,163.4,161.93,162.1,88700000 +1984-12-04,162.82,163.91,162.82,163.38,81250000 +1984-12-03,163.58,163.58,162.29,162.82,95300000 +1984-11-30,163.91,163.91,162.99,163.58,77580000 +1984-11-29,165.02,165.02,163.78,163.91,75860000 +1984-11-28,166.29,166.9,164.97,165.02,86300000 +1984-11-27,165.55,166.85,165.07,166.29,95470000 +1984-11-26,166.92,166.92,165.37,165.55,76520000 +1984-11-23,164.52,166.92,164.52,166.92,73910000 +1984-11-21,164.18,164.68,163.29,164.51,81620000 +1984-11-20,163.1,164.47,163.1,164.18,83240000 +1984-11-19,164.1,164.34,163.03,163.09,69730000 +1984-11-16,165.89,166.24,164.09,164.1,83140000 +1984-11-15,165.99,166.49,165.61,165.89,81530000 +1984-11-14,165.97,166.43,165.39,165.99,73940000 +1984-11-13,167.36,167.38,165.79,165.97,69790000 +1984-11-12,167.65,167.65,166.67,167.36,55610000 +1984-11-09,168.68,169.46,167.44,167.6,83620000 +1984-11-08,169.19,169.27,168.27,168.68,88580000 +1984-11-07,170.41,170.41,168.44,169.17,110800000 +1984-11-06,168.58,170.41,168.58,170.41,101200000 +1984-11-05,167.42,168.65,167.33,168.58,84730000 +1984-11-02,167.49,167.95,167.24,167.42,96810000 +1984-11-01,166.09,167.83,166.09,167.49,107300000 +1984-10-31,166.74,166.95,165.99,166.09,91890000 +1984-10-30,164.78,167.33,164.78,166.84,95200000 +1984-10-29,165.29,165.29,164.67,164.78,63200000 +1984-10-26,166.31,166.31,164.93,165.29,83900000 +1984-10-25,167.2,167.62,166.17,166.31,92760000 +1984-10-24,167.09,167.54,166.82,167.2,91620000 +1984-10-23,167.36,168.27,166.83,167.09,92260000 +1984-10-22,167.96,168.36,167.26,167.36,81020000 +1984-10-19,168.08,169.62,167.31,167.96,186900000 +1984-10-18,164.14,168.1,163.8,168.1,149500000 +1984-10-17,164.78,165.04,163.71,164.14,99740000 +1984-10-16,165.78,165.78,164.66,164.78,82930000 +1984-10-15,164.18,166.15,164.09,165.77,87590000 +1984-10-12,162.78,164.47,162.78,164.18,92190000 +1984-10-11,162.11,162.87,162,162.78,87020000 +1984-10-10,161.67,162.12,160.02,162.11,94270000 +1984-10-09,162.13,162.84,161.62,161.67,76840000 +1984-10-08,162.68,162.68,161.8,162.13,46360000 +1984-10-05,162.92,163.32,162.51,162.68,82950000 +1984-10-04,162.44,163.22,162.44,162.92,76700000 +1984-10-03,163.59,163.59,162.2,162.44,92400000 +1984-10-02,164.62,165.24,163.55,163.59,89360000 +1984-10-01,166.1,166.1,164.48,164.62,73630000 +1984-09-28,166.96,166.96,165.77,166.1,78950000 +1984-09-27,166.75,167.18,166.33,166.96,88880000 +1984-09-26,165.62,167.2,165.61,166.28,100200000 +1984-09-25,165.28,165.97,164.45,165.62,86250000 +1984-09-24,165.67,166.12,164.98,165.28,76380000 +1984-09-21,167.47,168.67,165.66,165.67,120600000 +1984-09-20,166.94,167.47,166.7,167.47,92030000 +1984-09-19,167.65,168.76,166.89,166.94,119900000 +1984-09-18,168.87,168.87,167.64,167.65,107700000 +1984-09-17,168.78,169.37,167.99,168.87,88790000 +1984-09-14,167.94,169.65,167.94,168.78,137400000 +1984-09-13,164.68,167.94,164.68,167.94,110500000 +1984-09-12,164.45,164.81,164.14,164.68,77980000 +1984-09-11,165.22,166.17,164.28,164.45,101300000 +1984-09-10,164.37,165.05,163.06,164.26,74410000 +1984-09-07,165.65,166.31,164.22,164.37,84110000 +1984-09-06,164.29,165.95,164.29,165.65,91920000 +1984-09-05,164.88,164.88,163.84,164.29,69250000 +1984-09-04,166.68,166.68,164.73,164.88,62110000 +1984-08-31,166.6,166.68,165.78,166.68,57460000 +1984-08-30,167.1,167.19,166.55,166.6,70840000 +1984-08-29,167.4,168.21,167.03,167.09,90660000 +1984-08-28,166.44,167.43,166.21,167.4,70560000 +1984-08-27,167.51,167.51,165.81,166.44,57660000 +1984-08-24,167.12,167.52,167.12,167.51,69640000 +1984-08-23,167.06,167.78,166.61,167.12,83130000 +1984-08-22,167.83,168.8,166.92,167.06,116000000 +1984-08-21,164.94,168.22,164.93,167.83,128100000 +1984-08-20,164.14,164.94,163.76,164.94,75450000 +1984-08-17,164.3,164.61,163.78,164.14,71500000 +1984-08-16,162.8,164.42,162.75,163.77,93610000 +1984-08-15,164.42,164.42,162.75,162.8,91880000 +1984-08-14,165.43,166.09,164.28,164.42,81470000 +1984-08-13,164.84,165.49,163.98,165.43,77960000 +1984-08-10,165.54,168.59,165.24,165.42,171000000 +1984-08-09,161.75,165.88,161.47,165.54,131100000 +1984-08-08,162.71,163.87,161.75,161.75,121200000 +1984-08-07,162.6,163.58,160.81,162.72,127900000 +1984-08-06,162.35,165.27,162.09,162.6,203000000 +1984-08-03,160.28,162.56,158,162.35,236500000 +1984-08-02,154.08,157.99,154.08,157.99,172800000 +1984-08-01,150.66,154.08,150.66,154.08,127500000 +1984-07-31,150.19,150.77,149.65,150.66,86910000 +1984-07-30,151.19,151.19,150.14,150.19,72330000 +1984-07-27,150.08,151.38,149.99,151.19,101350000 +1984-07-26,148.83,150.16,148.83,150.08,90410000 +1984-07-25,147.82,149.3,147.26,148.83,90520000 +1984-07-24,148.95,149.28,147.78,147.82,74370000 +1984-07-23,149.55,149.55,147.85,148.95,77990000 +1984-07-20,150.37,150.58,149.07,149.55,79090000 +1984-07-19,151.4,151.4,150.27,150.37,85230000 +1984-07-18,152.38,152.38,151.11,151.4,76640000 +1984-07-17,151.6,152.6,151.26,152.38,82890000 +1984-07-16,150.88,151.6,150.01,151.6,73420000 +1984-07-13,150.03,151.16,150.03,150.88,75480000 +1984-07-12,150.56,151.06,149.63,150.03,86050000 +1984-07-11,152.89,152.89,150.55,150.56,89540000 +1984-07-10,153.36,153.53,152.57,152.89,74010000 +1984-07-09,152.24,153.53,151.44,153.36,74830000 +1984-07-06,152.76,152.76,151.63,152.24,65850000 +1984-07-05,153.7,153.87,152.71,152.76,66100000 +1984-07-03,153.2,153.86,153.1,153.7,69960000 +1984-07-02,153.16,153.22,152.44,153.2,69230000 +1984-06-29,152.84,154.08,152.82,153.18,90770000 +1984-06-28,151.64,153.07,151.62,152.84,77660000 +1984-06-27,152.71,152.88,151.3,151.64,78400000 +1984-06-26,153.97,153.97,152.47,152.71,82600000 +1984-06-25,154.46,154.67,153.86,153.97,72850000 +1984-06-22,154.51,154.92,153.89,154.46,98400000 +1984-06-21,154.84,155.64,154.05,154.51,123380000 +1984-06-20,151.89,154.84,150.96,154.84,99090000 +1984-06-19,151.73,153,151.73,152.61,98000000 +1984-06-18,149.03,151.92,148.53,151.73,94900000 +1984-06-15,150.49,150.71,149.02,149.03,85460000 +1984-06-14,152.12,152.14,150.31,150.39,79120000 +1984-06-13,152.19,152.85,151.86,152.13,67510000 +1984-06-12,153.06,153.07,151.61,152.19,84660000 +1984-06-11,155.17,155.17,153,153.06,69050000 +1984-06-08,154.92,155.4,154.57,155.17,67840000 +1984-06-07,155.01,155.11,154.36,154.92,82120000 +1984-06-06,153.65,155.03,153.38,155.01,83440000 +1984-06-05,154.34,154.34,153.28,153.65,84840000 +1984-06-04,153.24,155.1,153.24,154.34,96740000 +1984-06-01,150.55,153.24,150.55,153.24,96040000 +1984-05-31,150.35,150.69,149.76,150.55,81890000 +1984-05-30,150.29,151.43,148.68,150.35,105660000 +1984-05-29,151.62,151.86,149.95,150.29,69060000 +1984-05-25,151.23,152.02,150.85,151.62,78190000 +1984-05-24,153.15,153.15,150.8,151.23,99040000 +1984-05-23,153.88,154.02,153.1,153.15,82690000 +1984-05-22,154.73,154.73,152.99,153.88,88030000 +1984-05-21,155.78,156.11,154.63,154.73,73380000 +1984-05-18,156.57,156.77,155.24,155.78,81270000 +1984-05-17,157.99,157.99,156.15,156.57,90310000 +1984-05-16,158,158.41,157.83,157.99,89210000 +1984-05-15,157.5,158.27,157.29,158,88250000 +1984-05-14,158.49,158.49,157.2,157.5,64900000 +1984-05-11,160,160,157.42,158.49,82780000 +1984-05-10,160.11,160.45,159.61,160,101810000 +1984-05-09,160.52,161.31,159.39,160.11,100590000 +1984-05-08,159.47,160.52,159.14,160.52,81610000 +1984-05-07,159.11,159.48,158.63,159.47,72760000 +1984-05-04,161.2,161.2,158.93,159.11,98580000 +1984-05-03,161.9,161.9,160.95,161.2,91910000 +1984-05-02,161.68,162.11,161.41,161.9,107080000 +1984-05-01,160.05,161.69,160.05,161.68,110550000 +1984-04-30,159.89,160.43,159.3,160.05,72740000 +1984-04-27,160.3,160.69,159.77,159.89,88530000 +1984-04-26,158.65,160.5,158.65,160.3,98000000 +1984-04-25,158.07,158.77,157.8,158.65,83520000 +1984-04-24,156.8,158.38,156.61,158.07,87060000 +1984-04-23,158.02,158.05,156.79,156.8,73080000 +1984-04-19,157.9,158.02,157.1,158.02,75860000 +1984-04-18,158.97,158.97,157.64,157.9,85040000 +1984-04-17,158.32,159.59,158.32,158.97,98150000 +1984-04-16,157.31,158.35,156.49,158.32,73870000 +1984-04-13,157.73,158.87,157.13,157.31,99620000 +1984-04-12,155,157.74,154.17,157.73,96330000 +1984-04-11,155.93,156.31,154.9,155,80280000 +1984-04-10,155.45,156.57,155.45,155.87,78990000 +1984-04-09,155.48,155.86,154.71,155.45,71570000 +1984-04-06,155.04,155.48,154.12,155.48,86620000 +1984-04-05,157.54,158.1,154.96,155.04,101750000 +1984-04-04,157.66,158.11,157.29,157.54,92860000 +1984-04-03,157.99,158.27,157.17,157.66,87980000 +1984-04-02,159.18,159.87,157.63,157.98,85680000 +1984-03-30,159.52,159.52,158.92,159.18,71590000 +1984-03-29,159.88,160.46,159.52,159.52,81470000 +1984-03-28,157.3,159.9,157.3,159.88,104870000 +1984-03-27,156.67,157.3,156.61,157.3,73670000 +1984-03-26,156.86,157.18,156.31,156.67,69070000 +1984-03-23,156.69,156.92,156.02,156.86,79760000 +1984-03-22,158.66,158.67,156.61,156.69,87340000 +1984-03-21,158.86,159.26,158.59,158.66,87170000 +1984-03-20,157.78,159.17,157.78,158.86,86460000 +1984-03-19,159.27,159.27,157.28,157.78,64060000 +1984-03-16,157.41,160.45,157.41,159.27,118000000 +1984-03-15,156.78,158.05,156.73,157.41,79520000 +1984-03-14,156.78,157.17,156.22,156.77,77250000 +1984-03-13,156.34,157.93,156.34,156.78,102600000 +1984-03-12,154.35,156.35,154.35,156.34,84470000 +1984-03-09,155.12,155.19,153.77,154.35,73170000 +1984-03-08,154.57,155.8,154.35,155.19,80630000 +1984-03-07,156.25,156.25,153.81,154.57,90080000 +1984-03-06,157.89,158.37,156.21,156.25,83590000 +1984-03-05,159.24,159.24,157.59,157.89,69870000 +1984-03-02,158.19,159.9,158.19,159.24,108270000 +1984-03-01,157.06,158.19,156.77,158.19,82010000 +1984-02-29,156.82,158.27,156.41,157.06,92810000 +1984-02-28,159.3,159.3,156.59,156.82,91010000 +1984-02-27,157.51,159.58,157.08,159.3,99140000 +1984-02-24,154.31,157.51,154.29,157.51,102620000 +1984-02-23,154.02,154.45,152.13,154.29,100220000 +1984-02-22,154.52,155.1,153.94,154.31,90080000 +1984-02-21,155.71,155.74,154.47,154.64,71890000 +1984-02-17,156.13,156.8,155.51,155.74,76600000 +1984-02-16,155.94,156.44,155.44,156.13,81750000 +1984-02-15,156.61,157.48,156.1,156.25,94870000 +1984-02-14,154.95,156.61,154.95,156.61,91800000 +1984-02-13,156.3,156.32,154.13,154.95,78460000 +1984-02-10,155.42,156.52,155.42,156.3,92220000 +1984-02-09,155.85,156.17,154.3,155.42,128190000 +1984-02-08,158.74,159.07,155.67,155.85,96890000 +1984-02-07,157.91,158.81,157.01,158.74,107640000 +1984-02-06,160.91,160.91,158.02,158.08,109090000 +1984-02-03,163.44,163.98,160.82,160.91,109100000 +1984-02-02,162.74,163.36,162.24,163.36,111330000 +1984-02-01,163.41,164,162.27,162.74,107100000 +1984-01-31,162.87,163.6,162.03,163.41,113510000 +1984-01-30,164.4,164.67,162.4,162.87,103120000 +1984-01-27,164.24,164.33,163.07,163.94,103720000 +1984-01-26,164.84,165.55,164.12,164.24,111100000 +1984-01-25,165.94,167.12,164.74,164.84,113470000 +1984-01-24,164.87,166.35,164.84,165.94,103050000 +1984-01-23,166.21,166.21,164.83,164.87,82010000 +1984-01-20,167.04,167.06,165.87,166.21,93360000 +1984-01-19,167.55,167.65,166.67,167.04,98340000 +1984-01-18,167.83,168.34,167.02,167.55,109010000 +1984-01-17,167.18,167.84,167.01,167.83,92750000 +1984-01-16,167.02,167.55,166.77,167.18,93790000 +1984-01-13,167.75,168.59,166.64,167.02,101790000 +1984-01-12,167.79,168.4,167.68,167.75,99410000 +1984-01-11,167.95,168.07,167.27,167.8,98660000 +1984-01-10,168.9,169.54,167.87,167.95,109570000 +1984-01-09,169.18,169.46,168.48,168.9,107100000 +1984-01-06,168.81,169.31,168.49,169.28,137590000 +1984-01-05,166.78,169.1,166.78,168.81,159990000 +1984-01-04,164.09,166.78,164.04,166.78,112980000 +1984-01-03,164.93,164.93,163.98,164.04,71340000 +1983-12-30,164.86,165.05,164.58,164.93,71840000 +1983-12-29,165.33,165.84,164.83,164.86,86560000 +1983-12-28,164.69,165.34,164.3,165.34,85660000 +1983-12-27,163.22,164.76,163.22,164.76,63800000 +1983-12-23,163.27,163.31,162.9,163.22,62710000 +1983-12-22,163.56,164.18,163.17,163.53,106260000 +1983-12-21,162,163.57,161.99,163.56,108080000 +1983-12-20,162.33,162.8,161.64,162,83740000 +1983-12-19,162.34,162.88,162.27,162.32,75180000 +1983-12-16,161.69,162.39,161.58,162.39,81030000 +1983-12-15,163.33,163.33,161.66,161.66,88300000 +1983-12-14,164.93,164.93,163.25,163.33,85430000 +1983-12-13,165.62,165.63,164.85,164.93,93500000 +1983-12-12,165.13,165.62,164.99,165.62,77340000 +1983-12-09,165.2,165.29,164.5,165.08,98280000 +1983-12-08,165.91,166.01,164.86,165.2,96530000 +1983-12-07,165.47,166.34,165.35,165.91,105670000 +1983-12-06,165.77,165.93,165.34,165.47,89690000 +1983-12-05,165.44,165.79,164.71,165.76,88330000 +1983-12-02,166.49,166.7,165.25,165.44,93960000 +1983-12-01,166.37,166.77,166.08,166.49,106970000 +1983-11-30,167.91,168.07,166.33,166.4,120130000 +1983-11-29,166.54,167.92,166.17,167.91,100460000 +1983-11-28,167.2,167.22,166.21,166.54,78210000 +1983-11-25,167.02,167.2,166.73,167.18,57820000 +1983-11-23,166.88,167.21,166.26,166.96,108080000 +1983-11-22,166.05,167.26,166.05,166.84,117550000 +1983-11-21,165.04,166.05,165,166.05,97740000 +1983-11-18,166.08,166.13,164.5,165.09,88280000 +1983-11-17,166.08,166.49,165.51,166.13,80740000 +1983-11-16,165.36,166.41,165.34,166.08,83380000 +1983-11-15,166.58,166.59,165.28,165.36,77840000 +1983-11-14,166.29,167.58,166.27,166.58,86880000 +1983-11-11,164.41,166.3,164.34,166.29,74270000 +1983-11-10,163.99,164.71,163.97,164.41,88730000 +1983-11-09,161.74,163.97,161.74,163.97,83100000 +1983-11-08,161.91,162.15,161.63,161.76,64900000 +1983-11-07,162.42,162.56,161.84,161.91,69400000 +1983-11-04,162.68,163.45,162.22,162.44,72080000 +1983-11-03,164.84,164.85,163.42,163.45,85350000 +1983-11-02,165.21,165.21,163.55,164.84,95210000 +1983-11-01,163.55,163.66,162.37,163.66,84460000 +1983-10-31,163.37,164.58,162.86,163.55,79460000 +1983-10-28,164.89,165.19,163.23,163.37,81180000 +1983-10-27,165.31,165.38,164.41,164.84,79570000 +1983-10-26,166.49,166.65,165.36,165.38,79570000 +1983-10-25,166,167.15,166,166.47,82530000 +1983-10-24,165.85,165.99,163.85,165.99,85420000 +1983-10-21,166.97,167.23,164.98,165.95,91640000 +1983-10-20,166.77,167.35,166.44,166.98,86000000 +1983-10-19,167.81,167.81,165.67,166.73,107790000 +1983-10-18,170.41,170.41,167.67,167.81,91080000 +1983-10-17,169.85,171.18,169.63,170.43,77730000 +1983-10-14,169.88,169.99,169.18,169.86,71600000 +1983-10-13,169.63,170.12,169.13,169.87,67750000 +1983-10-12,170.34,170.84,169.34,169.62,75630000 +1983-10-11,172.59,172.59,170.34,170.34,79510000 +1983-10-10,170.77,172.65,170.05,172.65,67050000 +1983-10-07,170.32,171.1,170.31,170.8,103630000 +1983-10-06,167.76,170.28,167.76,170.28,118270000 +1983-10-05,166.29,167.74,165.92,167.74,101710000 +1983-10-04,165.81,166.8,165.81,166.27,90270000 +1983-10-03,165.99,166.07,164.93,165.81,77230000 +1983-09-30,167.23,167.23,165.63,166.07,70860000 +1983-09-29,168.02,168.35,167.23,167.23,73730000 +1983-09-28,168.42,168.53,167.52,168,75820000 +1983-09-27,170.02,170.02,167.95,168.43,81100000 +1983-09-26,169.53,170.41,169.16,170.07,86400000 +1983-09-23,169.76,170.17,168.88,169.51,93180000 +1983-09-22,168.4,169.78,168.22,169.76,97050000 +1983-09-21,169.27,169.3,168.21,168.41,91280000 +1983-09-20,167.64,169.38,167.64,169.24,103050000 +1983-09-19,166.27,168.09,166.26,167.62,85630000 +1983-09-16,164.42,166.57,164.39,166.25,75530000 +1983-09-15,165.39,165.58,164.38,164.38,70420000 +1983-09-14,164.8,165.42,164.63,165.35,73370000 +1983-09-13,165.48,165.48,164.17,164.8,73970000 +1983-09-12,166.95,169.2,165.27,165.48,114020000 +1983-09-09,167.77,167.77,166.91,166.92,77990000 +1983-09-08,167.96,168.14,167.12,167.77,79250000 +1983-09-07,167.9,168.48,167.46,167.96,94240000 +1983-09-06,165.2,167.9,165.03,167.89,87500000 +1983-09-02,164.25,165.07,164.21,165,59300000 +1983-09-01,164.4,164.66,163.95,164.23,76120000 +1983-08-31,162.55,164.4,162.32,164.4,80800000 +1983-08-30,162.25,163.13,162.11,162.58,62370000 +1983-08-29,162.14,162.32,160.97,162.25,53030000 +1983-08-26,160.85,162.16,160.25,162.14,61650000 +1983-08-25,161.27,161.28,159.96,160.84,70140000 +1983-08-24,162.77,162.77,161.2,161.25,72200000 +1983-08-23,164.33,164.33,162.54,162.77,66800000 +1983-08-22,164.18,165.64,163.77,164.34,76420000 +1983-08-19,163.58,164.27,163.22,163.98,58950000 +1983-08-18,165.29,165.91,163.55,163.55,82280000 +1983-08-17,163.58,165.4,163.43,165.29,87800000 +1983-08-16,163.74,163.84,162.72,163.41,71780000 +1983-08-15,162.22,164.76,162.22,163.7,83200000 +1983-08-12,161.55,162.6,161.55,162.16,71840000 +1983-08-11,161.55,162.14,161.41,161.54,70630000 +1983-08-10,160.11,161.77,159.47,161.54,82900000 +1983-08-09,159.2,160.14,158.5,160.13,81420000 +1983-08-08,161.73,161.73,159.18,159.18,71460000 +1983-08-05,161.33,161.88,160.89,161.74,67850000 +1983-08-04,163.28,163.42,159.63,161.33,100870000 +1983-08-03,162.01,163.44,161.52,163.44,80370000 +1983-08-02,162.06,163.04,161.97,162.01,74460000 +1983-08-01,162.34,162.78,161.55,162.04,77210000 +1983-07-29,165.03,165.03,161.5,162.56,95240000 +1983-07-28,167.32,167.79,164.99,165.04,78410000 +1983-07-27,170.68,170.72,167.49,167.59,99290000 +1983-07-26,169.62,170.63,169.26,170.53,91280000 +1983-07-25,167.67,169.74,167.63,169.53,73680000 +1983-07-22,168.51,169.08,168.4,168.89,68850000 +1983-07-21,169.29,169.8,168.33,169.06,101830000 +1983-07-20,164.89,169.29,164.89,169.29,109310000 +1983-07-19,163.95,165.18,163.95,164.82,74030000 +1983-07-18,164.28,164.29,163.3,163.95,69110000 +1983-07-15,166.01,166.04,164.03,164.29,63160000 +1983-07-14,165.61,166.96,165.61,166.01,83500000 +1983-07-13,165,165.68,164.77,165.46,68900000 +1983-07-12,168.05,168.05,165.51,165.53,70220000 +1983-07-11,167.09,168.11,167.09,168.11,61610000 +1983-07-08,167.56,167.98,166.95,167.08,66520000 +1983-07-07,168.48,169.15,167.08,167.56,97130000 +1983-07-06,166.71,168.88,166.49,168.48,85670000 +1983-07-05,166.55,168.8,165.8,166.6,67320000 +1983-07-01,168.11,168.64,167.77,168.64,65110000 +1983-06-30,167.64,167.64,167.64,167.64,76310000 +1983-06-29,165.78,166.64,165.43,166.64,81580000 +1983-06-28,168.45,168.81,165.67,165.68,82730000 +1983-06-27,170.4,170.46,168.32,168.46,69360000 +1983-06-24,170.57,170.69,170.03,170.41,80810000 +1983-06-23,170.99,171,170.13,170.57,89590000 +1983-06-22,170.53,171.6,170.42,170.99,110270000 +1983-06-21,169.03,170.6,168.25,170.53,102880000 +1983-06-20,169.13,170.1,168.59,169.02,84270000 +1983-06-17,169.11,169.64,168.6,169.13,93630000 +1983-06-16,167.11,169.38,167.11,169.14,124560000 +1983-06-15,165.52,167.12,165.07,167.12,93410000 +1983-06-14,164.87,165.93,164.87,165.53,97710000 +1983-06-13,162.7,164.84,162.7,164.84,90700000 +1983-06-10,161.86,162.76,161.86,162.68,78470000 +1983-06-09,161.37,161.92,160.8,161.83,87440000 +1983-06-08,162.78,162.78,161.35,161.36,96600000 +1983-06-07,164.84,164.93,162.77,162.77,88550000 +1983-06-06,164.43,165.09,163.75,164.83,87670000 +1983-06-03,163.96,164.79,163.96,164.42,83110000 +1983-06-02,162.56,164,162.56,163.98,89750000 +1983-06-01,162.38,162.64,161.33,162.55,84460000 +1983-05-31,164.44,164.44,162.12,162.39,73910000 +1983-05-27,165.49,165.49,164.33,164.46,76290000 +1983-05-26,166.22,166.39,165.27,165.48,94980000 +1983-05-25,165.54,166.21,164.79,166.21,121050000 +1983-05-24,163.45,165.59,163.45,165.54,109850000 +1983-05-23,162.06,163.5,160.29,163.43,84960000 +1983-05-20,161.97,162.14,161.25,162.14,73150000 +1983-05-19,163.27,163.61,161.98,161.99,83260000 +1983-05-18,163.73,165.18,163.16,163.27,99780000 +1983-05-17,163.4,163.71,162.55,163.71,79510000 +1983-05-16,164.9,164.9,162.33,163.4,76250000 +1983-05-13,164.26,165.23,164.26,164.91,83110000 +1983-05-12,164.98,165.35,163.82,164.25,84060000 +1983-05-11,165.95,166.3,164.53,164.96,99820000 +1983-05-10,165.82,166.4,165.74,165.95,104010000 +1983-05-09,166.1,166.46,164.9,165.81,93670000 +1983-05-06,164.3,166.99,164.3,166.1,128200000 +1983-05-05,163.35,164.3,163.35,164.28,107860000 +1983-05-04,162.38,163.64,162.38,163.31,101690000 +1983-05-03,162.1,162.35,160.8,162.34,89550000 +1983-05-02,164.41,164.42,161.99,162.11,88170000 +1983-04-29,162.97,164.43,162.72,164.43,105750000 +1983-04-28,161.44,162.96,161.44,162.95,94410000 +1983-04-27,161.85,162.77,160.76,161.44,118140000 +1983-04-26,158.81,161.81,158.07,161.81,91210000 +1983-04-25,160.43,160.83,158.72,158.81,90150000 +1983-04-22,160.04,160.76,160.02,160.42,92270000 +1983-04-21,160.73,161.08,159.96,160.05,106170000 +1983-04-20,158.71,160.83,158.71,160.71,110240000 +1983-04-19,159.74,159.74,158.54,158.71,91210000 +1983-04-18,158.75,159.75,158.41,159.74,88560000 +1983-04-15,158.11,158.75,158.11,158.75,89590000 +1983-04-14,156.8,158.12,156.55,158.12,90160000 +1983-04-13,155.82,157.22,155.82,156.77,100520000 +1983-04-12,155.15,155.82,154.78,155.82,79900000 +1983-04-11,152.87,155.14,152.87,155.14,81440000 +1983-04-08,151.77,152.85,151.39,152.85,67710000 +1983-04-07,151.04,151.76,150.81,151.76,69480000 +1983-04-06,151.9,151.9,150.17,151.04,77140000 +1983-04-05,153.04,153.92,151.81,151.9,76810000 +1983-04-04,152.92,153.02,152.23,153.02,66010000 +1983-03-31,153.41,155.02,152.86,152.96,100570000 +1983-03-30,151.6,153.39,151.6,153.39,75800000 +1983-03-29,151.85,152.46,151.42,151.59,65300000 +1983-03-28,152.67,152.67,151.56,151.85,58510000 +1983-03-25,153.37,153.71,152.3,152.67,77330000 +1983-03-24,152.82,153.78,152.82,153.37,92340000 +1983-03-23,150.65,152.98,150.65,152.81,94980000 +1983-03-22,151.21,151.59,150.6,150.66,79610000 +1983-03-21,149.82,151.2,149.32,151.19,72160000 +1983-03-18,149.59,150.29,149.56,149.9,75110000 +1983-03-17,149.8,149.8,149.12,149.59,70290000 +1983-03-16,151.36,151.62,149.78,149.81,83570000 +1983-03-15,150.83,151.37,150.4,151.37,62410000 +1983-03-14,151.28,151.3,150.24,150.83,61890000 +1983-03-11,151.75,151.75,150.65,151.24,67240000 +1983-03-10,152.87,154.01,151.75,151.8,95410000 +1983-03-09,151.25,152.87,150.84,152.87,84250000 +1983-03-08,153.63,153.63,151.26,151.26,79410000 +1983-03-07,153.67,154,152.65,153.67,84020000 +1983-03-04,153.47,153.67,152.53,153.67,90930000 +1983-03-03,152.31,154.16,152.31,153.48,114440000 +1983-03-02,150.91,152.63,150.91,152.3,112600000 +1983-03-01,148.07,150.88,148.07,150.88,103750000 +1983-02-28,149.74,149.74,147.81,148.06,83750000 +1983-02-25,149.6,150.88,149.6,149.74,100970000 +1983-02-24,146.8,149.67,146.8,149.6,113220000 +1983-02-23,145.47,146.79,145.4,146.79,84100000 +1983-02-22,148.01,148.11,145.42,145.48,84080000 +1983-02-18,147.44,148.29,147.21,148,77420000 +1983-02-17,147.43,147.57,143.84,147.44,74930000 +1983-02-16,148.31,148.66,147.41,147.43,82100000 +1983-02-15,148.94,149.41,148.13,148.3,89040000 +1983-02-14,147.71,149.14,147.4,148.93,72640000 +1983-02-11,147.51,148.81,147.18,147.65,86700000 +1983-02-10,145.04,147.75,145.04,147.5,93510000 +1983-02-09,145.7,145.83,144.09,145,84520000 +1983-02-08,146.93,147.21,145.52,145.7,76580000 +1983-02-07,146.14,147.42,146.14,146.93,86030000 +1983-02-04,144.26,146.14,144.14,146.14,87000000 +1983-02-03,143.25,144.43,143.25,144.26,78890000 +1983-02-02,142.95,143.52,141.9,143.23,77220000 +1983-02-01,145.29,145.29,142.96,142.96,82750000 +1983-01-31,144.51,145.3,143.93,145.3,67140000 +1983-01-28,144.31,145.47,144.25,144.51,89490000 +1983-01-27,141.54,144.3,141.54,144.27,88120000 +1983-01-26,141.77,142.16,141.16,141.54,73720000 +1983-01-25,139.98,141.75,139.98,141.75,79740000 +1983-01-24,143.84,143.84,139.1,139.97,90800000 +1983-01-21,146.3,146.3,143.25,143.85,77110000 +1983-01-20,145.29,146.62,145.29,146.29,82790000 +1983-01-19,146.4,146.45,144.51,145.27,80900000 +1983-01-18,146.71,146.74,145.52,146.4,78380000 +1983-01-17,146.65,147.9,146.64,146.72,89210000 +1983-01-14,145.72,147.12,145.72,146.65,86480000 +1983-01-13,146.67,146.94,145.67,145.73,77030000 +1983-01-12,145.76,148.36,145.76,146.69,109850000 +1983-01-11,146.79,146.83,145.38,145.78,98250000 +1983-01-10,145.19,147.25,144.58,146.78,101890000 +1983-01-07,145.27,146.46,145.15,145.18,127290000 +1983-01-06,142.01,145.77,142.01,145.27,129410000 +1983-01-05,141.35,142.6,141.15,141.96,95390000 +1983-01-04,138.33,141.36,138.08,141.36,75530000 +1983-01-03,140.65,141.33,138.2,138.34,59080000 +1982-12-31,140.34,140.78,140.27,140.64,42110000 +1982-12-30,141.24,141.68,140.22,140.33,56380000 +1982-12-29,140.77,141.73,140.68,141.24,54810000 +1982-12-28,142.18,142.34,140.75,140.77,58610000 +1982-12-27,139.73,142.32,139.72,142.17,64690000 +1982-12-23,138.84,139.94,138.84,139.72,62880000 +1982-12-22,138.63,139.69,138.6,138.83,83470000 +1982-12-21,136.24,139.27,136.07,138.61,78010000 +1982-12-20,137.49,137.84,136.19,136.25,62210000 +1982-12-17,135.35,137.71,135.35,137.49,76010000 +1982-12-16,135.22,135.78,134.79,135.3,73680000 +1982-12-15,137.4,137.4,135.12,135.24,81030000 +1982-12-14,139.99,142.5,137.34,137.4,98380000 +1982-12-13,139.57,140.12,139.5,139.95,63140000 +1982-12-10,139.99,141.15,139.35,139.57,86430000 +1982-12-09,141.8,141.8,139.92,140,90320000 +1982-12-08,142.71,143.58,141.82,141.82,97430000 +1982-12-07,141.79,143.68,141.79,142.72,111620000 +1982-12-06,138.7,141.77,138.01,141.77,83880000 +1982-12-03,138.87,139.59,138.59,138.69,71540000 +1982-12-02,138.72,139.63,138.66,138.82,77600000 +1982-12-01,138.56,140.37,138.35,138.72,107850000 +1982-11-30,134.2,138.53,134.19,138.53,93470000 +1982-11-29,134.89,135.29,133.69,134.2,61080000 +1982-11-26,133.89,134.88,133.89,134.88,38810000 +1982-11-24,132.92,133.88,132.92,133.88,67220000 +1982-11-23,134.21,134.28,132.89,132.93,72920000 +1982-11-22,137.03,137.1,134.21,134.22,74960000 +1982-11-19,138.35,138.93,137,137.02,70310000 +1982-11-18,137.93,138.78,137.47,138.34,77620000 +1982-11-17,135.47,137.93,135.47,137.93,84440000 +1982-11-16,136.97,136.97,134.05,135.42,102910000 +1982-11-15,139.54,139.54,137,137.03,78900000 +1982-11-12,141.75,141.85,139.53,139.53,95080000 +1982-11-11,141.15,141.75,139.88,141.75,78410000 +1982-11-10,143.04,144.36,140.8,141.16,113240000 +1982-11-09,140.48,143.16,140.46,143.02,111220000 +1982-11-08,142.12,142.12,139.98,140.44,75240000 +1982-11-05,141.85,142.43,141.32,142.16,96550000 +1982-11-04,142.85,143.99,141.65,141.85,149350000 +1982-11-03,137.53,142.88,137.53,142.87,137010000 +1982-11-02,135.48,138.51,135.48,137.49,104770000 +1982-11-01,133.72,136.03,133.22,135.47,73530000 +1982-10-29,133.54,134.02,132.64,133.72,74830000 +1982-10-28,135.28,135.42,133.59,133.59,73590000 +1982-10-27,134.48,135.92,134.48,135.29,81670000 +1982-10-26,133.29,134.48,131.5,134.48,102080000 +1982-10-25,138.81,138.81,133.32,133.32,83720000 +1982-10-22,139.06,140.4,138.75,138.83,101120000 +1982-10-21,139.23,140.27,137.63,139.06,122460000 +1982-10-20,136.58,139.23,136.37,139.23,98680000 +1982-10-19,136.73,137.96,135.72,136.58,100850000 +1982-10-18,133.59,136.73,133.59,136.73,83790000 +1982-10-15,134.55,134.61,133.28,133.57,80290000 +1982-10-14,136.71,136.89,134.55,134.57,107530000 +1982-10-13,134.42,137.97,134.14,136.71,139800000 +1982-10-12,134.48,135.85,133.59,134.44,126310000 +1982-10-11,131.06,135.53,131.06,134.47,138530000 +1982-10-08,128.79,131.11,128.79,131.05,122250000 +1982-10-07,125.99,128.96,125.99,128.8,147070000 +1982-10-06,122,125.97,122,125.97,93570000 +1982-10-05,121.6,122.73,121.6,121.98,69770000 +1982-10-04,121.97,121.97,120.56,121.51,55650000 +1982-10-01,120.4,121.97,120.15,121.97,65000000 +1982-09-30,121.62,121.62,120.14,120.42,62610000 +1982-09-29,123.24,123.24,121.28,121.63,62550000 +1982-09-28,123.62,124.16,123.21,123.24,65900000 +1982-09-27,123.32,123.62,122.75,123.62,44840000 +1982-09-24,123.79,123.8,123.11,123.32,54600000 +1982-09-23,123.99,124.19,122.96,123.81,68260000 +1982-09-22,124.9,126.43,123.99,123.99,113150000 +1982-09-21,122.51,124.91,122.51,124.88,82920000 +1982-09-20,122.54,122.54,121.48,122.51,58520000 +1982-09-17,123.76,123.76,122.34,122.55,63950000 +1982-09-16,124.28,124.88,123.65,123.77,78900000 +1982-09-15,123.09,124.81,122.72,124.29,69680000 +1982-09-14,122.27,123.69,122.27,123.1,83070000 +1982-09-13,120.94,122.24,120.25,122.24,59520000 +1982-09-10,121.97,121.98,120.27,120.97,71080000 +1982-09-09,122.19,123.22,121.9,121.97,73090000 +1982-09-08,121.33,123.11,121.19,122.2,77960000 +1982-09-07,122.68,122.68,121.19,121.37,68960000 +1982-09-03,120.31,123.64,120.31,122.68,130910000 +1982-09-02,118.24,120.32,117.84,120.29,74740000 +1982-09-01,119.52,120.05,117.98,118.25,82830000 +1982-08-31,117.65,119.6,117.65,119.51,86360000 +1982-08-30,117.05,117.66,115.79,117.66,59560000 +1982-08-27,117.38,118.56,116.63,117.11,74410000 +1982-08-26,117.57,120.26,117.57,118.55,137330000 +1982-08-25,115.35,118.12,115.11,117.58,106200000 +1982-08-24,116.11,116.39,115.08,115.35,121650000 +1982-08-23,113.02,116.11,112.65,116.11,110310000 +1982-08-20,109.19,113.02,109.19,113.02,95890000 +1982-08-19,108.53,109.86,108.34,109.16,78270000 +1982-08-18,109.04,111.58,108.46,108.54,132690000 +1982-08-17,105.4,109.04,104.09,109.04,92860000 +1982-08-16,103.86,105.52,103.86,104.09,55420000 +1982-08-13,102.42,103.85,102.4,103.85,44720000 +1982-08-12,102.6,103.22,102.39,102.42,50080000 +1982-08-11,102.83,103.01,102.48,102.6,49040000 +1982-08-10,103.11,103.84,102.82,102.84,52680000 +1982-08-09,103.69,103.69,102.2,103.08,54560000 +1982-08-06,105.16,105.16,103.67,103.71,48660000 +1982-08-05,106.1,106.1,104.76,105.16,54700000 +1982-08-04,107.83,107.83,106.11,106.14,53440000 +1982-08-03,108.98,109.43,107.81,107.83,60480000 +1982-08-02,107.71,109.09,107.11,108.98,53460000 +1982-07-30,107.35,107.95,107.01,107.09,39270000 +1982-07-29,107.42,107.92,106.62,107.72,55680000 +1982-07-28,109.42,109.42,107.53,107.74,53830000 +1982-07-27,110.26,110.35,109.36,109.43,45740000 +1982-07-26,110.66,111.16,110.29,110.36,37740000 +1982-07-23,111.46,111.58,111.05,111.17,47280000 +1982-07-22,110.95,112.02,110.94,111.48,53870000 +1982-07-21,112.15,112.39,111.38,111.42,66770000 +1982-07-20,111.11,111.56,110.35,111.54,61060000 +1982-07-19,111.75,111.78,110.66,110.73,53030000 +1982-07-16,110.16,111.48,110.16,111.07,58740000 +1982-07-15,110.83,110.95,110.27,110.47,61090000 +1982-07-14,109.68,110.44,109.08,110.44,58160000 +1982-07-13,109.19,110.07,109.19,109.45,66170000 +1982-07-12,109.48,109.62,108.89,109.57,74690000 +1982-07-09,108.23,108.97,107.56,108.83,65870000 +1982-07-08,106.85,107.53,105.57,107.53,63270000 +1982-07-07,107.08,107.61,106.99,107.22,46920000 +1982-07-06,107.27,107.67,106.74,107.29,44350000 +1982-07-02,108.1,108.71,107.6,107.65,43760000 +1982-07-01,109.52,109.63,108.62,108.71,47900000 +1982-06-30,110.95,111,109.5,109.61,65280000 +1982-06-29,110.26,110.57,109.68,110.21,46990000 +1982-06-28,109.3,110.45,109.17,110.26,40700000 +1982-06-25,109.56,109.83,109.09,109.14,38740000 +1982-06-24,110.25,110.92,109.79,109.83,55860000 +1982-06-23,108.59,110.14,108.09,110.14,62710000 +1982-06-22,107.25,108.3,107.17,108.3,55290000 +1982-06-21,107.28,107.88,107.01,107.2,50370000 +1982-06-18,107.6,107.6,107.07,107.28,53800000 +1982-06-17,108.01,108.85,107.48,107.6,49230000 +1982-06-16,110.1,110.13,108.82,108.87,56280000 +1982-06-15,109.63,109.96,108.98,109.69,44970000 +1982-06-14,110.5,111.22,109.9,109.96,40100000 +1982-06-11,111.11,111.48,109.65,111.24,68610000 +1982-06-10,109.35,109.7,108.96,109.61,50950000 +1982-06-09,109.46,109.63,108.53,108.99,55770000 +1982-06-08,110.33,110.33,109.6,109.63,46820000 +1982-06-07,109.59,110.59,109.42,110.12,44630000 +1982-06-04,111.66,111.85,110.02,110.09,44110000 +1982-06-03,112.04,112.48,111.45,111.86,48450000 +1982-06-02,111.74,112.19,111.55,112.04,49220000 +1982-06-01,111.97,112.07,111.66,111.68,41650000 +1982-05-28,112.79,112.8,111.66,111.88,43900000 +1982-05-27,113.11,113.12,112.58,112.66,44730000 +1982-05-26,113.68,114.4,112.88,113.11,51250000 +1982-05-25,115.5,115.51,114.4,114.4,44010000 +1982-05-24,114.46,114.86,114.24,114.79,38510000 +1982-05-21,115.03,115.13,114.6,114.89,45260000 +1982-05-20,114.85,115.07,114.37,114.59,48330000 +1982-05-19,115.61,115.96,114.82,114.89,48840000 +1982-05-18,116.35,116.7,115.71,115.84,48970000 +1982-05-17,117.62,118.02,116.66,116.71,45600000 +1982-05-14,118.2,118.4,118.01,118.01,49900000 +1982-05-13,119.08,119.2,118.13,118.22,58230000 +1982-05-12,119.89,119.92,118.76,119.17,59210000 +1982-05-11,118.54,119.59,118.32,119.42,54680000 +1982-05-10,119.08,119.49,118.37,118.38,46300000 +1982-05-07,119.08,119.89,118.71,119.47,67130000 +1982-05-06,118.82,118.83,117.68,118.68,67540000 +1982-05-05,117.85,118.05,117.31,117.67,58860000 +1982-05-04,117.41,117.64,116.85,117.46,58720000 +1982-05-03,115.96,116.82,115.91,116.82,46490000 +1982-04-30,116.21,116.78,116.07,116.44,48200000 +1982-04-29,116.4,117.24,116.11,116.14,51330000 +1982-04-28,117.83,118.05,116.94,117.26,50530000 +1982-04-27,119.07,119.26,117.73,118,56480000 +1982-04-26,118.94,119.33,118.25,119.26,60500000 +1982-04-23,118.02,118.64,117.19,118.64,71840000 +1982-04-22,115.72,117.25,115.72,117.19,64470000 +1982-04-21,115.48,115.87,115.3,115.72,57820000 +1982-04-20,115.8,117.14,114.83,115.44,54610000 +1982-04-19,116.81,118.16,115.83,116.7,58470000 +1982-04-16,116.35,117.7,115.68,116.81,55890000 +1982-04-15,115.83,116.86,115.02,116.35,45700000 +1982-04-14,115.99,116.69,114.8,115.83,45150000 +1982-04-13,116,117.12,115.16,115.99,48660000 +1982-04-12,116.22,117.02,115.16,116,46520000 +1982-04-08,115.46,116.94,114.94,116.22,60190000 +1982-04-07,115.36,116.45,114.58,115.46,53130000 +1982-04-06,114.73,115.92,113.7,115.36,43200000 +1982-04-05,115.12,115.9,113.94,114.73,46900000 +1982-04-02,113.79,115.79,113.65,115.12,59800000 +1982-04-01,111.96,114.22,111.48,113.79,57100000 +1982-03-31,112.27,113.17,111.32,111.96,43300000 +1982-03-30,112.3,113.09,111.3,112.27,43900000 +1982-03-29,111.94,112.82,110.9,112.3,37100000 +1982-03-26,113.21,113.43,111.26,111.94,42400000 +1982-03-25,112.97,114.26,112.02,113.21,51970000 +1982-03-24,113.55,114.31,112.23,112.97,49380000 +1982-03-23,112.77,114.51,112.29,113.55,67130000 +1982-03-22,110.71,113.35,110.71,112.77,57610000 +1982-03-19,110.3,111.59,109.64,110.61,46250000 +1982-03-18,109.08,111.02,108.85,110.3,54270000 +1982-03-17,109.28,110.1,108.11,109.08,48900000 +1982-03-16,109.45,110.92,108.57,109.28,48900000 +1982-03-15,108.61,109.99,107.47,109.45,43370000 +1982-03-12,109.36,109.72,104.46,108.61,49600000 +1982-03-11,109.41,110.87,108.38,109.36,52960000 +1982-03-10,108.83,110.98,108.09,109.41,59440000 +1982-03-09,107.34,109.88,106.17,108.83,76060000 +1982-03-08,109.34,111.06,107.03,107.34,67330000 +1982-03-05,109.88,110.9,108.31,109.34,67440000 +1982-03-04,110.92,111.78,108.77,109.88,74340000 +1982-03-03,112.51,112.51,109.98,110.92,70230000 +1982-03-02,113.31,114.8,112.03,112.68,63800000 +1982-03-01,113.11,114.32,111.86,113.31,53010000 +1982-02-26,113.21,114.01,112.04,113.11,43840000 +1982-02-25,113.47,114.86,112.44,113.21,54160000 +1982-02-24,111.51,113.88,110.71,113.47,64800000 +1982-02-23,111.59,112.46,110.03,111.51,60100000 +1982-02-22,113.22,114.9,111.2,111.59,58310000 +1982-02-19,113.82,114.58,112.33,113.22,51340000 +1982-02-18,113.69,115.04,112.97,113.82,60810000 +1982-02-17,114.06,115.09,112.97,113.69,47660000 +1982-02-16,114.38,114.63,112.06,114.06,48880000 +1982-02-12,114.43,115.39,113.7,114.38,37070000 +1982-02-11,114.66,115.59,113.41,114.43,46730000 +1982-02-10,113.68,115.62,113.45,114.66,46620000 +1982-02-09,114.63,115.15,112.82,113.68,54420000 +1982-02-08,117.04,117.04,114.2,114.63,48500000 +1982-02-05,116.42,118.26,115.74,117.26,53350000 +1982-02-04,116.48,117.49,114.88,116.42,53300000 +1982-02-03,118.01,118.67,116.04,116.48,49560000 +1982-02-02,117.78,119.15,116.91,118.01,45020000 +1982-02-01,119.81,119.81,117.14,117.78,47720000 +1982-01-29,118.92,121.38,118.64,120.4,73400000 +1982-01-28,116.1,119.35,116.1,118.92,66690000 +1982-01-27,115.19,116.6,114.38,115.74,50060000 +1982-01-26,115.41,116.6,114.49,115.19,44870000 +1982-01-25,115.38,115.93,113.63,115.41,43170000 +1982-01-22,115.75,116.53,114.58,115.38,44370000 +1982-01-21,115.27,116.92,114.6,115.75,48610000 +1982-01-20,115.97,116.64,114.29,115.27,48860000 +1982-01-19,117.22,118.15,115.52,115.97,45070000 +1982-01-18,116.33,117.69,114.85,117.22,44920000 +1982-01-15,115.54,117.14,115.1,116.33,43310000 +1982-01-14,114.88,116.3,114.07,115.54,42940000 +1982-01-13,116.3,117.46,114.24,114.88,49130000 +1982-01-12,116.78,117.49,115.18,116.3,49800000 +1982-01-11,119.55,120.34,116.47,116.78,51900000 +1982-01-08,118.93,120.59,118.55,119.55,42050000 +1982-01-07,119.18,119.88,117.7,118.93,43410000 +1982-01-06,120.05,120.45,117.99,119.18,51510000 +1982-01-05,122.61,122.61,119.57,120.05,47510000 +1982-01-04,122.55,123.72,121.48,122.74,36760000 +1981-12-31,122.3,123.42,121.57,122.55,40780000 +1981-12-30,121.67,123.11,121.04,122.3,42960000 +1981-12-29,122.27,122.9,121.12,121.67,35300000 +1981-12-28,122.54,123.36,121.73,122.27,28320000 +1981-12-24,122.31,123.06,121.57,122.54,23940000 +1981-12-23,122.88,123.59,121.58,122.31,42910000 +1981-12-22,123.34,124.17,122.19,122.88,48320000 +1981-12-21,124,124.71,122.67,123.34,41290000 +1981-12-18,123.12,124.87,122.56,124,50940000 +1981-12-17,122.42,123.79,121.82,123.12,47230000 +1981-12-16,122.99,123.66,121.73,122.42,42770000 +1981-12-15,122.78,123.78,121.83,122.99,44130000 +1981-12-14,124.37,124.37,122.17,122.78,44740000 +1981-12-11,125.71,126.26,124.32,124.93,45850000 +1981-12-10,125.48,126.54,124.6,125.71,47020000 +1981-12-09,124.82,126.08,124.09,125.48,44810000 +1981-12-08,125.19,125.75,123.52,124.82,45140000 +1981-12-07,126.26,126.91,124.67,125.19,45720000 +1981-12-04,125.12,127.32,125.12,126.26,55040000 +1981-12-03,124.69,125.84,123.63,125.12,43770000 +1981-12-02,126.1,126.45,124.18,124.69,44510000 +1981-12-01,126.35,127.3,124.84,126.1,53980000 +1981-11-30,125.09,126.97,124.18,126.35,47580000 +1981-11-27,124.05,125.71,123.63,125.09,32770000 +1981-11-25,123.51,125.29,123.07,124.05,58570000 +1981-11-24,121.6,124.04,121.22,123.51,53200000 +1981-11-23,121.71,123.09,120.76,121.6,45250000 +1981-11-20,120.71,122.59,120.13,121.71,52010000 +1981-11-19,120.26,121.67,119.42,120.71,48890000 +1981-11-18,121.15,121.66,119.61,120.26,49980000 +1981-11-17,120.24,121.78,119.5,121.15,43190000 +1981-11-16,121.64,121.64,119.13,120.24,43740000 +1981-11-13,123.19,123.61,121.06,121.67,45550000 +1981-11-12,122.92,124.71,122.19,123.19,55720000 +1981-11-11,122.7,123.82,121.51,122.92,41920000 +1981-11-10,123.29,124.69,122.01,122.7,53940000 +1981-11-09,122.67,124.13,121.59,123.29,48310000 +1981-11-06,123.54,124.03,121.85,122.67,43270000 +1981-11-05,124.74,125.8,122.98,123.54,50860000 +1981-11-04,124.8,126,123.64,124.74,53450000 +1981-11-03,124.2,125.52,123.14,124.8,54620000 +1981-11-02,122.35,125.14,122.35,124.2,65100000 +1981-10-30,119.06,122.53,118.43,121.89,59570000 +1981-10-29,119.45,120.37,118.14,119.06,40070000 +1981-10-28,119.29,120.96,118.39,119.45,48100000 +1981-10-27,118.16,120.43,117.8,119.29,53030000 +1981-10-26,118.6,119,116.81,118.16,38210000 +1981-10-23,119.64,119.92,117.78,118.6,41990000 +1981-10-22,120.1,120.78,118.48,119.64,40630000 +1981-10-21,120.28,121.94,119.35,120.1,48490000 +1981-10-20,118.98,121.29,118.78,120.28,51530000 +1981-10-19,119.19,119.85,117.58,118.98,41590000 +1981-10-16,119.71,120.46,118.38,119.19,37800000 +1981-10-15,118.8,120.58,118.01,119.71,42830000 +1981-10-14,120.78,120.97,118.38,118.8,40260000 +1981-10-13,121.21,122.37,119.96,120.78,43360000 +1981-10-12,121.45,122.37,120.17,121.21,30030000 +1981-10-09,122.31,123.28,120.63,121.45,50060000 +1981-10-08,121.31,123.08,120.23,122.31,47090000 +1981-10-07,119.39,121.87,119.09,121.31,50030000 +1981-10-06,119.51,121.39,118.08,119.39,45460000 +1981-10-05,119.36,121.54,118.61,119.51,51290000 +1981-10-02,117.08,120.16,117.07,119.36,54540000 +1981-10-01,116.18,117.66,115,117.08,41600000 +1981-09-30,115.94,117.05,114.6,116.18,40700000 +1981-09-29,115.53,117.75,114.75,115.94,49800000 +1981-09-28,112.77,115.83,110.19,115.53,61320000 +1981-09-25,114.69,114.69,111.64,112.77,54390000 +1981-09-24,115.65,117.47,114.32,115.01,48880000 +1981-09-23,116.68,116.68,113.6,115.65,52700000 +1981-09-22,117.24,118.19,115.93,116.68,46830000 +1981-09-21,116.26,118.07,115.04,117.24,44570000 +1981-09-18,117.15,117.69,115.18,116.26,47350000 +1981-09-17,118.87,119.87,116.63,117.15,48300000 +1981-09-16,119.77,120,117.89,118.87,43660000 +1981-09-15,120.66,121.77,119.27,119.77,38580000 +1981-09-14,121.61,122,119.67,120.66,34040000 +1981-09-11,120.14,122.13,119.29,121.61,42170000 +1981-09-10,118.4,122.18,118.33,120.14,47430000 +1981-09-09,117.98,119.49,116.87,118.4,43910000 +1981-09-08,120.07,120.12,116.85,117.98,47340000 +1981-09-04,121.24,121.54,119.24,120.07,42760000 +1981-09-03,123.49,124.16,120.82,121.24,41730000 +1981-09-02,123.02,124.58,122.54,123.49,37570000 +1981-09-01,122.79,123.92,121.59,123.02,45110000 +1981-08-31,124.08,125.58,122.29,122.79,40360000 +1981-08-28,123.51,125.09,122.85,124.08,38020000 +1981-08-27,124.96,125.31,122.9,123.51,43900000 +1981-08-26,125.13,126.17,123.99,124.96,39980000 +1981-08-25,125.5,125.77,123,125.13,54600000 +1981-08-24,128.59,128.59,125.02,125.5,46750000 +1981-08-21,130.69,131.06,128.7,129.23,37670000 +1981-08-20,130.49,131.74,129.84,130.69,38270000 +1981-08-19,130.11,131.2,128.99,130.49,39390000 +1981-08-18,131.22,131.73,129.1,130.11,47270000 +1981-08-17,132.49,133.02,130.75,131.22,40840000 +1981-08-14,133.51,134.33,131.91,132.49,42580000 +1981-08-13,133.4,134.58,132.53,133.51,42460000 +1981-08-12,133.85,135.18,132.73,133.4,53650000 +1981-08-11,132.54,134.63,132.09,133.85,52600000 +1981-08-10,131.75,133.32,130.83,132.54,38370000 +1981-08-07,132.64,133.04,130.96,131.75,38370000 +1981-08-06,132.67,134.04,131.74,132.64,52070000 +1981-08-05,131.18,133.39,130.76,132.67,54290000 +1981-08-04,130.48,131.66,129.43,131.18,39460000 +1981-08-03,130.92,131.74,129.42,130.48,39650000 +1981-07-31,130.01,131.78,129.6,130.92,43480000 +1981-07-30,129.16,130.68,128.56,130.01,41560000 +1981-07-29,129.14,130.09,128.37,129.16,37610000 +1981-07-28,129.9,130.44,128.28,129.14,38160000 +1981-07-27,128.46,130.61,128.43,129.9,39610000 +1981-07-24,127.4,129.31,127.11,128.46,38880000 +1981-07-23,127.13,128.26,125.96,127.4,41790000 +1981-07-22,128.34,129.72,126.7,127.13,47500000 +1981-07-21,128.72,129.6,127.08,128.34,47280000 +1981-07-20,130.6,130.6,127.98,128.72,40240000 +1981-07-17,130.34,131.6,129.49,130.76,42780000 +1981-07-16,130.23,131.41,129.3,130.34,39010000 +1981-07-15,129.65,131.59,128.89,130.23,48950000 +1981-07-14,129.64,130.78,128.14,129.65,45230000 +1981-07-13,129.37,130.82,128.79,129.64,38100000 +1981-07-10,129.3,130.43,128.38,129.37,39950000 +1981-07-09,128.32,130.08,127.57,129.3,45510000 +1981-07-08,128.24,129.57,126.95,128.32,46000000 +1981-07-07,127.37,129.6,126.39,128.24,53560000 +1981-07-06,128.64,128.99,126.44,127.37,44590000 +1981-07-02,129.77,130.48,127.84,128.64,45100000 +1981-07-01,131.21,131.69,129.04,129.77,49080000 +1981-06-30,131.89,132.67,130.31,131.21,41550000 +1981-06-29,132.56,133.5,131.2,131.89,37930000 +1981-06-26,132.81,133.75,131.71,132.56,39240000 +1981-06-25,132.66,134.3,131.78,132.81,43920000 +1981-06-24,133.35,133.9,131.65,132.66,46650000 +1981-06-23,131.95,133.98,131.16,133.35,51840000 +1981-06-22,132.27,133.54,131.1,131.95,41790000 +1981-06-19,131.64,133.27,130.49,132.27,46430000 +1981-06-18,133.32,133.98,130.94,131.64,48400000 +1981-06-17,132.15,133.98,130.81,133.32,55470000 +1981-06-16,133.61,134,131.29,132.15,57780000 +1981-06-15,133.49,135.67,132.78,133.61,63350000 +1981-06-12,133.75,135.09,132.4,133.49,60790000 +1981-06-11,132.32,134.31,131.58,133.75,59530000 +1981-06-10,131.97,133.49,131.04,132.32,53200000 +1981-06-09,132.24,133.3,130.94,131.97,44600000 +1981-06-08,132.22,133.68,131.29,132.24,41580000 +1981-06-05,130.96,132.98,130.17,132.22,47180000 +1981-06-04,130.71,132.21,129.72,130.96,48940000 +1981-06-03,130.62,131.37,128.77,130.71,54700000 +1981-06-02,132.41,132.96,129.84,130.62,53930000 +1981-06-01,132.59,134.62,131.49,132.41,62170000 +1981-05-29,133.45,134.36,131.52,132.59,51580000 +1981-05-28,133.77,134.92,132,133.45,59500000 +1981-05-27,132.77,134.65,131.85,133.77,58730000 +1981-05-26,131.33,133.3,130.64,132.77,42760000 +1981-05-22,131.75,132.65,130.42,131.33,40710000 +1981-05-21,132,133.03,130.7,131.75,46820000 +1981-05-20,132.09,133.03,130.59,132,42370000 +1981-05-19,132.54,133.22,130.78,132.09,42220000 +1981-05-18,132.17,133.65,131.49,132.54,42510000 +1981-05-15,131.28,133.21,130.75,132.17,45460000 +1981-05-14,130.55,132.15,129.91,131.28,42750000 +1981-05-13,130.72,131.96,129.53,130.55,42600000 +1981-05-12,129.71,131.17,128.78,130.72,40440000 +1981-05-11,131.66,132.23,129.11,129.71,37640000 +1981-05-08,131.67,132.69,130.84,131.66,41860000 +1981-05-07,130.78,132.41,130.21,131.67,42590000 +1981-05-06,130.32,132.38,130.09,130.78,47100000 +1981-05-05,130.67,131.33,128.93,130.32,49000000 +1981-05-04,131.78,131.78,129.61,130.67,40430000 +1981-05-01,132.81,134.17,131.43,132.72,48360000 +1981-04-30,133.05,134.44,131.85,132.81,47970000 +1981-04-29,134.33,134.69,131.82,133.05,53340000 +1981-04-28,135.48,136.09,133.1,134.33,58210000 +1981-04-27,135.14,136.56,134.13,135.48,51080000 +1981-04-24,133.94,136,132.88,135.14,60000000 +1981-04-23,134.14,135.9,132.9,133.94,64200000 +1981-04-22,134.23,135.54,132.72,134.14,60660000 +1981-04-21,135.45,136.38,133.49,134.23,60280000 +1981-04-20,134.7,136.25,133.19,135.45,51020000 +1981-04-16,134.17,135.82,133.43,134.7,52950000 +1981-04-15,132.68,134.79,132.2,134.17,56040000 +1981-04-14,133.15,134.03,131.58,132.68,48350000 +1981-04-13,134.51,134.91,132.24,133.15,49860000 +1981-04-10,134.67,136.23,133.18,134.51,58130000 +1981-04-09,134.31,135.8,132.59,134.67,59520000 +1981-04-08,133.91,135.34,133.26,134.31,48000000 +1981-04-07,133.93,135.27,132.96,133.91,44540000 +1981-04-06,135.49,135.61,132.91,133.93,43190000 +1981-04-03,136.32,137.04,134.67,135.49,48680000 +1981-04-02,136.57,137.72,135.16,136.32,52570000 +1981-04-01,136,137.56,135.04,136.57,54880000 +1981-03-31,134.68,137.15,134.68,136,50980000 +1981-03-30,134.65,135.87,133.51,134.28,33500000 +1981-03-27,136.27,136.89,133.91,134.65,46930000 +1981-03-26,137.11,138.38,135.29,136.27,60370000 +1981-03-25,134.67,137.32,133.92,137.11,56320000 +1981-03-24,135.69,137.4,134.1,134.67,66400000 +1981-03-23,134.08,136.5,133.41,135.69,57880000 +1981-03-20,133.46,135.29,132.5,134.08,61980000 +1981-03-19,134.22,135.37,132.37,133.46,62440000 +1981-03-18,133.92,135.66,132.8,134.22,55740000 +1981-03-17,134.68,136.09,132.8,133.92,65920000 +1981-03-16,133.11,135.35,132.1,134.68,49940000 +1981-03-13,133.19,135.53,132.39,133.11,68290000 +1981-03-12,129.95,133.56,129.76,133.19,54640000 +1981-03-11,130.46,131.2,128.72,129.95,47390000 +1981-03-10,131.12,132.64,129.72,130.46,56610000 +1981-03-09,129.85,131.94,129.39,131.12,46180000 +1981-03-06,129.93,131.18,128.56,129.85,43940000 +1981-03-05,130.86,131.82,129.25,129.93,45380000 +1981-03-04,130.56,132.07,129.57,130.86,47260000 +1981-03-03,132.01,132.72,129.66,130.56,48730000 +1981-03-02,131.27,132.96,130.15,132.01,47710000 +1981-02-27,130.1,132.02,129.35,131.27,53210000 +1981-02-26,128.52,130.93,128.02,130.1,60300000 +1981-02-25,127.39,129.21,125.77,128.52,45710000 +1981-02-24,127.35,128.76,126.49,127.39,43960000 +1981-02-23,126.58,128.28,125.69,127.35,39590000 +1981-02-20,126.61,127.65,124.66,126.58,41900000 +1981-02-19,128.48,129.07,125.98,126.61,41630000 +1981-02-18,127.81,129.25,127.09,128.48,40410000 +1981-02-17,126.98,128.75,126.43,127.81,37940000 +1981-02-13,127.48,128.34,126.04,126.98,33360000 +1981-02-12,128.24,128.95,126.78,127.48,34700000 +1981-02-11,129.24,129.92,127.6,128.24,37770000 +1981-02-10,129.27,130.19,128.05,129.24,40820000 +1981-02-09,130.6,131.39,128.61,129.27,38330000 +1981-02-06,129.63,131.81,129.03,130.6,45820000 +1981-02-05,128.59,130.49,127.99,129.63,45320000 +1981-02-04,128.46,129.71,127.29,128.59,45520000 +1981-02-03,126.91,128.92,125.89,128.46,45950000 +1981-02-02,129.48,129.48,125.82,126.91,44070000 +1981-01-30,130.24,131.65,128.61,129.55,41160000 +1981-01-29,130.34,131.78,128.97,130.24,38170000 +1981-01-28,131.12,132.41,129.82,130.34,36690000 +1981-01-27,129.84,131.95,129.32,131.12,42260000 +1981-01-26,130.23,131.18,128.57,129.84,35380000 +1981-01-23,130.26,131.34,129,130.23,37220000 +1981-01-22,131.36,132.08,129.23,130.26,39880000 +1981-01-21,131.65,132.48,129.93,131.36,39190000 +1981-01-20,134.37,135.3,131.26,131.65,41750000 +1981-01-19,134.77,135.86,133.51,134.37,36470000 +1981-01-16,134.22,135.91,133.35,134.77,43260000 +1981-01-15,133.47,135.15,132.44,134.22,39640000 +1981-01-14,133.29,135.25,132.65,133.47,41390000 +1981-01-13,133.52,134.27,131.69,133.29,40890000 +1981-01-12,133.48,135.88,132.79,133.52,48760000 +1981-01-09,133.06,134.76,131.71,133.48,50190000 +1981-01-08,135.08,136.1,131.96,133.06,55350000 +1981-01-07,136.02,136.02,132.3,135.08,92890000 +1981-01-06,137.97,140.32,135.78,138.12,67400000 +1981-01-05,136.34,139.24,135.86,137.97,58710000 +1981-01-02,135.76,137.1,134.61,136.34,28870000 +1980-12-31,135.33,136.76,134.29,135.76,41210000 +1980-12-30,135.03,136.51,134.04,135.33,39750000 +1980-12-29,136.57,137.51,134.36,135.03,36060000 +1980-12-26,135.88,137.02,135.2,136.57,16130000 +1980-12-24,135.3,136.55,134.15,135.88,29490000 +1980-12-23,135.78,137.48,134.01,135.3,55260000 +1980-12-22,133.7,136.68,132.88,135.78,51950000 +1980-12-19,133,134,131.8,133.7,50770000 +1980-12-18,132.89,135.9,131.89,133,69570000 +1980-12-17,130.6,133.59,130.22,132.89,50800000 +1980-12-16,129.45,131.22,128.33,130.6,41630000 +1980-12-15,129.23,131.33,128.64,129.45,39700000 +1980-12-12,127.36,129.98,127.15,129.23,39530000 +1980-12-11,128.26,128.73,125.32,127.36,60220000 +1980-12-10,130.48,131.99,127.94,128.26,49860000 +1980-12-09,130.61,131.92,128.77,130.48,53220000 +1980-12-08,133.19,133.19,129.71,130.61,53390000 +1980-12-05,136.37,136.37,132.91,134.03,51990000 +1980-12-04,136.71,138.4,135.09,136.48,51170000 +1980-12-03,136.97,138.09,135.43,136.71,43430000 +1980-12-02,137.21,138.11,134.37,136.97,52340000 +1980-12-01,140.52,140.66,136.75,137.21,48180000 +1980-11-28,140.17,141.54,139,140.52,34240000 +1980-11-26,139.33,141.96,138.6,140.17,55340000 +1980-11-25,138.31,140.83,137.42,139.33,55840000 +1980-11-24,139.11,139.36,136.36,138.31,51120000 +1980-11-21,140.4,141.24,138.1,139.11,55950000 +1980-11-20,139.06,141.24,137.79,140.4,60180000 +1980-11-19,139.7,141.76,138.06,139.06,69230000 +1980-11-18,137.91,140.92,137.91,139.7,70380000 +1980-11-17,137.15,138.46,134.9,137.75,50260000 +1980-11-14,136.49,138.96,135.12,137.15,71630000 +1980-11-13,134.59,137.21,134.12,136.49,69340000 +1980-11-12,131.33,135.12,131.33,134.59,58500000 +1980-11-11,129.48,132.3,129.48,131.26,41520000 +1980-11-10,129.18,130.51,128.19,129.48,35720000 +1980-11-07,128.91,130.08,127.74,129.18,40070000 +1980-11-06,131.3,131.3,128.23,128.91,48890000 +1980-11-05,130.77,135.65,130.77,131.33,84080000 +1980-11-03,127.47,129.85,127.23,129.04,35820000 +1980-10-31,126.29,128.24,125.29,127.47,40110000 +1980-10-30,127.91,128.71,125.78,126.29,39060000 +1980-10-29,128.05,129.91,127.07,127.91,37200000 +1980-10-28,127.88,128.86,126.36,128.05,40300000 +1980-10-27,129.85,129.94,127.34,127.88,34430000 +1980-10-24,129.53,130.55,128.04,129.85,41050000 +1980-10-23,131.92,132.54,128.87,129.53,49200000 +1980-10-22,131.84,132.97,130.62,131.92,43060000 +1980-10-21,132.61,134.01,130.78,131.84,51220000 +1980-10-20,131.52,133.21,130.04,132.61,40910000 +1980-10-17,132.22,133.07,130.22,131.52,43920000 +1980-10-16,133.7,135.88,131.64,132.22,65450000 +1980-10-15,132.02,134.35,131.59,133.7,48260000 +1980-10-14,132.03,133.57,131.16,132.02,48830000 +1980-10-13,130.29,132.46,129.37,132.03,31360000 +1980-10-10,131.04,132.15,129.58,130.29,44040000 +1980-10-09,131.65,132.65,130.25,131.04,43980000 +1980-10-08,131,132.78,130.28,131.65,46580000 +1980-10-07,131.73,132.88,130.1,131,50310000 +1980-10-06,129.35,132.38,129.35,131.73,50130000 +1980-10-03,128.09,130.44,127.65,129.33,47510000 +1980-10-02,127.13,128.82,126.04,128.09,46160000 +1980-10-01,125.46,127.88,124.66,127.13,48720000 +1980-09-30,123.54,126.09,123.54,125.46,40290000 +1980-09-29,125.41,125.41,122.87,123.54,46410000 +1980-09-26,128.17,128.17,125.29,126.35,49460000 +1980-09-25,130.37,131.53,128.13,128.72,49510000 +1980-09-24,129.43,131.34,128.45,130.37,56860000 +1980-09-23,130.4,132.17,128.55,129.43,64390000 +1980-09-22,129.25,130.99,127.89,130.4,53140000 +1980-09-19,128.4,130.33,127.57,129.25,53780000 +1980-09-18,128.87,130.38,127.63,128.4,63390000 +1980-09-17,126.74,129.68,126.37,128.87,63990000 +1980-09-16,125.67,127.78,125.15,126.74,57290000 +1980-09-15,125.54,126.35,124.09,125.67,44630000 +1980-09-12,125.66,126.75,124.72,125.54,47180000 +1980-09-11,124.81,126.48,124.19,125.66,44770000 +1980-09-10,124.07,125.95,123.6,124.81,51430000 +1980-09-09,123.31,124.52,121.94,124.07,44460000 +1980-09-08,124.88,125.67,122.78,123.31,42050000 +1980-09-05,125.42,126.12,124.08,124.88,37990000 +1980-09-04,126.12,127.7,124.42,125.42,59030000 +1980-09-03,123.87,126.43,123.87,126.12,52370000 +1980-09-02,122.38,124.36,121.79,123.74,35290000 +1980-08-29,122.08,123.01,121.06,122.38,33510000 +1980-08-28,123.52,123.91,121.61,122.08,39890000 +1980-08-27,124.84,124.98,122.93,123.52,44000000 +1980-08-26,125.16,126.29,124.01,124.84,41700000 +1980-08-25,126.02,126.28,124.65,125.16,35400000 +1980-08-22,125.46,127.78,125.18,126.02,58210000 +1980-08-21,123.77,125.99,123.61,125.46,50770000 +1980-08-20,122.6,124.27,121.91,123.77,42560000 +1980-08-19,123.39,124,121.97,122.6,41930000 +1980-08-18,125.28,125.28,122.82,123.39,41890000 +1980-08-15,125.25,126.61,124.57,125.72,47780000 +1980-08-14,123.28,125.62,122.68,125.25,47700000 +1980-08-13,123.79,124.67,122.49,123.28,44350000 +1980-08-12,124.78,125.78,123.29,123.79,52050000 +1980-08-11,123.61,125.31,122.85,124.78,44690000 +1980-08-08,123.3,125.23,122.82,123.61,58860000 +1980-08-07,121.66,123.84,121.66,123.3,61820000 +1980-08-06,120.74,122.01,119.94,121.55,45050000 +1980-08-05,120.98,122.09,119.96,120.74,45510000 +1980-08-04,121.21,121.63,119.42,120.98,41550000 +1980-08-01,121.67,122.38,120.08,121.21,46440000 +1980-07-31,122.23,122.34,119.4,121.67,54610000 +1980-07-30,122.4,123.93,121.16,122.23,58060000 +1980-07-29,121.43,122.99,120.76,122.4,44840000 +1980-07-28,120.78,122.02,119.78,121.43,35330000 +1980-07-25,121.79,121.96,119.94,120.78,36250000 +1980-07-24,121.93,122.98,120.83,121.79,42420000 +1980-07-23,122.19,123.26,120.93,121.93,45890000 +1980-07-22,122.51,123.9,121.38,122.19,52230000 +1980-07-21,122.04,123.15,120.85,122.51,42750000 +1980-07-18,121.44,123.19,120.88,122.04,58040000 +1980-07-17,119.63,121.84,119.43,121.44,48850000 +1980-07-16,119.3,120.87,118.54,119.63,49140000 +1980-07-15,120.01,121.56,118.85,119.3,60920000 +1980-07-14,117.84,120.37,117.45,120.01,45500000 +1980-07-11,116.95,118.38,116.29,117.84,38310000 +1980-07-10,117.98,118.57,116.38,116.95,43730000 +1980-07-09,117.84,119.52,117.1,117.98,52010000 +1980-07-08,118.29,119.11,117.07,117.84,45830000 +1980-07-07,117.46,118.85,116.96,118.29,42540000 +1980-07-03,115.68,117.8,115.49,117.46,47230000 +1980-07-02,114.93,116.44,114.36,115.68,42950000 +1980-07-01,114.24,115.45,113.54,114.93,34340000 +1980-06-30,116,116.04,113.55,114.24,29910000 +1980-06-27,116.19,116.93,115.06,116,33110000 +1980-06-26,116.72,117.98,115.58,116.19,45110000 +1980-06-25,115.14,117.37,115.07,116.72,46500000 +1980-06-24,114.51,115.75,113.76,115.14,37730000 +1980-06-23,114.06,115.28,113.35,114.51,34180000 +1980-06-20,114.66,114.9,113.12,114.06,36530000 +1980-06-19,116.26,116.81,114.36,114.66,38280000 +1980-06-18,116.03,116.84,114.77,116.26,41960000 +1980-06-17,116.09,117.16,115.13,116.03,41990000 +1980-06-16,115.81,116.8,114.78,116.09,36190000 +1980-06-13,115.52,116.94,114.67,115.81,41880000 +1980-06-12,116.02,117.01,114.28,115.52,47300000 +1980-06-11,114.66,116.64,114.22,116.02,43800000 +1980-06-10,113.71,115.5,113.17,114.66,42030000 +1980-06-09,113.2,114.51,112.68,113.71,36820000 +1980-06-06,112.78,114.01,112.11,113.2,37230000 +1980-06-05,112.61,114.38,111.89,112.78,49070000 +1980-06-04,110.51,113.45,110.22,112.61,44180000 +1980-06-03,110.76,111.63,109.77,110.51,33150000 +1980-06-02,111.24,112.15,110.06,110.76,32710000 +1980-05-30,110.27,111.55,108.87,111.24,34820000 +1980-05-29,112.06,112.64,109.86,110.27,42000000 +1980-05-28,111.4,112.72,110.42,112.06,38580000 +1980-05-27,110.62,112.3,110.35,111.4,40810000 +1980-05-23,109.01,111.37,109.01,110.62,45790000 +1980-05-22,107.72,109.73,107.34,109.01,41040000 +1980-05-21,107.62,108.31,106.54,107.72,34830000 +1980-05-20,107.67,108.39,106.75,107.62,31800000 +1980-05-19,107.35,108.43,106.51,107.67,30970000 +1980-05-16,106.99,107.89,106.25,107.35,31710000 +1980-05-15,106.85,107.99,106.07,106.99,41120000 +1980-05-14,106.3,107.89,106,106.85,40840000 +1980-05-13,104.78,106.76,104.44,106.3,35460000 +1980-05-12,104.72,105.48,103.5,104.78,28220000 +1980-05-09,106.13,106.2,104.18,104.72,30280000 +1980-05-08,107.18,108.02,105.5,106.13,39280000 +1980-05-07,106.25,108.12,105.83,107.18,42600000 +1980-05-06,106.38,107.83,105.36,106.25,40160000 +1980-05-05,105.58,106.83,104.64,106.38,34090000 +1980-05-02,105.46,106.25,104.61,105.58,28040000 +1980-05-01,106.29,106.86,104.72,105.46,32480000 +1980-04-30,105.86,106.72,104.5,106.29,30850000 +1980-04-29,105.64,106.7,104.86,105.86,27940000 +1980-04-28,105.16,106.79,104.64,105.64,30600000 +1980-04-25,104.4,105.57,103.02,105.16,28590000 +1980-04-24,103.73,105.43,102.93,104.4,35790000 +1980-04-23,103.43,105.11,102.81,103.73,42620000 +1980-04-22,100.81,104.02,100.81,103.43,47920000 +1980-04-21,100.55,101.26,98.95,99.8,27560000 +1980-04-18,101.05,102.07,99.97,100.55,26880000 +1980-04-17,101.54,102.21,100.12,101.05,32770000 +1980-04-16,102.63,104.42,101.13,101.54,39730000 +1980-04-15,102.84,103.94,101.85,102.63,26670000 +1980-04-14,103.79,103.92,102.08,102.84,23060000 +1980-04-11,104.08,105.15,103.2,103.79,29960000 +1980-04-10,103.11,105,102.81,104.08,33940000 +1980-04-09,101.2,103.6,101.01,103.11,33020000 +1980-04-08,100.19,101.88,99.23,101.2,31700000 +1980-04-07,102.15,102.27,99.73,100.19,29130000 +1980-04-03,102.68,103.34,101.31,102.15,27970000 +1980-04-02,102.18,103.87,101.45,102.68,35210000 +1980-04-01,102.09,103.28,100.85,102.18,32230000 +1980-03-31,100.68,102.65,100.02,102.09,35840000 +1980-03-28,98.22,101.43,97.72,100.68,46720000 +1980-03-27,98.68,99.58,94.23,98.22,63680000 +1980-03-26,99.19,101.22,98.1,98.68,37370000 +1980-03-25,99.28,100.58,97.89,99.19,43790000 +1980-03-24,102.18,102.18,98.88,99.28,39230000 +1980-03-21,103.12,103.73,101.55,102.31,32220000 +1980-03-20,104.31,105.17,102.52,103.12,32580000 +1980-03-19,104.1,105.72,103.35,104.31,36520000 +1980-03-18,102.26,104.71,101.14,104.1,47340000 +1980-03-17,105.23,105.23,101.82,102.26,37020000 +1980-03-14,105.62,106.49,104.01,105.43,35180000 +1980-03-13,106.87,107.55,105.1,105.62,33070000 +1980-03-12,107.78,108.4,105.42,106.87,37990000 +1980-03-11,106.51,108.54,106.18,107.78,41350000 +1980-03-10,106.9,107.86,104.92,106.51,43750000 +1980-03-07,108.65,108.96,105.99,106.9,50950000 +1980-03-06,111.13,111.29,107.85,108.65,49610000 +1980-03-05,112.78,113.94,110.58,111.13,49240000 +1980-03-04,112.5,113.41,110.83,112.78,44310000 +1980-03-03,113.66,114.34,112.01,112.5,38690000 +1980-02-29,112.35,114.12,111.77,113.66,38810000 +1980-02-28,112.38,113.7,111.33,112.35,40330000 +1980-02-27,113.98,115.12,111.91,112.38,46430000 +1980-02-26,113.33,114.76,112.3,113.98,40000000 +1980-02-25,114.93,114.93,112.62,113.33,39140000 +1980-02-22,115.28,116.46,113.43,115.04,48210000 +1980-02-21,116.47,117.9,114.44,115.28,51530000 +1980-02-20,114.6,117.18,114.06,116.47,44340000 +1980-02-19,115.41,115.67,113.35,114.6,39480000 +1980-02-15,116.7,116.7,114.12,115.41,46680000 +1980-02-14,118.44,119.3,116.04,116.72,50540000 +1980-02-13,117.9,120.22,117.57,118.44,65230000 +1980-02-12,117.12,118.41,115.75,117.9,48090000 +1980-02-11,117.95,119.05,116.31,117.12,58660000 +1980-02-08,116.28,118.66,115.72,117.95,57860000 +1980-02-07,115.72,117.87,115.22,116.28,57690000 +1980-02-06,114.66,116.57,113.65,115.72,51950000 +1980-02-05,114.37,115.25,112.15,114.66,41880000 +1980-02-04,115.12,116.01,113.83,114.37,43070000 +1980-02-01,114.16,115.54,113.13,115.12,46610000 +1980-01-31,115.2,117.17,113.78,114.16,65900000 +1980-01-30,114.07,115.85,113.37,115.2,51170000 +1980-01-29,114.85,115.77,113.03,114.07,55480000 +1980-01-28,113.61,115.65,112.93,114.85,53620000 +1980-01-25,113.7,114.45,112.36,113.61,47100000 +1980-01-24,113.44,115.27,112.95,113.7,59070000 +1980-01-23,111.51,113.93,110.93,113.44,50730000 +1980-01-22,112.1,113.1,110.92,111.51,50620000 +1980-01-21,111.07,112.9,110.66,112.1,48040000 +1980-01-18,110.7,111.74,109.88,111.07,47150000 +1980-01-17,111.05,112.01,109.81,110.7,54170000 +1980-01-16,111.14,112.9,110.38,111.05,67700000 +1980-01-15,110.38,111.93,109.45,111.14,52320000 +1980-01-14,109.92,111.44,109.34,110.38,52930000 +1980-01-11,109.89,111.16,108.89,109.92,52890000 +1980-01-10,109.05,110.86,108.47,109.89,55980000 +1980-01-09,108.95,111.09,108.41,109.05,65260000 +1980-01-08,106.81,109.29,106.29,108.95,53390000 +1980-01-07,106.52,107.8,105.8,106.81,44500000 +1980-01-04,105.22,107.08,105.09,106.52,39130000 +1980-01-03,105.76,106.08,103.26,105.22,50480000 +1980-01-02,107.94,108.43,105.29,105.76,40610000 +1979-12-31,107.84,108.53,107.26,107.94,31530000 +1979-12-28,107.96,108.61,107.16,107.84,34430000 +1979-12-27,107.78,108.5,107.14,107.96,31410000 +1979-12-26,107.66,108.37,107.06,107.78,24960000 +1979-12-24,107.59,108.08,106.8,107.66,19150000 +1979-12-21,108.26,108.76,106.99,107.59,36160000 +1979-12-20,108.2,109.24,107.4,108.26,40380000 +1979-12-19,108.3,108.79,107.02,108.2,41780000 +1979-12-18,109.33,109.83,107.83,108.3,43310000 +1979-12-17,108.92,110.33,108.36,109.33,43830000 +1979-12-14,107.67,109.49,107.37,108.92,41800000 +1979-12-13,107.52,108.29,106.68,107.67,36690000 +1979-12-12,107.49,108.32,106.78,107.52,34630000 +1979-12-11,107.67,108.58,106.79,107.49,36160000 +1979-12-10,107.52,108.27,106.65,107.67,32270000 +1979-12-07,108,109.24,106.55,107.52,42370000 +1979-12-06,107.25,108.47,106.71,108,37510000 +1979-12-05,106.79,108.36,106.6,107.25,39300000 +1979-12-04,105.83,107.25,105.66,106.79,33510000 +1979-12-03,106.16,106.65,105.07,105.83,29030000 +1979-11-30,106.81,107.16,105.56,106.16,30480000 +1979-11-29,106.77,107.84,106.17,106.81,33550000 +1979-11-28,106.38,107.55,105.29,106.77,39690000 +1979-11-27,106.8,107.89,105.64,106.38,45140000 +1979-11-26,104.83,107.44,104.83,106.8,47940000 +1979-11-23,103.89,105.13,103.56,104.67,23300000 +1979-11-21,103.69,104.23,102.04,103.89,37020000 +1979-11-20,104.23,105.11,103.14,103.69,35010000 +1979-11-19,103.79,105.08,103.17,104.23,33090000 +1979-11-16,104.13,104.72,103.07,103.79,30060000 +1979-11-15,103.39,104.94,103.1,104.13,32380000 +1979-11-14,102.94,104.13,101.91,103.39,30970000 +1979-11-13,103.51,104.21,102.42,102.94,29240000 +1979-11-12,101.51,103.72,101.27,103.51,26640000 +1979-11-09,100.58,102.18,100.58,101.51,30060000 +1979-11-08,99.87,101,99.49,100.3,26270000 +1979-11-07,100.97,100.97,99.42,99.87,30830000 +1979-11-06,101.82,102.01,100.77,101.2,21960000 +1979-11-05,102.51,102.66,101.24,101.82,20470000 +1979-11-02,102.57,103.21,101.92,102.51,23670000 +1979-11-01,101.82,103.07,101.1,102.57,25880000 +1979-10-31,102.67,103.16,101.38,101.82,27780000 +1979-10-30,100.71,102.83,100.41,102.67,28890000 +1979-10-29,100.57,101.56,100.13,100.71,22720000 +1979-10-26,100,101.31,99.59,100.57,29660000 +1979-10-25,100.44,101.39,99.56,100,28440000 +1979-10-24,100.28,101.45,99.66,100.44,31480000 +1979-10-23,100.71,101.44,99.61,100.28,32910000 +1979-10-22,101.38,101.38,99.06,100.71,45240000 +1979-10-19,103.58,103.58,101.24,101.6,42430000 +1979-10-18,103.39,104.62,102.92,103.61,29590000 +1979-10-17,103.19,104.54,102.74,103.39,29650000 +1979-10-16,103.36,104.37,102.52,103.19,33770000 +1979-10-15,104.49,104.74,102.69,103.36,34850000 +1979-10-12,105.05,106.2,104.01,104.49,36390000 +1979-10-11,105.3,106.33,103.7,105.05,47530000 +1979-10-10,106.23,106.23,102.31,105.3,81620000 +1979-10-09,109.43,109.43,106.04,106.63,55560000 +1979-10-08,111.27,111.83,109.65,109.88,32610000 +1979-10-05,110.17,112.16,110.16,111.27,48250000 +1979-10-04,109.59,110.81,109.14,110.17,38800000 +1979-10-03,109.59,110.43,108.88,109.59,36470000 +1979-10-02,108.56,110.08,108.03,109.59,38310000 +1979-10-01,109.19,109.19,107.7,108.56,24980000 +1979-09-28,110.21,110.67,108.7,109.32,35950000 +1979-09-27,109.96,110.75,109.19,110.21,33110000 +1979-09-26,109.68,111.25,109.37,109.96,37700000 +1979-09-25,109.61,110.19,108.27,109.68,32410000 +1979-09-24,110.47,110.9,109.16,109.61,33790000 +1979-09-21,110.51,111.58,109.46,110.47,52380000 +1979-09-20,108.28,110.69,107.59,110.51,45100000 +1979-09-19,108,109.02,107.52,108.28,35370000 +1979-09-18,108.84,109,107.32,108,38750000 +1979-09-17,108.76,110.06,108.4,108.84,37610000 +1979-09-14,107.85,109.48,107.42,108.76,41980000 +1979-09-13,107.82,108.53,107.06,107.85,35240000 +1979-09-12,107.51,108.41,106.72,107.82,39350000 +1979-09-11,108.17,108.83,106.8,107.51,42530000 +1979-09-10,107.66,108.71,107.21,108.17,32980000 +1979-09-07,106.85,108.09,106.3,107.66,34360000 +1979-09-06,106.4,107.61,105.97,106.85,30330000 +1979-09-05,107.19,107.19,105.38,106.4,41650000 +1979-09-04,109.32,109.41,107.22,107.44,33350000 +1979-08-31,109.02,109.8,108.58,109.32,26370000 +1979-08-30,109.02,109.59,108.4,109.02,29300000 +1979-08-29,109.02,109.59,108.36,109.02,30810000 +1979-08-28,109.14,109.65,108.47,109.02,29430000 +1979-08-27,108.6,109.84,108.12,109.14,32050000 +1979-08-24,108.63,109.11,107.65,108.6,32730000 +1979-08-23,108.99,109.59,108.12,108.63,35710000 +1979-08-22,108.91,109.56,108.09,108.99,38450000 +1979-08-21,108.83,109.68,108.17,108.91,38860000 +1979-08-20,108.3,109.32,107.69,108.83,32300000 +1979-08-17,108.09,108.94,107.25,108.3,31630000 +1979-08-16,108.25,109.18,107.38,108.09,47000000 +1979-08-15,107.52,108.64,106.75,108.25,46130000 +1979-08-14,107.42,108.03,106.6,107.52,40910000 +1979-08-13,106.4,107.9,106.28,107.42,41980000 +1979-08-10,105.49,106.79,104.81,106.4,36740000 +1979-08-09,105.98,106.25,104.89,105.49,34630000 +1979-08-08,105.65,106.84,105.2,105.98,44970000 +1979-08-07,104.3,106.23,104.12,105.65,45410000 +1979-08-06,104.04,104.66,103.27,104.3,27190000 +1979-08-03,104.1,104.56,103.36,104.04,28160000 +1979-08-02,104.17,105.02,103.59,104.1,37720000 +1979-08-01,103.81,104.57,103.14,104.17,36570000 +1979-07-31,103.15,104.26,102.89,103.81,34360000 +1979-07-30,103.1,103.63,102.42,103.15,28640000 +1979-07-27,103.1,103.5,102.29,103.1,27760000 +1979-07-26,103.08,103.63,102.34,103.1,32270000 +1979-07-25,101.97,103.44,101.85,103.08,34890000 +1979-07-24,101.59,102.5,101.14,101.97,29690000 +1979-07-23,101.82,102.13,100.84,101.59,26860000 +1979-07-20,101.61,102.32,101.06,101.82,26360000 +1979-07-19,101.69,102.42,101.04,101.61,26780000 +1979-07-18,101.83,102.06,100.35,101.69,35950000 +1979-07-17,102.74,103.06,101.27,101.83,34270000 +1979-07-16,102.32,103.2,101.81,102.74,26620000 +1979-07-13,102.69,102.99,101.49,102.32,33080000 +1979-07-12,103.64,103.72,102.22,102.69,31780000 +1979-07-11,104.2,104.34,102.87,103.64,36650000 +1979-07-10,104.47,105.17,103.52,104.2,39730000 +1979-07-09,103.62,105.07,103.36,104.47,42460000 +1979-07-06,102.43,103.91,102.12,103.62,38570000 +1979-07-05,102.09,102.88,101.59,102.43,30290000 +1979-07-03,101.99,102.57,101.31,102.09,31670000 +1979-07-02,102.91,103,101.45,101.99,32060000 +1979-06-29,102.8,103.67,102.04,102.91,34690000 +1979-06-28,102.27,103.46,101.91,102.8,38470000 +1979-06-27,101.66,102.95,101.29,102.27,36720000 +1979-06-26,102.09,102.09,101.22,101.66,34680000 +1979-06-25,102.64,102.91,101.45,102.09,31330000 +1979-06-22,102.09,103.16,101.91,102.64,36410000 +1979-06-21,101.63,102.74,101.2,102.09,36490000 +1979-06-20,101.58,102.19,100.93,101.63,33790000 +1979-06-19,101.56,102.28,100.91,101.58,30780000 +1979-06-18,102.09,102.48,101.05,101.56,30970000 +1979-06-15,102.2,102.78,101.38,102.09,40740000 +1979-06-14,102.31,102.63,101.04,102.2,37850000 +1979-06-13,102.85,103.58,101.83,102.31,40740000 +1979-06-12,101.91,103.64,101.81,102.85,45450000 +1979-06-11,101.49,102.24,100.91,101.91,28270000 +1979-06-08,101.79,102.23,100.91,101.49,31470000 +1979-06-07,101.3,102.54,101.15,101.79,43380000 +1979-06-06,100.62,101.96,100.38,101.3,39830000 +1979-06-05,99.32,101.07,99.17,100.62,35050000 +1979-06-04,99.17,99.76,98.61,99.32,24040000 +1979-06-01,99.08,99.7,98.57,99.17,24560000 +1979-05-31,99.11,99.61,98.29,99.08,30300000 +1979-05-30,100.05,100.25,98.79,99.11,29250000 +1979-05-29,100.22,100.76,99.56,100.05,27040000 +1979-05-25,99.93,100.68,99.52,100.22,27810000 +1979-05-24,99.89,100.44,99.14,99.93,25710000 +1979-05-23,100.51,101.31,99.63,99.89,30390000 +1979-05-22,100.14,100.93,99.45,100.51,30400000 +1979-05-21,99.93,100.75,99.37,100.14,25550000 +1979-05-18,99.94,100.73,99.33,99.93,26590000 +1979-05-17,98.42,100.22,98.29,99.94,30550000 +1979-05-16,98.14,98.8,97.49,98.42,28350000 +1979-05-15,98.06,98.9,97.6,98.14,26190000 +1979-05-14,98.52,98.95,97.71,98.06,22450000 +1979-05-11,98.52,99.03,97.92,98.52,24010000 +1979-05-10,99.46,99.63,98.22,98.52,25230000 +1979-05-09,99.17,100.01,98.5,99.46,27670000 +1979-05-08,99.02,99.56,97.98,99.17,32720000 +1979-05-07,100.37,100.37,98.78,99.02,30480000 +1979-05-04,101.81,102.08,100.42,100.69,30630000 +1979-05-03,101.72,102.57,101.25,101.81,30870000 +1979-05-02,101.68,102.28,101,101.72,30510000 +1979-05-01,101.76,102.5,101.22,101.68,31040000 +1979-04-30,101.8,102.24,100.91,101.76,26440000 +1979-04-27,102.01,102.32,101.04,101.8,29610000 +1979-04-26,102.5,102.91,101.58,102.01,32400000 +1979-04-25,102.2,103.07,101.79,102.5,31750000 +1979-04-24,101.57,103.02,101.39,102.2,35540000 +1979-04-23,101.23,102,100.68,101.57,25610000 +1979-04-20,101.28,101.81,100.46,101.23,28830000 +1979-04-19,101.7,102.4,100.88,101.28,31150000 +1979-04-18,101.24,102.23,100.96,101.7,29510000 +1979-04-17,101.12,101.94,100.65,101.24,29260000 +1979-04-16,102,102.02,100.67,101.12,28050000 +1979-04-12,102.31,102.77,101.51,102,26780000 +1979-04-11,103.34,103.77,101.92,102.31,32900000 +1979-04-10,102.87,103.83,102.42,103.34,31900000 +1979-04-09,103.18,103.56,102.28,102.87,27230000 +1979-04-06,103.26,103.95,102.58,103.18,34710000 +1979-04-05,102.65,103.6,102.16,103.26,34520000 +1979-04-04,102.4,103.73,102.16,102.65,41940000 +1979-04-03,100.9,102.67,100.81,102.4,33530000 +1979-04-02,101.56,101.56,100.14,100.9,28990000 +1979-03-30,102.03,102.51,101.03,101.59,29970000 +1979-03-29,102.12,102.78,101.43,102.03,28510000 +1979-03-28,102.48,103.31,101.74,102.12,39920000 +1979-03-27,101.04,102.71,100.81,102.48,32940000 +1979-03-26,101.6,101.77,100.6,101.04,23430000 +1979-03-23,101.67,102.37,101.02,101.6,33570000 +1979-03-22,101.25,102.41,101.04,101.67,34380000 +1979-03-21,100.5,101.48,99.87,101.25,31120000 +1979-03-20,101.06,101.34,100.01,100.5,27180000 +1979-03-19,100.69,101.94,100.35,101.06,34620000 +1979-03-16,99.86,101.16,99.53,100.69,31770000 +1979-03-15,99.71,100.57,99.11,99.86,29370000 +1979-03-14,99.84,100.43,99.23,99.71,24630000 +1979-03-13,99.67,100.66,99.13,99.84,31170000 +1979-03-12,99.54,100.04,98.56,99.67,25740000 +1979-03-09,99.58,100.58,99.12,99.54,33410000 +1979-03-08,98.44,99.82,98.1,99.58,32000000 +1979-03-07,97.87,99.23,97.67,98.44,28930000 +1979-03-06,98.06,98.53,97.36,97.87,24490000 +1979-03-05,97.03,98.64,97.03,98.06,25690000 +1979-03-02,96.9,97.55,96.44,96.97,23130000 +1979-03-01,96.28,97.28,95.98,96.9,23830000 +1979-02-28,96.13,96.69,95.38,96.28,25090000 +1979-02-27,97.65,97.65,95.69,96.13,31470000 +1979-02-26,97.78,98.28,97.2,97.67,22620000 +1979-02-23,98.33,98.5,97.29,97.78,22750000 +1979-02-22,99.07,99.21,97.88,98.33,26290000 +1979-02-21,99.42,100.07,98.69,99.07,26050000 +1979-02-20,98.67,99.67,98.26,99.42,22010000 +1979-02-16,98.73,99.23,98.11,98.67,21110000 +1979-02-15,98.87,99.13,97.96,98.73,22550000 +1979-02-14,98.93,99.64,98.21,98.87,27220000 +1979-02-13,98.25,99.58,98.25,98.93,28470000 +1979-02-12,97.87,98.55,97.05,98.2,20610000 +1979-02-09,97.65,98.5,97.28,97.87,24320000 +1979-02-08,97.16,98.11,96.82,97.65,23360000 +1979-02-07,98.05,98.07,96.51,97.16,28450000 +1979-02-06,98.09,98.74,97.48,98.05,23570000 +1979-02-05,99.07,99.07,97.57,98.09,26490000 +1979-02-02,99.96,100.52,99.1,99.5,25350000 +1979-02-01,99.93,100.38,99.01,99.96,27930000 +1979-01-31,101.05,101.41,99.47,99.93,30330000 +1979-01-30,101.55,102.07,100.68,101.05,26910000 +1979-01-29,101.86,102.33,100.99,101.55,24170000 +1979-01-26,101.19,102.59,101.03,101.86,34230000 +1979-01-25,100.16,101.66,99.99,101.19,31440000 +1979-01-24,100.6,101.31,99.67,100.16,31730000 +1979-01-23,99.9,101.05,99.35,100.6,30130000 +1979-01-22,99.75,100.35,98.9,99.9,24390000 +1979-01-19,99.72,100.57,99.22,99.75,26800000 +1979-01-18,99.48,100.35,98.91,99.72,27260000 +1979-01-17,99.46,100,98.33,99.48,25310000 +1979-01-16,100.69,100.88,99.11,99.46,30340000 +1979-01-15,99.93,101.13,99.58,100.69,27520000 +1979-01-12,99.32,100.91,99.32,99.93,37120000 +1979-01-11,98.77,99.41,97.95,99.1,24580000 +1979-01-10,99.33,99.75,98.28,98.77,24990000 +1979-01-09,98.8,99.96,98.62,99.33,27340000 +1979-01-08,99.13,99.3,97.83,98.8,21440000 +1979-01-05,98.58,99.79,98.25,99.13,28890000 +1979-01-04,97.8,99.42,97.52,98.58,33290000 +1979-01-03,96.81,98.54,96.81,97.8,29180000 +1979-01-02,96.11,96.96,95.22,96.73,18340000 +1978-12-29,96.28,97.03,95.48,96.11,30030000 +1978-12-28,96.66,97.19,95.82,96.28,25440000 +1978-12-27,97.51,97.51,96.15,96.66,23580000 +1978-12-26,96.31,97.89,95.99,97.52,21470000 +1978-12-22,94.77,96.62,94.77,96.31,23790000 +1978-12-21,94.68,95.66,94.11,94.71,28670000 +1978-12-20,94.24,95.2,93.7,94.68,26520000 +1978-12-19,93.44,94.85,93.05,94.24,25960000 +1978-12-18,94.33,94.33,92.64,93.44,32900000 +1978-12-15,96.04,96.28,94.88,95.33,23620000 +1978-12-14,96.06,96.44,95.2,96.04,20840000 +1978-12-13,96.59,97.07,95.59,96.06,22480000 +1978-12-12,97.11,97.58,96.27,96.59,22210000 +1978-12-11,96.63,97.56,96.07,97.11,21000000 +1978-12-08,97.08,97.48,96.14,96.63,18560000 +1978-12-07,97.49,98.1,96.58,97.08,21170000 +1978-12-06,97.44,98.58,96.83,97.49,29680000 +1978-12-05,96.15,97.7,95.88,97.44,25670000 +1978-12-04,96.28,96.96,95.37,96.15,22020000 +1978-12-01,95.01,96.69,95.01,96.28,26830000 +1978-11-30,93.75,94.94,93.29,94.7,19900000 +1978-11-29,94.92,94.92,93.48,93.75,21160000 +1978-11-28,95.99,96.51,94.88,95.15,22740000 +1978-11-27,95.79,96.52,95.17,95.99,19790000 +1978-11-24,95.48,96.17,94.98,95.79,14590000 +1978-11-22,95.01,95.91,94.54,95.48,20010000 +1978-11-21,95.25,95.83,94.49,95.01,20750000 +1978-11-20,94.42,95.86,94.29,95.25,24440000 +1978-11-17,93.71,95.03,93.59,94.42,25170000 +1978-11-16,92.71,94.08,92.59,93.71,21340000 +1978-11-15,92.49,94,92.29,92.71,26280000 +1978-11-14,93.13,93.53,91.77,92.49,30610000 +1978-11-13,94.77,94.9,92.96,93.13,20960000 +1978-11-10,94.42,95.39,93.94,94.77,16750000 +1978-11-09,94.45,95.5,93.81,94.42,23320000 +1978-11-08,93.85,94.74,92.89,94.45,23560000 +1978-11-07,94.75,94.75,93.14,93.85,25320000 +1978-11-06,96.18,96.49,94.84,95.19,20450000 +1978-11-03,95.61,96.98,94.78,96.18,25990000 +1978-11-02,96.85,97.31,94.84,95.61,41030000 +1978-11-01,94.13,97.41,94.13,96.85,50450000 +1978-10-31,95.06,95.8,92.72,93.15,42720000 +1978-10-30,94.59,95.49,91.65,95.06,59480000 +1978-10-27,96.03,96.62,94.3,94.59,40360000 +1978-10-26,97.31,97.71,95.59,96.03,31990000 +1978-10-25,97.49,98.56,96.33,97.31,31380000 +1978-10-24,98.18,98.95,97.13,97.49,28880000 +1978-10-23,97.95,98.84,96.63,98.18,36090000 +1978-10-20,99.26,99.26,97.12,97.95,43670000 +1978-10-19,100.49,101.03,99.04,99.33,31810000 +1978-10-18,101.26,101.76,99.89,100.49,32940000 +1978-10-17,102.35,102.35,100.47,101.26,37870000 +1978-10-16,104.63,104.63,102.43,102.61,24600000 +1978-10-13,104.88,105.34,104.07,104.66,21920000 +1978-10-12,105.39,106.23,104.42,104.88,30170000 +1978-10-11,104.46,105.64,103.8,105.39,21740000 +1978-10-10,104.59,105.36,103.9,104.46,25470000 +1978-10-09,103.52,104.89,103.31,104.59,19720000 +1978-10-06,103.27,104.23,102.82,103.52,27380000 +1978-10-05,103.06,104.1,102.54,103.27,27820000 +1978-10-04,102.6,103.36,101.76,103.06,25090000 +1978-10-03,102.96,103.56,102.18,102.6,22540000 +1978-10-02,102.54,103.42,102.13,102.96,18700000 +1978-09-29,101.96,103.08,101.65,102.54,23610000 +1978-09-28,101.66,102.38,100.94,101.96,24390000 +1978-09-27,102.62,103.44,101.33,101.66,28370000 +1978-09-26,101.86,103.15,101.58,102.62,26330000 +1978-09-25,101.84,102.36,101.05,101.86,20970000 +1978-09-22,101.9,102.69,101.13,101.84,27960000 +1978-09-21,101.73,102.54,100.66,101.9,33640000 +1978-09-20,102.53,103.29,101.28,101.73,35080000 +1978-09-19,103.21,103.82,102.12,102.53,31660000 +1978-09-18,104.12,105.03,102.75,103.21,35860000 +1978-09-15,105.1,105.12,103.56,104.12,37290000 +1978-09-14,106.34,106.62,104.77,105.1,37400000 +1978-09-13,106.99,107.85,105.87,106.34,43340000 +1978-09-12,106.98,107.48,106.02,106.99,34400000 +1978-09-11,106.79,108.05,106.42,106.98,39670000 +1978-09-08,105.5,107.19,105.5,106.79,42170000 +1978-09-07,105.38,106.49,104.76,105.42,40310000 +1978-09-06,104.51,106.19,104.51,105.38,42600000 +1978-09-05,103.68,104.83,103.31,104.49,32170000 +1978-09-01,103.29,104.27,102.73,103.68,35070000 +1978-08-31,103.5,104.05,102.63,103.29,33850000 +1978-08-30,103.39,104.26,102.7,103.5,37750000 +1978-08-29,103.96,104.34,102.92,103.39,33780000 +1978-08-28,104.9,105.14,103.61,103.96,31760000 +1978-08-25,105.08,105.68,104.24,104.9,36190000 +1978-08-24,104.91,105.86,104.29,105.08,38500000 +1978-08-23,104.31,105.68,104.12,104.91,39630000 +1978-08-22,103.89,104.79,103.14,104.31,29620000 +1978-08-21,104.73,105.2,103.44,103.89,29440000 +1978-08-18,105.08,105.98,104.23,104.73,34650000 +1978-08-17,104.65,106.27,104.34,105.08,45270000 +1978-08-16,103.85,105.15,103.41,104.65,36120000 +1978-08-15,103.97,104.38,102.86,103.85,29760000 +1978-08-14,103.96,104.98,103.4,103.97,32320000 +1978-08-11,103.66,104.67,102.85,103.96,33550000 +1978-08-10,104.5,105.11,103.1,103.66,39760000 +1978-08-09,104.01,105.72,103.7,104.5,48800000 +1978-08-08,103.55,104.35,102.6,104.01,34290000 +1978-08-07,103.92,104.84,103.03,103.55,33350000 +1978-08-04,103.51,104.67,102.75,103.92,37910000 +1978-08-03,102.92,105.41,102.82,103.51,66370000 +1978-08-02,100.66,103.21,100.18,102.92,47470000 +1978-08-01,100.68,101.46,99.95,100.66,34810000 +1978-07-31,100,101.18,99.37,100.68,33990000 +1978-07-28,99.54,100.51,98.9,100,33390000 +1978-07-27,99.08,100.17,98.6,99.54,33970000 +1978-07-26,99.08,99.08,99.08,99.08,36830000 +1978-07-25,97.72,98.73,97.2,98.44,25400000 +1978-07-24,97.75,98.13,96.72,97.72,23280000 +1978-07-21,98.03,98.57,97.02,97.75,26060000 +1978-07-20,98.12,99.18,97.49,98.03,33350000 +1978-07-19,96.87,98.41,96.71,98.12,30850000 +1978-07-18,97.78,97.98,96.52,96.87,22860000 +1978-07-17,97.58,98.84,97.24,97.78,29180000 +1978-07-14,96.25,97.88,95.89,97.58,28370000 +1978-07-13,96.24,96.66,95.42,96.25,23620000 +1978-07-12,95.93,96.83,95.5,96.24,26640000 +1978-07-11,95.27,96.49,94.92,95.93,27470000 +1978-07-10,94.89,95.67,94.28,95.27,22470000 +1978-07-07,94.32,95.32,94.02,94.89,23480000 +1978-07-06,94.27,94.83,93.59,94.32,24990000 +1978-07-05,95.09,95.2,93.78,94.27,23730000 +1978-07-03,95.53,95.65,94.62,95.09,11560000 +1978-06-30,95.57,95.96,94.87,95.53,18100000 +1978-06-29,95.4,96.26,95,95.57,21660000 +1978-06-28,94.98,95.79,94.44,95.4,23260000 +1978-06-27,94.6,95.48,93.99,94.98,29280000 +1978-06-26,95.85,96.06,94.31,94.6,29250000 +1978-06-23,96.24,96.98,95.49,95.85,28530000 +1978-06-22,96.01,96.76,95.52,96.24,27160000 +1978-06-21,96.51,96.74,95.42,96.01,29100000 +1978-06-20,97.49,97.78,96.15,96.51,27920000 +1978-06-19,97.42,97.94,96.53,97.49,25500000 +1978-06-16,98.34,98.59,97.1,97.42,27690000 +1978-06-15,99.48,99.54,97.97,98.34,29280000 +1978-06-14,99.57,100.68,98.89,99.48,37290000 +1978-06-13,99.55,99.98,98.43,99.57,30760000 +1978-06-12,99.93,100.6,99.16,99.55,24440000 +1978-06-09,100.21,100.71,99.3,99.93,32470000 +1978-06-08,100.12,101.21,99.55,100.21,39380000 +1978-06-07,100.32,100.81,99.36,100.12,33060000 +1978-06-06,99.95,101.84,99.9,100.32,51970000 +1978-06-05,98.14,100.27,97.97,99.95,39580000 +1978-06-02,97.35,98.52,97.01,98.14,31860000 +1978-06-01,97.24,97.95,96.63,97.35,28750000 +1978-05-31,96.86,97.97,96.5,97.24,29070000 +1978-05-30,96.58,97.23,95.95,96.86,21040000 +1978-05-26,96.8,97.14,96.01,96.58,21410000 +1978-05-25,97.08,97.8,96.3,96.8,28410000 +1978-05-24,97.74,97.74,96.27,97.08,31450000 +1978-05-23,99.09,99.17,97.53,98.05,33230000 +1978-05-22,98.12,99.43,97.65,99.09,28680000 +1978-05-19,98.62,99.06,97.42,98.12,34360000 +1978-05-18,99.6,100.04,98.19,98.62,42270000 +1978-05-17,99.35,100.32,98.63,99.6,45490000 +1978-05-16,98.76,100.16,98.61,99.35,48170000 +1978-05-15,98.07,99.11,97.4,98.76,33890000 +1978-05-12,97.2,98.89,97.14,98.07,46600000 +1978-05-11,95.92,97.47,95.6,97.2,36630000 +1978-05-10,95.9,96.69,95.35,95.92,33330000 +1978-05-09,96.19,96.68,95.33,95.9,30860000 +1978-05-08,96.53,97.5,95.82,96.19,34680000 +1978-05-05,95.93,97.44,95.56,96.53,42680000 +1978-05-04,96.26,96.43,94.57,95.93,37520000 +1978-05-03,97.25,97.61,95.84,96.26,37560000 +1978-05-02,97.67,98.11,96.44,97.25,41400000 +1978-05-01,96.83,98.3,96.41,97.67,37020000 +1978-04-28,95.86,97.1,95.24,96.83,32850000 +1978-04-27,96.82,96.93,95.3,95.86,35470000 +1978-04-26,96.64,97.75,95.96,96.82,44430000 +1978-04-25,96.05,97.91,96.05,96.64,55800000 +1978-04-24,94.34,96,94.08,95.77,34510000 +1978-04-21,94.54,95.09,93.71,94.34,31540000 +1978-04-20,93.97,95.71,93.97,94.54,43230000 +1978-04-19,93.43,94.48,92.75,93.86,35060000 +1978-04-18,94.45,94.72,92.87,93.43,38950000 +1978-04-17,93.6,95.89,93.6,94.45,63510000 +1978-04-14,91.4,93.31,91.4,92.92,52280000 +1978-04-13,90.11,91.27,89.82,90.98,31580000 +1978-04-12,90.25,90.78,89.65,90.11,26210000 +1978-04-11,90.49,90.79,89.77,90.25,24300000 +1978-04-10,90.17,90.88,89.73,90.49,25740000 +1978-04-07,89.79,90.59,89.39,90.17,25160000 +1978-04-06,89.64,90.46,89.31,89.79,27360000 +1978-04-05,88.86,89.91,88.62,89.64,27260000 +1978-04-04,88.46,89.18,88.16,88.86,20130000 +1978-04-03,89.2,89.2,88.07,88.46,20230000 +1978-03-31,89.41,89.64,88.68,89.21,20130000 +1978-03-30,89.64,89.89,88.97,89.41,20460000 +1978-03-29,89.5,90.17,89.14,89.64,25450000 +1978-03-28,88.87,89.76,88.47,89.5,21600000 +1978-03-27,89.36,89.5,88.51,88.87,18870000 +1978-03-23,89.47,89.9,88.83,89.36,21290000 +1978-03-22,89.79,90.07,88.99,89.47,21950000 +1978-03-21,90.82,91.06,89.5,89.79,24410000 +1978-03-20,90.2,91.35,90.1,90.82,28360000 +1978-03-17,89.51,90.52,89.17,90.2,28470000 +1978-03-16,89.12,89.77,88.58,89.51,25400000 +1978-03-15,89.35,89.73,88.52,89.12,23340000 +1978-03-14,88.95,89.62,88.21,89.35,24300000 +1978-03-13,88.88,89.77,88.48,88.95,24070000 +1978-03-10,87.89,89.25,87.82,88.88,27090000 +1978-03-09,87.84,88.49,87.34,87.89,21820000 +1978-03-08,87.36,88.08,86.97,87.84,22030000 +1978-03-07,86.9,87.63,86.55,87.36,19900000 +1978-03-06,87.45,87.52,86.48,86.9,17230000 +1978-03-03,87.32,87.98,86.83,87.45,20120000 +1978-03-02,87.19,87.81,86.69,87.32,20280000 +1978-03-01,87.04,87.63,86.45,87.19,21010000 +1978-02-28,87.72,87.76,86.58,87.04,19750000 +1978-02-27,88.49,88.97,87.49,87.72,19990000 +1978-02-24,87.66,88.87,87.66,88.49,22510000 +1978-02-23,87.56,87.92,86.83,87.64,18720000 +1978-02-22,87.59,88.15,87.19,87.56,18450000 +1978-02-21,87.96,88.19,87.09,87.59,21890000 +1978-02-17,88.08,88.7,87.55,87.96,18500000 +1978-02-16,88.77,88.77,87.64,88.08,21570000 +1978-02-15,89.04,89.4,88.3,88.83,20170000 +1978-02-14,89.86,89.89,88.7,89.04,20470000 +1978-02-13,90.08,90.3,89.38,89.86,16810000 +1978-02-10,90.3,90.69,89.56,90.08,19480000 +1978-02-09,90.83,90.96,89.84,90.3,17940000 +1978-02-08,90.33,91.32,90.09,90.83,21300000 +1978-02-07,89.5,90.53,89.38,90.33,14730000 +1978-02-06,89.62,89.85,88.95,89.5,11630000 +1978-02-03,90.13,90.32,89.19,89.62,19400000 +1978-02-02,89.93,90.91,89.54,90.13,23050000 +1978-02-01,89.25,90.24,88.82,89.93,22240000 +1978-01-31,89.34,89.92,88.61,89.25,19870000 +1978-01-30,88.58,89.67,88.26,89.34,17400000 +1978-01-27,88.58,89.1,88.02,88.58,17600000 +1978-01-26,89.39,89.79,88.31,88.58,19600000 +1978-01-25,89.25,89.94,88.83,89.39,18690000 +1978-01-24,89.24,89.8,88.67,89.25,18690000 +1978-01-23,89.89,90.08,88.81,89.24,19380000 +1978-01-20,90.09,90.27,89.41,89.89,7580000 +1978-01-19,90.56,91.04,89.74,90.09,21500000 +1978-01-18,89.88,90.86,89.59,90.56,21390000 +1978-01-17,89.43,90.31,89.05,89.88,19360000 +1978-01-16,89.69,90.11,88.88,89.43,18760000 +1978-01-13,89.82,90.47,89.26,89.69,18010000 +1978-01-12,89.74,90.6,89.25,89.82,22730000 +1978-01-11,90.17,90.7,89.23,89.74,22880000 +1978-01-10,90.64,91.29,89.72,90.17,25180000 +1978-01-09,91.48,91.48,89.97,90.64,27990000 +1978-01-06,92.66,92.66,91.05,91.62,26150000 +1978-01-05,93.52,94.53,92.51,92.74,23570000 +1978-01-04,93.82,94.1,92.57,93.52,24090000 +1978-01-03,95.1,95.15,93.49,93.82,17720000 +1977-12-30,94.94,95.67,94.44,95.1,23560000 +1977-12-29,94.75,95.43,94.1,94.94,23610000 +1977-12-28,94.69,95.2,93.99,94.75,19630000 +1977-12-27,94.69,95.21,94.09,94.69,16750000 +1977-12-23,93.8,94.99,93.75,94.69,20080000 +1977-12-22,93.05,94.37,93.05,93.8,28100000 +1977-12-21,92.5,93.58,92.2,93.05,24510000 +1977-12-20,92.69,93,91.76,92.5,23250000 +1977-12-19,93.4,93.71,92.42,92.69,21150000 +1977-12-16,93.55,94.04,92.93,93.4,20270000 +1977-12-15,94.03,94.42,93.23,93.55,21610000 +1977-12-14,93.56,94.26,92.94,94.03,22110000 +1977-12-13,93.63,94.04,92.9,93.56,19190000 +1977-12-12,93.65,94.29,93.18,93.63,18180000 +1977-12-09,92.96,94.11,92.77,93.65,19210000 +1977-12-08,92.78,93.76,92.51,92.96,20400000 +1977-12-07,92.83,93.39,92.15,92.78,21050000 +1977-12-06,94.09,94.09,92.44,92.83,23770000 +1977-12-05,94.67,95.01,93.91,94.27,19160000 +1977-12-02,94.69,95.25,94.08,94.67,21160000 +1977-12-01,94.83,95.45,94.23,94.69,24220000 +1977-11-30,94.55,95.17,93.78,94.83,22670000 +1977-11-29,96.04,96.09,94.28,94.55,22950000 +1977-11-28,96.69,96.98,95.67,96.04,21570000 +1977-11-25,96.49,97.11,95.86,96.69,17910000 +1977-11-23,96.09,96.94,95.6,96.49,29150000 +1977-11-22,95.25,96.52,95.05,96.09,28600000 +1977-11-21,95.33,95.77,94.59,95.25,20110000 +1977-11-18,95.16,95.88,94.7,95.33,23930000 +1977-11-17,95.45,95.88,94.59,95.16,25110000 +1977-11-16,95.93,96.47,95.06,95.45,24950000 +1977-11-15,95.32,96.47,94.73,95.93,27740000 +1977-11-14,95.98,96.38,94.91,95.32,23220000 +1977-11-11,95.1,96.49,95.1,95.98,35260000 +1977-11-10,92.98,95.1,92.69,94.71,31980000 +1977-11-09,92.46,93.27,92.01,92.98,21330000 +1977-11-08,92.29,92.97,91.82,92.46,19210000 +1977-11-07,91.58,92.7,91.32,92.29,21270000 +1977-11-04,90.76,91.97,90.72,91.58,21700000 +1977-11-03,90.71,91.18,90.01,90.76,18090000 +1977-11-02,91.35,91.59,90.29,90.71,20760000 +1977-11-01,92.19,92.19,91,91.35,17170000 +1977-10-31,92.61,93.03,91.85,92.34,17070000 +1977-10-28,92.34,93.13,91.88,92.61,18050000 +1977-10-27,92.1,93.15,91.54,92.34,21920000 +1977-10-26,91,92.46,90.44,92.1,24860000 +1977-10-25,91.63,91.71,90.2,91,23590000 +1977-10-24,92.32,92.62,91.36,91.63,19210000 +1977-10-21,92.67,92.99,91.8,92.32,20230000 +1977-10-20,92.38,93.12,91.6,92.67,20520000 +1977-10-19,93.46,93.71,92.07,92.38,22030000 +1977-10-18,93.47,94.19,93.01,93.46,20130000 +1977-10-17,93.56,94.03,92.87,93.47,17340000 +1977-10-14,93.46,94.19,92.88,93.56,20410000 +1977-10-13,94.04,94.32,92.89,93.46,23870000 +1977-10-12,94.82,94.82,93.4,94.04,22440000 +1977-10-11,95.75,95.97,94.73,94.93,17870000 +1977-10-10,95.97,96.15,95.32,95.75,10580000 +1977-10-07,96.05,96.51,95.48,95.97,16250000 +1977-10-06,95.68,96.45,95.3,96.05,18490000 +1977-10-05,96.03,96.36,95.2,95.68,18300000 +1977-10-04,96.74,97.27,95.73,96.03,20850000 +1977-10-03,96.53,97.11,95.86,96.74,19460000 +1977-09-30,95.85,96.85,95.66,96.53,21170000 +1977-09-29,95.31,96.28,95.09,95.85,21160000 +1977-09-28,95.24,95.91,94.73,95.31,17960000 +1977-09-27,95.38,96.01,94.76,95.24,19080000 +1977-09-26,95.04,95.68,94.44,95.38,18230000 +1977-09-23,95.09,95.69,94.6,95.04,18760000 +1977-09-22,95.1,95.61,94.51,95.09,16660000 +1977-09-21,95.89,96.52,94.83,95.1,22200000 +1977-09-20,95.85,96.29,95.23,95.89,19030000 +1977-09-19,96.48,96.59,95.46,95.85,16890000 +1977-09-16,96.8,97.3,96.05,96.48,18340000 +1977-09-15,96.55,97.31,96.15,96.8,18230000 +1977-09-14,96.09,96.88,95.66,96.55,17330000 +1977-09-13,96.03,96.56,95.48,96.09,14900000 +1977-09-12,96.37,96.64,95.37,96.03,18700000 +1977-09-09,97.1,97.1,95.97,96.37,18100000 +1977-09-08,98.01,98.43,97.01,97.28,18290000 +1977-09-07,97.71,98.38,97.33,98.01,18070000 +1977-09-06,97.45,98.13,96.93,97.71,16130000 +1977-09-02,96.83,97.76,96.51,97.45,15620000 +1977-09-01,96.77,97.54,96.35,96.83,18820000 +1977-08-31,96.38,97,95.59,96.77,19080000 +1977-08-30,96.92,97.55,96.04,96.38,18220000 +1977-08-29,96.06,97.25,95.93,96.92,15280000 +1977-08-26,96.15,96.42,95.04,96.06,18480000 +1977-08-25,97.18,97.18,95.81,96.15,19400000 +1977-08-24,97.62,97.99,96.77,97.23,18170000 +1977-08-23,97.79,98.52,97.18,97.62,20290000 +1977-08-22,97.51,98.29,96.84,97.79,17870000 +1977-08-19,97.68,98.29,96.78,97.51,20800000 +1977-08-18,97.74,98.69,97.21,97.68,21040000 +1977-08-17,97.73,98.4,97.12,97.74,20920000 +1977-08-16,98.18,98.6,97.35,97.73,19340000 +1977-08-15,97.88,98.56,97.14,98.18,15750000 +1977-08-12,98.16,98.51,97.31,97.88,16870000 +1977-08-11,98.92,99.45,97.9,98.16,21740000 +1977-08-10,98.05,99.06,97.67,98.92,18280000 +1977-08-09,97.99,98.63,97.48,98.05,19900000 +1977-08-08,98.76,98.86,97.68,97.99,15870000 +1977-08-05,98.74,99.44,98.31,98.76,19940000 +1977-08-04,98.37,99.19,97.79,98.74,18870000 +1977-08-03,98.5,98.86,97.53,98.37,21710000 +1977-08-02,99.12,99.27,98.14,98.5,17910000 +1977-08-01,98.85,99.84,98.46,99.12,17920000 +1977-07-29,98.79,99.21,97.71,98.85,20350000 +1977-07-28,98.64,99.36,97.78,98.79,26340000 +1977-07-27,100.27,100.29,98.31,98.64,26440000 +1977-07-26,100.85,100.92,99.72,100.27,21390000 +1977-07-25,101.67,101.85,100.46,100.85,20430000 +1977-07-22,101.59,102.28,101.02,101.67,23110000 +1977-07-21,101.73,102.19,100.85,101.59,26880000 +1977-07-20,101.79,102.57,101.14,101.73,29380000 +1977-07-19,100.95,102.17,100.68,101.79,31930000 +1977-07-18,100.18,101.4,99.94,100.95,29890000 +1977-07-15,99.59,100.68,99.28,100.18,29120000 +1977-07-13,99.45,99.99,98.83,99.59,23160000 +1977-07-12,99.55,100.01,98.81,99.45,22470000 +1977-07-11,99.79,100.16,98.9,99.55,19790000 +1977-07-08,99.93,100.62,99.37,99.79,23820000 +1977-07-07,99.58,100.3,99.12,99.93,21740000 +1977-07-06,100.09,100.41,99.2,99.58,21230000 +1977-07-05,100.1,100.72,99.62,100.09,16850000 +1977-07-01,100.48,100.76,99.63,100.1,18160000 +1977-06-30,100.11,100.88,99.68,100.48,19410000 +1977-06-29,100.14,100.49,99.3,100.11,19000000 +1977-06-28,100.98,101.36,99.87,100.14,22670000 +1977-06-27,101.19,101.63,100.47,100.98,19870000 +1977-06-24,100.62,101.65,100.41,101.19,27490000 +1977-06-23,100.46,101.1,99.88,100.62,24330000 +1977-06-22,100.74,101.07,99.9,100.46,25070000 +1977-06-21,100.42,101.41,100.16,100.74,29730000 +1977-06-20,99.97,100.76,99.56,100.42,22950000 +1977-06-17,99.85,100.47,99.34,99.97,21960000 +1977-06-16,99.61,100.33,98.91,99.85,24310000 +1977-06-15,99.86,100.31,99.12,99.61,22640000 +1977-06-14,98.76,100.12,98.76,99.86,25390000 +1977-06-13,98.46,99.21,98.06,98.74,20250000 +1977-06-10,98.14,98.86,97.68,98.46,20630000 +1977-06-09,98.2,98.62,97.51,98.14,19940000 +1977-06-08,97.73,98.75,97.49,98.2,22200000 +1977-06-07,97.23,98.01,96.6,97.73,21110000 +1977-06-06,97.69,98.26,96.89,97.23,18930000 +1977-06-03,96.74,98.12,96.55,97.69,20330000 +1977-06-02,96.93,97.53,96.23,96.74,18620000 +1977-06-01,96.12,97.27,95.89,96.93,18320000 +1977-05-31,96.27,96.75,95.52,96.12,17800000 +1977-05-27,97.01,97.26,95.92,96.27,15730000 +1977-05-26,96.77,97.47,96.2,97.01,18620000 +1977-05-25,97.67,98.14,96.5,96.77,20710000 +1977-05-24,98.15,98.25,97,97.67,20050000 +1977-05-23,99.35,99.35,97.88,98.15,18290000 +1977-05-20,99.88,100.12,98.91,99.45,18950000 +1977-05-19,100.3,100.74,99.49,99.88,21280000 +1977-05-18,99.77,100.93,99.58,100.3,27800000 +1977-05-17,99.47,100.11,98.76,99.77,22290000 +1977-05-16,99.03,99.98,98.79,99.47,21170000 +1977-05-13,98.73,99.52,98.37,99.03,19780000 +1977-05-12,98.78,99.25,97.91,98.73,21980000 +1977-05-11,99.47,99.77,98.4,98.78,18980000 +1977-05-10,99.18,100.09,98.82,99.47,21090000 +1977-05-09,99.49,99.78,98.66,99.18,15230000 +1977-05-06,100.11,100.2,98.95,99.49,19370000 +1977-05-05,99.96,100.79,99.28,100.11,23450000 +1977-05-04,99.43,100.56,98.9,99.96,23330000 +1977-05-03,98.93,99.96,98.72,99.43,21950000 +1977-05-02,98.44,99.26,97.97,98.93,17970000 +1977-04-29,98.2,98.87,97.58,98.44,18330000 +1977-04-28,97.96,98.77,97.47,98.2,18370000 +1977-04-27,97.11,98.47,96.9,97.96,20590000 +1977-04-26,97.15,97.94,96.53,97.11,20040000 +1977-04-25,98.32,98.32,96.54,97.15,20440000 +1977-04-22,99.67,99.67,98.08,98.44,20700000 +1977-04-21,100.4,101.2,99.35,99.75,22740000 +1977-04-20,100.07,100.98,99.49,100.4,25090000 +1977-04-19,100.54,100.81,99.58,100.07,19510000 +1977-04-18,101.04,101.36,100.09,100.54,17830000 +1977-04-15,101,101.63,100.35,101.04,20230000 +1977-04-14,100.42,102.07,100.42,101,30490000 +1977-04-13,100.15,100.72,99.02,100.16,21800000 +1977-04-12,98.97,100.58,98.97,100.15,23760000 +1977-04-11,98.35,99.37,98.08,98.88,17650000 +1977-04-07,97.91,98.65,97.48,98.35,17260000 +1977-04-06,98.01,98.61,97.45,97.91,16600000 +1977-04-05,98.23,98.6,97.43,98.01,18330000 +1977-04-04,99.21,99.5,97.98,98.23,16250000 +1977-04-01,98.42,99.57,98.38,99.21,17050000 +1977-03-31,98.54,99.14,97.8,98.42,16510000 +1977-03-30,99.69,99.99,98.18,98.54,18810000 +1977-03-29,99,100.12,98.95,99.69,17030000 +1977-03-28,99.06,99.54,98.35,99,16710000 +1977-03-25,99.7,100.05,98.71,99.06,16550000 +1977-03-24,100.2,100.6,99.26,99.7,19650000 +1977-03-23,101,101.42,99.88,100.2,19360000 +1977-03-22,101.31,101.58,100.35,101,18660000 +1977-03-21,101.86,102.13,100.92,101.31,18040000 +1977-03-18,102.08,102.61,101.39,101.86,19840000 +1977-03-17,102.17,102.58,101.28,102.08,20700000 +1977-03-16,101.98,102.7,101.52,102.17,22140000 +1977-03-15,101.42,102.61,101.34,101.98,23940000 +1977-03-14,100.65,101.75,100.24,101.42,19290000 +1977-03-11,100.67,101.37,100.14,100.65,18230000 +1977-03-10,100.1,100.96,99.49,100.67,18620000 +1977-03-09,100.87,100.89,99.63,100.1,19680000 +1977-03-08,101.25,101.85,100.48,100.87,19520000 +1977-03-07,101.2,101.77,100.64,101.25,17410000 +1977-03-04,100.88,101.67,100.52,101.2,18950000 +1977-03-03,100.39,101.28,100.01,100.88,17560000 +1977-03-02,100.66,101.24,99.97,100.39,18010000 +1977-03-01,99.82,101.03,99.65,100.66,19480000 +1977-02-28,99.48,100.06,98.91,99.82,16220000 +1977-02-25,99.6,100.02,98.82,99.48,17610000 +1977-02-24,100.19,100.42,99.18,99.6,19730000 +1977-02-23,100.49,100.95,99.78,100.19,18240000 +1977-02-22,100.49,101.22,99.94,100.49,17730000 +1977-02-18,100.92,101.13,99.95,100.49,18040000 +1977-02-17,101.5,101.76,100.43,100.92,19040000 +1977-02-16,101.04,102.22,100.68,101.5,23430000 +1977-02-15,100.74,101.67,100.35,101.04,21620000 +1977-02-14,100.22,101.06,99.51,100.74,19230000 +1977-02-11,100.82,101.18,99.74,100.22,20510000 +1977-02-10,100.73,101.51,100.16,100.82,22340000 +1977-02-09,101.6,101.88,100.12,100.73,23640000 +1977-02-08,101.89,102.65,101.16,101.6,24040000 +1977-02-07,101.88,102.43,101.25,101.89,20700000 +1977-02-04,101.85,102.71,101.3,101.88,23130000 +1977-02-03,102.36,102.57,101.28,101.85,23790000 +1977-02-02,102.54,103.32,101.89,102.36,25700000 +1977-02-01,102.03,103.06,101.57,102.54,23700000 +1977-01-31,101.93,102.44,100.91,102.03,22920000 +1977-01-28,101.79,102.61,101.08,101.93,22700000 +1977-01-27,102.34,102.81,101.27,101.79,24360000 +1977-01-26,103.13,103.48,101.84,102.34,27840000 +1977-01-25,103.25,104.08,102.42,103.13,26340000 +1977-01-24,103.32,104.06,102.5,103.25,22890000 +1977-01-21,102.97,103.91,102.35,103.32,23930000 +1977-01-20,103.85,104.45,102.5,102.97,26520000 +1977-01-19,103.32,104.38,102.83,103.85,27120000 +1977-01-18,103.73,104.29,102.71,103.32,24380000 +1977-01-17,104.01,104.37,103.04,103.73,21060000 +1977-01-14,104.2,104.71,103.37,104.01,24480000 +1977-01-13,103.4,104.6,103.21,104.2,24780000 +1977-01-12,104.12,104.18,102.75,103.4,22670000 +1977-01-11,105.2,105.6,103.76,104.12,24100000 +1977-01-10,105.01,105.75,104.46,105.2,20860000 +1977-01-07,105.02,105.59,104.3,105.01,21720000 +1977-01-06,104.76,105.86,104.4,105.02,23920000 +1977-01-05,105.7,106.07,104.33,104.76,25010000 +1977-01-04,107,107.31,105.4,105.7,22740000 +1977-01-03,107.46,107.97,106.42,107,21280000 +1976-12-31,106.88,107.82,106.55,107.46,19170000 +1976-12-30,106.34,107.41,105.97,106.88,23700000 +1976-12-29,106.77,107.17,105.83,106.34,21910000 +1976-12-28,106.06,107.36,105.9,106.77,25790000 +1976-12-27,104.84,106.31,104.58,106.06,20130000 +1976-12-23,104.71,105.49,104.09,104.84,24560000 +1976-12-22,104.22,105.59,104.03,104.71,26970000 +1976-12-21,103.65,104.66,102.99,104.22,24390000 +1976-12-20,104.26,104.63,103.21,103.65,20690000 +1976-12-17,104.8,105.6,103.89,104.26,23870000 +1976-12-16,105.14,105.53,104.07,104.8,23920000 +1976-12-15,105.07,105.89,104.33,105.14,28300000 +1976-12-14,104.63,105.44,103.8,105.07,25130000 +1976-12-13,104.7,105.33,103.94,104.63,24830000 +1976-12-10,104.51,105.36,103.9,104.7,25960000 +1976-12-09,104.08,105.27,103.71,104.51,31800000 +1976-12-08,103.49,104.4,102.94,104.08,24560000 +1976-12-07,103.56,104.4,102.96,103.49,26140000 +1976-12-06,102.76,104.15,102.53,103.56,24830000 +1976-12-03,102.12,103.31,101.75,102.76,22640000 +1976-12-02,102.49,103.3,101.7,102.12,23300000 +1976-12-01,102.1,103.03,101.62,102.49,21960000 +1976-11-30,102.44,102.72,101.46,102.1,17030000 +1976-11-29,103.15,103.46,102.07,102.44,18750000 +1976-11-26,102.41,103.51,102.13,103.15,15000000 +1976-11-24,101.96,102.85,101.41,102.41,20420000 +1976-11-23,102.59,102.9,101.5,101.96,19090000 +1976-11-22,101.92,103.15,101.63,102.59,20930000 +1976-11-19,101.89,102.77,101.17,101.92,24550000 +1976-11-18,100.61,102.22,100.49,101.89,24000000 +1976-11-17,100.04,101.32,99.64,100.61,19900000 +1976-11-16,99.9,101.12,99.44,100.04,21020000 +1976-11-15,99.24,100.16,98.53,99.9,16710000 +1976-11-12,99.64,99.95,98.51,99.24,15550000 +1976-11-11,98.81,99.89,98.35,99.64,13230000 +1976-11-10,99.32,99.98,98.18,98.81,18890000 +1976-11-09,99.6,100.21,98.38,99.32,19210000 +1976-11-08,100.62,100.62,99.1,99.6,16520000 +1976-11-05,102.41,102.7,100.48,100.82,20780000 +1976-11-04,101.92,103.16,101.4,102.41,21700000 +1976-11-03,102.49,102.49,100.73,101.92,19350000 +1976-11-01,102.9,103.78,102.19,103.1,18390000 +1976-10-29,101.61,103.1,101.15,102.9,17030000 +1976-10-28,101.76,102.5,101.12,101.61,16920000 +1976-10-27,101.06,102.12,100.61,101.76,15790000 +1976-10-26,100.07,101.5,99.91,101.06,15490000 +1976-10-25,99.96,100.6,99.21,100.07,13310000 +1976-10-22,100.77,100.93,99.24,99.96,17870000 +1976-10-21,101.74,102.32,100.49,100.77,17980000 +1976-10-20,101.45,102.23,100.81,101.74,15860000 +1976-10-19,101.47,102.04,100.42,101.45,16200000 +1976-10-18,100.88,101.99,100.62,101.47,15710000 +1976-10-15,100.85,101.5,100.02,100.88,16210000 +1976-10-14,102.12,102.14,100.28,100.85,18610000 +1976-10-13,100.81,102.44,100.54,102.12,21690000 +1976-10-12,101.64,102.19,100.38,100.81,18210000 +1976-10-11,102.48,102.48,100.98,101.64,14620000 +1976-10-08,103.54,104,102.24,102.56,16740000 +1976-10-07,102.97,103.9,102.16,103.54,19830000 +1976-10-06,103.23,103.72,102.05,102.97,20870000 +1976-10-05,104.03,104.25,102.51,103.23,19200000 +1976-10-04,104.17,104.62,103.42,104.03,12630000 +1976-10-01,105.24,105.75,103.6,104.17,20620000 +1976-09-30,105.37,105.84,104.57,105.24,14700000 +1976-09-29,105.92,106.45,104.83,105.37,18090000 +1976-09-28,107.27,107.54,105.61,105.92,20440000 +1976-09-27,106.8,107.7,106.35,107.27,17430000 +1976-09-24,106.92,107.36,106.03,106.8,17400000 +1976-09-23,107.46,107.96,106.4,106.92,24210000 +1976-09-22,107.83,108.72,106.92,107.46,32970000 +1976-09-21,106.32,108.13,106.09,107.83,30300000 +1976-09-20,106.27,107.2,105.74,106.32,21730000 +1976-09-17,105.34,106.81,105.14,106.27,28270000 +1976-09-16,104.25,105.59,103.84,105.34,19620000 +1976-09-15,103.94,104.7,103.28,104.25,17570000 +1976-09-14,104.29,104.5,103.31,103.94,15550000 +1976-09-13,104.65,105.29,103.88,104.29,16100000 +1976-09-10,104.4,105.03,103.79,104.65,16930000 +1976-09-09,104.94,105.12,103.91,104.4,16540000 +1976-09-08,105.03,105.73,104.34,104.94,19750000 +1976-09-07,104.3,105.31,103.93,105.03,16310000 +1976-09-03,103.92,104.63,103.36,104.3,13280000 +1976-09-02,104.06,104.84,103.47,103.92,18920000 +1976-09-01,102.91,104.3,102.6,104.06,18640000 +1976-08-31,102.07,103.38,101.94,102.91,15480000 +1976-08-30,101.48,102.51,101.22,102.07,11140000 +1976-08-27,101.32,101.9,100.55,101.48,12120000 +1976-08-26,102.03,102.59,101.01,101.32,15270000 +1976-08-25,101.27,102.41,100.43,102.03,17400000 +1976-08-24,101.96,102.65,100.98,101.27,16740000 +1976-08-23,102.37,102.49,101.04,101.96,15450000 +1976-08-20,103.31,103.31,101.96,102.37,14920000 +1976-08-19,104.56,104.74,103.01,103.39,17230000 +1976-08-18,104.8,105.41,104.12,104.56,17150000 +1976-08-17,104.43,105.25,103.98,104.8,18500000 +1976-08-16,104.25,104.99,103.74,104.43,16210000 +1976-08-13,104.22,104.79,103.61,104.25,13930000 +1976-08-12,104.06,104.64,103.38,104.22,15560000 +1976-08-11,104.41,105.24,103.73,104.06,18710000 +1976-08-10,103.49,104.71,103.21,104.41,16690000 +1976-08-09,103.79,104.02,103.01,103.49,11700000 +1976-08-06,103.85,104.25,103.1,103.79,13930000 +1976-08-05,104.43,104.76,103.48,103.85,15530000 +1976-08-04,104.14,105.18,103.72,104.43,20650000 +1976-08-03,103.19,104.49,102.79,104.14,18500000 +1976-08-02,103.44,103.98,102.64,103.19,13870000 +1976-07-30,102.93,103.88,102.47,103.44,14830000 +1976-07-29,103.05,103.59,102.36,102.93,13330000 +1976-07-28,103.48,103.58,102.31,103.05,16000000 +1976-07-27,104.07,104.51,103.13,103.48,15580000 +1976-07-26,104.06,104.69,103.46,104.07,13530000 +1976-07-23,103.93,104.71,103.49,104.06,15870000 +1976-07-22,103.82,104.42,103.15,103.93,15600000 +1976-07-21,103.72,104.56,103.21,103.82,18350000 +1976-07-20,104.29,104.57,103.05,103.72,18810000 +1976-07-19,104.68,105.32,103.84,104.29,18200000 +1976-07-16,105.2,105.27,103.87,104.68,20450000 +1976-07-15,105.95,106.25,104.76,105.2,20400000 +1976-07-14,105.67,106.61,105.05,105.95,23840000 +1976-07-13,105.9,106.78,105.15,105.67,27550000 +1976-07-12,104.98,106.3,104.74,105.9,23750000 +1976-07-09,103.98,105.41,103.8,104.98,23500000 +1976-07-08,103.83,104.75,103.44,103.98,21710000 +1976-07-07,103.54,104.23,102.8,103.83,18470000 +1976-07-06,104.11,104.67,103.19,103.54,16130000 +1976-07-02,103.59,104.53,103.13,104.11,16730000 +1976-07-01,104.28,104.98,103.14,103.59,21130000 +1976-06-30,103.86,105.07,103.52,104.28,23830000 +1976-06-29,103.43,104.33,102.95,103.86,19620000 +1976-06-28,103.72,104.35,102.97,103.43,17490000 +1976-06-25,103.79,104.54,103.17,103.72,17830000 +1976-06-24,103.25,104.37,102.9,103.79,19850000 +1976-06-23,103.47,103.9,102.4,103.25,17530000 +1976-06-22,104.28,104.82,103.16,103.47,21150000 +1976-06-21,103.76,104.73,103.18,104.28,18930000 +1976-06-18,103.61,104.8,103.06,103.76,25720000 +1976-06-17,102.01,104.12,101.97,103.61,27810000 +1976-06-16,101.46,102.65,100.96,102.01,21620000 +1976-06-15,101.95,102.39,100.84,101.46,18440000 +1976-06-14,101,102.51,101,101.95,21250000 +1976-06-11,99.56,101.22,99.38,100.92,19470000 +1976-06-10,98.74,99.98,98.55,99.56,16100000 +1976-06-09,98.8,99.49,98.23,98.74,14560000 +1976-06-08,98.63,99.71,98.32,98.8,16660000 +1976-06-07,99.15,99.39,97.97,98.63,14510000 +1976-06-04,100.13,100.27,98.79,99.15,15960000 +1976-06-03,100.22,101.1,99.68,100.13,18900000 +1976-06-02,99.85,100.69,99.26,100.22,16120000 +1976-06-01,100.18,100.74,99.36,99.85,13880000 +1976-05-28,99.38,100.64,99,100.18,16860000 +1976-05-27,99.34,99.77,98.26,99.38,15310000 +1976-05-26,99.49,100.14,98.65,99.34,16750000 +1976-05-25,99.44,100.02,98.48,99.49,18770000 +1976-05-24,101.07,101.07,99.11,99.44,16560000 +1976-05-21,102,102.34,100.81,101.26,18730000 +1976-05-20,101.18,102.53,100.69,102,22560000 +1976-05-19,101.26,102.01,100.55,101.18,18450000 +1976-05-18,101.09,102,100.72,101.26,17410000 +1976-05-17,101.34,101.71,100.41,101.09,14720000 +1976-05-14,102.16,102.23,100.82,101.34,16800000 +1976-05-13,102.77,103.03,101.73,102.16,16730000 +1976-05-12,102.95,103.55,102.14,102.77,18510000 +1976-05-11,103.1,103.99,102.39,102.95,23590000 +1976-05-10,101.88,103.51,101.76,103.1,22760000 +1976-05-07,101.16,102.27,100.77,101.88,17810000 +1976-05-06,100.88,101.7,100.31,101.16,16200000 +1976-05-05,101.46,101.92,100.45,100.88,14970000 +1976-05-04,100.92,101.93,100.29,101.46,17240000 +1976-05-03,101.64,101.73,100.14,100.92,15180000 +1976-04-30,102.13,102.65,101.16,101.64,14530000 +1976-04-29,102.13,102.97,101.45,102.13,17740000 +1976-04-28,101.86,102.46,100.91,102.13,15790000 +1976-04-27,102.43,103.18,101.51,101.86,17760000 +1976-04-26,102.29,102.8,101.36,102.43,15520000 +1976-04-23,102.98,103.21,101.7,102.29,17000000 +1976-04-22,103.32,104.04,102.52,102.98,20220000 +1976-04-21,102.87,104.03,102.3,103.32,26600000 +1976-04-20,101.44,103.32,101.42,102.87,23500000 +1976-04-19,100.67,101.83,100.32,101.44,16500000 +1976-04-15,100.31,101.18,99.73,100.67,15100000 +1976-04-14,101.05,101.77,99.98,100.31,18440000 +1976-04-13,100.2,101.39,99.64,101.05,15990000 +1976-04-12,100.35,101.3,99.57,100.2,16030000 +1976-04-09,101.28,101.74,99.87,100.35,19050000 +1976-04-08,102.21,102.38,100.53,101.28,20860000 +1976-04-07,103.36,103.85,101.92,102.21,20190000 +1976-04-06,103.51,104.63,102.93,103.36,24170000 +1976-04-05,102.32,104.13,102.32,103.51,21940000 +1976-04-02,102.24,102.76,101.23,102.25,17420000 +1976-04-01,102.77,103.24,101.5,102.24,17910000 +1976-03-31,102.01,103.08,101.6,102.77,17520000 +1976-03-30,102.41,103.36,101.25,102.01,17930000 +1976-03-29,102.85,103.36,101.99,102.41,16100000 +1976-03-26,102.85,103.65,102.2,102.85,18510000 +1976-03-25,103.42,104,102.19,102.85,22510000 +1976-03-24,102.51,104.39,102.51,103.42,32610000 +1976-03-23,100.71,102.54,100.32,102.24,22450000 +1976-03-22,100.58,101.53,100.14,100.71,19410000 +1976-03-19,100.45,101.23,99.7,100.58,18090000 +1976-03-18,100.86,101.37,99.73,100.45,20330000 +1976-03-17,100.92,102.01,100.28,100.86,26190000 +1976-03-16,99.8,101.25,99.38,100.92,22780000 +1976-03-15,100.86,100.9,99.24,99.8,19570000 +1976-03-12,101.89,102.46,100.49,100.86,26020000 +1976-03-11,100.94,102.41,100.62,101.89,27300000 +1976-03-10,100.58,101.8,99.98,100.94,24900000 +1976-03-09,100.19,101.9,99.95,100.58,31770000 +1976-03-08,99.11,100.71,98.93,100.19,25060000 +1976-03-05,98.92,99.88,98.23,99.11,23030000 +1976-03-04,99.98,100.4,98.49,98.92,24410000 +1976-03-03,100.58,100.97,99.23,99.98,25450000 +1976-03-02,100.02,101.26,99.61,100.58,25590000 +1976-03-01,99.71,100.64,98.67,100.02,22070000 +1976-02-27,100.11,100.53,98.6,99.71,26940000 +1976-02-26,101.69,102.36,99.74,100.11,34320000 +1976-02-25,102.03,102.71,100.69,101.69,34680000 +1976-02-24,101.61,102.92,101.03,102.03,34380000 +1976-02-23,102.1,102.54,100.69,101.61,31460000 +1976-02-20,101.41,103.07,101.18,102.1,44510000 +1976-02-19,99.94,101.92,99.94,101.41,39210000 +1976-02-18,99.05,100.43,98.5,99.85,29900000 +1976-02-17,99.67,100.25,98.56,99.05,25460000 +1976-02-13,100.25,100.66,99.01,99.67,23870000 +1976-02-12,100.77,101.55,99.82,100.25,28610000 +1976-02-11,100.47,101.8,100.1,100.77,32300000 +1976-02-10,99.62,100.96,99.11,100.47,27660000 +1976-02-09,99.46,100.66,98.77,99.62,25340000 +1976-02-06,100.39,100.53,98.64,99.46,27360000 +1976-02-05,101.91,102.3,100.06,100.39,33780000 +1976-02-04,101.18,102.57,100.7,101.91,38270000 +1976-02-03,100.87,101.97,99.58,101.18,34080000 +1976-02-02,100.86,101.39,99.74,100.87,24000000 +1976-01-30,100.11,101.99,99.94,100.86,38510000 +1976-01-29,98.53,100.54,98.32,100.11,29800000 +1976-01-28,99.07,99.64,97.66,98.53,27370000 +1976-01-27,99.68,100.52,98.28,99.07,32070000 +1976-01-26,99.21,100.75,98.92,99.68,34470000 +1976-01-23,98.04,99.88,97.68,99.21,33640000 +1976-01-22,98.24,98.79,97.07,98.04,27420000 +1976-01-21,98.86,99.24,97.12,98.24,34470000 +1976-01-20,98.32,99.44,97.43,98.86,36690000 +1976-01-19,97,98.84,96.36,98.32,29450000 +1976-01-16,96.61,97.73,95.84,97,25940000 +1976-01-15,97.13,98.34,96.15,96.61,38450000 +1976-01-14,95.57,97.47,94.91,97.13,30340000 +1976-01-13,96.33,97.39,95.11,95.57,34530000 +1976-01-12,94.95,96.76,94.38,96.33,30440000 +1976-01-09,94.58,95.71,94.05,94.95,26510000 +1976-01-08,93.95,95.47,93.41,94.58,29030000 +1976-01-07,93.53,95.15,92.91,93.95,33170000 +1976-01-06,92.58,94.18,92.37,93.53,31270000 +1976-01-05,90.9,92.84,90.85,92.58,21960000 +1976-01-02,90.19,91.18,89.81,90.9,10300000 +1975-12-31,89.77,90.75,89.17,90.19,16970000 +1975-12-30,90.13,90.55,89.2,89.77,16040000 +1975-12-29,90.25,91.09,89.63,90.13,17070000 +1975-12-26,89.46,90.45,89.25,90.25,10020000 +1975-12-24,88.73,89.84,88.73,89.46,11150000 +1975-12-23,88.14,89.23,87.64,88.73,17750000 +1975-12-22,88.8,89.13,87.74,88.14,15340000 +1975-12-19,89.43,89.81,88.39,88.8,17720000 +1975-12-18,89.15,90.09,88.62,89.43,18040000 +1975-12-17,88.93,89.8,88.46,89.15,16560000 +1975-12-16,88.09,89.49,87.78,88.93,18350000 +1975-12-15,87.83,88.64,87.32,88.09,13960000 +1975-12-12,87.8,88.22,87.05,87.83,13100000 +1975-12-11,88.08,88.79,87.41,87.8,15300000 +1975-12-10,87.3,88.39,86.91,88.08,15680000 +1975-12-09,87.07,87.8,86.16,87.3,16040000 +1975-12-08,86.82,87.75,86.15,87.07,14150000 +1975-12-05,87.84,88.38,86.54,86.82,14050000 +1975-12-04,87.6,88.39,86.68,87.84,16380000 +1975-12-03,88.83,88.83,87.08,87.6,21320000 +1975-12-02,90.67,90.81,89.08,89.33,17930000 +1975-12-01,91.24,91.9,90.33,90.67,16050000 +1975-11-28,90.94,91.74,90.44,91.24,12870000 +1975-11-26,90.71,91.58,90.17,90.94,18780000 +1975-11-25,89.7,91.1,89.66,90.71,17490000 +1975-11-24,89.53,90.17,88.65,89.7,13930000 +1975-11-21,89.64,90.23,88.79,89.53,14110000 +1975-11-20,89.98,90.68,89.09,89.64,16460000 +1975-11-19,91,91.28,89.47,89.98,16820000 +1975-11-18,91.46,92.3,90.6,91,20760000 +1975-11-17,90.97,91.99,90.5,91.46,17660000 +1975-11-14,91.04,91.59,90.19,90.97,16460000 +1975-11-13,91.19,92.33,90.56,91.04,25070000 +1975-11-12,89.87,91.63,89.8,91.19,23960000 +1975-11-11,89.34,90.47,89.04,89.87,14640000 +1975-11-10,89.33,89.98,88.35,89.34,14910000 +1975-11-07,89.55,90.18,88.67,89.33,15930000 +1975-11-06,89.15,90.15,88.16,89.55,18600000 +1975-11-05,88.51,90.08,88.32,89.15,17390000 +1975-11-04,88.09,89.03,87.63,88.51,11570000 +1975-11-03,89.04,89.21,87.78,88.09,11400000 +1975-10-31,89.31,89.8,88.35,89.04,12910000 +1975-10-30,89.39,90.2,88.7,89.31,15080000 +1975-10-29,90.51,90.61,88.89,89.39,16110000 +1975-10-28,89.73,91.01,89.4,90.51,17060000 +1975-10-27,89.83,90.4,88.85,89.73,13100000 +1975-10-24,91.24,91.52,89.46,89.83,18120000 +1975-10-23,90.71,91.75,90.09,91.24,17900000 +1975-10-22,90.56,91.38,89.77,90.71,16060000 +1975-10-21,89.82,91.43,89.79,90.56,20800000 +1975-10-20,88.86,90.14,88.43,89.82,13250000 +1975-10-17,89.37,89.87,88.08,88.86,15650000 +1975-10-16,89.23,90.73,88.9,89.37,18910000 +1975-10-15,89.28,90.07,88.5,89.23,14440000 +1975-10-14,89.46,90.8,88.81,89.28,19960000 +1975-10-13,88.21,89.67,87.73,89.46,12020000 +1975-10-10,88.37,89.17,87.44,88.21,14880000 +1975-10-09,87.94,89.42,87.6,88.37,17770000 +1975-10-08,86.77,88.46,86.34,87.94,17800000 +1975-10-07,86.88,87.32,85.56,86.77,13530000 +1975-10-06,85.98,87.64,85.98,86.88,15470000 +1975-10-03,83.88,86.21,83.88,85.95,16360000 +1975-10-02,82.93,84.33,82.82,83.82,14290000 +1975-10-01,83.87,85.45,82.57,82.93,14070000 +1975-09-30,85.01,85.01,83.44,83.87,12520000 +1975-09-29,86.19,86.38,84.74,85.03,10580000 +1975-09-26,85.64,86.86,85.13,86.19,12570000 +1975-09-25,85.74,86.41,84.79,85.64,12890000 +1975-09-24,85.03,86.7,85.03,85.74,16060000 +1975-09-23,85.07,85.51,83.8,84.94,12800000 +1975-09-22,85.88,86.7,84.7,85.07,14750000 +1975-09-19,84.26,86.39,84.26,85.88,20830000 +1975-09-18,82.37,84.34,82.23,84.06,14560000 +1975-09-17,82.09,82.93,81.57,82.37,12190000 +1975-09-16,82.88,83.43,81.79,82.09,13090000 +1975-09-15,83.3,83.49,82.29,82.88,8670000 +1975-09-12,83.45,84.47,82.84,83.3,12230000 +1975-09-11,83.79,84.3,82.88,83.45,11100000 +1975-09-10,84.59,84.59,83,83.79,14780000 +1975-09-09,85.89,86.73,84.37,84.6,15790000 +1975-09-08,85.62,86.31,84.89,85.89,11500000 +1975-09-05,86.2,86.49,85.19,85.62,11680000 +1975-09-04,86.03,86.91,85.29,86.2,12810000 +1975-09-03,85.48,86.38,84.62,86.03,12260000 +1975-09-02,86.88,87.42,85.21,85.48,11460000 +1975-08-29,86.4,87.73,86.1,86.88,15480000 +1975-08-28,84.68,86.64,84.68,86.4,14530000 +1975-08-27,83.96,84.79,83.35,84.43,11100000 +1975-08-26,85.06,85.4,83.65,83.96,11350000 +1975-08-25,84.28,85.58,84.06,85.06,11250000 +1975-08-22,83.07,84.61,82.79,84.28,13050000 +1975-08-21,83.22,84.15,82.21,83.07,16610000 +1975-08-20,84.78,84.78,82.76,83.22,18630000 +1975-08-19,86.2,86.47,84.66,84.95,14990000 +1975-08-18,86.36,87.21,85.76,86.2,10810000 +1975-08-15,85.6,86.76,85.33,86.36,10610000 +1975-08-14,85.97,86.34,85.02,85.6,12460000 +1975-08-13,87.12,87.41,85.61,85.97,12000000 +1975-08-12,86.55,88.17,86.49,87.12,14510000 +1975-08-11,86.02,86.89,85.34,86.55,12350000 +1975-08-08,86.3,87,85.52,86.02,11660000 +1975-08-07,86.25,87.24,85.69,86.3,12390000 +1975-08-06,86.23,87.04,85.34,86.25,16280000 +1975-08-05,87.15,87.81,85.89,86.23,15470000 +1975-08-04,87.99,88.17,86.68,87.15,12620000 +1975-08-01,88.75,89.04,87.46,87.99,13320000 +1975-07-31,88.83,90.07,88.31,88.75,14540000 +1975-07-30,88.19,89.49,87.68,88.83,16150000 +1975-07-29,88.69,89.91,87.71,88.19,19000000 +1975-07-28,89.29,89.68,88.02,88.69,14850000 +1975-07-25,90.07,90.72,88.72,89.29,15110000 +1975-07-24,90.18,90.95,88.9,90.07,20550000 +1975-07-23,91.45,92.15,89.83,90.18,20150000 +1975-07-22,92.44,92.49,90.63,91.45,20660000 +1975-07-21,93.2,93.93,92.03,92.44,16690000 +1975-07-18,93.63,93.96,92.39,93.2,16870000 +1975-07-17,94.61,95.03,92.99,93.63,21420000 +1975-07-16,95.61,96.37,94.2,94.61,25250000 +1975-07-15,95.19,96.58,94.71,95.61,28340000 +1975-07-14,94.66,95.76,94.04,95.19,21900000 +1975-07-11,94.81,95.69,93.83,94.66,22210000 +1975-07-10,94.8,96.19,94.25,94.81,28880000 +1975-07-09,93.39,95.22,93.38,94.8,26350000 +1975-07-08,93.54,94.03,92.51,93.39,18990000 +1975-07-07,94.36,94.82,93.16,93.54,15850000 +1975-07-03,94.18,95.04,93.49,94.36,19000000 +1975-07-02,94.85,94.91,93.37,94.18,18530000 +1975-07-01,95.19,95.73,94.13,94.85,20390000 +1975-06-30,94.81,95.85,94.3,95.19,19430000 +1975-06-27,94.81,95.66,94.1,94.81,18820000 +1975-06-26,94.62,95.72,93.88,94.81,24560000 +1975-06-25,94.19,95.29,93.53,94.62,21610000 +1975-06-24,93.62,95.23,93.31,94.19,26620000 +1975-06-23,92.61,93.98,91.81,93.62,20720000 +1975-06-20,92.02,93.75,91.83,92.61,26260000 +1975-06-19,90.39,92.37,90.12,92.02,21450000 +1975-06-18,90.58,91.07,89.6,90.39,15590000 +1975-06-17,91.46,92.22,90.17,90.58,19440000 +1975-06-16,90.52,91.85,90.12,91.46,16660000 +1975-06-13,90.08,91.06,89.3,90.52,16300000 +1975-06-12,90.55,91.36,89.64,90.08,15970000 +1975-06-11,90.44,91.67,90,90.55,18230000 +1975-06-10,91.21,91.21,89.46,90.44,21130000 +1975-06-09,92.48,92.87,90.91,91.21,20670000 +1975-06-06,92.69,93.6,91.75,92.48,22230000 +1975-06-05,92.6,93.16,91.41,92.69,21610000 +1975-06-04,92.89,93.61,91.82,92.6,24900000 +1975-06-03,92.58,93.76,91.88,92.89,26560000 +1975-06-02,91.32,93.41,91.32,92.58,28240000 +1975-05-30,89.87,91.62,89.87,91.15,22670000 +1975-05-29,89.71,90.59,88.83,89.68,18570000 +1975-05-28,90.34,91.14,89.07,89.71,21850000 +1975-05-27,90.58,91.29,89.6,90.34,17050000 +1975-05-23,89.39,91.02,89.3,90.58,17870000 +1975-05-22,89.06,90.3,88.35,89.39,17610000 +1975-05-21,90.07,90.25,88.47,89.06,17640000 +1975-05-20,90.53,91.45,89.58,90.07,18310000 +1975-05-19,90.43,91.07,88.98,90.53,17870000 +1975-05-16,91.41,91.59,89.74,90.43,16630000 +1975-05-15,92.27,93.51,90.94,91.41,27690000 +1975-05-14,91.58,93.23,91.17,92.27,29050000 +1975-05-13,90.61,92.26,89.99,91.58,24950000 +1975-05-12,90.53,91.67,89.91,90.61,22410000 +1975-05-09,89.56,91.24,89.33,90.53,28440000 +1975-05-08,89.08,90.13,88.23,89.56,22980000 +1975-05-07,88.64,89.75,87.6,89.08,22250000 +1975-05-06,90.08,90.86,88.15,88.64,25410000 +1975-05-05,89.22,90.82,88.26,90.08,22370000 +1975-05-02,88.1,89.98,87.91,89.22,25210000 +1975-05-01,87.3,89.1,86.94,88.1,20660000 +1975-04-30,85.64,87.61,85,87.3,18060000 +1975-04-29,86.23,86.79,85.04,85.64,17740000 +1975-04-28,86.62,87.33,85.54,86.23,17850000 +1975-04-25,86.04,87.5,85.62,86.62,20260000 +1975-04-24,86.12,86.92,85,86.04,19050000 +1975-04-23,87.09,87.42,85.65,86.12,20040000 +1975-04-22,87.23,88.64,86.58,87.09,26120000 +1975-04-21,86.3,87.99,85.92,87.23,23960000 +1975-04-18,87.25,87.59,85.53,86.3,26610000 +1975-04-17,86.6,88.79,86.43,87.25,32650000 +1975-04-16,86.3,87.1,84.93,86.6,22970000 +1975-04-15,85.6,87.24,85.03,86.3,29620000 +1975-04-14,84.18,86.12,83.98,85.6,26800000 +1975-04-11,83.77,84.68,82.93,84.18,20160000 +1975-04-10,82.84,84.7,82.68,83.77,24990000 +1975-04-09,80.99,83.22,80.91,82.84,18120000 +1975-04-08,80.35,81.65,80.13,80.99,14320000 +1975-04-07,80.88,81.11,79.66,80.35,13860000 +1975-04-04,81.51,81.9,80.29,80.88,14170000 +1975-04-03,82.43,82.84,80.88,81.51,13920000 +1975-04-02,82.64,83.57,81.8,82.43,15600000 +1975-04-01,83.36,83.59,81.98,82.64,14480000 +1975-03-31,83.85,84.62,82.84,83.36,16270000 +1975-03-27,83.59,84.88,83.04,83.85,18300000 +1975-03-26,82.16,84.24,82.16,83.59,18580000 +1975-03-25,81.42,82.67,80.08,82.06,18500000 +1975-03-24,82.39,82.39,80.6,81.42,17810000 +1975-03-21,83.61,84.11,82.52,83.39,15940000 +1975-03-20,84.34,85.3,83.02,83.61,20960000 +1975-03-19,85.13,85.17,83.43,84.34,19030000 +1975-03-18,86.01,87.08,84.75,85.13,29180000 +1975-03-17,84.76,86.52,84.39,86.01,26780000 +1975-03-14,83.74,85.43,83.5,84.76,24840000 +1975-03-13,83.59,84.26,82.52,83.74,18620000 +1975-03-12,84.36,84.73,82.87,83.59,21560000 +1975-03-11,84.95,85.89,83.8,84.36,31280000 +1975-03-10,84.3,85.47,83.43,84.95,25890000 +1975-03-07,83.69,85.14,83.25,84.3,25930000 +1975-03-06,83.9,84.17,81.94,83.69,21780000 +1975-03-05,83.56,84.71,82.16,83.9,24120000 +1975-03-04,83.03,85.43,82.85,83.56,34140000 +1975-03-03,81.59,83.46,81.32,83.03,24100000 +1975-02-28,80.77,82.02,80.07,81.59,17560000 +1975-02-27,80.37,81.64,80.06,80.77,16430000 +1975-02-26,79.53,80.89,78.91,80.37,18790000 +1975-02-25,81.09,81.09,79.05,79.53,20910000 +1975-02-24,82.62,82.71,80.87,81.44,19150000 +1975-02-21,82.21,83.56,81.72,82.62,24440000 +1975-02-20,81.44,82.78,80.82,82.21,22260000 +1975-02-19,80.93,81.94,79.83,81.44,21930000 +1975-02-18,81.5,82.45,80.16,80.93,23990000 +1975-02-14,81.01,82.33,80.13,81.5,23290000 +1975-02-13,79.98,82.53,79.98,81.01,35160000 +1975-02-12,78.58,80.21,77.94,79.92,19790000 +1975-02-11,78.36,79.07,77.38,78.58,16470000 +1975-02-10,78.63,79.4,77.77,78.36,16120000 +1975-02-07,78.56,79.12,77,78.63,19060000 +1975-02-06,78.95,80.72,78.09,78.56,32020000 +1975-02-05,77.61,79.4,76.81,78.95,25830000 +1975-02-04,77.82,78.37,76,77.61,25040000 +1975-02-03,76.98,78.55,76.36,77.82,25400000 +1975-01-31,76.21,77.72,75.41,76.98,24640000 +1975-01-30,77.26,78.69,75.82,76.21,29740000 +1975-01-29,76.03,78.03,75.23,77.26,27410000 +1975-01-28,75.37,77.59,75.36,76.03,31760000 +1975-01-27,73.76,76.03,73.76,75.37,32130000 +1975-01-24,72.07,73.57,71.55,72.98,20670000 +1975-01-23,71.74,73.11,71.09,72.07,17960000 +1975-01-22,70.7,71.97,69.86,71.74,15330000 +1975-01-21,71.08,72.04,70.25,70.7,14780000 +1975-01-20,70.96,71.46,69.8,71.08,13450000 +1975-01-17,72.05,72.36,70.56,70.96,14260000 +1975-01-16,72.14,72.93,71.26,72.05,17110000 +1975-01-15,71.68,72.77,70.45,72.14,16580000 +1975-01-14,72.31,72.7,71.02,71.68,16610000 +1975-01-13,72.61,73.81,71.83,72.31,19780000 +1975-01-10,71.6,73.75,71.6,72.61,25890000 +1975-01-09,70.04,71.42,69.04,71.17,16340000 +1975-01-08,71.02,71.53,69.65,70.04,15600000 +1975-01-07,71.07,71.75,69.92,71.02,14890000 +1975-01-06,70.71,72.24,70.33,71.07,17550000 +1975-01-03,70.23,71.64,69.29,70.71,15270000 +1975-01-02,68.65,70.92,68.65,70.23,14800000 +1974-12-31,67.16,69.04,67.15,68.56,20970000 +1974-12-30,67.14,67.65,66.23,67.16,18520000 +1974-12-27,67.44,67.99,66.49,67.14,13060000 +1974-12-26,66.88,68.19,66.62,67.44,11810000 +1974-12-24,65.96,67.25,65.86,66.88,9540000 +1974-12-23,66.91,67.18,65.34,65.96,18040000 +1974-12-20,67.65,67.93,66.36,66.91,15840000 +1974-12-19,67.9,68.62,66.93,67.65,15900000 +1974-12-18,67.58,69.01,67.3,67.9,18050000 +1974-12-17,66.46,67.92,65.86,67.58,16880000 +1974-12-16,67.07,67.74,66.02,66.46,15370000 +1974-12-13,67.45,68.15,66.32,67.07,14000000 +1974-12-12,67.67,68.61,66.56,67.45,15390000 +1974-12-11,67.28,69.03,66.83,67.67,15700000 +1974-12-10,65.88,68.17,65.88,67.28,15690000 +1974-12-09,65.01,66.29,64.13,65.6,14660000 +1974-12-06,66.13,66.2,64.4,65.01,15500000 +1974-12-05,67.41,68,65.9,66.13,12890000 +1974-12-04,67.17,68.32,66.61,67.41,12580000 +1974-12-03,68.11,68.13,66.62,67.17,13620000 +1974-12-02,69.8,69.8,67.81,68.11,11140000 +1974-11-29,69.94,70.49,69.18,69.97,7400000 +1974-11-27,69.47,71.31,69.17,69.94,14810000 +1974-11-26,68.83,70.36,68.19,69.47,13600000 +1974-11-25,68.9,69.68,67.79,68.83,11300000 +1974-11-22,68.24,70,68.24,68.9,13020000 +1974-11-21,67.9,68.94,66.85,68.18,13820000 +1974-11-20,68.2,69.25,67.36,67.9,12430000 +1974-11-19,69.27,69.71,67.66,68.2,15720000 +1974-11-18,71.1,71.1,68.95,69.27,15230000 +1974-11-15,73.06,73.27,71.41,71.91,12480000 +1974-11-14,73.35,74.54,72.53,73.06,13540000 +1974-11-13,73.67,74.25,72.32,73.35,16040000 +1974-11-12,75.15,75.59,73.34,73.67,15040000 +1974-11-11,74.91,75.7,74.04,75.15,13220000 +1974-11-08,75.21,76,74.01,74.91,15890000 +1974-11-07,74.75,76.3,73.85,75.21,17150000 +1974-11-06,75.11,77.41,74.23,74.75,23930000 +1974-11-05,73.08,75.36,72.49,75.11,15960000 +1974-11-04,73.8,73.8,71.93,73.08,12740000 +1974-11-01,73.9,74.85,72.68,73.88,13470000 +1974-10-31,74.31,75.9,73.15,73.9,18840000 +1974-10-30,72.83,75.45,72.4,74.31,20130000 +1974-10-29,70.49,73.19,70.49,72.83,15610000 +1974-10-28,70.12,70.67,68.89,70.09,10540000 +1974-10-25,70.22,71.59,69.46,70.12,12650000 +1974-10-24,70.98,70.98,68.8,70.22,14910000 +1974-10-23,72.81,72.81,70.4,71.03,14200000 +1974-10-22,73.5,75.09,72.55,73.13,18930000 +1974-10-21,72.28,73.92,71.24,73.5,14500000 +1974-10-18,71.2,73.34,71.2,72.28,16460000 +1974-10-17,70.33,72,69.41,71.17,14470000 +1974-10-16,71.44,71.98,69.54,70.33,14790000 +1974-10-15,72.74,73.35,70.61,71.44,17390000 +1974-10-14,71.17,74.43,71.17,72.74,19770000 +1974-10-11,69.79,71.99,68.8,71.14,20090000 +1974-10-10,68.3,71.48,68.3,69.79,26360000 +1974-10-09,64.84,68.15,63.74,67.82,18820000 +1974-10-08,64.95,66.07,63.95,64.84,15460000 +1974-10-07,62.78,65.4,62.78,64.95,15000000 +1974-10-04,62.28,63.23,60.96,62.34,15910000 +1974-10-03,63.38,63.48,61.66,62.28,13150000 +1974-10-02,63.39,64.62,62.74,63.38,12230000 +1974-10-01,63.54,64.37,61.75,63.39,16890000 +1974-09-30,64.85,64.85,62.52,63.54,15000000 +1974-09-27,66.46,67.09,64.58,64.94,12320000 +1974-09-26,67.4,67.4,65.79,66.46,9060000 +1974-09-25,68.02,69.77,66.86,67.57,17620000 +1974-09-24,69.03,69.03,67.42,68.02,9840000 +1974-09-23,70.14,71.02,68.79,69.42,12130000 +1974-09-20,70.09,71.12,68.62,70.14,16250000 +1974-09-19,68.36,70.76,68.36,70.09,17000000 +1974-09-18,67.38,68.14,65.92,67.72,11760000 +1974-09-17,66.45,68.84,66.45,67.38,13730000 +1974-09-16,65.2,66.92,64.15,66.26,18370000 +1974-09-13,66.71,66.91,64.74,65.2,16070000 +1974-09-12,68.54,68.54,66.22,66.71,16920000 +1974-09-11,69.24,70,68.22,68.55,11820000 +1974-09-10,69.72,70.47,68.55,69.24,11980000 +1974-09-09,71.35,71.35,69.38,69.72,11160000 +1974-09-06,70.87,72.42,70.08,71.42,15130000 +1974-09-05,68.69,71.3,68.65,70.87,14210000 +1974-09-04,69.85,69.85,67.64,68.69,16930000 +1974-09-03,72.15,73.01,70.28,70.52,12750000 +1974-08-30,70.22,72.68,70.22,72.15,16230000 +1974-08-29,70.76,71.22,69.37,69.99,13690000 +1974-08-28,70.94,72.17,70.13,70.76,16670000 +1974-08-27,72.16,72.5,70.5,70.94,12970000 +1974-08-26,71.55,73.17,70.42,72.16,14630000 +1974-08-23,72.8,73.71,70.75,71.55,13590000 +1974-08-22,73.51,74.05,71.61,72.8,15690000 +1974-08-21,74.95,75.5,73.16,73.51,11650000 +1974-08-20,74.57,76.11,73.82,74.95,13820000 +1974-08-19,75.65,75.65,73.78,74.57,11670000 +1974-08-16,76.3,77.02,75.29,75.67,10510000 +1974-08-15,76.73,77.52,75.19,76.3,11130000 +1974-08-14,76.73,76.73,76.73,76.73,11750000 +1974-08-13,79.75,79.95,77.83,78.49,10140000 +1974-08-12,80.86,81.26,79.3,79.75,7780000 +1974-08-09,81.57,81.88,80.11,80.86,10160000 +1974-08-08,82.65,83.53,80.86,81.57,16060000 +1974-08-07,80.52,82.93,80.13,82.65,13380000 +1974-08-06,79.78,82.65,79.78,80.52,15770000 +1974-08-05,78.59,80.31,78.03,79.29,11230000 +1974-08-02,78.75,79.39,77.84,78.59,10110000 +1974-08-01,79.31,80.02,77.97,78.75,11470000 +1974-07-31,80.5,80.82,78.96,79.31,10960000 +1974-07-30,80.94,81.52,79.58,80.5,11360000 +1974-07-29,82.02,82.02,80.22,80.94,11560000 +1974-07-26,83.98,84.17,82,82.4,10420000 +1974-07-25,84.99,85.67,83.13,83.98,13310000 +1974-07-24,84.65,85.64,83.61,84.99,12870000 +1974-07-23,83.81,85.63,83.67,84.65,12910000 +1974-07-22,83.54,84.44,82.59,83.81,9290000 +1974-07-19,83.78,84.67,82.87,83.54,11080000 +1974-07-18,83.7,85.39,83.13,83.78,13980000 +1974-07-17,82.81,84.13,81.7,83.7,11320000 +1974-07-16,83.78,83.85,82.14,82.81,9920000 +1974-07-15,83.15,84.89,82.65,83.78,13560000 +1974-07-12,80.97,83.65,80.97,83.15,17770000 +1974-07-11,79.99,81.08,79.08,79.89,14640000 +1974-07-10,81.48,82.22,79.74,79.99,13490000 +1974-07-09,81.09,82.5,80.35,81.48,15580000 +1974-07-08,83.13,83.13,80.48,81.09,15510000 +1974-07-05,84.25,84.45,83.17,83.66,7400000 +1974-07-03,84.3,85.15,83.46,84.25,13430000 +1974-07-02,86.02,86.26,83.98,84.3,13460000 +1974-07-01,86,86.89,85.32,86.02,10270000 +1974-06-28,86.31,86.78,85.13,86,12010000 +1974-06-27,87.61,87.61,85.88,86.31,12650000 +1974-06-26,88.98,89.12,87.3,87.61,11410000 +1974-06-25,87.69,89.48,87.67,88.98,11920000 +1974-06-24,87.46,88.38,86.7,87.69,9960000 +1974-06-21,88.21,88.31,86.77,87.46,11830000 +1974-06-20,88.84,89.35,87.8,88.21,11990000 +1974-06-19,89.45,89.8,88.39,88.84,10550000 +1974-06-18,90.04,90.53,88.92,89.45,10110000 +1974-06-17,91.3,91.34,89.63,90.04,9680000 +1974-06-14,92.23,92.23,90.73,91.3,10030000 +1974-06-13,92.06,93.33,91.48,92.34,11540000 +1974-06-12,92.28,92.61,90.89,92.06,11150000 +1974-06-11,93.1,93.57,91.76,92.28,12380000 +1974-06-10,92.55,93.64,91.53,93.1,13540000 +1974-06-07,91.96,93.76,91.74,92.55,19020000 +1974-06-06,90.31,92.31,89.71,91.96,13360000 +1974-06-05,90.14,91.42,89.04,90.31,13680000 +1974-06-04,89.1,91.13,89.09,90.14,16040000 +1974-06-03,87.28,89.4,86.78,89.1,12490000 +1974-05-31,87.43,88.02,86.19,87.28,10810000 +1974-05-30,86.89,88.09,85.87,87.43,13580000 +1974-05-29,88.37,88.84,86.52,86.89,12300000 +1974-05-28,88.58,89.37,87.69,88.37,10580000 +1974-05-24,87.29,89.27,87.2,88.58,13740000 +1974-05-23,87.09,87.98,86.12,87.29,14770000 +1974-05-22,87.91,88.79,86.72,87.09,15450000 +1974-05-21,87.86,88.98,87.19,87.91,12190000 +1974-05-20,88.21,89.09,87.19,87.86,10550000 +1974-05-17,89.53,89.53,87.67,88.21,13870000 +1974-05-16,90.45,91.31,89.36,89.72,12090000 +1974-05-15,90.69,91.22,89.65,90.45,11240000 +1974-05-14,90.66,91.68,90.05,90.69,10880000 +1974-05-13,91.47,91.72,89.91,90.66,11290000 +1974-05-10,92.96,93.57,91.03,91.47,15270000 +1974-05-09,91.64,93.49,91.27,92.96,14710000 +1974-05-08,91.46,92.34,90.71,91.64,11850000 +1974-05-07,91.12,92.36,90.69,91.46,10710000 +1974-05-06,91.29,91.6,90.13,91.12,9450000 +1974-05-03,92.09,92.27,90.59,91.29,11080000 +1974-05-02,92.22,93.59,91.46,92.09,13620000 +1974-05-01,90.31,93.03,89.82,92.22,15120000 +1974-04-30,90,91.09,89.38,90.31,10980000 +1974-04-29,90.18,90.78,89.02,90,10170000 +1974-04-26,89.57,91.1,89.06,90.18,13250000 +1974-04-25,90.3,90.53,88.62,89.57,15870000 +1974-04-24,91.81,91.82,89.91,90.3,16010000 +1974-04-23,93.38,93.51,91.53,91.81,14110000 +1974-04-22,93.75,94.12,92.71,93.38,10520000 +1974-04-19,94.77,94.77,93.2,93.75,10710000 +1974-04-18,94.36,95.42,93.75,94.78,12470000 +1974-04-17,93.66,95.04,93.12,94.36,14020000 +1974-04-16,92.05,94.06,92.05,93.66,14530000 +1974-04-15,92.12,92.94,91.49,92.05,10130000 +1974-04-11,92.4,92.92,91.55,92.12,9970000 +1974-04-10,92.61,93.52,91.89,92.4,11160000 +1974-04-09,92.03,93.28,91.61,92.61,11330000 +1974-04-08,93,93,91.5,92.03,10740000 +1974-04-05,94.24,94.24,92.55,93.01,11670000 +1974-04-04,94.33,95.14,93.55,94.33,11650000 +1974-04-03,93.35,94.7,92.94,94.33,11500000 +1974-04-02,93.25,94.15,92.59,93.35,12010000 +1974-04-01,93.98,94.68,92.82,93.25,11470000 +1974-03-29,94.82,95.12,93.44,93.98,12150000 +1974-03-28,96.2,96.2,94.36,94.82,14940000 +1974-03-27,97.95,98.26,96.32,96.59,11690000 +1974-03-26,97.64,98.66,97.11,97.95,11840000 +1974-03-25,97.27,98.02,95.69,97.64,10540000 +1974-03-22,97.34,98.04,96.35,97.27,11930000 +1974-03-21,97.57,98.59,96.82,97.34,12950000 +1974-03-20,97.23,98.22,96.67,97.57,12960000 +1974-03-19,98.05,98.2,96.63,97.23,12800000 +1974-03-18,99.28,99.71,97.62,98.05,14010000 +1974-03-15,99.65,99.99,98.22,99.28,14500000 +1974-03-14,99.74,101.05,98.8,99.65,19770000 +1974-03-13,99.15,100.73,98.72,99.74,16820000 +1974-03-12,98.88,100.02,97.97,99.15,17250000 +1974-03-11,97.78,99.4,96.38,98.88,18470000 +1974-03-08,96.94,98.28,95.77,97.78,16210000 +1974-03-07,97.98,98.2,96.37,96.94,14500000 +1974-03-06,97.32,98.57,96.54,97.98,19140000 +1974-03-05,95.98,98.17,95.98,97.32,21980000 +1974-03-04,95.53,95.95,94.19,95.53,12270000 +1974-03-01,96.22,96.4,94.81,95.53,12880000 +1974-02-28,96.4,96.98,95.2,96.22,13680000 +1974-02-27,96,97.43,95.49,96.4,18730000 +1974-02-26,95.03,96.38,94.2,96,15860000 +1974-02-25,95.39,95.96,94.24,95.03,12900000 +1974-02-22,94.71,96.19,94.08,95.39,16360000 +1974-02-21,93.44,95.19,93.2,94.71,13930000 +1974-02-20,92.12,93.92,91.34,93.44,11670000 +1974-02-19,92.27,94.44,91.68,92.12,15940000 +1974-02-15,90.95,92.98,90.62,92.27,12640000 +1974-02-14,90.98,91.89,90.17,90.95,12230000 +1974-02-13,90.94,92.13,90.37,90.98,10990000 +1974-02-12,90.66,91.6,89.53,90.94,12920000 +1974-02-11,92.33,92.54,90.26,90.66,12930000 +1974-02-08,93.3,93.79,91.87,92.33,12990000 +1974-02-07,93.26,94.09,92.43,93.3,11750000 +1974-02-06,93,94.09,92.37,93.26,11610000 +1974-02-05,93.29,94.17,92.26,93,12820000 +1974-02-04,94.89,94.89,92.74,93.29,14380000 +1974-02-01,96.57,96.63,94.66,95.32,12480000 +1974-01-31,97.06,98.06,96.11,96.57,14020000 +1974-01-30,96.02,97.9,96.02,97.06,16790000 +1974-01-29,96.09,96.81,94.97,96.01,12850000 +1974-01-28,96.63,97.32,95.37,96.09,13410000 +1974-01-25,96.82,97.64,95.68,96.63,14860000 +1974-01-24,97.07,97.75,95.49,96.82,15980000 +1974-01-23,96.55,98.11,95.88,97.07,16890000 +1974-01-22,95.4,97.41,94.92,96.55,17330000 +1974-01-21,95.56,95.96,93.23,95.4,15630000 +1974-01-18,97.3,97.63,95,95.56,16470000 +1974-01-17,95.67,98.35,95.67,97.3,21040000 +1974-01-16,94.23,96.2,93.78,95.67,14930000 +1974-01-15,93.42,95.26,92.84,94.23,13250000 +1974-01-14,93.66,95.24,92.35,93.42,14610000 +1974-01-11,92.39,94.57,91.75,93.66,15140000 +1974-01-10,93.42,94.63,91.62,92.39,16120000 +1974-01-09,95.4,95.4,92.63,93.42,18070000 +1974-01-08,98.07,98.26,95.58,96.12,18080000 +1974-01-07,98.9,99.31,96.86,98.07,19070000 +1974-01-04,99.8,100.7,97.7,98.9,21700000 +1974-01-03,98.02,100.94,98.02,99.8,24850000 +1974-01-02,97.55,98.38,96.25,97.68,12060000 +1973-12-31,97.54,98.3,95.95,97.55,23470000 +1973-12-28,97.74,98.76,96.41,97.54,21310000 +1973-12-27,96,98.53,96,97.74,22720000 +1973-12-26,93.87,96.52,93.87,95.74,18620000 +1973-12-24,93.54,93.77,91.68,92.9,11540000 +1973-12-21,94.55,95.11,92.7,93.54,18680000 +1973-12-20,94.82,96.26,93.51,94.55,17340000 +1973-12-19,94.74,96.83,93.81,94.82,20670000 +1973-12-18,92.75,95.41,92.18,94.74,19490000 +1973-12-17,93.29,94,91.87,92.75,12930000 +1973-12-14,92.38,94.53,91.05,93.29,20000000 +1973-12-13,93.57,94.68,91.64,92.38,18130000 +1973-12-12,95.52,95.52,92.9,93.57,18190000 +1973-12-11,97.95,99.09,95.62,96.04,20100000 +1973-12-10,96.51,98.58,95.44,97.95,18590000 +1973-12-07,94.49,97.58,94.49,96.51,23230000 +1973-12-06,92.16,94.89,91.68,94.42,23260000 +1973-12-05,93.59,93.93,91.55,92.16,19180000 +1973-12-04,93.9,95.23,92.6,93.59,19030000 +1973-12-03,95.83,95.83,92.92,93.9,17900000 +1973-11-30,97.31,97.55,95.4,95.96,15380000 +1973-11-29,97.65,98.72,96.01,97.31,18870000 +1973-11-28,95.7,98.4,95.22,97.65,19990000 +1973-11-27,96.58,97.7,94.88,95.7,19750000 +1973-11-26,98.64,98.64,95.79,96.58,19830000 +1973-11-23,99.76,100.49,98.59,99.44,11470000 +1973-11-21,98.66,101.33,97.87,99.76,24260000 +1973-11-20,100.65,100.65,97.64,98.66,23960000 +1973-11-19,103.65,103.65,100.37,100.71,16700000 +1973-11-16,102.43,105.41,101.77,103.88,22510000 +1973-11-15,102.45,103.85,100.69,102.43,24530000 +1973-11-14,104.36,105.25,101.87,102.45,22710000 +1973-11-13,104.44,105.42,102.91,104.36,20310000 +1973-11-12,105.3,105.75,103.12,104.44,19250000 +1973-11-09,107.02,107.27,104.77,105.3,17320000 +1973-11-08,106.1,108.45,106.1,107.02,19650000 +1973-11-07,104.96,106.72,104.53,105.8,16570000 +1973-11-06,105.52,107,104.52,104.96,16430000 +1973-11-05,106.97,106.97,104.87,105.52,17150000 +1973-11-02,107.69,108.35,106.33,107.07,16340000 +1973-11-01,108.29,109.2,106.88,107.69,16920000 +1973-10-31,109.33,109.82,107.64,108.29,17890000 +1973-10-30,111.15,111.3,108.95,109.33,17580000 +1973-10-29,111.38,112.56,110.52,111.15,17960000 +1973-10-26,110.5,112.31,110.08,111.38,17800000 +1973-10-25,110.27,111.33,108.85,110.5,15580000 +1973-10-24,109.75,110.98,109.03,110.27,15840000 +1973-10-23,109.16,110.91,107.4,109.75,17230000 +1973-10-22,110.22,110.56,108.18,109.16,14290000 +1973-10-19,110.01,111.56,109.3,110.22,17880000 +1973-10-18,109.97,111.43,108.97,110.01,19210000 +1973-10-17,110.19,111.41,109.19,109.97,18600000 +1973-10-16,110.05,110.8,108.5,110.19,18780000 +1973-10-15,111.32,111.32,109.29,110.05,16160000 +1973-10-12,111.09,112.82,110.52,111.44,22730000 +1973-10-11,109.22,111.77,108.96,111.09,20740000 +1973-10-10,110.13,111.31,108.51,109.22,19010000 +1973-10-09,110.23,111.19,109.05,110.13,19440000 +1973-10-08,109.85,110.93,108.02,110.23,18990000 +1973-10-05,108.41,110.46,107.76,109.85,18820000 +1973-10-04,108.78,109.53,107.3,108.41,19730000 +1973-10-03,108.79,109.95,107.74,108.78,22040000 +1973-10-02,108.21,109.46,107.48,108.79,20770000 +1973-10-01,108.43,108.98,107.08,108.21,15830000 +1973-09-28,109.08,109.42,107.48,108.43,16300000 +1973-09-27,108.83,110.45,108.02,109.08,23660000 +1973-09-26,108.05,109.61,107.43,108.83,21130000 +1973-09-25,107.36,108.79,106.5,108.05,21530000 +1973-09-24,107.2,108.36,106.21,107.36,19490000 +1973-09-21,106.76,108.02,105.43,107.2,23760000 +1973-09-20,105.88,107.55,105.32,106.76,25960000 +1973-09-19,103.8,106.43,103.8,105.88,24570000 +1973-09-18,104.15,104.62,102.41,103.77,16400000 +1973-09-17,104.44,105.41,103.21,104.15,15100000 +1973-09-14,103.36,104.75,102.66,104.44,13760000 +1973-09-13,103.06,104.09,102.37,103.36,11670000 +1973-09-12,103.22,103.98,102.15,103.06,12040000 +1973-09-11,103.85,104.09,102.13,103.22,12690000 +1973-09-10,104.76,105.12,103.33,103.85,11620000 +1973-09-07,105.15,105.87,104.04,104.76,14930000 +1973-09-06,104.64,105.95,104.05,105.15,15670000 +1973-09-05,104.51,105.33,103.6,104.64,14580000 +1973-09-04,104.25,105.35,103.6,104.51,14210000 +1973-08-31,103.88,104.72,103.15,104.25,10530000 +1973-08-30,104.03,104.84,103.29,103.88,12100000 +1973-08-29,103.02,104.92,102.69,104.03,15690000 +1973-08-28,102.42,103.66,102.06,103.02,11810000 +1973-08-27,101.62,102.82,101.09,102.42,9740000 +1973-08-24,101.91,102.65,100.88,101.62,11200000 +1973-08-23,100.62,102.5,100.62,101.91,11390000 +1973-08-22,100.89,101.39,99.74,100.53,10770000 +1973-08-21,101.61,102.1,100.51,100.89,11480000 +1973-08-20,102.31,102.54,101.11,101.61,8970000 +1973-08-17,102.29,102.98,101.38,102.31,11110000 +1973-08-16,103.01,103.97,101.85,102.29,12990000 +1973-08-15,102.71,103.79,101.92,103.01,12040000 +1973-08-14,103.71,104.29,102.34,102.71,11740000 +1973-08-13,104.77,104.83,103.13,103.71,11330000 +1973-08-10,105.61,106.03,104.21,104.77,10870000 +1973-08-09,105.55,106.65,104.89,105.61,12880000 +1973-08-08,106.55,106.73,105.04,105.55,12440000 +1973-08-07,106.73,107.57,105.87,106.55,13510000 +1973-08-06,106.49,107.54,105.45,106.73,12320000 +1973-08-03,106.67,107.17,105.68,106.49,9940000 +1973-08-02,106.83,107.38,105.51,106.67,16080000 +1973-08-01,108.17,108.17,106.29,106.83,13530000 +1973-07-31,109.25,110.09,107.89,108.22,13530000 +1973-07-30,109.59,110.12,108.24,109.25,11170000 +1973-07-27,109.85,110.49,108.7,109.59,12910000 +1973-07-26,109.64,111.04,108.51,109.85,18410000 +1973-07-25,108.14,110.76,107.92,109.64,22220000 +1973-07-24,107.52,108.63,106.31,108.14,16280000 +1973-07-23,107.14,108.42,106.54,107.52,15580000 +1973-07-20,106.55,108.02,105.95,107.14,16300000 +1973-07-19,106.35,107.58,105.06,106.55,18650000 +1973-07-18,105.72,107.05,104.73,106.35,17020000 +1973-07-17,105.67,107.28,104.99,105.72,18750000 +1973-07-16,104.09,106.01,103.42,105.67,12920000 +1973-07-13,105.5,105.8,103.66,104.09,11390000 +1973-07-12,105.8,106.62,104.38,105.5,16400000 +1973-07-11,103.64,106.21,103.64,105.8,18730000 +1973-07-10,102.26,104.2,102.26,103.52,15090000 +1973-07-09,101.28,102.45,100.44,102.14,11560000 +1973-07-06,101.78,102.22,100.67,101.28,9980000 +1973-07-05,101.87,102.48,100.8,101.78,10500000 +1973-07-03,102.9,103.02,101.14,101.87,10560000 +1973-07-02,104.1,104.1,102.44,102.9,9830000 +1973-06-29,104.69,105.3,103.68,104.26,10770000 +1973-06-28,103.62,105.17,103.18,104.69,12760000 +1973-06-27,103.3,104.23,102.29,103.62,12660000 +1973-06-26,102.25,103.78,101.45,103.3,14040000 +1973-06-25,103.64,103.64,101.71,102.25,11670000 +1973-06-22,103.21,105.66,103.07,103.7,18470000 +1973-06-21,104.44,104.77,102.84,103.21,11630000 +1973-06-20,103.99,105.13,103.51,104.44,10600000 +1973-06-19,103.6,104.96,102.46,103.99,12970000 +1973-06-18,104.96,104.96,103.08,103.6,11460000 +1973-06-15,106.21,106.21,104.37,105.1,11970000 +1973-06-14,107.6,108.27,105.83,106.4,13210000 +1973-06-13,108.29,109.52,107.08,107.6,15700000 +1973-06-12,106.7,108.78,106.4,108.29,13840000 +1973-06-11,107.03,107.79,106.11,106.7,9940000 +1973-06-08,105.84,107.75,105.6,107.03,14050000 +1973-06-07,104.31,106.39,104.19,105.84,14160000 +1973-06-06,104.62,105.78,103.6,104.31,13080000 +1973-06-05,102.97,105.27,102.61,104.62,14080000 +1973-06-04,103.93,103.98,102.33,102.97,11230000 +1973-06-01,104.95,105.04,103.31,103.93,10410000 +1973-05-31,105.91,106.3,104.35,104.95,12190000 +1973-05-30,107.51,107.64,105.48,105.91,11730000 +1973-05-29,107.94,108.58,106.77,107.51,11300000 +1973-05-25,107.14,108.86,106.08,107.94,19270000 +1973-05-24,104.07,107.44,103.59,107.14,17310000 +1973-05-23,103.58,105.1,102.82,104.07,14950000 +1973-05-22,102.73,105.04,102.58,103.58,18020000 +1973-05-21,103.77,103.77,101.36,102.73,20690000 +1973-05-18,105.41,105.41,103.18,103.86,17080000 +1973-05-17,106.43,106.82,105.15,105.56,13060000 +1973-05-16,106.57,107.61,105.49,106.43,13800000 +1973-05-15,105.9,107.16,104.12,106.57,18530000 +1973-05-14,107.74,107.74,105.52,105.9,13520000 +1973-05-11,109.49,109.49,107.7,108.17,12980000 +1973-05-10,110.44,110.86,108.86,109.54,13520000 +1973-05-09,111.25,112.25,109.97,110.44,16050000 +1973-05-08,110.53,111.72,109.46,111.25,13730000 +1973-05-07,111,111.38,109.68,110.53,12500000 +1973-05-04,110.22,111.99,109.89,111,19510000 +1973-05-03,108.43,110.64,106.81,110.22,17760000 +1973-05-02,107.1,109.06,106.95,108.43,14380000 +1973-05-01,106.97,108,105.34,107.1,15380000 +1973-04-30,107.23,107.9,105.44,106.97,14820000 +1973-04-27,108.89,109.28,106.76,107.23,13730000 +1973-04-26,108.34,109.66,107.14,108.89,16210000 +1973-04-25,109.82,109.82,107.79,108.34,15960000 +1973-04-24,111.57,111.89,109.64,109.99,13830000 +1973-04-23,112.17,112.66,110.91,111.57,12580000 +1973-04-19,111.54,112.93,111.06,112.17,14560000 +1973-04-18,110.94,112.03,109.99,111.54,13890000 +1973-04-17,111.44,111.81,110.19,110.94,12830000 +1973-04-16,112.08,112.61,110.91,111.44,11350000 +1973-04-13,112.58,112.91,111.23,112.08,14390000 +1973-04-12,112.68,113.65,111.83,112.58,16360000 +1973-04-11,112.21,113.27,111.21,112.68,14890000 +1973-04-10,110.92,112.85,110.92,112.21,16770000 +1973-04-09,109.28,111.24,108.74,110.86,13740000 +1973-04-06,108.52,110.04,108.22,109.28,13890000 +1973-04-05,108.77,109.15,107.44,108.52,12750000 +1973-04-04,109.24,109.96,108.1,108.77,11890000 +1973-04-03,110.18,110.35,108.47,109.24,12910000 +1973-04-02,111.52,111.7,109.68,110.18,10640000 +1973-03-30,112.71,112.87,110.89,111.52,13740000 +1973-03-29,111.62,113.22,111.07,112.71,16050000 +1973-03-28,111.56,112.47,110.54,111.62,15850000 +1973-03-27,109.95,112.07,109.95,111.56,17500000 +1973-03-26,108.88,110.4,108.29,109.84,14980000 +1973-03-23,108.84,109.97,107.41,108.88,18470000 +1973-03-22,110.39,110.39,108.19,108.84,17130000 +1973-03-21,111.95,112.81,110.17,110.49,16080000 +1973-03-20,112.17,112.68,111.02,111.95,13250000 +1973-03-19,113.5,113.5,111.65,112.17,12460000 +1973-03-16,114.12,114.62,112.84,113.54,15130000 +1973-03-15,114.98,115.47,113.77,114.12,14450000 +1973-03-14,114.48,115.61,113.97,114.98,14460000 +1973-03-13,113.86,115.05,113.32,114.48,14210000 +1973-03-12,113.79,114.8,113.25,113.86,13810000 +1973-03-09,114.23,114.55,112.93,113.79,14070000 +1973-03-08,114.45,115.23,113.57,114.23,15100000 +1973-03-07,114.1,115.12,112.83,114.45,19310000 +1973-03-06,112.68,114.71,112.57,114.1,17710000 +1973-03-05,112.28,113.43,111.33,112.68,13720000 +1973-03-02,111.05,112.62,109.45,112.28,17710000 +1973-03-01,111.68,112.98,110.68,111.05,18210000 +1973-02-28,110.9,112.21,109.8,111.68,17950000 +1973-02-27,112.19,112.9,110.5,110.9,16130000 +1973-02-26,113.16,113.26,111.15,112.19,15860000 +1973-02-23,114.44,114.67,112.77,113.16,15450000 +1973-02-22,114.69,115.2,113.44,114.44,14570000 +1973-02-21,115.4,116.01,114.13,114.69,14880000 +1973-02-20,114.98,116.26,114.57,115.4,14020000 +1973-02-16,114.45,115.47,113.73,114.98,13320000 +1973-02-15,115.1,115.68,113.7,114.45,13940000 +1973-02-14,116.78,116.92,114.52,115.1,16520000 +1973-02-13,116.09,118.98,116.09,116.78,25320000 +1973-02-12,114.69,116.66,114.69,116.06,16130000 +1973-02-09,113.16,115.2,113.08,114.68,19260000 +1973-02-08,113.66,114.05,111.85,113.16,18440000 +1973-02-07,114.45,115.48,113.24,113.66,17960000 +1973-02-06,114.23,115.33,113.45,114.45,15720000 +1973-02-05,114.35,115.15,113.62,114.23,14580000 +1973-02-02,114.76,115.4,113.45,114.35,17470000 +1973-02-01,116.03,117.01,114.26,114.76,20670000 +1973-01-31,115.83,116.84,115.05,116.03,14870000 +1973-01-30,116.01,117.11,115.26,115.83,15270000 +1973-01-29,116.45,117.18,115.13,116.01,14680000 +1973-01-26,116.73,117.29,114.97,116.45,21130000 +1973-01-24,118.22,119.04,116.09,116.73,20870000 +1973-01-23,118.21,119,116.84,118.22,19060000 +1973-01-22,118.78,119.63,117.72,118.21,15570000 +1973-01-19,118.85,119.45,117.46,118.78,17020000 +1973-01-18,118.68,119.93,118.15,118.85,17810000 +1973-01-17,118.14,119.35,117.61,118.68,17680000 +1973-01-16,118.44,119.17,117.04,118.14,19170000 +1973-01-15,119.3,120.82,118.04,118.44,21520000 +1973-01-12,120.24,121.27,118.69,119.3,22230000 +1973-01-11,119.43,121.74,119.01,120.24,25050000 +1973-01-10,119.73,120.44,118.78,119.43,20880000 +1973-01-09,119.85,120.4,118.89,119.73,16830000 +1973-01-08,119.87,120.55,119.04,119.85,16840000 +1973-01-05,119.4,120.71,118.88,119.87,19330000 +1973-01-04,119.57,120.17,118.12,119.4,20230000 +1973-01-03,119.1,120.45,118.69,119.57,20620000 +1973-01-02,118.06,119.9,118.06,119.1,17090000 +1972-12-29,116.93,118.77,116.7,118.05,27550000 +1972-12-27,116.3,117.55,115.89,116.93,19100000 +1972-12-26,115.83,116.87,115.54,116.3,11120000 +1972-12-22,115.11,116.4,114.78,115.83,12540000 +1972-12-21,115.95,116.6,114.63,115.11,18290000 +1972-12-20,116.34,117.13,115.38,115.95,18490000 +1972-12-19,116.9,117.37,115.69,116.34,17000000 +1972-12-18,117.88,117.88,115.89,116.9,17540000 +1972-12-15,118.24,119.25,117.37,118.26,18300000 +1972-12-14,118.56,119.19,117.63,118.24,17930000 +1972-12-13,118.66,119.23,117.77,118.56,16540000 +1972-12-12,119.12,119.79,118.09,118.66,17040000 +1972-12-11,118.86,119.78,118.24,119.12,17230000 +1972-12-08,118.6,119.54,117.92,118.86,18030000 +1972-12-07,118.01,119.17,117.57,118.6,19320000 +1972-12-06,117.58,118.56,116.9,118.01,18610000 +1972-12-05,117.77,118.42,116.89,117.58,17800000 +1972-12-04,117.38,118.54,116.99,117.77,19730000 +1972-12-01,116.67,118.18,116.29,117.38,22570000 +1972-11-30,116.52,117.39,115.74,116.67,19340000 +1972-11-29,116.47,117.14,115.56,116.52,17380000 +1972-11-28,116.72,117.48,115.78,116.47,19210000 +1972-11-27,117.27,117.55,115.66,116.72,18190000 +1972-11-24,116.9,117.91,116.19,117.27,15760000 +1972-11-22,116.21,117.61,115.67,116.9,24510000 +1972-11-21,115.53,116.84,115.04,116.21,22110000 +1972-11-20,115.49,116.25,114.57,115.53,16680000 +1972-11-17,115.13,116.23,114.44,115.49,20220000 +1972-11-16,114.5,115.57,113.73,115.13,19580000 +1972-11-15,114.95,116.07,113.87,114.5,23270000 +1972-11-14,113.9,115.41,113.36,114.95,20200000 +1972-11-13,113.73,114.75,112.91,113.9,17210000 +1972-11-10,113.5,115.15,112.85,113.73,24360000 +1972-11-09,113.35,114.11,112.08,113.5,17040000 +1972-11-08,113.98,115.23,112.77,113.35,24620000 +1972-11-06,114.22,115.17,112.91,113.98,21330000 +1972-11-03,113.23,114.81,112.71,114.22,22510000 +1972-11-02,112.67,113.81,111.96,113.23,20690000 +1972-11-01,111.58,113.31,111.32,112.67,21360000 +1972-10-31,110.59,112.05,110.4,111.58,15450000 +1972-10-30,110.62,111.19,109.66,110.59,11820000 +1972-10-27,110.99,111.62,109.99,110.62,15470000 +1972-10-26,110.72,112.26,110.26,110.99,20790000 +1972-10-25,110.81,111.56,109.96,110.72,17430000 +1972-10-24,110.35,111.34,109.38,110.81,15240000 +1972-10-23,109.51,111.1,109.51,110.35,14190000 +1972-10-20,108.05,109.79,107.59,109.24,15740000 +1972-10-19,108.19,108.81,107.4,108.05,13850000 +1972-10-18,107.5,109.11,107.36,108.19,17290000 +1972-10-17,106.77,108.04,106.27,107.5,13410000 +1972-10-16,107.92,108.4,106.38,106.77,10940000 +1972-10-13,108.6,108.88,107.17,107.92,12870000 +1972-10-12,109.5,109.69,108.03,108.6,13130000 +1972-10-11,109.99,110.51,108.77,109.5,11900000 +1972-10-10,109.9,111.11,109.32,109.99,13310000 +1972-10-09,109.62,110.44,109.28,109.9,7940000 +1972-10-06,108.89,110.49,107.78,109.62,16630000 +1972-10-05,110.09,110.52,108.49,108.89,17730000 +1972-10-04,110.3,111.35,109.58,110.09,16640000 +1972-10-03,110.16,110.9,109.47,110.3,13090000 +1972-10-02,110.55,110.98,109.49,110.16,12440000 +1972-09-29,110.35,110.55,108.05,110.55,16250000 +1972-09-28,109.66,110.75,108.75,110.35,14710000 +1972-09-27,108.12,109.92,107.79,109.66,14620000 +1972-09-26,108.05,108.97,107.35,108.12,13150000 +1972-09-25,108.52,109.09,107.67,108.05,10920000 +1972-09-22,108.43,109.2,107.72,108.52,12570000 +1972-09-21,108.6,109.13,107.75,108.43,11940000 +1972-09-20,108.55,109.12,107.84,108.6,11980000 +1972-09-19,108.61,109.57,108.08,108.55,13330000 +1972-09-18,108.81,109.22,107.86,108.61,8880000 +1972-09-15,108.93,109.49,108.1,108.81,11690000 +1972-09-14,108.9,109.64,108.21,108.93,12500000 +1972-09-13,108.47,109.36,107.84,108.9,13070000 +1972-09-12,109.51,109.84,107.81,108.47,13560000 +1972-09-11,110.15,110.57,109.01,109.51,10710000 +1972-09-08,110.29,110.9,109.67,110.15,10980000 +1972-09-07,110.55,111.06,109.71,110.29,11090000 +1972-09-06,111.23,111.38,110.04,110.55,12010000 +1972-09-05,111.51,112.08,110.75,111.23,10630000 +1972-09-01,111.09,112.12,110.7,111.51,11600000 +1972-08-31,110.57,111.52,110.08,111.09,12340000 +1972-08-30,110.41,111.33,109.9,110.57,12470000 +1972-08-29,110.23,111.02,109.26,110.41,12300000 +1972-08-28,110.67,111.24,109.71,110.23,10720000 +1972-08-25,111.02,111.53,109.78,110.67,13840000 +1972-08-24,112.26,112.81,110.62,111.02,18280000 +1972-08-23,112.41,113.27,111.3,112.26,18670000 +1972-08-22,111.72,113.16,111.28,112.41,18560000 +1972-08-21,111.76,112.74,110.75,111.72,14290000 +1972-08-18,111.34,112.53,110.81,111.76,16150000 +1972-08-17,111.66,112.41,110.72,111.34,14360000 +1972-08-16,112.06,112.8,110.87,111.66,14950000 +1972-08-15,112.55,113.04,111.27,112.06,16670000 +1972-08-14,111.95,113.45,111.66,112.55,18870000 +1972-08-11,111.05,112.4,110.52,111.95,16570000 +1972-08-10,110.86,111.68,110.09,111.05,15260000 +1972-08-09,110.69,111.57,109.98,110.86,15730000 +1972-08-08,110.61,111.32,109.67,110.69,14550000 +1972-08-07,110.43,111.38,109.69,110.61,13220000 +1972-08-04,110.14,111.12,109.37,110.43,15700000 +1972-08-03,109.29,110.88,108.9,110.14,19970000 +1972-08-02,108.4,109.85,108.12,109.29,17920000 +1972-08-01,107.39,108.85,107.06,108.4,15540000 +1972-07-31,107.38,108.06,106.6,107.39,11120000 +1972-07-28,107.28,108.03,106.52,107.38,13050000 +1972-07-27,107.53,108.31,106.61,107.28,13870000 +1972-07-26,107.6,108.42,106.79,107.53,14130000 +1972-07-25,107.92,108.88,107.06,107.6,17180000 +1972-07-24,106.66,108.67,106.63,107.92,18020000 +1972-07-21,105.81,107.05,104.99,106.66,14010000 +1972-07-20,106.14,106.68,105.12,105.81,15050000 +1972-07-19,105.83,107.36,105.47,106.14,17880000 +1972-07-18,105.88,106.4,104.43,105.83,16820000 +1972-07-17,106.8,107.37,105.55,105.88,13170000 +1972-07-14,106.28,107.58,105.77,106.8,13910000 +1972-07-13,106.89,107.3,105.62,106.28,14740000 +1972-07-12,107.32,108.15,106.42,106.89,16150000 +1972-07-11,108.11,108.35,106.87,107.32,12830000 +1972-07-10,108.69,109.16,107.62,108.11,11700000 +1972-07-07,109.04,109.66,108.16,108.69,12900000 +1972-07-06,108.28,110.27,108.28,109.04,19520000 +1972-07-05,107.49,108.8,107.14,108.1,14710000 +1972-07-03,107.14,107.95,106.72,107.49,8140000 +1972-06-30,106.82,107.91,106.4,107.14,12860000 +1972-06-29,107.02,107.47,105.94,106.82,14610000 +1972-06-28,107.37,107.87,106.49,107.02,12140000 +1972-06-27,107.48,108.29,106.7,107.37,13750000 +1972-06-26,108.23,108.23,106.68,107.48,12720000 +1972-06-23,108.68,109.33,107.69,108.27,13940000 +1972-06-22,108.79,109.26,107.62,108.68,13410000 +1972-06-21,108.56,109.66,107.98,108.79,15510000 +1972-06-20,108.11,109.12,107.64,108.56,14970000 +1972-06-19,108.36,108.78,107.37,108.11,11660000 +1972-06-16,108.44,108.94,107.54,108.36,13010000 +1972-06-15,108.39,109.52,107.78,108.44,16940000 +1972-06-14,107.55,109.15,107.38,108.39,18320000 +1972-06-13,107.01,108.03,106.38,107.55,15710000 +1972-06-12,106.86,107.92,106.29,107.01,13390000 +1972-06-09,107.28,107.68,106.3,106.86,12790000 +1972-06-08,107.65,108.52,106.9,107.28,13820000 +1972-06-07,108.21,108.52,106.91,107.65,15220000 +1972-06-06,108.82,109.32,107.71,108.21,15980000 +1972-06-05,109.73,109.92,108.28,108.82,13450000 +1972-06-02,109.69,110.51,108.93,109.73,15400000 +1972-06-01,109.53,110.35,108.97,109.69,14910000 +1972-05-31,110.35,110.52,108.92,109.53,15230000 +1972-05-30,110.66,111.48,109.78,110.35,15810000 +1972-05-26,110.46,111.31,109.84,110.66,15730000 +1972-05-25,110.31,111.2,109.67,110.46,16480000 +1972-05-24,109.78,111.07,109.39,110.31,17870000 +1972-05-23,109.69,110.46,108.91,109.78,16410000 +1972-05-22,108.98,110.37,108.79,109.69,16030000 +1972-05-19,107.94,109.59,107.74,108.98,19580000 +1972-05-18,106.89,108.39,106.72,107.94,17370000 +1972-05-17,106.66,107.38,106.02,106.89,13600000 +1972-05-16,106.86,107.55,106.13,106.66,14070000 +1972-05-15,106.38,107.45,106.06,106.86,13600000 +1972-05-12,105.77,107.02,105.49,106.38,13990000 +1972-05-11,105.42,106.45,104.9,105.77,12900000 +1972-05-10,104.74,106.1,104.43,105.42,13870000 +1972-05-09,106.06,106.06,103.83,104.74,19910000 +1972-05-08,106.63,106.81,105.36,106.14,11250000 +1972-05-05,106.25,107.33,105.7,106.63,13210000 +1972-05-04,105.99,106.81,105.14,106.25,14790000 +1972-05-03,106.08,107.24,105.44,105.99,15900000 +1972-05-02,106.69,107.37,105.55,106.08,15370000 +1972-05-01,107.67,108,106.3,106.69,12880000 +1972-04-28,107.05,108.28,106.7,107.67,14160000 +1972-04-27,106.89,107.89,106.42,107.05,15740000 +1972-04-26,107.12,107.89,106.18,106.89,17710000 +1972-04-25,108.19,108.29,106.7,107.12,17030000 +1972-04-24,108.89,109.19,107.62,108.19,14650000 +1972-04-21,109.04,109.92,108.3,108.89,18200000 +1972-04-20,109.2,109.69,108.08,109.04,18190000 +1972-04-19,109.77,110.35,108.71,109.2,19180000 +1972-04-18,109.51,110.64,109.02,109.77,19410000 +1972-04-17,109.84,110.22,108.77,109.51,15390000 +1972-04-14,109.91,110.56,109.07,109.84,17460000 +1972-04-13,110.18,110.79,109.37,109.91,17990000 +1972-04-12,109.76,111.11,109.36,110.18,24690000 +1972-04-11,109.45,110.38,108.76,109.76,19930000 +1972-04-10,109.62,110.54,108.89,109.45,19470000 +1972-04-07,109.53,110.15,108.53,109.62,19900000 +1972-04-06,109,110.29,108.53,109.53,22830000 +1972-04-05,108.12,109.64,107.96,109,22960000 +1972-04-04,107.48,108.62,106.77,108.12,18110000 +1972-04-03,107.2,108.26,106.75,107.48,14990000 +1972-03-30,106.49,107.67,106.07,107.2,14360000 +1972-03-29,107.17,107.41,105.98,106.49,13860000 +1972-03-28,107.3,108.08,106.22,107.17,15380000 +1972-03-27,107.52,108,106.53,107.3,12180000 +1972-03-24,107.75,108.36,106.95,107.52,15390000 +1972-03-23,106.84,108.33,106.67,107.75,18380000 +1972-03-22,106.69,107.52,106,106.84,15400000 +1972-03-21,107.59,107.68,105.86,106.69,18610000 +1972-03-20,107.92,108.81,107.18,107.59,16420000 +1972-03-17,107.5,108.61,106.89,107.92,16040000 +1972-03-16,107.75,108.22,106.55,107.5,16700000 +1972-03-15,107.61,108.55,107.09,107.75,19460000 +1972-03-14,107.33,108.2,106.71,107.61,22370000 +1972-03-13,108.38,108.52,106.71,107.33,16730000 +1972-03-10,108.94,109.37,107.77,108.38,19690000 +1972-03-09,108.96,109.75,108.19,108.94,21460000 +1972-03-08,108.87,109.68,108.04,108.96,21290000 +1972-03-07,108.77,109.72,108.02,108.87,22640000 +1972-03-06,107.94,109.4,107.64,108.77,21000000 +1972-03-03,107.32,108.51,106.78,107.94,20420000 +1972-03-02,107.35,108.39,106.63,107.32,22200000 +1972-03-01,106.57,108.13,106.21,107.35,23670000 +1972-02-29,106.19,107.16,105.45,106.57,20320000 +1972-02-28,106.18,107.04,105.37,106.19,18200000 +1972-02-25,105.45,106.73,105.04,106.18,18180000 +1972-02-24,105.38,106.24,104.76,105.45,16000000 +1972-02-23,105.29,106.18,104.72,105.38,16770000 +1972-02-22,105.28,106.18,104.65,105.29,16670000 +1972-02-18,105.59,106.01,104.47,105.28,16590000 +1972-02-17,105.62,106.65,104.96,105.59,22330000 +1972-02-16,105.03,106.25,104.65,105.62,20670000 +1972-02-15,104.59,105.59,104.1,105.03,17770000 +1972-02-14,105.08,105.53,104.03,104.59,15840000 +1972-02-11,105.59,105.91,104.45,105.08,17850000 +1972-02-10,105.55,106.69,104.97,105.59,23460000 +1972-02-09,104.74,106.03,104.36,105.55,19850000 +1972-02-08,104.54,105.22,103.9,104.74,17390000 +1972-02-07,104.86,105.46,103.97,104.54,16930000 +1972-02-04,104.64,105.48,104.05,104.86,17890000 +1972-02-03,104.68,105.43,103.85,104.64,19880000 +1972-02-02,104.01,105.41,103.5,104.68,24070000 +1972-02-01,103.94,104.57,103.1,104.01,19600000 +1972-01-31,104.16,104.88,103.3,103.94,18250000 +1972-01-28,103.5,104.98,103.22,104.16,25000000 +1972-01-27,102.5,103.93,102.2,103.5,20360000 +1972-01-26,102.7,103.31,101.81,102.5,14940000 +1972-01-25,102.57,103.59,101.63,102.7,17570000 +1972-01-24,103.65,104.03,102.2,102.57,15640000 +1972-01-21,103.88,104.4,102.75,103.65,18810000 +1972-01-20,103.88,105,103.32,103.88,20210000 +1972-01-19,104.05,104.61,102.83,103.88,18800000 +1972-01-18,103.7,104.85,103.35,104.05,21070000 +1972-01-17,103.39,104.24,102.8,103.7,15860000 +1972-01-14,102.99,103.89,102.41,103.39,14960000 +1972-01-13,103.59,103.8,102.29,102.99,16410000 +1972-01-12,103.65,104.66,103.05,103.59,20970000 +1972-01-11,103.32,104.3,102.85,103.65,17970000 +1972-01-10,103.47,103.97,102.44,103.32,15320000 +1972-01-07,103.51,104.29,102.38,103.47,17140000 +1972-01-06,103.06,104.2,102.66,103.51,21100000 +1972-01-05,102.09,103.69,101.9,103.06,21350000 +1972-01-04,101.67,102.59,100.87,102.09,15190000 +1972-01-03,102.09,102.85,101.19,101.67,12570000 +1971-12-31,102.09,102.09,102.09,102.09,14040000 +1971-12-30,101.78,101.78,101.78,101.78,13810000 +1971-12-29,102.21,102.21,102.21,102.21,17150000 +1971-12-28,101.95,101.95,101.95,101.95,15090000 +1971-12-27,100.95,100.95,100.95,100.95,11890000 +1971-12-23,100.74,100.74,100.74,100.74,16000000 +1971-12-22,101.18,101.18,101.18,101.18,18930000 +1971-12-21,101.8,101.8,101.8,101.8,20460000 +1971-12-20,101.55,101.55,101.55,101.55,23810000 +1971-12-17,100.26,100.26,100.26,100.26,18270000 +1971-12-16,99.74,99.74,99.74,99.74,21070000 +1971-12-15,98.54,98.54,98.54,98.54,16890000 +1971-12-14,97.67,97.67,97.67,97.67,16070000 +1971-12-13,97.97,97.97,97.97,97.97,17020000 +1971-12-10,97.69,97.69,97.69,97.69,17510000 +1971-12-09,96.96,96.96,96.96,96.96,14710000 +1971-12-08,96.87,97.65,96.08,96.92,16650000 +1971-12-07,96.51,97.35,95.4,96.87,15250000 +1971-12-06,97.06,98.17,96.07,96.51,17480000 +1971-12-03,95.84,97.57,95.36,97.06,16760000 +1971-12-02,95.44,96.59,94.73,95.84,17780000 +1971-12-01,93.99,96.12,93.95,95.44,21040000 +1971-11-30,93.41,94.43,92.51,93.99,18320000 +1971-11-29,92.04,94.9,92.04,93.41,18910000 +1971-11-26,90.33,92.19,90.27,91.94,10870000 +1971-11-24,90.16,91.14,89.73,90.33,11870000 +1971-11-23,90.79,91.1,89.34,90.16,16840000 +1971-11-22,91.61,92.12,90.51,90.79,11390000 +1971-11-19,92.13,92.38,90.95,91.61,12420000 +1971-11-18,92.85,93.62,91.88,92.13,13010000 +1971-11-17,92.71,93.35,91.8,92.85,12840000 +1971-11-16,91.81,93.15,91.21,92.71,13300000 +1971-11-15,92.12,92.69,91.38,91.81,9370000 +1971-11-12,92.12,92.9,90.93,92.12,14540000 +1971-11-11,93.41,93.54,91.64,92.12,13310000 +1971-11-10,94.46,94.84,93.1,93.41,13410000 +1971-11-09,94.39,95.31,93.94,94.46,12080000 +1971-11-08,94.46,94.97,93.78,94.39,8520000 +1971-11-05,94.79,95.01,93.64,94.46,10780000 +1971-11-04,94.91,96.08,94.37,94.79,15750000 +1971-11-03,93.27,95.31,93.27,94.91,14590000 +1971-11-02,92.8,93.73,91.84,93.18,13330000 +1971-11-01,94.23,94.43,92.48,92.8,10960000 +1971-10-29,93.96,94.71,93.28,94.23,11710000 +1971-10-28,93.79,94.75,92.96,93.96,15530000 +1971-10-27,94.74,94.99,93.39,93.79,13480000 +1971-10-26,95.02,95.02,94.38,94.74,13390000 +1971-10-25,95.57,95.76,94.57,95.1,7340000 +1971-10-22,95.6,96.83,94.97,95.57,14560000 +1971-10-21,95.65,96.33,94.59,95.6,14990000 +1971-10-20,97,97.45,95.23,95.65,16340000 +1971-10-19,97.35,97.66,96.05,97,13040000 +1971-10-18,97.79,98.33,96.98,97.35,10420000 +1971-10-15,98.13,98.45,97.03,97.79,13120000 +1971-10-14,99.03,99.25,97.74,98.13,12870000 +1971-10-13,99.57,100.08,98.61,99.03,13540000 +1971-10-12,99.21,100.2,98.62,99.57,14340000 +1971-10-11,99.36,99.62,98.58,99.21,7800000 +1971-10-08,100.02,100.3,98.87,99.36,13870000 +1971-10-07,99.82,100.96,99.42,100.02,17780000 +1971-10-06,99.11,100.13,98.49,99.82,15630000 +1971-10-05,99.21,99.78,98.34,99.11,12360000 +1971-10-04,98.93,100.04,98.62,99.21,14570000 +1971-10-01,98.34,99.49,97.96,98.93,13400000 +1971-09-30,97.9,98.97,97.48,98.34,13490000 +1971-09-29,97.88,98.51,97.29,97.9,8580000 +1971-09-28,97.62,98.55,97.12,97.88,11250000 +1971-09-27,98.15,98.41,96.97,97.62,10220000 +1971-09-24,98.38,99.35,97.78,98.15,13460000 +1971-09-23,98.47,99.12,97.61,98.38,13250000 +1971-09-22,99.34,99.72,98.15,98.47,14250000 +1971-09-21,99.68,100.08,98.71,99.34,10640000 +1971-09-20,99.96,100.4,99.14,99.68,9540000 +1971-09-17,99.66,100.52,99.26,99.96,11020000 +1971-09-16,99.77,100.35,99.07,99.66,10550000 +1971-09-15,99.34,100.24,98.79,99.77,11080000 +1971-09-14,100.07,100.35,98.99,99.34,11410000 +1971-09-13,100.42,100.84,99.49,100.07,10000000 +1971-09-10,100.8,101.01,99.69,100.42,11380000 +1971-09-09,101.34,101.88,100.38,100.8,15790000 +1971-09-08,101.15,101.94,100.52,101.34,14230000 +1971-09-07,100.69,102.25,100.43,101.15,17080000 +1971-09-03,99.29,100.93,99.1,100.69,14040000 +1971-09-02,99.07,99.8,98.52,99.29,10690000 +1971-09-01,99.03,99.84,98.5,99.07,10770000 +1971-08-31,99.52,99.76,98.32,99.03,10430000 +1971-08-30,100.48,100.89,99.17,99.52,11140000 +1971-08-27,100.24,101.22,99.76,100.48,12490000 +1971-08-26,100.41,101.12,99.4,100.24,13990000 +1971-08-25,100.4,101.51,99.77,100.41,18280000 +1971-08-24,99.25,101.02,99.15,100.4,18700000 +1971-08-23,98.33,99.96,98.09,99.25,13040000 +1971-08-20,98.16,98.94,97.52,98.33,11890000 +1971-08-19,98.6,99.07,97.35,98.16,14190000 +1971-08-18,99.99,100.19,98.06,98.6,20680000 +1971-08-17,98.76,101,98.49,99.99,26790000 +1971-08-16,97.9,100.96,97.9,98.76,31730000 +1971-08-13,96,96.53,95.19,95.69,9960000 +1971-08-12,94.81,96.5,94.81,96,15910000 +1971-08-11,93.54,95.06,93.35,94.66,11370000 +1971-08-10,93.53,94.13,92.81,93.54,9460000 +1971-08-09,94.25,94.55,93.17,93.53,8110000 +1971-08-06,94.09,94.91,93.63,94.25,9490000 +1971-08-05,93.89,94.89,93.33,94.09,12100000 +1971-08-04,94.51,95.34,93.35,93.89,15410000 +1971-08-03,95.96,96.11,94.06,94.51,13490000 +1971-08-02,95.58,96.76,95.22,95.96,11870000 +1971-07-30,96.02,96.78,95.08,95.58,12970000 +1971-07-29,97.07,97.22,95.37,96.02,14570000 +1971-07-28,97.78,98.15,96.51,97.07,13940000 +1971-07-27,98.14,98.99,97.42,97.78,11560000 +1971-07-26,98.94,99.47,96.67,98.14,9930000 +1971-07-23,99.11,99.6,98.26,98.94,12370000 +1971-07-22,99.28,99.82,98.5,99.11,12570000 +1971-07-21,99.32,100,98.74,99.28,11920000 +1971-07-20,98.93,100.01,98.6,99.32,12540000 +1971-07-19,99.11,99.57,98.11,98.93,11430000 +1971-07-16,99.28,100.35,98.64,99.11,13870000 +1971-07-15,99.22,100.48,98.76,99.28,13080000 +1971-07-14,99.5,99.83,98.23,99.22,14360000 +1971-07-13,100.82,101.06,99.07,99.5,13540000 +1971-07-12,100.69,101.52,100.19,100.82,12020000 +1971-07-09,100.34,101.33,99.86,100.69,12640000 +1971-07-08,100.04,101.03,99.59,100.34,13920000 +1971-07-07,99.76,100.83,99.25,100.04,14520000 +1971-07-06,99.78,100.35,99.1,99.76,10440000 +1971-07-02,99.78,100.31,99.09,99.78,9960000 +1971-07-01,99.16,100.65,99.16,99.78,13090000 +1971-06-30,98.82,100.29,98.68,98.7,15410000 +1971-06-29,97.74,99.39,97.61,98.82,14460000 +1971-06-28,97.99,98.48,97.02,97.74,9810000 +1971-06-25,98.13,98.66,97.33,97.99,10580000 +1971-06-24,98.41,99,97.59,98.13,11360000 +1971-06-23,97.59,98.95,97.36,98.41,12640000 +1971-06-22,97.87,98.66,96.92,97.59,15200000 +1971-06-21,98.97,99.18,97.22,97.87,16490000 +1971-06-18,100.5,100.63,98.65,98.97,15040000 +1971-06-17,100.52,101.37,99.87,100.5,13980000 +1971-06-16,100.32,101.29,99.68,100.52,14300000 +1971-06-15,100.22,101.1,99.45,100.32,13550000 +1971-06-14,101.07,101.28,99.78,100.22,11530000 +1971-06-11,100.64,101.71,100.18,101.07,12270000 +1971-06-10,100.29,101.23,99.78,100.64,12450000 +1971-06-09,100.32,100.97,99.28,100.29,14250000 +1971-06-08,101.09,101.5,99.91,100.32,13610000 +1971-06-07,101.3,102.02,100.55,101.09,13800000 +1971-06-04,101.01,101.88,100.43,101.3,14400000 +1971-06-03,100.96,102.07,100.3,101.01,18790000 +1971-06-02,100.2,101.53,99.89,100.96,17740000 +1971-06-01,99.63,100.76,99.22,100.2,11930000 +1971-05-28,99.4,100.17,98.68,99.63,11760000 +1971-05-27,99.59,100.14,98.78,99.4,12610000 +1971-05-26,99.47,100.49,98.93,99.59,13550000 +1971-05-25,100.13,100.39,98.73,99.47,16050000 +1971-05-24,100.99,101.24,99.72,100.13,12060000 +1971-05-21,101.31,101.84,100.41,100.99,12090000 +1971-05-20,101.07,102.17,100.61,101.31,11740000 +1971-05-19,100.83,101.75,100.3,101.07,17640000 +1971-05-18,100.69,101.62,99.68,100.83,17640000 +1971-05-17,102.08,102.08,100.25,100.69,15980000 +1971-05-14,102.69,103.17,101.65,102.21,16430000 +1971-05-13,102.9,103.57,101.98,102.69,17640000 +1971-05-12,102.62,103.57,102.12,102.9,15140000 +1971-05-11,102.36,103.37,101.5,102.62,17730000 +1971-05-10,102.87,103.15,101.71,102.36,12810000 +1971-05-07,103.23,103.5,101.86,102.87,16490000 +1971-05-06,103.78,104.42,102.8,103.23,19300000 +1971-05-05,103.79,104.28,102.68,103.78,17270000 +1971-05-04,103.29,104.36,102.71,103.79,17310000 +1971-05-03,103.95,104.11,102.37,103.29,16120000 +1971-04-30,104.63,104.96,103.25,103.95,17490000 +1971-04-29,104.77,105.58,103.9,104.63,20340000 +1971-04-28,104.59,105.6,103.85,104.77,24820000 +1971-04-27,103.94,105.07,103.23,104.59,21250000 +1971-04-26,104.05,104.83,103.19,103.94,18860000 +1971-04-23,103.56,104.63,102.79,104.05,20150000 +1971-04-22,103.36,104.27,102.58,103.56,19270000 +1971-04-21,103.61,104.16,102.55,103.36,17040000 +1971-04-20,104.01,104.58,103.06,103.61,17880000 +1971-04-19,103.49,104.63,103.09,104.01,17730000 +1971-04-16,103.52,104.18,102.68,103.49,18280000 +1971-04-15,103.37,104.4,102.76,103.52,22540000 +1971-04-14,102.98,104.01,102.28,103.37,19440000 +1971-04-13,102.88,103.96,102.25,102.98,23200000 +1971-04-12,102.1,103.54,101.75,102.88,19410000 +1971-04-08,101.98,102.86,101.3,102.1,17590000 +1971-04-07,101.51,102.87,101.13,101.98,22270000 +1971-04-06,100.79,102.11,100.3,101.51,19990000 +1971-04-05,100.56,101.41,99.88,100.79,16040000 +1971-04-02,100.39,101.23,99.86,100.56,14520000 +1971-04-01,100.31,100.99,99.63,100.39,13470000 +1971-03-31,100.26,101.05,99.69,100.31,17610000 +1971-03-30,100.03,100.86,99.41,100.26,15430000 +1971-03-29,99.95,100.74,99.36,100.03,13650000 +1971-03-26,99.61,100.65,99.18,99.95,15560000 +1971-03-25,99.62,100.03,98.36,99.61,15870000 +1971-03-24,100.28,100.63,99.15,99.62,15770000 +1971-03-23,100.62,101.06,99.62,100.28,16470000 +1971-03-22,101.01,101.46,100.08,100.62,14290000 +1971-03-19,101.19,101.74,100.35,101.01,15150000 +1971-03-18,101.12,102.03,100.43,101.19,17910000 +1971-03-17,101.21,101.66,99.98,101.12,17070000 +1971-03-16,100.71,101.94,100.36,101.21,22270000 +1971-03-15,99.57,101.15,99.12,100.71,18920000 +1971-03-12,99.39,100.09,98.64,99.57,14680000 +1971-03-11,99.3,100.29,98.57,99.39,19830000 +1971-03-10,99.46,100.1,98.63,99.3,17220000 +1971-03-09,99.38,100.31,98.72,99.46,20490000 +1971-03-08,98.96,99.44,98.42,99.38,19340000 +1971-03-05,97.92,99.49,97.82,98.96,22430000 +1971-03-04,96.95,98.38,96.9,97.92,17350000 +1971-03-03,96.98,97.54,96.3,96.95,14680000 +1971-03-02,97,97.6,96.32,96.98,14870000 +1971-03-01,96.75,97.48,96.11,97,13020000 +1971-02-26,96.96,97.54,95.84,96.75,17250000 +1971-02-25,96.73,97.71,96.08,96.96,16200000 +1971-02-24,96.09,97.34,95.86,96.73,15930000 +1971-02-23,95.72,96.67,94.92,96.09,15080000 +1971-02-22,96.65,96.65,94.97,95.72,15840000 +1971-02-19,97.56,97.79,96.25,96.74,17860000 +1971-02-18,98.2,98.6,96.96,97.56,16650000 +1971-02-17,98.66,99.32,97.32,98.2,18720000 +1971-02-16,98.43,99.59,97.85,98.66,21350000 +1971-02-12,97.91,98.96,97.56,98.43,18470000 +1971-02-11,97.39,98.49,96.99,97.91,19260000 +1971-02-10,97.51,97.97,96.23,97.39,19040000 +1971-02-09,97.45,98.5,96.9,97.51,28250000 +1971-02-08,96.93,98.04,96.13,97.45,25590000 +1971-02-05,96.62,97.58,95.84,96.93,20480000 +1971-02-04,96.63,97.26,95.69,96.62,20860000 +1971-02-03,96.43,97.19,95.58,96.63,21680000 +1971-02-02,96.42,97.19,95.6,96.43,22030000 +1971-02-01,95.88,97.05,95.38,96.42,20650000 +1971-01-29,95.21,96.49,94.79,95.88,20960000 +1971-01-28,94.89,95.78,94.12,95.21,18840000 +1971-01-27,95.59,95.78,93.96,94.89,20640000 +1971-01-26,95.28,96.36,94.69,95.59,21380000 +1971-01-25,94.88,95.93,94.16,95.28,19050000 +1971-01-22,94.19,95.53,93.96,94.88,21680000 +1971-01-21,93.78,94.69,93.15,94.19,19060000 +1971-01-20,93.76,94.53,93.07,93.78,18330000 +1971-01-19,93.41,94.28,92.85,93.76,15800000 +1971-01-18,93.03,94.11,92.63,93.41,15400000 +1971-01-15,92.8,93.94,92.25,93.03,18010000 +1971-01-14,92.56,93.36,91.67,92.8,17600000 +1971-01-13,92.72,93.66,91.88,92.56,19070000 +1971-01-12,91.98,93.28,91.63,92.72,17820000 +1971-01-11,92.19,92.67,90.99,91.98,14720000 +1971-01-08,92.38,93.02,91.6,92.19,14100000 +1971-01-07,92.35,93.26,91.75,92.38,16460000 +1971-01-06,91.8,93,91.5,92.35,16960000 +1971-01-05,91.15,92.28,90.69,91.8,12600000 +1971-01-04,92.15,92.19,90.64,91.15,10010000 +1970-12-31,92.27,92.79,91.36,92.15,13390000 +1970-12-30,92.08,92.99,91.6,92.27,19140000 +1970-12-29,91.09,92.38,90.73,92.08,17750000 +1970-12-28,90.61,91.49,90.28,91.09,12290000 +1970-12-24,90.1,91.08,89.81,90.61,12140000 +1970-12-23,90.04,90.86,89.35,90.1,15400000 +1970-12-22,89.94,90.84,89.35,90.04,14510000 +1970-12-21,90.22,90.77,89.36,89.94,12690000 +1970-12-18,90.04,90.77,89.42,90.22,14360000 +1970-12-17,89.72,90.61,89.31,90.04,13660000 +1970-12-16,89.66,90.22,88.77,89.72,14240000 +1970-12-15,89.8,90.32,88.93,89.66,13420000 +1970-12-14,90.26,90.81,89.28,89.8,13810000 +1970-12-11,89.92,90.93,89.44,90.26,15790000 +1970-12-10,89.54,90.87,89.01,89.92,14610000 +1970-12-09,89.47,90.03,88.48,89.54,13550000 +1970-12-08,89.94,90.47,88.87,89.47,14370000 +1970-12-07,89.46,90.39,88.76,89.94,15530000 +1970-12-04,88.9,89.89,88.12,89.46,15980000 +1970-12-03,88.48,89.87,88.11,88.9,20480000 +1970-12-02,87.47,88.83,86.72,88.48,17960000 +1970-12-01,87.2,88.61,86.11,87.47,20170000 +1970-11-30,85.93,87.6,85.79,87.2,17700000 +1970-11-27,85.09,86.21,84.67,85.93,10130000 +1970-11-25,84.78,85.7,84.35,85.09,13490000 +1970-11-24,84.24,85.18,83.59,84.78,12560000 +1970-11-23,83.72,84.92,83.47,84.24,12720000 +1970-11-20,82.91,84.06,82.49,83.72,10920000 +1970-11-19,82.79,83.48,82.23,82.91,9280000 +1970-11-18,83.47,83.53,82.41,82.79,9850000 +1970-11-17,83.24,84.17,82.81,83.47,9450000 +1970-11-16,83.37,83.75,82.34,83.24,9160000 +1970-11-13,84.15,84.33,82.92,83.37,11890000 +1970-11-12,85.03,85.54,83.81,84.15,12520000 +1970-11-11,84.79,86.24,84.69,85.03,13520000 +1970-11-10,84.67,85.69,84.18,84.79,12030000 +1970-11-09,84.22,85.27,83.82,84.67,10890000 +1970-11-06,84.1,84.73,83.55,84.22,9970000 +1970-11-05,84.39,84.79,83.53,84.1,10800000 +1970-11-04,84.22,85.26,83.82,84.39,12180000 +1970-11-03,83.51,84.77,83.21,84.22,11760000 +1970-11-02,83.25,83.99,82.66,83.51,9470000 +1970-10-30,83.36,83.8,82.52,83.25,10520000 +1970-10-29,83.43,84.1,82.82,83.36,10440000 +1970-10-28,83.12,83.81,82.29,83.43,10660000 +1970-10-27,83.31,83.73,82.52,83.12,9680000 +1970-10-26,83.77,84.26,82.89,83.31,9200000 +1970-10-23,83.38,84.3,82.91,83.77,10270000 +1970-10-22,83.66,84.04,82.77,83.38,9000000 +1970-10-21,83.64,84.72,83.21,83.66,11330000 +1970-10-20,83.15,84.19,82.62,83.64,10630000 +1970-10-19,84.28,84.29,82.81,83.15,9890000 +1970-10-16,84.65,85.21,83.83,84.28,11300000 +1970-10-15,84.19,85.28,83.82,84.65,11250000 +1970-10-14,84.06,84.83,83.42,84.19,9920000 +1970-10-13,84.17,84.7,83.24,84.06,9500000 +1970-10-12,85.05,85.05,83.58,84.17,8570000 +1970-10-09,85.95,86.25,84.54,85.08,13980000 +1970-10-08,86.89,87.37,85.55,85.95,14500000 +1970-10-07,86.85,87.47,85.55,86.89,15610000 +1970-10-06,86.47,87.75,86.04,86.85,20240000 +1970-10-05,85.16,86.99,85.01,86.47,19760000 +1970-10-02,84.32,85.56,84.06,85.16,15420000 +1970-10-01,84.3,84.7,83.46,84.32,9700000 +1970-09-30,83.86,84.99,82.78,84.3,14830000 +1970-09-29,83.91,84.57,83.11,83.86,17880000 +1970-09-28,82.83,84.56,82.61,83.91,14390000 +1970-09-25,81.66,83.6,81.41,82.83,20470000 +1970-09-24,81.91,82.24,80.82,81.66,21340000 +1970-09-23,81.86,83.15,81.52,81.91,16940000 +1970-09-22,81.91,82.24,80.82,81.86,12110000 +1970-09-21,82.62,83.15,81.52,81.91,12540000 +1970-09-18,82.29,83.5,81.77,82.62,15900000 +1970-09-17,81.79,83.09,81.51,82.29,15530000 +1970-09-16,81.36,82.57,80.61,81.79,12090000 +1970-09-15,82.07,82.11,80.75,81.36,9830000 +1970-09-14,82.52,83.13,81.43,82.07,11900000 +1970-09-11,82.3,83.19,81.81,82.52,12140000 +1970-09-10,82.79,82.98,81.62,82.3,11900000 +1970-09-09,83.04,83.78,81.9,82.79,16250000 +1970-09-08,82.83,83.69,81.48,83.04,17110000 +1970-09-04,82.09,83.42,81.79,82.83,15360000 +1970-09-03,80.96,82.63,80.88,82.09,14110000 +1970-09-02,80.95,81.35,79.95,80.96,9710000 +1970-09-01,81.52,81.8,80.43,80.95,10960000 +1970-08-31,81.86,82.33,80.95,81.52,10740000 +1970-08-28,81.08,82.47,80.69,81.86,13820000 +1970-08-27,81.21,81.91,80.13,81.08,12440000 +1970-08-26,81.12,82.26,80.6,81.21,15970000 +1970-08-25,80.99,81.81,79.69,81.12,17520000 +1970-08-24,79.41,81.62,79.41,80.99,18910000 +1970-08-21,77.84,79.6,77.46,79.24,13420000 +1970-08-20,76.96,77.99,76.3,77.84,10170000 +1970-08-19,76.2,77.58,76.01,76.96,9870000 +1970-08-18,75.33,76.79,75.3,76.2,9500000 +1970-08-17,75.18,75.79,74.52,75.33,6940000 +1970-08-14,74.76,75.74,74.39,75.18,7850000 +1970-08-13,75.42,75.69,74.13,74.76,8640000 +1970-08-12,75.82,76.24,75.04,75.42,7440000 +1970-08-11,76.2,76.33,75.16,75.82,7330000 +1970-08-10,77.28,77.4,75.72,76.2,7580000 +1970-08-07,77.08,78.09,76.46,77.28,9370000 +1970-08-06,77.18,77.68,76.39,77.08,7560000 +1970-08-05,77.19,77.86,76.59,77.18,7660000 +1970-08-04,77.02,77.56,76.12,77.19,8310000 +1970-08-03,78.05,78.24,76.56,77.02,7650000 +1970-07-31,78.07,79.03,77.44,78.05,11640000 +1970-07-30,78.04,78.66,77.36,78.07,10430000 +1970-07-29,77.77,78.81,77.28,78.04,12580000 +1970-07-28,77.65,78.35,76.96,77.77,9040000 +1970-07-27,77.82,78.27,77.07,77.65,7460000 +1970-07-24,78,78.48,76.96,77.82,9520000 +1970-07-23,77.03,78.51,76.46,78,12460000 +1970-07-22,76.98,78.2,76.22,77.03,12460000 +1970-07-21,77.79,77.94,76.39,76.98,9940000 +1970-07-20,77.69,78.72,77.04,77.79,11660000 +1970-07-17,76.37,78.23,76.37,77.69,13870000 +1970-07-16,75.23,77.09,75.12,76.34,12200000 +1970-07-15,74.42,75.68,74.06,75.23,8860000 +1970-07-14,74.55,75.04,73.78,74.42,7360000 +1970-07-13,74.45,75.37,73.83,74.55,7450000 +1970-07-10,74.06,75.21,73.49,74.45,10160000 +1970-07-09,73,74.77,72.88,74.06,12820000 +1970-07-08,71.23,73.3,70.99,73,10970000 +1970-07-07,71.78,72.32,70.69,71.23,10470000 +1970-07-06,72.92,73.12,71.38,71.78,9340000 +1970-07-02,72.94,73.92,72.43,72.92,8440000 +1970-07-01,72.72,73.66,72.11,72.94,8610000 +1970-06-30,72.89,73.89,72.25,72.72,9280000 +1970-06-29,73.47,73.86,72.34,72.89,8770000 +1970-06-26,74.02,74.68,73.09,73.47,9160000 +1970-06-25,73.97,74.93,73.3,74.02,8200000 +1970-06-24,74.76,75.42,73.4,73.97,12630000 +1970-06-23,76.64,76.83,74.52,74.76,10790000 +1970-06-22,77.05,77.43,75.61,76.64,8700000 +1970-06-19,76.51,78.05,76.31,77.05,10980000 +1970-06-18,76,77.17,74.99,76.51,8870000 +1970-06-17,76.15,78.04,75.63,76,9870000 +1970-06-16,74.58,76.76,74.21,76.15,11330000 +1970-06-15,73.88,75.27,73.67,74.58,6920000 +1970-06-12,74.45,74.84,73.25,73.88,8890000 +1970-06-11,75.48,75.52,73.96,74.45,7770000 +1970-06-10,76.25,76.62,74.92,75.48,7240000 +1970-06-09,76.29,79.96,75.58,76.25,7050000 +1970-06-08,76.17,77.37,75.3,76.29,8040000 +1970-06-05,77.36,77.48,75.25,76.17,12450000 +1970-06-04,78.52,79.42,76.99,77.36,14380000 +1970-06-03,77.84,79.22,76.97,78.52,16600000 +1970-06-02,77.84,78.73,76.51,77.84,13480000 +1970-06-01,76.55,78.4,75.84,77.84,15020000 +1970-05-29,74.61,76.92,73.53,76.55,14630000 +1970-05-28,72.77,75.44,72.59,74.61,18910000 +1970-05-27,69.37,73.22,69.37,72.77,17460000 +1970-05-26,70.25,71.17,68.61,69.29,17030000 +1970-05-25,72.16,72.16,69.92,70.25,12660000 +1970-05-22,72.16,73.42,71.42,72.25,12170000 +1970-05-21,73.51,73.51,70.94,72.16,16710000 +1970-05-20,75.35,75.35,73.25,73.52,13020000 +1970-05-19,76.96,77.2,75.21,75.46,9480000 +1970-05-18,76.9,77.68,76.07,76.96,8280000 +1970-05-15,75.44,77.42,74.59,76.9,14570000 +1970-05-14,76.53,76.64,74.03,75.44,13920000 +1970-05-13,77.75,77.75,75.92,76.53,10720000 +1970-05-12,78.6,79.15,77.06,77.85,10850000 +1970-05-11,79.44,79.72,78.29,78.6,6650000 +1970-05-08,79.83,80.15,78.71,79.44,6930000 +1970-05-07,79.47,80.6,78.89,79.83,9530000 +1970-05-06,78.6,80.91,78.23,79.47,14380000 +1970-05-05,79.37,79.83,78.02,78.6,10580000 +1970-05-04,81.28,81.28,78.85,79.37,11450000 +1970-05-01,81.52,82.32,80.27,81.44,8290000 +1970-04-30,81.81,82.57,80.76,81.52,9880000 +1970-04-29,80.27,83.23,79.31,81.81,15800000 +1970-04-28,81.46,82.16,79.86,80.27,12620000 +1970-04-27,82.77,83.08,81.08,81.46,10240000 +1970-04-24,83.04,83.62,81.96,82.77,10410000 +1970-04-23,84.27,84.3,82.61,83.04,11050000 +1970-04-22,85.38,85.51,83.84,84.27,10780000 +1970-04-21,85.83,86.54,84.99,85.38,8490000 +1970-04-20,85.67,86.36,84.99,85.83,8280000 +1970-04-17,85.88,86.36,84.75,85.67,10990000 +1970-04-16,86.73,87.13,85.51,85.88,10250000 +1970-04-15,86.89,87.71,86.53,86.73,9410000 +1970-04-14,87.64,87.73,86.01,86.89,10840000 +1970-04-13,88.24,88.67,87.15,87.64,8810000 +1970-04-10,88.53,89.14,87.82,88.24,10020000 +1970-04-09,88.49,89.32,87.96,88.53,9060000 +1970-04-08,88.52,89.09,87.83,88.49,9070000 +1970-04-07,88.76,89.31,87.94,88.52,8490000 +1970-04-06,89.39,89.61,88.15,88.76,8380000 +1970-04-03,89.79,90.16,88.81,89.39,9920000 +1970-04-02,90.07,90.7,89.28,89.79,10520000 +1970-04-01,89.63,90.62,89.3,90.07,9810000 +1970-03-31,89.63,90.17,88.85,89.63,8370000 +1970-03-30,89.92,90.41,88.91,89.63,9600000 +1970-03-26,89.77,90.65,89.18,89.92,11350000 +1970-03-25,88.11,91.07,88.11,89.77,17500000 +1970-03-24,86.99,88.43,86.9,87.98,8840000 +1970-03-23,87.06,87.64,86.19,86.99,7330000 +1970-03-20,87.42,87.77,86.43,87.06,7910000 +1970-03-19,87.54,88.2,86.88,87.42,8930000 +1970-03-18,87.29,88.28,86.93,87.54,9790000 +1970-03-17,86.91,87.86,86.36,87.29,9090000 +1970-03-16,87.86,87.97,86.39,86.91,8910000 +1970-03-13,88.33,89.43,87.29,87.86,9560000 +1970-03-12,88.69,89.09,87.68,88.33,9140000 +1970-03-11,88.75,89.58,88.11,88.69,9180000 +1970-03-10,88.51,89.41,87.89,88.75,9450000 +1970-03-09,89.43,89.43,87.94,88.51,9760000 +1970-03-06,90,90.36,88.84,89.44,10980000 +1970-03-05,90.04,90.99,89.38,90,11370000 +1970-03-04,90.23,91.05,89.32,90.04,11850000 +1970-03-03,89.71,90.67,88.96,90.23,11700000 +1970-03-02,89.5,90.8,88.92,89.71,12270000 +1970-02-27,88.9,90.33,88.42,89.5,12890000 +1970-02-26,89.35,89.63,87.63,88.9,11540000 +1970-02-25,87.99,89.8,87.11,89.35,13210000 +1970-02-24,88.03,88.91,87.28,87.99,10810000 +1970-02-20,87.76,88.74,86.87,88.03,10790000 +1970-02-19,87.44,88.7,86.94,87.76,12890000 +1970-02-18,86.37,88.07,86.19,87.44,11950000 +1970-02-17,86.47,87.08,85.57,86.37,10140000 +1970-02-16,86.54,87.3,85.8,86.47,19780000 +1970-02-13,86.73,87.3,85.71,86.54,11060000 +1970-02-12,86.94,87.54,85.93,86.73,10010000 +1970-02-11,86.1,87.38,85.3,86.94,12260000 +1970-02-10,87.01,87.4,85.58,86.1,10110000 +1970-02-09,86.33,87.85,86.16,87.01,10830000 +1970-02-06,85.9,86.88,85.23,86.33,10150000 +1970-02-05,86.24,86.62,84.95,85.9,9430000 +1970-02-04,86.77,87.66,85.59,86.24,11040000 +1970-02-03,85.75,87.54,84.64,86.77,16050000 +1970-02-02,85.02,86.76,84.76,85.75,13440000 +1970-01-30,85.69,86.33,84.42,85.02,12320000 +1970-01-29,86.79,87.09,85.02,85.69,12210000 +1970-01-28,87.62,88.24,86.44,86.79,10510000 +1970-01-27,88.17,88.54,86.92,87.62,9630000 +1970-01-26,89.23,89.23,87.49,88.17,10670000 +1970-01-23,90.04,90.45,88.74,89.37,11000000 +1970-01-22,89.95,90.8,89.2,90.04,11050000 +1970-01-21,89.83,90.61,89.2,89.95,9880000 +1970-01-20,89.65,90.45,88.64,89.83,11050000 +1970-01-19,90.72,90.72,89.14,89.65,9500000 +1970-01-16,91.68,92.49,90.36,90.92,11940000 +1970-01-15,91.65,92.35,90.73,91.68,11120000 +1970-01-14,91.92,92.4,90.88,91.65,10380000 +1970-01-13,91.7,92.61,90.99,91.92,9870000 +1970-01-12,92.4,92.67,91.2,91.7,8900000 +1970-01-09,92.68,93.25,91.82,92.4,9380000 +1970-01-08,92.63,93.47,91.99,92.68,10670000 +1970-01-07,92.82,93.38,91.93,92.63,10010000 +1970-01-06,93.46,93.81,92.13,92.82,11460000 +1970-01-05,93,94.25,92.53,93.46,11490000 +1970-01-02,92.06,93.54,91.79,93,8050000 +1969-12-31,91.6,92.94,91.15,92.06,19380000 +1969-12-30,91.25,92.2,90.47,91.6,15790000 +1969-12-29,91.89,92.49,90.66,91.25,12500000 +1969-12-26,91.18,92.3,90.94,91.89,6750000 +1969-12-24,90.23,91.89,89.93,91.18,11670000 +1969-12-23,90.58,91.13,89.4,90.23,13890000 +1969-12-22,91.38,92.03,90.1,90.58,12680000 +1969-12-19,90.61,92.34,90.33,91.38,15420000 +1969-12-18,89.2,91.15,88.62,90.61,15950000 +1969-12-17,89.72,90.32,88.94,89.2,12840000 +1969-12-16,90.54,91.05,89.23,89.72,11880000 +1969-12-15,90.81,91.42,89.96,90.54,11100000 +1969-12-12,90.52,91.67,90.05,90.81,11630000 +1969-12-11,90.48,91.37,89.74,90.52,10430000 +1969-12-10,90.55,91.22,89.33,90.48,12590000 +1969-12-09,90.84,91.79,89.93,90.55,12290000 +1969-12-08,91.73,92.05,90.29,90.84,9990000 +1969-12-05,91.95,92.91,91.14,91.73,11150000 +1969-12-04,91.65,92.45,90.36,91.95,13230000 +1969-12-03,92.65,93.05,91.25,91.65,11300000 +1969-12-02,93.22,93.54,91.95,92.65,9940000 +1969-12-01,93.81,94.47,92.78,93.22,9950000 +1969-11-28,93.27,94.41,92.88,93.81,8550000 +1969-11-26,92.94,93.85,92.24,93.27,10630000 +1969-11-25,93.24,94.17,92.38,92.94,11560000 +1969-11-24,94.32,94.43,92.63,93.24,10940000 +1969-11-21,94.91,95.34,93.87,94.32,9840000 +1969-11-20,95.9,95.94,94.12,94.91,12010000 +1969-11-19,96.39,96.95,95.36,95.9,11240000 +1969-11-18,96.41,97,95.57,96.39,11010000 +1969-11-17,97.07,97.36,95.82,96.41,10120000 +1969-11-14,97.42,97.44,96.36,97.07,10580000 +1969-11-13,97.89,98.34,96.54,97.42,12090000 +1969-11-12,98.07,98.72,97.28,97.89,12480000 +1969-11-11,98.33,98.79,97.45,98.07,10080000 +1969-11-10,98.26,99.23,97.65,98.33,12490000 +1969-11-07,97.67,99.01,97.18,98.26,13280000 +1969-11-06,97.64,98.31,96.8,97.67,11110000 +1969-11-05,97.21,98.39,96.75,97.64,12110000 +1969-11-04,97.15,97.82,95.84,97.21,12340000 +1969-11-03,97.12,97.82,96.19,97.15,11140000 +1969-10-31,96.93,98.03,96.33,97.12,13100000 +1969-10-30,96.81,97.47,95.61,96.93,12820000 +1969-10-29,97.66,97.92,96.26,96.81,12380000 +1969-10-28,97.97,98.55,97.02,97.66,12410000 +1969-10-27,98.12,98.78,97.49,97.97,12160000 +1969-10-24,97.46,98.83,96.97,98.12,15430000 +1969-10-23,97.83,98.39,96.46,97.46,14780000 +1969-10-22,97.2,98.61,96.56,97.83,19320000 +1969-10-21,96.46,97.84,95.86,97.2,16460000 +1969-10-20,96.26,97.17,95.29,96.46,13540000 +1969-10-17,96.37,97.24,95.38,96.26,13740000 +1969-10-16,95.72,97.54,95.05,96.37,19500000 +1969-10-15,95.7,96.56,94.65,95.72,15740000 +1969-10-14,94.55,96.53,94.32,95.7,19950000 +1969-10-13,93.56,94.86,93.2,94.55,13620000 +1969-10-10,93.03,94.19,92.6,93.56,12210000 +1969-10-09,92.67,93.55,91.75,93.03,10420000 +1969-10-08,93.09,93.56,92.04,92.67,10370000 +1969-10-07,93.38,94.03,92.59,93.09,10050000 +1969-10-06,93.19,93.99,92.5,93.38,9180000 +1969-10-03,93.24,94.39,92.65,93.19,12410000 +1969-10-02,92.52,93.63,91.66,93.24,11430000 +1969-10-01,93.12,93.51,92.12,92.52,9090000 +1969-09-30,93.41,94.05,92.55,93.12,9180000 +1969-09-29,94.16,94.45,92.62,93.41,10170000 +1969-09-26,94.77,95.23,93.53,94.16,9680000 +1969-09-25,95.5,95.92,94.28,94.77,10690000 +1969-09-24,95.63,96.2,94.75,95.5,11320000 +1969-09-23,95.63,96.62,94.86,95.63,13030000 +1969-09-22,95.19,96.13,94.58,95.63,9280000 +1969-09-19,94.9,95.92,94.35,95.19,12270000 +1969-09-18,94.76,95.53,94.05,94.9,11170000 +1969-09-17,94.95,95.7,94.04,94.76,10980000 +1969-09-16,94.87,95.73,94.06,94.95,11160000 +1969-09-15,94.13,95.61,93.73,94.87,10680000 +1969-09-12,94.22,95.04,93.26,94.13,10800000 +1969-09-11,94.95,95.77,93.72,94.22,12370000 +1969-09-10,93.38,95.35,93.23,94.95,11490000 +1969-09-09,92.7,93.94,91.77,93.38,10980000 +1969-09-08,93.64,93.76,92.35,92.7,8310000 +1969-09-05,94.2,94.51,93.09,93.64,8890000 +1969-09-04,94.98,95.2,93.66,94.2,9380000 +1969-09-03,95.54,96.11,94.38,94.98,8760000 +1969-09-02,95.51,96.31,94.85,95.54,8560000 +1969-08-29,94.89,95.51,94.46,95.51,8850000 +1969-08-28,94.49,95.38,94.04,94.89,7730000 +1969-08-27,94.3,95.16,93.76,94.49,9100000 +1969-08-26,94.93,95.04,93.65,94.3,8910000 +1969-08-25,95.92,96.13,94.52,94.93,8410000 +1969-08-22,95.35,96.43,94.91,95.92,10140000 +1969-08-21,95.07,95.87,94.56,95.35,8420000 +1969-08-20,95.07,95.64,94.25,95.07,9680000 +1969-08-19,94.57,95.18,93.95,95.07,12640000 +1969-08-18,94,95,93.51,94.57,9420000 +1969-08-15,93.34,94.5,92.92,94,10210000 +1969-08-14,92.7,93.87,92.32,93.34,9690000 +1969-08-13,92.63,93.26,91.48,92.7,9910000 +1969-08-12,93.36,93.66,92.19,92.63,7870000 +1969-08-11,93.94,94.24,92.77,93.36,6680000 +1969-08-08,93.99,94.63,93.29,93.94,8760000 +1969-08-07,93.92,94.77,93.17,93.99,9450000 +1969-08-06,93.41,94.76,93.02,93.92,11100000 +1969-08-05,92.99,94.02,92.13,93.41,8940000 +1969-08-04,93.47,94.42,92.29,92.99,10700000 +1969-08-01,91.92,94.19,91.92,93.47,15070000 +1969-07-31,89.96,92.4,89.96,91.83,14160000 +1969-07-30,89.48,90.82,88.04,89.93,15580000 +1969-07-29,90.21,91.56,89.06,89.48,13630000 +1969-07-28,91.91,91.91,89.83,90.21,11800000 +1969-07-25,92.8,93.28,91.54,92.06,9800000 +1969-07-24,93.12,93.87,92.29,92.8,9750000 +1969-07-23,93.52,93.99,92.07,93.12,11680000 +1969-07-22,94.95,95.45,93.15,93.52,9780000 +1969-07-18,95.76,95.84,94.18,94.95,8590000 +1969-07-17,95.18,96.71,95.07,95.76,10450000 +1969-07-16,94.24,95.83,94.22,95.18,10470000 +1969-07-15,94.55,95,93.11,94.24,11110000 +1969-07-14,95.77,96.17,94.2,94.55,8310000 +1969-07-11,95.38,96.65,94.81,95.77,11730000 +1969-07-10,96.88,97.04,95.03,95.38,11450000 +1969-07-09,97.63,97.85,96.33,96.88,9320000 +1969-07-08,98.98,98.98,97.15,97.63,9320000 +1969-07-07,99.61,100.33,98.45,99.03,9970000 +1969-07-03,98.94,100.25,98.62,99.61,10110000 +1969-07-02,98.08,99.5,97.81,98.94,11350000 +1969-07-01,97.71,98.66,97.13,98.08,9890000 +1969-06-30,97.33,98.64,96.82,97.71,8640000 +1969-06-27,97.25,98.15,96.65,97.33,9020000 +1969-06-26,97.01,97.91,95.97,97.25,10310000 +1969-06-25,97.32,98.3,96.56,97.01,10490000 +1969-06-24,96.29,98.04,96.29,97.32,11460000 +1969-06-23,96.67,97.17,95.21,96.23,12900000 +1969-06-20,97.24,98.22,96.29,96.67,11360000 +1969-06-19,97.81,98.38,96.61,97.24,11160000 +1969-06-18,97.95,99.2,97.45,97.81,11290000 +1969-06-17,98.32,98.71,96.88,97.95,12210000 +1969-06-16,98.65,99.64,97.91,98.32,10400000 +1969-06-13,98.26,99.51,97.59,98.65,13070000 +1969-06-12,99.05,99.78,97.96,98.26,11790000 +1969-06-11,100.42,100.71,99.02,99.05,13640000 +1969-06-10,101.2,101.76,100.02,100.42,10660000 +1969-06-09,102.12,102.16,100.54,101.2,10650000 +1969-06-06,102.76,103.41,101.68,102.12,12520000 +1969-06-05,102.59,103.45,102.05,102.76,12350000 +1969-06-04,102.63,103.45,102.07,102.59,10840000 +1969-06-03,102.94,103.6,102.09,102.63,11190000 +1969-06-02,103.46,103.75,102.4,102.94,9180000 +1969-05-29,103.26,104.27,102.76,103.46,11770000 +1969-05-28,103.57,103.91,102.29,103.26,11330000 +1969-05-27,104.36,104.68,103.12,103.57,10580000 +1969-05-26,104.59,105.14,103.8,104.36,9030000 +1969-05-23,104.6,105.32,103.78,104.59,10900000 +1969-05-22,104.47,105.66,103.92,104.6,13710000 +1969-05-21,104.04,105.03,103.37,104.47,12100000 +1969-05-20,104.97,105.16,103.56,104.04,10280000 +1969-05-19,105.94,106.15,104.52,104.97,9790000 +1969-05-16,105.85,106.59,105.18,105.94,12280000 +1969-05-15,106.16,106.69,105.08,105.85,11930000 +1969-05-14,105.34,106.74,105.07,106.16,14360000 +1969-05-13,104.89,105.91,104.31,105.34,12910000 +1969-05-12,105.05,105.65,104.12,104.89,10550000 +1969-05-09,105.1,106.01,104.35,105.05,12530000 +1969-05-08,104.67,105.74,104.1,105.1,13050000 +1969-05-07,104.86,105.59,103.83,104.67,14030000 +1969-05-06,104.37,105.5,103.84,104.86,14700000 +1969-05-05,104,105.08,103.48,104.37,13300000 +1969-05-02,103.51,104.63,102.98,104,13070000 +1969-05-01,103.69,104.59,102.74,103.51,14380000 +1969-04-30,102.79,104.56,102.5,103.69,19350000 +1969-04-29,102.03,103.31,101.51,102.79,14730000 +1969-04-28,101.72,102.65,100.97,102.03,11120000 +1969-04-25,101.27,102.29,100.81,101.72,12480000 +1969-04-24,100.8,101.8,100.21,101.27,11340000 +1969-04-23,100.78,101.77,100.15,100.8,12220000 +1969-04-22,100.56,101.29,99.52,100.78,10250000 +1969-04-21,101.24,101.68,100.11,100.56,10010000 +1969-04-18,100.78,102.09,100.3,101.24,10850000 +1969-04-17,100.63,101.41,99.99,100.78,9360000 +1969-04-16,101.53,101.78,100.16,100.63,9680000 +1969-04-15,101.57,102.15,100.76,101.53,9610000 +1969-04-14,101.65,102.4,101.02,101.57,8990000 +1969-04-11,101.55,102.28,100.97,101.65,10650000 +1969-04-10,101.02,102.22,100.73,101.55,12200000 +1969-04-09,100.14,101.44,99.88,101.02,12530000 +1969-04-08,99.89,101.27,99.35,100.14,9360000 +1969-04-07,100.63,100.63,99.08,99.89,9430000 +1969-04-03,100.78,101.3,99.87,100.68,10300000 +1969-04-02,101.42,101.65,100.61,100.78,10110000 +1969-04-01,101.51,102.45,100.84,101.42,12360000 +1969-03-28,101.1,102.35,100.73,101.51,12430000 +1969-03-27,100.39,101.81,100.03,101.1,11900000 +1969-03-26,99.66,100.86,99.24,100.39,11030000 +1969-03-25,99.5,100.3,98.88,99.66,9820000 +1969-03-24,99.63,100.16,98.85,99.5,8110000 +1969-03-21,99.84,100.37,98.88,99.63,9830000 +1969-03-20,99.21,100.39,98.9,99.84,10260000 +1969-03-19,98.49,99.7,98.03,99.21,9740000 +1969-03-18,98.25,99.41,97.83,98.49,11210000 +1969-03-17,98,98.71,97.06,98.25,9150000 +1969-03-14,98.39,98.7,97.4,98,8640000 +1969-03-13,99.05,99.35,97.82,98.39,10030000 +1969-03-12,99.32,99.87,98.35,99.05,8720000 +1969-03-11,98.99,100.14,98.58,99.32,9870000 +1969-03-10,98.65,99.47,97.87,98.99,8920000 +1969-03-07,98.7,99.13,97.32,98.65,10830000 +1969-03-06,99.71,99.93,98.11,98.7,9670000 +1969-03-05,99.32,100.48,98.95,99.71,11370000 +1969-03-04,98.38,99.76,98.17,99.32,9320000 +1969-03-03,98.13,99.08,97.61,98.38,8260000 +1969-02-28,98.14,99.02,97.53,98.13,8990000 +1969-02-27,98.45,99,97.5,98.14,9670000 +1969-02-26,97.98,99.1,97.36,98.45,9540000 +1969-02-25,98.6,99.65,97.5,97.98,9540000 +1969-02-24,99.79,100.07,98.09,98.6,12730000 +1969-02-20,100.65,101.03,99.29,99.79,10990000 +1969-02-19,101.4,102.07,100.3,100.65,10390000 +1969-02-18,102.27,102.27,100.58,101.4,12490000 +1969-02-17,103.61,104.03,102.04,102.44,11670000 +1969-02-14,103.71,104.37,102.88,103.61,11460000 +1969-02-13,103.63,104.36,102.86,103.71,12010000 +1969-02-12,103.65,104.34,102.98,103.63,11530000 +1969-02-11,103.53,104.61,102.96,103.65,12320000 +1969-02-07,103.54,104.22,102.5,103.53,12780000 +1969-02-06,103.2,104.3,102.55,103.54,12570000 +1969-02-05,102.92,103.84,102.26,103.2,13750000 +1969-02-04,102.89,103.59,102.15,102.92,12550000 +1969-02-03,103.01,103.75,102.04,102.89,12510000 +1969-01-31,102.55,103.64,102.08,103.01,12020000 +1969-01-30,102.51,103.33,101.73,102.55,13010000 +1969-01-29,102.41,103.31,101.69,102.51,11470000 +1969-01-28,102.4,103.3,101.56,102.41,12070000 +1969-01-27,102.38,103.15,101.64,102.4,11020000 +1969-01-24,102.43,103.23,101.71,102.38,12520000 +1969-01-23,101.98,103.21,101.57,102.43,13140000 +1969-01-22,101.63,102.55,101.06,101.98,11480000 +1969-01-21,101.69,102.4,100.88,101.63,10910000 +1969-01-20,102.03,102.6,101,101.69,10950000 +1969-01-17,102.18,103.06,101.32,102.03,11590000 +1969-01-16,101.62,103.25,101.27,102.18,13120000 +1969-01-15,101.13,102.48,100.78,101.62,11810000 +1969-01-14,100.44,101.63,99.04,101.13,10700000 +1969-01-13,100.93,101.35,96.63,100.44,11160000 +1969-01-10,101.22,102.14,100.32,100.93,12680000 +1969-01-09,100.8,102.09,100.35,101.22,12100000 +1969-01-08,101.22,102.12,100.14,100.8,13840000 +1969-01-07,102.47,102.68,100.15,101.22,15740000 +1969-01-06,103.99,104.36,101.94,102.47,12720000 +1969-01-03,103.93,104.87,103.17,103.99,12750000 +1969-01-02,103.86,104.85,103.21,103.93,9800000 +1968-12-31,103.8,104.61,102.98,103.86,13130000 +1968-12-30,104.74,104.99,103.09,103.8,12080000 +1968-12-27,105.15,105.87,104.2,104.74,11200000 +1968-12-26,105.04,106.03,104.29,105.15,9670000 +1968-12-24,105.21,105.95,104.37,105.04,11540000 +1968-12-23,106.34,106.68,104.61,105.21,12970000 +1968-12-20,106.97,107.98,105.73,106.34,15910000 +1968-12-19,106.66,107.67,105.1,106.97,19630000 +1968-12-17,107.1,107.65,105.86,106.66,14700000 +1968-12-16,107.58,108.4,106.4,107.1,15950000 +1968-12-13,107.32,108.5,106.56,107.58,16740000 +1968-12-12,107.39,108.43,106.33,107.32,18160000 +1968-12-10,107.66,108.33,106.68,107.39,14500000 +1968-12-09,107.93,108.77,106.89,107.66,15800000 +1968-12-06,107.67,108.91,106.85,107.93,15320000 +1968-12-05,108.02,108.9,106.71,107.67,19330000 +1968-12-03,108.12,108.74,107.02,108.02,15460000 +1968-12-02,108.37,109.37,107.15,108.12,15390000 +1968-11-29,107.76,109.09,107.32,108.37,14390000 +1968-11-27,107.26,108.55,106.59,107.76,16550000 +1968-11-26,106.48,107.93,106.11,107.26,16360000 +1968-11-25,106.3,107.29,105.47,106.48,14490000 +1968-11-22,105.97,106.89,105.21,106.3,15420000 +1968-11-21,106.14,106.77,104.85,105.97,18320000 +1968-11-19,105.92,106.84,105.06,106.14,15120000 +1968-11-18,105.78,106.74,105.05,105.92,14390000 +1968-11-15,105.2,106.44,104.61,105.78,15040000 +1968-11-14,105.13,106.01,104.34,105.2,14900000 +1968-11-13,104.62,105.76,104.08,105.13,15660000 +1968-11-12,103.95,105.28,103.51,104.62,17250000 +1968-11-08,103.5,104.59,102.96,103.95,14250000 +1968-11-07,103.27,104.47,102.31,103.5,11660000 +1968-11-06,103.1,104.41,102.45,103.27,12640000 +1968-11-04,103.06,103.69,101.85,103.1,10930000 +1968-11-01,103.41,104.3,102.36,103.06,14480000 +1968-10-31,103.3,104.57,102.43,103.41,17650000 +1968-10-29,103.9,104.5,102.65,103.3,12340000 +1968-10-28,104.2,104.89,103.16,103.9,11740000 +1968-10-25,103.84,104.81,103.14,104.2,14150000 +1968-10-24,104.57,105.15,103.15,103.84,18300000 +1968-10-22,104.99,105.48,103.84,104.57,13970000 +1968-10-21,104.82,105.78,104.09,104.99,14380000 +1968-10-18,104.01,105.34,103.54,104.82,15130000 +1968-10-17,103.81,105.01,103.81,104.01,21060000 +1968-10-15,103.32,104.25,102.66,103.53,13410000 +1968-10-14,103.18,104.03,102.48,103.32,11980000 +1968-10-11,103.29,103.9,102.39,103.18,12650000 +1968-10-10,103.74,104.3,102.61,103.29,17000000 +1968-10-08,103.7,104.45,102.84,103.74,14000000 +1968-10-07,103.71,104.4,102.93,103.7,12420000 +1968-10-04,103.22,104.35,102.65,103.71,15350000 +1968-10-03,102.86,104.13,102.34,103.22,21110000 +1968-10-01,102.67,103.58,101.8,102.86,15560000 +1968-09-30,102.31,103.29,101.71,102.67,13610000 +1968-09-27,102.36,103.07,101.36,102.31,13860000 +1968-09-26,102.59,103.63,101.59,102.36,18950000 +1968-09-24,102.24,103.21,101.59,102.59,15210000 +1968-09-23,101.66,102.82,101.2,102.24,11550000 +1968-09-20,101.59,102.37,100.81,101.66,14190000 +1968-09-19,101.5,102.53,100.84,101.59,17910000 +1968-09-17,101.24,102.18,100.64,101.5,13920000 +1968-09-16,100.86,102.01,100.33,101.24,13260000 +1968-09-13,100.52,101.53,99.89,100.86,13070000 +1968-09-12,100.73,101.4,99.7,100.52,14630000 +1968-09-10,101.23,101.81,100.12,100.73,11430000 +1968-09-09,101.2,102.09,100.47,101.23,11890000 +1968-09-06,100.74,101.88,100.23,101.2,13180000 +1968-09-05,100.02,101.34,99.63,100.74,12980000 +1968-09-04,99.32,100.49,98.95,100.02,10040000 +1968-09-03,98.86,99.89,98.31,99.32,8620000 +1968-08-30,98.74,99.52,98.2,98.86,8190000 +1968-08-29,98.81,99.49,97.9,98.74,10940000 +1968-08-27,98.94,99.61,98.16,98.81,9710000 +1968-08-26,98.69,99.67,98.29,98.94,9740000 +1968-08-23,98.7,99.57,97.71,98.69,9890000 +1968-08-22,98.96,99.58,97.71,98.7,15140000 +1968-08-20,99,99.65,98.08,98.96,10640000 +1968-08-19,98.68,99.64,98.16,99,9900000 +1968-08-16,98.07,99.21,97.62,98.68,9940000 +1968-08-15,98.53,99.36,97.48,98.07,12710000 +1968-08-13,98.01,99.2,97.68,98.53,12730000 +1968-08-12,97.01,98.49,96.72,98.01,10420000 +1968-08-09,97.04,97.56,96.11,97.01,8390000 +1968-08-08,97.25,98.32,96.58,97.04,12920000 +1968-08-06,96.85,97.82,96.42,97.25,9620000 +1968-08-05,96.63,97.51,95.95,96.85,8850000 +1968-08-02,97.28,97.47,95.79,96.63,9860000 +1968-08-01,97.74,98.82,96.78,97.28,14380000 +1968-07-30,97.65,98.62,96.84,97.74,10250000 +1968-07-29,98.34,98.78,96.89,97.65,10940000 +1968-07-26,97.94,99.14,97.22,98.34,11690000 +1968-07-25,99.21,100.07,97.43,97.94,16140000 +1968-07-23,99.33,99.93,97.89,99.21,13570000 +1968-07-22,100.46,100.88,98.51,99.33,13530000 +1968-07-19,101.44,101.82,99.8,100.46,14620000 +1968-07-18,101.7,102.65,100.49,101.44,17420000 +1968-07-16,102.26,102.72,100.97,101.7,13380000 +1968-07-15,102.34,103.15,101.44,102.26,13390000 +1968-07-12,102.39,103.24,101.39,102.34,14810000 +1968-07-11,102.23,103.67,101.41,102.39,20290000 +1968-07-09,101.94,102.93,101.19,102.23,16540000 +1968-07-08,100.91,102.76,100.72,101.94,16860000 +1968-07-03,99.74,101.36,99.6,100.91,14390000 +1968-07-02,99.4,100.6,98.6,99.74,13350000 +1968-07-01,99.58,100.33,98.77,99.4,11280000 +1968-06-28,99.98,100.63,98.91,99.58,12040000 +1968-06-27,100.08,101.01,99.11,99.98,15370000 +1968-06-25,100.39,101.1,99.28,100.08,13200000 +1968-06-24,100.66,101.48,99.66,100.39,12320000 +1968-06-21,101.51,101.59,99.8,100.66,13450000 +1968-06-20,99.99,101.6,99.52,101.51,16290000 +1968-06-18,100.13,101.09,99.43,99.99,13630000 +1968-06-17,101.13,101.71,99.43,100.13,12570000 +1968-06-14,101.25,101.82,99.98,101.13,14690000 +1968-06-13,101.66,102.84,100.55,101.25,21350000 +1968-06-11,101.41,102.4,100.74,101.66,15700000 +1968-06-10,101.27,102.25,100.42,101.41,14640000 +1968-06-07,100.65,101.89,100.24,101.27,17320000 +1968-06-06,99.89,101.59,99.5,100.65,16130000 +1968-06-05,100.38,101.13,99.26,99.89,15590000 +1968-06-04,99.99,101.26,99.32,100.38,18030000 +1968-06-03,98.72,100.62,98.72,99.99,14970000 +1968-05-31,97.92,99.4,97.66,98.68,13090000 +1968-05-29,97.62,98.74,97.01,97.92,14100000 +1968-05-28,96.99,98.2,96.41,97.62,13850000 +1968-05-27,97.15,97.81,96.29,96.99,12720000 +1968-05-24,96.97,97.73,96.21,97.15,13300000 +1968-05-23,97.18,97.79,96.38,96.97,12840000 +1968-05-22,96.93,98.17,96.47,97.18,14200000 +1968-05-21,96.45,97.52,95.92,96.93,13160000 +1968-05-20,96.9,97.41,95.8,96.45,11180000 +1968-05-17,97.6,97.81,96.11,96.9,11830000 +1968-05-16,98.07,98.69,97.05,97.6,13030000 +1968-05-15,98.12,98.79,97.32,98.07,13180000 +1968-05-14,98.19,98.85,97.33,98.12,13160000 +1968-05-13,98.5,99.1,97.52,98.19,11860000 +1968-05-10,98.39,99.3,97.76,98.5,11700000 +1968-05-09,98.91,99.47,97.68,98.39,12890000 +1968-05-08,98.9,99.74,98.25,98.91,13120000 +1968-05-07,98.35,99.59,97.86,98.9,13920000 +1968-05-06,98.66,99.11,97.27,98.35,12160000 +1968-05-03,98.59,100.19,97.98,98.66,17990000 +1968-05-02,97.97,99.18,97.53,98.59,14260000 +1968-05-01,97.46,98.61,96.84,97.97,14440000 +1968-04-30,97.97,98.17,96.58,97.46,14380000 +1968-04-29,97.21,98.61,96.81,97.97,12030000 +1968-04-26,96.62,97.83,96.22,97.21,13500000 +1968-04-25,96.92,97.48,95.68,96.62,14430000 +1968-04-24,96.62,97.81,95.98,96.92,14810000 +1968-04-23,95.68,97.48,95.68,96.62,14010000 +1968-04-22,95.85,96.07,94.22,95.32,11720000 +1968-04-19,97.08,97.08,95.15,95.85,14560000 +1968-04-18,96.81,97.89,96.12,97.08,15890000 +1968-04-17,96.62,97.4,95.76,96.81,14090000 +1968-04-16,96.59,97.54,95.72,96.62,15680000 +1968-04-15,96.53,97.36,95.33,96.59,14220000 +1968-04-11,95.67,96.93,94.81,96.53,14230000 +1968-04-10,94.95,97.11,94.74,95.67,20410000 +1968-04-08,93.29,95.45,93.11,94.95,13010000 +1968-04-05,93.84,94.51,92.67,93.29,12570000 +1968-04-04,93.47,94.59,92.63,93.84,14340000 +1968-04-03,92.64,95.13,92.24,93.47,19290000 +1968-04-02,92.48,93.44,91.39,92.64,14520000 +1968-04-01,91.11,93.55,91.11,92.48,17730000 +1968-03-29,89.57,90.92,89.21,90.2,9000000 +1968-03-28,89.66,90.4,89.05,89.57,8000000 +1968-03-27,88.93,90.2,88.88,89.66,8970000 +1968-03-26,88.33,89.5,88.1,88.93,8670000 +1968-03-25,88.42,88.88,87.65,88.33,6700000 +1968-03-22,88.33,89.14,87.5,88.42,9900000 +1968-03-21,88.98,89.48,88.05,88.33,8580000 +1968-03-20,88.99,89.65,88.48,88.98,7390000 +1968-03-19,89.59,90.05,88.61,88.99,7410000 +1968-03-18,89.11,91.09,89.11,89.59,10800000 +1968-03-15,88.32,89.75,87.61,89.1,11210000 +1968-03-14,89.75,89.75,87.81,88.32,11640000 +1968-03-13,90.23,90.71,89.4,90.03,8990000 +1968-03-12,90.13,90.78,89.39,90.23,9250000 +1968-03-11,89.03,90.56,88.81,90.13,9520000 +1968-03-08,89.1,89.57,88.23,89.03,7410000 +1968-03-07,89.26,89.98,88.44,89.1,8630000 +1968-03-06,87.72,89.76,87.64,89.26,9900000 +1968-03-05,87.92,88.72,86.99,87.72,11440000 +1968-03-04,89.11,89.33,87.52,87.92,10590000 +1968-03-01,89.36,89.82,88.58,89.11,8610000 +1968-02-29,90.08,90.24,88.93,89.36,7700000 +1968-02-28,90.53,91.19,89.71,90.08,8020000 +1968-02-27,90.18,90.91,89.56,90.53,7600000 +1968-02-26,90.89,91.08,89.67,90.18,7810000 +1968-02-23,91.24,91.8,90.28,90.89,8810000 +1968-02-21,91.24,91.87,90.54,91.24,9170000 +1968-02-20,90.31,91.34,89.95,91.24,8800000 +1968-02-19,89.96,90.87,89.42,90.31,7270000 +1968-02-16,90.3,90.62,89.28,89.96,9070000 +1968-02-15,90.3,90.3,90.3,90.3,9770000 +1968-02-14,89.07,90.6,88.66,90.14,11390000 +1968-02-13,89.86,90.46,86.73,89.07,10830000 +1968-02-09,90.9,91,89.23,89.86,11850000 +1968-02-08,92.06,92.4,90.6,90.9,9660000 +1968-02-07,91.9,92.74,91.48,92.06,8380000 +1968-02-06,91.87,92.52,91.15,91.9,8560000 +1968-02-05,92.27,92.72,91.24,91.87,8980000 +1968-02-02,92.56,93.44,91.69,92.27,10120000 +1968-02-01,92.24,93.14,91.57,92.56,10590000 +1968-01-31,92.89,93.26,91.27,92.24,9410000 +1968-01-30,93.35,93.71,92.18,92.89,10110000 +1968-01-29,93.45,94.38,92.71,93.35,9950000 +1968-01-26,93.3,94.34,92.77,93.45,9980000 +1968-01-25,93.17,94.11,91.96,93.3,12410000 +1968-01-24,93.66,94.12,92.45,93.17,10570000 +1968-01-23,94.03,94.66,92.88,93.66,11030000 +1968-01-22,95.24,95.4,93.55,94.03,10630000 +1968-01-19,95.56,96.22,94.6,95.24,11950000 +1968-01-18,95.64,96.66,95.01,95.56,13840000 +1968-01-17,95.82,96.41,94.78,95.64,12910000 +1968-01-16,96.42,96.91,95.32,95.82,12340000 +1968-01-15,96.72,97.46,95.85,96.42,12640000 +1968-01-12,96.62,97.44,95.87,96.72,13080000 +1968-01-11,96.52,97.82,95.88,96.62,13220000 +1968-01-10,96.5,97.26,95.66,96.52,11670000 +1968-01-09,96.62,97.84,95.89,96.5,13720000 +1968-01-08,95.94,97.4,95.54,96.62,14260000 +1968-01-05,95.36,96.66,94.97,95.94,11880000 +1968-01-04,95.67,96.23,94.31,95.36,13440000 +1968-01-03,96.11,96.95,95.04,95.67,12650000 +1968-01-02,96.47,97.33,95.31,96.11,11080000 +1967-12-29,95.89,96.9,95.85,96.47,14950000 +1967-12-28,95.91,96.65,94.91,95.89,12530000 +1967-12-27,95.26,96.42,94.82,95.91,12690000 +1967-12-26,95.2,96.02,94.61,95.26,9150000 +1967-12-22,95.38,96.11,94.61,95.2,9570000 +1967-12-21,95.15,96.25,94.69,95.38,11010000 +1967-12-20,94.63,95.75,94.17,95.15,11390000 +1967-12-19,94.77,95.41,94,94.63,10610000 +1967-12-18,95.03,95.88,94.17,94.77,10320000 +1967-12-15,95.47,96.2,94.51,95.03,11530000 +1967-12-14,95.34,96.35,94.85,95.47,12310000 +1967-12-13,95.01,96,94.58,95.34,12480000 +1967-12-12,95.12,95.78,94.34,95.01,10860000 +1967-12-11,95.42,95.99,94.5,95.12,10500000 +1967-12-08,95.53,96.25,94.78,95.42,10710000 +1967-12-07,95.64,96.67,95.04,95.53,12490000 +1967-12-06,95.23,96.16,94.1,95.64,11940000 +1967-12-05,95.1,96.27,94.52,95.23,12940000 +1967-12-04,94.5,95.68,94.09,95.1,11740000 +1967-12-01,94,94.95,93.41,94.5,9740000 +1967-11-30,94.47,94.94,93.49,94,8860000 +1967-11-29,94.49,95.51,93.85,94.47,11400000 +1967-11-28,94.17,95.08,93.57,94.49,11040000 +1967-11-27,93.9,94.8,93.32,94.17,10040000 +1967-11-24,93.65,94.46,92.74,93.9,9470000 +1967-11-22,93.1,94.41,92.7,93.65,12180000 +1967-11-21,91.65,93.71,91.64,93.1,12300000 +1967-11-20,92.38,92.38,90.09,91.65,12750000 +1967-11-17,92.6,93.62,92.02,92.82,10050000 +1967-11-16,91.76,93.28,91.5,92.6,10570000 +1967-11-15,91.39,92.25,90.44,91.76,10000000 +1967-11-14,91.97,92.49,90.81,91.39,10350000 +1967-11-13,92.21,93.23,91.46,91.97,10130000 +1967-11-10,91.59,92.84,91.29,92.21,9960000 +1967-11-09,91.14,92.25,90.61,91.59,8890000 +1967-11-08,91.48,93.07,90.8,91.14,12630000 +1967-11-06,91.78,92.23,90.39,91.48,10320000 +1967-11-03,92.34,92.9,91.33,91.78,8800000 +1967-11-02,92.71,93.69,91.85,92.34,10760000 +1967-11-01,93.3,94.21,92.45,92.71,10930000 +1967-10-31,94.79,95.25,93.29,93.3,12020000 +1967-10-30,94.96,95.67,94.14,94.79,10250000 +1967-10-27,94.94,95.79,94.31,94.96,9880000 +1967-10-26,94.52,95.56,93.99,94.94,9920000 +1967-10-25,94.42,95.18,93.47,94.52,10300000 +1967-10-24,94.96,95.98,94.05,94.42,11110000 +1967-10-23,95.38,95.69,93.92,94.96,9680000 +1967-10-20,95.43,96.12,94.62,95.38,9510000 +1967-10-19,95.25,96.46,94.86,95.43,11620000 +1967-10-18,95,95.82,94.34,95.25,10500000 +1967-10-17,95.25,95.92,94.19,95,10290000 +1967-10-16,96,96.55,94.85,95.25,9080000 +1967-10-13,95.75,96.69,95.16,96,9040000 +1967-10-12,96.37,96.7,95.32,95.75,7770000 +1967-10-11,96.84,97.34,95.7,96.37,11230000 +1967-10-10,97.51,98.15,96.38,96.84,12000000 +1967-10-09,97.26,98.25,96.7,97.51,11180000 +1967-10-06,96.67,97.83,96.34,97.26,9830000 +1967-10-05,96.43,97.25,95.89,96.67,8490000 +1967-10-04,96.65,97.47,95.94,96.43,11520000 +1967-10-03,96.32,97.23,95.75,96.65,10320000 +1967-10-02,96.71,97.25,95.82,96.32,9240000 +1967-09-29,96.79,97.37,96.06,96.71,9710000 +1967-09-28,96.79,97.59,96.19,96.79,10470000 +1967-09-27,96.76,97.54,96,96.79,8810000 +1967-09-26,97.59,98.2,96.4,96.76,10940000 +1967-09-25,97,98.31,96.74,97.59,10910000 +1967-09-22,96.75,97.61,96.11,97,11160000 +1967-09-21,96.13,97.5,95.67,96.75,11290000 +1967-09-20,96.17,96.84,95.39,96.13,10980000 +1967-09-19,96.53,97.35,95.84,96.17,11540000 +1967-09-18,96.27,97.31,95.73,96.53,11620000 +1967-09-15,96.2,96.94,95.47,96.27,10270000 +1967-09-14,95.99,97.4,95.59,96.2,12220000 +1967-09-13,94.99,96.62,94.8,95.99,12400000 +1967-09-12,94.54,95.48,94.01,94.99,9930000 +1967-09-11,94.36,95.26,93.88,94.54,9170000 +1967-09-08,94.33,95.04,93.7,94.36,9300000 +1967-09-07,94.39,94.95,93.7,94.33,8910000 +1967-09-06,94.21,95.06,93.72,94.39,9550000 +1967-09-05,93.68,94.7,93.36,94.21,8320000 +1967-09-01,93.64,94.21,93,93.68,7460000 +1967-08-31,93.07,94.19,92.84,93.64,8840000 +1967-08-30,92.88,93.67,92.43,93.07,7200000 +1967-08-29,92.64,93.58,92.17,92.88,6350000 +1967-08-28,92.7,93.31,92.01,92.64,6270000 +1967-08-25,93.09,93.38,92.04,92.7,7250000 +1967-08-24,93.61,94.28,92.77,93.09,7740000 +1967-08-23,93.74,94.15,92.77,93.61,8760000 +1967-08-22,94.25,94.72,93.35,93.74,7940000 +1967-08-21,94.78,95.22,93.79,94.25,8600000 +1967-08-18,94.63,95.4,94.16,94.78,8250000 +1967-08-17,94.55,95.33,94.11,94.63,8790000 +1967-08-16,94.77,95.15,93.93,94.55,8220000 +1967-08-15,94.64,95.54,94.18,94.77,8710000 +1967-08-14,95.15,95.4,94.02,94.64,7990000 +1967-08-11,95.53,95.98,94.62,95.15,8250000 +1967-08-10,95.78,96.67,95.05,95.53,9040000 +1967-08-09,95.69,96.47,95.11,95.78,10100000 +1967-08-08,95.58,96.28,95.04,95.69,8970000 +1967-08-07,95.83,96.43,95.02,95.58,10160000 +1967-08-04,95.66,96.54,95.15,95.83,11130000 +1967-08-03,95.78,96.36,94.42,95.66,13440000 +1967-08-02,95.37,96.64,95.03,95.78,13510000 +1967-08-01,94.75,95.84,94.2,95.37,12290000 +1967-07-31,94.49,95.51,94.01,94.75,10330000 +1967-07-28,94.35,95.23,93.77,94.49,10900000 +1967-07-27,94.06,95.19,93.51,94.35,12400000 +1967-07-26,93.24,94.71,93.12,94.06,11160000 +1967-07-25,93.73,94.56,93.03,93.24,9890000 +1967-07-24,94.04,94.68,92.91,93.73,9580000 +1967-07-21,93.85,94.92,93.24,94.04,11710000 +1967-07-20,93.65,94.49,93.01,93.85,11160000 +1967-07-19,93.5,94.4,92.83,93.65,12850000 +1967-07-18,92.75,94.05,92.3,93.5,12060000 +1967-07-17,92.74,93.53,92.1,92.75,10390000 +1967-07-14,92.42,93.35,91.87,92.74,10880000 +1967-07-13,92.4,93.17,91.82,92.42,10730000 +1967-07-12,92.48,93.1,91.62,92.4,11240000 +1967-07-11,92.05,93.16,91.58,92.48,12400000 +1967-07-10,91.69,92.8,91.11,92.05,12130000 +1967-07-07,91.32,92.28,90.76,91.69,11540000 +1967-07-06,91.36,92.03,90.64,91.32,10170000 +1967-07-05,90.91,91.91,90.56,91.36,9170000 +1967-07-03,90.64,91.32,90.12,90.91,6040000 +1967-06-30,90.64,90.64,90.64,90.64,7850000 +1967-06-29,90.85,90.85,90.85,90.85,9940000 +1967-06-28,91.31,91.31,91.31,91.31,9310000 +1967-06-27,91.3,91.3,91.3,91.3,8780000 +1967-06-26,91.64,91.64,91.64,91.64,9040000 +1967-06-23,92,92,92,92,9130000 +1967-06-22,91.97,91.97,91.97,91.97,9550000 +1967-06-21,92.2,92.2,92.2,92.2,9760000 +1967-06-20,92.48,92.48,92.48,92.48,10350000 +1967-06-19,92.51,92.51,92.51,92.51,8570000 +1967-06-16,92.49,93.28,91.98,92.54,10740000 +1967-06-15,92.4,93.26,91.76,92.49,11240000 +1967-06-14,92.62,93.21,91.81,92.4,10960000 +1967-06-13,92.04,93.27,91.65,92.62,11570000 +1967-06-12,91.56,92.66,91.12,92.04,10230000 +1967-06-09,91.4,92.26,90.77,91.56,9650000 +1967-06-08,90.91,91.78,90.24,91.4,8300000 +1967-06-07,90.23,91.75,89.92,90.91,10170000 +1967-06-06,88.48,90.59,88.48,90.23,9230000 +1967-06-05,89.56,89.56,87.19,88.43,11110000 +1967-06-02,90.23,90.9,89.27,89.79,8070000 +1967-06-01,89.08,90.76,88.81,90.23,9040000 +1967-05-31,90.39,90.39,88.71,89.08,8870000 +1967-05-29,90.98,91.22,89.92,90.49,6590000 +1967-05-26,91.19,91.7,90.34,90.98,7810000 +1967-05-25,90.18,91.84,90.04,91.19,8960000 +1967-05-24,91.23,91.36,89.68,90.18,10290000 +1967-05-23,91.67,92.07,90.58,91.23,9810000 +1967-05-22,92.07,92.4,90.83,91.67,9600000 +1967-05-19,92.53,92.86,91.4,92.07,10560000 +1967-05-18,92.78,93.3,91.98,92.53,10290000 +1967-05-17,93.14,93.75,92.34,92.78,9560000 +1967-05-16,92.71,93.85,92.19,93.14,10700000 +1967-05-15,93.48,93.75,92.27,92.71,8320000 +1967-05-12,93.75,94.45,92.94,93.48,10470000 +1967-05-11,93.35,94.37,92.9,93.75,10320000 +1967-05-10,93.6,94.04,92.51,93.35,10410000 +1967-05-09,94.58,95.25,93.28,93.6,10830000 +1967-05-08,94.44,95.22,93.71,94.58,10330000 +1967-05-05,94.32,95.14,93.64,94.44,10630000 +1967-05-04,93.91,94.92,93.41,94.32,12850000 +1967-05-03,93.67,94.48,92.94,93.91,11550000 +1967-05-02,93.84,94.42,93.06,93.67,10260000 +1967-05-01,94.01,94.6,93.08,93.84,9410000 +1967-04-28,93.81,94.77,93.33,94.01,11200000 +1967-04-27,93.02,94.25,92.41,93.81,10250000 +1967-04-26,93.11,93.99,92.44,93.02,10560000 +1967-04-25,92.62,93.57,92.01,93.11,10420000 +1967-04-24,92.3,93.45,91.78,92.62,10250000 +1967-04-21,92.11,92.9,91.48,92.3,10210000 +1967-04-20,91.94,92.61,91.21,92.11,9690000 +1967-04-19,91.86,92.73,91.25,91.94,10860000 +1967-04-18,91.07,92.31,90.7,91.86,10500000 +1967-04-17,90.43,91.78,90.18,91.07,9070000 +1967-04-14,89.46,91.08,89.26,90.43,8810000 +1967-04-13,88.78,89.86,88.49,89.46,7610000 +1967-04-12,88.88,89.54,88.36,88.78,7750000 +1967-04-11,88.24,89.34,87.92,88.88,7710000 +1967-04-10,89.32,89.32,87.86,88.24,8110000 +1967-04-07,89.94,90.6,88.96,89.36,9090000 +1967-04-06,89.79,90.74,89.44,89.94,9470000 +1967-04-05,89.22,90.31,88.92,89.79,8810000 +1967-04-04,89.24,89.93,88.45,89.22,8750000 +1967-04-03,90.2,90.37,88.76,89.24,8530000 +1967-03-31,90.7,91.15,89.75,90.2,8130000 +1967-03-30,90.73,91.32,90.06,90.7,8340000 +1967-03-29,90.91,91.45,90.17,90.73,8430000 +1967-03-28,90.87,91.62,90.23,90.91,8940000 +1967-03-27,90.94,91.72,90.19,90.87,9260000 +1967-03-23,90.25,91.51,90.04,90.94,9500000 +1967-03-22,90,90.7,89.17,90.25,8820000 +1967-03-21,90.2,91.05,89.52,90,9820000 +1967-03-20,90.25,90.87,89.35,90.2,9040000 +1967-03-17,90.09,90.84,89.39,90.25,10020000 +1967-03-16,89.19,90.66,89.09,90.09,12170000 +1967-03-15,88.35,89.6,88,89.19,10830000 +1967-03-14,88.43,89.07,87.58,88.35,10260000 +1967-03-13,88.89,89.41,87.93,88.43,9910000 +1967-03-10,88.53,90.37,88.46,88.89,14900000 +1967-03-09,88.27,89.04,87.7,88.53,10480000 +1967-03-08,88.16,89.1,87.69,88.27,11070000 +1967-03-07,88.1,88.74,87.34,88.16,9810000 +1967-03-06,88.29,89.08,87.46,88.1,10400000 +1967-03-03,88.16,89,87.51,88.29,11100000 +1967-03-02,87.68,88.85,87.39,88.16,11900000 +1967-03-01,86.78,88.36,86.67,87.68,11510000 +1967-02-28,86.46,87.26,85.61,86.78,9970000 +1967-02-27,87.41,87.61,85.68,86.46,10210000 +1967-02-24,87.45,88.16,86.76,87.41,9830000 +1967-02-23,87.34,88,86.64,87.45,10010000 +1967-02-21,87.4,88.01,86.8,87.34,9030000 +1967-02-20,87.89,88.13,86.65,87.4,8640000 +1967-02-17,87.86,88.4,87.25,87.89,8530000 +1967-02-16,88.27,88.8,87.43,87.86,8490000 +1967-02-15,88.17,89,87.62,88.27,10480000 +1967-02-14,87.58,88.74,87.15,88.17,9760000 +1967-02-13,87.63,88.19,86.95,87.58,7570000 +1967-02-10,87.36,88.19,86.79,87.63,8850000 +1967-02-09,87.72,88.57,86.99,87.36,10970000 +1967-02-08,86.95,88.25,86.64,87.72,11220000 +1967-02-07,87.18,87.52,86.48,86.95,6400000 +1967-02-06,87.36,87.98,86.61,87.18,10680000 +1967-02-03,86.73,87.97,86.51,87.36,12010000 +1967-02-02,86.43,87.31,85.87,86.73,10720000 +1967-02-01,86.61,87.04,85.68,86.43,9580000 +1967-01-31,86.66,87.46,86.06,86.61,11540000 +1967-01-30,86.16,87.35,85.84,86.66,10250000 +1967-01-27,85.81,86.76,85.34,86.16,9690000 +1967-01-26,85.85,86.66,84.87,85.81,10630000 +1967-01-25,86.51,87.02,85.47,85.85,10260000 +1967-01-24,86.39,87,85.29,86.51,10430000 +1967-01-23,86.07,88.17,85.64,86.39,10830000 +1967-01-20,85.82,86.47,85.07,86.07,9530000 +1967-01-19,85.79,86.61,85.17,85.82,10230000 +1967-01-18,85.24,86.36,84.9,85.79,11390000 +1967-01-17,84.31,85.81,84.03,85.24,11590000 +1967-01-16,84.53,85.28,83.73,84.31,10280000 +1967-01-13,83.91,84.9,83.1,84.53,10000000 +1967-01-12,83.47,84.8,83.11,83.91,12830000 +1967-01-11,82.81,83.92,81.37,83.47,13230000 +1967-01-10,82.81,83.54,82.22,82.81,8120000 +1967-01-09,82.18,83.31,81.78,82.81,9180000 +1967-01-06,81.6,82.79,81.32,82.18,7830000 +1967-01-05,80.55,81.93,80.5,81.6,7320000 +1967-01-04,80.38,81.01,79.43,80.55,6150000 +1967-01-03,80.33,81.61,79.59,80.38,6100000 +1966-12-30,80.37,81.14,79.66,80.33,11330000 +1966-12-29,80.61,81.08,79.84,80.37,7900000 +1966-12-28,81,81.67,80.29,80.61,7160000 +1966-12-27,81.47,81.84,80.55,81,6280000 +1966-12-23,81.69,82.22,80.97,81.47,7350000 +1966-12-22,81.38,82.34,81,81.69,8560000 +1966-12-21,80.96,81.91,80.42,81.38,7690000 +1966-12-20,81.27,81.69,80.31,80.96,6830000 +1966-12-19,81.58,82.06,80.56,81.27,7340000 +1966-12-16,81.64,82.21,80.94,81.58,6980000 +1966-12-15,82.64,82.89,81.2,81.64,7150000 +1966-12-14,82.73,83.35,81.97,82.64,7470000 +1966-12-13,83,83.88,82.28,82.73,9650000 +1966-12-12,82.14,83.54,81.94,83,9530000 +1966-12-09,82.05,82.68,81.33,82.14,7650000 +1966-12-08,81.72,82.72,81.34,82.05,8370000 +1966-12-07,80.84,82.19,80.59,81.72,8980000 +1966-12-06,80.24,81.29,79.95,80.84,7670000 +1966-12-05,80.13,80.81,79.6,80.24,6470000 +1966-12-02,80.08,81.29,79.49,80.13,6230000 +1966-12-01,80.45,81.04,79.66,80.08,8480000 +1966-11-30,80.42,80.9,79.62,80.45,7230000 +1966-11-29,80.71,81.16,79.94,80.42,7320000 +1966-11-28,80.85,81.38,79.96,80.71,7630000 +1966-11-25,80.21,81.37,79.83,80.85,6810000 +1966-11-23,79.67,80.85,79.39,80.21,7350000 +1966-11-22,80.09,80.32,78.89,79.67,6430000 +1966-11-21,81.09,81.09,79.51,80.09,7450000 +1966-11-18,81.8,82.05,80.79,81.26,6900000 +1966-11-17,82.37,82.8,81.24,81.8,8900000 +1966-11-16,81.69,83.01,81.55,82.37,10350000 +1966-11-15,81.37,82.07,80.82,81.69,7190000 +1966-11-14,81.94,82.18,80.81,81.37,6540000 +1966-11-11,81.89,82.36,81.27,81.94,6690000 +1966-11-10,81.38,82.43,81,81.89,8870000 +1966-11-09,80.73,81.9,80.46,81.38,8390000 +1966-11-07,80.81,81.48,80.16,80.73,6120000 +1966-11-04,80.56,81.21,79.64,80.81,6530000 +1966-11-03,80.88,81.35,79.98,80.56,5860000 +1966-11-02,80.81,81.68,80.3,80.88,6740000 +1966-11-01,80.2,81.18,79.79,80.81,6480000 +1966-10-31,80.24,80.82,79.34,80.2,5860000 +1966-10-28,80.23,80.91,79.49,80.24,6420000 +1966-10-27,79.58,80.72,79.28,80.23,6670000 +1966-10-26,78.9,80.29,78.7,79.58,6760000 +1966-10-25,78.42,79.22,77.56,78.9,6190000 +1966-10-24,78.19,79.2,77.73,78.42,5780000 +1966-10-21,77.84,78.62,77.16,78.19,5690000 +1966-10-20,78.05,78.96,77.26,77.84,6840000 +1966-10-19,78.68,79.34,77.54,78.05,6460000 +1966-10-18,77.47,79.08,77.35,78.68,7180000 +1966-10-17,76.6,78.41,76.48,77.47,5570000 +1966-10-14,76.89,77.8,76.01,76.6,5610000 +1966-10-13,77.04,78.45,76.22,76.89,8680000 +1966-10-12,74.91,77.26,74.37,77.04,6910000 +1966-10-11,74.53,76.2,74.22,74.91,8430000 +1966-10-10,73.2,74.97,72.28,74.53,9630000 +1966-10-07,74.05,74.67,72.77,73.2,8140000 +1966-10-06,74.69,75.09,73.47,74.05,8110000 +1966-10-05,75.1,76.1,74.31,74.69,5880000 +1966-10-04,74.9,75.76,73.91,75.1,8910000 +1966-10-03,76.56,76.98,74.71,74.9,6490000 +1966-09-30,76.31,77.09,75.45,76.56,6170000 +1966-09-29,77.11,77.28,75.85,76.31,6110000 +1966-09-28,78.1,78.36,76.7,77.11,5990000 +1966-09-27,77.86,79.1,77.56,78.1,6300000 +1966-09-26,77.67,78.34,76.88,77.86,4960000 +1966-09-23,77.94,78.43,77.15,77.67,4560000 +1966-09-22,77.71,78.41,76.81,77.94,5760000 +1966-09-21,79.04,79.15,77.52,77.71,5360000 +1966-09-20,79.59,79.9,78.57,79.04,4560000 +1966-09-19,79.99,80.5,79.02,79.59,4920000 +1966-09-16,80.08,80.81,79.33,79.99,5150000 +1966-09-15,79.13,80.6,78.87,80.08,6140000 +1966-09-14,78.32,79.43,77.73,79.13,6250000 +1966-09-13,77.91,79.16,77.66,78.32,6870000 +1966-09-12,76.47,78.34,76.47,77.91,6780000 +1966-09-09,76.05,76.94,75.43,76.29,5280000 +1966-09-08,76.37,76.95,75.03,76.05,6660000 +1966-09-07,76.96,77.26,75.77,76.37,5530000 +1966-09-06,77.42,78.16,76.55,76.96,4350000 +1966-09-02,77.7,78.2,76.27,77.42,6080000 +1966-09-01,77.1,78.5,76.66,77.7,6250000 +1966-08-31,75.98,78.06,75.98,77.1,8690000 +1966-08-30,74.53,76.46,73.91,75.86,11230000 +1966-08-29,76.24,76.24,74.18,74.53,10900000 +1966-08-26,77.85,77.85,76.1,76.41,8190000 +1966-08-25,79.07,79.79,77.8,78.06,6760000 +1966-08-24,78.11,79.63,77.92,79.07,7050000 +1966-08-23,78.24,79.24,77.05,78.11,9830000 +1966-08-22,79.62,79.88,77.58,78.24,8690000 +1966-08-19,80.16,80.78,79.24,79.62,7070000 +1966-08-18,81.18,81.38,79.6,80.16,7000000 +1966-08-17,81.63,81.9,80.53,81.18,6630000 +1966-08-16,82.71,82.71,81.26,81.63,6130000 +1966-08-15,83.17,83.69,82.39,82.74,5680000 +1966-08-12,83.02,83.88,82.57,83.17,6230000 +1966-08-11,83.11,83.53,82.34,83.02,5700000 +1966-08-10,83.49,83.83,82.69,83.11,5290000 +1966-08-09,83.75,84.36,83.04,83.49,6270000 +1966-08-08,84,84.31,82.97,83.75,4900000 +1966-08-05,83.93,84.7,83.43,84,5500000 +1966-08-04,83.15,84.54,83.07,83.93,6880000 +1966-08-03,82.33,83.71,82.3,83.15,6220000 +1966-08-02,82.31,83.04,81.77,82.33,5710000 +1966-08-01,83.5,83.5,81.98,82.31,5880000 +1966-07-29,83.77,84.3,83.1,83.6,5150000 +1966-07-28,84.1,84.76,83.44,83.77,5680000 +1966-07-27,83.7,84.83,83.5,84.1,6070000 +1966-07-26,83.83,84.67,83.05,83.7,7610000 +1966-07-25,85.41,85.57,83.56,83.83,7050000 +1966-07-22,85.52,86.11,84.93,85.41,6540000 +1966-07-21,85.51,86.24,84.77,85.52,6200000 +1966-07-20,86.33,86.64,85.26,85.51,5470000 +1966-07-19,86.99,87.17,85.75,86.33,5960000 +1966-07-18,87.08,87.59,86.42,86.99,5110000 +1966-07-15,86.82,87.68,86.44,87.08,6090000 +1966-07-14,86.3,87.34,85.85,86.82,5950000 +1966-07-13,86.88,87.06,85.83,86.3,5580000 +1966-07-12,87.45,87.78,86.45,86.88,5180000 +1966-07-11,87.61,88.19,86.97,87.45,6200000 +1966-07-08,87.38,88.04,86.85,87.61,6100000 +1966-07-07,87.06,88.02,86.67,87.38,7200000 +1966-07-06,85.82,87.38,85.57,87.06,6860000 +1966-07-05,85.61,86.41,85.09,85.82,4610000 +1966-07-01,84.74,86.08,84.74,85.61,5200000 +1966-06-30,84.86,85.37,83.75,84.74,7250000 +1966-06-29,85.67,85.98,84.52,84.86,6020000 +1966-06-28,86.08,86.43,85,85.67,6280000 +1966-06-27,86.58,87.31,85.77,86.08,5330000 +1966-06-24,86.5,87.31,85.68,86.58,7140000 +1966-06-23,86.85,87.73,86.11,86.5,7930000 +1966-06-22,86.71,87.38,86.15,86.85,7800000 +1966-06-21,86.48,87.28,86.07,86.71,6860000 +1966-06-20,86.51,87.03,85.84,86.48,5940000 +1966-06-17,86.47,87.11,85.89,86.51,6580000 +1966-06-16,86.73,87.18,85.88,86.47,6870000 +1966-06-15,87.07,87.74,86.33,86.73,8520000 +1966-06-14,86.83,87.57,86.02,87.07,7600000 +1966-06-13,86.44,87.59,86.2,86.83,7600000 +1966-06-10,85.5,86.97,85.32,86.44,8240000 +1966-06-09,84.93,85.98,84.56,85.5,5810000 +1966-06-08,84.83,85.43,84.31,84.93,4580000 +1966-06-07,85.42,85.54,84.25,84.83,5040000 +1966-06-06,86.06,86.28,85.03,85.42,4260000 +1966-06-03,85.96,86.55,85.43,86.06,4430000 +1966-06-02,86.1,86.85,85.55,85.96,5080000 +1966-06-01,86.13,86.65,85.28,86.1,5290000 +1966-05-31,87.33,87.65,85.8,86.13,5770000 +1966-05-27,87.07,87.42,86.43,87.33,4790000 +1966-05-26,87.07,87.88,86.54,87.07,6080000 +1966-05-25,86.77,87.48,86.05,87.07,5820000 +1966-05-24,86.2,87.7,86.19,86.77,7210000 +1966-05-23,85.43,86.91,85.29,86.2,7080000 +1966-05-20,85.02,85.79,84.21,85.43,6430000 +1966-05-19,85.12,86.33,84.54,85.02,8640000 +1966-05-18,83.72,85.64,83.72,85.12,9310000 +1966-05-17,84.41,85.03,83.18,83.63,9870000 +1966-05-16,85.47,86.04,83.9,84.41,9260000 +1966-05-13,86.23,86.31,84.77,85.47,8970000 +1966-05-12,87.23,87.49,85.72,86.23,8210000 +1966-05-11,87.08,88.38,86.84,87.23,7470000 +1966-05-10,86.32,87.88,86.12,87.08,9050000 +1966-05-09,87.84,87.96,85.92,86.32,9290000 +1966-05-06,87.93,88.52,86.24,87.84,13110000 +1966-05-05,89.39,89.77,87.6,87.93,10100000 +1966-05-04,89.85,90.11,88.54,89.39,9740000 +1966-05-03,90.9,91.1,89.46,89.85,8020000 +1966-05-02,91.06,91.75,90.43,90.9,7070000 +1966-04-29,91.13,91.86,90.57,91.06,7220000 +1966-04-28,91.76,91.92,90.24,91.13,8310000 +1966-04-27,91.99,92.49,91.1,91.76,7950000 +1966-04-26,92.08,92.77,91.47,91.99,7540000 +1966-04-25,92.27,92.86,91.41,92.08,7270000 +1966-04-22,92.42,92.87,91.6,92.27,8650000 +1966-04-21,92.08,93.02,91.78,92.42,9560000 +1966-04-20,91.57,92.75,91.34,92.08,10530000 +1966-04-19,91.58,92.31,90.89,91.57,8820000 +1966-04-18,91.99,92.59,91.09,91.58,9150000 +1966-04-15,91.87,92.75,91.28,91.99,10270000 +1966-04-14,91.54,92.8,91.12,91.87,12980000 +1966-04-13,91.45,92.81,90.73,91.54,10440000 +1966-04-12,91.79,92.51,90.92,91.45,10500000 +1966-04-11,91.76,92.6,91.08,91.79,9310000 +1966-04-07,91.56,92.42,90.99,91.76,9650000 +1966-04-06,91.31,92.1,90.77,91.56,9040000 +1966-04-05,90.76,92.04,90.47,91.31,10560000 +1966-04-04,89.94,91.33,89.92,90.76,9360000 +1966-04-01,89.23,90.37,88.96,89.94,9050000 +1966-03-31,88.78,89.7,88.47,89.23,6690000 +1966-03-30,89.27,89.57,88.31,88.78,7980000 +1966-03-29,89.62,90.04,88.63,89.27,8300000 +1966-03-28,89.54,90.41,89.15,89.62,8640000 +1966-03-25,89.29,90.14,88.96,89.54,7750000 +1966-03-24,89.13,89.8,88.68,89.29,7880000 +1966-03-23,89.46,89.8,88.69,89.13,6720000 +1966-03-22,89.2,90.28,89.01,89.46,8910000 +1966-03-21,88.53,89.73,88.4,89.2,7230000 +1966-03-18,88.17,89.23,87.82,88.53,6450000 +1966-03-17,87.86,88.6,87.45,88.17,5460000 +1966-03-16,87.35,88.55,87.09,87.86,7330000 +1966-03-15,87.85,88.2,86.69,87.35,9440000 +1966-03-14,88.85,88.92,87.56,87.85,7400000 +1966-03-11,88.96,89.63,88.3,88.85,7000000 +1966-03-10,88.96,90.14,88.36,88.96,10310000 +1966-03-09,88.18,89.21,87.96,88.96,7980000 +1966-03-08,88.04,89,87.17,88.18,10120000 +1966-03-07,89.24,89.39,87.67,88.04,9370000 +1966-03-04,89.47,90.25,88.72,89.24,9000000 +1966-03-03,89.15,90.03,88.26,89.47,9900000 +1966-03-02,90.06,90.65,88.7,89.15,10470000 +1966-03-01,91.22,91.65,89.76,90.06,11030000 +1966-02-28,91.14,91.95,90.65,91.22,9910000 +1966-02-25,90.89,91.88,90.43,91.14,8140000 +1966-02-24,91.48,91.81,90.45,90.89,7860000 +1966-02-23,91.87,92.21,90.99,91.48,8080000 +1966-02-21,92.41,92.83,91.35,91.87,8510000 +1966-02-18,92.66,93.14,91.8,92.41,8470000 +1966-02-17,93.16,93.58,92.11,92.66,9330000 +1966-02-16,93.17,93.74,92.63,93.16,9180000 +1966-02-15,93.53,94.04,92.67,93.17,8750000 +1966-02-14,93.81,94.4,93.15,93.53,8360000 +1966-02-11,93.83,94.52,93.25,93.81,8150000 +1966-02-10,94.06,94.7,93.32,93.83,9790000 +1966-02-09,93.55,94.72,93.29,94.06,9760000 +1966-02-08,93.59,94.29,92.58,93.55,10560000 +1966-02-07,93.26,94.22,92.85,93.59,8000000 +1966-02-04,92.65,93.7,92.33,93.26,7560000 +1966-02-03,92.53,93.67,92.11,92.65,8160000 +1966-02-02,92.16,92.91,91.32,92.53,8130000 +1966-02-01,92.88,93.36,91.61,92.16,9090000 +1966-01-31,93.31,93.77,92.46,92.88,7800000 +1966-01-28,93.67,94.15,92.84,93.31,9000000 +1966-01-27,93.7,94.34,93.09,93.67,8970000 +1966-01-26,93.85,94.53,93.18,93.7,9910000 +1966-01-25,93.71,94.56,93.24,93.85,9300000 +1966-01-24,93.47,94.41,93.07,93.71,8780000 +1966-01-21,93.36,93.97,92.6,93.47,9180000 +1966-01-20,93.69,94.33,92.87,93.36,8670000 +1966-01-19,93.95,94.62,93.16,93.69,10230000 +1966-01-18,93.77,94.64,93.23,93.95,9790000 +1966-01-17,93.5,94.46,93.1,93.77,9430000 +1966-01-14,93.36,94.14,92.98,93.5,9210000 +1966-01-13,93.19,94,92.68,93.36,8860000 +1966-01-12,93.41,93.98,92.8,93.19,8530000 +1966-01-11,93.33,94.05,92.85,93.41,8910000 +1966-01-10,93.14,93.94,92.75,93.33,7720000 +1966-01-07,93.06,93.64,92.47,93.14,7600000 +1966-01-06,92.85,93.65,92.51,93.06,7880000 +1966-01-05,92.26,93.33,91.99,92.85,9650000 +1966-01-04,92.18,93.04,91.68,92.26,7540000 +1966-01-03,92.43,92.87,91.63,92.18,5950000 +1965-12-31,92.2,93.05,91.82,92.43,7240000 +1965-12-30,91.81,92.68,91.52,92.2,7060000 +1965-12-29,91.53,92.39,91.14,91.81,7610000 +1965-12-28,91.52,92.13,90.63,91.53,7280000 +1965-12-27,92.19,92.71,91.28,91.52,5950000 +1965-12-23,92.29,92.89,91.58,92.19,6870000 +1965-12-22,92.01,93.07,91.53,92.29,9720000 +1965-12-21,91.65,92.59,91.24,92.01,8230000 +1965-12-20,92.08,92.35,91.09,91.65,7350000 +1965-12-17,92.12,92.76,91.51,92.08,9490000 +1965-12-16,92.02,92.95,91.53,92.12,9950000 +1965-12-15,91.88,92.67,91.3,92.02,9560000 +1965-12-14,91.83,92.59,91.35,91.88,9920000 +1965-12-13,91.8,92.45,91.27,91.83,8660000 +1965-12-10,91.56,92.28,91.14,91.8,8740000 +1965-12-09,91.28,92.06,90.87,91.56,9150000 +1965-12-08,91.39,92.24,90.84,91.28,10120000 +1965-12-07,90.59,92,90.45,91.39,9340000 +1965-12-06,91.2,91.2,89.2,90.59,11440000 +1965-12-03,91.21,91.8,90.53,91.27,8160000 +1965-12-02,91.5,91.95,90.69,91.21,9070000 +1965-12-01,91.61,92.26,91.02,91.5,10140000 +1965-11-30,91.8,92.14,90.81,91.61,8990000 +1965-11-29,92.03,92.6,91.37,91.8,8760000 +1965-11-26,91.94,92.65,91.39,92.03,6970000 +1965-11-24,91.78,92.5,91.14,91.94,7870000 +1965-11-23,91.64,92.24,91.15,91.78,7150000 +1965-11-22,92.24,92.48,91.16,91.64,6370000 +1965-11-19,92.22,92.88,91.73,92.24,6850000 +1965-11-18,92.6,92.94,91.72,92.22,7040000 +1965-11-17,92.41,93.28,91.85,92.6,9120000 +1965-11-16,92.63,93.13,91.9,92.41,8380000 +1965-11-15,92.55,93.3,92.04,92.63,8310000 +1965-11-12,92.11,93.07,91.83,92.55,7780000 +1965-11-11,91.83,92.37,91.31,92.11,5430000 +1965-11-10,91.93,92.4,91.35,91.83,4860000 +1965-11-09,92.23,92.65,91.47,91.93,6680000 +1965-11-08,92.37,92.97,91.63,92.23,7000000 +1965-11-05,92.46,92.92,91.78,92.37,7310000 +1965-11-04,92.31,93.07,91.9,92.46,8380000 +1965-11-03,92.23,92.79,91.62,92.31,7520000 +1965-11-01,92.42,92.92,91.73,92.23,6340000 +1965-10-29,92.21,92.94,91.83,92.42,7240000 +1965-10-28,92.51,92.95,91.6,92.21,7230000 +1965-10-27,92.2,93.19,91.95,92.51,7670000 +1965-10-26,91.67,92.63,91.36,92.2,6750000 +1965-10-25,91.98,92.72,91.34,91.67,7090000 +1965-10-22,91.94,92.74,91.54,91.98,8960000 +1965-10-21,91.78,92.51,91.42,91.94,9170000 +1965-10-20,91.8,92.26,91.12,91.78,8200000 +1965-10-19,91.68,92.45,91.35,91.8,8620000 +1965-10-18,91.38,92.28,91.06,91.68,8180000 +1965-10-15,91.19,92.09,90.76,91.38,7470000 +1965-10-14,91.34,91.9,90.71,91.19,8580000 +1965-10-13,91.35,91.81,90.73,91.34,9470000 +1965-10-12,91.37,91.94,90.83,91.35,9470000 +1965-10-11,90.85,91.84,90.73,91.37,9600000 +1965-10-08,90.47,91.31,90.3,90.85,7670000 +1965-10-07,90.54,91.09,90.09,90.47,6670000 +1965-10-06,90.63,90.94,89.74,90.54,6010000 +1965-10-05,90.08,91.02,89.92,90.63,6980000 +1965-10-04,89.9,90.56,89.47,90.08,5590000 +1965-10-01,89.96,90.48,89.3,89.9,7470000 +1965-09-30,90.02,90.71,89.51,89.96,8670000 +1965-09-29,90.43,91.11,89.56,90.02,10600000 +1965-09-28,90.65,91.13,89.83,90.43,8750000 +1965-09-27,90.65,90.65,90.65,90.65,6820000 +1965-09-24,89.86,90.47,89.13,90.02,7810000 +1965-09-23,90.22,90.78,89.43,89.86,9990000 +1965-09-22,89.81,90.67,89.45,90.22,8290000 +1965-09-21,90.08,90.66,89.43,89.81,7750000 +1965-09-20,90.05,90.67,89.51,90.08,7040000 +1965-09-17,90.02,90.47,89.32,90.05,6610000 +1965-09-16,90.02,90.02,90.02,90.02,7410000 +1965-09-15,89.03,89.96,88.71,89.52,6220000 +1965-09-14,89.38,90.01,88.69,89.03,7830000 +1965-09-13,89.12,89.91,88.77,89.38,7020000 +1965-09-10,88.89,89.85,88.41,89.12,6650000 +1965-09-09,88.66,89.46,88.35,88.89,7360000 +1965-09-08,88.36,89.08,87.93,88.66,6240000 +1965-09-07,88.06,88.77,87.76,88.36,5750000 +1965-09-03,87.65,88.41,87.52,88.06,6010000 +1965-09-02,87.17,87.96,86.98,87.65,6470000 +1965-09-01,87.17,87.63,86.69,87.17,5890000 +1965-08-31,87.21,87.79,86.78,87.17,5170000 +1965-08-30,87.2,87.64,86.76,87.21,4400000 +1965-08-27,87.14,87.74,86.81,87.2,5570000 +1965-08-26,86.81,87.52,86.4,87.14,6010000 +1965-08-25,86.71,87.27,86.33,86.81,6240000 +1965-08-24,86.56,87.19,86.22,86.71,4740000 +1965-08-23,86.69,87.1,86.22,86.56,4470000 +1965-08-20,86.79,87.14,86.21,86.69,4170000 +1965-08-19,86.99,87.48,86.49,86.79,5000000 +1965-08-18,87.04,87.57,86.63,86.99,5850000 +1965-08-17,86.87,87.42,86.48,87.04,4520000 +1965-08-16,86.77,87.43,86.46,86.87,5270000 +1965-08-13,86.38,87.14,86.09,86.77,5430000 +1965-08-12,86.13,86.75,85.85,86.38,5160000 +1965-08-11,85.87,86.48,85.64,86.13,5030000 +1965-08-10,85.86,86.31,85.45,85.87,4690000 +1965-08-09,86.07,86.54,85.52,85.86,4540000 +1965-08-06,85.79,86.4,85.42,86.07,4200000 +1965-08-05,85.79,86.28,85.43,85.79,4920000 +1965-08-04,85.46,86.12,85.22,85.79,4830000 +1965-08-03,85.42,85.81,84.8,85.46,4640000 +1965-08-02,85.25,85.87,84.87,85.42,4220000 +1965-07-30,84.68,85.64,84.64,85.25,5200000 +1965-07-29,84.03,85,83.79,84.68,4690000 +1965-07-28,83.87,84.52,83.3,84.03,4760000 +1965-07-27,84.05,84.59,83.58,83.87,4190000 +1965-07-26,84.07,84.47,83.49,84.05,3790000 +1965-07-23,83.85,84.52,83.57,84.07,3600000 +1965-07-22,84.07,84.45,83.53,83.85,3310000 +1965-07-21,84.55,84.84,83.76,84.07,4350000 +1965-07-20,85.63,85.85,84.39,84.55,4670000 +1965-07-19,85.69,86.04,85.21,85.63,3220000 +1965-07-16,85.72,86.14,85.26,85.69,3520000 +1965-07-15,85.87,86.47,85.44,85.72,4420000 +1965-07-14,85.59,86.23,85.18,85.87,4100000 +1965-07-13,85.69,86.01,85.12,85.59,3260000 +1965-07-12,85.71,86.08,85.24,85.69,3690000 +1965-07-09,85.39,86.11,85.11,85.71,4800000 +1965-07-08,84.67,85.6,84.29,85.39,4380000 +1965-07-07,84.99,85.14,84.28,84.67,3020000 +1965-07-06,85.16,85.63,84.57,84.99,3400000 +1965-07-02,84.48,85.4,84.13,85.16,4260000 +1965-07-01,84.12,84.64,83.57,84.48,4520000 +1965-06-30,82.97,84.63,82.97,84.12,6930000 +1965-06-29,81.6,83.04,80.73,82.41,10450000 +1965-06-28,83.06,83.34,81.36,81.6,7650000 +1965-06-25,83.56,83.83,82.6,83.06,5790000 +1965-06-24,84.67,84.73,83.3,83.56,5840000 +1965-06-23,85.21,85.59,84.52,84.67,3580000 +1965-06-22,85.05,85.7,84.76,85.21,3330000 +1965-06-21,85.34,85.64,84.53,85.05,3280000 +1965-06-18,85.74,86.1,84.9,85.34,4330000 +1965-06-17,85.2,86.22,84.98,85.74,5220000 +1965-06-16,84.58,85.79,84.58,85.2,6290000 +1965-06-15,84.01,84.86,83.01,84.49,8450000 +1965-06-14,85.12,85.68,83.64,84.01,5920000 +1965-06-11,84.73,85.68,84.5,85.12,5350000 +1965-06-10,85.04,85.82,84.1,84.73,7470000 +1965-06-09,85.93,86.37,84.75,85.04,7070000 +1965-06-08,86.88,87.1,85.74,85.93,4660000 +1965-06-07,87.11,87.45,86.04,86.88,4680000 +1965-06-04,86.9,87.46,86.36,87.11,4530000 +1965-06-03,87.09,88.05,86.58,86.9,5720000 +1965-06-02,87.87,87.87,86.25,87.09,6790000 +1965-06-01,88.42,88.8,87.88,88.72,4830000 +1965-05-28,87.84,88.68,87.58,88.42,4270000 +1965-05-27,88.3,88.36,87.24,87.84,5520000 +1965-05-26,88.6,89.22,88.04,88.3,5330000 +1965-05-25,88.09,88.96,87.82,88.6,4950000 +1965-05-24,88.75,88.89,87.75,88.09,4790000 +1965-05-21,89.18,89.41,88.4,88.75,4660000 +1965-05-20,89.67,89.86,88.74,89.18,5750000 +1965-05-19,89.46,90.15,89.17,89.67,5860000 +1965-05-18,89.54,89.84,88.87,89.46,5130000 +1965-05-17,90.1,90.44,89.24,89.54,4980000 +1965-05-14,90.27,90.66,89.63,90.1,5860000 +1965-05-13,89.94,90.68,89.68,90.27,6460000 +1965-05-12,89.55,90.31,89.3,89.94,6310000 +1965-05-11,89.66,89.98,89.05,89.55,5150000 +1965-05-10,89.85,90.22,89.22,89.66,5600000 +1965-05-07,89.92,90.3,89.33,89.85,5820000 +1965-05-06,89.71,90.57,89.39,89.92,6340000 +1965-05-05,89.51,90.4,89.14,89.71,6350000 +1965-05-04,89.23,89.89,88.82,89.51,5720000 +1965-05-03,89.11,89.68,88.62,89.23,5340000 +1965-04-30,88.93,89.44,88.5,89.11,5190000 +1965-04-29,89,89.43,88.47,88.93,5510000 +1965-04-28,89.04,89.48,88.51,89,5680000 +1965-04-27,88.89,89.64,88.71,89.04,6310000 +1965-04-26,88.88,89.29,88.3,88.89,5410000 +1965-04-23,88.78,89.41,88.48,88.88,5860000 +1965-04-22,88.3,89.13,88.12,88.78,5990000 +1965-04-21,88.46,88.82,87.7,88.3,5590000 +1965-04-20,88.51,89.07,88.02,88.46,6480000 +1965-04-19,88.15,88.9,87.9,88.51,5700000 +1965-04-15,88.24,88.63,87.55,88.15,5830000 +1965-04-14,88.04,88.65,87.71,88.24,6580000 +1965-04-13,87.94,88.48,87.54,88.04,6690000 +1965-04-12,87.56,88.36,87.31,87.94,6040000 +1965-04-09,87.04,87.87,86.86,87.56,6580000 +1965-04-08,86.55,87.35,86.34,87.04,5770000 +1965-04-07,86.5,86.88,86.14,86.55,4430000 +1965-04-06,86.53,86.91,86.08,86.5,4610000 +1965-04-05,86.53,87.08,86.14,86.53,4920000 +1965-04-02,86.32,86.89,86.08,86.53,5060000 +1965-04-01,86.16,86.73,85.87,86.32,4890000 +1965-03-31,86.2,86.64,85.83,86.16,4470000 +1965-03-30,86.03,86.53,85.69,86.2,4270000 +1965-03-29,86.2,86.66,85.65,86.03,4590000 +1965-03-26,86.84,87.06,85.96,86.2,5020000 +1965-03-25,87.09,87.5,86.55,86.84,5460000 +1965-03-24,86.93,87.55,86.68,87.09,5420000 +1965-03-23,86.83,87.34,86.45,86.93,4820000 +1965-03-22,86.84,87.34,86.41,86.83,4920000 +1965-03-19,86.81,87.37,86.43,86.84,5040000 +1965-03-18,87.02,87.48,86.5,86.81,4990000 +1965-03-17,87.13,87.51,86.63,87.02,5120000 +1965-03-16,87.24,87.61,86.67,87.13,5480000 +1965-03-15,87.21,87.92,86.82,87.24,6000000 +1965-03-12,86.9,87.65,86.6,87.21,6370000 +1965-03-11,86.54,87.29,86.17,86.9,5770000 +1965-03-10,86.69,87.07,86.2,86.54,5100000 +1965-03-09,86.83,87.27,86.33,86.69,5210000 +1965-03-08,86.8,87.28,86.31,86.83,5250000 +1965-03-05,86.98,87.26,86,86.8,6120000 +1965-03-04,87.26,87.72,86.63,86.98,7300000 +1965-03-03,87.4,87.83,86.88,87.26,6600000 +1965-03-02,87.25,87.79,86.84,87.4,5730000 +1965-03-01,87.43,87.93,86.92,87.25,5780000 +1965-02-26,87.2,87.84,86.81,87.43,5800000 +1965-02-25,87.17,87.7,86.7,87.2,6680000 +1965-02-24,86.64,87.72,86.43,87.17,7160000 +1965-02-23,86.21,87.01,86.03,86.64,5880000 +1965-02-19,86.05,86.67,85.71,86.21,5560000 +1965-02-18,85.77,86.48,85.47,86.05,6060000 +1965-02-17,85.67,86.25,85.25,85.77,5510000 +1965-02-16,86.07,86.31,85.33,85.67,5000000 +1965-02-15,86.17,86.86,85.75,86.07,5760000 +1965-02-12,85.54,86.48,85.54,86.17,4960000 +1965-02-11,86.46,86.89,85.4,85.54,5800000 +1965-02-10,87.24,87.7,86.2,86.46,7210000 +1965-02-09,86.95,87.64,86.7,87.24,5690000 +1965-02-08,87,87,85.95,86.95,6010000 +1965-02-05,87.57,87.98,86.9,87.29,5690000 +1965-02-04,87.63,88.06,87.06,87.57,6230000 +1965-02-03,87.55,88.01,87.07,87.63,6130000 +1965-02-02,87.58,87.94,87.03,87.55,5460000 +1965-02-01,87.56,88.01,87.05,87.58,5690000 +1965-01-29,87.48,88.19,87.18,87.56,6940000 +1965-01-28,87.23,87.88,86.89,87.48,6730000 +1965-01-27,86.94,87.67,86.7,87.23,6010000 +1965-01-26,86.86,87.45,86.51,86.94,5760000 +1965-01-25,86.74,87.27,86.39,86.86,5370000 +1965-01-22,86.52,87.15,86.2,86.74,5430000 +1965-01-21,86.6,86.9,86.02,86.52,4780000 +1965-01-20,86.63,87.1,86.26,86.6,5550000 +1965-01-19,86.49,87.09,86.15,86.63,5550000 +1965-01-18,86.21,87.15,85.99,86.49,5550000 +1965-01-15,85.84,86.52,85.6,86.21,5340000 +1965-01-14,85.84,86.38,85.41,85.84,5810000 +1965-01-13,85.61,86.27,85.35,85.84,6160000 +1965-01-12,85.4,85.98,85.13,85.61,5400000 +1965-01-11,85.37,85.81,84.9,85.4,5440000 +1965-01-08,85.26,85.84,84.91,85.37,5340000 +1965-01-07,84.89,85.62,84.66,85.26,5080000 +1965-01-06,84.63,85.38,84.45,84.89,4850000 +1965-01-05,84.23,85.02,84.02,84.63,4110000 +1965-01-04,84.75,85.15,83.77,84.23,3930000 +1964-12-31,84.3,85.18,84.18,84.75,6470000 +1964-12-30,83.81,84.63,83.63,84.3,5610000 +1964-12-29,84.07,84.35,83.38,83.81,4450000 +1964-12-28,84.15,84.58,83.7,84.07,3990000 +1964-12-24,84.15,84.59,83.74,84.15,3600000 +1964-12-23,84.33,84.76,83.79,84.15,4470000 +1964-12-22,84.38,84.88,83.94,84.33,4520000 +1964-12-21,84.29,84.91,84.11,84.38,4470000 +1964-12-18,83.9,84.65,83.73,84.29,4630000 +1964-12-17,83.55,84.24,83.34,83.9,4850000 +1964-12-16,83.22,83.94,83,83.55,4610000 +1964-12-15,83.45,83.79,82.65,83.22,5340000 +1964-12-14,83.66,84.17,83.1,83.45,4340000 +1964-12-11,83.45,84.05,83.09,83.66,4530000 +1964-12-10,83.46,83.96,82.98,83.45,4790000 +1964-12-09,84,84.24,83.24,83.46,5120000 +1964-12-08,84.33,84.71,83.69,84,4990000 +1964-12-07,84.35,85.03,84.04,84.33,4770000 +1964-12-04,84.35,84.35,84.35,84.35,4340000 +1964-12-03,83.79,84.74,83.71,84.18,4250000 +1964-12-02,83.55,84.23,83.12,83.79,4930000 +1964-12-01,84.42,84.56,83.36,83.55,4940000 +1964-11-30,85.16,85.41,84.1,84.42,4890000 +1964-11-27,85.44,85.68,84.55,85.16,4070000 +1964-11-25,85.73,86.18,85.1,85.44,4800000 +1964-11-24,86,86.12,85.15,85.73,5070000 +1964-11-23,86.28,86.59,85.48,86,4860000 +1964-11-20,86.18,86.8,85.73,86.28,5210000 +1964-11-19,86.22,86.57,85.6,86.18,5570000 +1964-11-18,86.03,86.8,85.73,86.22,6560000 +1964-11-17,85.65,86.55,85.48,86.03,5920000 +1964-11-16,85.21,85.94,84.88,85.65,4870000 +1964-11-13,85.19,85.68,84.76,85.21,4860000 +1964-11-12,85.08,85.63,84.75,85.19,5250000 +1964-11-11,84.84,85.3,84.49,85.08,3790000 +1964-11-10,85.19,85.55,84.49,84.84,5020000 +1964-11-09,85.23,85.72,84.93,85.19,4560000 +1964-11-06,85.16,85.55,84.65,85.23,4810000 +1964-11-05,85.14,85.62,84.72,85.16,4380000 +1964-11-04,85.18,85.9,84.8,85.14,4720000 +1964-11-02,84.86,85.54,84.51,85.18,4430000 +1964-10-30,84.73,85.22,84.41,84.86,4120000 +1964-10-29,84.69,85.15,84.36,84.73,4390000 +1964-10-28,85,85.37,84.43,84.69,4890000 +1964-10-27,85,85.4,84.61,85,4470000 +1964-10-26,85.14,85.7,84.65,85,5230000 +1964-10-23,84.94,85.42,84.57,85.14,3830000 +1964-10-22,85.1,85.44,84.51,84.94,4670000 +1964-10-21,85.18,85.64,84.77,85.1,5170000 +1964-10-20,84.93,85.57,84.56,85.18,5140000 +1964-10-19,84.83,85.36,84.47,84.93,5010000 +1964-10-16,84.25,85.1,84.1,84.83,5140000 +1964-10-15,84.79,84.99,83.65,84.25,6500000 +1964-10-14,84.96,85.29,84.5,84.79,4530000 +1964-10-13,85.24,85.57,84.63,84.96,5400000 +1964-10-12,85.22,85.58,84.88,85.24,4110000 +1964-10-09,85.04,85.6,84.72,85.22,5290000 +1964-10-08,84.8,85.4,84.47,85.04,5060000 +1964-10-07,84.79,85.25,84.42,84.8,5090000 +1964-10-06,84.74,85.24,84.37,84.79,4820000 +1964-10-05,84.36,85.25,84.2,84.74,4850000 +1964-10-02,84.08,84.64,83.71,84.36,4370000 +1964-10-01,84.18,84.53,83.74,84.08,4470000 +1964-09-30,84.24,84.66,83.86,84.18,4720000 +1964-09-29,84.28,84.8,83.84,84.24,5070000 +1964-09-28,84.21,84.73,83.79,84.28,4810000 +1964-09-25,84,84.62,83.56,84.21,6170000 +1964-09-24,83.91,84.43,83.45,84,5840000 +1964-09-23,83.89,84.37,83.45,83.91,5920000 +1964-09-22,83.86,84.44,83.53,83.89,5250000 +1964-09-21,83.48,84.32,83.41,83.86,5310000 +1964-09-18,83.79,84.29,83.03,83.48,6160000 +1964-09-17,83.24,84.18,83.17,83.79,6380000 +1964-09-16,83,83.52,82.57,83.24,4230000 +1964-09-15,83.22,83.68,82.69,83,5690000 +1964-09-14,83.45,83.89,82.88,83.22,5370000 +1964-09-11,83.1,83.84,82.79,83.45,5630000 +1964-09-10,83.05,83.5,82.6,83.1,5470000 +1964-09-09,82.87,83.51,82.54,83.05,5690000 +1964-09-08,82.76,83.24,82.46,82.87,4090000 +1964-09-04,82.56,83.03,82.31,82.76,4210000 +1964-09-03,82.31,82.83,82.04,82.56,4310000 +1964-09-02,82.18,82.76,81.95,82.31,4800000 +1964-09-01,81.83,82.5,81.57,82.18,4650000 +1964-08-31,81.99,82.48,81.46,81.83,3340000 +1964-08-28,81.7,82.29,81.54,81.99,3760000 +1964-08-27,81.32,81.94,81.07,81.7,3560000 +1964-08-26,81.44,81.74,80.99,81.32,3300000 +1964-08-25,81.91,82.13,81.2,81.44,3780000 +1964-08-24,82.07,82.48,81.64,81.91,3790000 +1964-08-21,81.94,82.43,81.64,82.07,3620000 +1964-08-20,82.32,82.57,81.6,81.94,3840000 +1964-08-19,82.4,82.8,81.99,82.32,4160000 +1964-08-18,82.36,82.79,82.01,82.4,4180000 +1964-08-17,82.35,82.85,82.02,82.36,3780000 +1964-08-14,82.41,82.83,82.03,82.35,4080000 +1964-08-13,82.17,82.87,81.98,82.41,4600000 +1964-08-12,81.76,82.53,81.6,82.17,4140000 +1964-08-11,81.78,82.25,81.45,81.76,3450000 +1964-08-10,81.86,82.23,81.43,81.78,3050000 +1964-08-07,81.34,82.2,81.19,81.86,3190000 +1964-08-06,82.09,82.45,81.2,81.34,3940000 +1964-08-05,81.96,82.41,80.8,82.09,6160000 +1964-08-04,83,83.02,81.68,81.96,4780000 +1964-08-03,83.18,83.49,82.65,83,3780000 +1964-07-31,83.09,83.57,82.72,83.18,4220000 +1964-07-30,82.92,83.5,82.63,83.09,4530000 +1964-07-29,82.85,83.3,82.47,82.92,4050000 +1964-07-28,83.08,83.3,82.4,82.85,3860000 +1964-07-27,83.46,83.82,82.82,83.08,4090000 +1964-07-24,83.48,83.92,83.07,83.46,4210000 +1964-07-23,83.52,83.91,83.06,83.48,4560000 +1964-07-22,83.54,83.95,82.96,83.52,4570000 +1964-07-21,83.74,83.99,83.06,83.54,4570000 +1964-07-20,84.01,84.33,83.44,83.74,4390000 +1964-07-17,83.64,84.33,83.37,84.01,4640000 +1964-07-16,83.34,83.98,83.06,83.64,4640000 +1964-07-15,83.06,83.67,82.72,83.34,4610000 +1964-07-14,83.31,83.71,82.72,83.06,4760000 +1964-07-13,83.36,83.86,82.92,83.31,4800000 +1964-07-10,83.22,83.99,82.87,83.36,5420000 +1964-07-09,83.12,83.64,82.74,83.22,5040000 +1964-07-08,83.12,83.56,82.58,83.12,4760000 +1964-07-07,82.98,83.53,82.6,83.12,5240000 +1964-07-06,82.6,83.38,82.37,82.98,5080000 +1964-07-02,82.27,82.98,82.09,82.6,5230000 +1964-07-01,81.69,82.51,81.46,82.27,5320000 +1964-06-30,81.64,82.07,81.19,81.69,4360000 +1964-06-29,81.46,82.1,81.1,81.64,4380000 +1964-06-26,81.21,81.78,80.86,81.46,4440000 +1964-06-25,81.06,81.73,80.75,81.21,5010000 +1964-06-24,80.77,81.45,80.41,81.06,4840000 +1964-06-23,81.11,81.43,80.5,80.77,4060000 +1964-06-22,80.89,81.54,80.66,81.11,4540000 +1964-06-19,80.79,81.23,80.39,80.89,4050000 +1964-06-18,80.81,81.34,80.43,80.79,4730000 +1964-06-17,80.4,81.13,80.22,80.81,5340000 +1964-06-16,79.97,80.72,79.85,80.4,4590000 +1964-06-15,79.6,80.33,79.39,79.97,4110000 +1964-06-12,79.73,80.05,79.19,79.6,3840000 +1964-06-11,79.44,80.13,79.24,79.73,3620000 +1964-06-10,79.14,79.84,79.02,79.44,4170000 +1964-06-09,78.64,79.39,78.15,79.14,4470000 +1964-06-08,79.02,79.44,78.44,78.64,4010000 +1964-06-05,78.67,79.45,78.5,79.02,4240000 +1964-06-04,79.49,79.75,78.44,78.67,4880000 +1964-06-03,79.7,80.12,79.27,79.49,3990000 +1964-06-02,80.11,80.6,79.5,79.7,4180000 +1964-06-01,80.37,80.83,79.83,80.11,4300000 +1964-05-28,80.26,80.75,79.88,80.37,4560000 +1964-05-27,80.39,80.72,79.78,80.26,4450000 +1964-05-26,80.56,80.94,80.12,80.39,4290000 +1964-05-25,80.73,81.16,80.21,80.56,3990000 +1964-05-22,80.94,81.15,80.36,80.73,4640000 +1964-05-21,80.66,81.49,80.36,80.94,5350000 +1964-05-20,80.3,81.02,80.09,80.66,4790000 +1964-05-19,80.72,81.04,79.96,80.3,4360000 +1964-05-18,81.1,81.47,80.42,80.72,4590000 +1964-05-15,80.86,81.45,80.49,81.1,5070000 +1964-05-14,80.97,81.28,80.37,80.86,4720000 +1964-05-13,81.16,81.65,80.66,80.97,5890000 +1964-05-12,80.9,81.81,80.66,81.16,5200000 +1964-05-11,81,81.51,80.58,80.9,4490000 +1964-05-08,81,81,81,81,4910000 +1964-05-07,81.06,81.72,80.67,81.15,5600000 +1964-05-06,80.88,81.57,80.53,81.06,5560000 +1964-05-05,80.47,81.2,79.99,80.88,5340000 +1964-05-04,80.17,81.01,79.87,80.47,5360000 +1964-05-01,79.46,80.47,79.46,80.17,5990000 +1964-04-30,79.7,80.08,79.08,79.46,5690000 +1964-04-29,79.9,80.6,79.29,79.7,6200000 +1964-04-28,79.35,80.26,79.14,79.9,4790000 +1964-04-27,79.75,80.01,78.9,79.35,5070000 +1964-04-24,80.38,80.62,79.45,79.75,5610000 +1964-04-23,80.49,81.2,80.09,80.38,6690000 +1964-04-22,80.54,80.92,80.06,80.49,5390000 +1964-04-21,80.5,80.98,80.05,80.54,5750000 +1964-04-20,80.55,81.04,80.11,80.5,5560000 +1964-04-17,80.2,80.98,79.99,80.55,6030000 +1964-04-16,80.09,80.62,79.73,80.2,5240000 +1964-04-15,79.99,80.5,79.63,80.09,5270000 +1964-04-14,79.77,80.37,79.46,79.99,5120000 +1964-04-13,79.85,80.3,79.42,79.77,5330000 +1964-04-10,79.7,80.26,79.43,79.85,4990000 +1964-04-09,79.75,80.23,79.36,79.7,5300000 +1964-04-08,79.74,80.17,79.26,79.75,5380000 +1964-04-07,80.02,80.44,79.41,79.74,5900000 +1964-04-06,79.94,80.45,79.55,80.02,5840000 +1964-04-03,79.7,80.37,79.45,79.94,5990000 +1964-04-02,79.24,80.09,79.13,79.7,6840000 +1964-04-01,78.98,79.58,78.67,79.24,5510000 +1964-03-31,79.14,79.51,78.57,78.98,5270000 +1964-03-30,79.19,79.67,78.75,79.14,6060000 +1964-03-26,78.98,79.58,78.67,79.19,5760000 +1964-03-25,78.79,79.33,78.17,78.98,5420000 +1964-03-24,78.93,79.34,78.51,78.79,5210000 +1964-03-23,78.92,79.33,78.45,78.93,4940000 +1964-03-20,79.3,79.35,78.92,78.92,5020000 +1964-03-19,79.38,79.85,78.94,79.3,5670000 +1964-03-18,79.32,79.89,78.9,79.38,5890000 +1964-03-17,79.14,79.65,78.77,79.32,5480000 +1964-03-16,79.14,79.6,78.72,79.14,5140000 +1964-03-13,79.08,79.59,78.74,79.14,5660000 +1964-03-12,78.95,79.41,78.55,79.08,5290000 +1964-03-11,78.59,79.42,78.45,78.95,6180000 +1964-03-10,78.33,78.9,77.95,78.59,5500000 +1964-03-09,78.31,78.88,77.95,78.33,5510000 +1964-03-06,78.06,78.6,77.85,78.31,4790000 +1964-03-05,78.07,78.44,77.58,78.06,4680000 +1964-03-04,78.22,78.7,77.7,78.07,5250000 +1964-03-03,77.97,78.66,77.69,78.22,5350000 +1964-03-02,77.8,78.38,77.5,77.97,5690000 +1964-02-28,77.62,78.06,77.2,77.8,4980000 +1964-02-27,77.87,78.29,77.38,77.62,5420000 +1964-02-26,77.68,78.13,77.33,77.87,5350000 +1964-02-25,77.68,78.31,77.19,77.68,5010000 +1964-02-24,77.62,78.16,77.27,77.68,5630000 +1964-02-20,77.55,77.99,77.16,77.62,4690000 +1964-02-19,77.47,77.98,77.13,77.55,4280000 +1964-02-18,77.46,77.9,77,77.47,4660000 +1964-02-17,77.48,77.93,77.04,77.46,4780000 +1964-02-14,77.52,77.82,77.02,77.48,4360000 +1964-02-13,77.57,77.93,77.1,77.52,4820000 +1964-02-12,77.33,77.88,77.14,77.57,4650000 +1964-02-11,77.05,77.65,76.81,77.33,4040000 +1964-02-10,77.18,77.77,76.83,77.05,4150000 +1964-02-07,76.93,77.51,76.66,77.18,4710000 +1964-02-06,76.75,77.26,76.47,76.93,4110000 +1964-02-05,76.88,77.28,76.36,76.75,4010000 +1964-02-04,76.97,77.31,76.46,76.88,4320000 +1964-02-03,77.04,77.55,76.53,76.97,4140000 +1964-01-31,76.7,77.37,76.39,77.04,4000000 +1964-01-30,76.63,77.2,76.26,76.7,4230000 +1964-01-29,77.1,77.36,76.33,76.63,4450000 +1964-01-28,77.08,77.56,76.63,77.1,4720000 +1964-01-27,77.11,77.78,76.64,77.08,5240000 +1964-01-24,77.09,77.56,76.58,77.11,5080000 +1964-01-23,77.03,77.62,76.67,77.09,5380000 +1964-01-22,76.62,77.62,76.45,77.03,5430000 +1964-01-21,76.41,76.99,75.87,76.62,4800000 +1964-01-20,76.56,77.19,76.02,76.41,5570000 +1964-01-17,76.55,77.09,76.02,76.56,5600000 +1964-01-16,76.64,77.21,76.05,76.55,6200000 +1964-01-15,76.36,77.06,75.96,76.64,6750000 +1964-01-14,76.22,76.85,75.88,76.36,6500000 +1964-01-13,76.24,76.71,75.78,76.22,5440000 +1964-01-10,76.28,76.67,75.74,76.24,5260000 +1964-01-09,76,76.64,75.6,76.28,5180000 +1964-01-08,75.69,76.35,75.39,76,5380000 +1964-01-07,75.67,76.24,75.25,75.69,5700000 +1964-01-06,75.5,76.12,75.18,75.67,5480000 +1964-01-03,75.43,76.04,75.09,75.5,5550000 +1964-01-02,75.02,75.79,74.82,75.43,4680000 +1963-12-31,74.56,75.36,74.4,75.02,6730000 +1963-12-30,74.44,74.94,74.13,74.56,4930000 +1963-12-27,74.32,74.91,74.09,74.44,4360000 +1963-12-26,73.97,74.63,73.74,74.32,3700000 +1963-12-24,73.81,74.48,73.44,73.97,3970000 +1963-12-23,74.28,74.45,73.49,73.81,4540000 +1963-12-20,74.4,74.75,73.85,74.28,4600000 +1963-12-19,74.63,74.92,74.08,74.4,4410000 +1963-12-18,74.74,75.21,74.25,74.63,6000000 +1963-12-17,74.3,75.08,74.07,74.74,5140000 +1963-12-16,74.06,74.66,73.78,74.3,4280000 +1963-12-13,73.91,74.39,73.68,74.06,4290000 +1963-12-12,73.9,74.31,73.58,73.91,4220000 +1963-12-11,73.99,74.37,73.58,73.9,4400000 +1963-12-10,73.96,74.48,73.4,73.99,4560000 +1963-12-09,74,74.41,73.56,73.96,4430000 +1963-12-06,74.28,74.63,73.62,74,4830000 +1963-12-05,73.8,74.57,73.45,74.28,5190000 +1963-12-04,73.62,74.18,73.21,73.8,4790000 +1963-12-03,73.66,74.01,73.14,73.62,4520000 +1963-12-02,73.23,74.08,73.02,73.66,4770000 +1963-11-29,72.25,73.47,72.05,73.23,4810000 +1963-11-27,72.38,72.78,71.76,72.25,5210000 +1963-11-26,71.4,72.74,71.4,72.38,9320000 +1963-11-22,71.62,72.17,69.48,69.61,6630000 +1963-11-21,72.56,72.86,71.4,71.62,5670000 +1963-11-20,71.9,73.14,71.49,72.56,5330000 +1963-11-19,71.83,72.61,71.42,71.9,4430000 +1963-11-18,72.35,72.52,71.42,71.83,4730000 +1963-11-15,72.95,73.2,72.09,72.35,4790000 +1963-11-14,73.29,73.53,72.63,72.95,4610000 +1963-11-13,73.23,73.67,72.89,73.29,4710000 +1963-11-12,73.23,73.23,73.23,73.23,4610000 +1963-11-11,73.52,73.52,73.52,73.52,3970000 +1963-11-08,73.06,73.66,72.8,73.36,4570000 +1963-11-07,72.81,73.48,72.58,73.06,4320000 +1963-11-06,73.45,73.47,72.33,72.81,5600000 +1963-11-04,73.83,74.27,73.09,73.45,5440000 +1963-11-01,74.01,74.44,73.47,73.83,5240000 +1963-10-31,73.8,74.35,73.25,74.01,5030000 +1963-10-30,74.46,74.59,73.43,73.8,5170000 +1963-10-29,74.48,75.18,73.97,74.46,6100000 +1963-10-28,74.01,75.15,73.75,74.48,7150000 +1963-10-25,73.28,74.41,73.06,74.01,6390000 +1963-10-24,73,73.73,72.74,73.28,6280000 +1963-10-23,72.96,73.55,72.59,73,5830000 +1963-10-22,73.38,73.55,72.48,72.96,6420000 +1963-10-21,73.32,73.87,73.03,73.38,5450000 +1963-10-18,73.26,73.74,72.85,73.32,5830000 +1963-10-17,72.97,73.77,72.84,73.26,6790000 +1963-10-16,72.4,73.2,72.08,72.97,5570000 +1963-10-15,72.3,72.79,71.99,72.4,4550000 +1963-10-14,72.27,72.43,71.85,72.3,4270000 +1963-10-11,72.2,72.71,71.87,72.27,4740000 +1963-10-10,71.87,72.52,71.6,72.2,4470000 +1963-10-09,71.98,71.98,71.6,71.87,5520000 +1963-10-08,72.7,73.14,72.24,72.6,4920000 +1963-10-07,72.85,73.27,72.39,72.7,4050000 +1963-10-04,72.83,73.19,72.46,72.85,5120000 +1963-10-03,72.3,73.1,72.1,72.83,4510000 +1963-10-02,72.22,72.67,71.92,72.3,3780000 +1963-10-01,71.7,72.65,71.57,72.22,4420000 +1963-09-30,72.13,72.37,71.28,71.7,3730000 +1963-09-27,72.27,72.6,71.6,72.13,4350000 +1963-09-26,72.89,73.07,72.01,72.27,5100000 +1963-09-25,73.3,73.87,72.58,72.89,6340000 +1963-09-24,72.96,73.67,72.59,73.3,5520000 +1963-09-23,73.3,73.53,72.62,72.96,5140000 +1963-09-20,73.22,73.71,72.92,73.3,5310000 +1963-09-19,72.8,73.47,72.61,73.22,4080000 +1963-09-18,73.12,73.44,72.51,72.8,5070000 +1963-09-17,73.07,73.64,72.79,73.12,4950000 +1963-09-16,73.17,73.63,72.8,73.07,4740000 +1963-09-13,73.15,73.59,72.82,73.17,5230000 +1963-09-12,73.2,73.6,72.72,73.15,5560000 +1963-09-11,72.99,73.79,72.83,73.2,6670000 +1963-09-10,72.58,73.27,72.25,72.99,5310000 +1963-09-09,72.84,73.23,72.26,72.58,5020000 +1963-09-06,73,73.51,72.51,72.84,7160000 +1963-09-05,72.64,73.19,72.15,73,5700000 +1963-09-04,72.66,73.18,72.32,72.64,6070000 +1963-09-03,72.5,73.09,72.3,72.66,5570000 +1963-08-30,72.16,72.71,71.88,72.5,4560000 +1963-08-29,72.04,72.56,71.83,72.16,5110000 +1963-08-28,71.52,72.39,71.49,72.04,5120000 +1963-08-27,71.91,72.04,71.27,71.52,4080000 +1963-08-26,71.76,72.3,71.57,71.91,4700000 +1963-08-23,71.54,72.14,71.33,71.76,4880000 +1963-08-22,71.29,71.81,70.95,71.54,4540000 +1963-08-21,71.38,71.73,71,71.29,3820000 +1963-08-20,71.44,71.91,71.03,71.38,3660000 +1963-08-19,71.49,71.92,71.15,71.44,3650000 +1963-08-16,71.38,71.95,71.05,71.49,4130000 +1963-08-15,71.07,71.71,70.81,71.38,4980000 +1963-08-14,70.79,71.32,70.39,71.07,4420000 +1963-08-13,70.59,71.09,70.32,70.79,4450000 +1963-08-12,70.48,71,70.19,70.59,4770000 +1963-08-09,70.02,70.65,69.83,70.48,4050000 +1963-08-08,69.96,70.31,69.58,70.02,3460000 +1963-08-07,70.17,70.53,69.69,69.96,3790000 +1963-08-06,69.71,70.4,69.57,70.17,3760000 +1963-08-05,69.3,69.97,69.2,69.71,3370000 +1963-08-02,69.07,69.56,68.86,69.3,2940000 +1963-08-01,69.13,69.47,68.64,69.07,3410000 +1963-07-31,69.24,69.83,68.91,69.13,3960000 +1963-07-30,68.67,69.45,68.58,69.24,3550000 +1963-07-29,68.54,68.96,68.32,68.67,2840000 +1963-07-26,68.26,68.76,68.03,68.54,2510000 +1963-07-25,68.28,68.92,68.02,68.26,3710000 +1963-07-24,67.91,68.54,67.76,68.28,2810000 +1963-07-23,67.9,68.57,67.65,67.91,3500000 +1963-07-22,68.35,68.6,67.54,67.9,3700000 +1963-07-19,68.49,68.7,67.9,68.35,3340000 +1963-07-18,68.93,69.27,68.34,68.49,3710000 +1963-07-17,69.14,69.53,68.68,68.93,3940000 +1963-07-16,69.2,69.51,68.85,69.14,3000000 +1963-07-15,69.64,69.73,68.97,69.2,3290000 +1963-07-12,69.76,70.13,69.36,69.64,3660000 +1963-07-11,69.89,70.3,69.52,69.76,4100000 +1963-07-10,70.04,70.31,69.56,69.89,3730000 +1963-07-09,69.74,70.39,69.55,70.04,3830000 +1963-07-08,70.22,70.35,69.47,69.74,3290000 +1963-07-05,69.94,70.48,69.78,70.22,2910000 +1963-07-03,69.46,70.28,69.42,69.94,4030000 +1963-07-02,68.86,69.72,68.74,69.46,3540000 +1963-07-01,69.37,69.53,68.58,68.86,3360000 +1963-06-28,69.07,69.68,68.93,69.37,3020000 +1963-06-27,69.41,69.81,68.78,69.07,4540000 +1963-06-26,70.04,70.1,69.17,69.41,4500000 +1963-06-25,70.2,70.51,69.75,70.04,4120000 +1963-06-24,70.25,70.67,69.84,70.2,3700000 +1963-06-21,70.01,70.57,69.79,70.25,4190000 +1963-06-20,70.09,70.36,69.31,70.01,4970000 +1963-06-19,70.02,70.47,69.75,70.09,3970000 +1963-06-18,69.95,70.43,69.63,70.02,3910000 +1963-06-17,69.95,69.95,69.95,69.95,3510000 +1963-06-14,70.23,70.6,69.87,70.25,3840000 +1963-06-13,70.41,70.85,69.98,70.23,4690000 +1963-06-12,70.03,70.81,69.91,70.41,5210000 +1963-06-11,69.94,70.41,69.58,70.03,4390000 +1963-06-10,70.41,70.51,69.57,69.94,4690000 +1963-06-07,70.58,70.98,70.1,70.41,5110000 +1963-06-06,70.53,70.95,70.11,70.58,4990000 +1963-06-05,70.7,71.17,70.17,70.53,5860000 +1963-06-04,70.69,71.08,70.2,70.7,5970000 +1963-06-03,70.8,71.24,70.39,70.69,5400000 +1963-05-31,70.33,71.14,70.27,70.8,4680000 +1963-05-29,70.01,70.65,69.86,70.33,4320000 +1963-05-28,69.87,70.41,69.55,70.01,3860000 +1963-05-27,70.02,70.27,69.48,69.87,3760000 +1963-05-24,70.1,70.44,69.66,70.02,4320000 +1963-05-23,70.14,70.53,69.79,70.1,4400000 +1963-05-22,70.14,70.68,69.82,70.14,5560000 +1963-05-21,69.96,70.51,69.62,70.14,5570000 +1963-05-20,70.29,70.48,69.59,69.96,4710000 +1963-05-17,70.25,70.63,69.83,70.29,4410000 +1963-05-16,70.43,70.81,69.91,70.25,5640000 +1963-05-15,70.21,70.77,69.87,70.43,5650000 +1963-05-14,70.48,70.73,69.92,70.21,4740000 +1963-05-13,70.52,70.89,70.11,70.48,4920000 +1963-05-10,70.35,70.81,69.99,70.52,5260000 +1963-05-09,70.01,70.74,69.86,70.35,5600000 +1963-05-08,69.44,70.24,69.23,70.01,5140000 +1963-05-07,69.53,69.92,69.03,69.44,4140000 +1963-05-06,70.03,70.31,69.32,69.53,4090000 +1963-05-03,70.17,70.51,69.78,70.03,4760000 +1963-05-02,69.97,70.5,69.75,70.17,4480000 +1963-05-01,69.8,70.43,69.61,69.97,5060000 +1963-04-30,69.65,70.18,69.26,69.8,4680000 +1963-04-29,69.7,70.04,69.26,69.65,3980000 +1963-04-26,69.76,70.11,69.23,69.7,4490000 +1963-04-25,69.72,70.08,69.25,69.76,5070000 +1963-04-24,69.53,70.12,69.34,69.72,5910000 +1963-04-23,69.3,69.83,68.95,69.53,5220000 +1963-04-22,69.23,69.82,69.01,69.3,5180000 +1963-04-19,68.89,69.46,68.6,69.23,4660000 +1963-04-18,68.92,69.34,68.56,68.89,4770000 +1963-04-17,69.14,69.37,68.47,68.92,5220000 +1963-04-16,69.09,69.61,68.66,69.14,5570000 +1963-04-15,68.77,69.56,68.58,69.09,5930000 +1963-04-11,68.29,69.07,67.97,68.77,5250000 +1963-04-10,68.45,68.89,67.66,68.29,5880000 +1963-04-09,68.52,68.84,68.03,68.45,5090000 +1963-04-08,68.28,68.91,68.05,68.52,5940000 +1963-04-05,67.85,68.46,67.46,68.28,5240000 +1963-04-04,67.36,68.12,67.28,67.85,5300000 +1963-04-03,66.84,67.55,66.63,67.36,4660000 +1963-04-02,66.85,67.36,66.51,66.84,4360000 +1963-04-01,66.57,67.18,66.23,66.85,3890000 +1963-03-29,66.58,66.9,66.23,66.57,3390000 +1963-03-28,66.68,67.01,66.32,66.58,3890000 +1963-03-27,66.4,66.93,66.21,66.68,4270000 +1963-03-26,66.21,66.73,66.01,66.4,4100000 +1963-03-25,66.19,66.6,65.92,66.21,3700000 +1963-03-22,65.85,66.44,65.68,66.19,3820000 +1963-03-21,65.95,66.25,65.6,65.85,3220000 +1963-03-20,65.47,66.15,65.3,65.95,3690000 +1963-03-19,65.61,65.85,65.19,65.47,3180000 +1963-03-18,65.93,66.17,65.36,65.61,3250000 +1963-03-15,65.6,66.22,65.39,65.93,3400000 +1963-03-14,65.91,66.21,65.39,65.6,3540000 +1963-03-13,65.67,66.27,65.54,65.91,4120000 +1963-03-12,65.51,65.97,65.26,65.67,3350000 +1963-03-11,65.33,65.86,65.11,65.51,3180000 +1963-03-08,65.26,65.74,65.03,65.33,3360000 +1963-03-07,64.85,65.6,64.81,65.26,3350000 +1963-03-06,64.74,65.06,64.31,64.85,3100000 +1963-03-05,64.72,65.27,64.41,64.74,3280000 +1963-03-04,64.1,65.08,63.88,64.72,3650000 +1963-03-01,64.29,64.75,63.8,64.1,3920000 +1963-02-28,65.01,65.14,64.08,64.29,4090000 +1963-02-27,65.47,65.74,64.86,65.01,3680000 +1963-02-26,65.46,65.86,65.06,65.47,3670000 +1963-02-25,65.92,66.09,65.24,65.46,3680000 +1963-02-21,65.83,66.23,65.36,65.92,3980000 +1963-02-20,66.2,66.28,65.44,65.83,4120000 +1963-02-19,66.52,66.67,65.92,66.2,4130000 +1963-02-18,66.41,66.96,66.1,66.52,4700000 +1963-02-15,66.35,66.74,65.96,66.41,4410000 +1963-02-14,66.15,66.75,65.93,66.35,5640000 +1963-02-13,65.83,66.53,65.56,66.15,4960000 +1963-02-12,65.76,66.01,65.16,65.83,3710000 +1963-02-11,66.17,66.41,65.5,65.76,3880000 +1963-02-08,66.17,66.45,65.65,66.17,3890000 +1963-02-07,66.4,66.81,65.91,66.17,4240000 +1963-02-06,66.11,66.76,65.88,66.4,4340000 +1963-02-05,66.17,66.35,65.38,66.11,4050000 +1963-02-04,66.31,66.66,65.89,66.17,3670000 +1963-02-01,66.31,66.31,66.31,66.31,4280000 +1963-01-31,65.85,66.45,65.51,66.2,4270000 +1963-01-30,66.23,66.33,65.55,65.85,3740000 +1963-01-29,66.24,66.58,65.83,66.23,4360000 +1963-01-28,65.92,66.59,65.77,66.24,4720000 +1963-01-25,65.75,66.23,65.38,65.92,4770000 +1963-01-24,65.62,66.09,65.33,65.75,4810000 +1963-01-23,65.44,65.91,65.23,65.62,4820000 +1963-01-22,65.28,65.8,65.03,65.44,4810000 +1963-01-21,65.18,65.52,64.64,65.28,4090000 +1963-01-18,65.13,65.7,64.86,65.18,4760000 +1963-01-17,64.67,65.4,64.35,65.13,5230000 +1963-01-16,65.11,65.25,64.42,64.67,4260000 +1963-01-15,65.2,65.62,64.82,65.11,5930000 +1963-01-14,64.85,65.5,64.61,65.2,5000000 +1963-01-11,64.71,65.1,64.31,64.85,4410000 +1963-01-10,64.59,65.16,64.33,64.71,4520000 +1963-01-09,64.74,65.22,64.32,64.59,5110000 +1963-01-08,64.12,64.98,64,64.74,5410000 +1963-01-07,64.13,64.59,63.67,64.12,4440000 +1963-01-04,63.72,64.45,63.57,64.13,5400000 +1963-01-03,62.69,63.89,62.67,63.72,4570000 +1963-01-02,63.1,63.39,62.32,62.69,2540000 +1962-12-31,62.96,63.43,62.68,63.1,5420000 +1962-12-28,62.93,63.25,62.53,62.96,4140000 +1962-12-27,63.02,63.41,62.67,62.93,3670000 +1962-12-26,62.63,63.32,62.56,63.02,3370000 +1962-12-24,62.64,63.03,62.19,62.63,3180000 +1962-12-21,62.82,63.13,62.26,62.64,3470000 +1962-12-20,62.58,63.28,62.44,62.82,4220000 +1962-12-19,62.07,62.81,61.72,62.58,4000000 +1962-12-18,62.37,62.66,61.78,62.07,3620000 +1962-12-17,62.57,62.95,62.14,62.37,3590000 +1962-12-14,62.42,62.83,61.96,62.57,3280000 +1962-12-13,62.63,63.07,62.09,62.42,3380000 +1962-12-12,62.32,63.16,62.13,62.63,3760000 +1962-12-11,62.27,62.58,61.72,62.32,3700000 +1962-12-10,63.06,63.35,61.96,62.27,4270000 +1962-12-07,62.93,63.43,62.45,63.06,3900000 +1962-12-06,62.39,63.36,62.28,62.93,4600000 +1962-12-05,62.64,63.5,62.37,62.39,6280000 +1962-12-04,61.94,62.93,61.77,62.64,5210000 +1962-12-03,62.26,62.45,61.28,61.94,3810000 +1962-11-30,62.41,62.78,61.78,62.26,4570000 +1962-11-29,62.12,62.72,61.69,62.41,5810000 +1962-11-28,61.73,62.48,61.51,62.12,5980000 +1962-11-27,61.36,62.04,60.98,61.73,5500000 +1962-11-26,61.54,62.13,60.95,61.36,5650000 +1962-11-23,60.81,62.03,60.66,61.54,5660000 +1962-11-21,60.45,61.18,60.19,60.81,5100000 +1962-11-20,59.82,60.63,59.57,60.45,4290000 +1962-11-19,60.16,60.42,59.46,59.82,3410000 +1962-11-16,59.97,60.46,59.46,60.16,4000000 +1962-11-15,60.16,60.67,59.74,59.97,5050000 +1962-11-14,59.46,60.41,59.18,60.16,5090000 +1962-11-13,59.59,60.06,59.06,59.46,4550000 +1962-11-12,58.78,60,58.59,59.59,5090000 +1962-11-09,58.32,58.99,57.9,58.78,4340000 +1962-11-08,58.71,59.12,58.09,58.32,4160000 +1962-11-07,58.35,59.11,57.76,58.71,4580000 +1962-11-05,57.75,58.7,57.69,58.35,4320000 +1962-11-02,57.12,58.19,56.78,57.75,5470000 +1962-11-01,56.52,57.31,55.9,57.12,3400000 +1962-10-31,56.54,57,56.19,56.52,3090000 +1962-10-30,55.72,56.84,55.52,56.54,3830000 +1962-10-29,55.34,56.38,55.34,55.72,4280000 +1962-10-26,54.69,54.96,54.08,54.54,2580000 +1962-10-25,55.17,55.17,53.82,54.69,3950000 +1962-10-24,53.49,55.44,52.55,55.21,6720000 +1962-10-23,54.96,55.19,53.24,53.49,6110000 +1962-10-22,55.48,55.48,54.38,54.96,5690000 +1962-10-19,56.34,56.54,55.34,55.59,4650000 +1962-10-18,56.89,57.02,56.18,56.34,3280000 +1962-10-17,57.08,57.23,56.37,56.89,3240000 +1962-10-16,57.27,57.63,56.87,57.08,2860000 +1962-10-15,56.95,57.5,56.66,57.27,2640000 +1962-10-12,57.05,57.21,56.66,56.95,2020000 +1962-10-11,57.24,57.46,56.78,57.05,2460000 +1962-10-10,57.2,57.83,56.96,57.24,3040000 +1962-10-09,57.07,57.4,56.71,57.2,2340000 +1962-10-08,57.07,57.41,56.68,57.07,1950000 +1962-10-05,56.7,57.3,56.55,57.07,2730000 +1962-10-04,56.16,56.84,55.9,56.7,2530000 +1962-10-03,56.1,56.71,55.84,56.16,2610000 +1962-10-02,55.49,56.46,55.31,56.1,3000000 +1962-10-01,56.27,56.31,55.26,55.49,3090000 +1962-09-28,55.77,56.58,55.59,56.27,2850000 +1962-09-27,56.15,56.55,55.53,55.77,3540000 +1962-09-26,56.96,57.29,55.92,56.15,3550000 +1962-09-25,56.63,57.22,56.12,56.96,3620000 +1962-09-24,57.45,57.45,56.3,56.63,5000000 +1962-09-21,58.54,58.64,57.43,57.69,4280000 +1962-09-20,58.95,59.29,58.33,58.54,3350000 +1962-09-19,59.03,59.26,58.59,58.95,2950000 +1962-09-18,59.08,59.54,58.77,59.03,3690000 +1962-09-17,58.89,59.42,58.65,59.08,3330000 +1962-09-14,58.7,59.14,58.4,58.89,2880000 +1962-09-13,58.84,59.18,58.46,58.7,3100000 +1962-09-12,58.59,59.06,58.4,58.84,3100000 +1962-09-11,58.45,58.93,58.17,58.59,3040000 +1962-09-10,58.38,58.64,57.88,58.45,2520000 +1962-09-07,58.36,58.9,58.09,58.38,2890000 +1962-09-06,58.12,58.6,57.72,58.36,3180000 +1962-09-05,58.56,58.77,57.95,58.12,3050000 +1962-09-04,59.12,59.49,58.44,58.56,2970000 +1962-08-31,58.68,59.25,58.45,59.12,2830000 +1962-08-30,58.66,59.06,58.39,58.68,2260000 +1962-08-29,58.79,58.96,58.17,58.66,2900000 +1962-08-28,59.55,59.61,58.66,58.79,3180000 +1962-08-27,59.58,59.94,59.24,59.55,3140000 +1962-08-24,59.7,59.92,59.18,59.58,2890000 +1962-08-23,59.78,60.33,59.47,59.7,4770000 +1962-08-22,59.12,59.93,58.91,59.78,4520000 +1962-08-21,59.37,59.66,58.9,59.12,3730000 +1962-08-20,59.01,59.72,58.9,59.37,4580000 +1962-08-17,58.64,59.24,58.43,59.01,3430000 +1962-08-16,58.66,59.11,58.24,58.64,4180000 +1962-08-15,58.25,59.11,58.22,58.66,4880000 +1962-08-14,57.63,58.43,57.41,58.25,3640000 +1962-08-13,57.55,57.9,57.22,57.63,2670000 +1962-08-10,57.57,57.85,57.16,57.55,2470000 +1962-08-09,57.51,57.88,57.19,57.57,2670000 +1962-08-08,57.36,57.64,56.76,57.51,3080000 +1962-08-07,57.75,57.81,57.07,57.36,2970000 +1962-08-06,58.12,58.35,57.54,57.75,3110000 +1962-08-03,57.98,58.32,57.63,58.12,5990000 +1962-08-02,57.75,58.2,57.38,57.98,3410000 +1962-08-01,58.23,58.3,57.51,57.75,3100000 +1962-07-31,57.83,58.58,57.74,58.23,4190000 +1962-07-30,57.2,57.98,57.08,57.83,3200000 +1962-07-27,56.77,57.36,56.56,57.2,2890000 +1962-07-26,56.46,57.18,56.16,56.77,2790000 +1962-07-25,56.36,56.67,55.78,56.46,2910000 +1962-07-24,56.8,56.93,56.14,56.36,2560000 +1962-07-23,56.81,57.32,56.53,56.8,2770000 +1962-07-20,56.42,57.09,56.27,56.81,2610000 +1962-07-19,56.2,56.95,55.96,56.42,3090000 +1962-07-18,56.78,56.81,55.86,56.2,3620000 +1962-07-17,57.83,57.96,56.68,56.78,3500000 +1962-07-16,57.83,58.1,57.18,57.83,3130000 +1962-07-13,58.03,58.18,57.23,57.83,3380000 +1962-07-12,57.73,58.67,57.59,58.03,5370000 +1962-07-11,57.2,57.95,56.77,57.73,4250000 +1962-07-10,56.99,58.36,56.99,57.2,7120000 +1962-07-09,56.17,56.73,55.54,56.55,2950000 +1962-07-06,56.73,56.73,55.64,56.17,3110000 +1962-07-05,56.49,57.1,56.15,56.81,3350000 +1962-07-03,55.86,56.74,55.57,56.49,3920000 +1962-07-02,54.75,56.02,54.47,55.86,3450000 +1962-06-29,54.41,55.47,54.2,54.75,4720000 +1962-06-28,52.98,54.64,52.98,54.41,5440000 +1962-06-27,52.32,52.83,51.77,52.6,3890000 +1962-06-26,52.45,53.58,52.1,52.32,4630000 +1962-06-25,52.68,52.96,51.35,52.45,7090000 +1962-06-22,53.59,53.78,52.48,52.68,5640000 +1962-06-21,54.78,54.78,53.5,53.59,4560000 +1962-06-20,55.54,55.92,54.66,54.78,3360000 +1962-06-19,55.74,55.88,54.98,55.54,2680000 +1962-06-18,55.89,56.53,54.97,55.74,4580000 +1962-06-15,54.33,55.96,53.66,55.89,7130000 +1962-06-14,55.5,56,54.12,54.33,6240000 +1962-06-13,56.34,56.8,55.24,55.5,5850000 +1962-06-12,57.66,57.66,56.23,56.34,4690000 +1962-06-11,58.45,58.58,57.51,57.82,2870000 +1962-06-08,58.4,58.97,58.14,58.45,2560000 +1962-06-07,58.39,58.9,58,58.4,2760000 +1962-06-06,57.64,59.17,57.64,58.39,4190000 +1962-06-05,57.27,58.42,56.33,57.57,6140000 +1962-06-04,59.12,59.12,57.14,57.27,5380000 +1962-06-01,59.63,59.96,58.52,59.38,5760000 +1962-05-31,58.8,60.82,58.8,59.63,10710000 +1962-05-29,55.5,58.29,53.13,58.08,14750000 +1962-05-28,59.15,59.15,55.42,55.5,9350000 +1962-05-25,60.62,60.98,59,59.47,6380000 +1962-05-24,61.11,61.79,60.36,60.62,5250000 +1962-05-23,62.34,62.42,60.9,61.11,5450000 +1962-05-22,63.59,63.69,62.26,62.34,3640000 +1962-05-21,63.82,64,63.21,63.59,2260000 +1962-05-18,63.93,64.14,63.29,63.82,2490000 +1962-05-17,64.27,64.41,63.38,63.93,2950000 +1962-05-16,64.29,64.88,63.82,64.27,3360000 +1962-05-15,63.41,64.87,63.41,64.29,4780000 +1962-05-14,62.65,63.31,61.11,63.1,5990000 +1962-05-11,63.57,64.1,62.44,62.65,4510000 +1962-05-10,64.26,64.39,62.99,63.57,4730000 +1962-05-09,65.17,65.17,64.02,64.26,3670000 +1962-05-08,66.02,66.13,64.88,65.17,3020000 +1962-05-07,66.24,66.56,65.66,66.02,2530000 +1962-05-04,66.53,66.8,65.8,66.24,3010000 +1962-05-03,65.99,66.93,65.81,66.53,3320000 +1962-05-02,65.7,66.67,65.56,65.99,3780000 +1962-05-01,65.24,65.94,63.76,65.7,5100000 +1962-04-30,66.3,66.9,64.95,65.24,4150000 +1962-04-27,67.05,67.61,65.99,66.3,4140000 +1962-04-26,67.71,67.97,66.92,67.05,3650000 +1962-04-25,68.46,68.58,67.53,67.71,3340000 +1962-04-24,68.53,68.91,68.16,68.46,3040000 +1962-04-23,68.59,69.01,68.17,68.53,3240000 +1962-04-19,68.27,68.9,68.07,68.59,3100000 +1962-04-18,67.9,68.72,67.83,68.27,3350000 +1962-04-17,67.6,68.2,67.24,67.9,2940000 +1962-04-16,67.9,68.19,67.21,67.6,3070000 +1962-04-13,67.9,68.11,67.03,67.9,3470000 +1962-04-12,68.41,68.43,67.47,67.9,3320000 +1962-04-11,68.56,69.26,68.24,68.41,3240000 +1962-04-10,68.31,68.8,67.94,68.56,2880000 +1962-04-09,68.84,69.02,68.09,68.31,3020000 +1962-04-06,68.91,69.42,68.58,68.84,2730000 +1962-04-05,68.49,69.09,68.12,68.91,3130000 +1962-04-04,68.81,69.22,68.33,68.49,3290000 +1962-04-03,69.37,69.53,68.53,68.81,3350000 +1962-04-02,69.55,69.82,69.13,69.37,2790000 +1962-03-30,70.01,70.09,69.16,69.55,2950000 +1962-03-29,70.04,70.5,69.81,70.01,2870000 +1962-03-28,69.7,70.33,69.54,70.04,2940000 +1962-03-27,69.89,70.2,69.41,69.7,3090000 +1962-03-26,70.45,70.63,69.73,69.89,3040000 +1962-03-23,70.4,70.78,70.12,70.45,3050000 +1962-03-22,70.51,70.84,70.14,70.4,3130000 +1962-03-21,70.66,70.93,70.16,70.51,3360000 +1962-03-20,70.85,71.08,70.4,70.66,3060000 +1962-03-19,70.94,71.31,70.53,70.85,3220000 +1962-03-16,71.06,71.34,70.67,70.94,3060000 +1962-03-15,70.91,71.44,70.59,71.06,3250000 +1962-03-14,70.6,71.25,70.48,70.91,3670000 +1962-03-13,70.4,70.86,70.06,70.6,3200000 +1962-03-12,70.42,70.76,70.02,70.4,3280000 +1962-03-09,70.19,70.71,70,70.42,3340000 +1962-03-08,69.69,70.37,69.4,70.19,3210000 +1962-03-07,69.78,70.07,69.37,69.69,2890000 +1962-03-06,70.01,70.24,69.46,69.78,2870000 +1962-03-05,70.16,70.48,69.65,70.01,3020000 +1962-03-02,70.16,70.16,69.75,70.16,2980000 +1962-03-01,69.96,70.6,69.76,70.2,2960000 +1962-02-28,69.89,70.42,69.57,69.96,3030000 +1962-02-27,69.76,70.32,69.48,69.89,3110000 +1962-02-26,70.16,70.33,69.44,69.76,2910000 +1962-02-23,70.32,70.57,69.73,70.16,3230000 +1962-02-21,70.66,70.97,70.12,70.32,3310000 +1962-02-20,70.41,70.91,70.13,70.66,3300000 +1962-02-19,70.59,70.96,70.12,70.41,3350000 +1962-02-16,70.74,71.13,70.27,70.59,3700000 +1962-02-15,70.42,71.06,70.23,70.74,3470000 +1962-02-14,70.45,70.79,70.03,70.42,3630000 +1962-02-13,70.46,70.89,70.07,70.45,3400000 +1962-02-12,70.48,70.81,70.14,70.46,2620000 +1962-02-09,70.58,70.83,69.93,70.48,3370000 +1962-02-08,70.42,70.95,70.16,70.58,3810000 +1962-02-07,69.96,70.67,69.78,70.42,4140000 +1962-02-06,69.88,70.32,69.41,69.96,3650000 +1962-02-05,69.81,70.3,69.42,69.88,3890000 +1962-02-02,69.26,70.02,69.02,69.81,3950000 +1962-02-01,68.84,69.65,68.56,69.26,4260000 +1962-01-31,68.17,69.09,68.12,68.84,3840000 +1962-01-30,67.9,68.65,67.62,68.17,3520000 +1962-01-29,68.13,68.5,67.55,67.9,3050000 +1962-01-26,68.35,68.67,67.83,68.13,3330000 +1962-01-25,68.4,69.05,68.1,68.35,3560000 +1962-01-24,68.29,68.68,67.55,68.4,3760000 +1962-01-23,68.81,68.96,68,68.29,3350000 +1962-01-22,68.75,69.37,68.45,68.81,3810000 +1962-01-19,68.39,70.08,68.14,68.75,3800000 +1962-01-18,68.32,68.73,67.75,68.39,3460000 +1962-01-17,69.07,69.31,68.13,68.32,3780000 +1962-01-16,69.47,69.61,68.68,69.07,3650000 +1962-01-15,69.61,69.96,69.06,69.47,3450000 +1962-01-12,69.37,70.17,69.23,69.61,3730000 +1962-01-11,68.96,69.54,68.57,69.37,3390000 +1962-01-10,69.15,69.58,68.62,68.96,3300000 +1962-01-09,69.12,69.93,68.83,69.15,3600000 +1962-01-08,69.66,69.84,68.17,69.12,4620000 +1962-01-05,70.64,70.84,69.35,69.66,4630000 +1962-01-04,71.13,71.62,70.45,70.64,4450000 +1962-01-03,70.96,71.48,70.38,71.13,3590000 +1962-01-02,71.55,71.96,70.71,70.96,3120000 +1961-12-29,71.55,71.55,71.55,71.55,5370000 +1961-12-28,71.69,71.69,71.69,71.69,4530000 +1961-12-27,71.65,71.65,71.65,71.65,4170000 +1961-12-26,71.02,71.02,71.02,71.02,3180000 +1961-12-22,70.91,70.91,70.91,70.91,3390000 +1961-12-21,70.86,70.86,70.86,70.86,3440000 +1961-12-20,71.12,71.12,71.12,71.12,3640000 +1961-12-19,71.26,71.26,71.26,71.26,3440000 +1961-12-18,71.76,71.76,71.76,71.76,3810000 +1961-12-15,72.01,72.01,72.01,72.01,3710000 +1961-12-14,71.98,71.98,71.98,71.98,4350000 +1961-12-13,72.53,72.53,72.53,72.53,4890000 +1961-12-12,72.64,72.64,72.64,72.64,4680000 +1961-12-11,72.39,72.39,72.39,72.39,4360000 +1961-12-08,72.04,72.04,72.04,72.04,4010000 +1961-12-07,71.7,71.7,71.7,71.7,3900000 +1961-12-06,71.99,71.99,71.99,71.99,4200000 +1961-12-05,71.93,71.93,71.93,71.93,4330000 +1961-12-04,72.01,72.01,72.01,72.01,4560000 +1961-12-01,71.78,71.78,71.78,71.78,4420000 +1961-11-30,71.32,71.32,71.32,71.32,4210000 +1961-11-29,71.7,71.7,71.7,71.7,4550000 +1961-11-28,71.75,71.75,71.75,71.75,4360000 +1961-11-27,71.85,71.85,71.85,71.85,4700000 +1961-11-24,71.84,71.84,71.84,71.84,4020000 +1961-11-22,71.7,71.7,71.7,71.7,4500000 +1961-11-21,71.78,71.78,71.78,71.78,4890000 +1961-11-20,71.72,71.72,71.72,71.72,4190000 +1961-11-17,71.62,71.62,71.62,71.62,3960000 +1961-11-16,71.62,71.62,71.62,71.62,3980000 +1961-11-15,71.67,71.67,71.67,71.67,4660000 +1961-11-14,71.66,71.66,71.66,71.66,4750000 +1961-11-13,71.27,71.27,71.27,71.27,4540000 +1961-11-10,71.07,71.07,71.07,71.07,4180000 +1961-11-09,70.77,70.77,70.77,70.77,4680000 +1961-11-08,70.87,70.87,70.87,70.87,6090000 +1961-11-06,70.01,70.01,70.01,70.01,4340000 +1961-11-03,69.47,69.47,69.47,69.47,4070000 +1961-11-02,69.11,69.11,69.11,69.11,3890000 +1961-11-01,68.73,68.73,68.73,68.73,3210000 +1961-10-31,68.62,68.62,68.62,68.62,3350000 +1961-10-30,68.42,68.42,68.42,68.42,3430000 +1961-10-27,68.34,68.34,68.34,68.34,3200000 +1961-10-26,68.46,68.46,68.46,68.46,3330000 +1961-10-25,68.34,68.34,68.34,68.34,3590000 +1961-10-24,67.98,67.98,67.98,67.98,3430000 +1961-10-23,68.06,68.06,68.06,68.06,3440000 +1961-10-20,68,68,68,68,3470000 +1961-10-19,68.45,68.45,68.45,68.45,3850000 +1961-10-18,68.21,68.21,68.21,68.21,3520000 +1961-10-17,67.87,67.87,67.87,67.87,3110000 +1961-10-16,67.85,67.85,67.85,67.85,2840000 +1961-10-13,68.04,68.04,68.04,68.04,3090000 +1961-10-12,68.16,68.16,68.16,68.16,3060000 +1961-10-11,68.17,68.17,68.17,68.17,3670000 +1961-10-10,68.11,68.11,68.11,68.11,3430000 +1961-10-09,67.94,67.94,67.94,67.94,2920000 +1961-10-06,66.97,66.97,66.97,66.97,3470000 +1961-10-05,67.77,67.77,67.77,67.77,3920000 +1961-10-04,67.18,67.18,67.18,67.18,3380000 +1961-10-03,66.73,66.73,66.73,66.73,2680000 +1961-10-02,66.77,66.77,66.77,66.77,2800000 +1961-09-29,66.73,66.73,66.73,66.73,3060000 +1961-09-28,66.58,66.58,66.58,66.58,3000000 +1961-09-27,66.47,66.47,66.47,66.47,3440000 +1961-09-26,65.78,65.78,65.78,65.78,3320000 +1961-09-25,65.77,65.77,65.77,65.77,3700000 +1961-09-22,66.72,66.72,66.72,66.72,3070000 +1961-09-21,66.99,66.99,66.99,66.99,3340000 +1961-09-20,66.96,66.96,66.96,66.96,2700000 +1961-09-19,66.08,66.08,66.08,66.08,3260000 +1961-09-18,67.21,67.21,67.21,67.21,3550000 +1961-09-15,67.65,67.65,67.65,67.65,3130000 +1961-09-14,67.53,67.53,67.53,67.53,2920000 +1961-09-13,68.01,68.01,68.01,68.01,3110000 +1961-09-12,67.96,67.96,67.96,67.96,2950000 +1961-09-11,67.28,67.28,67.28,67.28,2790000 +1961-09-08,67.88,67.88,67.88,67.88,3430000 +1961-09-07,68.35,68.35,68.35,68.35,3900000 +1961-09-06,68.46,68.46,68.46,68.46,3440000 +1961-09-05,67.96,67.96,67.96,67.96,3000000 +1961-09-01,68.19,68.19,68.19,68.19,2710000 +1961-08-31,68.07,68.07,68.07,68.07,2920000 +1961-08-30,67.81,67.81,67.81,67.81,3220000 +1961-08-29,67.55,67.55,67.55,67.55,3160000 +1961-08-28,67.7,67.7,67.7,67.7,3150000 +1961-08-25,67.67,67.67,67.67,67.67,3050000 +1961-08-24,67.59,67.59,67.59,67.59,3090000 +1961-08-23,67.98,67.98,67.98,67.98,3550000 +1961-08-22,68.44,68.44,68.44,68.44,3640000 +1961-08-21,68.43,68.43,68.43,68.43,3880000 +1961-08-18,68.29,68.29,68.29,68.29,4030000 +1961-08-17,68.11,68.11,68.11,68.11,4130000 +1961-08-16,67.73,67.73,67.73,67.73,3430000 +1961-08-15,67.55,67.55,67.55,67.55,3320000 +1961-08-14,67.72,67.72,67.72,67.72,3120000 +1961-08-11,68.06,68.06,68.06,68.06,3260000 +1961-08-10,67.95,67.95,67.95,67.95,3570000 +1961-08-09,67.74,67.74,67.74,67.74,3710000 +1961-08-08,67.82,67.82,67.82,67.82,4050000 +1961-08-07,67.67,67.67,67.67,67.67,3560000 +1961-08-04,67.68,67.68,67.68,67.68,3710000 +1961-08-03,67.29,67.29,67.29,67.29,3650000 +1961-08-02,66.94,66.94,66.94,66.94,4300000 +1961-08-01,67.37,67.37,67.37,67.37,3990000 +1961-07-31,66.76,66.76,66.76,66.76,3170000 +1961-07-28,66.71,66.71,66.71,66.71,3610000 +1961-07-27,66.61,66.61,66.61,66.61,4170000 +1961-07-26,65.84,65.84,65.84,65.84,4070000 +1961-07-25,65.23,65.23,65.23,65.23,3010000 +1961-07-24,64.87,64.87,64.87,64.87,2490000 +1961-07-21,64.86,64.86,64.86,64.86,2360000 +1961-07-20,64.71,64.71,64.71,64.71,2530000 +1961-07-19,64.7,64.7,64.7,64.7,2940000 +1961-07-18,64.41,64.41,64.41,64.41,3010000 +1961-07-17,64.79,64.79,64.79,64.79,2690000 +1961-07-14,65.28,65.28,65.28,65.28,2760000 +1961-07-13,64.86,64.86,64.86,64.86,2670000 +1961-07-12,65.32,65.32,65.32,65.32,3070000 +1961-07-11,65.69,65.69,65.69,65.69,3160000 +1961-07-10,65.71,65.71,65.71,65.71,3180000 +1961-07-07,65.77,65.77,65.77,65.77,3030000 +1961-07-06,65.81,65.81,65.81,65.81,3470000 +1961-07-05,65.63,65.63,65.63,65.63,3270000 +1961-07-03,65.21,65.21,65.21,65.21,2180000 +1961-06-30,64.64,64.64,64.64,64.64,2380000 +1961-06-29,64.52,64.52,64.52,64.52,2560000 +1961-06-28,64.59,64.59,64.59,64.59,2830000 +1961-06-27,64.47,64.47,64.47,64.47,3090000 +1961-06-26,64.47,64.47,64.47,64.47,2690000 +1961-06-23,65.16,65.16,65.16,65.16,2720000 +1961-06-22,64.9,64.9,64.9,64.9,2880000 +1961-06-21,65.14,65.14,65.14,65.14,3210000 +1961-06-20,65.15,65.15,65.15,65.15,3280000 +1961-06-19,64.58,64.58,64.58,64.58,3980000 +1961-06-16,65.18,65.18,65.18,65.18,3380000 +1961-06-15,65.69,65.69,65.69,65.69,3220000 +1961-06-14,65.98,65.98,65.98,65.98,3430000 +1961-06-13,65.8,65.8,65.8,65.8,3030000 +1961-06-12,66.15,66.15,66.15,66.15,3260000 +1961-06-09,66.66,66.66,66.66,66.66,3520000 +1961-06-08,66.67,66.67,66.67,66.67,3810000 +1961-06-07,65.64,65.64,65.64,65.64,3980000 +1961-06-06,66.89,66.89,66.89,66.89,4250000 +1961-06-05,67.08,67.08,67.08,67.08,4150000 +1961-06-02,66.73,66.73,66.73,66.73,3670000 +1961-06-01,66.56,66.56,66.56,66.56,3770000 +1961-05-31,66.56,66.56,66.56,66.56,4320000 +1961-05-26,66.43,66.43,66.43,66.43,3780000 +1961-05-25,66.01,66.01,66.01,66.01,3760000 +1961-05-24,66.26,66.26,66.26,66.26,3970000 +1961-05-23,66.68,66.68,66.68,66.68,3660000 +1961-05-22,66.85,66.85,66.85,66.85,4070000 +1961-05-19,67.27,67.27,67.27,67.27,4200000 +1961-05-18,66.99,66.99,66.99,66.99,4610000 +1961-05-17,67.39,67.39,67.39,67.39,5520000 +1961-05-16,67.08,67.08,67.08,67.08,5110000 +1961-05-15,66.83,66.83,66.83,66.83,4840000 +1961-05-12,66.5,66.5,66.5,66.5,4840000 +1961-05-11,66.39,66.39,66.39,66.39,5170000 +1961-05-10,66.41,66.41,66.41,66.41,5450000 +1961-05-09,66.47,66.47,66.47,66.47,5380000 +1961-05-08,66.41,66.41,66.41,66.41,5170000 +1961-05-05,66.52,66.52,66.52,66.52,4980000 +1961-05-04,66.44,66.44,66.44,66.44,5350000 +1961-05-03,66.18,66.18,66.18,66.18,4940000 +1961-05-02,65.64,65.64,65.64,65.64,4110000 +1961-05-01,65.17,65.17,65.17,65.17,3710000 +1961-04-28,65.31,65.31,65.31,65.31,3710000 +1961-04-27,65.46,65.46,65.46,65.46,4450000 +1961-04-26,65.55,65.55,65.55,65.55,4980000 +1961-04-25,65.3,65.3,65.3,65.3,4670000 +1961-04-24,64.4,64.4,64.4,64.4,4590000 +1961-04-21,65.77,65.77,65.77,65.77,4340000 +1961-04-20,65.82,65.82,65.82,65.82,4810000 +1961-04-19,65.81,65.81,65.81,65.81,4870000 +1961-04-18,66.2,66.2,66.2,66.2,4830000 +1961-04-17,68.68,68.68,68.68,68.68,5860000 +1961-04-14,66.37,66.37,66.37,66.37,5240000 +1961-04-13,66.26,66.26,66.26,66.26,4770000 +1961-04-12,66.31,66.31,66.31,66.31,4870000 +1961-04-11,66.62,66.62,66.62,66.62,5230000 +1961-04-10,66.53,66.53,66.53,66.53,5550000 +1961-04-07,65.96,65.96,65.96,65.96,5100000 +1961-04-06,65.61,65.61,65.61,65.61,4910000 +1961-04-05,65.46,65.46,65.46,65.46,5430000 +1961-04-04,65.66,65.66,65.66,65.66,7080000 +1961-04-03,65.6,65.6,65.6,65.6,6470000 +1961-03-30,65.06,65.06,65.06,65.06,5610000 +1961-03-29,64.93,64.93,64.93,64.93,5330000 +1961-03-28,64.38,64.38,64.38,64.38,4630000 +1961-03-27,64.35,64.35,64.35,64.35,4190000 +1961-03-24,64.42,64.42,64.42,64.42,4390000 +1961-03-23,64.53,64.53,64.53,64.53,2170000 +1961-03-22,64.7,64.7,64.7,64.7,5840000 +1961-03-21,64.74,64.74,64.74,64.74,5800000 +1961-03-20,64.86,64.86,64.86,64.86,5780000 +1961-03-17,64,64,64,64,5960000 +1961-03-16,64.21,64.21,64.21,64.21,5610000 +1961-03-15,63.57,63.57,63.57,63.57,4900000 +1961-03-14,63.38,63.38,63.38,63.38,4900000 +1961-03-13,63.66,63.66,63.66,63.66,5080000 +1961-03-10,63.48,63.48,63.48,63.48,5950000 +1961-03-09,63.5,63.5,63.5,63.5,6010000 +1961-03-08,63.44,63.44,63.44,63.44,5910000 +1961-03-07,63.47,63.47,63.47,63.47,5540000 +1961-03-06,64.05,64.05,64.05,64.05,5650000 +1961-03-03,63.95,63.95,63.95,63.95,5530000 +1961-03-02,63.85,63.85,63.85,63.85,5300000 +1961-03-01,63.43,63.43,63.43,63.43,4970000 +1961-02-28,63.44,63.44,63.44,63.44,5830000 +1961-02-27,63.3,63.3,63.3,63.3,5470000 +1961-02-24,62.84,62.84,62.84,62.84,5330000 +1961-02-23,62.59,62.59,62.59,62.59,5620000 +1961-02-21,62.36,62.36,62.36,62.36,5070000 +1961-02-20,62.32,62.32,62.32,62.32,4680000 +1961-02-17,62.1,62.1,62.1,62.1,4640000 +1961-02-16,62.3,62.3,62.3,62.3,5070000 +1961-02-15,61.92,61.92,61.92,61.92,5200000 +1961-02-14,61.41,61.41,61.41,61.41,4490000 +1961-02-13,61.14,61.14,61.14,61.14,3560000 +1961-02-10,61.5,61.5,61.5,61.5,4840000 +1961-02-09,62.02,62.02,62.02,62.02,5590000 +1961-02-08,62.21,62.21,62.21,62.21,4940000 +1961-02-07,61.65,61.65,61.65,61.65,4020000 +1961-02-06,61.76,61.76,61.76,61.76,3890000 +1961-02-03,62.22,62.22,62.22,62.22,5210000 +1961-02-02,62.3,62.3,62.3,62.3,4900000 +1961-02-01,61.9,61.9,61.9,61.9,4380000 +1961-01-31,61.78,61.78,61.78,61.78,4690000 +1961-01-30,61.97,61.97,61.97,61.97,5190000 +1961-01-27,61.24,61.24,61.24,61.24,4510000 +1961-01-26,60.62,60.62,60.62,60.62,4110000 +1961-01-25,60.53,60.53,60.53,60.53,4470000 +1961-01-24,60.45,60.45,60.45,60.45,4280000 +1961-01-23,60.29,60.29,60.29,60.29,4450000 +1961-01-20,59.96,59.96,59.96,59.96,3270000 +1961-01-19,59.77,59.77,59.77,59.77,4740000 +1961-01-18,59.68,59.68,59.68,59.68,4390000 +1961-01-17,59.64,59.64,59.64,59.64,3830000 +1961-01-16,59.58,59.58,59.58,59.58,4510000 +1961-01-13,59.6,59.6,59.6,59.6,4520000 +1961-01-12,59.32,59.32,59.32,59.32,4270000 +1961-01-11,59.14,59.14,59.14,59.14,4370000 +1961-01-10,58.97,58.97,58.97,58.97,4840000 +1961-01-09,58.81,58.81,58.81,58.81,4210000 +1961-01-06,58.4,58.4,58.4,58.4,3620000 +1961-01-05,58.57,58.57,58.57,58.57,4130000 +1961-01-04,58.36,58.36,58.36,58.36,3840000 +1961-01-03,57.57,57.57,57.57,57.57,2770000 +1960-12-30,58.11,58.11,58.11,58.11,5300000 +1960-12-29,58.05,58.05,58.05,58.05,4340000 +1960-12-28,57.78,57.78,57.78,57.78,3620000 +1960-12-27,57.52,57.52,57.52,57.52,3270000 +1960-12-23,57.44,57.44,57.44,57.44,3580000 +1960-12-22,57.39,57.39,57.39,57.39,3820000 +1960-12-21,57.55,57.55,57.55,57.55,4060000 +1960-12-20,57.09,57.09,57.09,57.09,3340000 +1960-12-19,57.13,57.13,57.13,57.13,3630000 +1960-12-16,57.2,57.2,57.2,57.2,3770000 +1960-12-15,56.68,56.68,56.68,56.68,3660000 +1960-12-14,56.84,56.84,56.84,56.84,3880000 +1960-12-13,56.88,56.88,56.88,56.88,3500000 +1960-12-12,56.85,56.85,56.85,56.85,3020000 +1960-12-09,56.65,56.65,56.65,56.65,4460000 +1960-12-08,56.15,56.15,56.15,56.15,3540000 +1960-12-07,56.02,56.02,56.02,56.02,3660000 +1960-12-06,55.47,55.47,55.47,55.47,3360000 +1960-12-05,55.31,55.31,55.31,55.31,3290000 +1960-12-02,55.39,55.39,55.39,55.39,3140000 +1960-12-01,55.3,55.3,55.3,55.3,3090000 +1960-11-30,55.54,55.54,55.54,55.54,3080000 +1960-11-29,55.83,55.83,55.83,55.83,3630000 +1960-11-28,56.03,56.03,56.03,56.03,3860000 +1960-11-25,56.13,56.13,56.13,56.13,3190000 +1960-11-23,55.8,55.8,55.8,55.8,3000000 +1960-11-22,55.72,55.72,55.72,55.72,3430000 +1960-11-21,55.93,55.93,55.93,55.93,3090000 +1960-11-18,55.82,55.82,55.82,55.82,2760000 +1960-11-17,55.55,55.55,55.55,55.55,2450000 +1960-11-16,55.7,55.7,55.7,55.7,3110000 +1960-11-15,55.81,55.81,55.81,55.81,2990000 +1960-11-14,55.59,55.59,55.59,55.59,2660000 +1960-11-11,55.87,55.87,55.87,55.87,2730000 +1960-11-10,56.43,56.43,56.43,56.43,4030000 +1960-11-09,55.35,55.35,55.35,55.35,3450000 +1960-11-07,55.11,55.11,55.11,55.11,3540000 +1960-11-04,54.9,54.9,54.9,54.9,3050000 +1960-11-03,54.43,54.43,54.43,54.43,2580000 +1960-11-02,54.22,54.22,54.22,54.22,2780000 +1960-11-01,53.94,53.94,53.94,53.94,2600000 +1960-10-31,53.39,53.39,53.39,53.39,2460000 +1960-10-28,53.41,53.41,53.41,53.41,2490000 +1960-10-27,53.62,53.62,53.62,53.62,2900000 +1960-10-26,53.05,53.05,53.05,53.05,3020000 +1960-10-25,52.2,52.2,52.2,52.2,3030000 +1960-10-24,52.7,52.7,52.7,52.7,4420000 +1960-10-21,53.72,53.72,53.72,53.72,3090000 +1960-10-20,53.86,53.86,53.86,53.86,2910000 +1960-10-19,54.25,54.25,54.25,54.25,2410000 +1960-10-18,54.35,54.35,54.35,54.35,2220000 +1960-10-17,54.63,54.63,54.63,54.63,2280000 +1960-10-14,54.86,54.86,54.86,54.86,2470000 +1960-10-13,54.57,54.57,54.57,54.57,2220000 +1960-10-12,54.15,54.15,54.15,54.15,1890000 +1960-10-11,54.22,54.22,54.22,54.22,2350000 +1960-10-10,54.14,54.14,54.14,54.14,2030000 +1960-10-07,54.03,54.03,54.03,54.03,2530000 +1960-10-06,53.72,53.72,53.72,53.72,2510000 +1960-10-05,53.39,53.39,53.39,53.39,2650000 +1960-10-04,52.99,52.99,52.99,52.99,2270000 +1960-10-03,53.36,53.36,53.36,53.36,2220000 +1960-09-30,53.52,53.52,53.52,53.52,3370000 +1960-09-29,52.62,52.62,52.62,52.62,2850000 +1960-09-28,52.48,52.48,52.48,52.48,3520000 +1960-09-27,52.94,52.94,52.94,52.94,3170000 +1960-09-26,53.06,53.06,53.06,53.06,3930000 +1960-09-23,53.9,53.9,53.9,53.9,2580000 +1960-09-22,54.36,54.36,54.36,54.36,1970000 +1960-09-21,54.57,54.57,54.57,54.57,2930000 +1960-09-20,54.01,54.01,54.01,54.01,3660000 +1960-09-19,53.86,53.86,53.86,53.86,3790000 +1960-09-16,55.11,55.11,55.11,55.11,2340000 +1960-09-15,55.22,55.22,55.22,55.22,2870000 +1960-09-14,55.44,55.44,55.44,55.44,2530000 +1960-09-13,55.83,55.83,55.83,55.83,2180000 +1960-09-12,55.72,55.72,55.72,55.72,2160000 +1960-09-09,56.11,56.11,56.11,56.11,2750000 +1960-09-08,55.74,55.74,55.74,55.74,2670000 +1960-09-07,55.79,55.79,55.79,55.79,2850000 +1960-09-06,56.49,56.49,56.49,56.49,2580000 +1960-09-02,57,57,57,57,2680000 +1960-09-01,57.09,57.09,57.09,57.09,3460000 +1960-08-31,56.96,56.96,56.96,56.96,3130000 +1960-08-30,56.84,56.84,56.84,56.84,2890000 +1960-08-29,57.44,57.44,57.44,57.44,2780000 +1960-08-26,57.6,57.6,57.6,57.6,2780000 +1960-08-25,57.79,57.79,57.79,57.79,2680000 +1960-08-24,58.07,58.07,58.07,58.07,3500000 +1960-08-23,57.75,57.75,57.75,57.75,3560000 +1960-08-22,57.19,57.19,57.19,57.19,2760000 +1960-08-19,57.01,57.01,57.01,57.01,2570000 +1960-08-18,56.81,56.81,56.81,56.81,2890000 +1960-08-17,56.84,56.84,56.84,56.84,3090000 +1960-08-16,56.72,56.72,56.72,56.72,2710000 +1960-08-15,56.61,56.61,56.61,56.61,2450000 +1960-08-12,56.66,56.66,56.66,56.66,3160000 +1960-08-11,56.28,56.28,56.28,56.28,3070000 +1960-08-10,56.07,56.07,56.07,56.07,2810000 +1960-08-09,55.84,55.84,55.84,55.84,2700000 +1960-08-08,55.52,55.52,55.52,55.52,2960000 +1960-08-05,55.44,55.44,55.44,55.44,3000000 +1960-08-04,54.89,54.89,54.89,54.89,2840000 +1960-08-03,54.72,54.72,54.72,54.72,2470000 +1960-08-02,55.04,55.04,55.04,55.04,2090000 +1960-08-01,55.53,55.53,55.53,55.53,2440000 +1960-07-29,55.51,55.51,55.51,55.51,2730000 +1960-07-28,54.57,54.57,54.57,54.57,3020000 +1960-07-27,54.17,54.17,54.17,54.17,2560000 +1960-07-26,54.51,54.51,54.51,54.51,2720000 +1960-07-25,54.18,54.18,54.18,54.18,2840000 +1960-07-22,54.72,54.72,54.72,54.72,2850000 +1960-07-21,55.1,55.1,55.1,55.1,2510000 +1960-07-20,55.61,55.61,55.61,55.61,2370000 +1960-07-19,55.7,55.7,55.7,55.7,2490000 +1960-07-18,55.7,55.7,55.7,55.7,2350000 +1960-07-15,56.05,56.05,56.05,56.05,2140000 +1960-07-14,56.12,56.12,56.12,56.12,2480000 +1960-07-13,56.1,56.1,56.1,56.1,2590000 +1960-07-12,56.25,56.25,56.25,56.25,2860000 +1960-07-11,56.87,56.87,56.87,56.87,2920000 +1960-07-08,57.38,57.38,57.38,57.38,3010000 +1960-07-07,57.24,57.24,57.24,57.24,3050000 +1960-07-06,56.94,56.94,56.94,56.94,2970000 +1960-07-05,57.02,57.02,57.02,57.02,2780000 +1960-07-01,57.06,57.06,57.06,57.06,2620000 +1960-06-30,56.92,56.92,56.92,56.92,2940000 +1960-06-29,56.94,56.94,56.94,56.94,3160000 +1960-06-28,56.94,56.94,56.94,56.94,3120000 +1960-06-27,57.33,57.33,57.33,57.33,2960000 +1960-06-24,57.68,57.68,57.68,57.68,3220000 +1960-06-23,57.59,57.59,57.59,57.59,3620000 +1960-06-22,57.28,57.28,57.28,57.28,3600000 +1960-06-21,57.11,57.11,57.11,57.11,3860000 +1960-06-20,57.16,57.16,57.16,57.16,3970000 +1960-06-17,57.44,57.44,57.44,57.44,3920000 +1960-06-16,57.5,57.5,57.5,57.5,3540000 +1960-06-15,57.57,57.57,57.57,57.57,3630000 +1960-06-14,57.91,57.91,57.91,57.91,3430000 +1960-06-13,57.99,57.99,57.99,57.99,3180000 +1960-06-10,57.97,57.97,57.97,57.97,2940000 +1960-06-09,58,58,58,58,3820000 +1960-06-08,57.89,57.89,57.89,57.89,3800000 +1960-06-07,57.43,57.43,57.43,57.43,3710000 +1960-06-06,56.89,56.89,56.89,56.89,3220000 +1960-06-03,56.23,56.23,56.23,56.23,3340000 +1960-06-02,56.13,56.13,56.13,56.13,3730000 +1960-06-01,55.89,55.89,55.89,55.89,3770000 +1960-05-31,55.83,55.83,55.83,55.83,3750000 +1960-05-27,55.74,55.74,55.74,55.74,3040000 +1960-05-26,55.71,55.71,55.71,55.71,3720000 +1960-05-25,55.67,55.67,55.67,55.67,3440000 +1960-05-24,55.7,55.7,55.7,55.7,3240000 +1960-05-23,55.76,55.76,55.76,55.76,2530000 +1960-05-20,55.73,55.73,55.73,55.73,3170000 +1960-05-19,55.68,55.68,55.68,55.68,3700000 +1960-05-18,55.44,55.44,55.44,55.44,5240000 +1960-05-17,55.46,55.46,55.46,55.46,4080000 +1960-05-16,55.25,55.25,55.25,55.25,3530000 +1960-05-13,55.3,55.3,55.3,55.3,3750000 +1960-05-12,54.85,54.85,54.85,54.85,3220000 +1960-05-11,54.57,54.57,54.57,54.57,2900000 +1960-05-10,54.42,54.42,54.42,54.42,2870000 +1960-05-09,54.8,54.8,54.8,54.8,2670000 +1960-05-06,54.75,54.75,54.75,54.75,2560000 +1960-05-05,54.86,54.86,54.86,54.86,2670000 +1960-05-04,55.04,55.04,55.04,55.04,2870000 +1960-05-03,54.83,54.83,54.83,54.83,2910000 +1960-05-02,54.13,54.13,54.13,54.13,2930000 +1960-04-29,54.37,54.37,54.37,54.37,2850000 +1960-04-28,54.56,54.56,54.56,54.56,3190000 +1960-04-27,55.04,55.04,55.04,55.04,3020000 +1960-04-26,55.04,55.04,55.04,55.04,2940000 +1960-04-25,54.86,54.86,54.86,54.86,2980000 +1960-04-22,55.42,55.42,55.42,55.42,2850000 +1960-04-21,55.59,55.59,55.59,55.59,2700000 +1960-04-20,55.44,55.44,55.44,55.44,3150000 +1960-04-19,56.13,56.13,56.13,56.13,3080000 +1960-04-18,56.59,56.59,56.59,56.59,3200000 +1960-04-14,56.43,56.43,56.43,56.43,2730000 +1960-04-13,56.3,56.3,56.3,56.3,2730000 +1960-04-12,56.3,56.3,56.3,56.3,2470000 +1960-04-11,56.17,56.17,56.17,56.17,2520000 +1960-04-08,56.39,56.39,56.39,56.39,2820000 +1960-04-07,56.52,56.52,56.52,56.52,3070000 +1960-04-06,56.51,56.51,56.51,56.51,3450000 +1960-04-05,55.37,55.37,55.37,55.37,2840000 +1960-04-04,55.54,55.54,55.54,55.54,2450000 +1960-04-01,55.43,55.43,55.43,55.43,2260000 +1960-03-31,55.34,55.34,55.34,55.34,2690000 +1960-03-30,55.66,55.66,55.66,55.66,2450000 +1960-03-29,55.78,55.78,55.78,55.78,2320000 +1960-03-28,55.86,55.86,55.86,55.86,2500000 +1960-03-25,55.98,55.98,55.98,55.98,2640000 +1960-03-24,55.98,55.98,55.98,55.98,2940000 +1960-03-23,55.74,55.74,55.74,55.74,3020000 +1960-03-22,55.29,55.29,55.29,55.29,2490000 +1960-03-21,55.07,55.07,55.07,55.07,2500000 +1960-03-18,55.01,55.01,55.01,55.01,2620000 +1960-03-17,54.96,54.96,54.96,54.96,2140000 +1960-03-16,55.04,55.04,55.04,55.04,2960000 +1960-03-15,54.74,54.74,54.74,54.74,2690000 +1960-03-14,54.32,54.32,54.32,54.32,2530000 +1960-03-11,54.24,54.24,54.24,54.24,2770000 +1960-03-10,53.83,53.83,53.83,53.83,3350000 +1960-03-09,54.04,54.04,54.04,54.04,3580000 +1960-03-08,53.47,53.47,53.47,53.47,3370000 +1960-03-07,54.02,54.02,54.02,54.02,2900000 +1960-03-04,54.57,54.57,54.57,54.57,4060000 +1960-03-03,54.78,54.78,54.78,54.78,3160000 +1960-03-02,55.62,55.62,55.62,55.62,3110000 +1960-03-01,56.01,56.01,56.01,56.01,2920000 +1960-02-29,56.12,56.12,56.12,56.12,2990000 +1960-02-26,56.16,56.16,56.16,56.16,3380000 +1960-02-25,55.93,55.93,55.93,55.93,3600000 +1960-02-24,55.74,55.74,55.74,55.74,2740000 +1960-02-23,55.94,55.94,55.94,55.94,2960000 +1960-02-19,56.24,56.24,56.24,56.24,3230000 +1960-02-18,55.8,55.8,55.8,55.8,3800000 +1960-02-17,55.03,55.03,55.03,55.03,4210000 +1960-02-16,54.73,54.73,54.73,54.73,3270000 +1960-02-15,55.17,55.17,55.17,55.17,2780000 +1960-02-12,55.46,55.46,55.46,55.46,2230000 +1960-02-11,55.18,55.18,55.18,55.18,2610000 +1960-02-10,55.49,55.49,55.49,55.49,2440000 +1960-02-09,55.84,55.84,55.84,55.84,2860000 +1960-02-08,55.32,55.32,55.32,55.32,3350000 +1960-02-05,55.98,55.98,55.98,55.98,2530000 +1960-02-04,56.27,56.27,56.27,56.27,2600000 +1960-02-03,56.32,56.32,56.32,56.32,3020000 +1960-02-02,56.82,56.82,56.82,56.82,3080000 +1960-02-01,55.96,55.96,55.96,55.96,2820000 +1960-01-29,55.61,55.61,55.61,55.61,3060000 +1960-01-28,56.13,56.13,56.13,56.13,2630000 +1960-01-27,56.72,56.72,56.72,56.72,2460000 +1960-01-26,56.86,56.86,56.86,56.86,3060000 +1960-01-25,56.78,56.78,56.78,56.78,2790000 +1960-01-22,57.38,57.38,57.38,57.38,2690000 +1960-01-21,57.21,57.21,57.21,57.21,2700000 +1960-01-20,57.07,57.07,57.07,57.07,2720000 +1960-01-19,57.27,57.27,57.27,57.27,3100000 +1960-01-18,57.89,57.89,57.89,57.89,3020000 +1960-01-15,58.38,58.38,58.38,58.38,3400000 +1960-01-14,58.4,58.4,58.4,58.4,3560000 +1960-01-13,58.08,58.08,58.08,58.08,3470000 +1960-01-12,58.41,58.41,58.41,58.41,3760000 +1960-01-11,58.77,58.77,58.77,58.77,3470000 +1960-01-08,59.5,59.5,59.5,59.5,3290000 +1960-01-07,59.69,59.69,59.69,59.69,3310000 +1960-01-06,60.13,60.13,60.13,60.13,3730000 +1960-01-05,60.39,60.39,60.39,60.39,3710000 +1960-01-04,59.91,59.91,59.91,59.91,3990000 +1959-12-31,59.89,59.89,59.89,59.89,3810000 +1959-12-30,59.77,59.77,59.77,59.77,3680000 +1959-12-29,59.3,59.3,59.3,59.3,3020000 +1959-12-28,58.98,58.98,58.98,58.98,2830000 +1959-12-24,59,59,59,59,2320000 +1959-12-23,58.96,58.96,58.96,58.96,2890000 +1959-12-22,59.14,59.14,59.14,59.14,2930000 +1959-12-21,59.21,59.21,59.21,59.21,3290000 +1959-12-18,59.14,59.14,59.14,59.14,3230000 +1959-12-17,58.86,58.86,58.86,58.86,3040000 +1959-12-16,58.97,58.97,58.97,58.97,3270000 +1959-12-15,58.9,58.9,58.9,58.9,3450000 +1959-12-14,59.04,59.04,59.04,59.04,3100000 +1959-12-11,58.88,58.88,58.88,58.88,2910000 +1959-12-10,59.02,59.02,59.02,59.02,3170000 +1959-12-09,58.97,58.97,58.97,58.97,3430000 +1959-12-08,59.34,59.34,59.34,59.34,3870000 +1959-12-07,58.96,58.96,58.96,58.96,3620000 +1959-12-04,58.85,58.85,58.85,58.85,3590000 +1959-12-03,58.73,58.73,58.73,58.73,3280000 +1959-12-02,58.6,58.6,58.6,58.6,3490000 +1959-12-01,58.7,58.7,58.7,58.7,3990000 +1959-11-30,58.28,58.28,58.28,58.28,3670000 +1959-11-27,57.7,57.7,57.7,57.7,3030000 +1959-11-25,57.44,57.44,57.44,57.44,3550000 +1959-11-24,57.35,57.35,57.35,57.35,3650000 +1959-11-23,57.08,57.08,57.08,57.08,3400000 +1959-11-20,56.97,56.97,56.97,56.97,2960000 +1959-11-19,56.94,56.94,56.94,56.94,3230000 +1959-11-18,56.99,56.99,56.99,56.99,3660000 +1959-11-17,56.38,56.38,56.38,56.38,3570000 +1959-11-16,56.22,56.22,56.22,56.22,3710000 +1959-11-13,56.85,56.85,56.85,56.85,3050000 +1959-11-12,57.17,57.17,57.17,57.17,3600000 +1959-11-11,57.49,57.49,57.49,57.49,2820000 +1959-11-10,57.48,57.48,57.48,57.48,3020000 +1959-11-09,57.5,57.5,57.5,57.5,3700000 +1959-11-06,57.6,57.6,57.6,57.6,3450000 +1959-11-05,57.32,57.32,57.32,57.32,3170000 +1959-11-04,57.26,57.26,57.26,57.26,3940000 +1959-11-02,57.41,57.41,57.41,57.41,3320000 +1959-10-30,57.52,57.52,57.52,57.52,3560000 +1959-10-29,57.41,57.41,57.41,57.41,3890000 +1959-10-28,57.46,57.46,57.46,57.46,3920000 +1959-10-27,57.42,57.42,57.42,57.42,4160000 +1959-10-26,56.94,56.94,56.94,56.94,3580000 +1959-10-23,56.56,56.56,56.56,56.56,2880000 +1959-10-22,56,56,56,56,3060000 +1959-10-21,56.55,56.55,56.55,56.55,2730000 +1959-10-20,56.66,56.66,56.66,56.66,2740000 +1959-10-19,57.01,57.01,57.01,57.01,2470000 +1959-10-16,57.33,57.33,57.33,57.33,2760000 +1959-10-15,56.87,56.87,56.87,56.87,2190000 +1959-10-14,56.71,56.71,56.71,56.71,2320000 +1959-10-13,57.16,57.16,57.16,57.16,2530000 +1959-10-12,57.32,57.32,57.32,57.32,1750000 +1959-10-09,57,57,57,57,2540000 +1959-10-08,56.81,56.81,56.81,56.81,2510000 +1959-10-07,56.94,56.94,56.94,56.94,2380000 +1959-10-06,57.09,57.09,57.09,57.09,2330000 +1959-10-05,57.14,57.14,57.14,57.14,2100000 +1959-10-02,57.2,57.2,57.2,57.2,2270000 +1959-10-01,56.94,56.94,56.94,56.94,2660000 +1959-09-30,56.88,56.88,56.88,56.88,2850000 +1959-09-29,57.51,57.51,57.51,57.51,3220000 +1959-09-28,57.15,57.15,57.15,57.15,2640000 +1959-09-25,56.73,56.73,56.73,56.73,3280000 +1959-09-24,56.78,56.78,56.78,56.78,3480000 +1959-09-23,55.82,55.82,55.82,55.82,3010000 +1959-09-22,55.14,55.14,55.14,55.14,3000000 +1959-09-21,55.27,55.27,55.27,55.27,3240000 +1959-09-18,56.19,56.19,56.19,56.19,2530000 +1959-09-17,56.41,56.41,56.41,56.41,2090000 +1959-09-16,56.72,56.72,56.72,56.72,2180000 +1959-09-15,56.68,56.68,56.68,56.68,2830000 +1959-09-14,56.99,56.99,56.99,56.99,2590000 +1959-09-11,57.41,57.41,57.41,57.41,2640000 +1959-09-10,56.99,56.99,56.99,56.99,2520000 +1959-09-09,57.29,57.29,57.29,57.29,3030000 +1959-09-08,57.7,57.7,57.7,57.7,2940000 +1959-09-04,58.54,58.54,58.54,58.54,2300000 +1959-09-03,58.26,58.26,58.26,58.26,2330000 +1959-09-02,58.92,58.92,58.92,58.92,2370000 +1959-09-01,58.87,58.87,58.87,58.87,2430000 +1959-08-31,59.6,59.6,59.6,59.6,2140000 +1959-08-28,59.6,59.6,59.6,59.6,1930000 +1959-08-27,59.58,59.58,59.58,59.58,2550000 +1959-08-26,59.07,59.07,59.07,59.07,2210000 +1959-08-25,58.99,58.99,58.99,58.99,1960000 +1959-08-24,58.87,58.87,58.87,58.87,1860000 +1959-08-21,59.08,59.08,59.08,59.08,2000000 +1959-08-20,59.14,59.14,59.14,59.14,2450000 +1959-08-19,58.27,58.27,58.27,58.27,3050000 +1959-08-18,58.62,58.62,58.62,58.62,2280000 +1959-08-17,59.17,59.17,59.17,59.17,1980000 +1959-08-14,59.29,59.29,59.29,59.29,1990000 +1959-08-13,59.15,59.15,59.15,59.15,2020000 +1959-08-12,59.25,59.25,59.25,59.25,2700000 +1959-08-11,59.39,59.39,59.39,59.39,2980000 +1959-08-10,58.62,58.62,58.62,58.62,4190000 +1959-08-07,59.87,59.87,59.87,59.87,2580000 +1959-08-06,60.24,60.24,60.24,60.24,2610000 +1959-08-05,60.3,60.3,60.3,60.3,2630000 +1959-08-04,60.61,60.61,60.61,60.61,2530000 +1959-08-03,60.71,60.71,60.71,60.71,2410000 +1959-07-31,60.51,60.51,60.51,60.51,2270000 +1959-07-30,60.5,60.5,60.5,60.5,3240000 +1959-07-29,60.62,60.62,60.62,60.62,3460000 +1959-07-28,60.32,60.32,60.32,60.32,3190000 +1959-07-27,60.02,60.02,60.02,60.02,2910000 +1959-07-24,59.65,59.65,59.65,59.65,2720000 +1959-07-23,59.67,59.67,59.67,59.67,3310000 +1959-07-22,59.61,59.61,59.61,59.61,3310000 +1959-07-21,59.41,59.41,59.41,59.41,2950000 +1959-07-20,58.91,58.91,58.91,58.91,2500000 +1959-07-17,59.19,59.19,59.19,59.19,2510000 +1959-07-16,59.41,59.41,59.41,59.41,3170000 +1959-07-15,59.59,59.59,59.59,59.59,3280000 +1959-07-14,59.55,59.55,59.55,59.55,3230000 +1959-07-13,59.41,59.41,59.41,59.41,3360000 +1959-07-10,59.91,59.91,59.91,59.91,3600000 +1959-07-09,59.97,59.97,59.97,59.97,3560000 +1959-07-08,60.03,60.03,60.03,60.03,4010000 +1959-07-07,60.01,60.01,60.01,60.01,3840000 +1959-07-06,59.65,59.65,59.65,59.65,3720000 +1959-07-02,59.28,59.28,59.28,59.28,3610000 +1959-07-01,58.97,58.97,58.97,58.97,3150000 +1959-06-30,58.47,58.47,58.47,58.47,3200000 +1959-06-29,58.37,58.37,58.37,58.37,3000000 +1959-06-26,57.98,57.98,57.98,57.98,3100000 +1959-06-25,57.73,57.73,57.73,57.73,3250000 +1959-06-24,57.41,57.41,57.41,57.41,3180000 +1959-06-23,57.12,57.12,57.12,57.12,2600000 +1959-06-22,57.13,57.13,57.13,57.13,2630000 +1959-06-19,57.13,57.13,57.13,57.13,2260000 +1959-06-18,57.05,57.05,57.05,57.05,3150000 +1959-06-17,57.09,57.09,57.09,57.09,2850000 +1959-06-16,56.56,56.56,56.56,56.56,2440000 +1959-06-15,56.99,56.99,56.99,56.99,2410000 +1959-06-12,57.29,57.29,57.29,57.29,2580000 +1959-06-11,57.25,57.25,57.25,57.25,3120000 +1959-06-10,57.19,57.19,57.19,57.19,3310000 +1959-06-09,56.36,56.36,56.36,56.36,3490000 +1959-06-08,56.76,56.76,56.76,56.76,2970000 +1959-06-05,57.51,57.51,57.51,57.51,2800000 +1959-06-04,57.63,57.63,57.63,57.63,3210000 +1959-06-03,58.25,58.25,58.25,58.25,2910000 +1959-06-02,58.23,58.23,58.23,58.23,3120000 +1959-06-01,58.63,58.63,58.63,58.63,2730000 +1959-05-29,58.68,58.68,58.68,58.68,2790000 +1959-05-28,58.39,58.39,58.39,58.39,2970000 +1959-05-27,58.19,58.19,58.19,58.19,2940000 +1959-05-26,58.09,58.09,58.09,58.09,2910000 +1959-05-25,58.18,58.18,58.18,58.18,3260000 +1959-05-22,58.33,58.33,58.33,58.33,3030000 +1959-05-21,58.14,58.14,58.14,58.14,3230000 +1959-05-20,58.09,58.09,58.09,58.09,3550000 +1959-05-19,58.32,58.32,58.32,58.32,3170000 +1959-05-18,58.15,58.15,58.15,58.15,2970000 +1959-05-15,58.16,58.16,58.16,58.16,3510000 +1959-05-14,58.37,58.37,58.37,58.37,3660000 +1959-05-13,57.97,57.97,57.97,57.97,3540000 +1959-05-12,57.96,57.96,57.96,57.96,3550000 +1959-05-11,57.96,57.96,57.96,57.96,3860000 +1959-05-08,57.32,57.32,57.32,57.32,3930000 +1959-05-07,56.88,56.88,56.88,56.88,4530000 +1959-05-06,57.61,57.61,57.61,57.61,4110000 +1959-05-05,57.75,57.75,57.75,57.75,3360000 +1959-05-04,57.65,57.65,57.65,57.65,3060000 +1959-05-01,57.65,57.65,57.65,57.65,3020000 +1959-04-30,57.59,57.59,57.59,57.59,3510000 +1959-04-29,57.69,57.69,57.69,57.69,3470000 +1959-04-28,57.92,57.92,57.92,57.92,3920000 +1959-04-27,58.14,58.14,58.14,58.14,3850000 +1959-04-24,57.96,57.96,57.96,57.96,3790000 +1959-04-23,57.6,57.6,57.6,57.6,3310000 +1959-04-22,57.73,57.73,57.73,57.73,3430000 +1959-04-21,58.11,58.11,58.11,58.11,3650000 +1959-04-20,58.17,58.17,58.17,58.17,3610000 +1959-04-17,57.92,57.92,57.92,57.92,3870000 +1959-04-16,57.43,57.43,57.43,57.43,3790000 +1959-04-15,56.96,56.96,56.96,56.96,3680000 +1959-04-14,56.71,56.71,56.71,56.71,3320000 +1959-04-13,56.43,56.43,56.43,56.43,3140000 +1959-04-10,56.22,56.22,56.22,56.22,3000000 +1959-04-09,56.17,56.17,56.17,56.17,2830000 +1959-04-08,56.21,56.21,56.21,56.21,3260000 +1959-04-07,56.48,56.48,56.48,56.48,3020000 +1959-04-06,56.6,56.6,56.6,56.6,3510000 +1959-04-03,56.44,56.44,56.44,56.44,3680000 +1959-04-02,56,56,56,56,3220000 +1959-04-01,55.69,55.69,55.69,55.69,2980000 +1959-03-31,55.44,55.44,55.44,55.44,2820000 +1959-03-30,55.45,55.45,55.45,55.45,2940000 +1959-03-26,55.76,55.76,55.76,55.76,2900000 +1959-03-25,55.88,55.88,55.88,55.88,3280000 +1959-03-24,55.96,55.96,55.96,55.96,3000000 +1959-03-23,55.87,55.87,55.87,55.87,3700000 +1959-03-20,56.39,56.39,56.39,56.39,3770000 +1959-03-19,56.34,56.34,56.34,56.34,4150000 +1959-03-18,56.39,56.39,56.39,56.39,4530000 +1959-03-17,56.52,56.52,56.52,56.52,4730000 +1959-03-16,56.06,56.06,56.06,56.06,4420000 +1959-03-13,56.67,56.67,56.67,56.67,4880000 +1959-03-12,56.6,56.6,56.6,56.6,4690000 +1959-03-11,56.35,56.35,56.35,56.35,4160000 +1959-03-10,56.31,56.31,56.31,56.31,3920000 +1959-03-09,56.15,56.15,56.15,56.15,3530000 +1959-03-06,56.21,56.21,56.21,56.21,3930000 +1959-03-05,56.43,56.43,56.43,56.43,3930000 +1959-03-04,56.35,56.35,56.35,56.35,4150000 +1959-03-03,56.25,56.25,56.25,56.25,4790000 +1959-03-02,55.73,55.73,55.73,55.73,4210000 +1959-02-27,55.41,55.41,55.41,55.41,4300000 +1959-02-26,55.34,55.34,55.34,55.34,3930000 +1959-02-25,55.24,55.24,55.24,55.24,3780000 +1959-02-24,55.48,55.48,55.48,55.48,4340000 +1959-02-20,55.52,55.52,55.52,55.52,4190000 +1959-02-19,55.5,55.5,55.5,55.5,4160000 +1959-02-18,54.3,54.3,54.3,54.3,3480000 +1959-02-17,54.29,54.29,54.29,54.29,3190000 +1959-02-16,54.5,54.5,54.5,54.5,3480000 +1959-02-13,54.42,54.42,54.42,54.42,3070000 +1959-02-12,54,54,54,54,2630000 +1959-02-11,54.35,54.35,54.35,54.35,3000000 +1959-02-10,54.32,54.32,54.32,54.32,2960000 +1959-02-09,53.58,53.58,53.58,53.58,3130000 +1959-02-06,54.37,54.37,54.37,54.37,3010000 +1959-02-05,54.81,54.81,54.81,54.81,3140000 +1959-02-04,55.06,55.06,55.06,55.06,3170000 +1959-02-03,55.28,55.28,55.28,55.28,3220000 +1959-02-02,55.21,55.21,55.21,55.21,3610000 +1959-01-30,55.45,55.45,55.45,55.45,3600000 +1959-01-29,55.2,55.2,55.2,55.2,3470000 +1959-01-28,55.16,55.16,55.16,55.16,4190000 +1959-01-27,55.78,55.78,55.78,55.78,3480000 +1959-01-26,55.77,55.77,55.77,55.77,3980000 +1959-01-23,56,56,56,56,3600000 +1959-01-22,55.97,55.97,55.97,55.97,4250000 +1959-01-21,56.04,56.04,56.04,56.04,3940000 +1959-01-20,55.72,55.72,55.72,55.72,3680000 +1959-01-19,55.68,55.68,55.68,55.68,3840000 +1959-01-16,55.81,55.81,55.81,55.81,4300000 +1959-01-15,55.83,55.83,55.83,55.83,4500000 +1959-01-14,55.62,55.62,55.62,55.62,4090000 +1959-01-13,55.47,55.47,55.47,55.47,3790000 +1959-01-12,55.78,55.78,55.78,55.78,4320000 +1959-01-09,55.77,55.77,55.77,55.77,4760000 +1959-01-08,55.4,55.4,55.4,55.4,4030000 +1959-01-07,54.89,54.89,54.89,54.89,4140000 +1959-01-06,55.59,55.59,55.59,55.59,3690000 +1959-01-05,55.66,55.66,55.66,55.66,4210000 +1959-01-02,55.44,55.44,55.44,55.44,3380000 +1958-12-31,55.21,55.21,55.21,55.21,3970000 +1958-12-30,54.93,54.93,54.93,54.93,3900000 +1958-12-29,54.74,54.74,54.74,54.74,3790000 +1958-12-24,54.11,54.11,54.11,54.11,3050000 +1958-12-23,53.42,53.42,53.42,53.42,2870000 +1958-12-22,53.71,53.71,53.71,53.71,3030000 +1958-12-19,54.07,54.07,54.07,54.07,3540000 +1958-12-18,54.15,54.15,54.15,54.15,3900000 +1958-12-17,53.92,53.92,53.92,53.92,3900000 +1958-12-16,53.57,53.57,53.57,53.57,3970000 +1958-12-15,53.37,53.37,53.37,53.37,3340000 +1958-12-12,53.22,53.22,53.22,53.22,3140000 +1958-12-11,53.35,53.35,53.35,53.35,4250000 +1958-12-10,53.46,53.46,53.46,53.46,4340000 +1958-12-09,52.82,52.82,52.82,52.82,3790000 +1958-12-08,52.46,52.46,52.46,52.46,3590000 +1958-12-05,52.46,52.46,52.46,52.46,3360000 +1958-12-04,52.55,52.55,52.55,52.55,3630000 +1958-12-03,52.53,52.53,52.53,52.53,3460000 +1958-12-02,52.46,52.46,52.46,52.46,3320000 +1958-12-01,52.69,52.69,52.69,52.69,3800000 +1958-11-28,52.48,52.48,52.48,52.48,4120000 +1958-11-26,51.9,51.9,51.9,51.9,4090000 +1958-11-25,51.02,51.02,51.02,51.02,3940000 +1958-11-24,52.03,52.03,52.03,52.03,4770000 +1958-11-21,52.7,52.7,52.7,52.7,3950000 +1958-11-20,53.21,53.21,53.21,53.21,4320000 +1958-11-19,53.2,53.2,53.2,53.2,4090000 +1958-11-18,53.13,53.13,53.13,53.13,3820000 +1958-11-17,53.24,53.24,53.24,53.24,4540000 +1958-11-14,53.09,53.09,53.09,53.09,4390000 +1958-11-13,52.83,52.83,52.83,52.83,4200000 +1958-11-12,53.05,53.05,53.05,53.05,4440000 +1958-11-11,52.98,52.98,52.98,52.98,4040000 +1958-11-10,52.57,52.57,52.57,52.57,3730000 +1958-11-07,52.26,52.26,52.26,52.26,3700000 +1958-11-06,52.45,52.45,52.45,52.45,4890000 +1958-11-05,52.03,52.03,52.03,52.03,4080000 +1958-11-03,51.56,51.56,51.56,51.56,3240000 +1958-10-31,51.33,51.33,51.33,51.33,3920000 +1958-10-30,51.27,51.27,51.27,51.27,4360000 +1958-10-29,51.07,51.07,51.07,51.07,4790000 +1958-10-28,50.58,50.58,50.58,50.58,3670000 +1958-10-27,50.42,50.42,50.42,50.42,3980000 +1958-10-24,50.81,50.81,50.81,50.81,3770000 +1958-10-23,50.97,50.97,50.97,50.97,3610000 +1958-10-22,51.07,51.07,51.07,51.07,3500000 +1958-10-21,51.27,51.27,51.27,51.27,4010000 +1958-10-20,51.27,51.27,51.27,51.27,4560000 +1958-10-17,51.46,51.46,51.46,51.46,5360000 +1958-10-16,50.94,50.94,50.94,50.94,4560000 +1958-10-15,50.58,50.58,50.58,50.58,4810000 +1958-10-14,51.26,51.26,51.26,51.26,5110000 +1958-10-13,51.62,51.62,51.62,51.62,4550000 +1958-10-10,51.39,51.39,51.39,51.39,4610000 +1958-10-09,51.05,51.05,51.05,51.05,3670000 +1958-10-08,51.06,51.06,51.06,51.06,3680000 +1958-10-07,51.07,51.07,51.07,51.07,3570000 +1958-10-06,51.07,51.07,51.07,51.07,3570000 +1958-10-03,50.37,50.37,50.37,50.37,3830000 +1958-10-02,50.17,50.17,50.17,50.17,3750000 +1958-10-01,49.98,49.98,49.98,49.98,3780000 +1958-09-30,50.06,50.06,50.06,50.06,4160000 +1958-09-29,49.87,49.87,49.87,49.87,3680000 +1958-09-26,49.66,49.66,49.66,49.66,3420000 +1958-09-25,49.57,49.57,49.57,49.57,4490000 +1958-09-24,49.78,49.78,49.78,49.78,3120000 +1958-09-23,49.56,49.56,49.56,49.56,3950000 +1958-09-22,49.2,49.2,49.2,49.2,3490000 +1958-09-19,49.4,49.4,49.4,49.4,3880000 +1958-09-18,49.38,49.38,49.38,49.38,3460000 +1958-09-17,49.35,49.35,49.35,49.35,3790000 +1958-09-16,49.35,49.35,49.35,49.35,3940000 +1958-09-15,48.96,48.96,48.96,48.96,3040000 +1958-09-12,48.53,48.53,48.53,48.53,3100000 +1958-09-11,48.64,48.64,48.64,48.64,3300000 +1958-09-10,48.31,48.31,48.31,48.31,2820000 +1958-09-09,48.46,48.46,48.46,48.46,3480000 +1958-09-08,48.13,48.13,48.13,48.13,3030000 +1958-09-05,47.97,47.97,47.97,47.97,2520000 +1958-09-04,48.1,48.1,48.1,48.1,3100000 +1958-09-03,48.18,48.18,48.18,48.18,3240000 +1958-09-02,48,48,48,48,2930000 +1958-08-29,47.75,47.75,47.75,47.75,2260000 +1958-08-28,47.66,47.66,47.66,47.66,2540000 +1958-08-27,47.91,47.91,47.91,47.91,3250000 +1958-08-26,47.9,47.9,47.9,47.9,2910000 +1958-08-25,47.74,47.74,47.74,47.74,2610000 +1958-08-22,47.73,47.73,47.73,47.73,2660000 +1958-08-21,47.63,47.63,47.63,47.63,2500000 +1958-08-20,47.32,47.32,47.32,47.32,2460000 +1958-08-19,47.3,47.3,47.3,47.3,2250000 +1958-08-18,47.22,47.22,47.22,47.22,2390000 +1958-08-15,47.5,47.5,47.5,47.5,2960000 +1958-08-14,47.91,47.91,47.91,47.91,3370000 +1958-08-13,47.81,47.81,47.81,47.81,2790000 +1958-08-12,47.73,47.73,47.73,47.73,2600000 +1958-08-11,48.16,48.16,48.16,48.16,2870000 +1958-08-08,48.05,48.05,48.05,48.05,3650000 +1958-08-07,47.77,47.77,47.77,47.77,3200000 +1958-08-06,47.46,47.46,47.46,47.46,3440000 +1958-08-05,47.75,47.75,47.75,47.75,4210000 +1958-08-04,47.94,47.94,47.94,47.94,4000000 +1958-08-01,47.49,47.49,47.49,47.49,3380000 +1958-07-31,47.19,47.19,47.19,47.19,4440000 +1958-07-30,47.09,47.09,47.09,47.09,3680000 +1958-07-29,46.96,46.96,46.96,46.96,3310000 +1958-07-28,47.15,47.15,47.15,47.15,3940000 +1958-07-25,46.97,46.97,46.97,46.97,4430000 +1958-07-24,46.65,46.65,46.65,46.65,3740000 +1958-07-23,46.4,46.4,46.4,46.4,3550000 +1958-07-22,46.41,46.41,46.41,46.41,3420000 +1958-07-21,46.33,46.33,46.33,46.33,3440000 +1958-07-18,45.77,45.77,45.77,45.77,3350000 +1958-07-17,45.55,45.55,45.55,45.55,3180000 +1958-07-16,45.25,45.25,45.25,45.25,3240000 +1958-07-15,45.11,45.11,45.11,45.11,3090000 +1958-07-14,45.14,45.14,45.14,45.14,2540000 +1958-07-11,45.72,45.72,45.72,45.72,2400000 +1958-07-10,45.42,45.42,45.42,45.42,2510000 +1958-07-09,45.25,45.25,45.25,45.25,2630000 +1958-07-08,45.4,45.4,45.4,45.4,2430000 +1958-07-07,45.62,45.62,45.62,45.62,2510000 +1958-07-03,45.47,45.47,45.47,45.47,2630000 +1958-07-02,45.32,45.32,45.32,45.32,2370000 +1958-07-01,45.28,45.28,45.28,45.28,2600000 +1958-06-30,45.24,45.24,45.24,45.24,2820000 +1958-06-27,44.9,44.9,44.9,44.9,2800000 +1958-06-26,44.84,44.84,44.84,44.84,2910000 +1958-06-25,44.63,44.63,44.63,44.63,2720000 +1958-06-24,44.52,44.52,44.52,44.52,2560000 +1958-06-23,44.69,44.69,44.69,44.69,2340000 +1958-06-20,44.85,44.85,44.85,44.85,2590000 +1958-06-19,44.61,44.61,44.61,44.61,2690000 +1958-06-18,45.34,45.34,45.34,45.34,2640000 +1958-06-17,44.94,44.94,44.94,44.94,2950000 +1958-06-16,45.18,45.18,45.18,45.18,2870000 +1958-06-13,45.02,45.02,45.02,45.02,3100000 +1958-06-12,44.75,44.75,44.75,44.75,2760000 +1958-06-11,44.49,44.49,44.49,44.49,2570000 +1958-06-10,44.48,44.48,44.48,44.48,2390000 +1958-06-09,44.57,44.57,44.57,44.57,2380000 +1958-06-06,44.64,44.64,44.64,44.64,2680000 +1958-06-05,44.55,44.55,44.55,44.55,2600000 +1958-06-04,44.5,44.5,44.5,44.5,2690000 +1958-06-03,44.46,44.46,44.46,44.46,2780000 +1958-06-02,44.31,44.31,44.31,44.31,2770000 +1958-05-29,44.09,44.09,44.09,44.09,2350000 +1958-05-28,43.85,43.85,43.85,43.85,2260000 +1958-05-27,43.79,43.79,43.79,43.79,2180000 +1958-05-26,43.85,43.85,43.85,43.85,2500000 +1958-05-23,43.87,43.87,43.87,43.87,2570000 +1958-05-22,43.78,43.78,43.78,43.78,2950000 +1958-05-21,43.55,43.55,43.55,43.55,2580000 +1958-05-20,43.61,43.61,43.61,43.61,2500000 +1958-05-19,43.24,43.24,43.24,43.24,1910000 +1958-05-16,43.36,43.36,43.36,43.36,2030000 +1958-05-15,43.34,43.34,43.34,43.34,2470000 +1958-05-14,43.12,43.12,43.12,43.12,3060000 +1958-05-13,43.62,43.62,43.62,43.62,2940000 +1958-05-12,43.75,43.75,43.75,43.75,2780000 +1958-05-09,44.09,44.09,44.09,44.09,2760000 +1958-05-08,43.99,43.99,43.99,43.99,2790000 +1958-05-07,43.93,43.93,43.93,43.93,2770000 +1958-05-06,44.01,44.01,44.01,44.01,3110000 +1958-05-05,43.79,43.79,43.79,43.79,2670000 +1958-05-02,43.69,43.69,43.69,43.69,2290000 +1958-05-01,43.54,43.54,43.54,43.54,2630000 +1958-04-30,43.44,43.44,43.44,43.44,2900000 +1958-04-29,43,43,43,43,2190000 +1958-04-28,43.22,43.22,43.22,43.22,2400000 +1958-04-25,43.36,43.36,43.36,43.36,3020000 +1958-04-24,43.14,43.14,43.14,43.14,2870000 +1958-04-23,42.8,42.8,42.8,42.8,2720000 +1958-04-22,42.8,42.8,42.8,42.8,2440000 +1958-04-21,42.93,42.93,42.93,42.93,2550000 +1958-04-18,42.71,42.71,42.71,42.71,2700000 +1958-04-17,42.25,42.25,42.25,42.25,2500000 +1958-04-16,42.1,42.1,42.1,42.1,2240000 +1958-04-15,42.43,42.43,42.43,42.43,2590000 +1958-04-14,42,42,42,42,2180000 +1958-04-11,41.74,41.74,41.74,41.74,2060000 +1958-04-10,41.7,41.7,41.7,41.7,2000000 +1958-04-09,41.65,41.65,41.65,41.65,2040000 +1958-04-08,41.43,41.43,41.43,41.43,2190000 +1958-04-07,41.33,41.33,41.33,41.33,2090000 +1958-04-03,41.48,41.48,41.48,41.48,2130000 +1958-04-02,41.6,41.6,41.6,41.6,2390000 +1958-04-01,41.93,41.93,41.93,41.93,2070000 +1958-03-31,42.1,42.1,42.1,42.1,2050000 +1958-03-28,42.2,42.2,42.2,42.2,1930000 +1958-03-27,42.17,42.17,42.17,42.17,2140000 +1958-03-26,42.3,42.3,42.3,42.3,1990000 +1958-03-25,42.44,42.44,42.44,42.44,2210000 +1958-03-24,42.58,42.58,42.58,42.58,2580000 +1958-03-21,42.42,42.42,42.42,42.42,2430000 +1958-03-20,42.11,42.11,42.11,42.11,2280000 +1958-03-19,42.09,42.09,42.09,42.09,2410000 +1958-03-18,41.89,41.89,41.89,41.89,2070000 +1958-03-17,42.04,42.04,42.04,42.04,2130000 +1958-03-14,42.33,42.33,42.33,42.33,2150000 +1958-03-13,42.46,42.46,42.46,42.46,2830000 +1958-03-12,42.41,42.41,42.41,42.41,2420000 +1958-03-11,42.51,42.51,42.51,42.51,2640000 +1958-03-10,42.21,42.21,42.21,42.21,1980000 +1958-03-07,42.07,42.07,42.07,42.07,2130000 +1958-03-06,42,42,42,42,2470000 +1958-03-05,41.47,41.47,41.47,41.47,2020000 +1958-03-04,41.35,41.35,41.35,41.35,2010000 +1958-03-03,41.13,41.13,41.13,41.13,1810000 +1958-02-28,40.84,40.84,40.84,40.84,1580000 +1958-02-27,40.68,40.68,40.68,40.68,1670000 +1958-02-26,40.92,40.92,40.92,40.92,1880000 +1958-02-25,40.61,40.61,40.61,40.61,1920000 +1958-02-24,40.65,40.65,40.65,40.65,1570000 +1958-02-21,40.88,40.88,40.88,40.88,1700000 +1958-02-20,40.91,40.91,40.91,40.91,2060000 +1958-02-19,41.15,41.15,41.15,41.15,2070000 +1958-02-18,41.17,41.17,41.17,41.17,1680000 +1958-02-17,41.11,41.11,41.11,41.11,1700000 +1958-02-14,41.33,41.33,41.33,41.33,2070000 +1958-02-13,40.94,40.94,40.94,40.94,1880000 +1958-02-12,40.93,40.93,40.93,40.93,2030000 +1958-02-11,41.11,41.11,41.11,41.11,2110000 +1958-02-10,41.48,41.48,41.48,41.48,1900000 +1958-02-07,41.73,41.73,41.73,41.73,2220000 +1958-02-06,42.1,42.1,42.1,42.1,2210000 +1958-02-05,42.19,42.19,42.19,42.19,2480000 +1958-02-04,42.46,42.46,42.46,42.46,2970000 +1958-02-03,42.04,42.04,42.04,42.04,2490000 +1958-01-31,41.7,41.7,41.7,41.7,2030000 +1958-01-30,41.68,41.68,41.68,41.68,2150000 +1958-01-29,41.88,41.88,41.88,41.88,2220000 +1958-01-28,41.63,41.63,41.63,41.63,2030000 +1958-01-27,41.59,41.59,41.59,41.59,2320000 +1958-01-24,41.71,41.71,41.71,41.71,2830000 +1958-01-23,41.36,41.36,41.36,41.36,1910000 +1958-01-22,41.2,41.2,41.2,41.2,2390000 +1958-01-21,41.3,41.3,41.3,41.3,2160000 +1958-01-20,41.35,41.35,41.35,41.35,2310000 +1958-01-17,41.1,41.1,41.1,41.1,2200000 +1958-01-16,41.06,41.06,41.06,41.06,3950000 +1958-01-15,40.99,40.99,40.99,40.99,2080000 +1958-01-14,40.67,40.67,40.67,40.67,2010000 +1958-01-13,40.49,40.49,40.49,40.49,1860000 +1958-01-10,40.37,40.37,40.37,40.37,2010000 +1958-01-09,40.75,40.75,40.75,40.75,2180000 +1958-01-08,40.99,40.99,40.99,40.99,2230000 +1958-01-07,41,41,41,41,2220000 +1958-01-06,40.68,40.68,40.68,40.68,2500000 +1958-01-03,40.87,40.87,40.87,40.87,2440000 +1958-01-02,40.33,40.33,40.33,40.33,1800000 +1957-12-31,39.99,39.99,39.99,39.99,5070000 +1957-12-30,39.58,39.58,39.58,39.58,3750000 +1957-12-27,39.78,39.78,39.78,39.78,2620000 +1957-12-26,39.92,39.92,39.92,39.92,2280000 +1957-12-24,39.52,39.52,39.52,39.52,2220000 +1957-12-23,39.48,39.48,39.48,39.48,2790000 +1957-12-20,39.48,39.48,39.48,39.48,2500000 +1957-12-19,39.8,39.8,39.8,39.8,2740000 +1957-12-18,39.38,39.38,39.38,39.38,2750000 +1957-12-17,39.42,39.42,39.42,39.42,2820000 +1957-12-16,40.12,40.12,40.12,40.12,2350000 +1957-12-13,40.73,40.73,40.73,40.73,2310000 +1957-12-12,40.55,40.55,40.55,40.55,2330000 +1957-12-11,40.51,40.51,40.51,40.51,2240000 +1957-12-10,40.56,40.56,40.56,40.56,2360000 +1957-12-09,40.92,40.92,40.92,40.92,2230000 +1957-12-06,41.31,41.31,41.31,41.31,2350000 +1957-12-05,41.52,41.52,41.52,41.52,2020000 +1957-12-04,41.54,41.54,41.54,41.54,2220000 +1957-12-03,41.37,41.37,41.37,41.37,2060000 +1957-12-02,41.36,41.36,41.36,41.36,2430000 +1957-11-29,41.72,41.72,41.72,41.72,2740000 +1957-11-27,41.25,41.25,41.25,41.25,3330000 +1957-11-26,40.09,40.09,40.09,40.09,3650000 +1957-11-25,41.18,41.18,41.18,41.18,2600000 +1957-11-22,40.87,40.87,40.87,40.87,2850000 +1957-11-21,40.48,40.48,40.48,40.48,2900000 +1957-11-20,39.92,39.92,39.92,39.92,2400000 +1957-11-19,39.81,39.81,39.81,39.81,2240000 +1957-11-18,40.04,40.04,40.04,40.04,2110000 +1957-11-15,40.37,40.37,40.37,40.37,3510000 +1957-11-14,39.44,39.44,39.44,39.44,2450000 +1957-11-13,39.55,39.55,39.55,39.55,2120000 +1957-11-12,39.6,39.6,39.6,39.6,2050000 +1957-11-11,40.18,40.18,40.18,40.18,1540000 +1957-11-08,40.19,40.19,40.19,40.19,2140000 +1957-11-07,40.67,40.67,40.67,40.67,2580000 +1957-11-06,40.43,40.43,40.43,40.43,2550000 +1957-11-04,40.37,40.37,40.37,40.37,2380000 +1957-11-01,40.44,40.44,40.44,40.44,2060000 +1957-10-31,41.06,41.06,41.06,41.06,2170000 +1957-10-30,41.02,41.02,41.02,41.02,2060000 +1957-10-29,40.69,40.69,40.69,40.69,1860000 +1957-10-28,40.42,40.42,40.42,40.42,1800000 +1957-10-25,40.59,40.59,40.59,40.59,2400000 +1957-10-24,40.71,40.71,40.71,40.71,4030000 +1957-10-23,40.73,40.73,40.73,40.73,4600000 +1957-10-22,38.98,38.98,38.98,38.98,5090000 +1957-10-21,39.15,39.15,39.15,39.15,4670000 +1957-10-18,40.33,40.33,40.33,40.33,2670000 +1957-10-17,40.65,40.65,40.65,40.65,3060000 +1957-10-16,41.33,41.33,41.33,41.33,2050000 +1957-10-15,41.67,41.67,41.67,41.67,2620000 +1957-10-14,41.24,41.24,41.24,41.24,2770000 +1957-10-11,40.94,40.94,40.94,40.94,4460000 +1957-10-10,40.96,40.96,40.96,40.96,3300000 +1957-10-09,41.99,41.99,41.99,41.99,2120000 +1957-10-08,41.95,41.95,41.95,41.95,3190000 +1957-10-07,42.22,42.22,42.22,42.22,2490000 +1957-10-04,42.79,42.79,42.79,42.79,1520000 +1957-10-03,43.14,43.14,43.14,43.14,1590000 +1957-10-02,43.1,43.1,43.1,43.1,1760000 +1957-10-01,42.76,42.76,42.76,42.76,1680000 +1957-09-30,42.42,42.42,42.42,42.42,1520000 +1957-09-27,42.55,42.55,42.55,42.55,1750000 +1957-09-26,42.57,42.57,42.57,42.57,2130000 +1957-09-25,42.98,42.98,42.98,42.98,2770000 +1957-09-24,42.98,42.98,42.98,42.98,2840000 +1957-09-23,42.69,42.69,42.69,42.69,3160000 +1957-09-20,43.69,43.69,43.69,43.69,2340000 +1957-09-19,44.4,44.4,44.4,44.4,1520000 +1957-09-18,44.69,44.69,44.69,44.69,1540000 +1957-09-17,44.64,44.64,44.64,44.64,1490000 +1957-09-16,44.58,44.58,44.58,44.58,1290000 +1957-09-13,44.8,44.8,44.8,44.8,1620000 +1957-09-12,44.82,44.82,44.82,44.82,2010000 +1957-09-11,44.26,44.26,44.26,44.26,2130000 +1957-09-10,43.87,43.87,43.87,43.87,1870000 +1957-09-09,44.28,44.28,44.28,44.28,1420000 +1957-09-06,44.68,44.68,44.68,44.68,1320000 +1957-09-05,44.82,44.82,44.82,44.82,1420000 +1957-09-04,45.05,45.05,45.05,45.05,1260000 +1957-09-03,45.44,45.44,45.44,45.44,1490000 +1957-08-30,45.22,45.22,45.22,45.22,1600000 +1957-08-29,44.46,44.46,44.46,44.46,1630000 +1957-08-28,44.64,44.64,44.64,44.64,1840000 +1957-08-27,44.61,44.61,44.61,44.61,2250000 +1957-08-26,43.89,43.89,43.89,43.89,2680000 +1957-08-23,44.51,44.51,44.51,44.51,1960000 +1957-08-22,45.16,45.16,45.16,45.16,1500000 +1957-08-21,45.49,45.49,45.49,45.49,1720000 +1957-08-20,45.29,45.29,45.29,45.29,2700000 +1957-08-19,44.91,44.91,44.91,44.91,2040000 +1957-08-16,45.83,45.83,45.83,45.83,1470000 +1957-08-15,45.75,45.75,45.75,45.75,2040000 +1957-08-14,45.73,45.73,45.73,45.73,2040000 +1957-08-13,46.3,46.3,46.3,46.3,1580000 +1957-08-12,46.33,46.33,46.33,46.33,1650000 +1957-08-09,46.92,46.92,46.92,46.92,1570000 +1957-08-08,46.9,46.9,46.9,46.9,1690000 +1957-08-07,47.03,47.03,47.03,47.03,2460000 +1957-08-06,46.67,46.67,46.67,46.67,1910000 +1957-08-05,47.26,47.26,47.26,47.26,1790000 +1957-08-02,47.68,47.68,47.68,47.68,1610000 +1957-08-01,47.79,47.79,47.79,47.79,1660000 +1957-07-31,47.91,47.91,47.91,47.91,1830000 +1957-07-30,47.92,47.92,47.92,47.92,1780000 +1957-07-29,47.92,47.92,47.92,47.92,1990000 +1957-07-26,48.45,48.45,48.45,48.45,1710000 +1957-07-25,48.61,48.61,48.61,48.61,1800000 +1957-07-24,48.61,48.61,48.61,48.61,1730000 +1957-07-23,48.56,48.56,48.56,48.56,1840000 +1957-07-22,48.47,48.47,48.47,48.47,1950000 +1957-07-19,48.58,48.58,48.58,48.58,1930000 +1957-07-18,48.53,48.53,48.53,48.53,2130000 +1957-07-17,48.58,48.58,48.58,48.58,2060000 +1957-07-16,48.88,48.88,48.88,48.88,2510000 +1957-07-15,49.13,49.13,49.13,49.13,2480000 +1957-07-12,49.08,49.08,49.08,49.08,2240000 +1957-07-11,48.86,48.86,48.86,48.86,2830000 +1957-07-10,49,49,49,49,2880000 +1957-07-09,48.9,48.9,48.9,48.9,2450000 +1957-07-08,48.9,48.9,48.9,48.9,2840000 +1957-07-05,48.69,48.69,48.69,48.69,2240000 +1957-07-03,48.46,48.46,48.46,48.46,2720000 +1957-07-02,47.9,47.9,47.9,47.9,2450000 +1957-07-01,47.43,47.43,47.43,47.43,1840000 +1957-06-28,47.37,47.37,47.37,47.37,1770000 +1957-06-27,47.26,47.26,47.26,47.26,1800000 +1957-06-26,47.09,47.09,47.09,47.09,1870000 +1957-06-25,47.15,47.15,47.15,47.15,2000000 +1957-06-24,46.78,46.78,46.78,46.78,2040000 +1957-06-21,47.15,47.15,47.15,47.15,1970000 +1957-06-20,47.43,47.43,47.43,47.43,2050000 +1957-06-19,47.72,47.72,47.72,47.72,2220000 +1957-06-18,48.04,48.04,48.04,48.04,2440000 +1957-06-17,48.24,48.24,48.24,48.24,2220000 +1957-06-14,48.15,48.15,48.15,48.15,2090000 +1957-06-13,48.14,48.14,48.14,48.14,2630000 +1957-06-12,48.05,48.05,48.05,48.05,2600000 +1957-06-11,47.94,47.94,47.94,47.94,2850000 +1957-06-10,47.9,47.9,47.9,47.9,2050000 +1957-06-07,47.85,47.85,47.85,47.85,2380000 +1957-06-06,47.8,47.8,47.8,47.8,2300000 +1957-06-05,47.27,47.27,47.27,47.27,1940000 +1957-06-04,47.28,47.28,47.28,47.28,2200000 +1957-06-03,47.37,47.37,47.37,47.37,2050000 +1957-05-31,47.43,47.43,47.43,47.43,2050000 +1957-05-29,47.11,47.11,47.11,47.11,2270000 +1957-05-28,46.69,46.69,46.69,46.69,2070000 +1957-05-27,46.78,46.78,46.78,46.78,2290000 +1957-05-24,47.21,47.21,47.21,47.21,2340000 +1957-05-23,47.15,47.15,47.15,47.15,2110000 +1957-05-22,47.14,47.14,47.14,47.14,2060000 +1957-05-21,47.33,47.33,47.33,47.33,2370000 +1957-05-20,47.35,47.35,47.35,47.35,2300000 +1957-05-17,47.15,47.15,47.15,47.15,2510000 +1957-05-16,47.02,47.02,47.02,47.02,2690000 +1957-05-15,46.83,46.83,46.83,46.83,2590000 +1957-05-14,46.67,46.67,46.67,46.67,2580000 +1957-05-13,46.88,46.88,46.88,46.88,2720000 +1957-05-10,46.59,46.59,46.59,46.59,2430000 +1957-05-09,46.36,46.36,46.36,46.36,2520000 +1957-05-08,46.31,46.31,46.31,46.31,2590000 +1957-05-07,46.13,46.13,46.13,46.13,2300000 +1957-05-06,46.27,46.27,46.27,46.27,2210000 +1957-05-03,46.34,46.34,46.34,46.34,2390000 +1957-05-02,46.39,46.39,46.39,46.39,2860000 +1957-05-01,46.02,46.02,46.02,46.02,2310000 +1957-04-30,45.74,45.74,45.74,45.74,2200000 +1957-04-29,45.73,45.73,45.73,45.73,2290000 +1957-04-26,45.5,45.5,45.5,45.5,2380000 +1957-04-25,45.56,45.56,45.56,45.56,2640000 +1957-04-24,45.72,45.72,45.72,45.72,2990000 +1957-04-23,45.65,45.65,45.65,45.65,2840000 +1957-04-22,45.48,45.48,45.48,45.48,2560000 +1957-04-18,45.41,45.41,45.41,45.41,2480000 +1957-04-17,45.08,45.08,45.08,45.08,2290000 +1957-04-16,45.02,45.02,45.02,45.02,1890000 +1957-04-15,44.95,44.95,44.95,44.95,2010000 +1957-04-12,44.98,44.98,44.98,44.98,2370000 +1957-04-11,44.98,44.98,44.98,44.98,2350000 +1957-04-10,44.98,44.98,44.98,44.98,2920000 +1957-04-09,44.79,44.79,44.79,44.79,2400000 +1957-04-08,44.39,44.39,44.39,44.39,1950000 +1957-04-05,44.49,44.49,44.49,44.49,1830000 +1957-04-04,44.44,44.44,44.44,44.44,1820000 +1957-04-03,44.54,44.54,44.54,44.54,2160000 +1957-04-02,44.42,44.42,44.42,44.42,2300000 +1957-04-01,44.14,44.14,44.14,44.14,1620000 +1957-03-29,44.11,44.11,44.11,44.11,1650000 +1957-03-28,44.18,44.18,44.18,44.18,1930000 +1957-03-27,44.09,44.09,44.09,44.09,1710000 +1957-03-26,43.91,43.91,43.91,43.91,1660000 +1957-03-25,43.88,43.88,43.88,43.88,1590000 +1957-03-22,44.06,44.06,44.06,44.06,1610000 +1957-03-21,44.11,44.11,44.11,44.11,1630000 +1957-03-20,44.1,44.1,44.1,44.1,1830000 +1957-03-19,44.04,44.04,44.04,44.04,1540000 +1957-03-18,43.85,43.85,43.85,43.85,1450000 +1957-03-15,44.05,44.05,44.05,44.05,1600000 +1957-03-14,44.07,44.07,44.07,44.07,1580000 +1957-03-13,44.04,44.04,44.04,44.04,1840000 +1957-03-12,43.75,43.75,43.75,43.75,1600000 +1957-03-11,43.78,43.78,43.78,43.78,1650000 +1957-03-08,44.07,44.07,44.07,44.07,1630000 +1957-03-07,44.21,44.21,44.21,44.21,1830000 +1957-03-06,44.23,44.23,44.23,44.23,1840000 +1957-03-05,44.22,44.22,44.22,44.22,1860000 +1957-03-04,44.06,44.06,44.06,44.06,1890000 +1957-03-01,43.74,43.74,43.74,43.74,1700000 +1957-02-28,43.26,43.26,43.26,43.26,1620000 +1957-02-27,43.41,43.41,43.41,43.41,1620000 +1957-02-26,43.45,43.45,43.45,43.45,1580000 +1957-02-25,43.38,43.38,43.38,43.38,1710000 +1957-02-21,43.48,43.48,43.48,43.48,1680000 +1957-02-20,43.63,43.63,43.63,43.63,1790000 +1957-02-19,43.49,43.49,43.49,43.49,1670000 +1957-02-18,43.46,43.46,43.46,43.46,1800000 +1957-02-15,43.51,43.51,43.51,43.51,2060000 +1957-02-14,42.99,42.99,42.99,42.99,2220000 +1957-02-13,43.04,43.04,43.04,43.04,2380000 +1957-02-12,42.39,42.39,42.39,42.39,2550000 +1957-02-11,42.57,42.57,42.57,42.57,2740000 +1957-02-08,43.32,43.32,43.32,43.32,2120000 +1957-02-07,43.62,43.62,43.62,43.62,1840000 +1957-02-06,43.82,43.82,43.82,43.82,2110000 +1957-02-05,43.89,43.89,43.89,43.89,2610000 +1957-02-04,44.53,44.53,44.53,44.53,1750000 +1957-02-01,44.62,44.62,44.62,44.62,1680000 +1957-01-31,44.72,44.72,44.72,44.72,1920000 +1957-01-30,44.91,44.91,44.91,44.91,1950000 +1957-01-29,44.71,44.71,44.71,44.71,1800000 +1957-01-28,44.49,44.49,44.49,44.49,1700000 +1957-01-25,44.82,44.82,44.82,44.82,2010000 +1957-01-24,45.03,45.03,45.03,45.03,1910000 +1957-01-23,44.87,44.87,44.87,44.87,1920000 +1957-01-22,44.53,44.53,44.53,44.53,1920000 +1957-01-21,44.4,44.4,44.4,44.4,2740000 +1957-01-18,44.64,44.64,44.64,44.64,2400000 +1957-01-17,45.22,45.22,45.22,45.22,2140000 +1957-01-16,45.23,45.23,45.23,45.23,2210000 +1957-01-15,45.18,45.18,45.18,45.18,2370000 +1957-01-14,45.86,45.86,45.86,45.86,2350000 +1957-01-11,46.18,46.18,46.18,46.18,2340000 +1957-01-10,46.27,46.27,46.27,46.27,2470000 +1957-01-09,46.16,46.16,46.16,46.16,2330000 +1957-01-08,46.25,46.25,46.25,46.25,2230000 +1957-01-07,46.42,46.42,46.42,46.42,2500000 +1957-01-04,46.66,46.66,46.66,46.66,2710000 +1957-01-03,46.6,46.6,46.6,46.6,2260000 +1957-01-02,46.2,46.2,46.2,46.2,1960000 +1956-12-31,46.67,46.67,46.67,46.67,3680000 +1956-12-28,46.56,46.56,46.56,46.56,2790000 +1956-12-27,46.35,46.35,46.35,46.35,2420000 +1956-12-26,46.39,46.39,46.39,46.39,2440000 +1956-12-21,46.37,46.37,46.37,46.37,2380000 +1956-12-20,46.07,46.07,46.07,46.07,2060000 +1956-12-19,46.43,46.43,46.43,46.43,1900000 +1956-12-18,46.54,46.54,46.54,46.54,2370000 +1956-12-17,46.54,46.54,46.54,46.54,2500000 +1956-12-14,46.54,46.54,46.54,46.54,2450000 +1956-12-13,46.5,46.5,46.5,46.5,2370000 +1956-12-12,46.13,46.13,46.13,46.13,2180000 +1956-12-11,46.48,46.48,46.48,46.48,2210000 +1956-12-10,46.8,46.8,46.8,46.8,2600000 +1956-12-07,47.04,47.04,47.04,47.04,2400000 +1956-12-06,46.81,46.81,46.81,46.81,2470000 +1956-12-05,46.39,46.39,46.39,46.39,2360000 +1956-12-04,45.84,45.84,45.84,45.84,2180000 +1956-12-03,45.98,45.98,45.98,45.98,2570000 +1956-11-30,45.08,45.08,45.08,45.08,2300000 +1956-11-29,44.38,44.38,44.38,44.38,2440000 +1956-11-28,44.43,44.43,44.43,44.43,2190000 +1956-11-27,44.91,44.91,44.91,44.91,2130000 +1956-11-26,44.87,44.87,44.87,44.87,2230000 +1956-11-23,45.14,45.14,45.14,45.14,1880000 +1956-11-21,44.67,44.67,44.67,44.67,2310000 +1956-11-20,44.89,44.89,44.89,44.89,2240000 +1956-11-19,45.29,45.29,45.29,45.29,2560000 +1956-11-16,45.74,45.74,45.74,45.74,1820000 +1956-11-15,45.72,45.72,45.72,45.72,2210000 +1956-11-14,46.01,46.01,46.01,46.01,2290000 +1956-11-13,46.27,46.27,46.27,46.27,2140000 +1956-11-12,46.49,46.49,46.49,46.49,1600000 +1956-11-09,46.34,46.34,46.34,46.34,1690000 +1956-11-08,46.73,46.73,46.73,46.73,1970000 +1956-11-07,47.11,47.11,47.11,47.11,2650000 +1956-11-05,47.6,47.6,47.6,47.6,2830000 +1956-11-02,46.98,46.98,46.98,46.98,2180000 +1956-11-01,46.52,46.52,46.52,46.52,1890000 +1956-10-31,45.58,45.58,45.58,45.58,2280000 +1956-10-30,46.37,46.37,46.37,46.37,1830000 +1956-10-29,46.4,46.4,46.4,46.4,2420000 +1956-10-26,46.27,46.27,46.27,46.27,1800000 +1956-10-25,45.85,45.85,45.85,45.85,1580000 +1956-10-24,45.93,45.93,45.93,45.93,1640000 +1956-10-23,46.12,46.12,46.12,46.12,1390000 +1956-10-22,46.23,46.23,46.23,46.23,1430000 +1956-10-19,46.24,46.24,46.24,46.24,1720000 +1956-10-18,46.34,46.34,46.34,46.34,1640000 +1956-10-17,46.26,46.26,46.26,46.26,1640000 +1956-10-16,46.62,46.62,46.62,46.62,1580000 +1956-10-15,46.86,46.86,46.86,46.86,1610000 +1956-10-12,47,47,47,47,1330000 +1956-10-11,46.81,46.81,46.81,46.81,1760000 +1956-10-10,46.84,46.84,46.84,46.84,1620000 +1956-10-09,46.2,46.2,46.2,46.2,1220000 +1956-10-08,46.43,46.43,46.43,46.43,1450000 +1956-10-05,46.45,46.45,46.45,46.45,1580000 +1956-10-04,46.29,46.29,46.29,46.29,1600000 +1956-10-03,46.28,46.28,46.28,46.28,2180000 +1956-10-02,45.52,45.52,45.52,45.52,2400000 +1956-10-01,44.7,44.7,44.7,44.7,2600000 +1956-09-28,45.35,45.35,45.35,45.35,1720000 +1956-09-27,45.6,45.6,45.6,45.6,1770000 +1956-09-26,45.82,45.82,45.82,45.82,2370000 +1956-09-25,45.75,45.75,45.75,45.75,2100000 +1956-09-24,46.4,46.4,46.4,46.4,1840000 +1956-09-21,46.58,46.58,46.58,46.58,2110000 +1956-09-20,46.21,46.21,46.21,46.21,2150000 +1956-09-19,46.24,46.24,46.24,46.24,2040000 +1956-09-18,46.79,46.79,46.79,46.79,2200000 +1956-09-17,47.1,47.1,47.1,47.1,1940000 +1956-09-14,47.21,47.21,47.21,47.21,2110000 +1956-09-13,46.09,46.09,46.09,46.09,2000000 +1956-09-12,47.05,47.05,47.05,47.05,1930000 +1956-09-11,47.38,47.38,47.38,47.38,1920000 +1956-09-10,47.56,47.56,47.56,47.56,1860000 +1956-09-07,47.81,47.81,47.81,47.81,1690000 +1956-09-06,48.1,48.1,48.1,48.1,1550000 +1956-09-05,48.02,48.02,48.02,48.02,2130000 +1956-09-04,47.89,47.89,47.89,47.89,1790000 +1956-08-31,47.51,47.51,47.51,47.51,1620000 +1956-08-30,46.94,46.94,46.94,46.94,2050000 +1956-08-29,47.36,47.36,47.36,47.36,1530000 +1956-08-28,47.57,47.57,47.57,47.57,1400000 +1956-08-27,47.66,47.66,47.66,47.66,1420000 +1956-08-24,47.95,47.95,47.95,47.95,1530000 +1956-08-23,48,48,48,48,1590000 +1956-08-22,47.42,47.42,47.42,47.42,1570000 +1956-08-21,47.89,47.89,47.89,47.89,2440000 +1956-08-20,48.25,48.25,48.25,48.25,1770000 +1956-08-17,48.82,48.82,48.82,48.82,1720000 +1956-08-16,48.88,48.88,48.88,48.88,1790000 +1956-08-15,48.99,48.99,48.99,48.99,2000000 +1956-08-14,48,48,48,48,1790000 +1956-08-13,48.58,48.58,48.58,48.58,1730000 +1956-08-10,49.09,49.09,49.09,49.09,2040000 +1956-08-09,49.32,49.32,49.32,49.32,2550000 +1956-08-08,49.36,49.36,49.36,49.36,2480000 +1956-08-07,49.16,49.16,49.16,49.16,2180000 +1956-08-06,48.96,48.96,48.96,48.96,2280000 +1956-08-03,49.64,49.64,49.64,49.64,2210000 +1956-08-02,49.64,49.64,49.64,49.64,2530000 +1956-08-01,49.62,49.62,49.62,49.62,2230000 +1956-07-31,49.39,49.39,49.39,49.39,2520000 +1956-07-30,49,49,49,49,2100000 +1956-07-27,49.08,49.08,49.08,49.08,2240000 +1956-07-26,49.48,49.48,49.48,49.48,2060000 +1956-07-25,49.44,49.44,49.44,49.44,2220000 +1956-07-24,49.33,49.33,49.33,49.33,2040000 +1956-07-23,49.33,49.33,49.33,49.33,1970000 +1956-07-20,49.35,49.35,49.35,49.35,2020000 +1956-07-19,49.32,49.32,49.32,49.32,1950000 +1956-07-18,49.3,49.3,49.3,49.3,2530000 +1956-07-17,49.31,49.31,49.31,49.31,2520000 +1956-07-16,49.14,49.14,49.14,49.14,2260000 +1956-07-13,48.72,48.72,48.72,48.72,2020000 +1956-07-12,48.58,48.58,48.58,48.58,2180000 +1956-07-11,48.69,48.69,48.69,48.69,2520000 +1956-07-10,48.54,48.54,48.54,48.54,2450000 +1956-07-09,48.25,48.25,48.25,48.25,2180000 +1956-07-06,48.04,48.04,48.04,48.04,2180000 +1956-07-05,47.8,47.8,47.8,47.8,2240000 +1956-07-03,47.32,47.32,47.32,47.32,1840000 +1956-07-02,46.93,46.93,46.93,46.93,1610000 +1956-06-29,46.97,46.97,46.97,46.97,1780000 +1956-06-28,47.13,47.13,47.13,47.13,1900000 +1956-06-27,47.07,47.07,47.07,47.07,2090000 +1956-06-26,46.72,46.72,46.72,46.72,1730000 +1956-06-25,46.41,46.41,46.41,46.41,1500000 +1956-06-22,46.59,46.59,46.59,46.59,1630000 +1956-06-21,46.73,46.73,46.73,46.73,1820000 +1956-06-20,46.41,46.41,46.41,46.41,1670000 +1956-06-19,46.22,46.22,46.22,46.22,1430000 +1956-06-18,46.17,46.17,46.17,46.17,1440000 +1956-06-15,46.37,46.37,46.37,46.37,1550000 +1956-06-14,46.31,46.31,46.31,46.31,1670000 +1956-06-13,46.42,46.42,46.42,46.42,1760000 +1956-06-12,46.36,46.36,46.36,46.36,1900000 +1956-06-11,45.71,45.71,45.71,45.71,2000000 +1956-06-08,45.14,45.14,45.14,45.14,3630000 +1956-06-07,45.99,45.99,45.99,45.99,1630000 +1956-06-06,45.63,45.63,45.63,45.63,1460000 +1956-06-05,45.86,45.86,45.86,45.86,1650000 +1956-06-04,45.85,45.85,45.85,45.85,1500000 +1956-06-01,45.58,45.58,45.58,45.58,1440000 +1956-05-31,45.2,45.2,45.2,45.2,2020000 +1956-05-29,45.11,45.11,45.11,45.11,2430000 +1956-05-28,44.1,44.1,44.1,44.1,2780000 +1956-05-25,44.62,44.62,44.62,44.62,2570000 +1956-05-24,44.6,44.6,44.6,44.6,2600000 +1956-05-23,45.02,45.02,45.02,45.02,2140000 +1956-05-22,45.26,45.26,45.26,45.26,2290000 +1956-05-21,45.99,45.99,45.99,45.99,1940000 +1956-05-18,46.39,46.39,46.39,46.39,2020000 +1956-05-17,46.61,46.61,46.61,46.61,1970000 +1956-05-16,46.05,46.05,46.05,46.05,2080000 +1956-05-15,46.37,46.37,46.37,46.37,2650000 +1956-05-14,46.86,46.86,46.86,46.86,2440000 +1956-05-11,47.12,47.12,47.12,47.12,2450000 +1956-05-10,47.16,47.16,47.16,47.16,2850000 +1956-05-09,47.94,47.94,47.94,47.94,2550000 +1956-05-08,48.02,48.02,48.02,48.02,2440000 +1956-05-07,48.22,48.22,48.22,48.22,2550000 +1956-05-04,48.51,48.51,48.51,48.51,2860000 +1956-05-03,48.34,48.34,48.34,48.34,2640000 +1956-05-02,48.17,48.17,48.17,48.17,2440000 +1956-05-01,48.16,48.16,48.16,48.16,2500000 +1956-04-30,48.38,48.38,48.38,48.38,2730000 +1956-04-27,47.99,47.99,47.99,47.99,2760000 +1956-04-26,47.49,47.49,47.49,47.49,2630000 +1956-04-25,47.09,47.09,47.09,47.09,2270000 +1956-04-24,47.26,47.26,47.26,47.26,2500000 +1956-04-23,47.65,47.65,47.65,47.65,2440000 +1956-04-20,47.76,47.76,47.76,47.76,2320000 +1956-04-19,47.57,47.57,47.57,47.57,2210000 +1956-04-18,47.74,47.74,47.74,47.74,2470000 +1956-04-17,47.93,47.93,47.93,47.93,2330000 +1956-04-16,47.96,47.96,47.96,47.96,2310000 +1956-04-13,47.95,47.95,47.95,47.95,2450000 +1956-04-12,48.02,48.02,48.02,48.02,2700000 +1956-04-11,48.31,48.31,48.31,48.31,2440000 +1956-04-10,47.93,47.93,47.93,47.93,2590000 +1956-04-09,48.61,48.61,48.61,48.61,2760000 +1956-04-06,48.85,48.85,48.85,48.85,2600000 +1956-04-05,48.57,48.57,48.57,48.57,2950000 +1956-04-04,48.8,48.8,48.8,48.8,2760000 +1956-04-03,48.53,48.53,48.53,48.53,2760000 +1956-04-02,48.7,48.7,48.7,48.7,3120000 +1956-03-29,48.48,48.48,48.48,48.48,3480000 +1956-03-28,48.51,48.51,48.51,48.51,2610000 +1956-03-27,48.25,48.25,48.25,48.25,2540000 +1956-03-26,48.62,48.62,48.62,48.62,2720000 +1956-03-23,48.83,48.83,48.83,48.83,2980000 +1956-03-22,48.72,48.72,48.72,48.72,2650000 +1956-03-21,48.23,48.23,48.23,48.23,2930000 +1956-03-20,48.87,48.87,48.87,48.87,2960000 +1956-03-19,48.59,48.59,48.59,48.59,2570000 +1956-03-16,48.14,48.14,48.14,48.14,3120000 +1956-03-15,47.99,47.99,47.99,47.99,3270000 +1956-03-14,47.53,47.53,47.53,47.53,3140000 +1956-03-13,47.06,47.06,47.06,47.06,2790000 +1956-03-12,47.13,47.13,47.13,47.13,3110000 +1956-03-09,46.7,46.7,46.7,46.7,3430000 +1956-03-08,46.12,46.12,46.12,46.12,2500000 +1956-03-07,46.01,46.01,46.01,46.01,2380000 +1956-03-06,46.04,46.04,46.04,46.04,2770000 +1956-03-05,46.06,46.06,46.06,46.06,3090000 +1956-03-02,45.81,45.81,45.81,45.81,2860000 +1956-03-01,45.54,45.54,45.54,45.54,2410000 +1956-02-29,45.34,45.34,45.34,45.34,3900000 +1956-02-28,45.43,45.43,45.43,45.43,2540000 +1956-02-27,45.27,45.27,45.27,45.27,2440000 +1956-02-24,45.32,45.32,45.32,45.32,2890000 +1956-02-23,44.95,44.95,44.95,44.95,2900000 +1956-02-21,44.56,44.56,44.56,44.56,2240000 +1956-02-20,44.45,44.45,44.45,44.45,2530000 +1956-02-17,44.52,44.52,44.52,44.52,2840000 +1956-02-16,43.82,43.82,43.82,43.82,1750000 +1956-02-15,44.04,44.04,44.04,44.04,3000000 +1956-02-14,43.42,43.42,43.42,43.42,1590000 +1956-02-13,43.58,43.58,43.58,43.58,1420000 +1956-02-10,43.64,43.64,43.64,43.64,1770000 +1956-02-09,43.66,43.66,43.66,43.66,2080000 +1956-02-08,44.16,44.16,44.16,44.16,2170000 +1956-02-07,44.6,44.6,44.6,44.6,2060000 +1956-02-06,44.81,44.81,44.81,44.81,2230000 +1956-02-03,44.78,44.78,44.78,44.78,2110000 +1956-02-02,44.22,44.22,44.22,44.22,1900000 +1956-02-01,44.03,44.03,44.03,44.03,2010000 +1956-01-31,43.82,43.82,43.82,43.82,1900000 +1956-01-30,43.5,43.5,43.5,43.5,1830000 +1956-01-27,43.35,43.35,43.35,43.35,1950000 +1956-01-26,43.46,43.46,43.46,43.46,1840000 +1956-01-25,43.72,43.72,43.72,43.72,1950000 +1956-01-24,43.65,43.65,43.65,43.65,2160000 +1956-01-23,43.11,43.11,43.11,43.11,2720000 +1956-01-20,43.22,43.22,43.22,43.22,2430000 +1956-01-19,43.72,43.72,43.72,43.72,2500000 +1956-01-18,44.17,44.17,44.17,44.17,2110000 +1956-01-17,44.47,44.47,44.47,44.47,2050000 +1956-01-16,44.14,44.14,44.14,44.14,2260000 +1956-01-13,44.67,44.67,44.67,44.67,2120000 +1956-01-12,44.75,44.75,44.75,44.75,2330000 +1956-01-11,44.38,44.38,44.38,44.38,2310000 +1956-01-10,44.16,44.16,44.16,44.16,2640000 +1956-01-09,44.51,44.51,44.51,44.51,2700000 +1956-01-06,45.14,45.14,45.14,45.14,2570000 +1956-01-05,44.95,44.95,44.95,44.95,2110000 +1956-01-04,45,45,45,45,2290000 +1956-01-03,45.16,45.16,45.16,45.16,2390000 +1955-12-30,45.48,45.48,45.48,45.48,2820000 +1955-12-29,45.15,45.15,45.15,45.15,2190000 +1955-12-28,45.05,45.05,45.05,45.05,1990000 +1955-12-27,45.22,45.22,45.22,45.22,2010000 +1955-12-23,45.5,45.5,45.5,45.5,2090000 +1955-12-22,45.41,45.41,45.41,45.41,2650000 +1955-12-21,45.84,45.84,45.84,45.84,2540000 +1955-12-20,44.95,44.95,44.95,44.95,2280000 +1955-12-19,45.02,45.02,45.02,45.02,2380000 +1955-12-16,45.13,45.13,45.13,45.13,2310000 +1955-12-15,45.06,45.06,45.06,45.06,2260000 +1955-12-14,45.07,45.07,45.07,45.07,2670000 +1955-12-13,45.45,45.45,45.45,45.45,2430000 +1955-12-12,45.42,45.42,45.42,45.42,2510000 +1955-12-09,45.89,45.89,45.89,45.89,2660000 +1955-12-08,45.82,45.82,45.82,45.82,2970000 +1955-12-07,45.55,45.55,45.55,45.55,2480000 +1955-12-06,45.7,45.7,45.7,45.7,2540000 +1955-12-05,45.7,45.7,45.7,45.7,2440000 +1955-12-02,45.44,45.44,45.44,45.44,2400000 +1955-12-01,45.35,45.35,45.35,45.35,2370000 +1955-11-30,45.51,45.51,45.51,45.51,2900000 +1955-11-29,45.56,45.56,45.56,45.56,2370000 +1955-11-28,45.38,45.38,45.38,45.38,2460000 +1955-11-25,45.68,45.68,45.68,45.68,2190000 +1955-11-23,45.72,45.72,45.72,45.72,2550000 +1955-11-22,45.66,45.66,45.66,45.66,2270000 +1955-11-21,45.22,45.22,45.22,45.22,1960000 +1955-11-18,45.54,45.54,45.54,45.54,2320000 +1955-11-17,45.59,45.59,45.59,45.59,2310000 +1955-11-16,45.91,45.91,45.91,45.91,2460000 +1955-11-15,46.21,46.21,46.21,46.21,2560000 +1955-11-14,46.41,46.41,46.41,46.41,2760000 +1955-11-11,45.24,45.24,45.24,45.24,2000000 +1955-11-10,44.72,44.72,44.72,44.72,2550000 +1955-11-09,44.61,44.61,44.61,44.61,2580000 +1955-11-07,44.15,44.15,44.15,44.15,2230000 +1955-11-04,43.96,43.96,43.96,43.96,2430000 +1955-11-03,43.24,43.24,43.24,43.24,2260000 +1955-11-02,42.35,42.35,42.35,42.35,1610000 +1955-11-01,42.28,42.28,42.28,42.28,1590000 +1955-10-31,42.34,42.34,42.34,42.34,1800000 +1955-10-28,42.37,42.37,42.37,42.37,1720000 +1955-10-27,42.34,42.34,42.34,42.34,1830000 +1955-10-26,42.29,42.29,42.29,42.29,1660000 +1955-10-25,42.63,42.63,42.63,42.63,1950000 +1955-10-24,42.91,42.91,42.91,42.91,1820000 +1955-10-21,42.59,42.59,42.59,42.59,1710000 +1955-10-20,42.59,42.59,42.59,42.59,2160000 +1955-10-19,42.07,42.07,42.07,42.07,1760000 +1955-10-18,41.65,41.65,41.65,41.65,1550000 +1955-10-17,41.35,41.35,41.35,41.35,1480000 +1955-10-14,41.22,41.22,41.22,41.22,1640000 +1955-10-13,41.39,41.39,41.39,41.39,1980000 +1955-10-12,41.52,41.52,41.52,41.52,1900000 +1955-10-11,40.8,40.8,40.8,40.8,3590000 +1955-10-10,41.15,41.15,41.15,41.15,3100000 +1955-10-07,42.38,42.38,42.38,42.38,2150000 +1955-10-06,42.7,42.7,42.7,42.7,1690000 +1955-10-05,42.99,42.99,42.99,42.99,1920000 +1955-10-04,42.82,42.82,42.82,42.82,2020000 +1955-10-03,42.49,42.49,42.49,42.49,2720000 +1955-09-30,43.67,43.67,43.67,43.67,2140000 +1955-09-29,44.03,44.03,44.03,44.03,2560000 +1955-09-28,44.31,44.31,44.31,44.31,3780000 +1955-09-27,43.58,43.58,43.58,43.58,5500000 +1955-09-26,42.61,42.61,42.61,42.61,7720000 +1955-09-23,45.63,45.63,45.63,45.63,2540000 +1955-09-22,45.39,45.39,45.39,45.39,2550000 +1955-09-21,45.39,45.39,45.39,45.39,2460000 +1955-09-20,45.13,45.13,45.13,45.13,2090000 +1955-09-19,45.16,45.16,45.16,45.16,2390000 +1955-09-16,45.09,45.09,45.09,45.09,2540000 +1955-09-15,44.75,44.75,44.75,44.75,2890000 +1955-09-14,44.99,44.99,44.99,44.99,2570000 +1955-09-13,44.8,44.8,44.8,44.8,2580000 +1955-09-12,44.19,44.19,44.19,44.19,2520000 +1955-09-09,43.89,43.89,43.89,43.89,2480000 +1955-09-08,43.88,43.88,43.88,43.88,2470000 +1955-09-07,43.85,43.85,43.85,43.85,2380000 +1955-09-06,43.86,43.86,43.86,43.86,2360000 +1955-09-02,43.6,43.6,43.6,43.6,1700000 +1955-09-01,43.37,43.37,43.37,43.37,1860000 +1955-08-31,43.18,43.18,43.18,43.18,1850000 +1955-08-30,42.92,42.92,42.92,42.92,1740000 +1955-08-29,42.96,42.96,42.96,42.96,1910000 +1955-08-26,42.99,42.99,42.99,42.99,2200000 +1955-08-25,42.8,42.8,42.8,42.8,2120000 +1955-08-24,42.61,42.61,42.61,42.61,2140000 +1955-08-23,42.55,42.55,42.55,42.55,1890000 +1955-08-22,41.98,41.98,41.98,41.98,1430000 +1955-08-19,42.02,42.02,42.02,42.02,1400000 +1955-08-18,41.84,41.84,41.84,41.84,1560000 +1955-08-17,41.9,41.9,41.9,41.9,1570000 +1955-08-16,41.86,41.86,41.86,41.86,1520000 +1955-08-15,42.17,42.17,42.17,42.17,1230000 +1955-08-12,42.21,42.21,42.21,42.21,1530000 +1955-08-11,42.13,42.13,42.13,42.13,1620000 +1955-08-10,41.74,41.74,41.74,41.74,1580000 +1955-08-09,41.75,41.75,41.75,41.75,2240000 +1955-08-08,42.31,42.31,42.31,42.31,1730000 +1955-08-05,42.56,42.56,42.56,42.56,1690000 +1955-08-04,42.36,42.36,42.36,42.36,2210000 +1955-08-03,43.09,43.09,43.09,43.09,2190000 +1955-08-02,43.03,43.03,43.03,43.03,2260000 +1955-08-01,42.93,42.93,42.93,42.93,2190000 +1955-07-29,43.52,43.52,43.52,43.52,2070000 +1955-07-28,43.5,43.5,43.5,43.5,2090000 +1955-07-27,43.76,43.76,43.76,43.76,2170000 +1955-07-26,43.58,43.58,43.58,43.58,2340000 +1955-07-25,43.48,43.48,43.48,43.48,2500000 +1955-07-22,43,43,43,43,2500000 +1955-07-21,42.64,42.64,42.64,42.64,2530000 +1955-07-20,42.23,42.23,42.23,42.23,2080000 +1955-07-19,42.1,42.1,42.1,42.1,2300000 +1955-07-18,42.36,42.36,42.36,42.36,2160000 +1955-07-15,42.4,42.4,42.4,42.4,2230000 +1955-07-14,42.25,42.25,42.25,42.25,1980000 +1955-07-13,42.24,42.24,42.24,42.24,2360000 +1955-07-12,42.75,42.75,42.75,42.75,2630000 +1955-07-11,42.75,42.75,42.75,42.75,2420000 +1955-07-08,42.64,42.64,42.64,42.64,2450000 +1955-07-07,42.58,42.58,42.58,42.58,3300000 +1955-07-06,43.18,43.18,43.18,43.18,3140000 +1955-07-05,41.69,41.69,41.69,41.69,2680000 +1955-07-01,41.19,41.19,41.19,41.19,2540000 +1955-06-30,41.03,41.03,41.03,41.03,2370000 +1955-06-29,40.79,40.79,40.79,40.79,2180000 +1955-06-28,40.77,40.77,40.77,40.77,2180000 +1955-06-27,40.99,40.99,40.99,40.99,2250000 +1955-06-24,40.96,40.96,40.96,40.96,2410000 +1955-06-23,40.75,40.75,40.75,40.75,2900000 +1955-06-22,40.6,40.6,40.6,40.6,3010000 +1955-06-21,40.51,40.51,40.51,40.51,2720000 +1955-06-20,40.14,40.14,40.14,40.14,2490000 +1955-06-17,40.1,40.1,40.1,40.1,2340000 +1955-06-16,39.96,39.96,39.96,39.96,2760000 +1955-06-15,39.89,39.89,39.89,39.89,2650000 +1955-06-14,39.67,39.67,39.67,39.67,2860000 +1955-06-13,39.62,39.62,39.62,39.62,2770000 +1955-06-10,39.25,39.25,39.25,39.25,2470000 +1955-06-09,39.01,39.01,39.01,39.01,2960000 +1955-06-08,39.22,39.22,39.22,39.22,3300000 +1955-06-07,39.96,39.96,39.96,39.96,3230000 +1955-06-06,39.69,39.69,39.69,39.69,2560000 +1955-06-03,38.37,38.37,38.37,38.37,2590000 +1955-06-02,38.01,38.01,38.01,38.01,2610000 +1955-06-01,37.96,37.96,37.96,37.96,2510000 +1955-05-31,37.91,37.91,37.91,37.91,1990000 +1955-05-27,37.93,37.93,37.93,37.93,2220000 +1955-05-26,37.85,37.85,37.85,37.85,2260000 +1955-05-25,37.6,37.6,37.6,37.6,2100000 +1955-05-24,37.46,37.46,37.46,37.46,1650000 +1955-05-23,37.48,37.48,37.48,37.48,1900000 +1955-05-20,37.74,37.74,37.74,37.74,2240000 +1955-05-19,37.49,37.49,37.49,37.49,2380000 +1955-05-18,37.28,37.28,37.28,37.28,2010000 +1955-05-17,36.97,36.97,36.97,36.97,1900000 +1955-05-16,37.02,37.02,37.02,37.02,2160000 +1955-05-13,37.44,37.44,37.44,37.44,1860000 +1955-05-12,37.2,37.2,37.2,37.2,2830000 +1955-05-11,37.42,37.42,37.42,37.42,2120000 +1955-05-10,37.85,37.85,37.85,37.85,2150000 +1955-05-09,37.93,37.93,37.93,37.93,2090000 +1955-05-06,37.89,37.89,37.89,37.89,2250000 +1955-05-05,37.82,37.82,37.82,37.82,2270000 +1955-05-04,37.64,37.64,37.64,37.64,2220000 +1955-05-03,37.7,37.7,37.7,37.7,2630000 +1955-05-02,38.04,38.04,38.04,38.04,2220000 +1955-04-29,37.96,37.96,37.96,37.96,2230000 +1955-04-28,37.68,37.68,37.68,37.68,2550000 +1955-04-27,38.11,38.11,38.11,38.11,2660000 +1955-04-26,38.31,38.31,38.31,38.31,2720000 +1955-04-25,38.11,38.11,38.11,38.11,2720000 +1955-04-22,38.01,38.01,38.01,38.01,2800000 +1955-04-21,38.32,38.32,38.32,38.32,2810000 +1955-04-20,38.28,38.28,38.28,38.28,3090000 +1955-04-19,38.22,38.22,38.22,38.22,2700000 +1955-04-18,38.27,38.27,38.27,38.27,3080000 +1955-04-15,37.96,37.96,37.96,37.96,3180000 +1955-04-14,37.79,37.79,37.79,37.79,2890000 +1955-04-13,37.71,37.71,37.71,37.71,2820000 +1955-04-12,37.66,37.66,37.66,37.66,2770000 +1955-04-11,37.44,37.44,37.44,37.44,2680000 +1955-04-07,37.34,37.34,37.34,37.34,2330000 +1955-04-06,37.17,37.17,37.17,37.17,2500000 +1955-04-05,36.98,36.98,36.98,36.98,2100000 +1955-04-04,36.83,36.83,36.83,36.83,2500000 +1955-04-01,36.95,36.95,36.95,36.95,2660000 +1955-03-31,36.58,36.58,36.58,36.58,2680000 +1955-03-30,36.52,36.52,36.52,36.52,3410000 +1955-03-29,36.85,36.85,36.85,36.85,2770000 +1955-03-28,36.83,36.83,36.83,36.83,2540000 +1955-03-25,36.96,36.96,36.96,36.96,2540000 +1955-03-24,36.93,36.93,36.93,36.93,3170000 +1955-03-23,36.64,36.64,36.64,36.64,2730000 +1955-03-22,36.17,36.17,36.17,36.17,1910000 +1955-03-21,35.95,35.95,35.95,35.95,2020000 +1955-03-18,36.18,36.18,36.18,36.18,2050000 +1955-03-17,36.12,36.12,36.12,36.12,2200000 +1955-03-16,35.98,35.98,35.98,35.98,2900000 +1955-03-15,35.71,35.71,35.71,35.71,3160000 +1955-03-14,34.96,34.96,34.96,34.96,4220000 +1955-03-11,35.82,35.82,35.82,35.82,3040000 +1955-03-10,36.45,36.45,36.45,36.45,2760000 +1955-03-09,36.22,36.22,36.22,36.22,3590000 +1955-03-08,36.58,36.58,36.58,36.58,3160000 +1955-03-07,37.28,37.28,37.28,37.28,2630000 +1955-03-04,37.52,37.52,37.52,37.52,2770000 +1955-03-03,37.29,37.29,37.29,37.29,3330000 +1955-03-02,37.15,37.15,37.15,37.15,3370000 +1955-03-01,36.83,36.83,36.83,36.83,2830000 +1955-02-28,36.76,36.76,36.76,36.76,2620000 +1955-02-25,36.57,36.57,36.57,36.57,2540000 +1955-02-24,36.62,36.62,36.62,36.62,2920000 +1955-02-23,36.82,36.82,36.82,36.82,3030000 +1955-02-21,36.85,36.85,36.85,36.85,3010000 +1955-02-18,36.89,36.89,36.89,36.89,3660000 +1955-02-17,36.84,36.84,36.84,36.84,3030000 +1955-02-16,36.77,36.77,36.77,36.77,3660000 +1955-02-15,36.89,36.89,36.89,36.89,3510000 +1955-02-14,36.89,36.89,36.89,36.89,2950000 +1955-02-11,37.15,37.15,37.15,37.15,3260000 +1955-02-10,37.08,37.08,37.08,37.08,3460000 +1955-02-09,36.75,36.75,36.75,36.75,3360000 +1955-02-08,36.46,36.46,36.46,36.46,3400000 +1955-02-07,36.96,36.96,36.96,36.96,3610000 +1955-02-04,36.96,36.96,36.96,36.96,3370000 +1955-02-03,36.44,36.44,36.44,36.44,2890000 +1955-02-02,36.61,36.61,36.61,36.61,3210000 +1955-02-01,36.72,36.72,36.72,36.72,3320000 +1955-01-31,36.63,36.63,36.63,36.63,3500000 +1955-01-28,36.19,36.19,36.19,36.19,3290000 +1955-01-27,35.99,35.99,35.99,35.99,3500000 +1955-01-26,35.95,35.95,35.95,35.95,3860000 +1955-01-25,35.51,35.51,35.51,35.51,3230000 +1955-01-24,35.52,35.52,35.52,35.52,2910000 +1955-01-21,35.44,35.44,35.44,35.44,2690000 +1955-01-20,35.13,35.13,35.13,35.13,2210000 +1955-01-19,34.96,34.96,34.96,34.96,2760000 +1955-01-18,34.8,34.8,34.8,34.8,3020000 +1955-01-17,34.58,34.58,34.58,34.58,3360000 +1955-01-14,35.28,35.28,35.28,35.28,2630000 +1955-01-13,35.43,35.43,35.43,35.43,3350000 +1955-01-12,35.58,35.58,35.58,35.58,3400000 +1955-01-11,35.68,35.68,35.68,35.68,3680000 +1955-01-10,35.79,35.79,35.79,35.79,4300000 +1955-01-07,35.33,35.33,35.33,35.33,4030000 +1955-01-06,35.04,35.04,35.04,35.04,5300000 +1955-01-05,35.52,35.52,35.52,35.52,4640000 +1955-01-04,36.42,36.42,36.42,36.42,4420000 +1955-01-03,36.75,36.75,36.75,36.75,4570000 +1954-12-31,35.98,35.98,35.98,35.98,3840000 +1954-12-30,35.74,35.74,35.74,35.74,3590000 +1954-12-29,35.74,35.74,35.74,35.74,4430000 +1954-12-28,35.43,35.43,35.43,35.43,3660000 +1954-12-27,35.07,35.07,35.07,35.07,2970000 +1954-12-23,35.37,35.37,35.37,35.37,3310000 +1954-12-22,35.34,35.34,35.34,35.34,3460000 +1954-12-21,35.38,35.38,35.38,35.38,3630000 +1954-12-20,35.33,35.33,35.33,35.33,3770000 +1954-12-17,35.92,35.92,35.92,35.92,3730000 +1954-12-16,34.93,34.93,34.93,34.93,3390000 +1954-12-15,34.56,34.56,34.56,34.56,2740000 +1954-12-14,34.35,34.35,34.35,34.35,2650000 +1954-12-13,34.59,34.59,34.59,34.59,2750000 +1954-12-10,34.56,34.56,34.56,34.56,3250000 +1954-12-09,34.69,34.69,34.69,34.69,3300000 +1954-12-08,34.86,34.86,34.86,34.86,4150000 +1954-12-07,34.92,34.92,34.92,34.92,3820000 +1954-12-06,34.76,34.76,34.76,34.76,3960000 +1954-12-03,34.49,34.49,34.49,34.49,3790000 +1954-12-02,34.18,34.18,34.18,34.18,3190000 +1954-12-01,33.99,33.99,33.99,33.99,3100000 +1954-11-30,34.24,34.24,34.24,34.24,3440000 +1954-11-29,34.54,34.54,34.54,34.54,3300000 +1954-11-26,34.55,34.55,34.55,34.55,3010000 +1954-11-24,34.22,34.22,34.22,34.22,3990000 +1954-11-23,34.03,34.03,34.03,34.03,3690000 +1954-11-22,33.58,33.58,33.58,33.58,3000000 +1954-11-19,33.45,33.45,33.45,33.45,3130000 +1954-11-18,33.44,33.44,33.44,33.44,3530000 +1954-11-17,33.63,33.63,33.63,33.63,3830000 +1954-11-16,33.57,33.57,33.57,33.57,3260000 +1954-11-15,33.47,33.47,33.47,33.47,3080000 +1954-11-12,33.54,33.54,33.54,33.54,3720000 +1954-11-11,33.47,33.47,33.47,33.47,2960000 +1954-11-10,33.18,33.18,33.18,33.18,2070000 +1954-11-09,33.15,33.15,33.15,33.15,3240000 +1954-11-08,33.02,33.02,33.02,33.02,3180000 +1954-11-05,32.71,32.71,32.71,32.71,2950000 +1954-11-04,32.82,32.82,32.82,32.82,3140000 +1954-11-03,32.44,32.44,32.44,32.44,2700000 +1954-11-01,31.79,31.79,31.79,31.79,1790000 +1954-10-29,31.68,31.68,31.68,31.68,1900000 +1954-10-28,31.88,31.88,31.88,31.88,2190000 +1954-10-27,32.02,32.02,32.02,32.02,2030000 +1954-10-26,31.94,31.94,31.94,31.94,2010000 +1954-10-25,31.96,31.96,31.96,31.96,2340000 +1954-10-22,32.13,32.13,32.13,32.13,2080000 +1954-10-21,32.13,32.13,32.13,32.13,2320000 +1954-10-20,32.17,32.17,32.17,32.17,2380000 +1954-10-19,31.91,31.91,31.91,31.91,1900000 +1954-10-18,31.83,31.83,31.83,31.83,1790000 +1954-10-15,31.71,31.71,31.71,31.71,2250000 +1954-10-14,31.88,31.88,31.88,31.88,2540000 +1954-10-13,32.27,32.27,32.27,32.27,2070000 +1954-10-12,32.28,32.28,32.28,32.28,1620000 +1954-10-11,32.41,32.41,32.41,32.41,2100000 +1954-10-08,32.67,32.67,32.67,32.67,2120000 +1954-10-07,32.69,32.69,32.69,32.69,1810000 +1954-10-06,32.76,32.76,32.76,32.76,2570000 +1954-10-05,32.63,32.63,32.63,32.63,2300000 +1954-10-04,32.47,32.47,32.47,32.47,2000000 +1954-10-01,32.29,32.29,32.29,32.29,1850000 +1954-09-30,32.31,32.31,32.31,32.31,1840000 +1954-09-29,32.5,32.5,32.5,32.5,1810000 +1954-09-28,32.69,32.69,32.69,32.69,1800000 +1954-09-27,32.53,32.53,32.53,32.53,2190000 +1954-09-24,32.4,32.4,32.4,32.4,2340000 +1954-09-23,32.18,32.18,32.18,32.18,2340000 +1954-09-22,32,32,32,32,2260000 +1954-09-21,31.79,31.79,31.79,31.79,1770000 +1954-09-20,31.57,31.57,31.57,31.57,2060000 +1954-09-17,31.71,31.71,31.71,31.71,2250000 +1954-09-16,31.46,31.46,31.46,31.46,1880000 +1954-09-15,31.29,31.29,31.29,31.29,2110000 +1954-09-14,31.28,31.28,31.28,31.28,2120000 +1954-09-13,31.12,31.12,31.12,31.12,2030000 +1954-09-10,30.84,30.84,30.84,30.84,1870000 +1954-09-09,30.73,30.73,30.73,30.73,1700000 +1954-09-08,30.68,30.68,30.68,30.68,1970000 +1954-09-07,30.66,30.66,30.66,30.66,1860000 +1954-09-03,30.5,30.5,30.5,30.5,1630000 +1954-09-02,30.27,30.27,30.27,30.27,1600000 +1954-09-01,30.04,30.04,30.04,30.04,1790000 +1954-08-31,29.83,29.83,29.83,29.83,2640000 +1954-08-30,30.35,30.35,30.35,30.35,1950000 +1954-08-27,30.66,30.66,30.66,30.66,1740000 +1954-08-26,30.57,30.57,30.57,30.57,2060000 +1954-08-25,30.65,30.65,30.65,30.65,2280000 +1954-08-24,30.87,30.87,30.87,30.87,2000000 +1954-08-23,31,31,31,31,2020000 +1954-08-20,31.21,31.21,31.21,31.21,2110000 +1954-08-19,31.16,31.16,31.16,31.16,2320000 +1954-08-18,31.09,31.09,31.09,31.09,2390000 +1954-08-17,31.12,31.12,31.12,31.12,2900000 +1954-08-16,31.05,31.05,31.05,31.05,2760000 +1954-08-13,30.72,30.72,30.72,30.72,2500000 +1954-08-12,30.59,30.59,30.59,30.59,2680000 +1954-08-11,30.72,30.72,30.72,30.72,3440000 +1954-08-10,30.37,30.37,30.37,30.37,2890000 +1954-08-09,30.12,30.12,30.12,30.12,2280000 +1954-08-06,30.38,30.38,30.38,30.38,3350000 +1954-08-05,30.77,30.77,30.77,30.77,3150000 +1954-08-04,30.9,30.9,30.9,30.9,3620000 +1954-08-03,30.93,30.93,30.93,30.93,2970000 +1954-08-02,30.99,30.99,30.99,30.99,2850000 +1954-07-30,30.88,30.88,30.88,30.88,2800000 +1954-07-29,30.69,30.69,30.69,30.69,2710000 +1954-07-28,30.58,30.58,30.58,30.58,2740000 +1954-07-27,30.52,30.52,30.52,30.52,2690000 +1954-07-26,30.34,30.34,30.34,30.34,2110000 +1954-07-23,30.31,30.31,30.31,30.31,2520000 +1954-07-22,30.27,30.27,30.27,30.27,2890000 +1954-07-21,30.03,30.03,30.03,30.03,2510000 +1954-07-20,29.84,29.84,29.84,29.84,2580000 +1954-07-19,29.98,29.98,29.98,29.98,2370000 +1954-07-16,30.06,30.06,30.06,30.06,2540000 +1954-07-15,30.19,30.19,30.19,30.19,3000000 +1954-07-14,30.09,30.09,30.09,30.09,2520000 +1954-07-13,30.02,30.02,30.02,30.02,2430000 +1954-07-12,30.12,30.12,30.12,30.12,2330000 +1954-07-09,30.14,30.14,30.14,30.14,2240000 +1954-07-08,29.94,29.94,29.94,29.94,2080000 +1954-07-07,29.94,29.94,29.94,29.94,2380000 +1954-07-06,29.92,29.92,29.92,29.92,2560000 +1954-07-02,29.59,29.59,29.59,29.59,1980000 +1954-07-01,29.21,29.21,29.21,29.21,1860000 +1954-06-30,29.21,29.21,29.21,29.21,1950000 +1954-06-29,29.43,29.43,29.43,29.43,2580000 +1954-06-28,29.28,29.28,29.28,29.28,1890000 +1954-06-25,29.2,29.2,29.2,29.2,2060000 +1954-06-24,29.26,29.26,29.26,29.26,2260000 +1954-06-23,29.13,29.13,29.13,29.13,2090000 +1954-06-22,29.08,29.08,29.08,29.08,2100000 +1954-06-21,29.06,29.06,29.06,29.06,1820000 +1954-06-18,29.04,29.04,29.04,29.04,1580000 +1954-06-17,28.96,28.96,28.96,28.96,1810000 +1954-06-16,29.04,29.04,29.04,29.04,2070000 +1954-06-15,28.83,28.83,28.83,28.83,1630000 +1954-06-14,28.62,28.62,28.62,28.62,1420000 +1954-06-11,28.58,28.58,28.58,28.58,1630000 +1954-06-10,28.34,28.34,28.34,28.34,1610000 +1954-06-09,28.15,28.15,28.15,28.15,2360000 +1954-06-08,28.34,28.34,28.34,28.34,2540000 +1954-06-07,28.99,28.99,28.99,28.99,1520000 +1954-06-04,29.1,29.1,29.1,29.1,1720000 +1954-06-03,29.15,29.15,29.15,29.15,1810000 +1954-06-02,29.16,29.16,29.16,29.16,1930000 +1954-06-01,29.19,29.19,29.19,29.19,1850000 +1954-05-28,29.19,29.19,29.19,29.19,1940000 +1954-05-27,29.05,29.05,29.05,29.05,2230000 +1954-05-26,29.17,29.17,29.17,29.17,2180000 +1954-05-25,28.93,28.93,28.93,28.93,2050000 +1954-05-24,29,29,29,29,2330000 +1954-05-21,28.99,28.99,28.99,28.99,2620000 +1954-05-20,28.82,28.82,28.82,28.82,2070000 +1954-05-19,28.72,28.72,28.72,28.72,2170000 +1954-05-18,28.85,28.85,28.85,28.85,2250000 +1954-05-17,28.84,28.84,28.84,28.84,2040000 +1954-05-14,28.8,28.8,28.8,28.8,1970000 +1954-05-13,28.56,28.56,28.56,28.56,2340000 +1954-05-12,28.72,28.72,28.72,28.72,2210000 +1954-05-11,28.49,28.49,28.49,28.49,1770000 +1954-05-10,28.62,28.62,28.62,28.62,1800000 +1954-05-07,28.65,28.65,28.65,28.65,2070000 +1954-05-06,28.51,28.51,28.51,28.51,1980000 +1954-05-05,28.29,28.29,28.29,28.29,2020000 +1954-05-04,28.28,28.28,28.28,28.28,1990000 +1954-05-03,28.21,28.21,28.21,28.21,1870000 +1954-04-30,28.26,28.26,28.26,28.26,2450000 +1954-04-29,28.18,28.18,28.18,28.18,2150000 +1954-04-28,27.76,27.76,27.76,27.76,2120000 +1954-04-27,27.71,27.71,27.71,27.71,1970000 +1954-04-26,27.88,27.88,27.88,27.88,2150000 +1954-04-23,27.78,27.78,27.78,27.78,1990000 +1954-04-22,27.68,27.68,27.68,27.68,1750000 +1954-04-21,27.64,27.64,27.64,27.64,1870000 +1954-04-20,27.75,27.75,27.75,27.75,1860000 +1954-04-19,27.76,27.76,27.76,27.76,2430000 +1954-04-15,27.94,27.94,27.94,27.94,2200000 +1954-04-14,27.85,27.85,27.85,27.85,2330000 +1954-04-13,27.64,27.64,27.64,27.64,2020000 +1954-04-12,27.57,27.57,27.57,27.57,1790000 +1954-04-09,27.38,27.38,27.38,27.38,2360000 +1954-04-08,27.38,27.38,27.38,27.38,2300000 +1954-04-07,27.11,27.11,27.11,27.11,1830000 +1954-04-06,27.01,27.01,27.01,27.01,2120000 +1954-04-05,27.26,27.26,27.26,27.26,1710000 +1954-04-02,27.21,27.21,27.21,27.21,1830000 +1954-04-01,27.17,27.17,27.17,27.17,2270000 +1954-03-31,26.94,26.94,26.94,26.94,2690000 +1954-03-30,26.69,26.69,26.69,26.69,2130000 +1954-03-29,26.66,26.66,26.66,26.66,1870000 +1954-03-26,26.56,26.56,26.56,26.56,1550000 +1954-03-25,26.42,26.42,26.42,26.42,1720000 +1954-03-24,26.47,26.47,26.47,26.47,1900000 +1954-03-23,26.6,26.6,26.6,26.6,2180000 +1954-03-22,26.79,26.79,26.79,26.79,1800000 +1954-03-19,26.81,26.81,26.81,26.81,1930000 +1954-03-18,26.73,26.73,26.73,26.73,2020000 +1954-03-17,26.62,26.62,26.62,26.62,1740000 +1954-03-16,26.56,26.56,26.56,26.56,1540000 +1954-03-15,26.57,26.57,26.57,26.57,1680000 +1954-03-12,26.69,26.69,26.69,26.69,1980000 +1954-03-11,26.69,26.69,26.69,26.69,2050000 +1954-03-10,26.57,26.57,26.57,26.57,1870000 +1954-03-09,26.51,26.51,26.51,26.51,1630000 +1954-03-08,26.45,26.45,26.45,26.45,1650000 +1954-03-05,26.52,26.52,26.52,26.52,2030000 +1954-03-04,26.41,26.41,26.41,26.41,1830000 +1954-03-03,26.32,26.32,26.32,26.32,2240000 +1954-03-02,26.32,26.32,26.32,26.32,1980000 +1954-03-01,26.25,26.25,26.25,26.25,2040000 +1954-02-26,26.15,26.15,26.15,26.15,1910000 +1954-02-25,25.91,25.91,25.91,25.91,1470000 +1954-02-24,25.83,25.83,25.83,25.83,1350000 +1954-02-23,25.83,25.83,25.83,25.83,1470000 +1954-02-19,25.92,25.92,25.92,25.92,1510000 +1954-02-18,25.86,25.86,25.86,25.86,1500000 +1954-02-17,25.86,25.86,25.86,25.86,1740000 +1954-02-16,25.81,25.81,25.81,25.81,1870000 +1954-02-15,26.04,26.04,26.04,26.04,2080000 +1954-02-12,26.12,26.12,26.12,26.12,1730000 +1954-02-11,26.06,26.06,26.06,26.06,1860000 +1954-02-10,26.14,26.14,26.14,26.14,1790000 +1954-02-09,26.17,26.17,26.17,26.17,1880000 +1954-02-08,26.23,26.23,26.23,26.23,2180000 +1954-02-05,26.3,26.3,26.3,26.3,2030000 +1954-02-04,26.2,26.2,26.2,26.2,2040000 +1954-02-03,26.01,26.01,26.01,26.01,1690000 +1954-02-02,25.92,25.92,25.92,25.92,1420000 +1954-02-01,25.99,25.99,25.99,25.99,1740000 +1954-01-29,26.08,26.08,26.08,26.08,1950000 +1954-01-28,26.02,26.02,26.02,26.02,1730000 +1954-01-27,26.01,26.01,26.01,26.01,2020000 +1954-01-26,26.09,26.09,26.09,26.09,2120000 +1954-01-25,25.93,25.93,25.93,25.93,1860000 +1954-01-22,25.85,25.85,25.85,25.85,1890000 +1954-01-21,25.79,25.79,25.79,25.79,1780000 +1954-01-20,25.75,25.75,25.75,25.75,1960000 +1954-01-19,25.68,25.68,25.68,25.68,1840000 +1954-01-18,25.43,25.43,25.43,25.43,1580000 +1954-01-15,25.43,25.43,25.43,25.43,2180000 +1954-01-14,25.19,25.19,25.19,25.19,1530000 +1954-01-13,25.07,25.07,25.07,25.07,1420000 +1954-01-12,24.93,24.93,24.93,24.93,1250000 +1954-01-11,24.8,24.8,24.8,24.8,1220000 +1954-01-08,24.93,24.93,24.93,24.93,1260000 +1954-01-07,25.06,25.06,25.06,25.06,1540000 +1954-01-06,25.14,25.14,25.14,25.14,1460000 +1954-01-05,25.1,25.1,25.1,25.1,1520000 +1954-01-04,24.95,24.95,24.95,24.95,1310000 +1953-12-31,24.81,24.81,24.81,24.81,2490000 +1953-12-30,24.76,24.76,24.76,24.76,2050000 +1953-12-29,24.55,24.55,24.55,24.55,2140000 +1953-12-28,24.71,24.71,24.71,24.71,1570000 +1953-12-24,24.8,24.8,24.8,24.8,1270000 +1953-12-23,24.69,24.69,24.69,24.69,1570000 +1953-12-22,24.76,24.76,24.76,24.76,1720000 +1953-12-21,24.95,24.95,24.95,24.95,1690000 +1953-12-18,24.99,24.99,24.99,24.99,1550000 +1953-12-17,24.94,24.94,24.94,24.94,1600000 +1953-12-16,24.96,24.96,24.96,24.96,1880000 +1953-12-15,24.71,24.71,24.71,24.71,1450000 +1953-12-14,24.69,24.69,24.69,24.69,1540000 +1953-12-11,24.76,24.76,24.76,24.76,1440000 +1953-12-10,24.78,24.78,24.78,24.78,1420000 +1953-12-09,24.84,24.84,24.84,24.84,1410000 +1953-12-08,24.87,24.87,24.87,24.87,1390000 +1953-12-07,24.95,24.95,24.95,24.95,1410000 +1953-12-04,24.98,24.98,24.98,24.98,1390000 +1953-12-03,24.97,24.97,24.97,24.97,1740000 +1953-12-02,24.95,24.95,24.95,24.95,1850000 +1953-12-01,24.78,24.78,24.78,24.78,1580000 +1953-11-30,24.76,24.76,24.76,24.76,1960000 +1953-11-27,24.66,24.66,24.66,24.66,1600000 +1953-11-25,24.52,24.52,24.52,24.52,1540000 +1953-11-24,24.5,24.5,24.5,24.5,1470000 +1953-11-23,24.36,24.36,24.36,24.36,1410000 +1953-11-20,24.44,24.44,24.44,24.44,1300000 +1953-11-19,24.4,24.4,24.4,24.4,1420000 +1953-11-18,24.29,24.29,24.29,24.29,1250000 +1953-11-17,24.25,24.25,24.25,24.25,1250000 +1953-11-16,24.38,24.38,24.38,24.38,1490000 +1953-11-13,24.54,24.54,24.54,24.54,1540000 +1953-11-12,24.46,24.46,24.46,24.46,1390000 +1953-11-10,24.37,24.37,24.37,24.37,1340000 +1953-11-09,24.66,24.66,24.66,24.66,1440000 +1953-11-06,24.61,24.61,24.61,24.61,1700000 +1953-11-05,24.64,24.64,24.64,24.64,1720000 +1953-11-04,24.51,24.51,24.51,24.51,1480000 +1953-11-02,24.66,24.66,24.66,24.66,1340000 +1953-10-30,24.54,24.54,24.54,24.54,1400000 +1953-10-29,24.58,24.58,24.58,24.58,1610000 +1953-10-28,24.29,24.29,24.29,24.29,1260000 +1953-10-27,24.26,24.26,24.26,24.26,1170000 +1953-10-26,24.31,24.31,24.31,24.31,1340000 +1953-10-23,24.35,24.35,24.35,24.35,1330000 +1953-10-22,24.3,24.3,24.3,24.3,1330000 +1953-10-21,24.19,24.19,24.19,24.19,1320000 +1953-10-20,24.17,24.17,24.17,24.17,1280000 +1953-10-19,24.16,24.16,24.16,24.16,1190000 +1953-10-16,24.14,24.14,24.14,24.14,1620000 +1953-10-15,23.95,23.95,23.95,23.95,1710000 +1953-10-14,23.68,23.68,23.68,23.68,1290000 +1953-10-13,23.57,23.57,23.57,23.57,1130000 +1953-10-09,23.66,23.66,23.66,23.66,900000 +1953-10-08,23.62,23.62,23.62,23.62,960000 +1953-10-07,23.58,23.58,23.58,23.58,1010000 +1953-10-06,23.39,23.39,23.39,23.39,1100000 +1953-10-05,23.48,23.48,23.48,23.48,930000 +1953-10-02,23.59,23.59,23.59,23.59,890000 +1953-10-01,23.49,23.49,23.49,23.49,940000 +1953-09-30,23.35,23.35,23.35,23.35,940000 +1953-09-29,23.49,23.49,23.49,23.49,1170000 +1953-09-28,23.45,23.45,23.45,23.45,1150000 +1953-09-25,23.3,23.3,23.3,23.3,910000 +1953-09-24,23.24,23.24,23.24,23.24,1020000 +1953-09-23,23.23,23.23,23.23,23.23,1240000 +1953-09-22,23.2,23.2,23.2,23.2,1300000 +1953-09-21,22.88,22.88,22.88,22.88,1070000 +1953-09-18,22.95,22.95,22.95,22.95,1190000 +1953-09-17,23.07,23.07,23.07,23.07,1290000 +1953-09-16,23.01,23.01,23.01,23.01,1570000 +1953-09-15,22.9,22.9,22.9,22.9,2850000 +1953-09-14,22.71,22.71,22.71,22.71,2550000 +1953-09-11,23.14,23.14,23.14,23.14,1930000 +1953-09-10,23.41,23.41,23.41,23.41,1010000 +1953-09-09,23.65,23.65,23.65,23.65,860000 +1953-09-08,23.61,23.61,23.61,23.61,740000 +1953-09-04,23.57,23.57,23.57,23.57,770000 +1953-09-03,23.51,23.51,23.51,23.51,900000 +1953-09-02,23.56,23.56,23.56,23.56,1110000 +1953-09-01,23.42,23.42,23.42,23.42,1580000 +1953-08-31,23.32,23.32,23.32,23.32,2190000 +1953-08-28,23.74,23.74,23.74,23.74,1060000 +1953-08-27,23.79,23.79,23.79,23.79,1290000 +1953-08-26,23.86,23.86,23.86,23.86,1060000 +1953-08-25,23.93,23.93,23.93,23.93,1470000 +1953-08-24,24.09,24.09,24.09,24.09,1320000 +1953-08-21,24.35,24.35,24.35,24.35,850000 +1953-08-20,24.29,24.29,24.29,24.29,860000 +1953-08-19,24.31,24.31,24.31,24.31,1400000 +1953-08-18,24.46,24.46,24.46,24.46,1030000 +1953-08-17,24.56,24.56,24.56,24.56,910000 +1953-08-14,24.62,24.62,24.62,24.62,1000000 +1953-08-13,24.73,24.73,24.73,24.73,1040000 +1953-08-12,24.78,24.78,24.78,24.78,990000 +1953-08-11,24.72,24.72,24.72,24.72,940000 +1953-08-10,24.75,24.75,24.75,24.75,1090000 +1953-08-07,24.78,24.78,24.78,24.78,950000 +1953-08-06,24.8,24.8,24.8,24.8,1200000 +1953-08-05,24.68,24.68,24.68,24.68,1080000 +1953-08-04,24.78,24.78,24.78,24.78,1000000 +1953-08-03,24.84,24.84,24.84,24.84,1160000 +1953-07-31,24.75,24.75,24.75,24.75,1320000 +1953-07-30,24.49,24.49,24.49,24.49,1200000 +1953-07-29,24.26,24.26,24.26,24.26,1000000 +1953-07-28,24.11,24.11,24.11,24.11,1080000 +1953-07-27,24.07,24.07,24.07,24.07,1210000 +1953-07-24,24.23,24.23,24.23,24.23,890000 +1953-07-23,24.23,24.23,24.23,24.23,1000000 +1953-07-22,24.19,24.19,24.19,24.19,900000 +1953-07-21,24.16,24.16,24.16,24.16,850000 +1953-07-20,24.22,24.22,24.22,24.22,830000 +1953-07-17,24.35,24.35,24.35,24.35,840000 +1953-07-16,24.18,24.18,24.18,24.18,790000 +1953-07-15,24.15,24.15,24.15,24.15,840000 +1953-07-14,24.08,24.08,24.08,24.08,1030000 +1953-07-13,24.17,24.17,24.17,24.17,1120000 +1953-07-10,24.41,24.41,24.41,24.41,860000 +1953-07-09,24.43,24.43,24.43,24.43,910000 +1953-07-08,24.5,24.5,24.5,24.5,950000 +1953-07-07,24.51,24.51,24.51,24.51,1030000 +1953-07-06,24.38,24.38,24.38,24.38,820000 +1953-07-03,24.36,24.36,24.36,24.36,830000 +1953-07-02,24.31,24.31,24.31,24.31,1030000 +1953-07-01,24.24,24.24,24.24,24.24,910000 +1953-06-30,24.14,24.14,24.14,24.14,820000 +1953-06-29,24.14,24.14,24.14,24.14,800000 +1953-06-26,24.21,24.21,24.21,24.21,830000 +1953-06-25,24.19,24.19,24.19,24.19,1160000 +1953-06-24,24.09,24.09,24.09,24.09,1030000 +1953-06-23,24.12,24.12,24.12,24.12,1050000 +1953-06-22,23.96,23.96,23.96,23.96,1030000 +1953-06-19,23.84,23.84,23.84,23.84,890000 +1953-06-18,23.84,23.84,23.84,23.84,1010000 +1953-06-17,23.85,23.85,23.85,23.85,1150000 +1953-06-16,23.55,23.55,23.55,23.55,1370000 +1953-06-15,23.62,23.62,23.62,23.62,1090000 +1953-06-12,23.82,23.82,23.82,23.82,920000 +1953-06-11,23.75,23.75,23.75,23.75,1220000 +1953-06-10,23.54,23.54,23.54,23.54,1960000 +1953-06-09,23.6,23.6,23.6,23.6,2200000 +1953-06-08,24.01,24.01,24.01,24.01,1000000 +1953-06-05,24.09,24.09,24.09,24.09,1160000 +1953-06-04,24.03,24.03,24.03,24.03,1400000 +1953-06-03,24.18,24.18,24.18,24.18,1050000 +1953-06-02,24.22,24.22,24.22,24.22,1450000 +1953-06-01,24.15,24.15,24.15,24.15,1490000 +1953-05-29,24.54,24.54,24.54,24.54,920000 +1953-05-28,24.46,24.46,24.46,24.46,1240000 +1953-05-27,24.64,24.64,24.64,24.64,1330000 +1953-05-26,24.87,24.87,24.87,24.87,1160000 +1953-05-25,24.99,24.99,24.99,24.99,1180000 +1953-05-22,25.03,25.03,25.03,25.03,1350000 +1953-05-21,25.06,25.06,25.06,25.06,1590000 +1953-05-20,24.93,24.93,24.93,24.93,1690000 +1953-05-19,24.7,24.7,24.7,24.7,1120000 +1953-05-18,24.75,24.75,24.75,24.75,1080000 +1953-05-15,24.84,24.84,24.84,24.84,1200000 +1953-05-14,24.85,24.85,24.85,24.85,1210000 +1953-05-13,24.71,24.71,24.71,24.71,1120000 +1953-05-12,24.74,24.74,24.74,24.74,1080000 +1953-05-11,24.91,24.91,24.91,24.91,1010000 +1953-05-08,24.9,24.9,24.9,24.9,1220000 +1953-05-07,24.9,24.9,24.9,24.9,1110000 +1953-05-06,25,25,25,25,1110000 +1953-05-05,25.03,25.03,25.03,25.03,1290000 +1953-05-04,25,25,25,25,1520000 +1953-05-01,24.73,24.73,24.73,24.73,1200000 +1953-04-30,24.62,24.62,24.62,24.62,1140000 +1953-04-29,24.68,24.68,24.68,24.68,1310000 +1953-04-28,24.52,24.52,24.52,24.52,1330000 +1953-04-27,24.34,24.34,24.34,24.34,1400000 +1953-04-24,24.2,24.2,24.2,24.2,1780000 +1953-04-23,24.19,24.19,24.19,24.19,1920000 +1953-04-22,24.46,24.46,24.46,24.46,1390000 +1953-04-21,24.67,24.67,24.67,24.67,1250000 +1953-04-20,24.73,24.73,24.73,24.73,1520000 +1953-04-17,24.62,24.62,24.62,24.62,1430000 +1953-04-16,24.91,24.91,24.91,24.91,1310000 +1953-04-15,24.96,24.96,24.96,24.96,1580000 +1953-04-14,24.86,24.86,24.86,24.86,1480000 +1953-04-13,24.77,24.77,24.77,24.77,1280000 +1953-04-10,24.82,24.82,24.82,24.82,1360000 +1953-04-09,24.88,24.88,24.88,24.88,1520000 +1953-04-08,24.93,24.93,24.93,24.93,1860000 +1953-04-07,24.71,24.71,24.71,24.71,2500000 +1953-04-06,24.61,24.61,24.61,24.61,3050000 +1953-04-02,25.23,25.23,25.23,25.23,1720000 +1953-04-01,25.25,25.25,25.25,25.25,2240000 +1953-03-31,25.29,25.29,25.29,25.29,3120000 +1953-03-30,25.61,25.61,25.61,25.61,2740000 +1953-03-27,25.99,25.99,25.99,25.99,1640000 +1953-03-26,25.95,25.95,25.95,25.95,2000000 +1953-03-25,26.1,26.1,26.1,26.1,2320000 +1953-03-24,26.17,26.17,26.17,26.17,1970000 +1953-03-23,26.02,26.02,26.02,26.02,1750000 +1953-03-20,26.18,26.18,26.18,26.18,1730000 +1953-03-19,26.22,26.22,26.22,26.22,1840000 +1953-03-18,26.24,26.24,26.24,26.24,2110000 +1953-03-17,26.33,26.33,26.33,26.33,2110000 +1953-03-16,26.22,26.22,26.22,26.22,1770000 +1953-03-13,26.18,26.18,26.18,26.18,1760000 +1953-03-12,26.13,26.13,26.13,26.13,1780000 +1953-03-11,26.12,26.12,26.12,26.12,1890000 +1953-03-10,25.91,25.91,25.91,25.91,1530000 +1953-03-09,25.83,25.83,25.83,25.83,1600000 +1953-03-06,25.84,25.84,25.84,25.84,1690000 +1953-03-05,25.79,25.79,25.79,25.79,1540000 +1953-03-04,25.78,25.78,25.78,25.78,2010000 +1953-03-03,26,26,26,26,1850000 +1953-03-02,25.93,25.93,25.93,25.93,1760000 +1953-02-27,25.9,25.9,25.9,25.9,1990000 +1953-02-26,25.95,25.95,25.95,25.95,2290000 +1953-02-25,25.91,25.91,25.91,25.91,2360000 +1953-02-24,25.75,25.75,25.75,25.75,2300000 +1953-02-20,25.63,25.63,25.63,25.63,1400000 +1953-02-19,25.57,25.57,25.57,25.57,1390000 +1953-02-18,25.48,25.48,25.48,25.48,1220000 +1953-02-17,25.5,25.5,25.5,25.5,1290000 +1953-02-16,25.65,25.65,25.65,25.65,1330000 +1953-02-13,25.74,25.74,25.74,25.74,1350000 +1953-02-11,25.64,25.64,25.64,25.64,1240000 +1953-02-10,25.62,25.62,25.62,25.62,1350000 +1953-02-09,25.69,25.69,25.69,25.69,1780000 +1953-02-06,26.51,26.51,26.51,26.51,1870000 +1953-02-05,26.15,26.15,26.15,26.15,1900000 +1953-02-04,26.42,26.42,26.42,26.42,1660000 +1953-02-03,26.54,26.54,26.54,26.54,1560000 +1953-02-02,26.51,26.51,26.51,26.51,1890000 +1953-01-30,26.38,26.38,26.38,26.38,1760000 +1953-01-29,26.2,26.2,26.2,26.2,1830000 +1953-01-28,26.13,26.13,26.13,26.13,1640000 +1953-01-27,26.05,26.05,26.05,26.05,1550000 +1953-01-26,26.02,26.02,26.02,26.02,1420000 +1953-01-23,26.07,26.07,26.07,26.07,1340000 +1953-01-22,26.12,26.12,26.12,26.12,1380000 +1953-01-21,26.09,26.09,26.09,26.09,1300000 +1953-01-20,26.14,26.14,26.14,26.14,1490000 +1953-01-19,26.01,26.01,26.01,26.01,1360000 +1953-01-16,26.02,26.02,26.02,26.02,1710000 +1953-01-15,26.13,26.13,26.13,26.13,1450000 +1953-01-14,26.08,26.08,26.08,26.08,1370000 +1953-01-13,26.02,26.02,26.02,26.02,1680000 +1953-01-12,25.86,25.86,25.86,25.86,1500000 +1953-01-09,26.08,26.08,26.08,26.08,2080000 +1953-01-08,26.33,26.33,26.33,26.33,1780000 +1953-01-07,26.37,26.37,26.37,26.37,1760000 +1953-01-06,26.48,26.48,26.48,26.48,2080000 +1953-01-05,26.66,26.66,26.66,26.66,2130000 +1953-01-02,26.54,26.54,26.54,26.54,1450000 +1952-12-31,26.57,26.57,26.57,26.57,2050000 +1952-12-30,26.59,26.59,26.59,26.59,2070000 +1952-12-29,26.4,26.4,26.4,26.4,1820000 +1952-12-26,26.25,26.25,26.25,26.25,1290000 +1952-12-24,26.21,26.21,26.21,26.21,1510000 +1952-12-23,26.19,26.19,26.19,26.19,2100000 +1952-12-22,26.3,26.3,26.3,26.3,2100000 +1952-12-19,26.15,26.15,26.15,26.15,2050000 +1952-12-18,26.03,26.03,26.03,26.03,1860000 +1952-12-17,26.04,26.04,26.04,26.04,1700000 +1952-12-16,26.07,26.07,26.07,26.07,1980000 +1952-12-15,26.04,26.04,26.04,26.04,1940000 +1952-12-12,26.04,26.04,26.04,26.04,2030000 +1952-12-11,25.96,25.96,25.96,25.96,1790000 +1952-12-10,25.98,25.98,25.98,25.98,1880000 +1952-12-09,25.93,25.93,25.93,25.93,2120000 +1952-12-08,25.76,25.76,25.76,25.76,1790000 +1952-12-05,25.62,25.62,25.62,25.62,1510000 +1952-12-04,25.61,25.61,25.61,25.61,1570000 +1952-12-03,25.71,25.71,25.71,25.71,1610000 +1952-12-02,25.74,25.74,25.74,25.74,1610000 +1952-12-01,25.68,25.68,25.68,25.68,2100000 +1952-11-28,25.66,25.66,25.66,25.66,2160000 +1952-11-26,25.52,25.52,25.52,25.52,1920000 +1952-11-25,25.36,25.36,25.36,25.36,1930000 +1952-11-24,25.42,25.42,25.42,25.42,2100000 +1952-11-21,25.27,25.27,25.27,25.27,1760000 +1952-11-20,25.28,25.28,25.28,25.28,1740000 +1952-11-19,25.33,25.33,25.33,25.33,2350000 +1952-11-18,25.16,25.16,25.16,25.16,2250000 +1952-11-17,24.8,24.8,24.8,24.8,1490000 +1952-11-14,24.75,24.75,24.75,24.75,1700000 +1952-11-13,24.71,24.71,24.71,24.71,1330000 +1952-11-12,24.65,24.65,24.65,24.65,1490000 +1952-11-10,24.77,24.77,24.77,24.77,1360000 +1952-11-07,24.78,24.78,24.78,24.78,1540000 +1952-11-06,24.77,24.77,24.77,24.77,1390000 +1952-11-05,24.67,24.67,24.67,24.67,2030000 +1952-11-03,24.6,24.6,24.6,24.6,1670000 +1952-10-31,24.52,24.52,24.52,24.52,1760000 +1952-10-30,24.15,24.15,24.15,24.15,1090000 +1952-10-29,24.15,24.15,24.15,24.15,1020000 +1952-10-28,24.13,24.13,24.13,24.13,1080000 +1952-10-27,24.09,24.09,24.09,24.09,1000000 +1952-10-24,24.03,24.03,24.03,24.03,1060000 +1952-10-23,23.87,23.87,23.87,23.87,1260000 +1952-10-22,23.8,23.8,23.8,23.8,1160000 +1952-10-21,24.07,24.07,24.07,24.07,990000 +1952-10-20,24.13,24.13,24.13,24.13,1050000 +1952-10-17,24.2,24.2,24.2,24.2,1360000 +1952-10-16,23.91,23.91,23.91,23.91,1730000 +1952-10-15,24.06,24.06,24.06,24.06,1730000 +1952-10-14,24.48,24.48,24.48,24.48,1130000 +1952-10-10,24.55,24.55,24.55,24.55,1070000 +1952-10-09,24.57,24.57,24.57,24.57,1090000 +1952-10-08,24.58,24.58,24.58,24.58,1260000 +1952-10-07,24.4,24.4,24.4,24.4,950000 +1952-10-06,24.44,24.44,24.44,24.44,1070000 +1952-10-03,24.5,24.5,24.5,24.5,980000 +1952-10-02,24.52,24.52,24.52,24.52,1040000 +1952-10-01,24.48,24.48,24.48,24.48,1060000 +1952-09-30,24.54,24.54,24.54,24.54,1120000 +1952-09-29,24.68,24.68,24.68,24.68,970000 +1952-09-26,24.73,24.73,24.73,24.73,1180000 +1952-09-25,24.81,24.81,24.81,24.81,1210000 +1952-09-24,24.79,24.79,24.79,24.79,1390000 +1952-09-23,24.7,24.7,24.7,24.7,1240000 +1952-09-22,24.59,24.59,24.59,24.59,1160000 +1952-09-19,24.57,24.57,24.57,24.57,1150000 +1952-09-18,24.51,24.51,24.51,24.51,1030000 +1952-09-17,24.58,24.58,24.58,24.58,1000000 +1952-09-16,24.53,24.53,24.53,24.53,1140000 +1952-09-15,24.45,24.45,24.45,24.45,1100000 +1952-09-12,24.71,24.71,24.71,24.71,1040000 +1952-09-11,24.72,24.72,24.72,24.72,970000 +1952-09-10,24.69,24.69,24.69,24.69,1590000 +1952-09-09,24.86,24.86,24.86,24.86,1310000 +1952-09-08,25.11,25.11,25.11,25.11,1170000 +1952-09-05,25.21,25.21,25.21,25.21,1040000 +1952-09-04,25.24,25.24,25.24,25.24,1120000 +1952-09-03,25.25,25.25,25.25,25.25,1200000 +1952-09-02,25.15,25.15,25.15,25.15,970000 +1952-08-29,25.03,25.03,25.03,25.03,890000 +1952-08-28,24.97,24.97,24.97,24.97,980000 +1952-08-27,24.94,24.94,24.94,24.94,930000 +1952-08-26,24.83,24.83,24.83,24.83,890000 +1952-08-25,24.87,24.87,24.87,24.87,840000 +1952-08-22,24.99,24.99,24.99,24.99,910000 +1952-08-21,24.98,24.98,24.98,24.98,800000 +1952-08-20,24.95,24.95,24.95,24.95,960000 +1952-08-19,24.89,24.89,24.89,24.89,980000 +1952-08-18,24.94,24.94,24.94,24.94,1090000 +1952-08-15,25.2,25.2,25.2,25.2,890000 +1952-08-14,25.28,25.28,25.28,25.28,930000 +1952-08-13,25.28,25.28,25.28,25.28,990000 +1952-08-12,25.31,25.31,25.31,25.31,1110000 +1952-08-11,25.52,25.52,25.52,25.52,1160000 +1952-08-08,25.55,25.55,25.55,25.55,1170000 +1952-08-07,25.52,25.52,25.52,25.52,1180000 +1952-08-06,25.44,25.44,25.44,25.44,1140000 +1952-08-05,25.46,25.46,25.46,25.46,1050000 +1952-08-04,25.43,25.43,25.43,25.43,950000 +1952-08-01,25.45,25.45,25.45,25.45,1050000 +1952-07-31,25.4,25.4,25.4,25.4,1230000 +1952-07-30,25.37,25.37,25.37,25.37,1240000 +1952-07-29,25.26,25.26,25.26,25.26,1010000 +1952-07-28,25.2,25.2,25.2,25.2,1030000 +1952-07-25,25.16,25.16,25.16,25.16,1130000 +1952-07-24,25.24,25.24,25.24,25.24,1270000 +1952-07-23,25.11,25.11,25.11,25.11,1020000 +1952-07-22,25,25,25,25,910000 +1952-07-21,24.95,24.95,24.95,24.95,780000 +1952-07-18,24.85,24.85,24.85,24.85,1020000 +1952-07-17,25.05,25.05,25.05,25.05,1010000 +1952-07-16,25.16,25.16,25.16,25.16,1120000 +1952-07-15,25.16,25.16,25.16,25.16,1220000 +1952-07-14,25.03,25.03,25.03,25.03,1090000 +1952-07-11,24.98,24.98,24.98,24.98,1040000 +1952-07-10,24.81,24.81,24.81,24.81,1010000 +1952-07-09,24.86,24.86,24.86,24.86,1120000 +1952-07-08,24.96,24.96,24.96,24.96,850000 +1952-07-07,24.97,24.97,24.97,24.97,1080000 +1952-07-03,25.05,25.05,25.05,25.05,1150000 +1952-07-02,25.06,25.06,25.06,25.06,1320000 +1952-07-01,25.12,25.12,25.12,25.12,1450000 +1952-06-30,24.96,24.96,24.96,24.96,1380000 +1952-06-27,24.83,24.83,24.83,24.83,1210000 +1952-06-26,24.75,24.75,24.75,24.75,1190000 +1952-06-25,24.66,24.66,24.66,24.66,1230000 +1952-06-24,24.6,24.6,24.6,24.6,1200000 +1952-06-23,24.56,24.56,24.56,24.56,1200000 +1952-06-20,24.59,24.59,24.59,24.59,1190000 +1952-06-19,24.51,24.51,24.51,24.51,1320000 +1952-06-18,24.43,24.43,24.43,24.43,1270000 +1952-06-17,24.33,24.33,24.33,24.33,920000 +1952-06-16,24.3,24.3,24.3,24.3,980000 +1952-06-13,24.37,24.37,24.37,24.37,1130000 +1952-06-12,24.31,24.31,24.31,24.31,1370000 +1952-06-11,24.31,24.31,24.31,24.31,1190000 +1952-06-10,24.23,24.23,24.23,24.23,1220000 +1952-06-09,24.37,24.37,24.37,24.37,1270000 +1952-06-06,24.26,24.26,24.26,24.26,1520000 +1952-06-05,24.1,24.1,24.1,24.1,1410000 +1952-06-04,23.95,23.95,23.95,23.95,1200000 +1952-06-03,23.78,23.78,23.78,23.78,940000 +1952-06-02,23.8,23.8,23.8,23.8,1190000 +1952-05-29,23.86,23.86,23.86,23.86,1100000 +1952-05-28,23.84,23.84,23.84,23.84,1130000 +1952-05-27,23.88,23.88,23.88,23.88,1040000 +1952-05-26,23.94,23.94,23.94,23.94,940000 +1952-05-23,23.89,23.89,23.89,23.89,1150000 +1952-05-22,23.91,23.91,23.91,23.91,1360000 +1952-05-21,23.78,23.78,23.78,23.78,1210000 +1952-05-20,23.74,23.74,23.74,23.74,1150000 +1952-05-19,23.61,23.61,23.61,23.61,780000 +1952-05-16,23.56,23.56,23.56,23.56,910000 +1952-05-15,23.6,23.6,23.6,23.6,1050000 +1952-05-14,23.68,23.68,23.68,23.68,950000 +1952-05-13,23.78,23.78,23.78,23.78,890000 +1952-05-12,23.75,23.75,23.75,23.75,800000 +1952-05-09,23.84,23.84,23.84,23.84,960000 +1952-05-08,23.86,23.86,23.86,23.86,1230000 +1952-05-07,23.81,23.81,23.81,23.81,1120000 +1952-05-06,23.67,23.67,23.67,23.67,1120000 +1952-05-05,23.66,23.66,23.66,23.66,860000 +1952-05-02,23.56,23.56,23.56,23.56,1300000 +1952-05-01,23.17,23.17,23.17,23.17,1400000 +1952-04-30,23.32,23.32,23.32,23.32,1000000 +1952-04-29,23.49,23.49,23.49,23.49,1170000 +1952-04-28,23.55,23.55,23.55,23.55,980000 +1952-04-25,23.54,23.54,23.54,23.54,1240000 +1952-04-24,23.43,23.43,23.43,23.43,1580000 +1952-04-23,23.48,23.48,23.48,23.48,1090000 +1952-04-22,23.58,23.58,23.58,23.58,1240000 +1952-04-21,23.69,23.69,23.69,23.69,1110000 +1952-04-18,23.5,23.5,23.5,23.5,1240000 +1952-04-17,23.41,23.41,23.41,23.41,1620000 +1952-04-16,23.58,23.58,23.58,23.58,1400000 +1952-04-15,23.65,23.65,23.65,23.65,1720000 +1952-04-14,23.95,23.95,23.95,23.95,1790000 +1952-04-10,24.11,24.11,24.11,24.11,1130000 +1952-04-09,23.94,23.94,23.94,23.94,980000 +1952-04-08,23.91,23.91,23.91,23.91,1090000 +1952-04-07,23.8,23.8,23.8,23.8,1230000 +1952-04-04,24.02,24.02,24.02,24.02,1190000 +1952-04-03,24.12,24.12,24.12,24.12,1280000 +1952-04-02,24.12,24.12,24.12,24.12,1260000 +1952-04-01,24.18,24.18,24.18,24.18,1720000 +1952-03-31,24.37,24.37,24.37,24.37,1680000 +1952-03-28,24.18,24.18,24.18,24.18,1560000 +1952-03-27,23.99,23.99,23.99,23.99,1370000 +1952-03-26,23.78,23.78,23.78,23.78,1030000 +1952-03-25,23.79,23.79,23.79,23.79,1060000 +1952-03-24,23.93,23.93,23.93,23.93,1040000 +1952-03-21,23.93,23.93,23.93,23.93,1290000 +1952-03-20,23.89,23.89,23.89,23.89,1240000 +1952-03-19,23.82,23.82,23.82,23.82,1090000 +1952-03-18,23.87,23.87,23.87,23.87,1170000 +1952-03-17,23.92,23.92,23.92,23.92,1150000 +1952-03-14,23.75,23.75,23.75,23.75,1350000 +1952-03-13,23.75,23.75,23.75,23.75,1270000 +1952-03-12,23.73,23.73,23.73,23.73,1310000 +1952-03-11,23.62,23.62,23.62,23.62,1210000 +1952-03-10,23.6,23.6,23.6,23.6,1170000 +1952-03-07,23.72,23.72,23.72,23.72,1410000 +1952-03-06,23.69,23.69,23.69,23.69,1210000 +1952-03-05,23.71,23.71,23.71,23.71,1380000 +1952-03-04,23.68,23.68,23.68,23.68,1570000 +1952-03-03,23.29,23.29,23.29,23.29,1020000 +1952-02-29,23.26,23.26,23.26,23.26,1000000 +1952-02-28,23.29,23.29,23.29,23.29,1150000 +1952-02-27,23.18,23.18,23.18,23.18,1260000 +1952-02-26,23.15,23.15,23.15,23.15,1080000 +1952-02-25,23.23,23.23,23.23,23.23,1200000 +1952-02-21,23.16,23.16,23.16,23.16,1360000 +1952-02-20,23.09,23.09,23.09,23.09,1970000 +1952-02-19,23.36,23.36,23.36,23.36,1630000 +1952-02-18,23.74,23.74,23.74,23.74,1140000 +1952-02-15,23.86,23.86,23.86,23.86,1200000 +1952-02-14,23.87,23.87,23.87,23.87,1340000 +1952-02-13,23.92,23.92,23.92,23.92,1300000 +1952-02-11,24.11,24.11,24.11,24.11,1140000 +1952-02-08,24.24,24.24,24.24,24.24,1350000 +1952-02-07,24.11,24.11,24.11,24.11,1170000 +1952-02-06,24.18,24.18,24.18,24.18,1310000 +1952-02-05,24.11,24.11,24.11,24.11,1590000 +1952-02-04,24.12,24.12,24.12,24.12,1640000 +1952-02-01,24.3,24.3,24.3,24.3,1350000 +1952-01-31,24.14,24.14,24.14,24.14,1810000 +1952-01-30,24.23,24.23,24.23,24.23,1880000 +1952-01-29,24.57,24.57,24.57,24.57,1730000 +1952-01-28,24.61,24.61,24.61,24.61,1590000 +1952-01-25,24.55,24.55,24.55,24.55,1650000 +1952-01-24,24.56,24.56,24.56,24.56,1570000 +1952-01-23,24.54,24.54,24.54,24.54,1680000 +1952-01-22,24.66,24.66,24.66,24.66,1920000 +1952-01-21,24.46,24.46,24.46,24.46,1730000 +1952-01-18,24.25,24.25,24.25,24.25,1740000 +1952-01-17,24.2,24.2,24.2,24.2,1590000 +1952-01-16,24.09,24.09,24.09,24.09,1430000 +1952-01-15,24.06,24.06,24.06,24.06,1340000 +1952-01-14,24.16,24.16,24.16,24.16,1510000 +1952-01-11,23.98,23.98,23.98,23.98,1760000 +1952-01-10,23.86,23.86,23.86,23.86,1520000 +1952-01-09,23.74,23.74,23.74,23.74,1370000 +1952-01-08,23.82,23.82,23.82,23.82,1390000 +1952-01-07,23.91,23.91,23.91,23.91,1540000 +1952-01-04,23.92,23.92,23.92,23.92,1480000 +1952-01-03,23.88,23.88,23.88,23.88,1220000 +1952-01-02,23.8,23.8,23.8,23.8,1070000 +1951-12-31,23.77,23.77,23.77,23.77,1440000 +1951-12-28,23.69,23.69,23.69,23.69,1470000 +1951-12-27,23.65,23.65,23.65,23.65,1460000 +1951-12-26,23.44,23.44,23.44,23.44,1520000 +1951-12-24,23.54,23.54,23.54,23.54,680000 +1951-12-21,23.51,23.51,23.51,23.51,1250000 +1951-12-20,23.57,23.57,23.57,23.57,1340000 +1951-12-19,23.57,23.57,23.57,23.57,1510000 +1951-12-18,23.49,23.49,23.49,23.49,1290000 +1951-12-17,23.41,23.41,23.41,23.41,1220000 +1951-12-14,23.37,23.37,23.37,23.37,1360000 +1951-12-13,23.39,23.39,23.39,23.39,1380000 +1951-12-12,23.37,23.37,23.37,23.37,1280000 +1951-12-11,23.3,23.3,23.3,23.3,1360000 +1951-12-10,23.42,23.42,23.42,23.42,1340000 +1951-12-07,23.38,23.38,23.38,23.38,1990000 +1951-12-06,23.34,23.34,23.34,23.34,1840000 +1951-12-05,23.07,23.07,23.07,23.07,1330000 +1951-12-04,23.14,23.14,23.14,23.14,1280000 +1951-12-03,23.01,23.01,23.01,23.01,1220000 +1951-11-30,22.88,22.88,22.88,22.88,1530000 +1951-11-29,22.67,22.67,22.67,22.67,1070000 +1951-11-28,22.61,22.61,22.61,22.61,1150000 +1951-11-27,22.66,22.66,22.66,22.66,1310000 +1951-11-26,22.43,22.43,22.43,22.43,1180000 +1951-11-23,22.4,22.4,22.4,22.4,1210000 +1951-11-21,22.64,22.64,22.64,22.64,1090000 +1951-11-20,22.68,22.68,22.68,22.68,1130000 +1951-11-19,22.73,22.73,22.73,22.73,1030000 +1951-11-16,22.82,22.82,22.82,22.82,1140000 +1951-11-15,22.84,22.84,22.84,22.84,1200000 +1951-11-14,22.85,22.85,22.85,22.85,1220000 +1951-11-13,22.79,22.79,22.79,22.79,1160000 +1951-11-09,22.75,22.75,22.75,22.75,1470000 +1951-11-08,22.47,22.47,22.47,22.47,1410000 +1951-11-07,22.49,22.49,22.49,22.49,1490000 +1951-11-05,22.82,22.82,22.82,22.82,1130000 +1951-11-02,22.93,22.93,22.93,22.93,1230000 +1951-11-01,23.1,23.1,23.1,23.1,1430000 +1951-10-31,22.94,22.94,22.94,22.94,1490000 +1951-10-30,22.66,22.66,22.66,22.66,1530000 +1951-10-29,22.69,22.69,22.69,22.69,1780000 +1951-10-26,22.81,22.81,22.81,22.81,1710000 +1951-10-25,22.96,22.96,22.96,22.96,1360000 +1951-10-24,23.03,23.03,23.03,23.03,1670000 +1951-10-23,22.84,22.84,22.84,22.84,2110000 +1951-10-22,22.75,22.75,22.75,22.75,2690000 +1951-10-19,23.32,23.32,23.32,23.32,1990000 +1951-10-18,23.67,23.67,23.67,23.67,1450000 +1951-10-17,23.69,23.69,23.69,23.69,1460000 +1951-10-16,23.77,23.77,23.77,23.77,1730000 +1951-10-15,23.85,23.85,23.85,23.85,1720000 +1951-10-11,23.7,23.7,23.7,23.7,1760000 +1951-10-10,23.61,23.61,23.61,23.61,1320000 +1951-10-09,23.65,23.65,23.65,23.65,1750000 +1951-10-08,23.75,23.75,23.75,23.75,1860000 +1951-10-05,23.78,23.78,23.78,23.78,2080000 +1951-10-04,23.72,23.72,23.72,23.72,1810000 +1951-10-03,23.79,23.79,23.79,23.79,2780000 +1951-10-02,23.64,23.64,23.64,23.64,1870000 +1951-10-01,23.47,23.47,23.47,23.47,1330000 +1951-09-28,23.26,23.26,23.26,23.26,1390000 +1951-09-27,23.27,23.27,23.27,23.27,1540000 +1951-09-26,23.4,23.4,23.4,23.4,1520000 +1951-09-25,23.38,23.38,23.38,23.38,1740000 +1951-09-24,23.3,23.3,23.3,23.3,1630000 +1951-09-21,23.4,23.4,23.4,23.4,2180000 +1951-09-20,23.57,23.57,23.57,23.57,2100000 +1951-09-19,23.59,23.59,23.59,23.59,2070000 +1951-09-18,23.59,23.59,23.59,23.59,2030000 +1951-09-17,23.62,23.62,23.62,23.62,1800000 +1951-09-14,23.69,23.69,23.69,23.69,2170000 +1951-09-13,23.71,23.71,23.71,23.71,2350000 +1951-09-12,23.6,23.6,23.6,23.6,2180000 +1951-09-11,23.5,23.5,23.5,23.5,2040000 +1951-09-10,23.62,23.62,23.62,23.62,2190000 +1951-09-07,23.53,23.53,23.53,23.53,1970000 +1951-09-06,23.47,23.47,23.47,23.47,2150000 +1951-09-05,23.42,23.42,23.42,23.42,1850000 +1951-09-04,23.28,23.28,23.28,23.28,1520000 +1951-08-31,23.28,23.28,23.28,23.28,1530000 +1951-08-30,23.24,23.24,23.24,23.24,1950000 +1951-08-29,23.08,23.08,23.08,23.08,1520000 +1951-08-28,22.9,22.9,22.9,22.9,1280000 +1951-08-27,22.85,22.85,22.85,22.85,1080000 +1951-08-24,22.88,22.88,22.88,22.88,1210000 +1951-08-23,22.9,22.9,22.9,22.9,1230000 +1951-08-22,22.75,22.75,22.75,22.75,1130000 +1951-08-21,22.83,22.83,22.83,22.83,1400000 +1951-08-20,22.93,22.93,22.93,22.93,1130000 +1951-08-17,22.94,22.94,22.94,22.94,1620000 +1951-08-16,22.87,22.87,22.87,22.87,1750000 +1951-08-15,22.79,22.79,22.79,22.79,1340000 +1951-08-14,22.7,22.7,22.7,22.7,1180000 +1951-08-13,22.8,22.8,22.8,22.8,1320000 +1951-08-10,22.79,22.79,22.79,22.79,1260000 +1951-08-09,22.84,22.84,22.84,22.84,1500000 +1951-08-08,22.93,22.93,22.93,22.93,1410000 +1951-08-07,23.03,23.03,23.03,23.03,1810000 +1951-08-06,23.01,23.01,23.01,23.01,1600000 +1951-08-03,22.85,22.85,22.85,22.85,1570000 +1951-08-02,22.82,22.82,22.82,22.82,2130000 +1951-08-01,22.51,22.51,22.51,22.51,1680000 +1951-07-31,22.4,22.4,22.4,22.4,1550000 +1951-07-30,22.63,22.63,22.63,22.63,1600000 +1951-07-27,22.53,22.53,22.53,22.53,1450000 +1951-07-26,22.47,22.47,22.47,22.47,1480000 +1951-07-25,22.32,22.32,22.32,22.32,1870000 +1951-07-24,22.44,22.44,22.44,22.44,1740000 +1951-07-23,22.1,22.1,22.1,22.1,1320000 +1951-07-20,21.88,21.88,21.88,21.88,1390000 +1951-07-19,21.84,21.84,21.84,21.84,1120000 +1951-07-18,21.88,21.88,21.88,21.88,1370000 +1951-07-17,21.92,21.92,21.92,21.92,1280000 +1951-07-16,21.73,21.73,21.73,21.73,1200000 +1951-07-13,21.98,21.98,21.98,21.98,1320000 +1951-07-12,21.8,21.8,21.8,21.8,1050000 +1951-07-11,21.68,21.68,21.68,21.68,970000 +1951-07-10,21.63,21.63,21.63,21.63,990000 +1951-07-09,21.73,21.73,21.73,21.73,1110000 +1951-07-06,21.64,21.64,21.64,21.64,1170000 +1951-07-05,21.64,21.64,21.64,21.64,1410000 +1951-07-03,21.23,21.23,21.23,21.23,1250000 +1951-07-02,21.1,21.1,21.1,21.1,1350000 +1951-06-29,20.96,20.96,20.96,20.96,1730000 +1951-06-28,21.1,21.1,21.1,21.1,1940000 +1951-06-27,21.37,21.37,21.37,21.37,1360000 +1951-06-26,21.3,21.3,21.3,21.3,1260000 +1951-06-25,21.29,21.29,21.29,21.29,2440000 +1951-06-22,21.55,21.55,21.55,21.55,1340000 +1951-06-21,21.78,21.78,21.78,21.78,1100000 +1951-06-20,21.91,21.91,21.91,21.91,1120000 +1951-06-19,22.02,22.02,22.02,22.02,1100000 +1951-06-18,22.05,22.05,22.05,22.05,1050000 +1951-06-15,22.04,22.04,22.04,22.04,1370000 +1951-06-14,21.84,21.84,21.84,21.84,1300000 +1951-06-13,21.55,21.55,21.55,21.55,1060000 +1951-06-12,21.52,21.52,21.52,21.52,1200000 +1951-06-11,21.61,21.61,21.61,21.61,1220000 +1951-06-08,21.49,21.49,21.49,21.49,1000000 +1951-06-07,21.56,21.56,21.56,21.56,1340000 +1951-06-06,21.48,21.48,21.48,21.48,1200000 +1951-06-05,21.33,21.33,21.33,21.33,1180000 +1951-06-04,21.24,21.24,21.24,21.24,1100000 +1951-06-01,21.48,21.48,21.48,21.48,9810000 +1951-05-31,21.52,21.52,21.52,21.52,1220000 +1951-05-29,21.35,21.35,21.35,21.35,1190000 +1951-05-28,21.21,21.21,21.21,21.21,1240000 +1951-05-25,21.03,21.03,21.03,21.03,1210000 +1951-05-24,21.05,21.05,21.05,21.05,2580000 +1951-05-23,21.16,21.16,21.16,21.16,1540000 +1951-05-22,21.36,21.36,21.36,21.36,1440000 +1951-05-21,21.46,21.46,21.46,21.46,1580000 +1951-05-18,21.51,21.51,21.51,21.51,1660000 +1951-05-17,21.91,21.91,21.91,21.91,1370000 +1951-05-16,21.69,21.69,21.69,21.69,1660000 +1951-05-15,21.76,21.76,21.76,21.76,2020000 +1951-05-14,22.18,22.18,22.18,22.18,1250000 +1951-05-11,22.33,22.33,22.33,22.33,1640000 +1951-05-10,22.51,22.51,22.51,22.51,1660000 +1951-05-09,22.64,22.64,22.64,22.64,1960000 +1951-05-08,22.61,22.61,22.61,22.61,1600000 +1951-05-07,22.63,22.63,22.63,22.63,1580000 +1951-05-04,22.77,22.77,22.77,22.77,2050000 +1951-05-03,22.81,22.81,22.81,22.81,2060000 +1951-05-02,22.62,22.62,22.62,22.62,1900000 +1951-05-01,22.53,22.53,22.53,22.53,1760000 +1951-04-30,22.43,22.43,22.43,22.43,1790000 +1951-04-27,22.39,22.39,22.39,22.39,2120000 +1951-04-26,22.16,22.16,22.16,22.16,1800000 +1951-04-25,21.97,21.97,21.97,21.97,1520000 +1951-04-24,21.96,21.96,21.96,21.96,1420000 +1951-04-23,22.05,22.05,22.05,22.05,1160000 +1951-04-20,22.04,22.04,22.04,22.04,940000 +1951-04-19,22.04,22.04,22.04,22.04,1520000 +1951-04-18,22.13,22.13,22.13,22.13,1780000 +1951-04-17,22.09,22.09,22.09,22.09,1470000 +1951-04-16,22.04,22.04,22.04,22.04,1730000 +1951-04-13,22.09,22.09,22.09,22.09,2120000 +1951-04-12,21.83,21.83,21.83,21.83,1530000 +1951-04-11,21.64,21.64,21.64,21.64,1420000 +1951-04-10,21.65,21.65,21.65,21.65,1280000 +1951-04-09,21.68,21.68,21.68,21.68,1110000 +1951-04-06,21.72,21.72,21.72,21.72,1450000 +1951-04-05,21.69,21.69,21.69,21.69,1790000 +1951-04-04,21.4,21.4,21.4,21.4,1300000 +1951-04-03,21.26,21.26,21.26,21.26,1220000 +1951-04-02,21.32,21.32,21.32,21.32,1280000 +1951-03-30,21.48,21.48,21.48,21.48,1150000 +1951-03-29,21.33,21.33,21.33,21.33,1300000 +1951-03-28,21.26,21.26,21.26,21.26,1770000 +1951-03-27,21.51,21.51,21.51,21.51,1250000 +1951-03-26,21.53,21.53,21.53,21.53,1230000 +1951-03-22,21.73,21.73,21.73,21.73,1290000 +1951-03-21,21.64,21.64,21.64,21.64,1310000 +1951-03-20,21.52,21.52,21.52,21.52,1020000 +1951-03-19,21.56,21.56,21.56,21.56,1120000 +1951-03-16,21.64,21.64,21.64,21.64,1660000 +1951-03-15,21.29,21.29,21.29,21.29,2070000 +1951-03-14,21.25,21.25,21.25,21.25,2110000 +1951-03-13,21.41,21.41,21.41,21.41,2330000 +1951-03-12,21.7,21.7,21.7,21.7,1640000 +1951-03-09,21.95,21.95,21.95,21.95,1610000 +1951-03-08,21.95,21.95,21.95,21.95,1440000 +1951-03-07,21.86,21.86,21.86,21.86,1770000 +1951-03-06,21.79,21.79,21.79,21.79,1490000 +1951-03-05,21.79,21.79,21.79,21.79,1690000 +1951-03-02,21.93,21.93,21.93,21.93,1570000 +1951-03-01,21.85,21.85,21.85,21.85,1610000 +1951-02-28,21.8,21.8,21.8,21.8,1640000 +1951-02-27,21.76,21.76,21.76,21.76,1680000 +1951-02-26,21.93,21.93,21.93,21.93,1650000 +1951-02-23,21.92,21.92,21.92,21.92,1540000 +1951-02-21,21.86,21.86,21.86,21.86,1670000 +1951-02-20,21.79,21.79,21.79,21.79,2010000 +1951-02-19,21.83,21.83,21.83,21.83,1910000 +1951-02-16,22.13,22.13,22.13,22.13,1860000 +1951-02-15,22,22,22,22,1700000 +1951-02-14,22.12,22.12,22.12,22.12,2050000 +1951-02-13,22.18,22.18,22.18,22.18,2400000 +1951-02-09,22.17,22.17,22.17,22.17,2550000 +1951-02-08,22.09,22.09,22.09,22.09,2120000 +1951-02-07,21.99,21.99,21.99,21.99,2020000 +1951-02-06,22.12,22.12,22.12,22.12,2370000 +1951-02-05,22.2,22.2,22.2,22.2,2680000 +1951-02-02,21.96,21.96,21.96,21.96,3030000 +1951-02-01,21.77,21.77,21.77,21.77,2380000 +1951-01-31,21.66,21.66,21.66,21.66,2340000 +1951-01-30,21.74,21.74,21.74,21.74,2480000 +1951-01-29,21.67,21.67,21.67,21.67,2630000 +1951-01-26,21.26,21.26,21.26,21.26,2230000 +1951-01-25,21.03,21.03,21.03,21.03,2520000 +1951-01-24,21.16,21.16,21.16,21.16,1990000 +1951-01-23,21.26,21.26,21.26,21.26,2080000 +1951-01-22,21.18,21.18,21.18,21.18,2570000 +1951-01-19,21.36,21.36,21.36,21.36,3170000 +1951-01-18,21.4,21.4,21.4,21.4,3490000 +1951-01-17,21.55,21.55,21.55,21.55,3880000 +1951-01-16,21.46,21.46,21.46,21.46,3740000 +1951-01-15,21.3,21.3,21.3,21.3,2830000 +1951-01-12,21.11,21.11,21.11,21.11,2950000 +1951-01-11,21.19,21.19,21.19,21.19,3490000 +1951-01-10,20.85,20.85,20.85,20.85,3270000 +1951-01-09,21.12,21.12,21.12,21.12,3800000 +1951-01-08,21,21,21,21,2780000 +1951-01-05,20.87,20.87,20.87,20.87,3390000 +1951-01-04,20.87,20.87,20.87,20.87,3390000 +1951-01-03,20.69,20.69,20.69,20.69,3370000 +1951-01-02,20.77,20.77,20.77,20.77,3030000 +1950-12-29,20.43,20.43,20.43,20.43,3440000 +1950-12-28,20.38,20.38,20.38,20.38,3560000 +1950-12-27,20.3,20.3,20.3,20.3,2940000 +1950-12-26,19.92,19.92,19.92,19.92,2660000 +1950-12-22,20.07,20.07,20.07,20.07,2720000 +1950-12-21,19.98,19.98,19.98,19.98,3990000 +1950-12-20,19.97,19.97,19.97,19.97,3510000 +1950-12-19,19.96,19.96,19.96,19.96,3650000 +1950-12-18,19.85,19.85,19.85,19.85,4500000 +1950-12-15,19.33,19.33,19.33,19.33,2420000 +1950-12-14,19.43,19.43,19.43,19.43,2660000 +1950-12-13,19.67,19.67,19.67,19.67,2030000 +1950-12-12,19.68,19.68,19.68,19.68,2140000 +1950-12-11,19.72,19.72,19.72,19.72,2600000 +1950-12-08,19.4,19.4,19.4,19.4,2310000 +1950-12-07,19.4,19.4,19.4,19.4,1810000 +1950-12-06,19.45,19.45,19.45,19.45,2010000 +1950-12-05,19.31,19.31,19.31,19.31,1940000 +1950-12-04,19,19,19,19,2510000 +1950-12-01,19.66,19.66,19.66,19.66,1870000 +1950-11-30,19.51,19.51,19.51,19.51,2080000 +1950-11-29,19.37,19.37,19.37,19.37,2770000 +1950-11-28,19.56,19.56,19.56,19.56,2970000 +1950-11-27,20.18,20.18,20.18,20.18,1740000 +1950-11-24,20.32,20.32,20.32,20.32,2620000 +1950-11-22,20.16,20.16,20.16,20.16,2730000 +1950-11-21,19.88,19.88,19.88,19.88,2010000 +1950-11-20,19.93,19.93,19.93,19.93,2250000 +1950-11-17,19.86,19.86,19.86,19.86,2130000 +1950-11-16,19.72,19.72,19.72,19.72,1760000 +1950-11-15,19.82,19.82,19.82,19.82,1620000 +1950-11-14,19.86,19.86,19.86,19.86,1780000 +1950-11-13,20.01,20.01,20.01,20.01,1630000 +1950-11-10,19.94,19.94,19.94,19.94,1640000 +1950-11-09,19.79,19.79,19.79,19.79,1760000 +1950-11-08,19.56,19.56,19.56,19.56,1850000 +1950-11-06,19.36,19.36,19.36,19.36,2580000 +1950-11-03,19.85,19.85,19.85,19.85,1560000 +1950-11-02,19.73,19.73,19.73,19.73,1580000 +1950-11-01,19.56,19.56,19.56,19.56,1780000 +1950-10-31,19.53,19.53,19.53,19.53,2010000 +1950-10-30,19.61,19.61,19.61,19.61,1790000 +1950-10-27,19.77,19.77,19.77,19.77,1800000 +1950-10-26,19.61,19.61,19.61,19.61,3000000 +1950-10-25,20.05,20.05,20.05,20.05,1930000 +1950-10-24,20.08,20.08,20.08,20.08,1790000 +1950-10-23,19.96,19.96,19.96,19.96,1850000 +1950-10-20,19.96,19.96,19.96,19.96,1840000 +1950-10-19,20.02,20.02,20.02,20.02,2250000 +1950-10-18,20.01,20.01,20.01,20.01,2410000 +1950-10-17,19.89,19.89,19.89,19.89,2010000 +1950-10-16,19.71,19.71,19.71,19.71,1630000 +1950-10-13,19.85,19.85,19.85,19.85,2030000 +1950-10-11,19.86,19.86,19.86,19.86,2200000 +1950-10-10,19.78,19.78,19.78,19.78,1870000 +1950-10-09,20,20,20,20,2330000 +1950-10-06,20.12,20.12,20.12,20.12,2360000 +1950-10-05,19.89,19.89,19.89,19.89,2490000 +1950-10-04,20,20,20,20,2920000 +1950-10-03,19.66,19.66,19.66,19.66,2480000 +1950-10-02,19.69,19.69,19.69,19.69,2200000 +1950-09-29,19.45,19.45,19.45,19.45,1800000 +1950-09-28,19.42,19.42,19.42,19.42,2200000 +1950-09-27,19.41,19.41,19.41,19.41,2360000 +1950-09-26,19.14,19.14,19.14,19.14,2280000 +1950-09-25,19.42,19.42,19.42,19.42,2020000 +1950-09-22,19.44,19.44,19.44,19.44,2510000 +1950-09-21,19.37,19.37,19.37,19.37,1650000 +1950-09-20,19.21,19.21,19.21,19.21,2100000 +1950-09-19,19.31,19.31,19.31,19.31,1590000 +1950-09-18,19.37,19.37,19.37,19.37,2040000 +1950-09-15,19.29,19.29,19.29,19.29,2410000 +1950-09-14,19.18,19.18,19.18,19.18,2350000 +1950-09-13,19.09,19.09,19.09,19.09,2600000 +1950-09-12,18.87,18.87,18.87,18.87,1680000 +1950-09-11,18.61,18.61,18.61,18.61,1860000 +1950-09-08,18.75,18.75,18.75,18.75,1960000 +1950-09-07,18.59,18.59,18.59,18.59,1340000 +1950-09-06,18.54,18.54,18.54,18.54,1300000 +1950-09-05,18.68,18.68,18.68,18.68,1250000 +1950-09-01,18.55,18.55,18.55,18.55,1290000 +1950-08-31,18.42,18.42,18.42,18.42,1140000 +1950-08-30,18.43,18.43,18.43,18.43,1490000 +1950-08-29,18.54,18.54,18.54,18.54,1890000 +1950-08-28,18.53,18.53,18.53,18.53,1300000 +1950-08-25,18.54,18.54,18.54,18.54,1610000 +1950-08-24,18.79,18.79,18.79,18.79,1620000 +1950-08-23,18.82,18.82,18.82,18.82,1580000 +1950-08-22,18.68,18.68,18.68,18.68,1550000 +1950-08-21,18.7,18.7,18.7,18.7,1840000 +1950-08-18,18.68,18.68,18.68,18.68,1780000 +1950-08-17,18.54,18.54,18.54,18.54,2170000 +1950-08-16,18.34,18.34,18.34,18.34,1770000 +1950-08-15,18.32,18.32,18.32,18.32,1330000 +1950-08-14,18.29,18.29,18.29,18.29,1280000 +1950-08-11,18.28,18.28,18.28,18.28,1680000 +1950-08-10,18.48,18.48,18.48,18.48,1870000 +1950-08-09,18.61,18.61,18.61,18.61,1760000 +1950-08-08,18.46,18.46,18.46,18.46,2180000 +1950-08-07,18.41,18.41,18.41,18.41,1850000 +1950-08-04,18.14,18.14,18.14,18.14,1600000 +1950-08-03,17.99,17.99,17.99,17.99,1660000 +1950-08-02,17.95,17.95,17.95,17.95,1980000 +1950-08-01,18.02,18.02,18.02,18.02,1970000 +1950-07-31,17.84,17.84,17.84,17.84,1590000 +1950-07-28,17.69,17.69,17.69,17.69,2050000 +1950-07-27,17.5,17.5,17.5,17.5,2300000 +1950-07-26,17.27,17.27,17.27,17.27,2460000 +1950-07-25,17.23,17.23,17.23,17.23,2770000 +1950-07-24,17.48,17.48,17.48,17.48,2300000 +1950-07-21,17.59,17.59,17.59,17.59,2810000 +1950-07-20,17.61,17.61,17.61,17.61,3160000 +1950-07-19,17.36,17.36,17.36,17.36,2430000 +1950-07-18,17.06,17.06,17.06,17.06,1820000 +1950-07-17,16.68,16.68,16.68,16.68,1520000 +1950-07-14,16.87,16.87,16.87,16.87,1900000 +1950-07-13,16.69,16.69,16.69,16.69,2660000 +1950-07-12,16.87,16.87,16.87,16.87,3200000 +1950-07-11,17.32,17.32,17.32,17.32,3250000 +1950-07-10,17.59,17.59,17.59,17.59,1960000 +1950-07-07,17.67,17.67,17.67,17.67,1870000 +1950-07-06,17.91,17.91,17.91,17.91,1570000 +1950-07-05,17.81,17.81,17.81,17.81,1400000 +1950-07-03,17.64,17.64,17.64,17.64,1550000 +1950-06-30,17.69,17.69,17.69,17.69,2660000 +1950-06-29,17.44,17.44,17.44,17.44,3040000 +1950-06-28,18.11,18.11,18.11,18.11,2600000 +1950-06-27,17.91,17.91,17.91,17.91,4860000 +1950-06-26,18.11,18.11,18.11,18.11,3950000 +1950-06-23,19.14,19.14,19.14,19.14,1700000 +1950-06-22,19.16,19.16,19.16,19.16,1830000 +1950-06-21,19,19,19,19,1750000 +1950-06-20,18.83,18.83,18.83,18.83,1470000 +1950-06-19,18.92,18.92,18.92,18.92,1290000 +1950-06-16,18.97,18.97,18.97,18.97,1180000 +1950-06-15,18.93,18.93,18.93,18.93,1530000 +1950-06-14,18.98,18.98,18.98,18.98,1650000 +1950-06-13,19.25,19.25,19.25,19.25,1790000 +1950-06-12,19.4,19.4,19.4,19.4,1790000 +1950-06-09,19.26,19.26,19.26,19.26,2130000 +1950-06-08,19.14,19.14,19.14,19.14,1780000 +1950-06-07,18.93,18.93,18.93,18.93,1750000 +1950-06-06,18.88,18.88,18.88,18.88,2250000 +1950-06-05,18.6,18.6,18.6,18.6,1630000 +1950-06-02,18.79,18.79,18.79,18.79,1450000 +1950-06-01,18.77,18.77,18.77,18.77,1580000 +1950-05-31,18.78,18.78,18.78,18.78,1530000 +1950-05-29,18.72,18.72,18.72,18.72,1110000 +1950-05-26,18.67,18.67,18.67,18.67,1330000 +1950-05-25,18.69,18.69,18.69,18.69,1480000 +1950-05-24,18.69,18.69,18.69,18.69,1850000 +1950-05-23,18.71,18.71,18.71,18.71,1460000 +1950-05-22,18.6,18.6,18.6,18.6,1620000 +1950-05-19,18.68,18.68,18.68,18.68,2110000 +1950-05-18,18.56,18.56,18.56,18.56,5240000 +1950-05-17,18.52,18.52,18.52,18.52,2020000 +1950-05-16,18.44,18.44,18.44,18.44,1730000 +1950-05-15,18.26,18.26,18.26,18.26,1220000 +1950-05-12,18.18,18.18,18.18,18.18,1790000 +1950-05-11,18.29,18.29,18.29,18.29,1750000 +1950-05-10,18.29,18.29,18.29,18.29,1880000 +1950-05-09,18.27,18.27,18.27,18.27,1720000 +1950-05-08,18.27,18.27,18.27,18.27,1680000 +1950-05-05,18.22,18.22,18.22,18.22,1800000 +1950-05-04,18.12,18.12,18.12,18.12,2150000 +1950-05-03,18.27,18.27,18.27,18.27,2120000 +1950-05-02,18.11,18.11,18.11,18.11,2250000 +1950-05-01,18.22,18.22,18.22,18.22,2390000 +1950-04-28,17.96,17.96,17.96,17.96,2190000 +1950-04-27,17.86,17.86,17.86,17.86,2070000 +1950-04-26,17.76,17.76,17.76,17.76,1880000 +1950-04-25,17.83,17.83,17.83,17.83,1830000 +1950-04-24,17.83,17.83,17.83,17.83,2310000 +1950-04-21,17.96,17.96,17.96,17.96,2710000 +1950-04-20,17.93,17.93,17.93,17.93,2590000 +1950-04-19,18.05,18.05,18.05,18.05,2950000 +1950-04-18,18.03,18.03,18.03,18.03,3320000 +1950-04-17,17.88,17.88,17.88,17.88,2520000 +1950-04-14,17.96,17.96,17.96,17.96,2750000 +1950-04-13,17.98,17.98,17.98,17.98,2410000 +1950-04-12,17.94,17.94,17.94,17.94,2010000 +1950-04-11,17.75,17.75,17.75,17.75,2010000 +1950-04-10,17.85,17.85,17.85,17.85,2070000 +1950-04-06,17.78,17.78,17.78,17.78,2000000 +1950-04-05,17.63,17.63,17.63,17.63,1430000 +1950-04-04,17.55,17.55,17.55,17.55,2010000 +1950-04-03,17.53,17.53,17.53,17.53,1570000 +1950-03-31,17.29,17.29,17.29,17.29,1880000 +1950-03-30,17.3,17.3,17.3,17.3,2370000 +1950-03-29,17.44,17.44,17.44,17.44,2090000 +1950-03-28,17.53,17.53,17.53,17.53,1780000 +1950-03-27,17.46,17.46,17.46,17.46,1930000 +1950-03-24,17.56,17.56,17.56,17.56,1570000 +1950-03-23,17.56,17.56,17.56,17.56,2020000 +1950-03-22,17.55,17.55,17.55,17.55,2010000 +1950-03-21,17.45,17.45,17.45,17.45,1400000 +1950-03-20,17.44,17.44,17.44,17.44,1430000 +1950-03-17,17.45,17.45,17.45,17.45,1600000 +1950-03-16,17.49,17.49,17.49,17.49,2060000 +1950-03-15,17.45,17.45,17.45,17.45,1830000 +1950-03-14,17.25,17.25,17.25,17.25,1140000 +1950-03-13,17.12,17.12,17.12,17.12,1060000 +1950-03-10,17.09,17.09,17.09,17.09,1260000 +1950-03-09,17.07,17.07,17.07,17.07,1330000 +1950-03-08,17.19,17.19,17.19,17.19,1360000 +1950-03-07,17.2,17.2,17.2,17.2,1590000 +1950-03-06,17.32,17.32,17.32,17.32,1470000 +1950-03-03,17.29,17.29,17.29,17.29,1520000 +1950-03-02,17.23,17.23,17.23,17.23,1340000 +1950-03-01,17.24,17.24,17.24,17.24,1410000 +1950-02-28,17.22,17.22,17.22,17.22,1310000 +1950-02-27,17.28,17.28,17.28,17.28,1410000 +1950-02-24,17.28,17.28,17.28,17.28,1710000 +1950-02-23,17.21,17.21,17.21,17.21,1310000 +1950-02-21,17.17,17.17,17.17,17.17,1260000 +1950-02-20,17.2,17.2,17.2,17.2,1420000 +1950-02-17,17.15,17.15,17.15,17.15,1940000 +1950-02-16,16.99,16.99,16.99,16.99,1920000 +1950-02-15,17.06,17.06,17.06,17.06,1730000 +1950-02-14,17.06,17.06,17.06,17.06,2210000 +1950-02-10,17.24,17.24,17.24,17.24,1790000 +1950-02-09,17.28,17.28,17.28,17.28,1810000 +1950-02-08,17.21,17.21,17.21,17.21,1470000 +1950-02-07,17.23,17.23,17.23,17.23,1360000 +1950-02-06,17.32,17.32,17.32,17.32,1490000 +1950-02-03,17.29,17.29,17.29,17.29,2210000 +1950-02-02,17.23,17.23,17.23,17.23,2040000 +1950-02-01,17.05,17.05,17.05,17.05,1810000 +1950-01-31,17.05,17.05,17.05,17.05,1690000 +1950-01-30,17.02,17.02,17.02,17.02,1640000 +1950-01-27,16.82,16.82,16.82,16.82,1250000 +1950-01-26,16.73,16.73,16.73,16.73,1150000 +1950-01-25,16.74,16.74,16.74,16.74,1700000 +1950-01-24,16.86,16.86,16.86,16.86,1250000 +1950-01-23,16.92,16.92,16.92,16.92,1340000 +1950-01-20,16.9,16.9,16.9,16.9,1440000 +1950-01-19,16.87,16.87,16.87,16.87,1170000 +1950-01-18,16.85,16.85,16.85,16.85,1570000 +1950-01-17,16.86,16.86,16.86,16.86,1790000 +1950-01-16,16.72,16.72,16.72,16.72,1460000 +1950-01-13,16.67,16.67,16.67,16.67,3330000 +1950-01-12,16.76,16.76,16.76,16.76,2970000 +1950-01-11,17.09,17.09,17.09,17.09,2630000 +1950-01-10,17.03,17.03,17.03,17.03,2160000 +1950-01-09,17.08,17.08,17.08,17.08,2520000 +1950-01-06,16.98,16.98,16.98,16.98,2010000 +1950-01-05,16.93,16.93,16.93,16.93,2550000 +1950-01-04,16.85,16.85,16.85,16.85,1890000 +1950-01-03,16.66,16.66,16.66,16.66,1260000 From 0605107007ad3040359359dfe52b640d9937878a Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Sun, 27 Oct 2024 19:32:25 -0400 Subject: [PATCH 09/18] feat: Full chain streaming prototypes (#1092) Signed-off-by: Dave Skender <8432125+DaveSkender@users.noreply.github.com> --- .editorconfig | 16 +- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- .github/workflows/test-examples.yml | 4 +- .github/workflows/test-indicators.yml | 2 +- .github/workflows/test-performance.yml | 21 +- .github/workflows/test-website-a11y.yml | 4 +- .github/workflows/test-website-links.yml | 4 +- .gitignore | 13 +- Stock.Indicators.sln | 9 +- docs/_data/aliases.yml | 10 +- docs/_includes/candle-properties.md | 2 +- docs/_includes/candle-result.md | 2 +- docs/_indicators/Adl.md | 18 +- docs/_indicators/Adx.md | 6 +- docs/_indicators/Alligator.md | 6 +- docs/_indicators/Alma.md | 6 +- docs/_indicators/Aroon.md | 6 +- docs/_indicators/Atr.md | 10 +- docs/_indicators/AtrStop.md | 14 +- docs/_indicators/Awesome.md | 6 +- docs/_indicators/Beta.md | 8 +- docs/_indicators/BollingerBands.md | 6 +- docs/_indicators/Bop.md | 6 +- docs/_indicators/Cci.md | 6 +- docs/_indicators/ChaikinOsc.md | 6 +- docs/_indicators/Chandelier.md | 6 +- docs/_indicators/Chop.md | 6 +- docs/_indicators/Cmf.md | 6 +- docs/_indicators/Cmo.md | 6 +- docs/_indicators/ConnorsRsi.md | 6 +- docs/_indicators/Correlation.md | 10 +- docs/_indicators/Dema.md | 6 +- docs/_indicators/Doji.md | 4 +- docs/_indicators/Donchian.md | 6 +- docs/_indicators/Dpo.md | 6 +- docs/_indicators/Dynamic.md | 6 +- docs/_indicators/ElderRay.md | 6 +- docs/_indicators/Ema.md | 6 +- docs/_indicators/Epma.md | 6 +- docs/_indicators/Fcb.md | 6 +- docs/_indicators/FisherTransform.md | 6 +- docs/_indicators/ForceIndex.md | 6 +- docs/_indicators/Fractal.md | 6 +- docs/_indicators/Gator.md | 8 +- docs/_indicators/HeikinAshi.md | 8 +- docs/_indicators/Hma.md | 6 +- docs/_indicators/HtTrendline.md | 6 +- docs/_indicators/Hurst.md | 6 +- docs/_indicators/Ichimoku.md | 10 +- docs/_indicators/Kama.md | 6 +- docs/_indicators/Keltner.md | 6 +- docs/_indicators/Kvo.md | 6 +- docs/_indicators/MaEnvelopes.md | 6 +- docs/_indicators/Macd.md | 6 +- docs/_indicators/Mama.md | 6 +- docs/_indicators/Marubozu.md | 4 +- docs/_indicators/Mfi.md | 6 +- docs/_indicators/Obv.md | 20 +- docs/_indicators/ParabolicSar.md | 8 +- docs/_indicators/PivotPoints.md | 6 +- docs/_indicators/Pivots.md | 6 +- docs/_indicators/Pmo.md | 10 +- docs/_indicators/Prs.md | 20 +- docs/_indicators/Pvo.md | 6 +- .../{BasicQuote.md => QuotePart.md} | 28 +- docs/_indicators/Renko.md | 12 +- docs/_indicators/Roc.md | 14 +- docs/_indicators/RocWb.md | 6 +- docs/_indicators/RollingPivots.md | 6 +- docs/_indicators/Rsi.md | 6 +- docs/_indicators/Slope.md | 6 +- docs/_indicators/Sma.md | 8 +- docs/_indicators/Smi.md | 6 +- docs/_indicators/Smma.md | 6 +- docs/_indicators/StarcBands.md | 6 +- docs/_indicators/Stc.md | 6 +- docs/_indicators/StdDev.md | 12 +- docs/_indicators/StdDevChannels.md | 6 +- docs/_indicators/Stoch.md | 8 +- docs/_indicators/StochRsi.md | 6 +- docs/_indicators/SuperTrend.md | 6 +- docs/_indicators/T3.md | 6 +- docs/_indicators/Tema.md | 6 +- docs/_indicators/Trix.md | 14 +- docs/_indicators/Tsi.md | 6 +- docs/_indicators/UlcerIndex.md | 6 +- docs/_indicators/Ultimate.md | 6 +- docs/_indicators/VolatilityStop.md | 6 +- docs/_indicators/Vortex.md | 6 +- docs/_indicators/Vwap.md | 8 +- docs/_indicators/Vwma.md | 6 +- docs/_indicators/WilliamsR.md | 6 +- docs/_indicators/Wma.md | 6 +- docs/_indicators/ZigZag.md | 6 +- docs/examples/Backtest/Program.cs | 183 ++--- docs/examples/ConsoleApp/Program.cs | 117 ++- docs/examples/CustomIndicators/AtrWma.cs | 11 +- docs/examples/CustomIndicators/README.md | 14 +- .../examples/CustomIndicatorsUsage/Program.cs | 86 +-- docs/examples/Examples.sln | 11 +- docs/examples/ObserveStream/Program.cs | 210 +++--- docs/examples/README.md | 2 +- docs/examples/UseQuoteApi/Program.cs | 203 +++--- docs/pages/guide.md | 149 ++-- docs/pages/home.md | 11 +- docs/pages/utilities.md | 38 +- src/GlobalSuppressions.cs | 48 +- src/Indicators.csproj | 2 +- src/_common/BinarySettings.cs | 111 +++ src/_common/Candles/Candles.Models.cs | 63 ++ src/_common/Candles/Candles.Utilities.cs | 27 + src/_common/Candles/Candles.cs | 51 -- src/_common/Candles/Models.cs | 36 - src/_common/Enums.cs | 52 +- src/_common/Generics/Pruning.cs | 29 +- src/_common/Generics/Seek.cs | 47 +- src/_common/Generics/Series.Model.cs | 6 - src/_common/Generics/Sorting.cs | 15 +- src/_common/Generics/Transforms.cs | 7 +- src/_common/Generics/info.xml | 41 -- src/_common/Globals.cs | 21 +- src/_common/ISeries.cs | 24 + src/_common/Incrementals/IIncremental.cs | 52 ++ src/_common/Math/NullMath.cs | 26 +- src/_common/Math/{Numerix.cs => Numerical.cs} | 63 +- src/_common/Observables/ChainProvider.cs | 194 ----- src/_common/Observables/IStreamHub.cs | 128 ++++ src/_common/Observables/IStreamObservable.cs | 108 +++ src/_common/Observables/IStreamObserver.cs | 117 +++ src/_common/Observables/QuoteObserver.cs | 29 - src/_common/Observables/QuoteProvider.cs | 186 ----- .../Observables/StreamHub.Observable.cs | 119 +++ src/_common/Observables/StreamHub.Observer.cs | 66 ++ .../Observables/StreamHub.Utilities.cs | 201 +++++ src/_common/Observables/StreamHub.cs | 408 +++++++++++ src/_common/Observables/TupleObserver.cs | 29 - src/_common/Observables/TupleProvider.cs | 174 ----- src/_common/ObsoleteV2.cs | 69 -- src/_common/ObsoleteV3.cs | 134 ++++ src/_common/ObsoleteV3.md | 107 +++ src/_common/Quotes/Quote.Aggregates.cs | 68 +- src/_common/Quotes/Quote.Converters.cs | 144 +--- src/_common/Quotes/Quote.Exceptions.cs | 1 + src/_common/Quotes/Quote.Models.cs | 116 ++- src/_common/Quotes/Quote.StreamHub.cs | 72 ++ src/_common/Quotes/Quote.Validation.cs | 61 +- src/_common/Quotes/Use.Api.cs | 21 - src/_common/Quotes/Use.Observer.cs | 76 -- src/_common/Quotes/info.xml | 34 +- src/_common/Results/Result.Models.cs | 14 - src/_common/Results/Result.Syncing.cs | 102 --- src/_common/Results/Result.Utilities.cs | 75 -- src/_common/Results/info.xml | 74 -- src/_common/Reusable/IReusable.cs | 13 + src/_common/Reusable/Reusable.Utilities.cs | 66 ++ .../Use (QuotePart)/QuotePart.Models.cs | 19 + .../Use (QuotePart)/QuotePart.StaticSeries.cs | 45 ++ .../Use (QuotePart)/QuotePart.StreamHub.cs | 56 ++ .../Use (QuotePart)/QuotePart.Utilities.cs | 78 ++ src/a-d/Adl/Adl.Api.cs | 15 - src/a-d/Adl/Adl.Models.cs | 20 +- src/a-d/Adl/Adl.Series.cs | 62 -- src/a-d/Adl/Adl.StaticSeries.cs | 33 + src/a-d/Adl/Adl.StreamHub.cs | 48 ++ src/a-d/Adl/Adl.Utilities.cs | 60 ++ src/a-d/Adl/info.xml | 17 - src/a-d/Adx/Adx.Api.cs | 15 - src/a-d/Adx/Adx.Models.cs | 21 +- .../{Adx.Series.cs => Adx.StaticSeries.cs} | 84 ++- src/a-d/Adx/Adx.Utilities.cs | 23 +- src/a-d/Adx/info.xml | 43 +- src/a-d/Alligator/Alligator.Api.cs | 63 -- src/a-d/Alligator/Alligator.Models.cs | 18 +- src/a-d/Alligator/Alligator.Series.cs | 185 ----- src/a-d/Alligator/Alligator.StaticSeries.cs | 143 ++++ src/a-d/Alligator/Alligator.StreamHub.cs | 177 +++++ src/a-d/Alligator/Alligator.Utilities.cs | 73 +- src/a-d/Alligator/info.xml | 24 - src/a-d/Alma/Alma.Api.cs | 36 - src/a-d/Alma/Alma.Models.cs | 15 +- src/a-d/Alma/Alma.Series.cs | 84 --- src/a-d/Alma/Alma.StaticSeries.cs | 63 ++ src/a-d/Alma/Alma.Utilities.cs | 35 +- src/a-d/Aroon/Aroon.Api.cs | 15 - src/a-d/Aroon/Aroon.Models.cs | 19 +- src/a-d/Aroon/Aroon.Series.cs | 69 -- src/a-d/Aroon/Aroon.StaticSeries.cs | 73 ++ src/a-d/Aroon/Aroon.Utilities.cs | 21 +- src/a-d/Atr/Atr.Api.cs | 15 - src/a-d/Atr/Atr.Models.cs | 19 +- src/a-d/Atr/Atr.Series.cs | 84 --- src/a-d/Atr/Atr.StaticSeries.cs | 90 +++ src/a-d/Atr/Atr.StreamHub.cs | 98 +++ src/a-d/Atr/Atr.Utilities.cs | 56 +- src/a-d/AtrStop/AtrStop.Api.cs | 17 - src/a-d/AtrStop/AtrStop.Models.cs | 36 +- src/a-d/AtrStop/AtrStop.Series.cs | 111 --- src/a-d/AtrStop/AtrStop.StaticSeries.cs | 120 +++ src/a-d/AtrStop/AtrStop.StreamHub.cs | 205 ++++++ src/a-d/AtrStop/AtrStop.Utilities.cs | 37 +- src/a-d/Awesome/Awesome.Api.cs | 33 - src/a-d/Awesome/Awesome.Models.cs | 17 +- src/a-d/Awesome/Awesome.Series.cs | 69 -- src/a-d/Awesome/Awesome.StaticSeries.cs | 63 ++ src/a-d/Awesome/Awesome.Utilities.cs | 26 +- src/a-d/BasicQuote/BasicData.Models.cs | 15 - src/a-d/BasicQuote/BasicQuote.Api.cs | 14 - src/a-d/BasicQuote/info.xml | 17 - src/a-d/Beta/Beta.Api.cs | 58 -- src/a-d/Beta/Beta.Models.cs | 26 +- src/a-d/Beta/Beta.Series.cs | 173 ----- src/a-d/Beta/Beta.StaticSeries.cs | 163 +++++ src/a-d/Beta/Beta.Utilities.cs | 30 +- src/a-d/BollingerBands/BollingerBands.Api.cs | 33 - .../BollingerBands/BollingerBands.Models.cs | 26 +- .../BollingerBands/BollingerBands.Series.cs | 75 -- .../BollingerBands.StaticSeries.cs | 71 ++ .../BollingerBands.Utilities.cs | 28 +- src/a-d/Bop/Bop.Api.cs | 15 - src/a-d/Bop/Bop.Models.cs | 15 +- src/a-d/Bop/Bop.Series.cs | 54 -- src/a-d/Bop/Bop.StaticSeries.cs | 53 ++ src/a-d/Bop/Bop.Utilities.cs | 23 +- src/a-d/Cci/Cci.Api.cs | 15 - src/a-d/Cci/Cci.Models.cs | 15 +- .../{Cci.Series.cs => Cci.StaticSeries.cs} | 43 +- src/a-d/Cci/Cci.Utilities.cs | 24 +- src/a-d/ChaikinOsc/ChaikinOsc.Api.cs | 16 - src/a-d/ChaikinOsc/ChaikinOsc.Models.cs | 21 +- src/a-d/ChaikinOsc/ChaikinOsc.Series.cs | 64 -- src/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.cs | 46 ++ src/a-d/ChaikinOsc/ChaikinOsc.Utilities.cs | 30 +- src/a-d/Chandelier/Chandelier.Api.cs | 17 - src/a-d/Chandelier/Chandelier.Models.cs | 15 +- ...r.Series.cs => Chandelier.StaticSeries.cs} | 64 +- src/a-d/Chandelier/Chandelier.Utilities.cs | 28 +- src/a-d/Chop/Chop.Api.cs | 15 - src/a-d/Chop/Chop.Models.cs | 15 +- src/a-d/Chop/Chop.Series.cs | 79 -- src/a-d/Chop/Chop.StaticSeries.cs | 73 ++ src/a-d/Chop/Chop.Utilities.cs | 23 +- src/a-d/Cmf/Cmf.Api.cs | 15 - src/a-d/Cmf/Cmf.Models.cs | 19 +- src/a-d/Cmf/Cmf.Series.cs | 70 -- src/a-d/Cmf/Cmf.StaticSeries.cs | 68 ++ src/a-d/Cmf/Cmf.Utilities.cs | 24 +- src/a-d/Cmo/Cmo.Api.cs | 30 - src/a-d/Cmo/Cmo.Models.cs | 15 +- src/a-d/Cmo/Cmo.Series.cs | 85 --- src/a-d/Cmo/Cmo.StaticSeries.cs | 93 +++ src/a-d/Cmo/Cmo.Utilities.cs | 23 +- src/a-d/ConnorsRsi/ConnorsRsi.Api.cs | 36 - src/a-d/ConnorsRsi/ConnorsRsi.Models.cs | 24 +- src/a-d/ConnorsRsi/ConnorsRsi.Series.cs | 157 ---- src/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.cs | 168 +++++ src/a-d/ConnorsRsi/ConnorsRsi.Utilities.cs | 35 +- src/a-d/Correlation/Correlation.Api.cs | 51 -- src/a-d/Correlation/Correlation.Models.cs | 23 +- src/a-d/Correlation/Correlation.Series.cs | 119 --- .../Correlation/Correlation.StaticSeries.cs | 114 +++ src/a-d/Correlation/Correlation.Utilities.cs | 30 +- src/a-d/Dema/Dema.Api.cs | 30 - src/a-d/Dema/Dema.Models.cs | 15 +- src/a-d/Dema/Dema.Series.cs | 69 -- src/a-d/Dema/Dema.StaticSeries.cs | 69 ++ src/a-d/Dema/Dema.Utilities.cs | 25 +- src/a-d/Doji/Doji.Api.cs | 14 - src/a-d/Doji/Doji.Series.cs | 49 -- src/a-d/Doji/Doji.StaticSeries.cs | 60 ++ src/a-d/Doji/Doji.Utilities.cs | 18 + src/a-d/Doji/info.xml | 17 - src/a-d/Donchian/Donchian.Api.cs | 15 - src/a-d/Donchian/Donchian.Models.cs | 20 +- src/a-d/Donchian/Donchian.Series.cs | 69 -- src/a-d/Donchian/Donchian.StaticSeries.cs | 66 ++ src/a-d/Donchian/Donchian.Utilities.cs | 30 +- src/a-d/Dpo/Dpo.Api.cs | 30 - src/a-d/Dpo/Dpo.Models.cs | 17 +- src/a-d/Dpo/Dpo.Series.cs | 50 -- src/a-d/Dpo/Dpo.StaticSeries.cs | 50 ++ src/a-d/Dpo/Dpo.Utilities.cs | 19 + src/a-d/Dynamic/Dynamic.Api.cs | 33 - src/a-d/Dynamic/Dynamic.Models.cs | 15 +- src/a-d/Dynamic/Dynamic.Series.cs | 75 -- src/a-d/Dynamic/Dynamic.StaticSeries.cs | 44 ++ src/a-d/Dynamic/Dynamic.Utilities.cs | 36 + src/e-k/ElderRay/ElderRay.Api.cs | 15 - src/e-k/ElderRay/ElderRay.Models.cs | 19 +- src/e-k/ElderRay/ElderRay.Series.cs | 46 -- src/e-k/ElderRay/ElderRay.StaticSeries.cs | 44 ++ src/e-k/ElderRay/ElderRay.Utilities.cs | 23 +- src/e-k/Ema/Ema.Api.cs | 52 -- src/e-k/Ema/Ema.Increments.cs | 90 +++ src/e-k/Ema/Ema.Models.cs | 15 +- src/e-k/Ema/Ema.Observer.cs | 144 ---- src/e-k/Ema/Ema.Series.cs | 50 -- src/e-k/Ema/Ema.StaticSeries.cs | 52 ++ src/e-k/Ema/Ema.StreamHub.cs | 76 ++ src/e-k/Ema/Ema.Utilities.cs | 44 +- src/e-k/Ema/info.xml | 33 +- src/e-k/Epma/Epma.Api.cs | 30 - src/e-k/Epma/Epma.Models.cs | 15 +- src/e-k/Epma/Epma.Series.cs | 48 -- src/e-k/Epma/Epma.StaticSeries.cs | 33 + src/e-k/Epma/Epma.Utilities.cs | 23 +- src/e-k/Fcb/Fcb.Api.cs | 15 - src/e-k/Fcb/Fcb.Models.cs | 16 +- src/e-k/Fcb/Fcb.Series.cs | 58 -- src/e-k/Fcb/Fcb.StaticSeries.cs | 47 ++ src/e-k/Fcb/Fcb.Utilities.cs | 32 +- .../FisherTransform/FisherTransform.Api.cs | 30 - .../FisherTransform/FisherTransform.Models.cs | 17 +- .../FisherTransform/FisherTransform.Series.cs | 73 -- .../FisherTransform.StaticSeries.cs | 72 ++ .../FisherTransform.Utilities.cs | 18 + src/e-k/ForceIndex/ForceIndex.Api.cs | 15 - src/e-k/ForceIndex/ForceIndex.Models.cs | 15 +- src/e-k/ForceIndex/ForceIndex.Series.cs | 75 -- src/e-k/ForceIndex/ForceIndex.StaticSeries.cs | 71 ++ src/e-k/ForceIndex/ForceIndex.Utilities.cs | 23 +- src/e-k/Fractal/Fractal.Api.cs | 27 - src/e-k/Fractal/Fractal.Models.cs | 16 +- src/e-k/Fractal/Fractal.Series.cs | 95 --- src/e-k/Fractal/Fractal.StaticSeries.cs | 97 +++ src/e-k/Fractal/Fractal.Utilities.cs | 23 +- src/e-k/Gator/Gator.Api.cs | 39 - src/e-k/Gator/Gator.Models.cs | 21 +- src/e-k/Gator/Gator.Series.cs | 29 - src/e-k/Gator/Gator.StaticSeries.cs | 68 ++ src/e-k/Gator/Gator.Utilities.cs | 18 +- src/e-k/Gator/info.xml | 15 - src/e-k/HeikinAshi/HeikinAshi.Api.cs | 14 - src/e-k/HeikinAshi/HeikinAshi.Models.cs | 21 +- ...i.Series.cs => HeikinAshi.StaticSeries.cs} | 32 +- src/e-k/HeikinAshi/HeikinAshi.Utilities.cs | 19 - src/e-k/Hma/Hma.Api.cs | 30 - src/e-k/Hma/Hma.Models.cs | 15 +- src/e-k/Hma/Hma.Series.cs | 70 -- src/e-k/Hma/Hma.StaticSeries.cs | 68 ++ src/e-k/Hma/Hma.Utilities.cs | 21 +- src/e-k/HtTrendline/HtTrendline.Api.cs | 27 - src/e-k/HtTrendline/HtTrendline.Models.cs | 19 +- ....Series.cs => HtTrendline.StaticSeries.cs} | 70 +- src/e-k/HtTrendline/HtTrendline.Utilities.cs | 12 +- src/e-k/HtTrendline/info.xml | 15 - src/e-k/Hurst/Hurst.Api.cs | 29 - src/e-k/Hurst/Hurst.Models.cs | 15 +- ...{Hurst.Series.cs => Hurst.StaticSeries.cs} | 57 +- src/e-k/Hurst/Hurst.Utilities.cs | 23 +- src/e-k/Ichimoku/Ichimoku.Api.cs | 56 -- src/e-k/Ichimoku/Ichimoku.Models.cs | 22 +- src/e-k/Ichimoku/Ichimoku.Series.cs | 192 ----- src/e-k/Ichimoku/Ichimoku.StaticSeries.cs | 226 ++++++ src/e-k/Ichimoku/Ichimoku.Utilities.cs | 46 +- src/e-k/Kama/Kama.Api.cs | 35 - src/e-k/Kama/Kama.Models.cs | 17 +- src/e-k/Kama/Kama.Series.cs | 98 --- src/e-k/Kama/Kama.StaticSeries.cs | 91 +++ src/e-k/Kama/Kama.Utilities.cs | 39 +- src/e-k/Keltner/Keltner.Api.cs | 17 - src/e-k/Keltner/Keltner.Models.cs | 20 +- src/e-k/Keltner/Keltner.Series.cs | 80 -- src/e-k/Keltner/Keltner.StaticSeries.cs | 63 ++ src/e-k/Keltner/Keltner.Utilities.cs | 44 +- src/e-k/Kvo/Kvo.Api.cs | 17 - src/e-k/Kvo/Kvo.Models.cs | 17 +- .../{Kvo.Series.cs => Kvo.StaticSeries.cs} | 89 ++- src/e-k/Kvo/Kvo.Utilities.cs | 37 +- src/m-r/MaEnvelopes/MaEnvelopes.Api.cs | 36 - src/m-r/MaEnvelopes/MaEnvelopes.Models.cs | 18 +- src/m-r/MaEnvelopes/MaEnvelopes.Series.cs | 151 ---- .../MaEnvelopes/MaEnvelopes.StaticSeries.cs | 155 ++++ src/m-r/MaEnvelopes/MaEnvelopes.Utilities.cs | 23 +- src/m-r/Macd/Macd.Models.cs | 26 +- src/m-r/Macd/Macd.Series.cs | 88 --- src/m-r/Macd/Macd.StaticSeries.cs | 82 +++ src/m-r/Macd/Macd.Utilities.cs | 37 +- src/m-r/Macd/MacdApi.cs | 36 - src/m-r/Mama/Mama.Api.cs | 33 - src/m-r/Mama/Mama.Models.cs | 17 +- .../{Mama.Series.cs => Mama.StaticSeries.cs} | 145 ++-- src/m-r/Mama/Mama.Utilities.cs | 30 +- src/m-r/Marubozu/Marubozu.Api.cs | 14 - src/m-r/Marubozu/Marubozu.Series.cs | 54 -- src/m-r/Marubozu/Marubozu.StaticSeries.cs | 46 ++ src/m-r/Marubozu/Marubozu.Utilities.cs | 28 + src/m-r/Marubozu/info.xml | 4 +- src/m-r/Mfi/Mfi.Api.cs | 15 - src/m-r/Mfi/Mfi.Models.cs | 15 +- src/m-r/Mfi/Mfi.Series.cs | 101 --- src/m-r/Mfi/Mfi.StaticSeries.cs | 102 +++ src/m-r/Mfi/Mfi.Utilities.cs | 23 +- src/m-r/Obv/Obv.Api.cs | 15 - src/m-r/Obv/Obv.Models.cs | 16 +- src/m-r/Obv/Obv.Series.cs | 71 -- src/m-r/Obv/Obv.StaticSeries.cs | 50 ++ src/m-r/Obv/info.xml | 1 - src/m-r/ParabolicSar/ParabolicSar.Api.cs | 34 - src/m-r/ParabolicSar/ParabolicSar.Models.cs | 17 +- ...Series.cs => ParabolicSar.StaticSeries.cs} | 135 ++-- .../ParabolicSar/ParabolicSar.Utilities.cs | 49 +- src/m-r/PivotPoints/PivotPoints.Api.cs | 16 - src/m-r/PivotPoints/PivotPoints.Models.cs | 58 +- ....Series.cs => PivotPoints.StaticSeries.cs} | 192 +++-- src/m-r/PivotPoints/PivotPoints.Utilities.cs | 25 +- src/m-r/Pivots/Pivots.Api.cs | 18 - src/m-r/Pivots/Pivots.Models.cs | 32 +- src/m-r/Pivots/Pivots.Series.cs | 135 ---- src/m-r/Pivots/Pivots.StaticSeries.cs | 141 ++++ src/m-r/Pivots/Pivots.Utilities.cs | 39 +- src/m-r/Pmo/Pmo.Api.cs | 36 - src/m-r/Pmo/Pmo.Models.cs | 20 +- src/m-r/Pmo/Pmo.Series.cs | 155 ---- src/m-r/Pmo/Pmo.StaticSeries.cs | 112 +++ src/m-r/Pmo/Pmo.Utilities.cs | 37 +- src/m-r/Prs/Prs.Api.cs | 53 -- src/m-r/Prs/Prs.Models.cs | 18 +- src/m-r/Prs/Prs.Series.cs | 109 --- src/m-r/Prs/Prs.StaticSeries.cs | 66 ++ src/m-r/Prs/Prs.Utilities.cs | 49 ++ src/m-r/Prs/info.xml | 1 - src/m-r/Pvo/Pvo.Api.cs | 19 - src/m-r/Pvo/Pvo.Models.cs | 19 +- src/m-r/Pvo/Pvo.Series.cs | 87 --- src/m-r/Pvo/Pvo.StaticSeries.cs | 120 +++ src/m-r/Pvo/Pvo.Utilities.cs | 37 +- src/m-r/Renko/Renko.Api.cs | 16 - src/m-r/Renko/Renko.Models.cs | 25 +- src/m-r/Renko/Renko.Series.cs | 156 ---- src/m-r/Renko/Renko.StaticSeries.cs | 94 +++ src/m-r/Renko/Renko.StreamHub.cs | 200 +++++ src/m-r/Renko/Renko.Utilities.cs | 63 ++ src/m-r/Renko/RenkoAtr.Api.cs | 15 - src/m-r/Renko/RenkoAtr.Series.cs | 24 - src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs | 25 + src/m-r/Roc/Roc.Api.cs | 33 - src/m-r/Roc/Roc.Models.cs | 18 +- src/m-r/Roc/Roc.Series.cs | 68 -- src/m-r/Roc/Roc.StaticSeries.cs | 54 ++ src/m-r/Roc/Roc.Utilities.cs | 23 +- src/m-r/Roc/info.xml | 1 - src/m-r/RocWb/Roc.Models.cs | 17 - src/m-r/RocWb/RocWb.Api.cs | 36 - src/m-r/RocWb/RocWb.Models.cs | 14 + src/m-r/RocWb/RocWb.Series.cs | 107 --- src/m-r/RocWb/RocWb.StaticSeries.cs | 83 +++ src/m-r/RocWb/RocWb.Utilities.cs | 37 +- src/m-r/RollingPivots/RollingPivots.Api.cs | 17 - src/m-r/RollingPivots/RollingPivots.Models.cs | 24 +- src/m-r/RollingPivots/RollingPivots.Series.cs | 85 --- .../RollingPivots.StaticSeries.cs | 80 ++ .../RollingPivots/RollingPivots.Utilities.cs | 30 +- src/m-r/Rsi/Rsi.Api.cs | 30 - src/m-r/Rsi/Rsi.Models.cs | 15 +- src/m-r/Rsi/Rsi.Series.cs | 94 --- src/m-r/Rsi/Rsi.StaticSeries.cs | 96 +++ src/m-r/Rsi/Rsi.Utilities.cs | 23 +- src/s-z/Slope/Slope.Api.cs | 30 - src/s-z/Slope/Slope.Models.cs | 23 +- src/s-z/Slope/Slope.Series.cs | 104 --- src/s-z/Slope/Slope.StaticSeries.cs | 111 +++ src/s-z/Slope/Slope.Utilities.cs | 23 +- src/s-z/Sma/Sma.Analysis.cs | 49 -- src/s-z/Sma/Sma.Api.cs | 77 -- src/s-z/Sma/Sma.Models.cs | 30 +- src/s-z/Sma/Sma.Observer.cs | 139 ---- src/s-z/Sma/Sma.Series.cs | 31 - src/s-z/Sma/Sma.StaticSeries.cs | 58 ++ src/s-z/Sma/Sma.StreamHub.cs | 60 ++ src/s-z/Sma/Sma.Utilities.cs | 124 +++- src/s-z/Sma/info.xml | 35 +- src/s-z/SmaAnalysis/SmaAnalysis.Models.cs | 19 + .../SmaAnalysis/SmaAnalysis.StaticSeries.cs | 54 ++ src/s-z/Smi/Smi.Api.cs | 22 - src/s-z/Smi/Smi.Models.cs | 17 +- src/s-z/Smi/Smi.Series.cs | 140 ---- src/s-z/Smi/Smi.StaticSeries.cs | 132 ++++ src/s-z/Smi/Smi.Utilities.cs | 44 +- src/s-z/Smma/Smma.Api.cs | 30 - src/s-z/Smma/Smma.Models.cs | 15 +- src/s-z/Smma/Smma.Series.cs | 66 -- src/s-z/Smma/Smma.StaticSeries.cs | 63 ++ src/s-z/Smma/Smma.Utilities.cs | 23 +- src/s-z/StarcBands/StarcBands.Api.cs | 17 - src/s-z/StarcBands/StarcBands.Models.cs | 18 +- src/s-z/StarcBands/StarcBands.Series.cs | 67 -- src/s-z/StarcBands/StarcBands.StaticSeries.cs | 46 ++ src/s-z/StarcBands/StarcBands.Utilities.cs | 44 +- src/s-z/Stc/Stc.Api.cs | 36 - src/s-z/Stc/Stc.Models.cs | 15 +- src/s-z/Stc/Stc.Series.cs | 75 -- src/s-z/Stc/Stc.StaticSeries.cs | 45 ++ src/s-z/Stc/Stc.Utilities.cs | 37 +- src/s-z/StdDev/StdDev.Api.cs | 33 - src/s-z/StdDev/StdDev.Models.cs | 20 +- src/s-z/StdDev/StdDev.Series.cs | 83 --- src/s-z/StdDev/StdDev.StaticSeries.cs | 68 ++ src/s-z/StdDev/StdDev.Utilities.cs | 23 +- src/s-z/StdDev/info.xml | 1 - src/s-z/StdDevChannels/StdDevChannels.Api.cs | 33 - .../StdDevChannels/StdDevChannels.Models.cs | 20 +- .../StdDevChannels/StdDevChannels.Series.cs | 68 -- .../StdDevChannels.StaticSeries.cs | 58 ++ .../StdDevChannels.Utilities.cs | 39 +- src/s-z/Stoch/Stoch.Api.cs | 41 -- src/s-z/Stoch/Stoch.Models.cs | 21 +- src/s-z/Stoch/Stoch.Series.cs | 213 ------ src/s-z/Stoch/Stoch.StaticSeries.cs | 216 ++++++ src/s-z/Stoch/Stoch.Utilities.cs | 58 +- src/s-z/StochRsi/StochRsi.Api.cs | 51 -- src/s-z/StochRsi/StochRsi.Models.cs | 17 +- src/s-z/StochRsi/StochRsi.Series.cs | 91 --- src/s-z/StochRsi/StochRsi.StaticSeries.cs | 64 ++ src/s-z/StochRsi/StochRsi.Utilities.cs | 44 +- src/s-z/SuperTrend/SuperTrend.Api.cs | 16 - src/s-z/SuperTrend/SuperTrend.Models.cs | 18 +- src/s-z/SuperTrend/SuperTrend.Series.cs | 98 --- src/s-z/SuperTrend/SuperTrend.StaticSeries.cs | 105 +++ src/s-z/SuperTrend/SuperTrend.Utilities.cs | 37 +- src/s-z/T3/T3.Api.cs | 33 - src/s-z/T3/T3.Models.cs | 15 +- src/s-z/T3/T3.Series.cs | 82 --- src/s-z/T3/T3.StaticSeries.cs | 63 ++ src/s-z/T3/T3.Utilities.cs | 25 + src/s-z/Tema/Tema.Api.cs | 30 - src/s-z/Tema/Tema.Models.cs | 15 +- src/s-z/Tema/Tema.Series.cs | 73 -- src/s-z/Tema/Tema.StaticSeries.cs | 74 ++ src/s-z/Tema/Tema.Utilities.cs | 25 +- src/s-z/Tr/Tr.Api.cs | 14 - src/s-z/Tr/Tr.Models.cs | 14 +- src/s-z/Tr/Tr.Series.cs | 38 - src/s-z/Tr/Tr.StaticSeries.cs | 38 + src/s-z/Tr/Tr.StreamHub.cs | 58 ++ src/s-z/Tr/Tr.Utilities.cs | 17 + src/s-z/Tr/info.xml | 38 +- src/s-z/Trix/Trix.Api.cs | 33 - src/s-z/Trix/Trix.Models.cs | 18 +- src/s-z/Trix/Trix.Series.cs | 88 --- src/s-z/Trix/Trix.StaticSeries.cs | 78 ++ src/s-z/Trix/Trix.Utilities.cs | 25 +- src/s-z/Trix/info.xml | 1 - src/s-z/Tsi/Tsi.Api.cs | 36 - src/s-z/Tsi/Tsi.Models.cs | 17 +- src/s-z/Tsi/Tsi.Series.cs | 161 ----- src/s-z/Tsi/Tsi.StaticSeries.cs | 149 ++++ src/s-z/Tsi/Tsi.Utilities.cs | 37 +- src/s-z/UlcerIndex/UlcerIndex.Api.cs | 30 - src/s-z/UlcerIndex/UlcerIndex.Models.cs | 16 +- src/s-z/UlcerIndex/UlcerIndex.Series.cs | 66 -- src/s-z/UlcerIndex/UlcerIndex.StaticSeries.cs | 66 ++ src/s-z/UlcerIndex/UlcerIndex.Utilities.cs | 23 +- src/s-z/Ultimate/Ultimate.Api.cs | 17 - src/s-z/Ultimate/Ultimate.Models.cs | 15 +- src/s-z/Ultimate/Ultimate.Series.cs | 102 --- src/s-z/Ultimate/Ultimate.StaticSeries.cs | 98 +++ src/s-z/Ultimate/Ultimate.Utilities.cs | 29 +- src/s-z/VolatilityStop/VolatilityStop.Api.cs | 16 - .../VolatilityStop/VolatilityStop.Models.cs | 22 +- .../VolatilityStop/VolatilityStop.Series.cs | 124 ---- .../VolatilityStop.StaticSeries.cs | 119 +++ .../VolatilityStop.Utilities.cs | 30 +- src/s-z/Vortex/Vortex.Api.cs | 15 - src/s-z/Vortex/Vortex.Models.cs | 16 +- ...ortex.Series.cs => Vortex.StaticSeries.cs} | 51 +- src/s-z/Vortex/Vortex.Utilities.cs | 30 +- src/s-z/Vwap/Vwap.Api.cs | 15 - src/s-z/Vwap/Vwap.Models.cs | 15 +- src/s-z/Vwap/Vwap.Series.cs | 69 -- src/s-z/Vwap/Vwap.StaticSeries.cs | 66 ++ src/s-z/Vwap/Vwap.Utilities.cs | 30 +- src/s-z/Vwma/Vwma.Api.cs | 15 - src/s-z/Vwma/Vwma.Models.cs | 15 +- src/s-z/Vwma/Vwma.Series.cs | 57 -- src/s-z/Vwma/Vwma.StaticSeries.cs | 60 ++ src/s-z/Vwma/Vwma.Utilities.cs | 23 +- src/s-z/WilliamsR/WilliamsR.Api.cs | 15 - src/s-z/WilliamsR/WilliamsR.Models.cs | 15 +- src/s-z/WilliamsR/WilliamsR.Series.cs | 32 - src/s-z/WilliamsR/WilliamsR.StaticSeries.cs | 29 + src/s-z/WilliamsR/WilliamsR.Utilities.cs | 23 +- src/s-z/Wma/Wma.Api.cs | 30 - src/s-z/Wma/Wma.Models.cs | 15 +- src/s-z/Wma/Wma.Series.cs | 52 -- src/s-z/Wma/Wma.StaticSeries.cs | 50 ++ src/s-z/Wma/Wma.Utilities.cs | 23 +- src/s-z/ZigZag/ZigZag.Api.cs | 16 - src/s-z/ZigZag/ZigZag.Models.cs | 23 +- ...igZag.Series.cs => ZigZag.StaticSeries.cs} | 162 +++-- src/s-z/ZigZag/ZigZag.Utilities.cs | 23 +- tests/application/GlobalUsings.cs | 2 + tests/application/Program.cs | 216 ++++++ tests/application/README.md | 3 + tests/application/Test.Application.csproj | 19 + .../application/_testdata/TestData.Getter.cs | 52 ++ .../application/_testdata/TestData.Imports.cs | 46 ++ .../_testdata}/data/compare.csv | 0 .../_testdata}/data/default.csv | 0 .../_testdata}/data/intraday.csv | 0 .../_testdata}/data/longest.csv | 0 .../_testdata}/data/longish.csv | 0 tests/indicators/GlobalSuppressions.cs | 25 - tests/indicators/GlobalUsings.cs | 2 +- tests/indicators/TestBase.cs | 81 +++ tests/indicators/Tests.Indicators.csproj | 7 +- tests/indicators/_Initialize.cs | 30 - .../_common/Candles/Candles.Tests.cs | 90 +-- .../_common/Generics/BinarySettingsTests.cs | 73 ++ .../_common/Generics/Pruning.Tests.cs | 31 - .../_common/Generics/RemoveWarmup.Tests.cs | 31 + .../indicators/_common/Generics/Seek.Tests.cs | 34 +- .../indicators/_common/Generics/Sort.Tests.cs | 32 - .../_common/Generics/Sorting.Tests.cs | 32 + .../_common/Generics/Transforms.Tests.cs | 12 +- tests/indicators/_common/Helper.Getter.cs | 212 ------ tests/indicators/_common/Helper.Importer.cs | 73 -- tests/indicators/_common/Helper.Random.cs | 77 -- .../{Numerix.Tests.cs => Numerical.Tests.cs} | 42 +- .../Observables/StreamHub.CacheMgmt.Tests.cs | 159 ++++ .../Observables/StreamHub.Observable.Tests.cs | 112 +++ .../Observables/StreamHub.Observer.Tests.cs | 62 ++ .../StreamHub.Stackoverflow.Tests.cs | 260 +++++++ .../StreamHub.Utilities.StaticSeries.Tests.cs | 189 +++++ .../_common/Quotes/Quote.Aggregates.Tests.cs | 62 +- .../_common/Quotes/Quote.Converters.Tests.cs | 259 +------ .../_common/Quotes/Quote.Equality.Tests.cs | 26 + .../_common/Quotes/Quote.Exceptions.Tests.cs | 26 +- .../_common/Quotes/Quote.StreamHub.Tests.cs | 134 ++++ .../_common/Quotes/Quote.Validation.Tests.cs | 92 ++- .../_common/Quotes/Test.Quote.Provider.cs | 61 -- .../_common/Quotes/Test.Use.Observer.cs | 99 --- .../_common/Results/Result.Syncing.Tests.cs | 101 --- .../_common/Results/Result.Utilities.Tests.cs | 71 -- .../Reusable/Reusable.Utilities.Tests.cs | 98 +++ .../QuotePart.StaticSeries.Tests.cs | 88 +++ .../QuotePart.StreamHub.Tests.cs | 142 ++++ .../QuotePart.Utilities.Tests.cs | 89 +++ .../Use (QuotePart)/Use.Calc.xlsx} | Bin .../Data.Aggregate.xlsx | Bin .../{_common => _testdata}/Data.Candles.xlsx | Bin .../{_common => _testdata}/Data.Quotes.xlsx | Bin tests/indicators/_testdata/TestData.Getter.cs | 141 ++++ .../indicators/_testdata/TestData.Imports.cs | 46 ++ tests/indicators/_testdata/TestData.Random.cs | 79 ++ tests/indicators/_testdata/TestData.Tests.cs | 77 ++ .../{_common => _testdata}/data/bad.csv | 0 .../{_common => _testdata}/data/bitcoin.csv | 0 .../data/btcusd15x69k.csv | 0 .../_testdata}/data/compare.csv | 0 .../_testdata}/data/default.csv | 0 .../_testdata}/data/intraday.csv | 0 .../_testdata}/data/longest.csv | 2 +- .../_testdata}/data/longish.csv | 0 .../{_common => _testdata}/data/max.csv | 0 .../{_common => _testdata}/data/mismatch.csv | 2 +- .../{_common => _testdata}/data/msft.csv | 0 .../{_common => _testdata}/data/penny.csv | 0 .../{_common => _testdata}/data/spx.csv | 0 .../{_common => _testdata}/data/toobig.csv | 0 .../{_common => _testdata}/data/zeros.csv | 0 .../a-d/Adl/Adl.StaticSeries.Tests.cs | 82 +++ .../indicators/a-d/Adl/Adl.StreamHub.Tests.cs | 128 ++++ tests/indicators/a-d/Adl/Adl.Tests.cs | 117 --- ...Adx.Tests.cs => Adx.StaticSeries.Tests.cs} | 66 +- tests/indicators/a-d/Adx/issue859quotes.csv | 2 +- ...sts.cs => Alligator.StaticSeries.Tests.cs} | 126 ++-- .../Alligator/Alligator.StreamHub.Tests.cs | 135 ++++ ...ma.Tests.cs => Alma.StaticSeries.Tests.cs} | 88 +-- ...n.Tests.cs => Aroon.StaticSeries.Tests.cs} | 50 +- ...Atr.Tests.cs => Atr.StaticSeries.Tests.cs} | 71 +- .../indicators/a-d/Atr/Atr.StreamHub.Tests.cs | 120 +++ ...Tests.cs => AtrStop.StaticSeries.Tests.cs} | 81 +-- .../a-d/AtrStop/AtrStop.StreamHub.Tests.cs | 107 +++ ...Tests.cs => Awesome.StaticSeries.Tests.cs} | 77 +- .../a-d/BasicQuote/BasicQuote.Tests.cs | 74 -- ...ta.Tests.cs => Beta.StaticSeries.Tests.cs} | 174 ++--- ...s => BollingerBands.StaticSeries.Tests.cs} | 77 +- ...Bop.Tests.cs => Bop.StaticSeries.Tests.cs} | 56 +- ...Cci.Tests.cs => Cci.StaticSeries.Tests.cs} | 50 +- ...ts.cs => ChaikinOsc.StaticSeries.Tests.cs} | 52 +- ...ts.cs => Chandelier.StaticSeries.Tests.cs} | 61 +- ...op.Tests.cs => Chop.StaticSeries.Tests.cs} | 55 +- ...Cmf.Tests.cs => Cmf.StaticSeries.Tests.cs} | 55 +- ...Cmo.Tests.cs => Cmo.StaticSeries.Tests.cs} | 83 +-- ...ts.cs => ConnorsRsi.StaticSeries.Tests.cs} | 81 +-- ...s.cs => Correlation.StaticSeries.Tests.cs} | 88 +-- ...ma.Tests.cs => Dema.StaticSeries.Tests.cs} | 75 +- ...ji.Tests.cs => Doji.StaticSeries.Tests.cs} | 43 +- ...ests.cs => Donchian.StaticSeries.Tests.cs} | 52 +- ...Dpo.Tests.cs => Dpo.StaticSeries.Tests.cs} | 79 +- ...Tests.cs => Dynamic.StaticSeries.Tests.cs} | 70 +- ...ests.cs => ElderRay.StaticSeries.Tests.cs} | 50 +- .../e-k/Ema/Ema.Increments.Tests.cs | 72 ++ tests/indicators/e-k/Ema/Ema.Obs.Tests.cs | 100 --- ...tic.Tests.cs => Ema.StaticSeries.Tests.cs} | 104 ++- .../indicators/e-k/Ema/Ema.StreamHub.Tests.cs | 176 +++++ ...ma.Tests.cs => Epma.StaticSeries.Tests.cs} | 75 +- ...Fcb.Tests.cs => Fcb.StaticSeries.Tests.cs} | 50 +- ... => FisherTransform.StaticSeries.Tests.cs} | 66 +- ...ts.cs => ForceIndex.StaticSeries.Tests.cs} | 47 +- ...Tests.cs => Fractal.StaticSeries.Tests.cs} | 46 +- ...r.Tests.cs => Gator.StaticSeries.Tests.cs} | 82 +-- .../HeikinAshi.StaticSeries.Tests.cs | 54 ++ .../e-k/HeikinAshi/HeikinAshi.Tests.cs | 83 --- ...Hma.Tests.cs => Hma.StaticSeries.Tests.cs} | 75 +- ...s.cs => HtTrendline.StaticSeries.Tests.cs} | 80 +- ...t.Tests.cs => Hurst.StaticSeries.Tests.cs} | 73 +- ...ests.cs => Ichimoku.StaticSeries.Tests.cs} | 58 +- ...ma.Tests.cs => Kama.StaticSeries.Tests.cs} | 97 +-- ...Tests.cs => Keltner.StaticSeries.Tests.cs} | 56 +- ...Kvo.Tests.cs => Kvo.StaticSeries.Tests.cs} | 54 +- ...s.cs => MaEnvelopes.StaticSeries.Tests.cs} | 193 +++-- ...cd.Tests.cs => Macd.StaticSeries.Tests.cs} | 79 +- ...ma.Tests.cs => Mama.StaticSeries.Tests.cs} | 77 +- ...ests.cs => Marubozu.StaticSeries.Tests.cs} | 43 +- ...Mfi.Tests.cs => Mfi.StaticSeries.Tests.cs} | 55 +- tests/indicators/m-r/Obv/Obv.Calc.xlsx | Bin 68934 -> 68868 bytes .../m-r/Obv/Obv.StaticSeries.Tests.cs | 66 ++ tests/indicators/m-r/Obv/Obv.Tests.cs | 98 --- ....cs => ParabolicSar.StaticSeries.Tests.cs} | 68 +- ...s.cs => PivotPoints.StaticSeries.Tests.cs} | 72 +- ....Tests.cs => Pivots.StaticSeries.Tests.cs} | 57 +- tests/indicators/m-r/Pmo/Pmo.Calc.xlsx | Bin 100121 -> 119865 bytes ...Pmo.Tests.cs => Pmo.StaticSeries.Tests.cs} | 79 +- .../m-r/Prs/Prs.StaticSeries.Tests.cs | 109 +++ tests/indicators/m-r/Prs/Prs.Tests.cs | 136 ---- ...Pvo.Tests.cs => Pvo.StaticSeries.Tests.cs} | 54 +- tests/indicators/m-r/Renko/Renko.Calc.xlsx | Bin 133684 -> 135491 bytes ...o.Tests.cs => Renko.StaticSeries.Tests.cs} | 49 +- .../m-r/Renko/Renko.StreamHub.Tests.cs | 147 ++++ .../m-r/Roc/Roc.StaticSeries.Tests.cs | 107 +++ tests/indicators/m-r/Roc/Roc.Tests.cs | 162 ----- ...b.Tests.cs => RocWb.StaticSeries.Tests.cs} | 79 +- ...cs => RollingPivots.StaticSeries.Tests.cs} | 69 +- ...Rsi.Tests.cs => Rsi.StaticSeries.Tests.cs} | 93 +-- ...e.Tests.cs => Slope.StaticSeries.Tests.cs} | 80 +- .../indicators/s-z/Sma/Sma.Analysis.Tests.cs | 117 --- tests/indicators/s-z/Sma/Sma.Obs.Tests.cs | 126 ---- ...tic.Tests.cs => Sma.StaticSeries.Tests.cs} | 102 +-- .../indicators/s-z/Sma/Sma.StreamHub.Tests.cs | 166 +++++ .../SmaAnalysis.StaticSeries.Tests.cs | 98 +++ ...Smi.Tests.cs => Smi.StaticSeries.Tests.cs} | 66 +- ...ma.Tests.cs => Smma.StaticSeries.Tests.cs} | 75 +- ...ts.cs => StarcBands.StaticSeries.Tests.cs} | 56 +- ...Stc.Tests.cs => Stc.StaticSeries.Tests.cs} | 91 +-- .../s-z/StdDev/StdDev.StaticSeries.Tests.cs | 127 ++++ tests/indicators/s-z/StdDev/StdDev.Tests.cs | 185 ----- ...s => StdDevChannels.StaticSeries.Tests.cs} | 84 +-- ...h.Tests.cs => Stoch.StaticSeries.Tests.cs} | 97 ++- ...ests.cs => StochRsi.StaticSeries.Tests.cs} | 88 +-- ...ts.cs => SuperTrend.StaticSeries.Tests.cs} | 63 +- .../{T3.Tests.cs => T3.StaticSeries.Tests.cs} | 68 +- ...ma.Tests.cs => Tema.StaticSeries.Tests.cs} | 77 +- .../{Tr.Tests.cs => Tr.StaticSeries.Tests.cs} | 46 +- tests/indicators/s-z/Tr/Tr.StreamHub.Tests.cs | 120 +++ ...ix.Tests.cs => Trix.StaticSeries.Tests.cs} | 83 +-- ...Tsi.Tests.cs => Tsi.StaticSeries.Tests.cs} | 96 +-- .../UlcerIndex.StaticSeries.Tests.cs | 97 +++ .../s-z/UlcerIndex/UlcerIndex.Tests.cs | 116 --- ...ests.cs => Ultimate.StaticSeries.Tests.cs} | 54 +- ...s => VolatilityStop.StaticSeries.Tests.cs} | 54 +- ....Tests.cs => Vortex.StaticSeries.Tests.cs} | 52 +- ...ap.Tests.cs => Vwap.StaticSeries.Tests.cs} | 75 +- ...ma.Tests.cs => Vwma.StaticSeries.Tests.cs} | 50 +- ...sts.cs => WilliamsR.StaticSeries.Tests.cs} | 71 +- .../s-z/WilliamsR/issue1127quotes.csv | 2 +- ...Wma.Tests.cs => Wma.StaticSeries.Tests.cs} | 87 +-- ....Tests.cs => ZigZag.StaticSeries.Tests.cs} | 83 +-- tests/indicators/s-z/ZigZag/data.ethusdt.json | 600 +++++++-------- .../indicators/s-z/ZigZag/data.issue632.json | 34 +- .../s-z/ZigZag/data.schrodinger.json | 684 +++++++++--------- tests/observe/Program.cs | 122 ---- tests/observe/README.md | 10 - tests/other/Convergence.Tests.cs | 270 +++---- tests/other/Custom.Indicator.Tests.cs | 162 +++++ tests/other/Custom.Quotes.Tests.cs | 178 +++++ tests/other/Custom.Results.Tests.cs | 86 +++ tests/other/CustomIndicator.Tests.cs | 264 ------- tests/other/GlobalUsings.cs | 3 +- tests/other/PublicApi.Interface.Tests.cs | 147 ++++ tests/other/PublicApi.Tests.cs | 237 ------ tests/other/Sut.CustomItems.cs | 138 ++++ tests/other/Tests.Other.csproj | 5 + tests/performance/GlobalSuppressions.cs | 23 +- tests/performance/GlobalUsings.cs | 2 +- tests/performance/Perf.Helpers.cs | 40 - tests/performance/Perf.Increments.cs | 95 +++ tests/performance/Perf.Indicators.Static.cs | 324 --------- tests/performance/Perf.Indicators.Stream.cs | 62 -- tests/performance/Perf.StaticSeries.cs | 277 +++++++ tests/performance/Perf.StreamHub.Externals.cs | 42 ++ tests/performance/Perf.StreamHub.cs | 78 ++ ...erf.Internals.cs => Perf.Utility.Maths.cs} | 12 +- tests/performance/Perf.Utility.cs | 31 + tests/performance/Program.cs | 11 +- tests/performance/Tests.Performance.csproj | 27 +- tests/performance/helpers/Helper.Getter.cs | 48 -- tests/performance/helpers/Helper.Importer.cs | 73 -- tests/simulate/Program.cs | 37 + .../Test.Simulation.csproj} | 5 +- tests/simulate/Utilities.cs | 140 ++++ 801 files changed, 22282 insertions(+), 21354 deletions(-) rename docs/_indicators/{BasicQuote.md => QuotePart.md} (74%) create mode 100644 src/_common/BinarySettings.cs create mode 100644 src/_common/Candles/Candles.Models.cs create mode 100644 src/_common/Candles/Candles.Utilities.cs delete mode 100644 src/_common/Candles/Candles.cs delete mode 100644 src/_common/Candles/Models.cs delete mode 100644 src/_common/Generics/Series.Model.cs delete mode 100644 src/_common/Generics/info.xml create mode 100644 src/_common/ISeries.cs create mode 100644 src/_common/Incrementals/IIncremental.cs rename src/_common/Math/{Numerix.cs => Numerical.cs} (70%) delete mode 100644 src/_common/Observables/ChainProvider.cs create mode 100644 src/_common/Observables/IStreamHub.cs create mode 100644 src/_common/Observables/IStreamObservable.cs create mode 100644 src/_common/Observables/IStreamObserver.cs delete mode 100644 src/_common/Observables/QuoteObserver.cs delete mode 100644 src/_common/Observables/QuoteProvider.cs create mode 100644 src/_common/Observables/StreamHub.Observable.cs create mode 100644 src/_common/Observables/StreamHub.Observer.cs create mode 100644 src/_common/Observables/StreamHub.Utilities.cs create mode 100644 src/_common/Observables/StreamHub.cs delete mode 100644 src/_common/Observables/TupleObserver.cs delete mode 100644 src/_common/Observables/TupleProvider.cs delete mode 100644 src/_common/ObsoleteV2.cs create mode 100644 src/_common/ObsoleteV3.cs create mode 100644 src/_common/ObsoleteV3.md create mode 100644 src/_common/Quotes/Quote.StreamHub.cs delete mode 100644 src/_common/Quotes/Use.Api.cs delete mode 100644 src/_common/Quotes/Use.Observer.cs delete mode 100644 src/_common/Results/Result.Models.cs delete mode 100644 src/_common/Results/Result.Syncing.cs delete mode 100644 src/_common/Results/Result.Utilities.cs delete mode 100644 src/_common/Results/info.xml create mode 100644 src/_common/Reusable/IReusable.cs create mode 100644 src/_common/Reusable/Reusable.Utilities.cs create mode 100644 src/_common/Use (QuotePart)/QuotePart.Models.cs create mode 100644 src/_common/Use (QuotePart)/QuotePart.StaticSeries.cs create mode 100644 src/_common/Use (QuotePart)/QuotePart.StreamHub.cs create mode 100644 src/_common/Use (QuotePart)/QuotePart.Utilities.cs delete mode 100644 src/a-d/Adl/Adl.Api.cs delete mode 100644 src/a-d/Adl/Adl.Series.cs create mode 100644 src/a-d/Adl/Adl.StaticSeries.cs create mode 100644 src/a-d/Adl/Adl.StreamHub.cs create mode 100644 src/a-d/Adl/Adl.Utilities.cs delete mode 100644 src/a-d/Adl/info.xml delete mode 100644 src/a-d/Adx/Adx.Api.cs rename src/a-d/Adx/{Adx.Series.cs => Adx.StaticSeries.cs} (61%) delete mode 100644 src/a-d/Alligator/Alligator.Api.cs delete mode 100644 src/a-d/Alligator/Alligator.Series.cs create mode 100644 src/a-d/Alligator/Alligator.StaticSeries.cs create mode 100644 src/a-d/Alligator/Alligator.StreamHub.cs delete mode 100644 src/a-d/Alligator/info.xml delete mode 100644 src/a-d/Alma/Alma.Api.cs delete mode 100644 src/a-d/Alma/Alma.Series.cs create mode 100644 src/a-d/Alma/Alma.StaticSeries.cs delete mode 100644 src/a-d/Aroon/Aroon.Api.cs delete mode 100644 src/a-d/Aroon/Aroon.Series.cs create mode 100644 src/a-d/Aroon/Aroon.StaticSeries.cs delete mode 100644 src/a-d/Atr/Atr.Api.cs delete mode 100644 src/a-d/Atr/Atr.Series.cs create mode 100644 src/a-d/Atr/Atr.StaticSeries.cs create mode 100644 src/a-d/Atr/Atr.StreamHub.cs delete mode 100644 src/a-d/AtrStop/AtrStop.Api.cs delete mode 100644 src/a-d/AtrStop/AtrStop.Series.cs create mode 100644 src/a-d/AtrStop/AtrStop.StaticSeries.cs create mode 100644 src/a-d/AtrStop/AtrStop.StreamHub.cs delete mode 100644 src/a-d/Awesome/Awesome.Api.cs delete mode 100644 src/a-d/Awesome/Awesome.Series.cs create mode 100644 src/a-d/Awesome/Awesome.StaticSeries.cs delete mode 100644 src/a-d/BasicQuote/BasicData.Models.cs delete mode 100644 src/a-d/BasicQuote/BasicQuote.Api.cs delete mode 100644 src/a-d/BasicQuote/info.xml delete mode 100644 src/a-d/Beta/Beta.Api.cs delete mode 100644 src/a-d/Beta/Beta.Series.cs create mode 100644 src/a-d/Beta/Beta.StaticSeries.cs delete mode 100644 src/a-d/BollingerBands/BollingerBands.Api.cs delete mode 100644 src/a-d/BollingerBands/BollingerBands.Series.cs create mode 100644 src/a-d/BollingerBands/BollingerBands.StaticSeries.cs delete mode 100644 src/a-d/Bop/Bop.Api.cs delete mode 100644 src/a-d/Bop/Bop.Series.cs create mode 100644 src/a-d/Bop/Bop.StaticSeries.cs delete mode 100644 src/a-d/Cci/Cci.Api.cs rename src/a-d/Cci/{Cci.Series.cs => Cci.StaticSeries.cs} (59%) delete mode 100644 src/a-d/ChaikinOsc/ChaikinOsc.Api.cs delete mode 100644 src/a-d/ChaikinOsc/ChaikinOsc.Series.cs create mode 100644 src/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.cs delete mode 100644 src/a-d/Chandelier/Chandelier.Api.cs rename src/a-d/Chandelier/{Chandelier.Series.cs => Chandelier.StaticSeries.cs} (53%) delete mode 100644 src/a-d/Chop/Chop.Api.cs delete mode 100644 src/a-d/Chop/Chop.Series.cs create mode 100644 src/a-d/Chop/Chop.StaticSeries.cs delete mode 100644 src/a-d/Cmf/Cmf.Api.cs delete mode 100644 src/a-d/Cmf/Cmf.Series.cs create mode 100644 src/a-d/Cmf/Cmf.StaticSeries.cs delete mode 100644 src/a-d/Cmo/Cmo.Api.cs delete mode 100644 src/a-d/Cmo/Cmo.Series.cs create mode 100644 src/a-d/Cmo/Cmo.StaticSeries.cs delete mode 100644 src/a-d/ConnorsRsi/ConnorsRsi.Api.cs delete mode 100644 src/a-d/ConnorsRsi/ConnorsRsi.Series.cs create mode 100644 src/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.cs delete mode 100644 src/a-d/Correlation/Correlation.Api.cs delete mode 100644 src/a-d/Correlation/Correlation.Series.cs create mode 100644 src/a-d/Correlation/Correlation.StaticSeries.cs delete mode 100644 src/a-d/Dema/Dema.Api.cs delete mode 100644 src/a-d/Dema/Dema.Series.cs create mode 100644 src/a-d/Dema/Dema.StaticSeries.cs delete mode 100644 src/a-d/Doji/Doji.Api.cs delete mode 100644 src/a-d/Doji/Doji.Series.cs create mode 100644 src/a-d/Doji/Doji.StaticSeries.cs create mode 100644 src/a-d/Doji/Doji.Utilities.cs delete mode 100644 src/a-d/Doji/info.xml delete mode 100644 src/a-d/Donchian/Donchian.Api.cs delete mode 100644 src/a-d/Donchian/Donchian.Series.cs create mode 100644 src/a-d/Donchian/Donchian.StaticSeries.cs delete mode 100644 src/a-d/Dpo/Dpo.Api.cs delete mode 100644 src/a-d/Dpo/Dpo.Series.cs create mode 100644 src/a-d/Dpo/Dpo.StaticSeries.cs create mode 100644 src/a-d/Dpo/Dpo.Utilities.cs delete mode 100644 src/a-d/Dynamic/Dynamic.Api.cs delete mode 100644 src/a-d/Dynamic/Dynamic.Series.cs create mode 100644 src/a-d/Dynamic/Dynamic.StaticSeries.cs create mode 100644 src/a-d/Dynamic/Dynamic.Utilities.cs delete mode 100644 src/e-k/ElderRay/ElderRay.Api.cs delete mode 100644 src/e-k/ElderRay/ElderRay.Series.cs create mode 100644 src/e-k/ElderRay/ElderRay.StaticSeries.cs delete mode 100644 src/e-k/Ema/Ema.Api.cs create mode 100644 src/e-k/Ema/Ema.Increments.cs delete mode 100644 src/e-k/Ema/Ema.Observer.cs delete mode 100644 src/e-k/Ema/Ema.Series.cs create mode 100644 src/e-k/Ema/Ema.StaticSeries.cs create mode 100644 src/e-k/Ema/Ema.StreamHub.cs delete mode 100644 src/e-k/Epma/Epma.Api.cs delete mode 100644 src/e-k/Epma/Epma.Series.cs create mode 100644 src/e-k/Epma/Epma.StaticSeries.cs delete mode 100644 src/e-k/Fcb/Fcb.Api.cs delete mode 100644 src/e-k/Fcb/Fcb.Series.cs create mode 100644 src/e-k/Fcb/Fcb.StaticSeries.cs delete mode 100644 src/e-k/FisherTransform/FisherTransform.Api.cs delete mode 100644 src/e-k/FisherTransform/FisherTransform.Series.cs create mode 100644 src/e-k/FisherTransform/FisherTransform.StaticSeries.cs create mode 100644 src/e-k/FisherTransform/FisherTransform.Utilities.cs delete mode 100644 src/e-k/ForceIndex/ForceIndex.Api.cs delete mode 100644 src/e-k/ForceIndex/ForceIndex.Series.cs create mode 100644 src/e-k/ForceIndex/ForceIndex.StaticSeries.cs delete mode 100644 src/e-k/Fractal/Fractal.Api.cs delete mode 100644 src/e-k/Fractal/Fractal.Series.cs create mode 100644 src/e-k/Fractal/Fractal.StaticSeries.cs delete mode 100644 src/e-k/Gator/Gator.Api.cs delete mode 100644 src/e-k/Gator/Gator.Series.cs create mode 100644 src/e-k/Gator/Gator.StaticSeries.cs delete mode 100644 src/e-k/Gator/info.xml delete mode 100644 src/e-k/HeikinAshi/HeikinAshi.Api.cs rename src/e-k/HeikinAshi/{HeikinAshi.Series.cs => HeikinAshi.StaticSeries.cs} (62%) delete mode 100644 src/e-k/HeikinAshi/HeikinAshi.Utilities.cs delete mode 100644 src/e-k/Hma/Hma.Api.cs delete mode 100644 src/e-k/Hma/Hma.Series.cs create mode 100644 src/e-k/Hma/Hma.StaticSeries.cs delete mode 100644 src/e-k/HtTrendline/HtTrendline.Api.cs rename src/e-k/HtTrendline/{HtTrendline.Series.cs => HtTrendline.StaticSeries.cs} (63%) delete mode 100644 src/e-k/HtTrendline/info.xml delete mode 100644 src/e-k/Hurst/Hurst.Api.cs rename src/e-k/Hurst/{Hurst.Series.cs => Hurst.StaticSeries.cs} (71%) delete mode 100644 src/e-k/Ichimoku/Ichimoku.Api.cs delete mode 100644 src/e-k/Ichimoku/Ichimoku.Series.cs create mode 100644 src/e-k/Ichimoku/Ichimoku.StaticSeries.cs delete mode 100644 src/e-k/Kama/Kama.Api.cs delete mode 100644 src/e-k/Kama/Kama.Series.cs create mode 100644 src/e-k/Kama/Kama.StaticSeries.cs delete mode 100644 src/e-k/Keltner/Keltner.Api.cs delete mode 100644 src/e-k/Keltner/Keltner.Series.cs create mode 100644 src/e-k/Keltner/Keltner.StaticSeries.cs delete mode 100644 src/e-k/Kvo/Kvo.Api.cs rename src/e-k/Kvo/{Kvo.Series.cs => Kvo.StaticSeries.cs} (60%) delete mode 100644 src/m-r/MaEnvelopes/MaEnvelopes.Api.cs delete mode 100644 src/m-r/MaEnvelopes/MaEnvelopes.Series.cs create mode 100644 src/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.cs delete mode 100644 src/m-r/Macd/Macd.Series.cs create mode 100644 src/m-r/Macd/Macd.StaticSeries.cs delete mode 100644 src/m-r/Macd/MacdApi.cs delete mode 100644 src/m-r/Mama/Mama.Api.cs rename src/m-r/Mama/{Mama.Series.cs => Mama.StaticSeries.cs} (51%) delete mode 100644 src/m-r/Marubozu/Marubozu.Api.cs delete mode 100644 src/m-r/Marubozu/Marubozu.Series.cs create mode 100644 src/m-r/Marubozu/Marubozu.StaticSeries.cs create mode 100644 src/m-r/Marubozu/Marubozu.Utilities.cs delete mode 100644 src/m-r/Mfi/Mfi.Api.cs delete mode 100644 src/m-r/Mfi/Mfi.Series.cs create mode 100644 src/m-r/Mfi/Mfi.StaticSeries.cs delete mode 100644 src/m-r/Obv/Obv.Api.cs delete mode 100644 src/m-r/Obv/Obv.Series.cs create mode 100644 src/m-r/Obv/Obv.StaticSeries.cs delete mode 100644 src/m-r/ParabolicSar/ParabolicSar.Api.cs rename src/m-r/ParabolicSar/{ParabolicSar.Series.cs => ParabolicSar.StaticSeries.cs} (55%) delete mode 100644 src/m-r/PivotPoints/PivotPoints.Api.cs rename src/m-r/PivotPoints/{PivotPoints.Series.cs => PivotPoints.StaticSeries.cs} (52%) delete mode 100644 src/m-r/Pivots/Pivots.Api.cs delete mode 100644 src/m-r/Pivots/Pivots.Series.cs create mode 100644 src/m-r/Pivots/Pivots.StaticSeries.cs delete mode 100644 src/m-r/Pmo/Pmo.Api.cs delete mode 100644 src/m-r/Pmo/Pmo.Series.cs create mode 100644 src/m-r/Pmo/Pmo.StaticSeries.cs delete mode 100644 src/m-r/Prs/Prs.Api.cs delete mode 100644 src/m-r/Prs/Prs.Series.cs create mode 100644 src/m-r/Prs/Prs.StaticSeries.cs create mode 100644 src/m-r/Prs/Prs.Utilities.cs delete mode 100644 src/m-r/Pvo/Pvo.Api.cs delete mode 100644 src/m-r/Pvo/Pvo.Series.cs create mode 100644 src/m-r/Pvo/Pvo.StaticSeries.cs delete mode 100644 src/m-r/Renko/Renko.Api.cs delete mode 100644 src/m-r/Renko/Renko.Series.cs create mode 100644 src/m-r/Renko/Renko.StaticSeries.cs create mode 100644 src/m-r/Renko/Renko.StreamHub.cs create mode 100644 src/m-r/Renko/Renko.Utilities.cs delete mode 100644 src/m-r/Renko/RenkoAtr.Api.cs delete mode 100644 src/m-r/Renko/RenkoAtr.Series.cs create mode 100644 src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs delete mode 100644 src/m-r/Roc/Roc.Api.cs delete mode 100644 src/m-r/Roc/Roc.Series.cs create mode 100644 src/m-r/Roc/Roc.StaticSeries.cs delete mode 100644 src/m-r/RocWb/Roc.Models.cs delete mode 100644 src/m-r/RocWb/RocWb.Api.cs create mode 100644 src/m-r/RocWb/RocWb.Models.cs delete mode 100644 src/m-r/RocWb/RocWb.Series.cs create mode 100644 src/m-r/RocWb/RocWb.StaticSeries.cs delete mode 100644 src/m-r/RollingPivots/RollingPivots.Api.cs delete mode 100644 src/m-r/RollingPivots/RollingPivots.Series.cs create mode 100644 src/m-r/RollingPivots/RollingPivots.StaticSeries.cs delete mode 100644 src/m-r/Rsi/Rsi.Api.cs delete mode 100644 src/m-r/Rsi/Rsi.Series.cs create mode 100644 src/m-r/Rsi/Rsi.StaticSeries.cs delete mode 100644 src/s-z/Slope/Slope.Api.cs delete mode 100644 src/s-z/Slope/Slope.Series.cs create mode 100644 src/s-z/Slope/Slope.StaticSeries.cs delete mode 100644 src/s-z/Sma/Sma.Analysis.cs delete mode 100644 src/s-z/Sma/Sma.Api.cs delete mode 100644 src/s-z/Sma/Sma.Observer.cs delete mode 100644 src/s-z/Sma/Sma.Series.cs create mode 100644 src/s-z/Sma/Sma.StaticSeries.cs create mode 100644 src/s-z/Sma/Sma.StreamHub.cs create mode 100644 src/s-z/SmaAnalysis/SmaAnalysis.Models.cs create mode 100644 src/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.cs delete mode 100644 src/s-z/Smi/Smi.Api.cs delete mode 100644 src/s-z/Smi/Smi.Series.cs create mode 100644 src/s-z/Smi/Smi.StaticSeries.cs delete mode 100644 src/s-z/Smma/Smma.Api.cs delete mode 100644 src/s-z/Smma/Smma.Series.cs create mode 100644 src/s-z/Smma/Smma.StaticSeries.cs delete mode 100644 src/s-z/StarcBands/StarcBands.Api.cs delete mode 100644 src/s-z/StarcBands/StarcBands.Series.cs create mode 100644 src/s-z/StarcBands/StarcBands.StaticSeries.cs delete mode 100644 src/s-z/Stc/Stc.Api.cs delete mode 100644 src/s-z/Stc/Stc.Series.cs create mode 100644 src/s-z/Stc/Stc.StaticSeries.cs delete mode 100644 src/s-z/StdDev/StdDev.Api.cs delete mode 100644 src/s-z/StdDev/StdDev.Series.cs create mode 100644 src/s-z/StdDev/StdDev.StaticSeries.cs delete mode 100644 src/s-z/StdDevChannels/StdDevChannels.Api.cs delete mode 100644 src/s-z/StdDevChannels/StdDevChannels.Series.cs create mode 100644 src/s-z/StdDevChannels/StdDevChannels.StaticSeries.cs delete mode 100644 src/s-z/Stoch/Stoch.Api.cs delete mode 100644 src/s-z/Stoch/Stoch.Series.cs create mode 100644 src/s-z/Stoch/Stoch.StaticSeries.cs delete mode 100644 src/s-z/StochRsi/StochRsi.Api.cs delete mode 100644 src/s-z/StochRsi/StochRsi.Series.cs create mode 100644 src/s-z/StochRsi/StochRsi.StaticSeries.cs delete mode 100644 src/s-z/SuperTrend/SuperTrend.Api.cs delete mode 100644 src/s-z/SuperTrend/SuperTrend.Series.cs create mode 100644 src/s-z/SuperTrend/SuperTrend.StaticSeries.cs delete mode 100644 src/s-z/T3/T3.Api.cs delete mode 100644 src/s-z/T3/T3.Series.cs create mode 100644 src/s-z/T3/T3.StaticSeries.cs create mode 100644 src/s-z/T3/T3.Utilities.cs delete mode 100644 src/s-z/Tema/Tema.Api.cs delete mode 100644 src/s-z/Tema/Tema.Series.cs create mode 100644 src/s-z/Tema/Tema.StaticSeries.cs delete mode 100644 src/s-z/Tr/Tr.Api.cs delete mode 100644 src/s-z/Tr/Tr.Series.cs create mode 100644 src/s-z/Tr/Tr.StaticSeries.cs create mode 100644 src/s-z/Tr/Tr.StreamHub.cs create mode 100644 src/s-z/Tr/Tr.Utilities.cs delete mode 100644 src/s-z/Trix/Trix.Api.cs delete mode 100644 src/s-z/Trix/Trix.Series.cs create mode 100644 src/s-z/Trix/Trix.StaticSeries.cs delete mode 100644 src/s-z/Tsi/Tsi.Api.cs delete mode 100644 src/s-z/Tsi/Tsi.Series.cs create mode 100644 src/s-z/Tsi/Tsi.StaticSeries.cs delete mode 100644 src/s-z/UlcerIndex/UlcerIndex.Api.cs delete mode 100644 src/s-z/UlcerIndex/UlcerIndex.Series.cs create mode 100644 src/s-z/UlcerIndex/UlcerIndex.StaticSeries.cs delete mode 100644 src/s-z/Ultimate/Ultimate.Api.cs delete mode 100644 src/s-z/Ultimate/Ultimate.Series.cs create mode 100644 src/s-z/Ultimate/Ultimate.StaticSeries.cs delete mode 100644 src/s-z/VolatilityStop/VolatilityStop.Api.cs delete mode 100644 src/s-z/VolatilityStop/VolatilityStop.Series.cs create mode 100644 src/s-z/VolatilityStop/VolatilityStop.StaticSeries.cs delete mode 100644 src/s-z/Vortex/Vortex.Api.cs rename src/s-z/Vortex/{Vortex.Series.cs => Vortex.StaticSeries.cs} (66%) delete mode 100644 src/s-z/Vwap/Vwap.Api.cs delete mode 100644 src/s-z/Vwap/Vwap.Series.cs create mode 100644 src/s-z/Vwap/Vwap.StaticSeries.cs delete mode 100644 src/s-z/Vwma/Vwma.Api.cs delete mode 100644 src/s-z/Vwma/Vwma.Series.cs create mode 100644 src/s-z/Vwma/Vwma.StaticSeries.cs delete mode 100644 src/s-z/WilliamsR/WilliamsR.Api.cs delete mode 100644 src/s-z/WilliamsR/WilliamsR.Series.cs create mode 100644 src/s-z/WilliamsR/WilliamsR.StaticSeries.cs delete mode 100644 src/s-z/Wma/Wma.Api.cs delete mode 100644 src/s-z/Wma/Wma.Series.cs create mode 100644 src/s-z/Wma/Wma.StaticSeries.cs delete mode 100644 src/s-z/ZigZag/ZigZag.Api.cs rename src/s-z/ZigZag/{ZigZag.Series.cs => ZigZag.StaticSeries.cs} (62%) create mode 100644 tests/application/GlobalUsings.cs create mode 100644 tests/application/Program.cs create mode 100644 tests/application/README.md create mode 100644 tests/application/Test.Application.csproj create mode 100644 tests/application/_testdata/TestData.Getter.cs create mode 100644 tests/application/_testdata/TestData.Imports.cs rename tests/{indicators/_common => application/_testdata}/data/compare.csv (100%) rename tests/{indicators/_common => application/_testdata}/data/default.csv (100%) rename tests/{indicators/_common => application/_testdata}/data/intraday.csv (100%) rename tests/{indicators/_common => application/_testdata}/data/longest.csv (100%) rename tests/{indicators/_common => application/_testdata}/data/longish.csv (100%) create mode 100644 tests/indicators/TestBase.cs delete mode 100644 tests/indicators/_Initialize.cs create mode 100644 tests/indicators/_common/Generics/BinarySettingsTests.cs delete mode 100644 tests/indicators/_common/Generics/Pruning.Tests.cs create mode 100644 tests/indicators/_common/Generics/RemoveWarmup.Tests.cs delete mode 100644 tests/indicators/_common/Generics/Sort.Tests.cs create mode 100644 tests/indicators/_common/Generics/Sorting.Tests.cs delete mode 100644 tests/indicators/_common/Helper.Getter.cs delete mode 100644 tests/indicators/_common/Helper.Importer.cs delete mode 100644 tests/indicators/_common/Helper.Random.cs rename tests/indicators/_common/Math/{Numerix.Tests.cs => Numerical.Tests.cs} (67%) create mode 100644 tests/indicators/_common/Observables/StreamHub.CacheMgmt.Tests.cs create mode 100644 tests/indicators/_common/Observables/StreamHub.Observable.Tests.cs create mode 100644 tests/indicators/_common/Observables/StreamHub.Observer.Tests.cs create mode 100644 tests/indicators/_common/Observables/StreamHub.Stackoverflow.Tests.cs create mode 100644 tests/indicators/_common/Observables/StreamHub.Utilities.StaticSeries.Tests.cs create mode 100644 tests/indicators/_common/Quotes/Quote.Equality.Tests.cs create mode 100644 tests/indicators/_common/Quotes/Quote.StreamHub.Tests.cs delete mode 100644 tests/indicators/_common/Quotes/Test.Quote.Provider.cs delete mode 100644 tests/indicators/_common/Quotes/Test.Use.Observer.cs delete mode 100644 tests/indicators/_common/Results/Result.Syncing.Tests.cs delete mode 100644 tests/indicators/_common/Results/Result.Utilities.Tests.cs create mode 100644 tests/indicators/_common/Reusable/Reusable.Utilities.Tests.cs create mode 100644 tests/indicators/_common/Use (QuotePart)/QuotePart.StaticSeries.Tests.cs create mode 100644 tests/indicators/_common/Use (QuotePart)/QuotePart.StreamHub.Tests.cs create mode 100644 tests/indicators/_common/Use (QuotePart)/QuotePart.Utilities.Tests.cs rename tests/indicators/{a-d/BasicQuote/BasicQuote.Calc.xlsx => _common/Use (QuotePart)/Use.Calc.xlsx} (100%) rename tests/indicators/{_common => _testdata}/Data.Aggregate.xlsx (100%) rename tests/indicators/{_common => _testdata}/Data.Candles.xlsx (100%) rename tests/indicators/{_common => _testdata}/Data.Quotes.xlsx (100%) create mode 100644 tests/indicators/_testdata/TestData.Getter.cs create mode 100644 tests/indicators/_testdata/TestData.Imports.cs create mode 100644 tests/indicators/_testdata/TestData.Random.cs create mode 100644 tests/indicators/_testdata/TestData.Tests.cs rename tests/indicators/{_common => _testdata}/data/bad.csv (100%) rename tests/indicators/{_common => _testdata}/data/bitcoin.csv (100%) rename tests/indicators/{_common => _testdata}/data/btcusd15x69k.csv (100%) rename tests/{performance/helpers => indicators/_testdata}/data/compare.csv (100%) rename tests/{performance/helpers => indicators/_testdata}/data/default.csv (100%) rename tests/{performance/helpers => indicators/_testdata}/data/intraday.csv (100%) rename tests/{performance/helpers => indicators/_testdata}/data/longest.csv (99%) rename tests/{performance/helpers => indicators/_testdata}/data/longish.csv (100%) rename tests/indicators/{_common => _testdata}/data/max.csv (100%) rename tests/indicators/{_common => _testdata}/data/mismatch.csv (99%) rename tests/indicators/{_common => _testdata}/data/msft.csv (100%) rename tests/indicators/{_common => _testdata}/data/penny.csv (100%) rename tests/indicators/{_common => _testdata}/data/spx.csv (100%) rename tests/indicators/{_common => _testdata}/data/toobig.csv (100%) rename tests/indicators/{_common => _testdata}/data/zeros.csv (100%) create mode 100644 tests/indicators/a-d/Adl/Adl.StaticSeries.Tests.cs create mode 100644 tests/indicators/a-d/Adl/Adl.StreamHub.Tests.cs delete mode 100644 tests/indicators/a-d/Adl/Adl.Tests.cs rename tests/indicators/a-d/Adx/{Adx.Tests.cs => Adx.StaticSeries.Tests.cs} (62%) rename tests/indicators/a-d/Alligator/{Alligator.Tests.cs => Alligator.StaticSeries.Tests.cs} (55%) create mode 100644 tests/indicators/a-d/Alligator/Alligator.StreamHub.Tests.cs rename tests/indicators/a-d/Alma/{Alma.Tests.cs => Alma.StaticSeries.Tests.cs} (53%) rename tests/indicators/a-d/Aroon/{Aroon.Tests.cs => Aroon.StaticSeries.Tests.cs} (68%) rename tests/indicators/a-d/Atr/{Atr.Tests.cs => Atr.StaticSeries.Tests.cs} (59%) create mode 100644 tests/indicators/a-d/Atr/Atr.StreamHub.Tests.cs rename tests/indicators/a-d/AtrStop/{AtrStop.Tests.cs => AtrStop.StaticSeries.Tests.cs} (63%) create mode 100644 tests/indicators/a-d/AtrStop/AtrStop.StreamHub.Tests.cs rename tests/indicators/a-d/Awesome/{Awesome.Tests.cs => Awesome.StaticSeries.Tests.cs} (57%) delete mode 100644 tests/indicators/a-d/BasicQuote/BasicQuote.Tests.cs rename tests/indicators/a-d/Beta/{Beta.Tests.cs => Beta.StaticSeries.Tests.cs} (50%) rename tests/indicators/a-d/BollingerBands/{BollingerBands.Tests.cs => BollingerBands.StaticSeries.Tests.cs} (62%) rename tests/indicators/a-d/Bop/{Bop.Tests.cs => Bop.StaticSeries.Tests.cs} (57%) rename tests/indicators/a-d/Cci/{Cci.Tests.cs => Cci.StaticSeries.Tests.cs} (52%) rename tests/indicators/a-d/ChaikinOsc/{ChaikinOsc.Tests.cs => ChaikinOsc.StaticSeries.Tests.cs} (60%) rename tests/indicators/a-d/Chandelier/{Chandelier.Tests.cs => Chandelier.StaticSeries.Tests.cs} (54%) rename tests/indicators/a-d/Chop/{Chop.Tests.cs => Chop.StaticSeries.Tests.cs} (59%) rename tests/indicators/a-d/Cmf/{Cmf.Tests.cs => Cmf.StaticSeries.Tests.cs} (65%) rename tests/indicators/a-d/Cmo/{Cmo.Tests.cs => Cmo.StaticSeries.Tests.cs} (51%) rename tests/indicators/a-d/ConnorsRsi/{ConnorsRsi.Tests.cs => ConnorsRsi.StaticSeries.Tests.cs} (60%) rename tests/indicators/a-d/Correlation/{Correlation.Tests.cs => Correlation.StaticSeries.Tests.cs} (54%) rename tests/indicators/a-d/Dema/{Dema.Tests.cs => Dema.StaticSeries.Tests.cs} (53%) rename tests/indicators/a-d/Doji/{Doji.Tests.cs => Doji.StaticSeries.Tests.cs} (67%) rename tests/indicators/a-d/Donchian/{Donchian.Tests.cs => Donchian.StaticSeries.Tests.cs} (74%) rename tests/indicators/a-d/Dpo/{Dpo.Tests.cs => Dpo.StaticSeries.Tests.cs} (51%) rename tests/indicators/a-d/Dynamic/{Dynamic.Tests.cs => Dynamic.StaticSeries.Tests.cs} (54%) rename tests/indicators/e-k/ElderRay/{ElderRay.Tests.cs => ElderRay.StaticSeries.Tests.cs} (70%) create mode 100644 tests/indicators/e-k/Ema/Ema.Increments.Tests.cs delete mode 100644 tests/indicators/e-k/Ema/Ema.Obs.Tests.cs rename tests/indicators/e-k/Ema/{Ema.Static.Tests.cs => Ema.StaticSeries.Tests.cs} (55%) create mode 100644 tests/indicators/e-k/Ema/Ema.StreamHub.Tests.cs rename tests/indicators/e-k/Epma/{Epma.Tests.cs => Epma.StaticSeries.Tests.cs} (54%) rename tests/indicators/e-k/Fcb/{Fcb.Tests.cs => Fcb.StaticSeries.Tests.cs} (68%) rename tests/indicators/e-k/FisherTransform/{FisherTransform.Tests.cs => FisherTransform.StaticSeries.Tests.cs} (63%) rename tests/indicators/e-k/ForceIndex/{ForceIndex.Tests.cs => ForceIndex.StaticSeries.Tests.cs} (58%) rename tests/indicators/e-k/Fractal/{Fractal.Tests.cs => Fractal.StaticSeries.Tests.cs} (75%) rename tests/indicators/e-k/Gator/{Gator.Tests.cs => Gator.StaticSeries.Tests.cs} (81%) create mode 100644 tests/indicators/e-k/HeikinAshi/HeikinAshi.StaticSeries.Tests.cs delete mode 100644 tests/indicators/e-k/HeikinAshi/HeikinAshi.Tests.cs rename tests/indicators/e-k/Hma/{Hma.Tests.cs => Hma.StaticSeries.Tests.cs} (50%) rename tests/indicators/e-k/HtTrendline/{HtTrendline.Tests.cs => HtTrendline.StaticSeries.Tests.cs} (65%) rename tests/indicators/e-k/Hurst/{Hurst.Tests.cs => Hurst.StaticSeries.Tests.cs} (50%) rename tests/indicators/e-k/Ichimoku/{Ichimoku.Tests.cs => Ichimoku.StaticSeries.Tests.cs} (69%) rename tests/indicators/e-k/Kama/{Kama.Tests.cs => Kama.StaticSeries.Tests.cs} (53%) rename tests/indicators/e-k/Keltner/{Keltner.Tests.cs => Keltner.StaticSeries.Tests.cs} (69%) rename tests/indicators/e-k/Kvo/{Kvo.Tests.cs => Kvo.StaticSeries.Tests.cs} (71%) rename tests/indicators/m-r/MaEnvelopes/{MaEnvelopes.Tests.cs => MaEnvelopes.StaticSeries.Tests.cs} (74%) rename tests/indicators/m-r/Macd/{Macd.Tests.cs => Macd.StaticSeries.Tests.cs} (65%) rename tests/indicators/m-r/Mama/{Mama.Tests.cs => Mama.StaticSeries.Tests.cs} (63%) rename tests/indicators/m-r/Marubozu/{Marubozu.Tests.cs => Marubozu.StaticSeries.Tests.cs} (66%) rename tests/indicators/m-r/Mfi/{Mfi.Tests.cs => Mfi.StaticSeries.Tests.cs} (60%) create mode 100644 tests/indicators/m-r/Obv/Obv.StaticSeries.Tests.cs delete mode 100644 tests/indicators/m-r/Obv/Obv.Tests.cs rename tests/indicators/m-r/ParabolicSar/{ParabolicSar.Tests.cs => ParabolicSar.StaticSeries.Tests.cs} (71%) rename tests/indicators/m-r/PivotPoints/{PivotPoints.Tests.cs => PivotPoints.StaticSeries.Tests.cs} (89%) rename tests/indicators/m-r/Pivots/{Pivots.Tests.cs => Pivots.StaticSeries.Tests.cs} (74%) rename tests/indicators/m-r/Pmo/{Pmo.Tests.cs => Pmo.StaticSeries.Tests.cs} (56%) create mode 100644 tests/indicators/m-r/Prs/Prs.StaticSeries.Tests.cs delete mode 100644 tests/indicators/m-r/Prs/Prs.Tests.cs rename tests/indicators/m-r/Pvo/{Pvo.Tests.cs => Pvo.StaticSeries.Tests.cs} (71%) rename tests/indicators/m-r/Renko/{Renko.Tests.cs => Renko.StaticSeries.Tests.cs} (76%) create mode 100644 tests/indicators/m-r/Renko/Renko.StreamHub.Tests.cs create mode 100644 tests/indicators/m-r/Roc/Roc.StaticSeries.Tests.cs delete mode 100644 tests/indicators/m-r/Roc/Roc.Tests.cs rename tests/indicators/m-r/RocWb/{RocWb.Tests.cs => RocWb.StaticSeries.Tests.cs} (70%) rename tests/indicators/m-r/RollingPivots/{RollingPivots.Tests.cs => RollingPivots.StaticSeries.Tests.cs} (89%) rename tests/indicators/m-r/Rsi/{Rsi.Tests.cs => Rsi.StaticSeries.Tests.cs} (54%) rename tests/indicators/s-z/Slope/{Slope.Tests.cs => Slope.StaticSeries.Tests.cs} (63%) delete mode 100644 tests/indicators/s-z/Sma/Sma.Analysis.Tests.cs delete mode 100644 tests/indicators/s-z/Sma/Sma.Obs.Tests.cs rename tests/indicators/s-z/Sma/{Sma.Static.Tests.cs => Sma.StaticSeries.Tests.cs} (59%) create mode 100644 tests/indicators/s-z/Sma/Sma.StreamHub.Tests.cs create mode 100644 tests/indicators/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.Tests.cs rename tests/indicators/s-z/Smi/{Smi.Tests.cs => Smi.StaticSeries.Tests.cs} (71%) rename tests/indicators/s-z/Smma/{Smma.Tests.cs => Smma.StaticSeries.Tests.cs} (54%) rename tests/indicators/s-z/StarcBands/{StarcBands.Tests.cs => StarcBands.StaticSeries.Tests.cs} (70%) rename tests/indicators/s-z/Stc/{Stc.Tests.cs => Stc.StaticSeries.Tests.cs} (55%) create mode 100644 tests/indicators/s-z/StdDev/StdDev.StaticSeries.Tests.cs delete mode 100644 tests/indicators/s-z/StdDev/StdDev.Tests.cs rename tests/indicators/s-z/StdDevChannels/{StdDevChannels.Tests.cs => StdDevChannels.StaticSeries.Tests.cs} (73%) rename tests/indicators/s-z/Stoch/{Stoch.Tests.cs => Stoch.StaticSeries.Tests.cs} (75%) rename tests/indicators/s-z/StochRsi/{StochRsi.Tests.cs => StochRsi.StaticSeries.Tests.cs} (65%) rename tests/indicators/s-z/SuperTrend/{SuperTrend.Tests.cs => SuperTrend.StaticSeries.Tests.cs} (68%) rename tests/indicators/s-z/T3/{T3.Tests.cs => T3.StaticSeries.Tests.cs} (57%) rename tests/indicators/s-z/Tema/{Tema.Tests.cs => Tema.StaticSeries.Tests.cs} (51%) rename tests/indicators/s-z/Tr/{Tr.Tests.cs => Tr.StaticSeries.Tests.cs} (61%) create mode 100644 tests/indicators/s-z/Tr/Tr.StreamHub.Tests.cs rename tests/indicators/s-z/Trix/{Trix.Tests.cs => Trix.StaticSeries.Tests.cs} (51%) rename tests/indicators/s-z/Tsi/{Tsi.Tests.cs => Tsi.StaticSeries.Tests.cs} (55%) create mode 100644 tests/indicators/s-z/UlcerIndex/UlcerIndex.StaticSeries.Tests.cs delete mode 100644 tests/indicators/s-z/UlcerIndex/UlcerIndex.Tests.cs rename tests/indicators/s-z/Ultimate/{Ultimate.Tests.cs => Ultimate.StaticSeries.Tests.cs} (57%) rename tests/indicators/s-z/VolatilityStop/{VolatilityStop.Tests.cs => VolatilityStop.StaticSeries.Tests.cs} (70%) rename tests/indicators/s-z/Vortex/{Vortex.Tests.cs => Vortex.StaticSeries.Tests.cs} (63%) rename tests/indicators/s-z/Vwap/{Vwap.Tests.cs => Vwap.StaticSeries.Tests.cs} (61%) rename tests/indicators/s-z/Vwma/{Vwma.Tests.cs => Vwma.StaticSeries.Tests.cs} (58%) rename tests/indicators/s-z/WilliamsR/{WilliamsR.Tests.cs => WilliamsR.StaticSeries.Tests.cs} (64%) rename tests/indicators/s-z/Wma/{Wma.Tests.cs => Wma.StaticSeries.Tests.cs} (51%) rename tests/indicators/s-z/ZigZag/{ZigZag.Tests.cs => ZigZag.StaticSeries.Tests.cs} (77%) delete mode 100644 tests/observe/Program.cs delete mode 100644 tests/observe/README.md create mode 100644 tests/other/Custom.Indicator.Tests.cs create mode 100644 tests/other/Custom.Quotes.Tests.cs create mode 100644 tests/other/Custom.Results.Tests.cs delete mode 100644 tests/other/CustomIndicator.Tests.cs create mode 100644 tests/other/PublicApi.Interface.Tests.cs delete mode 100644 tests/other/PublicApi.Tests.cs create mode 100644 tests/other/Sut.CustomItems.cs delete mode 100644 tests/performance/Perf.Helpers.cs create mode 100644 tests/performance/Perf.Increments.cs delete mode 100644 tests/performance/Perf.Indicators.Static.cs delete mode 100644 tests/performance/Perf.Indicators.Stream.cs create mode 100644 tests/performance/Perf.StaticSeries.cs create mode 100644 tests/performance/Perf.StreamHub.Externals.cs create mode 100644 tests/performance/Perf.StreamHub.cs rename tests/performance/{Perf.Internals.cs => Perf.Utility.Maths.cs} (55%) create mode 100644 tests/performance/Perf.Utility.cs delete mode 100644 tests/performance/helpers/Helper.Getter.cs delete mode 100644 tests/performance/helpers/Helper.Importer.cs create mode 100644 tests/simulate/Program.cs rename tests/{observe/Observe.Streaming.csproj => simulate/Test.Simulation.csproj} (75%) create mode 100644 tests/simulate/Utilities.cs diff --git a/.editorconfig b/.editorconfig index 8be070d53..6eba79cb1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,18 +15,9 @@ max_line_length = 150 trim_trailing_whitespace = true insert_final_newline = true -[*.{csproj,props,targets}] -indent_style = space -indent_size = 2 - -[*.yml] -indent_style = space -indent_size = 2 - [*.{cs,vb}] tab_width = 4 indent_size = 4 -end_of_line = lf #### Naming styles #### @@ -70,11 +61,6 @@ dotnet_naming_style.pascal_case.required_suffix = dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.capitalization = pascal_case -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case - dotnet_code_quality_unused_parameters = all:suggestion dotnet_sort_system_directives_first = true @@ -87,7 +73,7 @@ dotnet_style_null_propagation = true:suggestion dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion dotnet_style_prefer_auto_properties = true:silent dotnet_style_object_initializer = true:suggestion -dotnet_style_prefer_collection_expression = true:suggestion +dotnet_style_prefer_collection_expression = false:suggestion [*.cs] # ref: https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 921dda23d..3b7f7d542 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -23,7 +23,7 @@ body: render: csharp placeholder: | // example (put your own code here) - IEnumerable results = quotes.GetEma(14); + IReadOnlyList results = quotes.GetEma(14); validations: required: false - type: textarea diff --git a/.github/workflows/test-examples.yml b/.github/workflows/test-examples.yml index 7b63a2fd1..4b81db50c 100644 --- a/.github/workflows/test-examples.yml +++ b/.github/workflows/test-examples.yml @@ -2,12 +2,12 @@ name: Examples on: push: - branches: ["main"] + branches: ["main","v3"] paths: - docs/examples/** pull_request: - branches: ["main"] + branches: ["main","v3"] paths: - docs/examples/** - ".github/workflows/test-examples.yml" diff --git a/.github/workflows/test-indicators.yml b/.github/workflows/test-indicators.yml index 72d8ef4d6..a5a0932a2 100644 --- a/.github/workflows/test-indicators.yml +++ b/.github/workflows/test-indicators.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: os: [windows-latest, ubuntu-latest, macos-latest] - dotnet-version: ["2.0.x", "6.x", "8.x"] + dotnet-version: ["6.x", "8.x"] env: diff --git a/.github/workflows/test-performance.yml b/.github/workflows/test-performance.yml index 2335e0d63..6682f6b06 100644 --- a/.github/workflows/test-performance.yml +++ b/.github/workflows/test-performance.yml @@ -42,12 +42,29 @@ jobs: --configuration Release --property:ContinuousIntegrationBuild=true - - name: Benchmark indicators + - name: Test performance working-directory: tests/performance run: dotnet run -c Release + - name: Save test results + uses: actions/upload-artifact@v4 + with: + name: test-summaries + path: tests/performance/BenchmarkDotNet.Artifacts/results + - name: Publish summary working-directory: tests/performance/BenchmarkDotNet.Artifacts/results run: | echo "### Package version ${{ steps.gitversion.outputs.fullSemVer }}" >> $GITHUB_STEP_SUMMARY - cat Tests.Performance.IndicatorsStatic-report-github.md >> $GITHUB_STEP_SUMMARY + + echo "## Series indicators" >> $GITHUB_STEP_SUMMARY + cat Performance.SeriesIndicators-report-github.md >> $GITHUB_STEP_SUMMARY + + echo "## Stream indicators (with Quote caching)" >> $GITHUB_STEP_SUMMARY + cat Performance.StreamIndicators-report-github.md >> $GITHUB_STEP_SUMMARY + + echo "## Incremental indicators (with buffer)" >> $GITHUB_STEP_SUMMARY + cat Performance.Incrementals-report-github.md >> $GITHUB_STEP_SUMMARY + + echo "## Utilities" >> $GITHUB_STEP_SUMMARY + cat Performance.Utility-report-github.md >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/test-website-a11y.yml b/.github/workflows/test-website-a11y.yml index d7164fb04..a92abccc3 100644 --- a/.github/workflows/test-website-a11y.yml +++ b/.github/workflows/test-website-a11y.yml @@ -1,8 +1,10 @@ name: Website on: + push: + branches: ["v3"] pull_request: - branches: [main] + branches: ["main"] paths: - 'docs/**' - ".github/workflows/test-website-a11y.yml" diff --git a/.github/workflows/test-website-links.yml b/.github/workflows/test-website-links.yml index 4d9136ec2..19cddb540 100644 --- a/.github/workflows/test-website-links.yml +++ b/.github/workflows/test-website-links.yml @@ -1,8 +1,10 @@ name: Website on: + push: + branches: ["v3"] pull_request: - branches: [main] + branches: ["main"] paths: - 'docs/**' - ".github/workflows/test-website-links.yml" diff --git a/.gitignore b/.gitignore index 7a97cd5dc..a517802a1 100644 --- a/.gitignore +++ b/.gitignore @@ -111,12 +111,13 @@ $tf/ # Guidance Automation Toolkit *.gpState -# ReSharper is a .NET coding add-in +# ReSharper IDE extension _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user +*.DotSettings -# JustCode is a .NET coding add-in +# JustCode IDE extension .JustCode # TeamCity is a build add-in @@ -218,7 +219,7 @@ ClientBin/ *.publishsettings orleans.codegen.cs -# Including strong name files can present a security risk +# Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk @@ -314,7 +315,7 @@ __pycache__/ # OpenCover UI analysis results OpenCover/ -# Azure Stream Analytics local run output +# Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log @@ -323,11 +324,11 @@ ASALocalRun/ # NVidia Nsight GPU debugger configuration file *.nvuser -# MFractors (Xamarin productivity tool) working folder +# MFractors (Xamarin productivity tool) working folder .mfractor/ # Jekyll site _site/ # zip artifacts -.DS_Store \ No newline at end of file +.DS_Store diff --git a/Stock.Indicators.sln b/Stock.Indicators.sln index 42164b6bc..8d241723d 100644 --- a/Stock.Indicators.sln +++ b/Stock.Indicators.sln @@ -19,13 +19,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Other", "tests\other\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Performance", "tests\performance\Tests.Performance.csproj", "{3BD4837B-D197-41FD-A286-A3256D0770E1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Observe.Streaming", "tests\observe\Observe.Streaming.csproj", "{14DEC3AF-9AF2-4A66-8BEE-C342C6CC4307}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.Application", "tests\application\Test.Application.csproj", "{14DEC3AF-9AF2-4A66-8BEE-C342C6CC4307}" ProjectSection(ProjectDependencies) = postProject {11CD6C7E-871F-4903-AEAD-58E034C6521D} = {11CD6C7E-871F-4903-AEAD-58E034C6521D} {8D0F1781-EDA3-4C51-B05D-D33FF1156E49} = {8D0F1781-EDA3-4C51-B05D-D33FF1156E49} EndProjectSection EndProject - +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.Simulation", "tests\simulate\Test.Simulation.csproj", "{9C9045D2-9928-41F8-97FC-ECCBDD3B9868}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -52,6 +53,10 @@ Global {14DEC3AF-9AF2-4A66-8BEE-C342C6CC4307}.Debug|Any CPU.Build.0 = Debug|Any CPU {14DEC3AF-9AF2-4A66-8BEE-C342C6CC4307}.Release|Any CPU.ActiveCfg = Release|Any CPU {14DEC3AF-9AF2-4A66-8BEE-C342C6CC4307}.Release|Any CPU.Build.0 = Release|Any CPU + {9C9045D2-9928-41F8-97FC-ECCBDD3B9868}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C9045D2-9928-41F8-97FC-ECCBDD3B9868}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C9045D2-9928-41F8-97FC-ECCBDD3B9868}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C9045D2-9928-41F8-97FC-ECCBDD3B9868}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/docs/_data/aliases.yml b/docs/_data/aliases.yml index 9950409f0..5d1bf4b87 100644 --- a/docs/_data/aliases.yml +++ b/docs/_data/aliases.yml @@ -59,23 +59,23 @@ type: price-characteristic - title: HL2 - permalink: /indicators/BasicQuote/ + permalink: /indicators/quotepart/ type: price-transform - title: HLC3 - permalink: /indicators/BasicQuote/ + permalink: /indicators/quotepart/ type: price-transform - title: OC2 - permalink: /indicators/BasicQuote/ + permalink: /indicators/quotepart/ type: price-transform - title: OHL3 - permalink: /indicators/BasicQuote/ + permalink: /indicators/quotepart/ type: price-transform - title: OHLC4 - permalink: /indicators/BasicQuote/ + permalink: /indicators/quotepart/ type: price-transform - title: Price Channels diff --git a/docs/_includes/candle-properties.md b/docs/_includes/candle-properties.md index b601afa7f..0d514b063 100644 --- a/docs/_includes/candle-properties.md +++ b/docs/_includes/candle-properties.md @@ -2,7 +2,7 @@ | name | type | notes | -- |-- |-- -| `Date` | DateTime | Date +| `Timestamp` | DateTime | Close date | `Open` | decimal | Open price | `High` | decimal | High price | `Low` | decimal | Low price diff --git a/docs/_includes/candle-result.md b/docs/_includes/candle-result.md index 920aec1c3..efb0c4081 100644 --- a/docs/_includes/candle-result.md +++ b/docs/_includes/candle-result.md @@ -1,6 +1,6 @@ ### CandleResult -**`Date`** _`DateTime`_ - Date +**`Timestamp`** _`DateTime`_ - Time of last trade in candle period **`Price`** _`decimal`_ - Price of the most relevant OHLC candle element when a signal is present diff --git a/docs/_indicators/Adl.md b/docs/_indicators/Adl.md index 48acef9f0..eeb10b3e5 100644 --- a/docs/_indicators/Adl.md +++ b/docs/_indicators/Adl.md @@ -16,19 +16,11 @@ Created by Marc Chaikin, the [Accumulation/Distribution Line/Index](https://en.w ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetAdl(); - -// usage with optional overlay SMA of ADL (shown above) -IEnumerable results = - quotes.GetAdl(smaPeriods); ``` -## Parameters - -**`smaPeriods`** _`int`_ - Optional. Number of periods (`N`) in the moving average of ADL. Must be greater than 0, if specified. - -### Historical quotes requirements +## Historical quotes requirements You must have at least two historical quotes to cover the warmup periods; however, since this is a trendline, more is recommended. @@ -37,7 +29,7 @@ You must have at least two historical quotes to cover the warmup periods; howeve ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -46,7 +38,7 @@ IEnumerable ### AdlResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`MoneyFlowMultiplier`** _`double`_ - Money Flow Multiplier @@ -54,8 +46,6 @@ IEnumerable **`Adl`** _`double`_ - Accumulation Distribution Line (ADL) -**`AdlSma`** _`double`_ - Moving average (SMA) of ADL based on `smaPeriods` periods, if specified - > 🚩 **Warning**: absolute values in ADL and MFV are somewhat meaningless. Use with caution. ### Utilities diff --git a/docs/_indicators/Adx.md b/docs/_indicators/Adx.md index afcfe2ece..feb4e5612 100644 --- a/docs/_indicators/Adx.md +++ b/docs/_indicators/Adx.md @@ -16,7 +16,7 @@ Created by J. Welles Wilder, the Directional Movement Index (DMI) and [Average D ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetAdx(lookbackPeriods); ``` @@ -33,7 +33,7 @@ You must have at least `2×N+100` periods of `quotes` to cover the [warmup and c ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -45,7 +45,7 @@ IEnumerable ### AdxResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Pdi`** _`double`_ - Plus Directional Index (+DI) diff --git a/docs/_indicators/Alligator.md b/docs/_indicators/Alligator.md index c3433c43f..7c367f0d1 100644 --- a/docs/_indicators/Alligator.md +++ b/docs/_indicators/Alligator.md @@ -16,7 +16,7 @@ Created by Bill Williams, Alligator is a depiction of three smoothed moving aver ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetAlligator(jawPeriods,jawOffset,teethPeriods,teethOffset,lipsPeriods,lipsOffset); ``` @@ -43,7 +43,7 @@ You must have at least `JP+JO+100` periods of `quotes` to cover the [warmup and ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -55,7 +55,7 @@ IEnumerable ### AlligatorResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Jaw`** _`double`_ - Alligator's Jaw diff --git a/docs/_indicators/Alma.md b/docs/_indicators/Alma.md index d378287cf..c0624deea 100644 --- a/docs/_indicators/Alma.md +++ b/docs/_indicators/Alma.md @@ -16,7 +16,7 @@ Created by Arnaud Legoux and Dimitrios Kouzis-Loukas, [ALMA]({{site.github.repos ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetAlma(lookbackPeriods, offset, sigma); ``` @@ -37,7 +37,7 @@ You must have at least `N` periods of `quotes` to cover the warmup periods. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -47,7 +47,7 @@ IEnumerable ### AlmaResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Alma`** _`double`_ - Arnaud Legoux Moving Average diff --git a/docs/_indicators/Aroon.md b/docs/_indicators/Aroon.md index 14d8b3fb7..6d4b43417 100644 --- a/docs/_indicators/Aroon.md +++ b/docs/_indicators/Aroon.md @@ -16,7 +16,7 @@ Created by Tushar Chande, [Aroon](https://school.stockcharts.com/doku.php?id=tec ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetAroon(lookbackPeriods); ``` @@ -33,7 +33,7 @@ You must have at least `N` periods of `quotes` to cover the warmup periods. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -43,7 +43,7 @@ IEnumerable ### AroonResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`AroonUp`** _`double`_ - Based on last High price diff --git a/docs/_indicators/Atr.md b/docs/_indicators/Atr.md index 7b851348e..c8d2bbfc5 100644 --- a/docs/_indicators/Atr.md +++ b/docs/_indicators/Atr.md @@ -16,15 +16,15 @@ Created by J. Welles Wilder, True Range and [Average True Range](https://en.wiki ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetAtr(lookbackPeriods); // ATR with custom moving average -IEnumerable results = +IReadOnlyList results = quotes.GetTr().GetSmma(lookbackPeriods); // raw True Range (TR) only -IEnumerable results = +IReadOnlyList results = quote.GetTr(); ``` @@ -41,7 +41,7 @@ You must have at least `N+100` periods of `quotes` to cover the [warmup and conv ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -53,7 +53,7 @@ IEnumerable ### AtrResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Tr`** _`double`_ - True Range for current period diff --git a/docs/_indicators/AtrStop.md b/docs/_indicators/AtrStop.md index 742eba953..c1db90504 100644 --- a/docs/_indicators/AtrStop.md +++ b/docs/_indicators/AtrStop.md @@ -16,7 +16,7 @@ Created by Welles Wilder, the ATR Trailing Stop indicator attempts to determine ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetAtrStop(lookbackPeriods, multiplier, endType); ``` @@ -43,7 +43,7 @@ You must have at least `N+100` periods of `quotes` to cover the [warmup and conv ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -55,13 +55,15 @@ IEnumerable ### AtrStopResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` -**`AtrStop`** _`decimal`_ - ATR Trailing Stop line contains both Upper and Lower segments +**`AtrStop`** _`double`_ - ATR Trailing Stop line contains both Upper and Lower segments -**`BuyStop`** _`decimal`_ - Upper band only (green) +**`BuyStop`** _`double`_ - Upper band only (green) -**`SellStop`** _`decimal`_ - Lower band only (red) +**`SellStop`** _`double`_ - Lower band only (red) + +**`Atr`** _`double`_ - Average True Range `BuyStop` and `SellStop` values are provided to differentiate buy vs sell stop lines and to clearly demark trend reversal. `AtrStop` is the contiguous combination of both upper and lower line data. diff --git a/docs/_indicators/Awesome.md b/docs/_indicators/Awesome.md index cbade963f..3963f5d26 100644 --- a/docs/_indicators/Awesome.md +++ b/docs/_indicators/Awesome.md @@ -16,7 +16,7 @@ Created by Bill Williams, the Awesome Oscillator (aka Super AO) is a measure of ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetAwesome(fastPeriods, slowPeriods); ``` @@ -35,7 +35,7 @@ You must have at least `S` periods of `quotes` to cover the warmup periods. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -45,7 +45,7 @@ IEnumerable ### AwesomeResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Oscillator`** _`double`_ - Awesome Oscillator diff --git a/docs/_indicators/Beta.md b/docs/_indicators/Beta.md index bf47c3e59..b41d716cb 100644 --- a/docs/_indicators/Beta.md +++ b/docs/_indicators/Beta.md @@ -16,13 +16,13 @@ layout: indicator ```csharp // C# usage syntax -IEnumerable results = quotesEval +IReadOnlyList results = quotesEval .GetBeta(quotesMarket, lookbackPeriods, type); ``` ## Parameters -**`quotesMarket`** _`IEnumerable`_ - [Historical quotes]({{site.baseurl}}/guide/#historical-quotes) market data should be at any consistent frequency (day, hour, minute, etc). This `market` quotes will be used to establish the baseline. +**`quotesMarket`** _`IReadOnlyList`_ - [Historical quotes]({{site.baseurl}}/guide/#historical-quotes) market data should be at any consistent frequency (day, hour, minute, etc). This `market` quotes will be used to establish the baseline. **`lookbackPeriods`** _`int`_ - Number of periods (`N`) in the lookback window. Must be greater than 0 to calculate; however we suggest a larger period for statistically appropriate sample size and especially when using Beta +/-. @@ -51,7 +51,7 @@ You must have at least `N` periods of `quotesEval` to cover the warmup periods. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -61,7 +61,7 @@ IEnumerable ### BetaResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Beta`** _`double`_ - Beta coefficient based diff --git a/docs/_indicators/BollingerBands.md b/docs/_indicators/BollingerBands.md index 94837ad65..4052c8793 100644 --- a/docs/_indicators/BollingerBands.md +++ b/docs/_indicators/BollingerBands.md @@ -16,7 +16,7 @@ Created by John Bollinger, [Bollinger Bands](https://en.wikipedia.org/wiki/Bolli ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetBollingerBands(lookbackPeriods, standardDeviations); ``` @@ -35,7 +35,7 @@ You must have at least `N` periods of `quotes` to cover the warmup periods. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -45,7 +45,7 @@ IEnumerable ### BollingerBandsResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Sma`** _`double`_ - Simple moving average (SMA) of price (center line) diff --git a/docs/_indicators/Bop.md b/docs/_indicators/Bop.md index 6fa6f02c9..39b193bc9 100644 --- a/docs/_indicators/Bop.md +++ b/docs/_indicators/Bop.md @@ -16,7 +16,7 @@ Created by Igor Levshin, the [Balance of Power](https://school.stockcharts.com/d ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetBop(smoothPeriods); ``` @@ -33,7 +33,7 @@ You must have at least `N` periods of `quotes` to cover the warmup periods. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -43,7 +43,7 @@ IEnumerable ### BopResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Bop`** _`double`_ - Balance of Power diff --git a/docs/_indicators/Cci.md b/docs/_indicators/Cci.md index 9b9c27cb6..d8eb341aa 100644 --- a/docs/_indicators/Cci.md +++ b/docs/_indicators/Cci.md @@ -16,7 +16,7 @@ Created by Donald Lambert, the [Commodity Channel Index](https://en.wikipedia.or ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetCci(lookbackPeriods); ``` @@ -33,7 +33,7 @@ You must have at least `N+1` periods of `quotes` to cover the warmup periods. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -43,7 +43,7 @@ IEnumerable ### CciResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Cci`** _`double`_ - Commodity Channel Index diff --git a/docs/_indicators/ChaikinOsc.md b/docs/_indicators/ChaikinOsc.md index 7bc660857..398aea5dd 100644 --- a/docs/_indicators/ChaikinOsc.md +++ b/docs/_indicators/ChaikinOsc.md @@ -16,7 +16,7 @@ Created by Marc Chaikin, the [Chaikin Oscillator](https://en.wikipedia.org/wiki/ ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetChaikinOsc(fastPeriods, slowPeriods); ``` @@ -35,7 +35,7 @@ You must have at least `2×S` or `S+100` periods of `quotes`, whichever is more, ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -47,7 +47,7 @@ IEnumerable ### ChaikinOscResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`MoneyFlowMultiplier`** _`double`_ - Money Flow Multiplier diff --git a/docs/_indicators/Chandelier.md b/docs/_indicators/Chandelier.md index bbcaec658..e257ed37f 100644 --- a/docs/_indicators/Chandelier.md +++ b/docs/_indicators/Chandelier.md @@ -17,7 +17,7 @@ Created by Charles Le Beau, the [Chandelier Exit](https://school.stockcharts.com ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetChandelier(lookbackPeriods, multiplier, type); ``` @@ -44,7 +44,7 @@ You must have at least `N+1` periods of `quotes` to cover the warmup periods. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -54,7 +54,7 @@ IEnumerable ### ChandelierResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`ChandelierExit`** _`double`_ - Exit line diff --git a/docs/_indicators/Chop.md b/docs/_indicators/Chop.md index 491f1bfb0..8604480d1 100644 --- a/docs/_indicators/Chop.md +++ b/docs/_indicators/Chop.md @@ -15,7 +15,7 @@ Created by E.W. Dreiss, the Choppiness Index measures the trendiness or choppine ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetChop(lookbackPeriods); ``` @@ -32,7 +32,7 @@ You must have at least `N+1` periods of `quotes` to cover the warmup periods. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -42,7 +42,7 @@ IEnumerable ### ChopResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Chop`** _`double`_ - Choppiness Index diff --git a/docs/_indicators/Cmf.md b/docs/_indicators/Cmf.md index 8a254c0b4..b1bd1abbf 100644 --- a/docs/_indicators/Cmf.md +++ b/docs/_indicators/Cmf.md @@ -16,7 +16,7 @@ Created by Marc Chaikin, [Chaikin Money Flow](https://en.wikipedia.org/wiki/Chai ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetCmf(lookbackPeriods); ``` @@ -33,7 +33,7 @@ You must have at least `N+1` periods of `quotes` to cover the warmup periods. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -43,7 +43,7 @@ IEnumerable ### CmfResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`MoneyFlowMultiplier`** _`double`_ - Money Flow Multiplier diff --git a/docs/_indicators/Cmo.md b/docs/_indicators/Cmo.md index a57e08b48..40eb2a605 100644 --- a/docs/_indicators/Cmo.md +++ b/docs/_indicators/Cmo.md @@ -16,7 +16,7 @@ Created by Tushar Chande, the [Chande Momentum Oscillator](https://www.investope ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetCmo(lookbackPeriods); ``` @@ -33,7 +33,7 @@ You must have at least `N+1` periods of `quotes` to cover the warmup periods. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -43,7 +43,7 @@ IEnumerable ### CmoResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Cmo`** _`double`_ - Chande Momentum Oscillator diff --git a/docs/_indicators/ConnorsRsi.md b/docs/_indicators/ConnorsRsi.md index df6a07227..746d5fdb5 100644 --- a/docs/_indicators/ConnorsRsi.md +++ b/docs/_indicators/ConnorsRsi.md @@ -16,7 +16,7 @@ Created by Laurence Connors, the [ConnorsRSI](https://alvarezquanttrading.com/wp ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetConnorsRsi(rsiPeriods, streakPeriods, rankPeriods); ``` @@ -37,7 +37,7 @@ IEnumerable results = ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -49,7 +49,7 @@ IEnumerable ### ConnorsRsiResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Rsi`** _`double`_ - `RSI(R)` of the price. diff --git a/docs/_indicators/Correlation.md b/docs/_indicators/Correlation.md index 7f0a307a3..740374667 100644 --- a/docs/_indicators/Correlation.md +++ b/docs/_indicators/Correlation.md @@ -16,13 +16,13 @@ Created by Karl Pearson, the [Correlation Coefficient](https://en.wikipedia.org/ ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotesA.GetCorrelation(quotesB, lookbackPeriods); ``` ## Parameters -**`quotesB`** _`IEnumerable`_ - [Historical quotes]({{site.baseurl}}/guide/#historical-quotes) (B) must have at least the same matching date elements of `quotesA`. +**`quotesB`** _`IReadOnlyList`_ - [Historical quotes]({{site.baseurl}}/guide/#historical-quotes) (B) must have at least the same matching date elements of `quotesA`. **`lookbackPeriods`** _`int`_ - Number of periods (`N`) in the lookback period. Must be greater than 0 to calculate; however we suggest a larger period for statistically appropriate sample size. @@ -30,12 +30,12 @@ IEnumerable results = You must have at least `N` periods for both versions of `quotes` to cover the warmup periods. Mismatch histories will produce a `InvalidQuotesException`. Historical price quotes should have a consistent frequency (day, hour, minute, etc). -`quotesA` is an `IEnumerable` collection of historical price quotes. It should have a consistent frequency (day, hour, minute, etc). See [the Guide]({{site.baseurl}}/guide/#historical-quotes) for more information. +`quotesA` is an `IReadOnlyList` collection of historical price quotes. It should have a consistent frequency (day, hour, minute, etc). See [the Guide]({{site.baseurl}}/guide/#historical-quotes) for more information. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -45,7 +45,7 @@ IEnumerable ### CorrResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`VarianceA`** _`double`_ - Variance of A diff --git a/docs/_indicators/Dema.md b/docs/_indicators/Dema.md index 6cd007d5e..7c40c943e 100644 --- a/docs/_indicators/Dema.md +++ b/docs/_indicators/Dema.md @@ -18,7 +18,7 @@ Created by Patrick G. Mulloy, the [Double exponential moving average](https://en ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetDema(lookbackPeriods); ``` @@ -35,7 +35,7 @@ You must have at least `3×N` or `2×N+100` periods of `quotes`, whichever is mo ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -47,7 +47,7 @@ IEnumerable ### DemaResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Dema`** _`double`_ - Double exponential moving average diff --git a/docs/_indicators/Doji.md b/docs/_indicators/Doji.md index 42c32d134..6b7af9d17 100644 --- a/docs/_indicators/Doji.md +++ b/docs/_indicators/Doji.md @@ -16,7 +16,7 @@ type: candlestick-pattern ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetDoji(maxPriceChangePercent); ``` @@ -33,7 +33,7 @@ You must have at least one historical quote; however, more is typically provided ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. diff --git a/docs/_indicators/Donchian.md b/docs/_indicators/Donchian.md index 8f1c1d9a8..64de9aa65 100644 --- a/docs/_indicators/Donchian.md +++ b/docs/_indicators/Donchian.md @@ -17,7 +17,7 @@ Created by Richard Donchian, [Donchian Channels](https://en.wikipedia.org/wiki/D ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetDonchian(lookbackPeriods); ``` @@ -34,7 +34,7 @@ You must have at least `N+1` periods of `quotes` to cover the warmup periods. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -44,7 +44,7 @@ IEnumerable ### DonchianResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`UpperBand`** _`decimal`_ - Upper line is the highest High over `N` periods diff --git a/docs/_indicators/Dpo.md b/docs/_indicators/Dpo.md index 17a346fdb..b843e96f3 100644 --- a/docs/_indicators/Dpo.md +++ b/docs/_indicators/Dpo.md @@ -16,7 +16,7 @@ layout: indicator ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetDpo(lookbackPeriods); ``` @@ -33,7 +33,7 @@ You must have at least `N` historical quotes to cover the warmup periods. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -43,7 +43,7 @@ IEnumerable ### DpoResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Sma`** _`double`_ - Simple moving average offset by `N/2+1` periods diff --git a/docs/_indicators/Dynamic.md b/docs/_indicators/Dynamic.md index 05a8e9fdc..c3c4b9dfd 100644 --- a/docs/_indicators/Dynamic.md +++ b/docs/_indicators/Dynamic.md @@ -16,7 +16,7 @@ Created by John R. McGinley, the [McGinley Dynamic](https://www.investopedia.com ```csharp // C# usage syntax (with Close price) -IEnumerable results = +IReadOnlyList results = quotes.GetDynamic(lookbackPeriods, kFactor); ``` @@ -41,7 +41,7 @@ You must have at least `2` periods of `quotes`, to cover the [warmup and converg ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -53,7 +53,7 @@ IEnumerable ### DynamicResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Dynamic`** _`double`_ - McGinley Dynamic diff --git a/docs/_indicators/ElderRay.md b/docs/_indicators/ElderRay.md index a3fadf142..f32c5b2f9 100644 --- a/docs/_indicators/ElderRay.md +++ b/docs/_indicators/ElderRay.md @@ -16,7 +16,7 @@ Created by Alexander Elder, the [Elder-ray Index](https://www.investopedia.com/t ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetElderRay(lookbackPeriods); ``` @@ -33,7 +33,7 @@ You must have at least `2×N` or `N+100` periods of `quotes`, whichever is more, ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -45,7 +45,7 @@ IEnumerable ### ElderRayResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Ema`** _`double`_ - Exponential moving average diff --git a/docs/_indicators/Ema.md b/docs/_indicators/Ema.md index 56c29d04a..3c041a4f9 100644 --- a/docs/_indicators/Ema.md +++ b/docs/_indicators/Ema.md @@ -16,7 +16,7 @@ layout: indicator ```csharp // C# usage syntax (with Close price) -IEnumerable results = +IReadOnlyList results = quotes.GetEma(lookbackPeriods); ``` @@ -33,7 +33,7 @@ You must have at least `2×N` or `N+100` periods of `quotes`, whichever is more, ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -45,7 +45,7 @@ IEnumerable ### EmaResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Ema`** _`double`_ - Exponential moving average diff --git a/docs/_indicators/Epma.md b/docs/_indicators/Epma.md index f37865059..26df4c8c9 100644 --- a/docs/_indicators/Epma.md +++ b/docs/_indicators/Epma.md @@ -16,7 +16,7 @@ Endpoint Moving Average (EPMA), also known as Least Squares Moving Average (LSMA ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetEpma(lookbackPeriods); ``` @@ -33,7 +33,7 @@ You must have at least `N` periods of `quotes` to cover the warmup periods. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -43,7 +43,7 @@ IEnumerable ### EpmaResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Epma`** _`double`_ - Endpoint moving average diff --git a/docs/_indicators/Fcb.md b/docs/_indicators/Fcb.md index f0ccdc7f6..71caada0b 100644 --- a/docs/_indicators/Fcb.md +++ b/docs/_indicators/Fcb.md @@ -16,7 +16,7 @@ Created by Edward William Dreiss, Fractal Chaos Bands outline high and low price ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetFcb(windowSpan); ``` @@ -35,7 +35,7 @@ You must have at least `2×S+1` periods of `quotes` to cover the warmup periods; ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -45,7 +45,7 @@ IEnumerable ### FcbResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`UpperBand`** _`decimal`_ - FCB upper band diff --git a/docs/_indicators/FisherTransform.md b/docs/_indicators/FisherTransform.md index 99a71ad22..294eb7c01 100644 --- a/docs/_indicators/FisherTransform.md +++ b/docs/_indicators/FisherTransform.md @@ -16,7 +16,7 @@ Created by John Ehlers, the [Fisher Transform](https://www.investopedia.com/term ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetFisherTransform(lookbackPeriods); ``` @@ -33,7 +33,7 @@ You must have at least `N` periods of `quotes` to cover the [warmup and converge ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -44,7 +44,7 @@ IEnumerable ### FisherTransformResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Fisher`** _`double`_ - Fisher Transform diff --git a/docs/_indicators/ForceIndex.md b/docs/_indicators/ForceIndex.md index 37535c53f..c7df2c284 100644 --- a/docs/_indicators/ForceIndex.md +++ b/docs/_indicators/ForceIndex.md @@ -16,7 +16,7 @@ Created by Alexander Elder, the [Force Index](https://en.wikipedia.org/wiki/Forc ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetForceIndex(lookbackPeriods); ``` @@ -33,7 +33,7 @@ You must have at least `N+100` for `2×N` periods of `quotes`, whichever is more ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -45,7 +45,7 @@ IEnumerable ### ForceIndexResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`ForceIndex`** _`double`_ - Force Index diff --git a/docs/_indicators/Fractal.md b/docs/_indicators/Fractal.md index 78087ebf0..b8f4a8cfe 100644 --- a/docs/_indicators/Fractal.md +++ b/docs/_indicators/Fractal.md @@ -16,7 +16,7 @@ Created by Larry Williams, [Fractal](https://www.investopedia.com/terms/f/fracta ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetFractal(windowSpan); ``` @@ -43,7 +43,7 @@ You must have at least `2×S+1` periods of `quotes` to cover the warmup periods; ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -55,7 +55,7 @@ IEnumerable ### FractalResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`FractalBear`** _`decimal`_ - Value indicates a **high** point; otherwise `null` is returned. diff --git a/docs/_indicators/Gator.md b/docs/_indicators/Gator.md index 13e72fe9c..0f19d6b48 100644 --- a/docs/_indicators/Gator.md +++ b/docs/_indicators/Gator.md @@ -16,11 +16,11 @@ Created by Bill Williams, the Gator Oscillator is an expanded oscillator view of ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetGator(); // with custom Alligator configuration -IEnumerable results = quotes +IReadOnlyList results = quotes .GetAlligator([see Alligator docs]) .GetGator(); ``` @@ -34,7 +34,7 @@ If using default settings, you must have at least 121 periods of `quotes` to cov ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -46,7 +46,7 @@ IEnumerable ### GatorResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Upper`** _`double`_ - Absolute value of Alligator `Jaw-Teeth` diff --git a/docs/_indicators/HeikinAshi.md b/docs/_indicators/HeikinAshi.md index 2d7a1e171..906056792 100644 --- a/docs/_indicators/HeikinAshi.md +++ b/docs/_indicators/HeikinAshi.md @@ -16,7 +16,7 @@ Created by Munehisa Homma, [Heikin-Ashi](https://en.wikipedia.org/wiki/Candlesti ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetHeikinAshi(); ``` @@ -29,7 +29,7 @@ You must have at least two periods of `quotes` to cover the warmup periods; howe ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -39,7 +39,7 @@ IEnumerable ### HeikinAshiResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Open`** _`decimal`_ - Modified open price @@ -58,7 +58,7 @@ IEnumerable - .ToQuotes() to convert to a `Quote` collection. Example: ```csharp - IEnumerable results = quotes + IReadOnlyList results = quotes .GetHeikinAshi() .ToQuotes(); ``` diff --git a/docs/_indicators/Hma.md b/docs/_indicators/Hma.md index bde67341b..6dc3e8cf2 100644 --- a/docs/_indicators/Hma.md +++ b/docs/_indicators/Hma.md @@ -16,7 +16,7 @@ Created by Alan Hull, the [Hull Moving Average](https://alanhull.com/hull-moving ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetHma(lookbackPeriods); ``` @@ -33,7 +33,7 @@ You must have at least `N+(integer of SQRT(N))-1` periods of `quotes` to cover t ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -43,7 +43,7 @@ IEnumerable ### HmaResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Hma`** _`double`_ - Hull moving average diff --git a/docs/_indicators/HtTrendline.md b/docs/_indicators/HtTrendline.md index e27e52baa..12e7586f5 100644 --- a/docs/_indicators/HtTrendline.md +++ b/docs/_indicators/HtTrendline.md @@ -16,7 +16,7 @@ Created by John Ehlers, the Hilbert Transform Instantaneous Trendline is a 5-per ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetHtTrendline(); ``` @@ -29,7 +29,7 @@ You must have at least `100` periods of `quotes` to cover the [warmup and conver ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -42,7 +42,7 @@ IEnumerable ### HtlResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`DcPeriods`** _`int`_ - Dominant cycle periods (smoothed) diff --git a/docs/_indicators/Hurst.md b/docs/_indicators/Hurst.md index 0595ecca5..6ca508984 100644 --- a/docs/_indicators/Hurst.md +++ b/docs/_indicators/Hurst.md @@ -16,7 +16,7 @@ The [Hurst Exponent](https://en.wikipedia.org/wiki/Hurst_exponent) (`H`) is part ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetHurst(lookbackPeriods); ``` @@ -33,7 +33,7 @@ You must have at least `N+1` periods of `quotes` to cover the warmup periods. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -43,7 +43,7 @@ IEnumerable ### HurstResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`HurstExponent`** _`double`_ - Hurst Exponent (`H`) diff --git a/docs/_indicators/Ichimoku.md b/docs/_indicators/Ichimoku.md index ea4ce2618..fa4beb8f9 100644 --- a/docs/_indicators/Ichimoku.md +++ b/docs/_indicators/Ichimoku.md @@ -16,15 +16,15 @@ Created by Goichi Hosoda (細田悟一, Hosoda Goichi), [Ichimoku Cloud](https:/ ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetIchimoku(tenkanPeriods, kijunPeriods, senkouBPeriods); // usage with custom offset -IEnumerable results = +IReadOnlyList results = quotes.GetIchimoku(tenkanPeriods, kijunPeriods, senkouBPeriods, offsetPeriods); // usage with different custom offsets -IEnumerable results = +IReadOnlyList results = quotes.GetIchimoku(tenkanPeriods, kijunPeriods, senkouBPeriods, senkouOffset, chikouOffset); ``` @@ -53,7 +53,7 @@ You must have at least the greater of `T`,`K`, `S`, and offset periods for `quot ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -63,7 +63,7 @@ IEnumerable ### IchimokuResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`TenkanSen`** _`decimal`_ - Conversion / signal line diff --git a/docs/_indicators/Kama.md b/docs/_indicators/Kama.md index 24b53c843..d07ddd23a 100644 --- a/docs/_indicators/Kama.md +++ b/docs/_indicators/Kama.md @@ -16,7 +16,7 @@ Created by Perry Kaufman, [KAMA](https://school.stockcharts.com/doku.php?id=tech ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetKama(erPeriods, fastPeriods, slowPeriods); ``` @@ -37,7 +37,7 @@ You must have at least `6×E` or `E+100` periods of `quotes`, whichever is more, ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -49,7 +49,7 @@ IEnumerable ### KamaResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`ER`** _`double`_ - Efficiency Ratio is the fractal efficiency of price changes diff --git a/docs/_indicators/Keltner.md b/docs/_indicators/Keltner.md index d56ed4ed5..35ee9836a 100644 --- a/docs/_indicators/Keltner.md +++ b/docs/_indicators/Keltner.md @@ -16,7 +16,7 @@ Created by Chester W. Keltner, [Keltner Channels](https://en.wikipedia.org/wiki/ ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetKeltner(emaPeriods, multiplier, atrPeriods); ``` @@ -37,7 +37,7 @@ You must have at least `2×N` or `N+100` periods of `quotes`, whichever is more, ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -49,7 +49,7 @@ IEnumerable ### KeltnerResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`UpperBand`** _`double`_ - Upper band of Keltner Channel diff --git a/docs/_indicators/Kvo.md b/docs/_indicators/Kvo.md index 257b741ae..03f3a52ba 100644 --- a/docs/_indicators/Kvo.md +++ b/docs/_indicators/Kvo.md @@ -16,7 +16,7 @@ Created by Stephen Klinger, the [Klinger Volume Oscillator](https://www.investop ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetKvo(shortPeriods, longPeriods, signalPeriods); ``` @@ -37,7 +37,7 @@ You must have at least `L+100` periods of `quotes` to cover the [warmup and conv ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -49,7 +49,7 @@ IEnumerable ### KvoResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Oscillator`** _`double`_ - Klinger Oscillator diff --git a/docs/_indicators/MaEnvelopes.md b/docs/_indicators/MaEnvelopes.md index 88ba0c220..d7b76248a 100644 --- a/docs/_indicators/MaEnvelopes.md +++ b/docs/_indicators/MaEnvelopes.md @@ -16,7 +16,7 @@ layout: indicator ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetMaEnvelopes(lookbackPeriods, percentOffset, movingAverageType); ``` @@ -61,7 +61,7 @@ These are the supported moving average types: ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -73,7 +73,7 @@ IEnumerable ### MaEnvelopeResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Centerline`** _`double`_ - Moving average diff --git a/docs/_indicators/Macd.md b/docs/_indicators/Macd.md index 72014585a..6428d0d97 100644 --- a/docs/_indicators/Macd.md +++ b/docs/_indicators/Macd.md @@ -16,7 +16,7 @@ Created by Gerald Appel, [MACD](https://en.wikipedia.org/wiki/MACD) is a simple ```csharp // C# usage syntax (with Close price) -IEnumerable results = +IReadOnlyList results = quotes.GetMacd(fastPeriods, slowPeriods, signalPeriods); ``` @@ -37,7 +37,7 @@ You must have at least `2×(S+P)` or `S+P+100` worth of `quotes`, whichever is m ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -49,7 +49,7 @@ IEnumerable ### MacdResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Macd`** _`double`_ - The MACD line is the difference between slow and fast moving averages (`MACD = FastEma - SlowEma`) diff --git a/docs/_indicators/Mama.md b/docs/_indicators/Mama.md index ce985f3e5..1e8e6697f 100644 --- a/docs/_indicators/Mama.md +++ b/docs/_indicators/Mama.md @@ -16,7 +16,7 @@ Created by John Ehlers, the [MAMA](https://mesasoftware.com/papers/MAMA.pdf) ind ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetMama(fastLimit, slowLimit); ``` @@ -35,7 +35,7 @@ You must have at least `50` periods of `quotes` to cover the [warmup and converg ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -47,7 +47,7 @@ IEnumerable ### MamaResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Mama`** _`decimal`_ - MESA adaptive moving average (MAMA) diff --git a/docs/_indicators/Marubozu.md b/docs/_indicators/Marubozu.md index 75abe1d69..d29b57109 100644 --- a/docs/_indicators/Marubozu.md +++ b/docs/_indicators/Marubozu.md @@ -16,7 +16,7 @@ type: candlestick-pattern ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetMarubozu(minBodyPercent); ``` @@ -33,7 +33,7 @@ You must have at least one historical quote; however, more is typically provided ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. diff --git a/docs/_indicators/Mfi.md b/docs/_indicators/Mfi.md index 16f58a2b0..01168929e 100644 --- a/docs/_indicators/Mfi.md +++ b/docs/_indicators/Mfi.md @@ -16,7 +16,7 @@ Created by Quong and Soudack, the [Money Flow Index](https://en.wikipedia.org/wi ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetMfi(lookbackPeriods); ``` @@ -33,7 +33,7 @@ You must have at least `N+1` historical quotes to cover the warmup periods. ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -43,7 +43,7 @@ IEnumerable ### MfiResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Mfi`** _`decimal`_ - Money Flow Index diff --git a/docs/_indicators/Obv.md b/docs/_indicators/Obv.md index 4da972f7d..f53f24094 100644 --- a/docs/_indicators/Obv.md +++ b/docs/_indicators/Obv.md @@ -16,19 +16,11 @@ Popularized by Joseph Granville, [On-balance Volume](https://en.wikipedia.org/wi ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetObv(); - -// usage with optional overlay SMA of OBV (shown above) -IEnumerable results = - quotes.GetObv(smaPeriods); ``` -## Parameters - -**`smaPeriods`** _`int`_ - Optional. Number of periods (`N`) in the moving average of OBV. Must be greater than 0, if specified. - -### Historical quotes requirements +## Historical quotes requirements You must have at least two historical quotes to cover the warmup periods; however, since this is a trendline, more is recommended. @@ -37,22 +29,20 @@ You must have at least two historical quotes to cover the warmup periods; howeve ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. - It always returns the same number of elements as there are in the historical quotes. - It does not return a single incremental indicator value. -- The first period OBV will have `0` value since there's not enough data to calculate. +- The first period OBV will have a `0` value since there's not enough data to calculate. ### ObvResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Obv`** _`double`_ - On-balance Volume -**`ObvSma`** _`double`_ - Moving average (SMA) of OBV based on `smaPeriods` periods, if specified - > 🚩 **Warning**: absolute values in OBV are somewhat meaningless. Use with caution. ### Utilities diff --git a/docs/_indicators/ParabolicSar.md b/docs/_indicators/ParabolicSar.md index ee3bebee7..b3ab3d4b4 100644 --- a/docs/_indicators/ParabolicSar.md +++ b/docs/_indicators/ParabolicSar.md @@ -16,11 +16,11 @@ Created by J. Welles Wilder, [Parabolic SAR](https://en.wikipedia.org/wiki/Parab ```csharp // C# usage syntax (standard) -IEnumerable results = +IReadOnlyList results = quotes.GetParabolicSar(accelerationStep, maxAccelerationFactor); // alternate usage with custom initial Factor -IEnumerable results = +IReadOnlyList results = quotes.GetParabolicSar(accelerationStep, maxAccelerationFactor, initialFactor); ``` @@ -41,7 +41,7 @@ You must have at least two historical quotes to cover the warmup periods; howeve ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -51,7 +51,7 @@ IEnumerable ### ParabolicSarResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`Sar`** _`double`_ - Stop and Reverse value diff --git a/docs/_indicators/PivotPoints.md b/docs/_indicators/PivotPoints.md index 727dc0d1e..62f717ba4 100644 --- a/docs/_indicators/PivotPoints.md +++ b/docs/_indicators/PivotPoints.md @@ -17,7 +17,7 @@ layout: indicator ```csharp // C# usage syntax -IEnumerable results = +IReadOnlyList results = quotes.GetPivotPoints(windowSize, pointType); ``` @@ -58,7 +58,7 @@ You must have at least `2` windows of `quotes` to cover the warmup periods. For ## Response ```csharp -IEnumerable +IReadOnlyList ``` - This method returns a time series of all available indicator values for the `quotes` provided. @@ -72,7 +72,7 @@ IEnumerable ### PivotPointsResult -**`Date`** _`DateTime`_ - Date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` **`R3`** _`decimal`_ - Resistance level 3 diff --git a/docs/_indicators/Pivots.md b/docs/_indicators/Pivots.md index 006f7d3cb..929a0098a 100644 --- a/docs/_indicators/Pivots.md +++ b/docs/_indicators/Pivots.md @@ -16,7 +16,7 @@ Pivots is an extended customizable version of = e.Signal - && trdQty != 1) - { - // emulates BTC + BTO - rlzGain += trdGain; - trdQty = 1; - trdPrice = q.Close; - cross = "LONG"; - } - - // check for SHORT event - // condition: Stoch RSI was >= 80 and Stoch RSI crosses under Signal - else if (l.StochRsi >= 80 - && l.StochRsi > l.Signal - && e.StochRsi <= e.Signal - && trdQty != -1) - { - // emulates STC + STO - rlzGain += trdGain; - trdQty = -1; - trdPrice = q.Close; - cross = "SHORT"; - } - - if (cross != string.Empty) - { - Console.WriteLine( - $"{q.Date,10:yyyy-MM-dd} " + - $"{q.Close,10:c2}" + - $"{e.StochRsi,7:N1}" + - $"{e.Signal,7:N1}" + - $"{cross,7}" + - $"{rlzGain + trdGain,13:c2}"); - } - } - } +/************************************************************ - private static Collection GetQuotesFromFeed() - { - /************************************************************ + This is a basic 20-year backtest-style analysis of + Stochastic RSI. It will buy-to-open (BTO) one share + when the Stoch RSI (%K) is below 20 and crosses over the + Signal (%D). The reverse Sell-to-Close (STC) and + Sell-To-Open (STO) occurs when the Stoch RSI is above 80 and + crosses below the Signal. - We're mocking a data provider here by simply importing a - JSON file, a similar format of many public APIs. + As a result, there will always be one open LONG or SHORT + position that is opened and closed at signal crossover + points in the overbought and oversold regions of the indicator. - This approach will vary widely depending on where you are - getting your quote history. + ************************************************************/ - See https://github.com/DaveSkender/Stock.Indicators/discussions/579 - for free or inexpensive market data providers and examples. +// Fetch historical quotes from data provider. +// We're mocking with a simple JSON file import +string json = File.ReadAllText("quotes.data.json"); - The return type of IEnumerable can also be List - or ICollection or other IEnumerable compatible types. +Collection quotes = JsonSerializer + .Deserialize>(json) + .ToSortedCollection(); - ************************************************************/ +// Calculate Stochastic RSI +IReadOnlyList resultsList = + quotes + .GetStochRsi(14, 14, 3) + .ToList(); + +// initialize +decimal trdPrice = 0; +decimal trdQty = 0; +decimal rlzGain = 0; + +Console.WriteLine(" Date Close StRSI Signal Cross Net Gains"); +Console.WriteLine("-------------------------------------------------------"); + +// roll through source values +for (int i = 1; i < quotes.Count; i++) +{ + Quote q = quotes[i]; - string json = File.ReadAllText("quotes.data.json"); + StochRsiResult e = resultsList[i]; // evaluation period + StochRsiResult l = resultsList[i - 1]; // last (prior) period + string cross = string.Empty; - Collection quotes = JsonSerializer - .Deserialize>(json) - .ToSortedCollection(); + // unrealized gain on open trade + decimal trdGain = trdQty * (q.Close - trdPrice); - return quotes; + // check for LONG event + // condition: Stoch RSI was <= 20 and Stoch RSI crosses over Signal + if (l.StochRsi <= 20 + && l.StochRsi < l.Signal + && e.StochRsi >= e.Signal + && trdQty != 1) + { + // emulates BTC + BTO + rlzGain += trdGain; + trdQty = 1; + trdPrice = q.Close; + cross = "LONG"; + } + + // check for SHORT event + // condition: Stoch RSI was >= 80 and Stoch RSI crosses under Signal + else if (l.StochRsi >= 80 + && l.StochRsi > l.Signal + && e.StochRsi <= e.Signal + && trdQty != -1) + { + // emulates STC + STO + rlzGain += trdGain; + trdQty = -1; + trdPrice = q.Close; + cross = "SHORT"; + } + + if (cross != string.Empty) + { + Console.WriteLine( + $"{q.Date,10:yyyy-MM-dd} " + + $"{q.Close,10:c2}" + + $"{e.StochRsi,7:N1}" + + $"{e.Signal,7:N1}" + + $"{cross,7}" + + $"{rlzGain + trdGain,13:c2}"); } } diff --git a/docs/examples/ConsoleApp/Program.cs b/docs/examples/ConsoleApp/Program.cs index e8099bc19..b8f5185f8 100644 --- a/docs/examples/ConsoleApp/Program.cs +++ b/docs/examples/ConsoleApp/Program.cs @@ -3,94 +3,63 @@ using System.Text.Json; using Skender.Stock.Indicators; -namespace ConsoleApp; +/* BASIC CONSOLE APP | SIMPLE MOVING AVERAGE */ -public static class Program -{ - public static void Main() - { - // fetch historical quotes from data provider - IEnumerable quotes = GetQuotesFromFeed(); - - // calculate 10-period SMA - IEnumerable results = quotes.GetSma(10); - - // show results - Console.WriteLine("SMA Results ---------------------------"); - - foreach (SmaResult r in results.TakeLast(10)) - { - // only showing last 10 records for brevity - Console.WriteLine($"SMA on {r.Date:u} was ${r.Sma:N3}"); - } - - // optionally, you can lookup individual values by date - DateTime lookupDate = DateTime - .Parse("2021-08-12T17:08:17.9746795+02:00", CultureInfo.InvariantCulture); - - double? specificSma = results.Find(lookupDate).Sma; - - Console.WriteLine(); - Console.WriteLine("SMA on Specific Date ------------------"); - Console.WriteLine($"SMA on {lookupDate:u} was ${specificSma:N3}"); +// Fetch historical quotes from data provider. +// We're mocking with a simple JSON file import +string json = File.ReadAllText("quotes.data.json"); - // analyze results (compare to quote values) - Console.WriteLine(); - Console.WriteLine("SMA Analysis --------------------------"); +Collection quotes = JsonSerializer + .Deserialize>(json) + .ToSortedCollection(); - /************************************************************ - Results are usually returned with the same number of - elements as the provided quotes; see individual indicator - docs for more information. +// Calculate 10-period SMA +IEnumerable results = quotes.GetSma(10); - As such, converting to List means they can be indexed - with the same ordinal position. - ************************************************************/ +// show results +Console.WriteLine("SMA Results ---------------------------"); - List quotesList = quotes - .ToList(); - - List resultsList = results - .ToList(); - - for (int i = quotesList.Count - 25; i < quotesList.Count; i++) - { - // only showing ~25 records for brevity - - Quote q = quotesList[i]; - SmaResult r = resultsList[i]; +foreach (SmaResult r in results.TakeLast(10)) +{ + // only showing last 10 records for brevity + Console.WriteLine($"SMA on {r.Date:u} was ${r.Sma:N3}"); +} - bool isBullish = (double)q.Close > r.Sma; +// optionally, you can lookup individual values by date +DateTime lookupDate = DateTime + .Parse("2021-08-12T17:08:17.9746795+02:00", CultureInfo.InvariantCulture); - Console.WriteLine($"SMA on {r.Date:u} was ${r.Sma:N3}" - + $" and Bullishness is {isBullish}"); - } - } +double? specificSma = results.Find(lookupDate).Sma; - private static Collection GetQuotesFromFeed() - { - /************************************************************ +Console.WriteLine(); +Console.WriteLine("SMA on Specific Date ------------------"); +Console.WriteLine($"SMA on {lookupDate:u} was ${specificSma:N3}"); - We're mocking a data provider here by simply importing a - JSON file, a similar format of many public APIs. +// analyze results (compare to quote values) +Console.WriteLine(); +Console.WriteLine("SMA Analysis --------------------------"); - This approach will vary widely depending on where you are - getting your quote history. +/************************************************************ + Results are usually returned with the same number of + elements as the provided quotes; see individual indicator + docs for more information. - See https://github.com/DaveSkender/Stock.Indicators/discussions/579 - for free or inexpensive market data providers and examples. + As such, converting to List means they can be indexed + with the same ordinal position. + ************************************************************/ - The return type of IEnumerable can also be List - or ICollection or other IEnumerable compatible types. +IReadOnlyList resultsList + = results.ToList(); - ************************************************************/ +for (int i = quotes.Count - 25; i < quotes.Count; i++) +{ + // only showing ~25 records for brevity - string json = File.ReadAllText("quotes.data.json"); + Quote q = quotes[i]; + SmaResult r = resultsList[i]; - Collection quotes = JsonSerializer - .Deserialize>(json) - .ToSortedCollection(); + bool isBullish = (double)q.Close > r.Sma; - return quotes; - } + Console.WriteLine($"SMA on {r.Date:u} was ${r.Sma:N3}" + + $" and Bullishness is {isBullish}"); } diff --git a/docs/examples/CustomIndicators/AtrWma.cs b/docs/examples/CustomIndicators/AtrWma.cs index 2f0412f23..9414070b2 100644 --- a/docs/examples/CustomIndicators/AtrWma.cs +++ b/docs/examples/CustomIndicators/AtrWma.cs @@ -1,4 +1,3 @@ -using System.Collections.ObjectModel; using Skender.Stock.Indicators; namespace Custom.Stock.Indicators; @@ -18,24 +17,24 @@ public sealed class AtrWmaResult : ResultBase, IReusableResult public static class CustomIndicators { // Custom ATR WMA calculation - public static IEnumerable GetAtrWma( - this IEnumerable quotes, + public static IReadOnlyList GetAtrWma( + this IReadOnlyList quotes, int lookbackPeriods) where TQuote : IQuote { // sort quotes and convert to collection or list - Collection quotesList = quotes + IReadOnlyList quotesList = quotes .ToSortedCollection(); // initialize results List results = new(quotesList.Count); // perform pre-requisite calculations to get ATR values - List atrResults = quotes + IReadOnlyList atrResults = quotes .GetAtr(lookbackPeriods) .ToList(); - // roll through quotes + // roll through source values for (int i = 0; i < quotesList.Count; i++) { TQuote q = quotesList[i]; diff --git a/docs/examples/CustomIndicators/README.md b/docs/examples/CustomIndicators/README.md index ca5bccc03..23073a902 100644 --- a/docs/examples/CustomIndicators/README.md +++ b/docs/examples/CustomIndicators/README.md @@ -21,14 +21,14 @@ using Skender.Stock.Indicators; namespace Custom.Stock.Indicators; // custom results class -public class AtrWmaResult : ResultBase, IReusableResult +public class AtrWmaResult : ResultBase, IReusable { // date property is inherited here, // so you only need to add custom items public double? AtrWma { get; set; } // to enable further chaining - double? IReusableResult.Value => AtrWma; + double? IReusable.Value => AtrWma; } ``` @@ -44,8 +44,8 @@ namespace Custom.Stock.Indicators; public static class CustomIndicator { // Custom ATR WMA calculation - public static IEnumerable GetAtrWma( - this IEnumerable quotes, + public static IReadOnlyList GetAtrWma( + this IReadOnlyList quotes, int lookbackPeriods) where TQuote : IQuote { @@ -61,7 +61,7 @@ public static class CustomIndicator .GetAtr(lookbackPeriods) .ToList(); - // roll through quotes + // roll through source values for (int i = 0; i < quotesList.Count; i++) { TQuote q = quotesList[i]; @@ -107,10 +107,10 @@ using Custom.Stock.Indicators; // your custom library [..] // fetch historical quotes from your feed (your method) -IEnumerable quotes = GetQuotesFromFeed("MSFT"); +IReadOnlyList quotes = GetQuotesFromFeed("MSFT"); // calculate 10-period ATR WMA -IEnumerable results = quotes.GetAtrWma(10); +IReadOnlyList results = quotes.GetAtrWma(10); // use results as needed for your use case (example only) foreach (AtrWmaResult r in results) diff --git a/docs/examples/CustomIndicatorsUsage/Program.cs b/docs/examples/CustomIndicatorsUsage/Program.cs index 5439a977e..11eae1f42 100644 --- a/docs/examples/CustomIndicatorsUsage/Program.cs +++ b/docs/examples/CustomIndicatorsUsage/Program.cs @@ -3,74 +3,44 @@ using Custom.Stock.Indicators; using Skender.Stock.Indicators; -namespace CustomIndicatorsUsage; - // USE CUSTOM INDICATORS exactly the same as // other indicators in the library -public static class Program -{ - public static void Main() - { - // fetch historical quotes from data provider - IEnumerable quotes = GetQuotesFromFeed(); - - // calculate 10-period custom AtrWma - IEnumerable results = quotes - .GetAtrWma(10); - - // show results - Console.WriteLine("ATR WMA Results ---------------------------"); - - foreach (AtrWmaResult r in results.Take(30)) - { - // only showing first 30 records for brevity - Console.WriteLine($"ATR WMA on {r.Date:u} was ${r.AtrWma:N3}"); - } - - // optional: demo of a converter (nulls to NaN) - - Console.WriteLine(); - Console.WriteLine("ATR WMA Results with NaN (optional) -------"); +// Fetch historical quotes from data provider. +// We're mocking with a simple JSON file import +string json = File.ReadAllText("quotes.data.json"); - // tip: converting ToList() and using For loops is faster to iterate - List resultsList = results - .Take(30) - .ToList(); +Collection quotes = JsonSerializer + .Deserialize>(json) + .ToSortedCollection(); - for (int i = 0; i < resultsList.Count; i++) - { - AtrWmaResult r = resultsList[i]; - r.AtrWma = r.AtrWma.Null2NaN(); - - Console.WriteLine($"ATR WMA on {r.Date:u} was ${r.AtrWma:N3}"); - } - } - - private static Collection GetQuotesFromFeed() - { - /************************************************************ - - We're mocking a data provider here by simply importing a - JSON file, a similar format of many public APIs. +// Calculate 10-period custom AtrWma +IReadOnlyList results = quotes + .GetAtrWma(10); - This approach will vary widely depending on where you are - getting your quote history. +// Show results +Console.WriteLine("ATR WMA Results ---------------------------"); - See https://github.com/DaveSkender/Stock.Indicators/discussions/579 - for free or inexpensive market data providers and examples. +foreach (AtrWmaResult r in results.Take(30)) +{ + // only showing first 30 records for brevity + Console.WriteLine($"ATR WMA on {r.Date:u} was ${r.AtrWma:N3}"); +} - The return type of IEnumerable can also be List - or ICollection or other IEnumerable compatible types. +// optional: demo of a converter (nulls to NaN) - ************************************************************/ +Console.WriteLine(); +Console.WriteLine("ATR WMA Results with NaN (optional) -------"); - string json = File.ReadAllText("quotes.data.json"); +// TIP: converting ToList() and using For loops is faster to iterate +List resultsList = results + .Take(30) + .ToList(); - Collection quotes = JsonSerializer - .Deserialize>(json) - .ToSortedCollection(); +for (int i = 0; i < resultsList.Count; i++) +{ + AtrWmaResult r = resultsList[i]; + r.AtrWma = r.AtrWma.Null2NaN(); - return quotes; - } + Console.WriteLine($"ATR WMA on {r.Date:u} was ${r.AtrWma:N3}"); } diff --git a/docs/examples/Examples.sln b/docs/examples/Examples.sln index ce9820ae4..7ae0622cc 100644 --- a/docs/examples/Examples.sln +++ b/docs/examples/Examples.sln @@ -13,7 +13,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomIndicatorsUsage", "Cu EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ObserveStream", "ObserveStream\ObserveStream.csproj", "{5FAD383B-DFCD-42FD-A847-53D772876595}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UseQuoteApi", "UseQuoteApi\UseQuoteApi.csproj", "{E6B2E0AE-6457-47F3-9BA5-01F4AA84118A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UseQuoteApi", "UseQuoteApi\UseQuoteApi.csproj", "{E6B2E0AE-6457-47F3-9BA5-01F4AA84118A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BF2C9401-10F2-4D06-A610-A2372336FCA8}" + ProjectSection(SolutionItems) = preProject + README.md = README.md + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -45,6 +50,10 @@ Global {E6B2E0AE-6457-47F3-9BA5-01F4AA84118A}.Debug|Any CPU.Build.0 = Debug|Any CPU {E6B2E0AE-6457-47F3-9BA5-01F4AA84118A}.Release|Any CPU.ActiveCfg = Release|Any CPU {E6B2E0AE-6457-47F3-9BA5-01F4AA84118A}.Release|Any CPU.Build.0 = Release|Any CPU + {9B9ED801-0F01-4851-A086-9A3805584BE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B9ED801-0F01-4851-A086-9A3805584BE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B9ED801-0F01-4851-A086-9A3805584BE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B9ED801-0F01-4851-A086-9A3805584BE2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/docs/examples/ObserveStream/Program.cs b/docs/examples/ObserveStream/Program.cs index 80b9a54be..ef63e0efd 100644 --- a/docs/examples/ObserveStream/Program.cs +++ b/docs/examples/ObserveStream/Program.cs @@ -1,125 +1,107 @@ using Alpaca.Markets; using Skender.Stock.Indicators; -namespace ObserveStream; +string symbol = "BTC/USD"; +Console.WriteLine($"STREAMING QUOTES FOR {symbol}"); +Console.WriteLine(); -internal class Program +// get and validate keys, see README.md +string ALPACA_KEY = Environment.GetEnvironmentVariable("ALPACA_KEY"); +string ALPACA_SECRET = Environment.GetEnvironmentVariable("ALPACA_SECRET"); + +if (string.IsNullOrEmpty(ALPACA_KEY)) { - private static async Task Main(string[] args) - { - if (args.Length != 0) - { - Console.WriteLine(args); - } + throw new ArgumentNullException( + ALPACA_KEY, + $"API KEY missing, use `setx ALPACA_KEY \"MY-ALPACA-KEY\"` to set."); +} + +if (string.IsNullOrEmpty(ALPACA_SECRET)) +{ + throw new ArgumentNullException( + ALPACA_SECRET, + $"API SECRET missing, use `setx AlpacaApiSecret \"MY-ALPACA-SECRET\"` to set."); +} + +// initialize our quote provider and a few subscribers +QuoteProvider provider = new(); + +SmaObserver sma = provider.GetSma(3); +EmaObserver ema = provider.GetEma(5); +EmaObserver emaChain = provider + .Use(CandlePart.HL2) + .GetEma(7); + +// connect to Alpaca WebSocket +SecretKey secretKey = new(ALPACA_KEY, ALPACA_SECRET); + +IAlpacaCryptoStreamingClient client + = Environments + .Paper + .GetAlpacaCryptoStreamingClient(secretKey); + +await client.ConnectAndAuthenticateAsync(); + +// TODO: is this needed? +AutoResetEvent[] waitObjects = [new AutoResetEvent(false)]; + +IAlpacaDataSubscription quoteSubscription + = client.GetMinuteBarSubscription(symbol); - string symbol = "BTC/USD"; - Console.WriteLine($"STREAMING QUOTES FOR {symbol}"); - Console.WriteLine(); +await client.SubscribeAsync(quoteSubscription); - await SubscribeToQuotes(symbol); +// console display header +Console.WriteLine("A new quote will be shown when they arrive every minute."); +Console.WriteLine("PLEASE WAIT > 8 MINUTES BEFORE EXITING TO SEE ALL 3 INDICATORS CALCULATED."); +Console.WriteLine("Press any key to EXIT the process and to see results."); +Console.WriteLine(); + +Console.WriteLine("Date Close price SMA(3) EMA(5) EMA(7,HL2)"); +Console.WriteLine("----------------------------------------------------------------------"); + +// handle new quotes +quoteSubscription.Received += (q) => +{ + // add to our provider + provider.Add(new Quote + { + Date = q.TimeUtc, + Open = q.Open, + High = q.High, + Low = q.Low, + Close = q.Close, + Volume = q.Volume + }); + + // display live results + string liveMessage = $"{q.TimeUtc:u} ${q.Close:N2}"; + + SmaResult s = sma.Results.Last(); + EmaResult e = ema.Results.Last(); + EmaResult c = emaChain.Results.Last(); + + if (s.Sma is not null) + { + liveMessage += $"{s.Sma,12:N1}"; } - public static async Task SubscribeToQuotes(string symbol) + if (e.Ema is not null) { - // get and validate keys, see README.md - string ALPACA_KEY = Environment.GetEnvironmentVariable("ALPACA_KEY"); - string ALPACA_SECRET = Environment.GetEnvironmentVariable("ALPACA_SECRET"); - - if (string.IsNullOrEmpty(ALPACA_KEY)) - { - throw new ArgumentNullException( - ALPACA_KEY, - $"API KEY missing, use `setx ALPACA_KEY \"MY-ALPACA-KEY\"` to set."); - } - - if (string.IsNullOrEmpty(ALPACA_SECRET)) - { - throw new ArgumentNullException( - ALPACA_SECRET, - $"API SECRET missing, use `setx AlpacaApiSecret \"MY-ALPACA-SECRET\"` to set."); - } - - // initialize our quote provider and a few subscribers - QuoteProvider provider = new(); - - SmaObserver sma = provider.GetSma(3); - EmaObserver ema = provider.GetEma(5); - EmaObserver emaChain = provider - .Use(CandlePart.HL2) - .GetEma(7); - - // connect to Alpaca WebSocket - SecretKey secretKey = new(ALPACA_KEY, ALPACA_SECRET); - - IAlpacaCryptoStreamingClient client - = Environments - .Paper - .GetAlpacaCryptoStreamingClient(secretKey); - - await client.ConnectAndAuthenticateAsync(); - - // TODO: is this needed? - AutoResetEvent[] waitObjects = [new AutoResetEvent(false)]; - - IAlpacaDataSubscription quoteSubscription - = client.GetMinuteBarSubscription(symbol); - - await client.SubscribeAsync(quoteSubscription); - - // console display header - Console.WriteLine("A new quote will be shown when they arrive every minute."); - Console.WriteLine("PLEASE WAIT > 8 MINUTES BEFORE EXITING TO SEE ALL 3 INDICATORS CALCULATED."); - Console.WriteLine("Press any key to EXIT the process and to see results."); - Console.WriteLine(); - - Console.WriteLine("Date Close price SMA(3) EMA(5) EMA(7,HL2)"); - Console.WriteLine("----------------------------------------------------------------------"); - - // handle new quotes - quoteSubscription.Received += (q) => - { - // add to our provider - provider.Add(new Quote - { - Date = q.TimeUtc, - Open = q.Open, - High = q.High, - Low = q.Low, - Close = q.Close, - Volume = q.Volume - }); - - // display live results - string liveMessage = $"{q.TimeUtc:u} ${q.Close:N2}"; - - SmaResult s = sma.Results.Last(); - EmaResult e = ema.Results.Last(); - EmaResult c = emaChain.Results.Last(); - - if (s.Sma is not null) - { - liveMessage += $"{s.Sma,12:N1}"; - } - - if (e.Ema is not null) - { - liveMessage += $"{e.Ema,12:N1}"; - } - - if (c.Ema is not null) - { - liveMessage += $"{c.Ema,12:N1}"; - } - - Console.WriteLine(liveMessage); - }; - - // to stop watching on key press - Console.ReadKey(); - - // terminate subscriptions - provider.EndTransmission(); - await client.UnsubscribeAsync(quoteSubscription); - await client.DisconnectAsync(); + liveMessage += $"{e.Ema,12:N1}"; } -} + + if (c.Ema is not null) + { + liveMessage += $"{c.Ema,12:N1}"; + } + + Console.WriteLine(liveMessage); +}; + +// to stop watching on key press +Console.ReadKey(); + +// terminate subscriptions +provider.EndTransmission(); +await client.UnsubscribeAsync(quoteSubscription); +await client.DisconnectAsync(); diff --git a/docs/examples/README.md b/docs/examples/README.md index 6fabc94ad..c49860719 100644 --- a/docs/examples/README.md +++ b/docs/examples/README.md @@ -78,7 +78,7 @@ decimal rlzGain = 0; Console.WriteLine(" Date Close StRSI Signal Cross Net Gains"); Console.WriteLine("-------------------------------------------------------"); -// roll through history +// roll through source values for (int i = 1; i < quotesList.Count; i++) { Quote q = quotesList[i]; diff --git a/docs/examples/UseQuoteApi/Program.cs b/docs/examples/UseQuoteApi/Program.cs index 024a2c0a1..74ece1876 100644 --- a/docs/examples/UseQuoteApi/Program.cs +++ b/docs/examples/UseQuoteApi/Program.cs @@ -1,131 +1,114 @@ using Alpaca.Markets; using Skender.Stock.Indicators; -namespace UseQuoteApi; +string symbol = "AAPL"; -internal class Program +/************************************************************ + + We're using Alpaca SDK for .NET to access their public APIs. + + This approach will vary widely depending on where you are + getting your quote history. + + See https://github.com/DaveSkender/Stock.Indicators/discussions/579 + for free or inexpensive market data providers and examples. + + The return type of IEnumerable can also be List + or ICollection or other IEnumerable compatible types. + + ************************************************************/ + +// get and validate keys, see README.md +string ALPACA_KEY = Environment.GetEnvironmentVariable("ALPACA_KEY"); +string ALPACA_SECRET = Environment.GetEnvironmentVariable("ALPACA_SECRET"); + +if (string.IsNullOrEmpty(ALPACA_KEY)) { - private static async Task Main() - { - string symbol = "AAPL"; + throw new ArgumentNullException( + ALPACA_KEY, + $"API KEY missing, use `setx ALPACA_KEY \"MY-ALPACA-KEY\"` to set."); +} - // fetch historical quotes from data provider - IEnumerable quotes = await GetQuotesFromFeed(symbol); +if (string.IsNullOrEmpty(ALPACA_SECRET)) +{ + throw new ArgumentNullException( + ALPACA_SECRET, + $"API SECRET missing, use `setx AlpacaApiSecret \"MY-ALPACA-SECRET\"` to set."); +} - // calculate 10-period SMA - IEnumerable results = quotes.GetSma(10); +// connect to Alpaca REST API +SecretKey secretKey = new(ALPACA_KEY, ALPACA_SECRET); - if (!results.Any() || results == null) - { - throw new NullReferenceException("No indicator results were returned."); - } +IAlpacaDataClient client = Environments.Paper.GetAlpacaDataClient(secretKey); - // show results - Console.WriteLine($"{symbol} Results ------- (last 10 of {results.Count()}) --"); +// compose request +// (excludes last 15 minutes for free delayed quotes) +DateTime into = DateTime.Now.Subtract(TimeSpan.FromMinutes(16)); +DateTime from = into.Subtract(TimeSpan.FromDays(1000)); - foreach (SmaResult r in results.TakeLast(10)) - { - // only showing last 10 records for brevity - Console.WriteLine($"SMA on {r.Date:u} was ${r.Sma:N3}"); - } +HistoricalBarsRequest request = new(symbol, from, into, BarTimeFrame.Minute); - // analyze results (compare to quote values) - Console.WriteLine(); - Console.WriteLine($"{symbol} Analysis --------------------------"); +// fetch minute-bar quotes in Alpaca's format +IPage barSet = await client.ListHistoricalBarsAsync(request); - /************************************************************ - Results are usually returned with the same number of - elements as the provided quotes; see individual indicator - docs for more information. +// convert library compatible quotes +List quotes = barSet + .Items + .Select(bar => new Quote + { + Date = bar.TimeUtc, + Open = bar.Open, + High = bar.High, + Low = bar.Low, + Close = bar.Close, + Volume = bar.Volume + }) + .OrderBy(x => x.Date) + .ToList(); - As such, converting to List means they can be indexed - with the same ordinal position. - ************************************************************/ - List quotesList = quotes - .ToList(); +// calculate 10-period SMA +IEnumerable results = quotes.GetSma(10); - List resultsList = results - .ToList(); +if (!results.Any() || results == null) +{ + throw new NullReferenceException("No indicator results were returned."); +} + +// show results +Console.WriteLine($"{symbol} Results ------- (last 10 of {results.Count()}) --"); - for (int i = quotesList.Count - 25; i < quotesList.Count; i++) - { - // only showing ~25 records for brevity +foreach (SmaResult r in results.TakeLast(10)) +{ + // only showing last 10 records for brevity + Console.WriteLine($"SMA on {r.Date:u} was ${r.Sma:N3}"); +} - Quote q = quotesList[i]; - SmaResult r = resultsList[i]; +// analyze results (compare to quote values) +Console.WriteLine(); +Console.WriteLine($"{symbol} Analysis --------------------------"); - bool isBullish = (double)q.Close > r.Sma; +/************************************************************ + Results are usually returned with the same number of + elements as the provided quotes; see individual indicator + docs for more information. - Console.WriteLine($"SMA on {r.Date:u} was ${r.Sma:N3}" - + $" and Bullishness is {isBullish}"); - } - } + As such, converting to List means they can be indexed + with the same ordinal position. + ************************************************************/ - private static async Task> GetQuotesFromFeed(string symbol) - { - /************************************************************ - - We're using Alpaca SDK for .NET to access their public APIs. - - This approach will vary widely depending on where you are - getting your quote history. - - See https://github.com/DaveSkender/Stock.Indicators/discussions/579 - for free or inexpensive market data providers and examples. - - The return type of IEnumerable can also be List - or ICollection or other IEnumerable compatible types. - - ************************************************************/ - - // get and validate keys, see README.md - string ALPACA_KEY = Environment.GetEnvironmentVariable("ALPACA_KEY"); - string ALPACA_SECRET = Environment.GetEnvironmentVariable("ALPACA_SECRET"); - - if (string.IsNullOrEmpty(ALPACA_KEY)) - { - throw new ArgumentNullException( - ALPACA_KEY, - $"API KEY missing, use `setx ALPACA_KEY \"MY-ALPACA-KEY\"` to set."); - } - - if (string.IsNullOrEmpty(ALPACA_SECRET)) - { - throw new ArgumentNullException( - ALPACA_SECRET, - $"API SECRET missing, use `setx AlpacaApiSecret \"MY-ALPACA-SECRET\"` to set."); - } - - // connect to Alpaca REST API - SecretKey secretKey = new(ALPACA_KEY, ALPACA_SECRET); - - IAlpacaDataClient client = Environments.Paper.GetAlpacaDataClient(secretKey); - - // compose request - // (excludes last 15 minutes for free delayed quotes) - DateTime into = DateTime.Now.Subtract(TimeSpan.FromMinutes(16)); - DateTime from = into.Subtract(TimeSpan.FromDays(1000)); - - HistoricalBarsRequest request = new(symbol, from, into, BarTimeFrame.Minute); - - // fetch minute-bar quotes in Alpaca's format - IPage barSet = await client.ListHistoricalBarsAsync(request); - - // convert library compatible quotes - IEnumerable quotes = barSet - .Items - .Select(bar => new Quote - { - Date = bar.TimeUtc, - Open = bar.Open, - High = bar.High, - Low = bar.Low, - Close = bar.Close, - Volume = bar.Volume - }) - .OrderBy(x => x.Date); // optional - - return quotes; - } +List resultsList = results + .ToList(); + +for (int i = quotes.Count - 25; i < quotes.Count; i++) +{ + // only showing ~25 records for brevity + + Quote q = quotes[i]; + SmaResult r = resultsList[i]; + + bool isBullish = (double)q.Close > r.Sma; + + Console.WriteLine($"SMA on {r.Date:u} was ${r.Sma:N3}" + + $" and Bullishness is {isBullish}"); } diff --git a/docs/pages/guide.md b/docs/pages/guide.md index e075c2d2a..40b1adad4 100644 --- a/docs/pages/guide.md +++ b/docs/pages/guide.md @@ -15,7 +15,6 @@ layout: page
  • Example usage
  • Historical quotes
  • Using custom quote classes
  • -
  • Using custom results classes
  • Generating indicator of indicators
  • Candlestick patterns
  • Creating custom indicators
  • @@ -44,7 +43,7 @@ Most indicators require that you provide historical quote data and additional co You must get historical quotes from your own market data provider. For clarification, the `GetQuotesFromFeed()` method shown in the example below **is not part of this library**, but rather an example to represent your own acquisition of historical quotes. -Historical price data can be provided as a `List`, `IEnumerable`, or `ICollection` of the `Quote` class ([see below](#historical-quotes)); however, it can also be supplied as a generic [custom TQuote type](#using-custom-quote-classes) if you prefer to use your own quote model. +Historical price data can be provided as a `List`, `IReadOnlyList`, or `ICollection` of the `Quote` class ([see below](#historical-quotes)); however, it can also be supplied as a generic [custom TQuote type](#using-custom-quote-classes) if you prefer to use your own quote model. For additional configuration parameters, default values are provided when there is an industry standard. You can, of course, override these and provide your own values. @@ -58,16 +57,16 @@ using Skender.Stock.Indicators; [..] // fetch historical quotes from your feed (your method) -IEnumerable quotes = GetQuotesFromFeed("MSFT"); +IReadOnlyList quotes = GetQuotesFromFeed("MSFT"); // calculate 20-period SMA -IEnumerable results = quotes +IReadOnlyList results = quotes .GetSma(20); // use results as needed for your use case (example only) foreach (SmaResult r in results) { - Console.WriteLine($"SMA on {r.Date:d} was ${r.Sma:N4}"); + Console.WriteLine($"SMA on {r.Timestamp:d} was ${r.Sma:N4}"); } ``` @@ -90,11 +89,11 @@ More examples available: ## Historical quotes -You must provide historical price quotes to the library in the standard OHLCV `IEnumerable` or a compatible `List` or `ICollection` format. It should have a consistent period frequency (day, hour, minute, etc). See [using custom quote classes](#using-custom-quote-classes) if you prefer to use your own quote class. +You must provide historical price quotes to the library in the standard OHLCV `IReadOnlyList` or a compatible `List` or `ICollection` format. It should have a consistent period frequency (day, hour, minute, etc). See [using custom quote classes](#using-custom-quote-classes) if you prefer to use your own quote class. | name | type | notes | -- |-- |-- -| `Date` | DateTime | Date +| `Timestamp` | DateTime | Close date | `Open` | decimal | Open price | `High` | decimal | High price | `Low` | decimal | Low price @@ -117,17 +116,19 @@ Each indicator will need different amounts of price `quotes` to calculate. You ### Using custom quote classes -If you would like to use your own custom `MyCustomQuote` class, to avoid needing to transpose into the library `Quote` class, you only need to add the `IQuote` interface. +If you would like to use your own custom `MyCustomQuote` class, to avoid needing to transpose into the built-in library `Quote` class, you only need to add the `IQuote` interface and ensure that you've implemented a correct and compatible quote `record` or class. + +> 🚩 **IMPORTANT!** +> Your custom quote class needs to be [equatable using property values](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/how-to-define-value-equality-for-a-type). Since this can be complicated to setup, we've provided the shown `EquatableQuote` base class. You can exclude this base and write your own `IEquatable` interface by only implementing the `IQuote` interface; however, if you do not define it fully with `==` and `!=` operator overrides correctly, it may cause problems with streaming overflow handling. **We recommend using the equatable `record` class** type for your custom quote class. ```csharp using Skender.Stock.Indicators; -[..] - -public class MyCustomQuote : IQuote +/// EASY METHOD (use record class) +public record class MyCustomQuote : IQuote { // required base properties - public DateTime Date { get; set; } + public DateTime Timestamp { get; set; } public decimal Open { get; set; } public decimal High { get; set; } public decimal Low { get; set; } @@ -136,83 +137,109 @@ public class MyCustomQuote : IQuote // custom properties public int MyOtherProperty { get; set; } + + // required mapping method for equality + public bool Equals(IQuote? other) + => base.Equals(other); } ``` ```csharp -// fetch historical quotes from your favorite feed -IEnumerable myQuotes = GetQuotesFromFeed("MSFT"); - -// example: get 20-period simple moving average -IEnumerable results = myQuotes.GetSma(20); -``` +/// EASY METHOD (use our base equatable class) +public class MyCustomQuote : EquatableQuote, IQuote +{ + // required inherited base properties do not need to be redefined, + // however, if you prefer to explicitly define for clarity, + // use the override keyword (optional) -#### Using custom quote property names + public override DateTime Timestamp { get; set; } + public override decimal Open { get; set; } + public override decimal High { get; set; } + public override decimal Low { get; set; } + public override decimal Close { get; set; } + public override decimal Volume { get; set; } -If you have a model that has different properties names, but the same meaning, you only need to map them. For example, if your class has a property called `CloseDate` instead of `Date`, it could be represented like this: + // custom properties + public int MyOtherProperty { get; set; } +} +``` ```csharp -public class MyCustomQuote : IQuote // + ISeries +/// HARD METHOD (define your own equatable overrides) +public class MyCustomQuote : IQuote { // required base properties - DateTime ISeries.Date => CloseDate; + public DateTime Timestamp { get; set; } public decimal Open { get; set; } public decimal High { get; set; } public decimal Low { get; set; } public decimal Close { get; set; } - decimal IQuote.Volume => Vol; + public decimal Volume { get; set; } // custom properties public int MyOtherProperty { get; set; } - public DateTime CloseDate { get; set; } - public decimal Vol { get; set; } + + // equatable overrides + public override bool Equals(object? obj) => this.Equals(obj); + public bool Equals(IQuote? other) => this.Equals(other); + public bool Equals(MyCustomQuote? other) { ... } + public static bool operator ==( ... ) { ... } + public static bool operator !=( ... ) { ... } + public override int GetHashCode() + => HashCode.Combine( + Timestamp, Open, High, Low, Close, Volume); } ``` -Note the use of explicit interface (property declaration is `ISeries.Date`), this is because having two properties that expose the same information can be confusing, this way `Date` property is only accessible when working with the included `Quote` type, while if you are working with a `MyCustomQuote` the `Date` property will be hidden, avoiding confusion. +```csharp +// USAGE +// fetch historical quotes from your favorite feed +IReadOnlyList myQuotes = GetQuotesFromFeed("MSFT"); -For more information on explicit interfaces, refer to the [C# Programming Guide](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/explicit-interface-implementation). +// example: get 20-period simple moving average +IReadOnlyList results = myQuotes.GetSma(20); +``` -## Using custom results classes +#### Using custom quote property names -The indicator result classes can be customized in your code. There are many ways to do this, but the benefit of using derived `ResultBase` is that your custom class will inherit all of the [utility results extension methods]({{site.baseurl}}/utilities/#utilities-for-indicator-results). Here's one example: +If you have a model that has different properties names, but the same meaning, you only need to map them. For example, if your class has a property called `CloseDate` instead of `Timestamp`, it could be represented like this: ```csharp -// your custom class with an EMA profile -public class MyEma : ResultBase +// if using record type +public record class MyCustomQuote : IQuote { - // my properties - public int MyId { get; set; } - public double? Ema { get; set; } -} - -public void MyClass(){ - - // fetch historical quotes from your feed (your method) - IEnumerable quotes = GetQuotesFromFeed("SPY"); + // redirect required base properties + // with your custom properties + public DateTime Timestamp => CloseDate; + public decimal Volume => Vol; - // compute indicator - INumerable emaResults = quotes.GetEma(14); - - // convert to my Ema class list [using LINQ] - List myEmaResults = emaResults - .Select(e => new MyEma - { - MyId = 123, - Date = e.Date, - Ema = e.Ema - }) - .ToList(); + // custom properties + public int MyOtherProperty { get; set; } + public DateTime CloseDate { get; set; } + public decimal Vol { get; set; } +} +``` - // randomly selecting first record from the - // collection here for the example - MyEma r = myEmaResults.FirstOrDefault(); +```csharp +// if using inherited equatable class type +public class MyCustomQuote : EquatableQuote, IQuote +{ + // override inherited, required base properties + // with your custom properties + public override DateTime Timestamp => CloseDate; + public override decimal Volume => Vol; - // use your custom quote data - Console.WriteLine($"On {r.Date}, EMA was {r.Ema} for my EMA ID {r.MyId}."); + // custom properties + public int MyOtherProperty { get; set; } + public DateTime CloseDate { get; set; } + public decimal Vol { get; set; } } ``` +Note the use of explicit interface (property declaration is `ISeries.Timestamp`), this is because having two properties that expose the same information can be confusing, this way `Timestamp` property is only accessible when working with the included `Quote` type, while if you are working with a `MyCustomQuote` the `Timestamp` property will be hidden, avoiding confusion. + +For more information on explicit interfaces, refer to the [C# Programming Guide](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/explicit-interface-implementation). + ## Generating indicator of indicators If you want to compute an indicator of indicators, such as an SMA of an ADX or an [RSI of an OBV](https://medium.com/@robswc/this-is-what-happens-when-you-combine-the-obv-and-rsi-indicators-6616d991773d), use _**chaining**_ to calculate an indicator from prior results. @@ -220,17 +247,17 @@ Example: ```csharp // fetch historical quotes from your feed (your method) -IEnumerable quotes = GetQuotesFromFeed("SPY"); +IReadOnlyList quotes = GetQuotesFromFeed("SPY"); // calculate RSI of OBV -IEnumerable results +IReadOnlyList results = quotes .GetObv() .GetRsi(14); // or with two separate operations -IEnumerable obvResults = quotes.GetObv(); -IEnumerable rsiOfObv = obvResults.GetRsi(14); +IReadOnlyList obvResults = quotes.GetObv(); +IReadOnlyList rsiOfObv = obvResults.GetRsi(14); ``` ## Candlestick patterns diff --git a/docs/pages/home.md b/docs/pages/home.md index a9dba6239..b82ad38e3 100644 --- a/docs/pages/home.md +++ b/docs/pages/home.md @@ -40,7 +40,7 @@ You'll get all of the industry standard indicators out-of-the-box. Additionally ```csharp // example: get 20-period simple moving average -IEnumerable results = quotes.GetSma(20); +IReadOnlyList results = quotes.GetSma(20); ``` See more [usage examples]({{site.baseurl}}/guide/#example-usage). @@ -51,13 +51,13 @@ Optional chaining enables advanced uses cases; such as, indicator of indicators, ```csharp // example: advanced chaining (RSI of OBV) -IEnumerable results +IReadOnlyList results = quotes .GetObv() .GetRsi(14); // example: use any candle variant -IEnumerable results +IReadOnlyList results = quotes .Use(CandlePart.HL2) .GetEma(20); @@ -67,10 +67,9 @@ See the [guide]({{site.baseurl}}/guide/#content) and the [full list of indicator ## Optimized for modern .NET frameworks -Our [NuGet library](https://www.nuget.org/packages/Skender.Stock.Indicators) directly targets all current frameworks for peak performance, including the .NET Standard for older framework compatibility. +Our [NuGet library](https://www.nuget.org/packages/Skender.Stock.Indicators) directly targets all current frameworks for peak performance. -- .NET 8.0, 7.0, 6.0 -- .NET Standard 2.1, 2.0 +- .NET 8.0, 6.0 The compiled library package is [Common Language Specification (CLS) compliant](https://docs.microsoft.com/en-us/dotnet/standard/common-type-system) and can be used in other programming languages, including Python and everything in the .NET universe. diff --git a/docs/pages/utilities.md b/docs/pages/utilities.md index f227ba937..e86794df1 100644 --- a/docs/pages/utilities.md +++ b/docs/pages/utilities.md @@ -28,13 +28,9 @@ var results = quotes {% include candlepart-options.md %} -### Using tuple quotes - -`quotes.ToTupleCollection()` is a method for converting any `TQuote` collection to a simple [tuple](https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/value-tuples) `(DateTime, double)` formatted `Collection`. Most indicators in our library will accept this tuple format. With that said, there are many indicators that also require the full OHLCV quote format, so it cannot be used universally. - ### Sort quotes -`quotes.ToSortedCollection()` sorts any collection of `TQuote` or tuple `(DateTime, double)` and returns it as a `Collection` sorted by ascending `Date`. You do not need to sort quotes before using library indicators; however, if you are creating [custom indicators]({{site.baseurl}}/custom-indicators/#content) it's important to analyze `quotes` in a proper sequence. +`quotes.ToSortedList()` sorts any collection of `TQuote` or `ISeries` and returns it as a `IReadOnlyList` sorted by ascending `Timestamp`. You **do need to sort quotes** before using library indicators. ### Resize quote history @@ -42,7 +38,7 @@ var results = quotes ```csharp // aggregate into larger bars -IEnumerable dayBarQuotes = +IReadOnlyList dayBarQuotes = minuteBarQuotes.Aggregate(PeriodSize.Day); ``` @@ -50,7 +46,7 @@ An alternate version of this utility is provided where you can use any native `T ```csharp // alternate usage with TimeSpan -IEnumerable dayBarQuotes = +IReadOnlyList dayBarQuotes = minuteBarQuotes.Aggregate(TimeSpan timeSpan); ``` @@ -80,18 +76,18 @@ IEnumerable dayBarQuotes = CandleProperties candle = quote.ToCandle(); // collection of quotes -IEnumerable candles = quotes.ToCandles(); +IReadOnlyList candles = quotes.ToCandles(); ``` {% include candle-properties.md %} ### Validate quote history -`quotes.Validate()` is an advanced check of your `IEnumerable quotes`. It will check for duplicate dates and other bad data and will throw an `InvalidQuotesException` if validation fails. This comes at a small performance cost, so we did not automatically add these advanced checks in the indicator methods. Of course, you can and should do your own validation of `quotes` prior to using it in this library. Bad historical quotes can produce unexpected results. +`quotes.Validate()` is an advanced check of your `IReadOnlyList quotes`. It will check for duplicate dates and other bad data and will throw an `InvalidQuotesException` if validation fails. This comes at a small performance cost, so we did not automatically add these advanced checks in the indicator methods. Of course, you can and should do your own validation of `quotes` prior to using it in this library. Bad historical quotes can produce unexpected results. ```csharp // advanced validation -IEnumerable validatedQuotes = quotes.Validate(); +IReadOnlyList validatedQuotes = quotes.Validate(); // and can be used inline with chaining var results = quotes @@ -108,7 +104,7 @@ var results = quotes ```csharp // example: only show Marubozu signals -IEnumerable results +IReadOnlyList results = quotes.GetMarubozu(..).Condense(); ``` @@ -120,11 +116,13 @@ IEnumerable results ```csharp // calculate indicator series -IEnumerable results = quotes.GetSma(20); +IReadOnlyList results = quotes.GetSma(20); // find result on a specific date DateTime lookupDate = [..] // the date you want to find SmaResult result = results.Find(lookupDate); + +// throws 'InvalidOperationException' when not found ``` ### Remove warmup periods @@ -133,12 +131,12 @@ SmaResult result = results.Find(lookupDate); ```csharp // auto remove recommended warmup periods -IEnumerable results = +IReadOnlyList results = quotes.GetAdx(14).RemoveWarmupPeriods(); // remove a specific quantity of periods int n = 14; -IEnumerable results = +IReadOnlyList results = quotes.GetAdx(n).RemoveWarmupPeriods(n+100); ``` @@ -148,19 +146,9 @@ See [individual indicator pages]({{site.baseurl}}/indicators/#content) for infor > > 🚩 **Warning**: without a specified `removePeriods` value, this utility will reverse-engineer the pruning amount. When there are unusual results or when chaining multiple indicators, there will be an erroneous increase in the amount of pruning. If you want more certainty, use a specific number for `removePeriods`. Using this method on chained indicators without `removePeriods` is strongly discouraged. -### Using tuple results - -`results.ToTupleCollection()` converts results to a simpler `(DateTime Date, double? Value)` [tuple](https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/value-tuples) `Collection`. - -`results.ToTupleNaN()` converts results to simpler `(DateTime Date, double Value)` tuple `Collection` with `null` values converted to `double.NaN`. - -`results.ToTupleChainable()` is a specialty converter used to prepare [custom indicators]({{site.baseurl}}/custom-indicators/#content) for chaining by removing `null` warmup periods and converting all remaining `null` values to `double.NaN`. - -> 🚩 **Warning**: warmup periods are pruned when using `.ToTupleChainable()`, resulting in fewer records. - ### Sort results -`results.ToSortedCollection()` sorts any collection of indicator results and returns it as a `Collection` sorted by ascending `Date`. Results from the library indicators are already sorted, so you'd only potentially need this if you're creating [custom indicators]({{site.baseurl}}/custom-indicators/#content). +`results.ToSortedList()` sorts any collection of indicator results and returns it as a `IReadOnlyList` sorted by ascending `Timestamp`. Results from the library indicators are already sorted, so you'd only potentially need this if you're creating [custom indicators]({{site.baseurl}}/custom-indicators/#content). ## Utilities for numerical analysis diff --git a/src/GlobalSuppressions.cs b/src/GlobalSuppressions.cs index 751a398ee..ed7d4e0c5 100644 --- a/src/GlobalSuppressions.cs +++ b/src/GlobalSuppressions.cs @@ -1,25 +1,16 @@ using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage( - "Naming", - "CA1716:Identifiers should not match keywords", - Justification = "Not really an issue.", - Scope = "member", - Target = "~P:Skender.Stock.Indicators.ISeries.Date")] - -[assembly: SuppressMessage( - "Naming", - "CA1716:Identifiers should not match keywords", - Justification = "Not really an issue.", - Scope = "member", - Target = "~P:Skender.Stock.Indicators.Quote.Date")] + "Maintainability", + "CA1510:Use ArgumentNullException throw helper", + Justification = "Does not support .NET Standard.")] [assembly: SuppressMessage( "Naming", - "CA1716:Identifiers should not match keywords", + "CA1720:Identifier contains type name", Justification = "Not really an issue.", Scope = "member", - Target = "~P:Skender.Stock.Indicators.IBasicData.Date")] + Target = "~F:Skender.Stock.Indicators.ChandelierType.Long")] [assembly: SuppressMessage("Naming", "CA1720:Identifier contains type name" @@ -34,39 +25,14 @@ Scope = "member", Target = "~F:Skender.Stock.Indicators.ChandelierType.Short")] -[assembly: SuppressMessage( - "StyleCop.CSharp.SpacingRules", - "SA1008:Opening parenthesis should be spaced correctly", - Justification = "Not compatible with `or` statement (analyzer bug)", - Scope = "member", - Target = "~M:Skender.Stock.Indicators.ResultUtility.Condense``1(System.Collections.Generic.IEnumerable{``0})~System.Collections.Generic.IEnumerable{``0}")] - [assembly: SuppressMessage( "Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Can only use with .NET 6 or later. We support .NET Framework and .NET Standard.")] -[assembly: SuppressMessage( - "StyleCop.CSharp.SpacingRules", - "SA1010:Opening square brackets should be spaced correctly", - Justification = "Invalid for new C# 12 [ collection ] syntax.")] - -[assembly: SuppressMessage( - "Naming", - "CA1725:Parameter names should match base declaration", - Justification = "The microsoft OnError implementation uses reserved word Error", - Scope = "member", - Target = "~M:Skender.Stock.Indicators.QuoteObserver.OnError(System.Exception)")] - [assembly: SuppressMessage( "Naming", "CA1716:Identifiers should not match keywords", - Justification = "The microsoft OnError implementation uses reserved word Error", + Justification = "Temporary, during deprecation period.", Scope = "member", - Target = "~M:Skender.Stock.Indicators.QuoteObserver.OnError(System.Exception)")] - -[assembly: SuppressMessage( - "Naming", - "CA1716:Identifiers should not match keywords", - Justification = "The microsoft OnError implementation uses reserved word Error", - Scope = "member", Target = "~M:Skender.Stock.Indicators.TupleObserver.OnError(System.Exception)")] + Target = "~P:Skender.Stock.Indicators.ISeries.Date")] diff --git a/src/Indicators.csproj b/src/Indicators.csproj index d78154c16..c54bdd1e7 100644 --- a/src/Indicators.csproj +++ b/src/Indicators.csproj @@ -1,7 +1,7 @@ - net8.0;net7.0;net6.0;netstandard2.1;netstandard2.0 + net8.0 Dave Skender Stock Indicators for .NET diff --git a/src/_common/BinarySettings.cs b/src/_common/BinarySettings.cs new file mode 100644 index 000000000..4f8f9e63f --- /dev/null +++ b/src/_common/BinarySettings.cs @@ -0,0 +1,111 @@ +namespace Skender.Stock.Indicators; + +/// +/// Binary on/off switches for high performance access +/// to behaviors and characteristics. +/// +/// +/// Initializes a new instance of the struct. +/// The Mask parameter is optional and defaults to 0b11111111 where all bits +/// pass through to "combinor" sets. +/// +/// Example of accessing a specific bit: +/// +/// BinarySettings settings = new(0b00000001); // bit 0 is set to 1 +/// bool isBit0Set = settings[0]; // true +/// bool isBit1Set = settings[1]; // false +/// +/// +/// Example of re/setting a specific bit: +/// +/// BinarySettings settings = new(0); +/// settings = settings with { [0] = true }; // set bit 0 to true +/// settings = settings with { [1] = false }; // set bit 1 to false +/// +/// +/// +/// Binary settings. +/// Default is 0b00000000 (binary literal of 0). +/// +/// +/// Mask for settings inheritence. +/// Default is 0b11111111 (binary literal of 255). +/// +[Serializable] +public readonly struct BinarySettings( + byte settings, + byte mask = 0b11111111) : IEquatable +{ + public byte Settings { get; } = settings; + public byte Mask { get; } = mask; + + // use default settings (none) and mask + // important: this explicit parameterless ctor required for struct + public BinarySettings() : this(settings: 0b00000000) { } + + /// + /// Gets the value of the bit at the specified index. + /// + /// The index of the bit to get. + /// True if the bit is set; otherwise, false. + public bool this[short index] + => (Settings & (1 << index)) != 0; + + /// + /// Combines the current settings with another instance + /// using a bitwise OR operation, excluding the bits masked by the parent settings. + /// + /// The parent instance to combine with. + /// + /// A new instance with combined settings. + /// Notably, it does not modify the current read-only instance. + /// + /// + /// + /// The mask is used to determine which bits from the parent settings should be excluded + /// during the combination. By default, the mask is set to 0b11111111, meaning all bits + /// are included. If a different mask is provided, the corresponding bits in the parent + /// settings will be excluded based on the mask. + /// + /// In other words, the mask you provide on instantiation will determine which bits are + /// genetic material passed on to the "combinor" child settings. The child settings will inherit the + /// bits from the parent settings that the parent decides to pass along. + /// + /// + /// Usage example (default mask): + /// + /// BinarySettings srcSettings = new(0b01101001); + /// BinarySettings defSettings = new(0b00000010); + /// BinarySettings newSettings = defSettings.Combine(srcSettings); // result: 0b01101011 + /// + /// Using a custom mask: + /// + /// BinarySettings customMaskSettings = new(0b01101001, 0b11111110); // do not pass 0th bit value + /// BinarySettings newSettingsWithCustomMask = defSettings.Combine(customMaskSettings); // result: 0b01101010 + /// + /// + public BinarySettings Combine(BinarySettings parentSettings) + { + // add parent bits according to their mask template + byte maskedParentSettings = (byte)(parentSettings.Settings & parentSettings.Mask); + + // combine the settings + return new BinarySettings((byte)(Settings | maskedParentSettings), parentSettings.Mask); + } + + public override bool Equals(object? obj) + => obj is BinarySettings other && Equals(other); + + public bool Equals(BinarySettings other) + => Settings == other.Settings && Mask == other.Mask; + + public override int GetHashCode() + => HashCode.Combine(Settings, Mask); + + public static bool operator ==(BinarySettings left, BinarySettings right) + => left.Equals(right); + + public static bool operator !=(BinarySettings left, BinarySettings right) + => !(left == right); +} + diff --git a/src/_common/Candles/Candles.Models.cs b/src/_common/Candles/Candles.Models.cs new file mode 100644 index 000000000..3ed861d29 --- /dev/null +++ b/src/_common/Candles/Candles.Models.cs @@ -0,0 +1,63 @@ +namespace Skender.Stock.Indicators; + +// CANDLESTICK MODELS + +[Serializable] +public record CandleProperties +( + DateTime Timestamp, + decimal Open, + decimal High, + decimal Low, + decimal Close, + decimal Volume +) : Quote(Timestamp, Open, High, Low, Close, Volume) +{ + // raw sizes + public decimal? Size => High - Low; + public decimal? Body => Open > Close ? Open - Close : Close - Open; + public decimal? UpperWick => High - (Open > Close ? Open : Close); + public decimal? LowerWick => (Open > Close ? Close : Open) - Low; + + // percent sizes + public double? BodyPct => Size != 0 ? (double?)(Body / Size) : 1; + public double? UpperWickPct => Size != 0 ? (double?)(UpperWick / Size) : 1; + public double? LowerWickPct => Size != 0 ? (double?)(LowerWick / Size) : 1; + + // directional info + public bool IsBullish => Close > Open; + public bool IsBearish => Close < Open; +} + +[Serializable] +public record CandleResult : ISeries +{ + public CandleResult( + DateTime timestamp, + IQuote quote, + Match match, + decimal? price) + { + Timestamp = timestamp; + Price = price; + Match = match; + Candle = quote.ToCandle(); + } + + public CandleResult( + DateTime timestamp, + CandleProperties candle, + Match match, + decimal? price) + { + Timestamp = timestamp; + Price = price; + Match = match; + Candle = candle; + } + + public DateTime Timestamp { get; init; } + public decimal? Price { get; init; } + public Match Match { get; init; } + public CandleProperties Candle { get; init; } +} diff --git a/src/_common/Candles/Candles.Utilities.cs b/src/_common/Candles/Candles.Utilities.cs new file mode 100644 index 000000000..82d278637 --- /dev/null +++ b/src/_common/Candles/Candles.Utilities.cs @@ -0,0 +1,27 @@ +namespace Skender.Stock.Indicators; + +public static partial class Utility +{ + public static IReadOnlyList Condense( + this IReadOnlyList candleResults) => candleResults + .Where(candle => candle.Match != Match.None) + .ToList(); + + public static CandleProperties ToCandle( + this TQuote quote) + where TQuote : IQuote => new( + Timestamp: quote.Timestamp, + Open: quote.Open, + High: quote.High, + Low: quote.Low, + Close: quote.Close, + Volume: quote.Volume); + + // convert/sort quotes into candles list + public static IReadOnlyList ToCandles( + this IReadOnlyList quotes) + where TQuote : IQuote => quotes + .Select(x => x.ToCandle()) + .OrderBy(x => x.Timestamp) + .ToList(); +} diff --git a/src/_common/Candles/Candles.cs b/src/_common/Candles/Candles.cs deleted file mode 100644 index bda0ef617..000000000 --- a/src/_common/Candles/Candles.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace Skender.Stock.Indicators; - -public static class Candlesticks -{ - public static IEnumerable Condense( - this IEnumerable candleResults) => candleResults - .Where(candle => candle.Match != Match.None) - .ToList(); - - public static CandleProperties ToCandle( - this TQuote quote) - where TQuote : IQuote => new() { - Date = quote.Date, - Open = quote.Open, - High = quote.High, - Low = quote.Low, - Close = quote.Close, - Volume = quote.Volume - }; - - // convert/sort quotes into candles list - public static IEnumerable ToCandles( - this IEnumerable quotes) - where TQuote : IQuote - { - List candlesList = quotes - .Select(x => x.ToCandle()) - .OrderBy(x => x.Date) - .ToList(); - - // validate - return candlesList; - } - - // convert/sort quotes into candle results - internal static List ToCandleResults( - this IEnumerable quotes) - where TQuote : IQuote - { - List candlesList = quotes - .Select(x => new CandleResult(x.Date) { - Match = Match.None, - Candle = x.ToCandle() - }) - .OrderBy(x => x.Date) - .ToList(); - - // validate - return candlesList; - } -} diff --git a/src/_common/Candles/Models.cs b/src/_common/Candles/Models.cs deleted file mode 100644 index f8faeb996..000000000 --- a/src/_common/Candles/Models.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CANDLESTICK MODELS - -[Serializable] -public class CandleProperties : Quote -{ - // raw sizes - public decimal? Size => High - Low; - public decimal? Body => (Open > Close) ? (Open - Close) : (Close - Open); - public decimal? UpperWick => High - (Open > Close ? Open : Close); - public decimal? LowerWick => (Open > Close ? Close : Open) - Low; - - // percent sizes - public double? BodyPct => (Size != 0) ? (double?)(Body / Size) : 1; - public double? UpperWickPct => (Size != 0) ? (double?)(UpperWick / Size) : 1; - public double? LowerWickPct => (Size != 0) ? (double?)(LowerWick / Size) : 1; - - // directional info - public bool IsBullish => Close > Open; - public bool IsBearish => Close < Open; -} - -[Serializable] -public class CandleResult : ResultBase -{ - public CandleResult(DateTime date) - { - Date = date; - Candle = new CandleProperties(); - } - - public decimal? Price { get; set; } - public Match Match { get; set; } - public CandleProperties Candle { get; set; } -} diff --git a/src/_common/Enums.cs b/src/_common/Enums.cs index ba68801c3..f254baaea 100644 --- a/src/_common/Enums.cs +++ b/src/_common/Enums.cs @@ -1,8 +1,37 @@ namespace Skender.Stock.Indicators; // SHARED ENUMERATIONS -// note: indicator unique ENUMS specified in indicator models +// note: indicator unique ENUMS filed with their models +/// +/// Cache action instruction or outcome +/// +internal enum Act +{ + /// + /// Adds item to end of cache or rebuild if older. + /// + Add, + + /// + /// Does nothing to cache (aborted). + /// + Ignore, + + /// + /// Insert item without rebuilding cache. + /// + Insert, + + /// + /// Reset and rebuild from marker position. + /// + Rebuild +} + +/// +/// Part or value of a quote candle +/// public enum CandlePart { Open, @@ -17,12 +46,18 @@ public enum CandlePart OHLC4 } +/// +/// Candle close or high/low wick values +/// public enum EndType { Close = 0, HighLow = 1 } +/// +/// Candlestick pattern matching type +/// public enum Match { BullConfirmed = 200, @@ -35,6 +70,9 @@ public enum Match BearConfirmed = -200 } +/// +/// Moving average type +/// public enum MaType { ALMA, @@ -50,6 +88,10 @@ public enum MaType WMA } +/// +/// Period size. Usually referring to the +/// time period represented in a quote candle. +/// public enum PeriodSize { Month, @@ -65,11 +107,3 @@ public enum PeriodSize TwoMinutes, OneMinute } - -public enum SyncType -{ - Prepend, - AppendOnly, - RemoveOnly, - FullMatch -} diff --git a/src/_common/Generics/Pruning.cs b/src/_common/Generics/Pruning.cs index 1cdc85e16..997957a91 100644 --- a/src/_common/Generics/Pruning.cs +++ b/src/_common/Generics/Pruning.cs @@ -1,13 +1,12 @@ namespace Skender.Stock.Indicators; // REMOVE AND PRUNING of SERIES -public static class Pruning + +public static partial class Utility { // REMOVE SPECIFIC PERIODS - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable series, + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList series, int removePeriods) => removePeriods < 0 ? throw new ArgumentOutOfRangeException(nameof(removePeriods), removePeriods, @@ -16,7 +15,7 @@ public static IEnumerable RemoveWarmupPeriods( // REMOVE PERIODS internal static List Remove( - this IEnumerable series, + this IReadOnlyList series, int removePeriods) { List seriesList = series.ToList(); @@ -25,17 +24,17 @@ internal static List Remove( { return []; } - else - { - if (removePeriods > 0) - { - for (int i = 0; i < removePeriods; i++) - { - seriesList.RemoveAt(0); - } - } + if (removePeriods <= 0) + { return seriesList; } + + for (int i = 0; i < removePeriods; i++) + { + seriesList.RemoveAt(0); + } + + return seriesList; } } diff --git a/src/_common/Generics/Seek.cs b/src/_common/Generics/Seek.cs index 0b0387b71..8397739af 100644 --- a/src/_common/Generics/Seek.cs +++ b/src/_common/Generics/Seek.cs @@ -4,22 +4,37 @@ namespace Skender.Stock.Indicators; public static class Seeking { - // FIND SERIES by DATE - /// - /// - public static TSeries? Find( - this IEnumerable series, + // FIND by DATE + public static T? Find( + this IReadOnlyList series, DateTime lookupDate) - where TSeries : ISeries => series - .FirstOrDefault(x => x.Date == lookupDate); + where T : ISeries + { + ArgumentNullException.ThrowIfNull(series); - // FIND INDEX by DATE - /// - /// - public static int FindIndex( - this List series, - DateTime lookupDate) - where TSeries : ISeries => series == null - ? -1 - : series.FindIndex(x => x.Date == lookupDate); + int low = 0; + int high = series.Count - 1; + + while (low <= high) + { + int mid = (low + high) >> 1; + DateTime midTimestamp = series[mid].Timestamp; + + if (midTimestamp == lookupDate) + { + return series[mid]; // found + } + else if (midTimestamp < lookupDate) + { + low = mid + 1; + } + else + { + high = mid - 1; + } + } + + // not found + return default; + } } diff --git a/src/_common/Generics/Series.Model.cs b/src/_common/Generics/Series.Model.cs deleted file mode 100644 index 8f0c4e77e..000000000 --- a/src/_common/Generics/Series.Model.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Skender.Stock.Indicators; - -public interface ISeries -{ - public DateTime Date { get; } -} diff --git a/src/_common/Generics/Sorting.cs b/src/_common/Generics/Sorting.cs index 49522ed3e..56ed632c6 100644 --- a/src/_common/Generics/Sorting.cs +++ b/src/_common/Generics/Sorting.cs @@ -1,22 +1,13 @@ -using System.Collections.ObjectModel; - namespace Skender.Stock.Indicators; // SORTED of SERIES -public static class Sorting +public static partial class Utility { - public static Collection ToSortedCollection( - this IEnumerable series) - where TSeries : ISeries - => series - .OrderBy(x => x.Date) - .ToCollection(); - - internal static List ToSortedList( + public static IReadOnlyList ToSortedList( this IEnumerable series) where TSeries : ISeries => series - .OrderBy(x => x.Date) + .OrderBy(x => x.Timestamp) .ToList(); } diff --git a/src/_common/Generics/Transforms.cs b/src/_common/Generics/Transforms.cs index 2e7385dc9..11e6b3ffd 100644 --- a/src/_common/Generics/Transforms.cs +++ b/src/_common/Generics/Transforms.cs @@ -4,15 +4,12 @@ namespace Skender.Stock.Indicators; // GENERIC TRANSFORMS -public static class Transforms +public static partial class Utility { // TO COLLECTION internal static Collection ToCollection(this IEnumerable source) { - if (source is null) - { - throw new ArgumentNullException(nameof(source)); - } + ArgumentNullException.ThrowIfNull(source); Collection collection = [.. source]; diff --git a/src/_common/Generics/info.xml b/src/_common/Generics/info.xml deleted file mode 100644 index 3d2e60d7a..000000000 --- a/src/_common/Generics/info.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - Finds time series values on a specific date. - - See documentation for more information. - - - Any series type. - Time series to evaluate. - Exact date to lookup. - First record in the series on the date specified. - - - - Finds time series index on a specific date. - - See documentation for more information. - - - Any series type. - Time series to evaluate. - Exact date to lookup. - - First index in the series of the date specified or -1 if not found. - - - - Removes a specific quantity from the beginning of the time series list. - - See documentation for more information. - - - Any series type. - Collection to evaluate. - Exact quantity to remove from the beginning of the series. - Time series, pruned. - Invalid parameter value provided. - - \ No newline at end of file diff --git a/src/_common/Globals.cs b/src/_common/Globals.cs index 8d9c8893a..620494b62 100644 --- a/src/_common/Globals.cs +++ b/src/_common/Globals.cs @@ -1,24 +1,5 @@ -using System.Globalization; using System.Runtime.CompilerServices; [assembly: CLSCompliant(true)] -[assembly: InternalsVisibleTo("Tests.Indicators")] +[assembly: InternalsVisibleTo("Tests.Indicators")] // these test internals [assembly: InternalsVisibleTo("Tests.Performance")] - -namespace Skender.Stock.Indicators; - -/// Technical indicators and overlays. See -/// -/// the Guide for more information. -public static partial class Indicator -{ - private static readonly CultureInfo invCulture = CultureInfo.InvariantCulture; - private static readonly Calendar invCalendar = invCulture.Calendar; - - // Gets the DTFI properties required by GetWeekOfYear. - private static readonly CalendarWeekRule invCalendarWeekRule - = invCulture.DateTimeFormat.CalendarWeekRule; - - private static readonly DayOfWeek invFirstDayOfWeek - = invCulture.DateTimeFormat.FirstDayOfWeek; -} diff --git a/src/_common/ISeries.cs b/src/_common/ISeries.cs new file mode 100644 index 000000000..3483eddde --- /dev/null +++ b/src/_common/ISeries.cs @@ -0,0 +1,24 @@ +namespace Skender.Stock.Indicators; + +/// +/// Time-series base interface. +/// +public interface ISeries +{ + // TODO: consider adding (long) UnixDate (seconds) to ISeries + + /// + /// Date/time of record. + /// + /// + /// For types, this is the + /// date/time from the matching aggregate quote period. + /// For types, this is the + /// Close/end date and time of the OHLCV aggregate period. + /// From a practical perspective, Timestamp is the correlation ID. + /// + DateTime Timestamp { get; } + + [Obsolete("Deprecated. Use 'Timestamp' instead.")] + DateTime Date => Timestamp; +} diff --git a/src/_common/Incrementals/IIncremental.cs b/src/_common/Incrementals/IIncremental.cs new file mode 100644 index 000000000..90de40106 --- /dev/null +++ b/src/_common/Incrementals/IIncremental.cs @@ -0,0 +1,52 @@ +namespace Skender.Stock.Indicators; + +public interface IAddReusable +{ + /// + /// Converts an incremental value into + /// the next incremental indicator value + /// and added it to the list. + /// + /// Date context + /// Next value + void Add(DateTime timestamp, double value); + + /// + /// Converts an incremental reusable value into + /// the next incremental indicator value + /// and added it to the list. + /// + /// Next value + void Add(IReusable value); + + /// + /// Converts batch of reusable values into + /// the next incremental indicator values + /// and added them to the list. + /// + /// + /// Chronologically ordered batch of IReusable info + /// + void Add(IReadOnlyList values); +} + +public interface IAddQuote +{ + /// + /// Converts an incremental quote into + /// the next incremental indicator value + /// and added it to the list. + /// + /// Next quote value + void Add(IQuote quote); + + /// + /// Converts batch of quotes into + /// the next incremental indicator values + /// and added them to the list. + /// + /// + /// Chronologically ordered batch of quotes + /// + void Add(IReadOnlyList quotes); +} diff --git a/src/_common/Math/NullMath.cs b/src/_common/Math/NullMath.cs index 71aad8c1c..a9a197922 100644 --- a/src/_common/Math/NullMath.cs +++ b/src/_common/Math/NullMath.cs @@ -1,23 +1,28 @@ namespace Skender.Stock.Indicators; -// NULLABLE SYSTEM.MATH -// System.Math does not allow or handle null input values. -// Instead of putting a lot of inline defensive code -// we're building nullable equivalents here. +/// +/// Nullable System. functions. +/// +/// +/// System.Math infamously does not allow +/// or handle nullable input values. +/// Instead of adding repetitive inline defensive code, +/// we're using these equivalents. Most are simple wrappers. +/// public static class NullMath { public static double? Abs(this double? value) - => (value is null) + => value is null ? null : value < 0 ? (double)-value : (double)value; public static decimal? Round(this decimal? value, int digits) - => (value is null) + => value is null ? null : Math.Round((decimal)value, digits); public static double? Round(this double? value, int digits) - => (value is null) + => value is null ? null : Math.Round((double)value, digits); @@ -30,8 +35,13 @@ public static decimal Round(this decimal value, int digits) public static double Null2NaN(this double? value) => value ?? double.NaN; + public static double Null2NaN(this decimal? value) + => value is null + ? double.NaN + : (double)value; + public static double? NaN2Null(this double? value) - => (value is not null and double.NaN) + => value is double.NaN ? null : value; diff --git a/src/_common/Math/Numerix.cs b/src/_common/Math/Numerical.cs similarity index 70% rename from src/_common/Math/Numerix.cs rename to src/_common/Math/Numerical.cs index b41698b0b..bba184344 100644 --- a/src/_common/Math/Numerix.cs +++ b/src/_common/Math/Numerical.cs @@ -1,58 +1,50 @@ namespace Skender.Stock.Indicators; -public static class Numerix +public static class Numerical { // STANDARD DEVIATION public static double StdDev(this double[] values) { - // validate parameters - if (values is null) + ArgumentNullException.ThrowIfNull( + values, "StdDev values cannot be null."); + + int n = values.Length; + + if (n <= 1) { - throw new ArgumentNullException(nameof(values), "StdDev values cannot be null."); + return 0; } - double sd = 0; - int n = values.Length; - if (n > 1) + double sum = 0; + + for (int i = 0; i < n; i++) { - double sum = 0; - for (int i = 0; i < n; i++) - { - sum += values[i]; - } - - double avg = sum / n; - - double sumSq = 0; - for (int i = 0; i < n; i++) - { - double v = values[i]; - sumSq += (v - avg) * (v - avg); - } - - sd = Math.Sqrt(sumSq / n); + sum += values[i]; } - return sd; + double avg = sum / n; + + double sumSq = 0; + for (int i = 0; i < n; i++) + { + double v = values[i]; + sumSq += (v - avg) * (v - avg); + } + + return Math.Sqrt(sumSq / n); } // SLOPE of BEST FIT LINE public static double Slope(double[] x, double[] y) { // validate parameters - if (x is null) - { - throw new ArgumentNullException(nameof(x), "Slope X values cannot be null."); - } - - if (y is null) - { - throw new ArgumentNullException(nameof(y), "Slope Y values cannot be null."); - } + ArgumentNullException.ThrowIfNull(x, "Slope X values cannot be null."); + ArgumentNullException.ThrowIfNull(y, "Slope Y values cannot be null."); if (x.Length != y.Length) { - throw new ArgumentException("Slope X and Y arrays must be the same size."); + throw new ArgumentException( + "Slope X and Y arrays must be the same size."); } int length = x.Length; @@ -89,7 +81,8 @@ public static double Slope(double[] x, double[] y) } // DATE ROUNDING - internal static DateTime RoundDown(this DateTime dateTime, TimeSpan interval) + internal static DateTime RoundDown( + this DateTime dateTime, TimeSpan interval) => interval == TimeSpan.Zero ? dateTime : dateTime diff --git a/src/_common/Observables/ChainProvider.cs b/src/_common/Observables/ChainProvider.cs deleted file mode 100644 index 85c2b3af4..000000000 --- a/src/_common/Observables/ChainProvider.cs +++ /dev/null @@ -1,194 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TUPLE OBSERVER and TUPLE PROVIDER (CHAIN STREAM) - -public abstract class ChainProvider - : TupleObserver, IObservable<(DateTime Date, double Value)> -{ - // fields - private readonly List> observers; - - // initialize - protected ChainProvider() - { - observers = []; - ProtectedChain = []; - Warmup = true; - } - - // properties - internal IEnumerable<(DateTime Date, double Value)> Output => ProtectedChain; - - internal List<(DateTime Date, double Value)> ProtectedChain { get; set; } - - private int OverflowCount { get; set; } - - private bool Warmup { get; set; } - - // METHODS - - // subscribe observer - public IDisposable Subscribe(IObserver<(DateTime Date, double Value)> observer) - { - if (!observers.Contains(observer)) - { - observers.Add(observer); - } - - return new Unsubscriber(observers, observer); - } - - // close all observations - public void EndTransmission() - { - foreach (IObserver<(DateTime Date, double Value)> observer in observers.ToArray()) - { - if (observers.Contains(observer)) - { - observer.OnCompleted(); - } - } - - observers.Clear(); - } - - // add one - internal void SendToChain(TResult result) - where TResult : IReusableResult - { - // candidate result - (DateTime Date, double Value) r = new(result.Date, result.Value.Null2NaN()); - - int length = ProtectedChain.Count; - - // initialize - if (length == 0 && result.Value != null) - { - // add new tuple - ProtectedChain.Add(r); - Warmup = false; - - // notify observers - NotifyObservers(r); - return; - } - - // do not proceed until first non-null Value recieved - if (Warmup && result.Value == null) - { - return; - } - else - { - Warmup = false; - } - - (DateTime lastDate, _) = ProtectedChain[length - 1]; - - // add tuple - if (r.Date > lastDate) - { - // add new tuple - ProtectedChain.Add(r); - - // notify observers - NotifyObservers(r); - } - - // same date or tuple recieved - else if (r.Date <= lastDate) - { - // check for overflow condition - // where same tuple continues (possible circular condition) - if (r.Date == lastDate) - { - OverflowCount++; - - if (OverflowCount > 100) - { - string msg = "A repeated Chain update exceeded the 100 attempt threshold. " - + "Check and remove circular chains or check your Chain provider."; - - EndTransmission(); - - throw new OverflowException(msg); - } - } - else - { - OverflowCount = 0; - } - - // seek old tuple - int foundIndex = ProtectedChain - .FindIndex(x => x.Date == r.Date); - - // found - if (foundIndex >= 0) - { - ProtectedChain[foundIndex] = r; - } - - // add missing tuple - else - { - ProtectedChain.Add(r); - - // re-sort cache - ProtectedChain = ProtectedChain - .ToSortedList(); - } - - // let observer handle old + duplicates - NotifyObservers(r); - } - } - - // add many - internal void SendToChain(IEnumerable results) - where TResult : IReusableResult - { - List added = results - .ToSortedList(); - - for (int i = 0; i < added.Count; i++) - { - SendToChain(added[i]); - } - } - - // notify observers - private void NotifyObservers((DateTime Date, double Value) tuple) - { - List> obsList = observers.ToList(); - - for (int i = 0; i < obsList.Count; i++) - { - IObserver<(DateTime Date, double Value)> obs = obsList[i]; - obs.OnNext(tuple); - } - } - - // unsubscriber - private class Unsubscriber : IDisposable - { - private readonly List> observers; - private readonly IObserver<(DateTime Date, double Value)> observer; - - // identify and save observer - public Unsubscriber(List> observers, IObserver<(DateTime Date, double Value)> observer) - { - this.observers = observers; - this.observer = observer; - } - - // remove single observer - public void Dispose() - { - if (observer != null && observers.Contains(observer)) - { - observers.Remove(observer); - } - } - } -} diff --git a/src/_common/Observables/IStreamHub.cs b/src/_common/Observables/IStreamHub.cs new file mode 100644 index 000000000..f73b1250d --- /dev/null +++ b/src/_common/Observables/IStreamHub.cs @@ -0,0 +1,128 @@ +namespace Skender.Stock.Indicators; + +// STREAM HUB INTERFACE + +/// +/// Streaming hub: management of observer +/// and observable indicator data +/// +/// +/// Type of inbound provider data. +/// +/// +/// Type of outbound indicator data. +/// +public interface IStreamHub + where TIn : ISeries +{ + /// + /// Read-only list of the stored cache values. + /// + IReadOnlyList Results { get; } + + /// + /// The cache and provider failed and is no longer operational. + /// + /// + /// This occurs when there is an overflow condition + /// from a circular chain or + /// when there were too many sequential duplicates. + /// + /// Use + /// to remove this flag. + /// + /// + bool IsFaulted { get; } + + /// + /// Resets the flag and + /// overflow counter. Use this after recovering + /// from an error. + /// + /// + /// You may also need to + /// , or + /// . + /// + void ResetFault(); + + /// + /// Add a single new item. + /// We'll determine if it's new or an update. + /// + /// + /// New item to add + /// + void Add(TIn newIn); + + /// + /// Add a batch of new items. + /// We'll determine if they're new or updated. + /// + /// + /// Batch of new items to add + /// + void Add(IEnumerable batchIn); + + /// + /// Insert a new item without rebuilding the cache. + /// + /// + /// This is used in situations when inserting an older item + /// and where newer cache entries do not need to be rebuilt. + /// Typically, this is only used for provider-only hubs. + /// + /// + /// Item to insert + /// + void Insert(TIn newIn); + + /// + /// Delete an item from the cache. + /// + /// Cached item to delete + /// + void Remove(TOut cachedItem); + + /// + /// Delete an item from the cache, from a specific position. + /// + /// Position in cache to delete + /// + void RemoveAt(int cacheIndex); + + /// + /// Deletes newer cached records from point in time (inclusive). + /// + /// + /// For observers, if your intention is to rebuild from a provider, + /// use alternate . + /// + /// + /// All periods (inclusive) after this DateTime will be removed. + /// + /// + /// Notify subscribers of the delete point. + /// + void RemoveRange(DateTime fromTimestamp, bool notify); + + /// + /// Deletes newer cached records from an index position (inclusive). + /// + /// + /// For observers, if your intention is to rebuild from a provider, + /// use alternate . + /// + /// From index, inclusive + /// + /// Notify subscribers of the delete position. + /// + void RemoveRange(int fromIndex, bool notify); + + /// + /// Returns a short text label for the hub + /// with parameter values, e.g. "EMA(10)" + /// + /// String label + string ToString(); +} diff --git a/src/_common/Observables/IStreamObservable.cs b/src/_common/Observables/IStreamObservable.cs new file mode 100644 index 000000000..394678c6f --- /dev/null +++ b/src/_common/Observables/IStreamObservable.cs @@ -0,0 +1,108 @@ +namespace Skender.Stock.Indicators; + +// STREAM (OBSERVABLE) INTERFACE + +#region chain and quote variants + +/// +public interface IQuoteProvider : IChainProvider + where T : IQuote +{ + IReadOnlyList Quotes { get; } +} + +/// +public interface IChainProvider : IStreamObservable + where T : IReusable; +#endregion + +/// +/// Provider of data + management of and notification to observing subscribers. +/// +/// +/// The object that provides notification information. +/// +public interface IStreamObservable +{ + /// + /// Hub observable properties and behaviors. + /// + /// + /// This struct holds cumulative overrides for + /// a streaming hub. Observer hubs inherit these values cumulatively when + /// instantiated as a , except where masked. + /// + /// Default settings are a binary set of 0b00000000, where 1 values represent + /// exceptional (atypical) behaviors. + /// + /// + /// + /// 0 + /// + /// Disable observer: a non-observing observable (e.g. base provider). + /// + /// + /// + /// 1 + /// + /// Allow duplicates: + /// bypass rebuild analysis and duplicate prevention when caching new results. + /// + /// + /// + /// 2-7 + /// [unused positions] + /// + /// + /// + BinarySettings Properties { get; } + + /// + /// Current number of subscribers + /// + int ObserverCount { get; } + + /// + /// Provider currently has subscribers + /// + bool HasObservers { get; } + + /// + /// Checks if a specific observer is subscribed + /// + /// + /// Subscriber IStreamObserver reference + /// + /// True if subscribed/registered + bool HasSubscriber(IStreamObserver observer); + + /// + /// Notifies the provider that an observer is to receive notifications. + /// + /// + /// The object that is to receive notifications. + /// + /// + /// A reference to an interface that allows observers + /// to stop receiving notifications before the provider + /// has finished sending them. + /// + IDisposable Subscribe(IStreamObserver observer); + + /// + /// Unsubscribe from the data provider. + /// + /// + bool Unsubscribe(IStreamObserver observer); + + /// + /// Unsubscribe all observers (subscribers) + /// + void EndTransmission(); + + /// + /// Get a readonly reference of the observable cache. + /// + /// Read-only list of cached items. + IReadOnlyList GetCacheRef(); +} diff --git a/src/_common/Observables/IStreamObserver.cs b/src/_common/Observables/IStreamObserver.cs new file mode 100644 index 000000000..32398e668 --- /dev/null +++ b/src/_common/Observables/IStreamObserver.cs @@ -0,0 +1,117 @@ +namespace Skender.Stock.Indicators; + +// STREAM (OBSERVER) INTERFACE + +/// +/// Management of observing + processing of streamed inbound data. +/// +/// +/// The object that provides notification information. +/// +public interface IStreamObserver +{ + /// + /// Current state of subscription to provider. + /// + bool IsSubscribed { get; } + + /// + /// Unsubscribe from the data provider. + /// + void Unsubscribe(); + + /// + /// Provides the observer with new data. + /// + /// + /// The current notification information. + /// + /// + /// Notify subscribers of the new item. + /// + /// + /// Provider index hint, if known. + /// + void OnAdd(T item, bool notify, int? indexHint); + + /// + /// Provides the observer with starting point in timeline + /// to rebuild and cascade to all its own subscribers. + /// + /// + /// Starting point in timeline to rebuild. + /// + void OnChange(DateTime fromTimestamp); + + /// + /// Provides the observer with errors from the provider + /// that have produced a faulted state. + /// + /// + /// An exception with additional information about the error. + /// + void OnError(Exception exception); + + /// + /// Provides the observer with final notice that the data + /// provider has finished sending push-based notifications. + /// + /// + /// Completion indicates that publisher will never send + /// additional data. This is only used for finite data + /// streams; and is different from faulted OnError(). + /// + void OnCompleted(); + + /// + /// Full reset of the provider subscription. + /// + /// + /// This unsubscribes from the provider, + /// rebuilds the cache, resets faulted states, + /// and then re-subscribes to the provider. + /// + /// This is done automatically on hub + /// instantiation, so it's only needed if you + /// want to manually reset the hub. + /// + /// + /// If you only need to rebuild the cache, + /// use instead. + /// + /// + void Reinitialize(); + + /// + /// Resets the entire results cache + /// and rebuilds it from provider sources, + /// with cascading updates to subscribers. + /// + /// + /// This is different from . + /// It does not reset the provider subscription. + /// + void Rebuild(); + + /// + /// Resets the results cache from a point in time + /// and rebuilds it from provider sources, + /// with cascading updates to subscribers. + /// + /// + /// All periods (inclusive) after this date/time + /// will be removed and recalculated. + /// + void Rebuild(DateTime fromTimestamp); + + /// + /// Resets the results cache from an index position + /// and rebuilds it from provider sources, + /// with cascading updates to subscribers. + /// + /// + /// All periods (inclusive) after this index position + /// will be removed and recalculated. + /// + void Rebuild(int fromIndex); +} diff --git a/src/_common/Observables/QuoteObserver.cs b/src/_common/Observables/QuoteObserver.cs deleted file mode 100644 index 628364594..000000000 --- a/src/_common/Observables/QuoteObserver.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Skender.Stock.Indicators; - -// OBSERVER of QUOTES (BOILERPLATE) - -public abstract class QuoteObserver : IObserver -{ - // fields - private IDisposable? unsubscriber; - - // properites - internal QuoteProvider? Supplier { get; set; } - - // methods - public virtual void Subscribe() - => unsubscriber = Supplier != null - ? Supplier.Subscribe(this) - : throw new ArgumentNullException(nameof(Supplier)); - - public virtual void OnCompleted() => Unsubscribe(); - - public virtual void OnError(Exception error) => throw error; - - public virtual void OnNext(Quote value) - { - // » handle new quote with override in observer - } - - public virtual void Unsubscribe() => unsubscriber?.Dispose(); -} diff --git a/src/_common/Observables/QuoteProvider.cs b/src/_common/Observables/QuoteProvider.cs deleted file mode 100644 index 9f55669fb..000000000 --- a/src/_common/Observables/QuoteProvider.cs +++ /dev/null @@ -1,186 +0,0 @@ -namespace Skender.Stock.Indicators; - -// QUOTES as PROVIDER - -public class QuoteProvider : IObservable -{ - // fields - private readonly List> observers; - - // initialize - public QuoteProvider() - { - observers = []; - ProtectedQuotes = []; - } - - // properties - public IEnumerable Quotes => ProtectedQuotes; - - internal List ProtectedQuotes { get; private set; } - - private int OverflowCount { get; set; } - - // METHODS - - // add one - public void Add(Quote quote) - { - // validate quote - if (quote == null) - { - throw new ArgumentNullException(nameof(quote), "Quote cannot be null."); - } - - int length = ProtectedQuotes.Count; - - if (length == 0) - { - // add new quote - ProtectedQuotes.Add(quote); - - // notify observers - NotifyObservers(quote); - - return; - } - - Quote last = ProtectedQuotes[length - 1]; - - // add quote - if (quote.Date > last.Date) - { - // add new quote - ProtectedQuotes.Add(quote); - - // notify observers - NotifyObservers(quote); - } - - // same date or quote recieved - else if (quote.Date <= last.Date) - { - // check for overflow condition - // where same quote continues (possible circular condition) - if (quote.Date == last.Date) - { - OverflowCount++; - - if (OverflowCount > 100) - { - string msg = "A repeated Quote update exceeded the 100 attempt threshold. " - + "Check and remove circular chains or check your Quote provider."; - - EndTransmission(); - - throw new OverflowException(msg); - } - } - else - { - OverflowCount = 0; - } - - // seek old quote - int foundIndex = ProtectedQuotes - .FindIndex(x => x.Date == quote.Date); - - // found - if (foundIndex >= 0) - { - Quote old = ProtectedQuotes[foundIndex]; - - old.Open = quote.Open; - old.High = quote.High; - old.Low = quote.Low; - old.Close = quote.Close; - old.Volume = quote.Volume; - } - - // add missing quote - else - { - ProtectedQuotes.Add(quote); - - // re-sort cache - ProtectedQuotes = ProtectedQuotes - .ToSortedList(); - } - - // let observer handle old + duplicates - NotifyObservers(quote); - } - } - - // add many - public void Add(IEnumerable quotes) - { - List added = quotes - .ToSortedList(); - - for (int i = 0; i < added.Count; i++) - { - Add(added[i]); - } - } - - // subscribe observer - public IDisposable Subscribe(IObserver observer) - { - if (!observers.Contains(observer)) - { - observers.Add(observer); - } - - return new Unsubscriber(observers, observer); - } - - // close all observations - public void EndTransmission() - { - foreach (IObserver observer in observers.ToArray()) - { - if (observers.Contains(observer)) - { - observer.OnCompleted(); - } - } - - observers.Clear(); - } - - // notify observers - private void NotifyObservers(Quote quote) - { - List> obsList = observers.ToList(); - - for (int i = 0; i < obsList.Count; i++) - { - IObserver obs = obsList[i]; - obs.OnNext(quote); - } - } - - // unsubscriber - private class Unsubscriber : IDisposable - { - private readonly List> observers; - private readonly IObserver observer; - - // identify and save observer - public Unsubscriber(List> observers, IObserver observer) - { - this.observers = observers; - this.observer = observer; - } - - // remove single observer - public void Dispose() - { - if (observer != null && observers.Contains(observer)) - { - observers.Remove(observer); - } - } - } -} diff --git a/src/_common/Observables/StreamHub.Observable.cs b/src/_common/Observables/StreamHub.Observable.cs new file mode 100644 index 000000000..5f6ec2269 --- /dev/null +++ b/src/_common/Observables/StreamHub.Observable.cs @@ -0,0 +1,119 @@ +namespace Skender.Stock.Indicators; + +// STREAM HUB (OBSERVABLE) + +public abstract partial class StreamHub : IStreamObservable +{ + private readonly HashSet> _observers = new(); + + /// + public bool HasObservers => _observers.Count > 0; + + /// + public int ObserverCount => _observers.Count; + + /// + public IReadOnlyList ReadCache => Cache; + + /// + public virtual BinarySettings Properties { get; init; } = new(0); // default 0b00000000 + + #region SUBSCRIPTION SERVICES + + /// + public IDisposable Subscribe(IStreamObserver observer) + { + _observers.Add(observer); + return new Unsubscriber(_observers, observer); + } + + /// + public bool Unsubscribe(IStreamObserver observer) + => _observers.Remove(observer); + + /// + public bool HasSubscriber(IStreamObserver observer) + => _observers.Contains(observer); + + /// + /// A disposable subscription to the stream provider. + /// Unsubscribed with + /// + /// + /// Registry of all subscribers (by ref) + /// + /// + /// Your unique subscription as provided. + /// + private class Unsubscriber( + ISet> observers, + IStreamObserver observer) : IDisposable + { + private readonly ISet> _observers = observers; + private readonly IStreamObserver _observer = observer; + + /// + /// Remove single observer. + /// + public void Dispose() => _observers.Remove(_observer); + } + + /// + public void EndTransmission() + { + foreach (IStreamObserver observer + in _observers.ToArray()) + { + if (_observers.Contains(observer)) + { + // subscriber removes itself + observer.OnCompleted(); + } + } + + _observers.Clear(); + } + #endregion + + #region SUBSCRIBER NOTIFICATIONS + + /// + /// Sends new TSeries item to subscribers. + /// + /// TSeries item to send. + /// Provider index hint. + private void NotifyObserversOnAdd(TOut item, int? indexHint) + { + // send to subscribers + foreach (IStreamObserver o in _observers.ToArray()) + { + o.OnAdd(item, notify: true, indexHint); + } + } + + /// + /// Sends rebuilds point in time to all subscribers. + /// + /// Rebuild starting positions. + private void NotifyObserversOnChange(DateTime fromTimestamp) + { + foreach (IStreamObserver o in _observers.ToArray()) + { + o.OnChange(fromTimestamp); + } + } + + /// + /// Sends error (exception) to all subscribers. + /// + /// The exception to send. + private void NotifyObserversOnError(Exception exception) + { + // send to subscribers + foreach (IStreamObserver o in _observers.ToArray()) + { + o.OnError(exception); + } + } + #endregion +} diff --git a/src/_common/Observables/StreamHub.Observer.cs b/src/_common/Observables/StreamHub.Observer.cs new file mode 100644 index 000000000..c2b374336 --- /dev/null +++ b/src/_common/Observables/StreamHub.Observer.cs @@ -0,0 +1,66 @@ +namespace Skender.Stock.Indicators; + +// STREAM HUB (OBSERVER) + +public abstract partial class StreamHub : IStreamObserver +{ + /// + public bool IsSubscribed => Provider.HasSubscriber(this); + + /// + /// Data provider that this observer subscribes to. + /// + protected IStreamObservable Provider { get; init; } + + /// + /// Subscription token for managing the subscription lifecycle. + /// + private IDisposable? Subscription { get; set; } + + /// + /// Lock object to ensure thread safety during unsubscription. + /// + private readonly object _unsubscribeLock = new(); + + // Observer methods + + /// + public virtual void OnAdd(TIn item, bool notify, int? indexHint) + { + // Convert the input item to the output type and append it to the cache. + // Override this method if the input and output types are not indexed 1:1. + + (TOut result, int _) = ToIndicator(item, indexHint); // TODO: make this return array, loop appendation? + AppendCache(result, notify); + } + + /// + public void OnChange(DateTime fromTimestamp) + => Rebuild(fromTimestamp); + + /// + public void OnError(Exception exception) + => throw exception; + + /// + public void OnCompleted() + => Unsubscribe(); + + /// + public void Unsubscribe() + { + // Ensure thread-safety for EndTransmission > OnCompleted-type race conditions + // see https://learn.microsoft.com/en-us/dotnet/standard/events/observer-design-pattern-best-practices + + lock (_unsubscribeLock) + { + if (IsSubscribed) + { + Provider.Unsubscribe(this); + } + + Subscription?.Dispose(); + Subscription = null; // ensure the ref is cleared + } + } +} diff --git a/src/_common/Observables/StreamHub.Utilities.cs b/src/_common/Observables/StreamHub.Utilities.cs new file mode 100644 index 000000000..49b1c528c --- /dev/null +++ b/src/_common/Observables/StreamHub.Utilities.cs @@ -0,0 +1,201 @@ +namespace Skender.Stock.Indicators; + +// STREAM HUB (STATIC UTILITIES) + +public static class StreamHub +{ + /// + /// Try to find index position of the provided timestamp + /// + /// + /// Timestamp to seek + /// + /// Index of timestamp or -1 when not found + /// + /// True if found + internal static bool TryFindIndex( + this IReadOnlyList cache, + DateTime timestamp, + out int index) + where T : ISeries + { + index = cache.GetIndex(timestamp, false); + return index != -1; + } + + /// + /// Get the cache index based on item equality. + /// + /// + /// + /// Time-series object to find in cache + /// + /// + /// Throw exception when item is not found + /// + /// Index position + /// + /// When items is not found (should never happen). + /// + internal static int GetIndex( + this IReadOnlyList cache, + T cachedItem, + bool throwOnFail) + where T : ISeries + { + int low = 0; + int high = cache.Count - 1; + int firstMatchIndex = -1; + DateTime targetTimestamp = cachedItem.Timestamp; + + while (low <= high) + { + int mid = (low + high) >> 1; + int comparison = cache[mid].Timestamp.CompareTo(targetTimestamp); + + if (comparison == 0) + { + // Found a match by Timestamp, + // store the index of the first match + if (firstMatchIndex == -1) + { + firstMatchIndex = mid; + } + + // Verify with Equals for an exact match + if (cache[mid].Equals(cachedItem)) + { + return mid; // exact match found + } + + high = mid - 1; // continue searching to the left + } + else if (comparison < 0) + { + low = mid + 1; + } + else + { + high = mid - 1; + } + } + + // If a timestamp match was found but no exact + // match, try to find an exact match in the range + // of duplicate timestamps (e.g. Renko bricks), + // biased towards later duplicates. + if (firstMatchIndex != -1) + { + // Find the last occurrence of the matching timestamp + for (int i = cache.Count - 1; i >= firstMatchIndex; i--) + { + if (cache[i].Timestamp == targetTimestamp + && cache[i].Equals(cachedItem)) + { + return i; // exact match found among duplicates + } + } + } + + // not found + return throwOnFail + ? throw new ArgumentException( + "Matching source history not found.", nameof(cachedItem)) + : -1; + } + + /// + /// Get the cache index based on a timestamp. + /// + /// + /// Only use this when you are looking for a point in time + /// without a matching item for context. In most cases + /// is more appropriate. + /// + /// + /// + /// Timestamp of cached item + /// + /// + /// Throw exception when timestamp is not found + /// + /// Index position + /// + /// When timestamp is not found (should never happen). + /// + internal static int GetIndex( + this IReadOnlyList cache, + DateTime timestamp, + bool throwOnFail) + where T : ISeries + { + int low = 0; + int high = cache.Count - 1; + + while (low <= high) + { + int mid = (low + high) >> 1; + DateTime midTimestamp = cache[mid].Timestamp; + + if (midTimestamp == timestamp) + { + return mid; + } + else if (midTimestamp < timestamp) + { + low = mid + 1; + } + else + { + high = mid - 1; + } + } + + // not found + return throwOnFail + ? throw new ArgumentException( + "Matching source history not found.", nameof(timestamp)) + : -1; + } + + /// + /// Get the first cache index on or after a timestamp. + /// + /// + /// Only use this when you are looking for a point in time + /// without a matching item for context. In most cases + /// is more appropriate. + /// + /// + /// + /// Timestamp of cached item + /// + /// First index position or -1 if not found + internal static int GetIndexGte( + this IReadOnlyList cache, + DateTime timestamp) + where T : ISeries + { + int low = 0; + int high = cache.Count; + while (low < high) + { + int mid = low + ((high - low) / 2); + if (cache[mid].Timestamp < timestamp) + { + low = mid + 1; + } + else + { + high = mid; + } + } + + // At this point, low is the index of the first + // element that is greater than or equal to timestamp + // or Cache.Count if all elements are less than timestamp. + // If low is equal to Cache.Count, it means there are + // no elements greater than or equal to timestamp. + return low < cache.Count ? low : -1; + } +} diff --git a/src/_common/Observables/StreamHub.cs b/src/_common/Observables/StreamHub.cs new file mode 100644 index 000000000..53c6302f4 --- /dev/null +++ b/src/_common/Observables/StreamHub.cs @@ -0,0 +1,408 @@ +namespace Skender.Stock.Indicators; + +// STREAM HUB (BASE/CACHE) + +/// +public abstract partial class StreamHub : IStreamHub + where TIn : ISeries + where TOut : ISeries +{ + #region constructor + + /// + /// Streaming data provider + /// + private protected StreamHub(IStreamObservable provider) + { + // store provider reference + Provider = provider; + + // set provider cache reference + ProviderCache = provider.GetCacheRef(); + + // inherit settings (reinstantiate struct on heap) + Properties = Properties.Combine(provider.Properties); + } + + #endregion + + #region PROPERTIES + + /// + public IReadOnlyList Results => Cache; + + /// + public bool IsFaulted { get; private set; } + + /// + /// Cache of stored values (base). + /// + internal List Cache { get; } = new(); + + /// + /// Current count of repeated caching attempts. + /// An overflow condition is triggered after 100. + /// + internal byte OverflowCount { get; private set; } + + /// + /// Reference to this hub's provider's cache. + /// + protected IReadOnlyList ProviderCache { get; } + + /// + /// Most recent item saved to cache. + /// + private TOut? LastItem { get; set; } + + #endregion + + // reset fault flag and condition + /// + public void ResetFault() + { + OverflowCount = 0; + IsFaulted = false; + } + + // fetch cache reference + /// + public IReadOnlyList GetCacheRef() => Cache; + + public abstract override string ToString(); + + /// + /// Converts incremental value into + /// an indicator candidate and cache position. + /// + /// New item from provider + /// Provider index hint + /// Cacheable item candidate and index hint + protected abstract (TOut result, int index) + ToIndicator(TIn item, int? indexHint); + + #region ADD & ANALYZE + + public void Add(TIn newIn) + => OnAdd(newIn, notify: true, null); + + public void Add(IEnumerable batchIn) + { + foreach (TIn newIn in batchIn.OrderBy(x => x.Timestamp)) + { + OnAdd(newIn, notify: true, null); + } + } + + public void Insert(TIn newIn) + { + // note: should only be used when newer timestamps + // are not impacted by the insertion of an older item + + // generate candidate result + (TOut result, int index) = ToIndicator(newIn, null); + + // insert, then rebuild observers (no self-rebuild) + if (index > 0) + { + // check overflow/duplicates + if (IsOverflowing(result)) + { + return; // duplicate found + } + + Cache.Insert(index, result); + NotifyObserversOnChange(result.Timestamp); + } + + // normal add + else + { + AppendCache(result, notify: true); + } + } + + /// + /// Perform appropriate caching action after analysis. + /// It will add if new, ignore if duplicate, or rebuild if late-arrival. + /// + /// TSeries item to cache. + /// + /// Notify subscribers of change (send to observers). + /// This is disabled for bulk operations like rebuild. + /// + protected void AppendCache(TOut result, bool notify) + { + // check overflow/duplicates + if (IsOverflowing(result)) + { + return; + } + + bool bypassRebuild = Properties[1]; // forced add/caching w/o rebuild + + // consider timeline + Act act = bypassRebuild || Cache.Count == 0 || result.Timestamp > Cache[^1].Timestamp + ? Act.Add + : Act.Rebuild; + + // fulfill action + switch (act) + { + // add to cache + case Act.Add: + Add(result, notify); + break; + + // rebuild cache + case Act.Rebuild: + Rebuild(result.Timestamp); + break; + + // would never happen + default: + throw new InvalidOperationException(); + } + } + + /// + /// Add item to cache and notify observers. + /// + /// Item to add to end of cache + /// Inherited notification instructions. + private void Add(TOut item, bool notify) + { + // notes: + // 1. Should only be called from AppendCache() + // 2. Notify has to be disabled for bulk operations, like rebuild. + // 3. Forced caching (rebuild analysis bypass) is inherited property. + + // add to cache + Cache.Add(item); + IsFaulted = false; + + // notify subscribers + if (notify) + { + NotifyObserversOnAdd(item, Cache.Count - 1); + } + } + + /// + /// Validate outbound item and compare to prior cached item, + /// to gracefully manage and prevent overflow conditions. + /// + /// Cacheable time-series object + /// + /// True if item is repeating and duplicate was suppressed. + /// + /// + /// Too many sequential duplicates were detected. + /// + private bool IsOverflowing(TOut item) + { + // skip first arrival + if (LastItem is null) + { + LastItem = item; + return false; + } + + // track/check for overflow condition + if (item.Timestamp == LastItem.Timestamp && item.Equals(LastItem)) + { + // ^^ using progressive check to avoid Equals() on every item + + OverflowCount++; + + // handle overflow + if (OverflowCount > 100) + { + const string msg = """ + A repeated stream update exceeded the 100 attempt threshold. + Check and remove circular chains or check your stream provider. + Provider terminated. + """; + + IsFaulted = true; + + // emit error + OverflowException exception = new(msg); + NotifyObserversOnError(exception); + throw exception; + } + + // bypass duplicate prevention + // when forced caching is enabled + if (Properties[1]) + { + return false; + + // note: will still overflow + // when the 100 limit is reached + } + + return true; + } + + // not repeating + OverflowCount = 0; + LastItem = item; + return false; + } + #endregion + + #region REMOVE & REMOVE RANGE + + /// remove cached item + /// + public void Remove(TOut cachedItem) + { + Cache.Remove(cachedItem); + NotifyObserversOnChange(cachedItem.Timestamp); + } + + /// remove cached item at index position + /// + public void RemoveAt(int cacheIndex) + { + TOut cachedItem = Cache[cacheIndex]; + Cache.RemoveAt(cacheIndex); + NotifyObserversOnChange(cachedItem.Timestamp); + } + + /// remove cache range from timestamp + /// + public void RemoveRange(DateTime fromTimestamp, bool notify) + { + // rollback internal state + RollbackState(fromTimestamp); + + // remove cache entries + Cache.RemoveAll(c => c.Timestamp >= fromTimestamp); + + // notify observers + if (notify) + { + NotifyObserversOnChange(fromTimestamp); + } + } + + /// remove cache range from index + /// + public void RemoveRange(int fromIndex, bool notify) + { + // nothing to do + if (Cache.Count == 0 || fromIndex >= Cache.Count) + { + return; + } + + // remove cache entries + DateTime fromTimestamp = fromIndex <= 0 + ? DateTime.MinValue + : Cache[fromIndex].Timestamp; + + RemoveRange(fromTimestamp, notify); + } + #endregion + + #region REBUILD & REINITIALIZE + + // full reset + /// + public void Reinitialize() + { + Unsubscribe(); + ResetFault(); + Rebuild(); + Subscription = Provider.Subscribe(this); + + // TODO: make reinitialization abstract, + // and build initial Cache from faster static method + + // TODO: evaluate race condition between rebuild + // and subscribe; will it miss any high frequency data? + } + + // rebuild cache + /// + public void Rebuild() + => Rebuild(DateTime.MinValue); + + // rebuild cache from timestamp + /// + public void Rebuild(DateTime fromTimestamp) + { + // clear cache + RemoveRange(fromTimestamp, notify: false); + + // get provider position + int provIndex = ProviderCache.GetIndexGte(fromTimestamp); + + // rebuild + if (provIndex >= 0) + { + for (int i = provIndex; i < ProviderCache.Count; i++) + { + OnAdd(ProviderCache[i], notify: false, i); + } + } + + // notify observers + NotifyObserversOnChange(fromTimestamp); + } + + // rebuild cache from index + /// + public void Rebuild(int fromIndex) + { + // find timestamp + DateTime fromTimestamp = fromIndex <= 0 || Cache.Count == 0 + ? DateTime.MinValue + : Cache[fromIndex].Timestamp; + + // rebuild & notify + Rebuild(fromTimestamp); + } + + /// + /// Rollback internal state to a point in time. + /// Behavior varies by indicator. + /// + /// + /// Override when indicator needs to rollback state to a + /// point in time (e.g. when rebuilding cache). Example: + /// + /// + /// + /// Point in time to restore. + /// + protected virtual void RollbackState(DateTime timestamp) + { + // note: override when rollback is needed + // default: do nothing + // see AtrStopHub() for example + } + #endregion +} + +#region chain and quote variants + +/// +public abstract class QuoteProvider( + IStreamObservable provider +) : StreamHub(provider), IQuoteProvider + where TIn : IReusable + where TOut : IQuote +{ + public IReadOnlyList Quotes => Cache; +}; + +/// +public abstract class ChainProvider( + IStreamObservable provider +) : StreamHub(provider), IChainProvider + where TIn : IReusable + where TOut : IReusable; +#endregion diff --git a/src/_common/Observables/TupleObserver.cs b/src/_common/Observables/TupleObserver.cs deleted file mode 100644 index ae6809f85..000000000 --- a/src/_common/Observables/TupleObserver.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Skender.Stock.Indicators; - -// OBSERVER of TUPLES (BOILERPLATE) - -public abstract class TupleObserver : IObserver<(DateTime Date, double Value)> -{ - // fields - private IDisposable? unsubscriber; - - // properites - internal TupleProvider? Supplier { get; set; } - - // methods - public virtual void Subscribe() - => unsubscriber = Supplier != null - ? Supplier.Subscribe(this) - : throw new ArgumentNullException(nameof(Supplier)); - - public virtual void OnCompleted() => Unsubscribe(); - - public virtual void OnError(Exception error) => throw error; - - public virtual void OnNext((DateTime Date, double Value) value) - { - // » handle new quote with override in observer - } - - public virtual void Unsubscribe() => unsubscriber?.Dispose(); -} diff --git a/src/_common/Observables/TupleProvider.cs b/src/_common/Observables/TupleProvider.cs deleted file mode 100644 index 3304496c6..000000000 --- a/src/_common/Observables/TupleProvider.cs +++ /dev/null @@ -1,174 +0,0 @@ -namespace Skender.Stock.Indicators; - -// QUOTE OBSERVER and TUPLE PROVIDER - -public abstract class TupleProvider - : QuoteObserver, IObservable<(DateTime Date, double Value)> -{ - // fields - private readonly List> observers; - - // initialize - protected TupleProvider() - { - observers = []; - ProtectedTuples = []; - } - - // properties - internal IEnumerable<(DateTime Date, double Value)> Output => ProtectedTuples; - - internal List<(DateTime Date, double Value)> ProtectedTuples { get; set; } - - private int OverflowCount { get; set; } - - // METHODS - - // subscribe observer - public IDisposable Subscribe(IObserver<(DateTime Date, double Value)> observer) - { - if (!observers.Contains(observer)) - { - observers.Add(observer); - } - - return new Unsubscriber(observers, observer); - } - - // close all observations - public void EndTransmission() - { - foreach (IObserver<(DateTime Date, double Value)> observer in observers.ToArray()) - { - if (observers.Contains(observer)) - { - observer.OnCompleted(); - } - } - - observers.Clear(); - } - - // add one - internal void AddSend((DateTime Date, double Value) tuple) - { - int length = ProtectedTuples.Count; - - if (length == 0) - { - // add new tuple - ProtectedTuples.Add(tuple); - - // notify observers - NotifyObservers(tuple); - return; - } - - (DateTime lastDate, _) = ProtectedTuples[length - 1]; - - // add tuple - if (tuple.Date > lastDate) - { - // add new tuple - ProtectedTuples.Add(tuple); - - // notify observers - NotifyObservers(tuple); - } - - // same date or tuple recieved - else if (tuple.Date <= lastDate) - { - // check for overflow condition - // where same tuple continues (possible circular condition) - if (tuple.Date == lastDate) - { - OverflowCount++; - - if (OverflowCount > 100) - { - string msg = "A repeated Tuple update exceeded the 100 attempt threshold. " - + "Check and remove circular chains or check your Tuple provider."; - - EndTransmission(); - - throw new OverflowException(msg); - } - } - else - { - OverflowCount = 0; - } - - // seek old tuple - int foundIndex = ProtectedTuples - .FindIndex(x => x.Date == tuple.Date); - - // found - if (foundIndex >= 0) - { - ProtectedTuples[foundIndex] = tuple; - } - - // add missing tuple - else - { - ProtectedTuples.Add(tuple); - - // re-sort cache - ProtectedTuples = ProtectedTuples - .ToSortedList(); - } - - // let observer handle old + duplicates - NotifyObservers(tuple); - } - } - - // add many - internal void AddSend(IEnumerable<(DateTime Date, double Value)> tuples) - { - List<(DateTime Date, double Value)> added = tuples - .ToSortedList(); - - for (int i = 0; i < added.Count; i++) - { - AddSend(added[i]); - } - } - - // notify observers - private void NotifyObservers((DateTime Date, double Value) tuple) - { - List> obsList = observers.ToList(); - - for (int i = 0; i < obsList.Count; i++) - { - IObserver<(DateTime Date, double Value)> obs = obsList[i]; - obs.OnNext(tuple); - } - } - - // unsubscriber - private class Unsubscriber : IDisposable - { - private readonly List> observers; - private readonly IObserver<(DateTime Date, double Value)> observer; - - // identify and save observer - public Unsubscriber(List> observers, IObserver<(DateTime Date, double Value)> observer) - { - this.observers = observers; - this.observer = observer; - } - - // remove single observer - public void Dispose() - { - if (observer != null && observers.Contains(observer)) - { - observers.Remove(observer); - } - } - } -} diff --git a/src/_common/ObsoleteV2.cs b/src/_common/ObsoleteV2.cs deleted file mode 100644 index 15a6dca2f..000000000 --- a/src/_common/ObsoleteV2.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; - -namespace Skender.Stock.Indicators; - -// OBSOLETE IN v2.0.0 -public static partial class Indicator -{ -#pragma warning disable CA1002 // Do not expose generic lists - - // 2.4.1 - [ExcludeFromCodeCoverage] - [Obsolete("Rename 'ToBasicTuple(..)' to 'ToTuple(..)' to fix.", false)] - public static List<(DateTime, double)> ToBasicTuple( - this IEnumerable quotes, - CandlePart candlePart) - where TQuote : IQuote - => quotes.ToTuple(candlePart); - - [ExcludeFromCodeCoverage] - [Obsolete("Rename 'ToResultTuple(..)' to 'ToTuple(..)' to fix.", false)] - public static List<(DateTime Date, double Value)> ToResultTuple( - this IEnumerable basicData) - => basicData.ToTuple(); - - // v2.4.8 - [ExcludeFromCodeCoverage] - [Obsolete("Rename 'ToTupleCollection(..)' to 'ToTupleChainable(..)' to fix.", false)] - public static Collection<(DateTime Date, double Value)> ToTupleCollection( - this IEnumerable reusable) - => reusable - .ToTupleChainable(); - - [ExcludeFromCodeCoverage] - [Obsolete("Rename 'ToTupleCollection(NullTo..)' to either 'ToTupleNaN(..)' or 'ToTupleNull(..)' to fix.", false)] - public static Collection<(DateTime Date, double? Value)> ToTupleCollection( - this IEnumerable reusable, NullTo nullTo) - { - List reList = reusable.ToSortedList(); - int length = reList.Count; - - Collection<(DateTime Date, double? Value)> results = []; - - for (int i = 0; i < length; i++) - { - IReusableResult r = reList[i]; - results.Add(new(r.Date, r.Value.Null2NaN())); - } - - return results; - } - - // v2.4.10 - [ExcludeFromCodeCoverage] - [Obsolete("Change 'GetStarcBands()' to 'GetStarcBands(20)' to fix.", false)] - public static IEnumerable GetStarcBands( - this IEnumerable quotes) - where TQuote : IQuote - => quotes.GetStarcBands(20); - -#pragma warning restore CA1002 // Do not expose generic lists -} - -// v2.4.8 (see above) -public enum NullTo -{ - NaN, - Null -} diff --git a/src/_common/ObsoleteV3.cs b/src/_common/ObsoleteV3.cs new file mode 100644 index 000000000..c0a2a245e --- /dev/null +++ b/src/_common/ObsoleteV3.cs @@ -0,0 +1,134 @@ +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; + +// ReSharper disable all + +namespace Skender.Stock.Indicators; + +// OBSOLETE IN v3 +public static partial class Indicator +{ + // GENERAL INDICATOR METHODS + + [ExcludeFromCodeCoverage] + [Obsolete("Use alternate 'ToAlligator' variant. Tuple arguments were removed.", false)] // v3.0.0 + public static IEnumerable GetAlligator( + this IEnumerable<(DateTime d, double v)> priceTuples, + int jawPeriods = 13, + int jawOffset = 8, + int teethPeriods = 8, + int teethOffset = 5, + int lipsPeriods = 5, + int lipsOffset = 3) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToList() + .ToAlligator( + jawPeriods, jawOffset, + teethPeriods, teethOffset, + lipsPeriods, lipsOffset); + + [ExcludeFromCodeCoverage] + [Obsolete("Replace `GetEma(..)` with `ToEma(..)`", false)] // v3.0.0 + public static IEnumerable GetEma( + this IReadOnlyList quotes, int lookbackPeriods) + where TQuote : IQuote + => quotes.ToSortedList().ToEma(lookbackPeriods); + + // REMOVAL OF INTEGRATED SMAs (evaluates to ERRORs) + + [ExcludeFromCodeCoverage] + [Obsolete("Use a chained `results.ToSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 + public static IEnumerable GetAdl( + this IReadOnlyList quotes, int smaPeriods) + where TQuote : IQuote + => quotes.ToSortedList().ToAdl(); + + [ExcludeFromCodeCoverage] + [Obsolete("Use a chained `results.ToSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 + public static IEnumerable GetObv( + this IReadOnlyList quotes, int smaPeriods) + where TQuote : IQuote + => quotes.ToObv(); + + [ExcludeFromCodeCoverage] + [Obsolete("Use a chained `results.ToSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 + public static IEnumerable GetPrs( + this IEnumerable quotesEval, IEnumerable quotesBase, int lookbackPeriods, int smaPeriods) + where TQuote : IQuote + => quotesEval + .ToSortedList() + .Use(CandlePart.Close) + .ToPrs( + quotesBase.ToSortedList() + .Use(CandlePart.Close), lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use a chained `results.ToSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 + public static IEnumerable GetRoc( + this IReadOnlyList quotes, int lookbackPeriods, int smaPeriods) + where TQuote : IQuote + => quotes.Use(CandlePart.Close).ToRoc(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use a chained `results.ToSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 + public static IEnumerable GetStdDev( + this IReadOnlyList quotes, int lookbackPeriods, int smaPeriods) + where TQuote : IQuote + => quotes.Use(CandlePart.Close).ToStdDev(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use a chained `results.ToSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 + public static IEnumerable GetTrix( + this IReadOnlyList quotes, int lookbackPeriods, int smaPeriods) + where TQuote : IQuote + => quotes.Use(CandlePart.Close).ToTrix(lookbackPeriods); + + // UTILITIES + + [ExcludeFromCodeCoverage] + [Obsolete("This method no longer defaults to Close. Rename Use() to Use(CandlePart.Close) for an explicit conversion.", false)] // v3.0.0 + public static IEnumerable<(DateTime Timestamp, double Value)> Use( + this IReadOnlyList quotes) + where TQuote : IQuote + => quotes.Select(x => (x.Timestamp, x.Value)); + + [ExcludeFromCodeCoverage] + [Obsolete("Refactor to use `ToSortedList()`", true)] // v3.0.0 + public static Collection ToSortedCollection( + this IReadOnlyList series) + where TSeries : ISeries + => series + .OrderBy(x => x.Timestamp) + .ToCollection(); + + [ExcludeFromCodeCoverage] + [Obsolete("Refactor to use `ToReusable()`", true)] // v3.0.0 + public static Collection<(DateTime Timestamp, double Value)> ToTupleChainable( + this IEnumerable reusable) + where TResult : IReusable + => reusable.Select(x => (x.Timestamp, x.Value)).OrderBy(x => x.Timestamp).ToCollection(); + + [ExcludeFromCodeCoverage] + [Obsolete("Refactor to use `List.First(c => c.Timestamp == lookupDate)`", false)] // v3.0.0 + public static TSeries Find(this IEnumerable series, DateTime lookupDate) + where TSeries : ISeries => series.First(x => x.Timestamp == lookupDate); + + [ExcludeFromCodeCoverage] + [Obsolete("Refactor to use `List.FindIndex(c => c.Timestamp == lookupDate)`", false)] // v3.0.0 + public static int FindIndex(this List series, DateTime lookupDate) + where TSeries : ISeries => series?.FindIndex(x => x.Timestamp == lookupDate) ?? -1; +} + +// CLASSES AND INTERFACES + +[Obsolete("Rename `IReusableResult` to `IReusable`", true)] // v3.0.0 +public interface IReusableResult : IReusable; + +[ExcludeFromCodeCoverage] +[Obsolete("Rename `BasicData` to `QuotePart`", true)] // v3.0.0 +public sealed class BasicData : IReusable +{ + public DateTime Timestamp { get; set; } + public double Value { get; set; } +} diff --git a/src/_common/ObsoleteV3.md b/src/_common/ObsoleteV3.md new file mode 100644 index 000000000..9561066da --- /dev/null +++ b/src/_common/ObsoleteV3.md @@ -0,0 +1,107 @@ +# v3 migration guide + +We've discontinued all bridge features for v1 backwards compatibility. +Correct all Warnings from this library before migrating from v2 to v3. +If you are still using v1, migrate to v2 first, to ease the transition to v3. + +Where possible, we've added bridge features to ease transition from v2 to v3. +You will see supporting migration Warning in your compiler with additional instructions for [deprecated changes](#deprecation-changes). + +In addition there are [breaking changes](#breaking-changes) that will require your attention. + +## Deprecation changes + +See your compiler `Warning` to identify these in your code. + +- All static time-series API method prefix were renamed from `GetX()` to `ToX()` to better reflect their purpose as a conversion utility. The former "Get" prefix innaccurately implied a retrieval operation. + +- `Use()` method parameter `candlePart` is now required and no longer defaults to `CandlePart.Close`. +- `Use()` now returns a chainable `QuotePart` instead of a tuple. These also replace the redundant `GetBaseQuote()` and `BaseQuote` items, respectively. + +- `UlcerIndexResult` property `UI` was renamed to `UlcerIndex` + +- **Deprecated 'GetX' tuple interfaces**. + +- **Deprecated internal signals**: several indicators were originally built with integrated but optional + moving averages, often by specifying an optional `smaPeriods` parameter. With more moving average chaining options, + these are obsolete, so we've removed them for simplification. These were persisted to avoid breaking your code; + however, you will see a compiler `Warnings` to help you identify areas to refactor. Check for use in ADL, OBV, ROC, STDDEV, TRIX, and others. + Future versions will not support the old API and will produce compiler `Errors`. + + ```csharp + // To refactor, here's an example replacement for ADL: + + var results = quotes.ToAdl(10); + var adlSma = results.ToSma(5); + + // ref: old usage example + + var results = quotes + .GetAdl(lookbackPeriods: 10, smaPeriods: 5); + ``` + +## Breaking changes + +Not all, but some of these will be shown as compiler `Errors` in your code. +Items marked with 🚩 require special attention since they will not produce compiler Errors or Warnings. + +- all v1 backwards compatibility accommodations were removed. + +- no longer supporting .NET Standard 2.0 for older .NET Framework compatibility. + +### Common breaking changes + +- `Quote` type (built-in) was changed to an _**immutable**_ `record` type; and its `IQuote` interface `Date` property was widely renamed to `Timestamp`, to avoid a conflict with a C# reserved name. This will break your implementation if you were using `Quote` as an inherited base type. To fix, define your custom `TQuote` type on the `IQuote` interface instead (example below). + +- `IQuote` is now a reusable (chainable) type. It auto-selects `Close` price as the _default_ consumed value. + +- `TQuote` custom quote types now have to implement the `IReusable` interface to support chaining operations. The best way to fix is to change your `TQuote` to implement the `IReusable.Value` pointer to your `IQuote.Close` price. See [the Guide](/guide) for more information. Example: + + ```csharp + public record MyCustomQuote ( + + // `IQuote` properties + DateTime Timestamp, + decimal Open, + decimal High, + decimal Low, + decimal MyClose, // custom + decimal Volume, + + // custom properties + string? MyCustomProperty = default + + ) : IQuote // » IQuote now inherits an IReusable + { + // custom mapped properties + decimal IQuote.Close + => MyClose; + + // `IReusable` requires a default 'Value' property. + // Map it to your 'Close' price. + double IReusable.Value + => (double)Close; + } + ``` + +- `IReusableResult` was renamed to `IReusable` since it is no longer limited to _result_ types. + +- 🚩 `IReusableResult.Value` property was changed to non-nullable and returns `double.NaN` instead of `null` for incalculable periods. The standard results (e.g. `EmaResult.Ema`) continue to return `null` for incalculable periods. This was done to improve internal chaining and streaming performance. + +- Indicator return types, like `EmaResult`, were changed from `sealed class` to _**immutable**_ `record` types to improve internal chaining and streaming performance. This should not impact negatively; however, these can now be inherited as base classes. + +### Less common breaking changes + +- Return type for the `Use()` utility method was renamed from `UseResult` to `QuotePart` for clarity and for of its wider purpose. + +- `Numerixs` class was renamed to `Numerical`. + +- `GetBaseQuote()` indicator and related `BasicData` return types were removed since they are redundant to the `Use()` method and `QuotePart` return types, respectively. + +- `AtrStopResult` values were changed from `decimal` to `double` numeric return types. + +- `SyncSeries()` utility function and related `SyncType` enum were removed. This was primarily an internal utility, but was part of the public API to support user who wanted to build custom indicator development. Internally, we've refactored indicators to auto-initialize and heal, so they no longer require re-sizing to support explicit warmup periods. + +- `ToTupleCollection()` utility method was deprecated. This was available to support custom indicator development, but is no longer needed. We've discontinued using _tuples_ as an interface to chainable indicators. + +- `Find()` and `FindIndex()` utility methods were removed. These were redundant to the native C# `List.Find()` method and `List.FindIndex()` methods, respectively. diff --git a/src/_common/Quotes/Quote.Aggregates.cs b/src/_common/Quotes/Quote.Aggregates.cs index 8ce2dc29f..8af1b3ccd 100644 --- a/src/_common/Quotes/Quote.Aggregates.cs +++ b/src/_common/Quotes/Quote.Aggregates.cs @@ -2,45 +2,45 @@ namespace Skender.Stock.Indicators; // QUOTE UTILITIES -public static partial class QuoteUtility +public static partial class Quotes { // aggregation (quantization) /// /// - public static IEnumerable Aggregate( - this IEnumerable quotes, + public static IReadOnlyList Aggregate( + this IReadOnlyList quotes, PeriodSize newSize) where TQuote : IQuote { - if (newSize != PeriodSize.Month) - { - // parameter conversion - TimeSpan newTimeSpan = newSize.ToTimeSpan(); - - // convert - return quotes.Aggregate(newTimeSpan); - } - else // month + if (newSize == PeriodSize.Month) { return quotes - .OrderBy(x => x.Date) - .GroupBy(x => new DateTime(x.Date.Year, x.Date.Month, 1)) - .Select(x => new Quote { - Date = x.Key, - Open = x.First().Open, - High = x.Max(t => t.High), - Low = x.Min(t => t.Low), - Close = x.Last().Close, - Volume = x.Sum(t => t.Volume) - }); + .OrderBy(x => x.Timestamp) + .GroupBy(x => new DateTime(x.Timestamp.Year, x.Timestamp.Month, 1)) + .Select(x => new Quote( + Timestamp: x.Key, + Open: x.First().Open, + High: x.Max(t => t.High), + Low: x.Min(t => t.Low), + Close: x.Last().Close, + Volume: x.Sum(t => t.Volume))) + .ToList(); } + + // parameter conversion + TimeSpan newTimeSpan = newSize.ToTimeSpan(); + + // convert + return quotes.Aggregate(newTimeSpan); + + // month } // aggregation (quantization) using TimeSpan /// /// - public static IEnumerable Aggregate( - this IEnumerable quotes, + public static IReadOnlyList Aggregate( + this IReadOnlyList quotes, TimeSpan timeSpan) where TQuote : IQuote { @@ -52,15 +52,15 @@ public static IEnumerable Aggregate( // return aggregation return quotes - .OrderBy(x => x.Date) - .GroupBy(x => x.Date.RoundDown(timeSpan)) - .Select(x => new Quote { - Date = x.Key, - Open = x.First().Open, - High = x.Max(t => t.High), - Low = x.Min(t => t.Low), - Close = x.Last().Close, - Volume = x.Sum(t => t.Volume) - }); + .OrderBy(x => x.Timestamp) + .GroupBy(x => x.Timestamp.RoundDown(timeSpan)) + .Select(x => new Quote( + Timestamp: x.Key, + Open: x.First().Open, + High: x.Max(t => t.High), + Low: x.Min(t => t.Low), + Close: x.Last().Close, + Volume: x.Sum(t => t.Volume))) + .ToList(); } } diff --git a/src/_common/Quotes/Quote.Converters.cs b/src/_common/Quotes/Quote.Converters.cs index 6e0941d5b..0a03e5e13 100644 --- a/src/_common/Quotes/Quote.Converters.cs +++ b/src/_common/Quotes/Quote.Converters.cs @@ -1,122 +1,52 @@ -using System.Collections.ObjectModel; -using System.Globalization; - namespace Skender.Stock.Indicators; -// QUOTE UTILITIES +// QUOTE UTILITIES (CONVERTERS) -public static partial class QuoteUtility +public static partial class Quotes { + /* LISTS */ - // TUPLE QUOTES - - // convert quotes to tuple list - public static Collection<(DateTime, double)> ToTupleCollection( - this IEnumerable quotes, - CandlePart candlePart) + // convert TQuote type list to built-in Quote type list + public static IReadOnlyList ToQuoteList( + this IReadOnlyList quotes) where TQuote : IQuote - => quotes - .ToTuple(candlePart) - .ToCollection(); - - internal static List<(DateTime, double)> ToTuple( - this IEnumerable quotes, - CandlePart candlePart) - where TQuote : IQuote => quotes - .OrderBy(x => x.Date) - .Select(x => x.ToTuple(candlePart)) - .ToList(); - - // convert tuples to list, with sorting - public static Collection<(DateTime, double)> ToSortedCollection( - this IEnumerable<(DateTime date, double value)> tuples) - => tuples - .ToSortedList() - .ToCollection(); - internal static List<(DateTime, double)> ToSortedList( - this IEnumerable<(DateTime date, double value)> tuples) - => tuples - .OrderBy(x => x.date) + => quotes + .OrderBy(x => x.Timestamp) + .Select(x => x.ToQuote()) .ToList(); - // DOUBLE QUOTES - - // convert to quotes in double precision - internal static List ToQuoteD( - this IEnumerable quotes) - where TQuote : IQuote => quotes - .Select(x => new QuoteD { - Date = x.Date, - Open = (double)x.Open, - High = (double)x.High, - Low = (double)x.Low, - Close = (double)x.Close, - Volume = (double)x.Volume - }) - .OrderBy(x => x.Date) - .ToList(); + // convert TQuote type list to QuoteD type list + internal static List ToQuoteDList( + this IReadOnlyList quotes) + where TQuote : IQuote - // convert quoteD list to tuples - internal static List<(DateTime, double)> ToTuple( - this List qdList, - CandlePart candlePart) => qdList - .OrderBy(x => x.Date) - .Select(x => x.ToTuple(candlePart)) + => quotes + .Select(x => x.ToQuoteD()) .ToList(); - /* ELEMENTS */ - - // convert TQuote element to basic tuple - internal static (DateTime date, double value) ToTuple( - this TQuote q, - CandlePart candlePart) - where TQuote : IQuote => candlePart switch { - CandlePart.Open => (q.Date, (double)q.Open), - CandlePart.High => (q.Date, (double)q.High), - CandlePart.Low => (q.Date, (double)q.Low), - CandlePart.Close => (q.Date, (double)q.Close), - CandlePart.Volume => (q.Date, (double)q.Volume), - CandlePart.HL2 => (q.Date, (double)(q.High + q.Low) / 2), - CandlePart.HLC3 => (q.Date, (double)(q.High + q.Low + q.Close) / 3), - CandlePart.OC2 => (q.Date, (double)(q.Open + q.Close) / 2), - CandlePart.OHL3 => (q.Date, (double)(q.Open + q.High + q.Low) / 3), - CandlePart.OHLC4 => (q.Date, (double)(q.Open + q.High + q.Low + q.Close) / 4), - _ => throw new ArgumentOutOfRangeException(nameof(candlePart), candlePart, "Invalid candlePart provided."), - }; + /* TYPES */ - // convert TQuote element to basic double class - internal static BasicData ToBasicData( - this TQuote q, - CandlePart candlePart) - where TQuote : IQuote => candlePart switch { - CandlePart.Open => new BasicData { Date = q.Date, Value = (double)q.Open }, - CandlePart.High => new BasicData { Date = q.Date, Value = (double)q.High }, - CandlePart.Low => new BasicData { Date = q.Date, Value = (double)q.Low }, - CandlePart.Close => new BasicData { Date = q.Date, Value = (double)q.Close }, - CandlePart.Volume => new BasicData { Date = q.Date, Value = (double)q.Volume }, - CandlePart.HL2 => new BasicData { Date = q.Date, Value = (double)(q.High + q.Low) / 2 }, - CandlePart.HLC3 => new BasicData { Date = q.Date, Value = (double)(q.High + q.Low + q.Close) / 3 }, - CandlePart.OC2 => new BasicData { Date = q.Date, Value = (double)(q.Open + q.Close) / 2 }, - CandlePart.OHL3 => new BasicData { Date = q.Date, Value = (double)(q.Open + q.High + q.Low) / 3 }, - CandlePart.OHLC4 => new BasicData { Date = q.Date, Value = (double)(q.Open + q.High + q.Low + q.Close) / 4 }, - _ => throw new ArgumentOutOfRangeException(nameof(candlePart), candlePart, "Invalid candlePart provided."), - }; + // convert any IQuote type to native Quote type + public static Quote ToQuote(this TQuote quote) + where TQuote : IQuote - // convert quoteD element to basic tuple - internal static (DateTime, double) ToTuple( - this QuoteD q, - CandlePart candlePart) => candlePart switch { - CandlePart.Open => (q.Date, q.Open), - CandlePart.High => (q.Date, q.High), - CandlePart.Low => (q.Date, q.Low), - CandlePart.Close => (q.Date, q.Close), - CandlePart.Volume => (q.Date, q.Volume), - CandlePart.HL2 => (q.Date, (q.High + q.Low) / 2), - CandlePart.HLC3 => (q.Date, (q.High + q.Low + q.Close) / 3), - CandlePart.OC2 => (q.Date, (q.Open + q.Close) / 2), - CandlePart.OHL3 => (q.Date, (q.Open + q.High + q.Low) / 3), - CandlePart.OHLC4 => (q.Date, (q.Open + q.High + q.Low + q.Close) / 4), - _ => throw new ArgumentOutOfRangeException(nameof(candlePart), candlePart, "Invalid candlePart provided."), - }; + => new( + Timestamp: quote.Timestamp, + Open: quote.Open, + High: quote.High, + Low: quote.Low, + Close: quote.Close, + Volume: quote.Volume); + + // convert to quote in double precision + internal static QuoteD ToQuoteD(this IQuote quote) + + => new( + Timestamp: quote.Timestamp, + Open: (double)quote.Open, + High: (double)quote.High, + Low: (double)quote.Low, + Close: (double)quote.Close, + Volume: (double)quote.Volume); } diff --git a/src/_common/Quotes/Quote.Exceptions.cs b/src/_common/Quotes/Quote.Exceptions.cs index 23cec65b1..2ba860334 100644 --- a/src/_common/Quotes/Quote.Exceptions.cs +++ b/src/_common/Quotes/Quote.Exceptions.cs @@ -1,5 +1,6 @@ namespace Skender.Stock.Indicators; +[Serializable] public class InvalidQuotesException : ArgumentOutOfRangeException { public InvalidQuotesException() diff --git a/src/_common/Quotes/Quote.Models.cs b/src/_common/Quotes/Quote.Models.cs index 6a5c96a26..46e65e5b6 100644 --- a/src/_common/Quotes/Quote.Models.cs +++ b/src/_common/Quotes/Quote.Models.cs @@ -2,33 +2,109 @@ namespace Skender.Stock.Indicators; // QUOTE MODELS -public interface IQuote : ISeries +/// +/// Quote interface for standard OHLCV aggregate period. +/// This is commonly known as a "bar" or "candle" and represents +/// and asset price range over a specific time range, +/// +/// If implementing your own custom TQuote:IQuote type: +/// +/// +/// For chaining compatibility ( +/// compliance), add the following TQuote property +/// (pointer) to your price. +/// +/// double IReusable.Value => (double)Close; +/// +/// +/// +/// TIP: If you do not need a custom quote type, +/// use the built-in . +/// +/// +public interface IQuote : IReusable { - public decimal Open { get; } - public decimal High { get; } - public decimal Low { get; } - public decimal Close { get; } - public decimal Volume { get; } + /// + /// Aggregate bar's first tick price + /// + decimal Open { get; } + + /// + /// Aggregate bar's highest tick price + /// + decimal High { get; } + + /// + /// Aggregate bar's lowest tick price + /// + decimal Low { get; } + + /// + /// Aggregate bar's last tick price + /// + decimal Close { get; } + + /// + /// Aggregate bar's tick volume + /// + decimal Volume { get; } } +/// +/// Built-in Quote type, representing an OHLCV aggregate price period. +/// +/// +/// Close date/time of the aggregate period +/// +/// +/// Aggregate bar's first tick price +/// +/// +/// Aggregate bar's highest tick price +/// +/// +/// Aggregate bar's lowest tick price +/// +/// +/// Aggregate bar's last tick price +/// +/// +/// Aggregate bar's tick volume +/// +/// [Serializable] -public class Quote : IQuote +public record Quote +( + DateTime Timestamp, + decimal Open, + decimal High, + decimal Low, + decimal Close, + decimal Volume +) : IQuote { - public DateTime Date { get; set; } - public decimal Open { get; set; } - public decimal High { get; set; } - public decimal Low { get; set; } - public decimal Close { get; set; } - public decimal Volume { get; set; } + public double Value => (double)Close; + + // TODO: add [Obsolete] auto-getter/setter for 'Date' property + // but only for a short transition period. See if there can be + // a full overload of 'Quote' that has the 'Date' property and + // can support new(){ ... } initialization. } +/// +/// Double-point precision Quote, for internal use only. +/// +/// [Serializable] -internal class QuoteD +internal record QuoteD +( + DateTime Timestamp, + double Open, + double High, + double Low, + double Close, + double Volume +) : IReusable { - internal DateTime Date { get; set; } - internal double Open { get; set; } - internal double High { get; set; } - internal double Low { get; set; } - internal double Close { get; set; } - internal double Volume { get; set; } + public double Value => Close; } diff --git a/src/_common/Quotes/Quote.StreamHub.cs b/src/_common/Quotes/Quote.StreamHub.cs new file mode 100644 index 000000000..6f43feb02 --- /dev/null +++ b/src/_common/Quotes/Quote.StreamHub.cs @@ -0,0 +1,72 @@ +namespace Skender.Stock.Indicators; + +#region hub initializer + +public static partial class Quotes +{ + public static QuoteHub ToQuote( + this IQuoteProvider quoteProvider) + where TQuote : IQuote => new(quoteProvider); +} +#endregion + +/// +/// Quote provider (abstract base) +/// +public class QuoteHub + : QuoteProvider + where TQuote : IQuote +{ + public QuoteHub() : base(new EmptyQuoteProvider()) { } + + public QuoteHub( + IQuoteProvider provider) + : base(provider) + { + Reinitialize(); + } + + // METHODS + + protected override (TQuote result, int index) + ToIndicator(TQuote item, int? indexHint) + { + int index = indexHint + ?? Cache.GetIndexGte(item.Timestamp); + + return (item, index == -1 ? Cache.Count : index); + } + + public override string ToString() + => $"QUOTES<{typeof(TQuote).Name}>: {Quotes.Count} items"; +} + +/// +/// Empty quote provider for base Quote Hub initialization. +/// +/// Internal use only. Do not use directly. +/// +public class EmptyQuoteProvider + : IQuoteProvider + where TQuote : IQuote +{ + /// + /// Default quote provider is parent-less Quote Hub. + /// It does not transfer its setting to its children. + /// + public BinarySettings Properties { get; } = new(0b00000001, 0b11111110); + public int ObserverCount => 0; + public bool HasObservers => false; + public IReadOnlyList Quotes { get; } = Array.Empty(); + public IReadOnlyList GetCacheRef() => Array.Empty(); + public bool HasSubscriber(IStreamObserver observer) => false; + + public IDisposable Subscribe(IStreamObserver observer) + => throw new InvalidOperationException(); + + public bool Unsubscribe(IStreamObserver observer) + => throw new InvalidOperationException(); + + public void EndTransmission() + => throw new InvalidOperationException(); +} diff --git a/src/_common/Quotes/Quote.Validation.cs b/src/_common/Quotes/Quote.Validation.cs index 540b4a7c6..5713b6105 100644 --- a/src/_common/Quotes/Quote.Validation.cs +++ b/src/_common/Quotes/Quote.Validation.cs @@ -2,35 +2,60 @@ namespace Skender.Stock.Indicators; -// QUOTE UTILITIES +// QUOTE UTILITIES: VALIDATION -public static partial class QuoteUtility +public static partial class Quotes { - private static readonly CultureInfo invCulture = CultureInfo.InvariantCulture; + private static readonly CultureInfo invariantCulture + = CultureInfo.InvariantCulture; - // VALIDATION - /// - /// - public static IEnumerable Validate( - this IEnumerable quotes) + /// + /// Check that quotes are valid and in ascending order. + /// + /// IQuote type + /// List of quotes + /// Valid list of quotes + /// + /// List of quotes cannot be a null reference. + /// + /// + /// Duplicate or out of sequence quotes found. + /// + public static IReadOnlyList Validate( + this IReadOnlyList quotes) where TQuote : IQuote { - // we cannot rely on date consistency when looking back, so we force sort - List quotesList = quotes.ToSortedList(); + ArgumentNullException.ThrowIfNull(quotes); - // check for duplicates - DateTime lastDate = DateTime.MinValue; - foreach (TQuote q in quotesList) + if (quotes.Count == 0) { - if (lastDate == q.Date) + return quotes; + } + + DateTime lastDate = quotes[0].Timestamp; + for (int i = 1; i < quotes.Count; i++) + { + DateTime currentDate = quotes[i].Timestamp; + + if (lastDate == currentDate) { - throw new InvalidQuotesException( - $"Duplicate date found on {q.Date.ToString("o", invCulture)}."); + string msg = + $"Duplicate date found on {currentDate.ToString("o", invariantCulture)}."; + + throw new InvalidQuotesException(nameof(quotes), msg); + } + + if (lastDate > currentDate) + { + string msg = + $"Quotes are out of sequence on {currentDate.ToString("o", invariantCulture)}."; + + throw new InvalidQuotesException(nameof(quotes), msg); } - lastDate = q.Date; + lastDate = currentDate; } - return quotesList; + return quotes; } } diff --git a/src/_common/Quotes/Use.Api.cs b/src/_common/Quotes/Use.Api.cs deleted file mode 100644 index f9a3f2a45..000000000 --- a/src/_common/Quotes/Use.Api.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Skender.Stock.Indicators; - -// USE (API) - -public static partial class QuoteUtility -{ - // convert TQuotes to basic double tuple list - /// - /// - public static IEnumerable<(DateTime Date, double Value)> Use( - this IEnumerable quotes, - CandlePart candlePart = CandlePart.Close) - where TQuote : IQuote => quotes - .Select(x => x.ToTuple(candlePart)); - - // OBSERVER, from Quote Provider - public static UseObserver Use( - this QuoteProvider provider, - CandlePart candlePart = CandlePart.Close) - => new(provider, candlePart); -} diff --git a/src/_common/Quotes/Use.Observer.cs b/src/_common/Quotes/Use.Observer.cs deleted file mode 100644 index 935b27753..000000000 --- a/src/_common/Quotes/Use.Observer.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Skender.Stock.Indicators; - -// USE (STREAMING) -public class UseObserver : TupleProvider -{ - public UseObserver( - QuoteProvider? provider, - CandlePart candlePart) - { - Supplier = provider; - CandlePartSelection = candlePart; - Initialize(); - } - - // PROPERTIES - - public IEnumerable<(DateTime Date, double Value)> Results => ProtectedTuples; - - private CandlePart CandlePartSelection { get; set; } - - // NON-STATIC METHODS - - // handle quote arrival - public override void OnNext(Quote value) => HandleArrival(value); - - // add new quote - internal void HandleArrival(Quote quote) - { - // candidate result - (DateTime date, double value) r = quote.ToTuple(CandlePartSelection); - - // initialize - int length = ProtectedTuples.Count; - - if (length == 0) - { - AddSend(r); - return; - } - - // check against last entry - (DateTime lastDate, _) = ProtectedTuples[length - 1]; - - // add new - if (r.date > lastDate) - { - AddSend(r); - } - - // update last - else if (r.date == lastDate) - { - ProtectedTuples[length - 1] = r; - } - - // late arrival - else - { - AddSend(r); - throw new NotImplementedException(); - } - } - - // calculate initial cache of quotes - private void Initialize() - { - if (Supplier != null) - { - ProtectedTuples = Supplier - .ProtectedQuotes - .ToTuple(CandlePartSelection); - } - - Subscribe(); - } -} diff --git a/src/_common/Quotes/info.xml b/src/_common/Quotes/info.xml index 2da909c1a..721479e5b 100644 --- a/src/_common/Quotes/info.xml +++ b/src/_common/Quotes/info.xml @@ -1,35 +1,7 @@ - - - Optionally select which candle part to use in the calculation. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - The OHLCV element or simply calculated value type. - Time series of Quote tuple values. - Invalid candle part provided. - - - - Validate historical quotes. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Time series of historical quote values. - Validation check failed. - + Converts historical quotes into larger bar sizes. @@ -45,6 +17,7 @@ Time series of historical quote values. Invalid parameter value provided. + Converts historical quotes into larger bar sizes. @@ -60,4 +33,5 @@ Time series of historical quote values. Invalid parameter value provided. - \ No newline at end of file + + diff --git a/src/_common/Results/Result.Models.cs b/src/_common/Results/Result.Models.cs deleted file mode 100644 index f031f7d84..000000000 --- a/src/_common/Results/Result.Models.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RESULT MODELS - -public interface IReusableResult : ISeries -{ - public double? Value { get; } -} - -[Serializable] -public abstract class ResultBase : ISeries -{ - public DateTime Date { get; set; } -} diff --git a/src/_common/Results/Result.Syncing.cs b/src/_common/Results/Result.Syncing.cs deleted file mode 100644 index a39b0c9f9..000000000 --- a/src/_common/Results/Result.Syncing.cs +++ /dev/null @@ -1,102 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RESULTS UTILITIES - -public static partial class ResultUtility -{ - // SYNCHRONIZING RESULTS - RESIZE TO MATCH OTHER - /// - /// - public static IEnumerable SyncIndex( - this IEnumerable syncMe, - IEnumerable toMatch, - SyncType syncType = SyncType.FullMatch) - where TResultA : ISeries - where TResultB : ISeries - { - // initialize - List syncMeList = syncMe.ToSortedList(); - List toMatchList = toMatch.ToSortedList(); - - if (syncMeList.Count == 0 || toMatchList.Count == 0) - { - return new List(); - } - - bool prepend = false; - bool append = false; - bool remove = false; - - switch (syncType) - { - case SyncType.Prepend: - prepend = true; - break; - - case SyncType.AppendOnly: - prepend = append = true; - break; - - case SyncType.RemoveOnly: - remove = true; - break; - - case SyncType.FullMatch: - prepend = append = remove = true; - break; - - default: - throw new ArgumentOutOfRangeException(nameof(syncType)); - } - - Type type = syncMeList[0].GetType(); - - // add plugs for missing values - if (prepend || append) - { - List toAppend = []; - - for (int i = 0; i < toMatchList.Count; i++) - { - TResultB? m = toMatchList[i]; - TResultA? r = syncMeList.Find(m.Date); - - if (r is null) - { - TResultA? n = (TResultA?)Activator.CreateInstance(type, m.Date); - if (n != null) - { - toAppend.Add(n); - } - } - else if (!append) - { - break; - } - } - - syncMeList.AddRange(toAppend); - } - - // remove unmatched results - if (remove) - { - List toRemove = []; - - for (int i = 0; i < syncMeList.Count; i++) - { - TResultA? r = syncMeList[i]; - TResultB? m = toMatchList.Find(r.Date); - - if (m is null) - { - toRemove.Add(r); - } - } - - syncMeList.RemoveAll(x => toRemove.Contains(x)); - } - - return syncMeList.ToSortedList(); - } -} diff --git a/src/_common/Results/Result.Utilities.cs b/src/_common/Results/Result.Utilities.cs deleted file mode 100644 index eca3f3ac3..000000000 --- a/src/_common/Results/Result.Utilities.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Collections.ObjectModel; - -namespace Skender.Stock.Indicators; - -// RESULTS UTILITIES - -public static partial class ResultUtility -{ - // CONDENSE (REMOVE null and NaN results) - /// - /// - public static IEnumerable Condense( - this IEnumerable results) - where TResult : IReusableResult - { - List resultsList = results - .ToList(); - - resultsList - .RemoveAll(match: - x => x.Value is null or (not null and double.NaN)); - - return resultsList.ToSortedList(); - } - - // CONVERT TO TUPLE (default with pruning) - /// - /// - public static Collection<(DateTime Date, double Value)> ToTupleChainable( - this IEnumerable reusable) - where TResult : IReusableResult - => reusable - .ToTuple() - .ToCollection(); - - internal static List<(DateTime Date, double Value)> ToTuple( - this IEnumerable reusable) - where TResult : IReusableResult - { - List<(DateTime date, double value)> prices = []; - List reList = reusable.ToList(); - - // find first non-nulled - int first = reList.FindIndex(x => x.Value != null); - - for (int i = first; i < reList.Count; i++) - { - IReusableResult r = reList[i]; - prices.Add(new(r.Date, r.Value.Null2NaN())); - } - - return prices.OrderBy(x => x.date).ToList(); - } - - // CONVERT TO TUPLE with non-nullable NaN value option and no pruning - /// - /// - public static Collection<(DateTime Date, double Value)> ToTupleNaN( - this IEnumerable reusable) - where TResult : IReusableResult - { - List reList = reusable.ToSortedList(); - int length = reList.Count; - - Collection<(DateTime Date, double Value)> results = []; - - for (int i = 0; i < length; i++) - { - IReusableResult r = reList[i]; - results.Add(new(r.Date, r.Value.Null2NaN())); - } - - return results; - } -} diff --git a/src/_common/Results/info.xml b/src/_common/Results/info.xml deleted file mode 100644 index 11ad40769..000000000 --- a/src/_common/Results/info.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - Converts results into a reusable tuple with warmup periods removed and nulls converted to NaN. - See - documentation for more information. - - - Any reusable result type. - Indicator results to evaluate. - Collection of non-nullable tuple time series of results, without null warmup periods. - - - Converts results into a tuple collection with non-nullable NaN to replace null values. - See - documentation for more information. - - Any reusable result type. - Indicator results to evaluate. - Collection of tuple time series of - results with specified handling of nulls, without pruning. - - - Removes the recommended quantity of results from the beginning of the results list - using a reverse-engineering approach. See - documentation for more information. - - Indicator - results to evaluate. - Time - series of results, pruned. - - - Removes non-essential records containing null values with unique consideration for - this indicator. See - documentation for more information. - - Indicator results to evaluate. - Time series of - indicator results, condensed. - - - Removes non-essential records containing null or NaN values. See - documentation for more information. - - Any result type. - Indicator results to evaluate. - Time series of indicator results, - condensed. - - - - Forces indicator results to have the same date-based records as another result baseline. - - This utility is undocumented. - - - Any indicator result series type to be transformed. - Any indicator result series type to be matched. - The indicator result series to be modified. - The indicator result series to compare for matching. - Synchronization behavior See options in SyncType enum. - Indicator result series, synchronized to a comparator match. - - Invalid parameter value provided. - - - \ No newline at end of file diff --git a/src/_common/Reusable/IReusable.cs b/src/_common/Reusable/IReusable.cs new file mode 100644 index 000000000..fdd04b2a7 --- /dev/null +++ b/src/_common/Reusable/IReusable.cs @@ -0,0 +1,13 @@ +namespace Skender.Stock.Indicators; + +/// +/// A time-series type that identifies +/// a single chainable value. +/// +public interface IReusable : ISeries +{ + /// + /// Value that is passed to chained indicators. + /// + double Value { get; } +} diff --git a/src/_common/Reusable/Reusable.Utilities.cs b/src/_common/Reusable/Reusable.Utilities.cs new file mode 100644 index 000000000..d0439f591 --- /dev/null +++ b/src/_common/Reusable/Reusable.Utilities.cs @@ -0,0 +1,66 @@ +namespace Skender.Stock.Indicators; + +// REUSABLE TYPE UTILITIES + +public static partial class Reusable +{ + // convert IQuote type list to IReusable list + public static IReadOnlyList ToReusableList( + this IReadOnlyList quotes, + CandlePart candlePart) + where TQuote : IQuote + + => quotes + .OrderBy(x => x.Timestamp) + .Select(x => x.ToReusable(candlePart)) + .ToList(); + + /// + /// Removes non-essential records containing null or NaN values. + /// + /// Any reusable result type. + /// Indicator results to evaluate. + /// Time series of indicator results, condensed. + public static IReadOnlyList Condense( + this IReadOnlyList results) + where T : IReusable + { + List resultsList = results + .ToList(); + + resultsList + .RemoveAll(match: + x => double.IsNaN(x.Value)); + + return resultsList; + } + + /// + /// Removes the recommended quantity of results from the beginning + /// of the results list using a reverse-engineering approach. + /// + /// Any reusable result type. + /// Indicator results to evaluate. + /// Time series of results, pruned. + internal static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) + where T : IReusable + { + // this is the default implementation, it will + // be overridden in the specific indicator class + + int removePeriods = results + .ToList() + .FindIndex(x => !double.IsNaN(x.Value)); + + return results.Remove(removePeriods); + + // TODO: remove specific indicator 'RemoveWarmupPeriods()' methods + // that are now redundant to this generic method (not all are). + // Note: Some or all of these may already be removed. + } + + // convert TQuote element to a basic chainable class + internal static IReusable ToReusable(this IQuote q, CandlePart candlePart) + => q.ToQuotePart(candlePart); +} diff --git a/src/_common/Use (QuotePart)/QuotePart.Models.cs b/src/_common/Use (QuotePart)/QuotePart.Models.cs new file mode 100644 index 000000000..8bd3e4851 --- /dev/null +++ b/src/_common/Use (QuotePart)/QuotePart.Models.cs @@ -0,0 +1,19 @@ +namespace Skender.Stock.Indicators; + +/// +/// Chainable component part of an . +/// +[Serializable] +public record QuotePart +( + DateTime Timestamp, + double Value +) : IReusable +{ + public QuotePart(IReusable reusable) + : this(reusable?.Timestamp ?? default, + reusable?.Value ?? default) + { } + + public double Value { get; } = Value; +} diff --git a/src/_common/Use (QuotePart)/QuotePart.StaticSeries.cs b/src/_common/Use (QuotePart)/QuotePart.StaticSeries.cs new file mode 100644 index 000000000..53821b9f0 --- /dev/null +++ b/src/_common/Use (QuotePart)/QuotePart.StaticSeries.cs @@ -0,0 +1,45 @@ +namespace Skender.Stock.Indicators; + +// USE / QUOTE CONVERTER (SERIES) + +public static partial class QuoteParts +{ + /// + /// Converts to + /// an list. + /// + /// + /// Use this conversion if indicator needs to + /// use something other than the default Close price. + /// + /// + /// Sorted list of IQuote or IReusable items + /// + /// List of IReusable items + public static IReadOnlyList ToQuotePart( + this IReadOnlyList quotes, + CandlePart candlePart) + where TQuote : IQuote + { + ArgumentNullException.ThrowIfNull(quotes); + int length = quotes.Count; + List result = new(length); + + for (int i = 0; i < length; i++) + { + result.Add(quotes[i].ToQuotePart(candlePart)); + } + return result; + } + + // QuotePart alias + /// + public static IReadOnlyList Use( + this IReadOnlyList quotes, + CandlePart candlePart) + where TQuote : IQuote + => ToQuotePart(quotes, candlePart); + + // TODO: should we deprecate Use in favor of "ToQuotePart"? + // Probably not, this is a fairly simple alias. +} diff --git a/src/_common/Use (QuotePart)/QuotePart.StreamHub.cs b/src/_common/Use (QuotePart)/QuotePart.StreamHub.cs new file mode 100644 index 000000000..4e7448fbe --- /dev/null +++ b/src/_common/Use (QuotePart)/QuotePart.StreamHub.cs @@ -0,0 +1,56 @@ +namespace Skender.Stock.Indicators; + +// USE / QUOTE CONVERTER (STREAM HUB) + +#region hub interface and initializer +public interface IQuotePartHub +{ + CandlePart CandlePartSelection { get; } + + // TODO: consider renaming to IBarPartHub, with IQuote to IBar +} + +public static partial class QuoteParts +{ + public static QuotePartHub ToQuotePart( + this IQuoteProvider quoteProvider, + CandlePart candlePart) + where TIn : IQuote + => new(quoteProvider, candlePart); +} +#endregion + +public class QuotePartHub + : ChainProvider, IQuotePartHub + where TQuote : IQuote +{ + #region constructors + + internal QuotePartHub( + IQuoteProvider provider, + CandlePart candlePart) + : base(provider) + { + CandlePartSelection = candlePart; + + Reinitialize(); + } + #endregion + + public CandlePart CandlePartSelection { get; init; } + + // METHODS + + protected override (QuotePart result, int index) + ToIndicator(TQuote item, int? indexHint) + { + // candidate result + QuotePart r + = item.ToQuotePart(CandlePartSelection); + + return (r, indexHint ?? Cache.Count); + } + + public override string ToString() + => $"QUOTE-PART({CandlePartSelection.ToString().ToUpperInvariant()})"; +} diff --git a/src/_common/Use (QuotePart)/QuotePart.Utilities.cs b/src/_common/Use (QuotePart)/QuotePart.Utilities.cs new file mode 100644 index 000000000..487cc7173 --- /dev/null +++ b/src/_common/Use (QuotePart)/QuotePart.Utilities.cs @@ -0,0 +1,78 @@ +namespace Skender.Stock.Indicators; + +// QUOTEPART TYPE UTILITIES + +public static partial class QuoteParts +{ + // convert TQuote element to a basic QuotePart class + internal static QuotePart ToQuotePart(this IQuote q, CandlePart candlePart) + => new(q.Timestamp, q.ToQuotePartValue(candlePart)); + + // convert IQuote to value based on CandlePart + internal static double ToQuotePartValue(this IQuote q, CandlePart candlePart) + + => candlePart switch { + + CandlePart.Open => (double)q.Open, + CandlePart.High => (double)q.High, + CandlePart.Low => (double)q.Low, + CandlePart.Close => (double)q.Close, + CandlePart.Volume => (double)q.Volume, + CandlePart.HL2 => (double)(q.High + q.Low) / 2, + CandlePart.HLC3 => (double)(q.High + q.Low + q.Close) / 3, + CandlePart.OC2 => (double)(q.Open + q.Close) / 2, + CandlePart.OHL3 => (double)(q.Open + q.High + q.Low) / 3, + CandlePart.OHLC4 => (double)(q.Open + q.High + q.Low + q.Close) / 4, + + _ => throw new ArgumentOutOfRangeException( + nameof(candlePart), candlePart, "Invalid candlePart provided.") + }; + + // conditional HL2 value if IQuote type + internal static double Hl2OrValue( + this T item) + where T : IReusable + => item.QuotePartOrValue(CandlePart.HL2); + + // conditional CandlePart value if IQuote type + internal static double QuotePartOrValue( + this T item, CandlePart candlePart) + where T : IReusable + => item is IQuote q + ? q.ToQuotePartValue(candlePart) + : item.Value; + + /// + /// Uses CandlePart to sort and convert a list from an + /// to and type. + /// + /// + /// Use this conversion if your source list needs to + /// conditionally use something other than Close price + /// as the IReusable value. + /// + /// If you provide a list of + /// IReusable items that are not IQuote, it will simply + /// cast itself from . + /// + /// + /// + /// List of IQuote or IReusable items + /// + /// List of IReusable items + internal static IReadOnlyList ToPreferredList( + this IReadOnlyList items, CandlePart candlePart) + where T : IReusable + { + ArgumentNullException.ThrowIfNull(items); + + if (items is IReadOnlyList quotes) + { + return quotes.ToQuotePart(candlePart); + } + else + { + return items.Cast().ToList(); + } + } +} diff --git a/src/a-d/Adl/Adl.Api.cs b/src/a-d/Adl/Adl.Api.cs deleted file mode 100644 index 578643174..000000000 --- a/src/a-d/Adl/Adl.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ACCUMULATION/DISTRIBUTION LINE (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetAdl( - this IEnumerable quotes, - int? smaPeriods = null) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcAdl(smaPeriods); -} diff --git a/src/a-d/Adl/Adl.Models.cs b/src/a-d/Adl/Adl.Models.cs index d8bbe1f88..4e62a66ee 100644 --- a/src/a-d/Adl/Adl.Models.cs +++ b/src/a-d/Adl/Adl.Models.cs @@ -1,17 +1,13 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class AdlResult : ResultBase, IReusableResult +public record AdlResult +( + DateTime Timestamp, + double Adl, + double? MoneyFlowMultiplier = null, + double? MoneyFlowVolume = null +) : IReusable { - public AdlResult(DateTime date) - { - Date = date; - } - - public double? MoneyFlowMultiplier { get; set; } - public double? MoneyFlowVolume { get; set; } - public double Adl { get; set; } - public double? AdlSma { get; set; } - - double? IReusableResult.Value => Adl; + public double Value => Adl; } diff --git a/src/a-d/Adl/Adl.Series.cs b/src/a-d/Adl/Adl.Series.cs deleted file mode 100644 index 57d125d0f..000000000 --- a/src/a-d/Adl/Adl.Series.cs +++ /dev/null @@ -1,62 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ACCUMULATION/DISTRIBUTION LINE (SERIES) -public static partial class Indicator -{ - internal static List CalcAdl( - this List qdList, - int? smaPeriods) - { - // check parameter arguments - ValidateAdl(smaPeriods); - - // initialize - List results = new(qdList.Count); - double prevAdl = 0; - - // roll through quotes - for (int i = 0; i < qdList.Count; i++) - { - QuoteD q = qdList[i]; - - double mfm = (q.High == q.Low) ? 0 : (q.Close - q.Low - (q.High - q.Close)) / (q.High - q.Low); - double mfv = mfm * q.Volume; - double adl = mfv + prevAdl; - - AdlResult r = new(q.Date) { - MoneyFlowMultiplier = mfm, - MoneyFlowVolume = mfv, - Adl = adl - }; - results.Add(r); - - prevAdl = adl; - - // optional SMA - if (smaPeriods != null && i + 1 >= smaPeriods) - { - double? sumSma = 0; - for (int p = i + 1 - (int)smaPeriods; p <= i; p++) - { - sumSma += results[p].Adl; - } - - r.AdlSma = sumSma / smaPeriods; - } - } - - return results; - } - - // parameter validation - private static void ValidateAdl( - int? smaPeriods) - { - // check parameter arguments - if (smaPeriods is not null and <= 0) - { - throw new ArgumentOutOfRangeException(nameof(smaPeriods), smaPeriods, - "SMA periods must be greater than 0 for ADL."); - } - } -} diff --git a/src/a-d/Adl/Adl.StaticSeries.cs b/src/a-d/Adl/Adl.StaticSeries.cs new file mode 100644 index 000000000..10f833ed4 --- /dev/null +++ b/src/a-d/Adl/Adl.StaticSeries.cs @@ -0,0 +1,33 @@ +namespace Skender.Stock.Indicators; + +// ACCUMULATION/DISTRIBUTION LINE (SERIES) + +public static partial class Adl +{ + public static IReadOnlyList ToAdl( + this IReadOnlyList source) + where TQuote : IQuote + { + ArgumentNullException.ThrowIfNull(source); + + // initialize + int length = source.Count; + List results = new(length); + + // roll through source values + for (int i = 0; i < length; i++) + { + AdlResult r = Increment( + source[i].Timestamp, + source[i].High, + source[i].Low, + source[i].Close, + source[i].Volume, + i > 0 ? results[i - 1].Adl : 0); + + results.Add(r); + } + + return results; + } +} diff --git a/src/a-d/Adl/Adl.StreamHub.cs b/src/a-d/Adl/Adl.StreamHub.cs new file mode 100644 index 000000000..e336e51b3 --- /dev/null +++ b/src/a-d/Adl/Adl.StreamHub.cs @@ -0,0 +1,48 @@ +namespace Skender.Stock.Indicators; + +// ACCUMULATION/DISTRIBUTION LINE (STREAM HUB) + +#region hub initializer + +public static partial class Adl +{ + public static AdlHub ToAdl( + this IQuoteProvider quoteProvider) + where TIn : IQuote + => new(quoteProvider); +} +#endregion + +public class AdlHub : ChainProvider + where TIn : IQuote +{ + #region constructors + + internal AdlHub(IQuoteProvider provider) + : base(provider) + { + Reinitialize(); + } + #endregion + + // METHODS + + protected override (AdlResult result, int index) + ToIndicator(TIn item, int? indexHint) + { + int i = indexHint ?? ProviderCache.GetIndex(item, true); + + // candidate result + AdlResult r = Adl.Increment( + item.Timestamp, + item.High, + item.Low, + item.Close, + item.Volume, + i > 0 ? Cache[i - 1].Value : 0); + + return (r, i); + } + + public override string ToString() => Cache.Count == 0 ? "ADL" : $"ADL({Cache[0].Timestamp:d})"; +} diff --git a/src/a-d/Adl/Adl.Utilities.cs b/src/a-d/Adl/Adl.Utilities.cs new file mode 100644 index 000000000..d964a392a --- /dev/null +++ b/src/a-d/Adl/Adl.Utilities.cs @@ -0,0 +1,60 @@ +namespace Skender.Stock.Indicators; + +// ACCUMULATION/DISTRIBUTION LINE (UTILITIES) + +/// +/// See the +/// Stock Indicators for .NET online guide for more information. +/// +public static partial class Adl +{ + /// + /// Get the next incremental Accumulation/Distribution Line(ADL) value. + /// + /// timestamp + /// High price, current period + /// Low price, current period + /// Close price, current period + /// Volume, current period + /// New ADL result value + /// + /// Last ADL value, from prior period + /// + public static AdlResult Increment( + DateTime timestamp, + double high, + double low, + double close, + double volume, + double prevAdl) + { + double mfm = high - low == 0 + ? 0 + : (close - low - (high - close)) + / (high - low); + + double mfv = mfm * volume; + double adl = mfv + prevAdl; + + return new AdlResult( + Timestamp: timestamp, + Adl: adl, + MoneyFlowMultiplier: mfm, + MoneyFlowVolume: mfv); + } + + internal static AdlResult Increment( + DateTime timestamp, + decimal high, + decimal low, + decimal close, + decimal volume, + double prevAdl) + => Increment( + timestamp, + (double)high, + (double)low, + (double)close, + (double)volume, + prevAdl); +} diff --git a/src/a-d/Adl/info.xml b/src/a-d/Adl/info.xml deleted file mode 100644 index ac4e19d27..000000000 --- a/src/a-d/Adl/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Accumulation/Distribution Line (ADL) is a rolling accumulation of Chaikin Money Flow Volume. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Optional. Number of periods in the moving average of ADL. - Time series of ADL values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/Adx/Adx.Api.cs b/src/a-d/Adx/Adx.Api.cs deleted file mode 100644 index 4d1ceac96..000000000 --- a/src/a-d/Adx/Adx.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// AVERAGE DIRECTIONAL INDEX (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetAdx( - this IEnumerable quotes, - int lookbackPeriods = 14) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcAdx(lookbackPeriods); -} diff --git a/src/a-d/Adx/Adx.Models.cs b/src/a-d/Adx/Adx.Models.cs index 3c179a51c..11a3bb56d 100644 --- a/src/a-d/Adx/Adx.Models.cs +++ b/src/a-d/Adx/Adx.Models.cs @@ -1,17 +1,14 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class AdxResult : ResultBase, IReusableResult +public record AdxResult +( + DateTime Timestamp, + double? Pdi = null, + double? Mdi = null, + double? Adx = null, + double? Adxr = null +) : IReusable { - public AdxResult(DateTime date) - { - Date = date; - } - - public double? Pdi { get; set; } - public double? Mdi { get; set; } - public double? Adx { get; set; } - public double? Adxr { get; set; } - - double? IReusableResult.Value => Adx; + public double Value => Adx.Null2NaN(); } diff --git a/src/a-d/Adx/Adx.Series.cs b/src/a-d/Adx/Adx.StaticSeries.cs similarity index 61% rename from src/a-d/Adx/Adx.Series.cs rename to src/a-d/Adx/Adx.StaticSeries.cs index 1c1b8d870..45839098a 100644 --- a/src/a-d/Adx/Adx.Series.cs +++ b/src/a-d/Adx/Adx.StaticSeries.cs @@ -1,17 +1,25 @@ namespace Skender.Stock.Indicators; // AVERAGE DIRECTIONAL INDEX (SERIES) -public static partial class Indicator + +public static partial class Adx { - internal static List CalcAdx( - this List qdList, - int lookbackPeriods) + public static IReadOnlyList ToAdx( + this IReadOnlyList quotes, + int lookbackPeriods = 14) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcAdx(lookbackPeriods); + + private static List CalcAdx( + this IReadOnlyList source, + int lookbackPeriods = 14) { // check parameter arguments - ValidateAdx(lookbackPeriods); + Validate(lookbackPeriods); // initialize - int length = qdList.Count; + int length = source.Count; List results = new(length); double prevHigh = 0; @@ -27,13 +35,10 @@ internal static List CalcAdx( double sumMdm = 0; double sumDx = 0; - // roll through quotes + // roll through source values for (int i = 0; i < length; i++) { - QuoteD q = qdList[i]; - - AdxResult r = new(q.Date); - results.Add(r); + QuoteD q = source[i]; // skip first period if (i == 0) @@ -41,6 +46,8 @@ internal static List CalcAdx( prevHigh = q.High; prevLow = q.Low; prevClose = q.Close; + + results.Add(new(Timestamp: q.Timestamp)); continue; } @@ -69,6 +76,7 @@ internal static List CalcAdx( // skip DM initialization period if (i < lookbackPeriods) { + results.Add(new(Timestamp: q.Timestamp)); continue; } @@ -77,6 +85,7 @@ internal static List CalcAdx( double pdm; double mdm; + // TODO: update healing, without requiring specific indexing if (i == lookbackPeriods) { trs = sumTr; @@ -85,17 +94,18 @@ internal static List CalcAdx( } else { - trs = prevTrs - (prevTrs / lookbackPeriods) + tr; - pdm = prevPdm - (prevPdm / lookbackPeriods) + pdm1; - mdm = prevMdm - (prevMdm / lookbackPeriods) + mdm1; + trs = prevTrs - prevTrs / lookbackPeriods + tr; + pdm = prevPdm - prevPdm / lookbackPeriods + pdm1; + mdm = prevMdm - prevMdm / lookbackPeriods + mdm1; } prevTrs = trs; prevPdm = pdm; prevMdm = mdm; - if (trs is 0) + if (trs == 0) { + results.Add(new(Timestamp: q.Timestamp)); continue; } @@ -103,57 +113,51 @@ internal static List CalcAdx( double pdi = 100 * pdm / trs; double mdi = 100 * mdm / trs; - r.Pdi = pdi; - r.Mdi = mdi; - // calculate ADX - double dx = (pdi == mdi) + double dx = pdi - mdi == 0 ? 0 - : (pdi + mdi != 0) + : pdi + mdi != 0 ? 100 * Math.Abs(pdi - mdi) / (pdi + mdi) : double.NaN; - double adx; + double adx = double.NaN; + double adxr = double.NaN; - if (i > (2 * lookbackPeriods) - 1) + if (i > 2 * lookbackPeriods - 1) { - adx = ((prevAdx * (lookbackPeriods - 1)) + dx) / lookbackPeriods; - r.Adx = adx.NaN2Null(); + adx = (prevAdx * (lookbackPeriods - 1) + dx) / lookbackPeriods; - double? priorAdx = results[i + 1 - lookbackPeriods].Adx; + double priorAdx = results[i - lookbackPeriods + 1].Adx.Null2NaN(); - r.Adxr = (adx + priorAdx).NaN2Null() / 2; + adxr = (adx + priorAdx) / 2; prevAdx = adx; } // initial ADX - else if (i == (2 * lookbackPeriods) - 1) + else if (i == 2 * lookbackPeriods - 1) { sumDx += dx; adx = sumDx / lookbackPeriods; - r.Adx = adx.NaN2Null(); prevAdx = adx; } // ADX initialization period + // TODO: update healing, without requiring specific indexing else { sumDx += dx; } - } - return results; - } + AdxResult r = new( + Timestamp: q.Timestamp, + Pdi: pdi, + Mdi: mdi, + Adx: adx.NaN2Null(), + Adxr: adxr.NaN2Null()); - // parameter validation - private static void ValidateAdx( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for ADX."); + results.Add(r); } + + return results; } } diff --git a/src/a-d/Adx/Adx.Utilities.cs b/src/a-d/Adx/Adx.Utilities.cs index 9f815a1ac..1611769cd 100644 --- a/src/a-d/Adx/Adx.Utilities.cs +++ b/src/a-d/Adx/Adx.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// AVERAGE DIRECTIONAL INDEX (UTILITIES) + +public static partial class Adx { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int n = results .ToList() @@ -14,4 +15,16 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove((2 * n) + 100); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for ADX."); + } + } } diff --git a/src/a-d/Adx/info.xml b/src/a-d/Adx/info.xml index 36ca24a0a..578fe7127 100644 --- a/src/a-d/Adx/info.xml +++ b/src/a-d/Adx/info.xml @@ -1,18 +1,33 @@ - - Directional Movement Index (DMI) and Average Directional Movement Index (ADX) is a measure of price directional movement. - It includes upward and downward indicators, and is often used to measure strength of trend. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of ADX and Plus/Minus Directional values. - Invalid parameter value provided. + + + Directional Movement Index (DMI) and Average Directional Movement Index (ADX) is a measure of price directional movement. + It includes upward and downward indicators, and is often used to measure strength of trend. + + See + documentation + for more information. + + + Configurable Quote type. See Guide for more information. + Historical price quotes. + Number of periods in the lookback window. + Time series of ADX and Plus/Minus Directional values. + Invalid parameter value provided. + + + + Get the next incremental Average Directional Movement Index (ADX) result. + + See + documentation + for more information. + + + Configurable Quote type. See Guide for more information. + Historical price quote. + New AdxResult value. + diff --git a/src/a-d/Alligator/Alligator.Api.cs b/src/a-d/Alligator/Alligator.Api.cs deleted file mode 100644 index 9fdbb8842..000000000 --- a/src/a-d/Alligator/Alligator.Api.cs +++ /dev/null @@ -1,63 +0,0 @@ -namespace Skender.Stock.Indicators; - -// WILLIAMS ALLIGATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetAlligator( - this IEnumerable quotes, - int jawPeriods = 13, - int jawOffset = 8, - int teethPeriods = 8, - int teethOffset = 5, - int lipsPeriods = 5, - int lipsOffset = 3) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.HL2) - .CalcAlligator( - jawPeriods, - jawOffset, - teethPeriods, - teethOffset, - lipsPeriods, - lipsOffset); - - // SERIES, from CHAIN - public static IEnumerable GetAlligator( - this IEnumerable results, - int jawPeriods = 13, - int jawOffset = 8, - int teethPeriods = 8, - int teethOffset = 5, - int lipsPeriods = 5, - int lipsOffset = 3) => results - .ToTuple() - .CalcAlligator( - jawPeriods, - jawOffset, - teethPeriods, - teethOffset, - lipsPeriods, - lipsOffset) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetAlligator( - this IEnumerable<(DateTime, double)> priceTuples, - int jawPeriods = 13, - int jawOffset = 8, - int teethPeriods = 8, - int teethOffset = 5, - int lipsPeriods = 5, - int lipsOffset = 3) => priceTuples - .ToSortedList() - .CalcAlligator( - jawPeriods, - jawOffset, - teethPeriods, - teethOffset, - lipsPeriods, - lipsOffset); -} diff --git a/src/a-d/Alligator/Alligator.Models.cs b/src/a-d/Alligator/Alligator.Models.cs index 75f928142..3b689004b 100644 --- a/src/a-d/Alligator/Alligator.Models.cs +++ b/src/a-d/Alligator/Alligator.Models.cs @@ -1,14 +1,10 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class AlligatorResult : ResultBase -{ - public AlligatorResult(DateTime date) - { - Date = date; - } - - public double? Jaw { get; set; } - public double? Teeth { get; set; } - public double? Lips { get; set; } -} +public record AlligatorResult +( + DateTime Timestamp, + double? Jaw, + double? Teeth, + double? Lips +) : ISeries; diff --git a/src/a-d/Alligator/Alligator.Series.cs b/src/a-d/Alligator/Alligator.Series.cs deleted file mode 100644 index 7115eb881..000000000 --- a/src/a-d/Alligator/Alligator.Series.cs +++ /dev/null @@ -1,185 +0,0 @@ -namespace Skender.Stock.Indicators; - -// WILLIAMS ALLIGATOR (SERIES) -public static partial class Indicator -{ - internal static List CalcAlligator( - this List<(DateTime Date, double Value)> tpList, - int jawPeriods, - int jawOffset, - int teethPeriods, - int teethOffset, - int lipsPeriods, - int lipsOffset) - { - // check parameter arguments - ValidateAlligator( - jawPeriods, - jawOffset, - teethPeriods, - teethOffset, - lipsPeriods, - lipsOffset); - - // initialize - int length = tpList.Count; - double[] pr = new double[length]; // median price - - List results = - tpList - .Select(x => new AlligatorResult(x.Date)) - .ToList(); - - // roll through quotes - for (int i = 0; i < length; i++) - { - (DateTime _, double value) = tpList[i]; - pr[i] = value; - - // only calculate jaw if the array offset is still in valid range - if (i + jawOffset < length) - { - AlligatorResult jawResult = results[i + jawOffset]; - - // calculate alligator's jaw - // first value: calculate SMA - if (i + 1 == jawPeriods) - { - double sumMedianPrice = 0; - for (int p = i + 1 - jawPeriods; p <= i; p++) - { - sumMedianPrice += pr[p]; - } - - jawResult.Jaw = sumMedianPrice / jawPeriods; - } - - // remaining values: SMMA - else if (i + 1 > jawPeriods) - { - double? prevValue = results[i + jawOffset - 1].Jaw; - jawResult.Jaw = ((prevValue * (jawPeriods - 1)) + pr[i]) / jawPeriods; - } - - jawResult.Jaw = jawResult.Jaw.NaN2Null(); - } - - // only calculate teeth if the array offset is still in valid range - if (i + teethOffset < length) - { - AlligatorResult teethResult = results[i + teethOffset]; - - // calculate alligator's teeth - // first value: calculate SMA - if (i + 1 == teethPeriods) - { - double sumMedianPrice = 0; - for (int p = i + 1 - teethPeriods; p <= i; p++) - { - sumMedianPrice += pr[p]; - } - - teethResult.Teeth = sumMedianPrice / teethPeriods; - } - - // remaining values: SMMA - else if (i + 1 > teethPeriods) - { - double? prevValue = results[i + teethOffset - 1].Teeth; - teethResult.Teeth = ((prevValue * (teethPeriods - 1)) + pr[i]) / teethPeriods; - } - - teethResult.Teeth = teethResult.Teeth.NaN2Null(); - } - - // only calculate lips if the array offset is still in valid range - if (i + lipsOffset < length) - { - AlligatorResult lipsResult = results[i + lipsOffset]; - - // calculate alligator's lips - // first value: calculate SMA - if (i + 1 == lipsPeriods) - { - double sumMedianPrice = 0; - for (int p = i + 1 - lipsPeriods; p <= i; p++) - { - sumMedianPrice += pr[p]; - } - - lipsResult.Lips = sumMedianPrice / lipsPeriods; - } - - // remaining values: SMMA - else if (i + 1 > lipsPeriods) - { - double? prevValue = results[i + lipsOffset - 1].Lips; - lipsResult.Lips = ((prevValue * (lipsPeriods - 1)) + pr[i]) / lipsPeriods; - } - - lipsResult.Lips = lipsResult.Lips.NaN2Null(); - } - } - - return results; - } - - // parameter validation - private static void ValidateAlligator( - int jawPeriods, - int jawOffset, - int teethPeriods, - int teethOffset, - int lipsPeriods, - int lipsOffset) - { - // check parameter arguments - if (jawPeriods <= teethPeriods) - { - throw new ArgumentOutOfRangeException(nameof(jawPeriods), jawPeriods, - "Jaw lookback periods must be greater than Teeth lookback periods for Alligator."); - } - - if (teethPeriods <= lipsPeriods) - { - throw new ArgumentOutOfRangeException(nameof(teethPeriods), teethPeriods, - "Teeth lookback periods must be greater than Lips lookback periods for Alligator."); - } - - if (lipsPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lipsPeriods), lipsPeriods, - "Lips lookback periods must be greater than 0 for Alligator."); - } - - if (jawOffset <= 0) - { - throw new ArgumentOutOfRangeException(nameof(jawOffset), jawOffset, - "Jaw offset periods must be greater than 0 for Alligator."); - } - - if (teethOffset <= 0) - { - throw new ArgumentOutOfRangeException(nameof(teethOffset), teethOffset, - "Jaw offset periods must be greater than 0 for Alligator."); - } - - if (lipsOffset <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lipsOffset), lipsOffset, - "Jaw offset periods must be greater than 0 for Alligator."); - } - - if (jawPeriods + jawOffset <= teethPeriods + teethOffset) - { - throw new ArgumentOutOfRangeException(nameof(jawPeriods), jawPeriods, - "Jaw lookback + offset are too small for Alligator."); - } - - if (teethPeriods + teethOffset <= lipsPeriods + lipsOffset) - { - throw new ArgumentOutOfRangeException(nameof(teethPeriods), teethPeriods, - "Teeth lookback + offset are too small for Alligator."); - } - } -} diff --git a/src/a-d/Alligator/Alligator.StaticSeries.cs b/src/a-d/Alligator/Alligator.StaticSeries.cs new file mode 100644 index 000000000..f35217b70 --- /dev/null +++ b/src/a-d/Alligator/Alligator.StaticSeries.cs @@ -0,0 +1,143 @@ +namespace Skender.Stock.Indicators; + +// WILLIAMS ALLIGATOR (SERIES) + +public static partial class Alligator +{ + // SERIES, from CHAIN + /// + /// Williams Alligator is an indicator that transposes multiple moving averages, + /// showing chart patterns that creator Bill Williams compared to an alligator's + /// feeding habits when describing market movement. + /// + /// + /// T must be or type + /// + /// Time-series values to transform. + /// Lookback periods for the Jaw line. + /// Offset periods for the Jaw line. + /// Lookback periods for the Teeth line. + /// Offset periods for the Teeth line. + /// Lookback periods for the Lips line. + /// Offset periods for the Lips line. + /// Time series of Alligator values. + /// + /// Invalid parameter value provided. + /// + public static IReadOnlyList ToAlligator( + this IReadOnlyList source, + int jawPeriods = 13, + int jawOffset = 8, + int teethPeriods = 8, + int teethOffset = 5, + int lipsPeriods = 5, + int lipsOffset = 3) + where T : IReusable + { + // check parameter arguments + Validate( + jawPeriods, + jawOffset, + teethPeriods, + teethOffset, + lipsPeriods, + lipsOffset); + + // prefer HL2 when IQuote + IReadOnlyList values + = source.ToPreferredList(CandlePart.HL2); + + // initialize + int length = values.Count; + List results = new(length); + + // roll through source values + for (int i = 0; i < length; i++) + { + double jaw = double.NaN; + double lips = double.NaN; + double teeth = double.NaN; + + // calculate alligator's jaw, when in range + if (i >= jawPeriods + jawOffset - 1) + { + // first/reset value: calculate SMA + if (results[i - 1].Jaw is null) + { + double sum = 0; + for (int p = i - jawPeriods - jawOffset + 1; p <= i - jawOffset; p++) + { + sum += values[p].Value; + } + + jaw = sum / jawPeriods; + } + + // remaining values: SMMA + else + { + double prevJaw = results[i - 1].Jaw.Null2NaN(); + + jaw = ((prevJaw * (jawPeriods - 1)) + values[i - jawOffset].Value) / jawPeriods; + } + } + + // calculate alligator's teeth, when in range + if (i >= teethPeriods + teethOffset - 1) + { + // first/reset value: calculate SMA + if (results[i - 1].Teeth is null) + { + double sum = 0; + for (int p = i - teethPeriods - teethOffset + 1; p <= i - teethOffset; p++) + { + sum += values[p].Value; + } + + teeth = sum / teethPeriods; + } + + // remaining values: SMMA + else + { + double prevTooth = results[i - 1].Teeth.Null2NaN(); + + teeth = ((prevTooth * (teethPeriods - 1)) + values[i - teethOffset].Value) / teethPeriods; + } + } + + // calculate alligator's lips, when in range + if (i >= lipsPeriods + lipsOffset - 1) + { + // first/reset value: calculate SMA + if (results[i - 1].Lips is null) + { + double sum = 0; + for (int p = i - lipsPeriods - lipsOffset + 1; p <= i - lipsOffset; p++) + { + sum += values[p].Value; + } + + lips = sum / lipsPeriods; + } + + // remaining values: SMMA + else + { + double prevLips = results[i - 1].Lips.Null2NaN(); + + lips = ((prevLips * (lipsPeriods - 1)) + values[i - lipsOffset].Value) / lipsPeriods; + } + } + + // result + results.Add(new AlligatorResult( + values[i].Timestamp, + jaw.NaN2Null(), + teeth.NaN2Null(), + lips.NaN2Null())); + } + + return results; + } +} diff --git a/src/a-d/Alligator/Alligator.StreamHub.cs b/src/a-d/Alligator/Alligator.StreamHub.cs new file mode 100644 index 000000000..8ade9b1ac --- /dev/null +++ b/src/a-d/Alligator/Alligator.StreamHub.cs @@ -0,0 +1,177 @@ +namespace Skender.Stock.Indicators; + +// WILLIAMS ALLIGATOR (STREAM HUB) + +#region hub interface and initializer + +public interface IAlligatorHub +{ + int JawPeriods { get; } + int JawOffset { get; } + int TeethPeriods { get; } + int TeethOffset { get; } + int LipsPeriods { get; } + int LipsOffset { get; } +} + +public static partial class Alligator +{ + // HUB, from Chain Provider + public static AlligatorHub ToAlligator( + this IChainProvider chainProvider, + int jawPeriods = 13, + int jawOffset = 8, + int teethPeriods = 8, + int teethOffset = 5, + int lipsPeriods = 5, + int lipsOffset = 3) + where TIn : IReusable + => new( + chainProvider, + jawPeriods, + jawOffset, + teethPeriods, + teethOffset, + lipsPeriods, + lipsOffset); +} +#endregion + +public class AlligatorHub + : StreamHub, IAlligatorHub + where TIn : IReusable +{ + #region constructors + + private readonly string hubName; + + internal AlligatorHub( + IChainProvider provider, + int jawPeriods, int jawOffset, + int teethPeriods, int teethOffset, + int lipsPeriods, int lipsOffset) + : base(provider) + { + Alligator.Validate( + jawPeriods, jawOffset, + teethPeriods, teethOffset, + lipsPeriods, lipsOffset); + + JawPeriods = jawPeriods; + JawOffset = jawOffset; + TeethPeriods = teethPeriods; + TeethOffset = teethOffset; + LipsPeriods = lipsPeriods; + LipsOffset = lipsOffset; + + hubName = $"ALLIGATOR({jawPeriods},{jawOffset},{teethPeriods},{teethOffset},{lipsPeriods},{lipsOffset})"; + + Reinitialize(); + } + #endregion + + public int JawPeriods { get; init; } + public int JawOffset { get; init; } + public int TeethPeriods { get; init; } + public int TeethOffset { get; init; } + public int LipsPeriods { get; init; } + public int LipsOffset { get; init; } + + // METHODS + + public override string ToString() => hubName; + + protected override (AlligatorResult result, int index) + ToIndicator(TIn item, int? indexHint) + { + double jaw = double.NaN; + double lips = double.NaN; + double teeth = double.NaN; + + int i = indexHint ?? ProviderCache.GetIndex(item, true); + + // calculate alligator's jaw, when in range + if (i >= JawPeriods + JawOffset - 1) + { + // first/reset value: calculate SMA + if (Cache[i - 1].Jaw is null) + { + double sum = 0; + for (int p = i - JawPeriods - JawOffset + 1; p <= i - JawOffset; p++) + { + sum += ProviderCache[p].Hl2OrValue(); + } + + jaw = sum / JawPeriods; + } + + // remaining values: SMMA + else + { + double prevJaw = Cache[i - 1].Jaw.Null2NaN(); + double newVal = ProviderCache[i - JawOffset].Hl2OrValue(); + + jaw = ((prevJaw * (JawPeriods - 1)) + newVal) / JawPeriods; + } + } + + // calculate alligator's teeth, when in range + if (i >= TeethPeriods + TeethOffset - 1) + { + // first/reset value: calculate SMA + if (Cache[i - 1].Teeth is null) + { + double sum = 0; + for (int p = i - TeethPeriods - TeethOffset + 1; p <= i - TeethOffset; p++) + { + sum += ProviderCache[p].Hl2OrValue(); + } + + teeth = sum / TeethPeriods; + } + + // remaining values: SMMA + else + { + double prevTooth = Cache[i - 1].Teeth.Null2NaN(); + double newVal = ProviderCache[i - TeethOffset].Hl2OrValue(); + + teeth = ((prevTooth * (TeethPeriods - 1)) + newVal) / TeethPeriods; + } + } + + // calculate alligator's lips, when in range + if (i >= LipsPeriods + LipsOffset - 1) + { + // first/reset value: calculate SMA + if (Cache[i - 1].Lips is null) + { + double sum = 0; + for (int p = i - LipsPeriods - LipsOffset + 1; p <= i - LipsOffset; p++) + { + sum += ProviderCache[p].Hl2OrValue(); + } + + lips = sum / LipsPeriods; + } + + // remaining values: SMMA + else + { + double prevLips = Cache[i - 1].Lips.Null2NaN(); + double newVal = ProviderCache[i - LipsOffset].Hl2OrValue(); + + lips = ((prevLips * (LipsPeriods - 1)) + newVal) / LipsPeriods; + } + } + + // candidate result + AlligatorResult r = new( + item.Timestamp, + jaw.NaN2Null(), + teeth.NaN2Null(), + lips.NaN2Null()); + + return (r, i); + } +} diff --git a/src/a-d/Alligator/Alligator.Utilities.cs b/src/a-d/Alligator/Alligator.Utilities.cs index e45557794..03050cb34 100644 --- a/src/a-d/Alligator/Alligator.Utilities.cs +++ b/src/a-d/Alligator/Alligator.Utilities.cs @@ -1,11 +1,11 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// WILLIAMS ALLIGATOR (UTILITIES) + +public static partial class Alligator { // CONDENSE (REMOVE null results) - /// - /// - public static IEnumerable Condense( + public static IReadOnlyList Condense( this IEnumerable results) { List resultsList = results @@ -19,10 +19,8 @@ public static IEnumerable Condense( } // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -30,4 +28,63 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int jawPeriods, + int jawOffset, + int teethPeriods, + int teethOffset, + int lipsPeriods, + int lipsOffset) + { + // check parameter arguments + if (jawPeriods <= teethPeriods) + { + throw new ArgumentOutOfRangeException(nameof(jawPeriods), jawPeriods, + "Jaw lookback periods must be greater than Teeth lookback periods for Alligator."); + } + + if (teethPeriods <= lipsPeriods) + { + throw new ArgumentOutOfRangeException(nameof(teethPeriods), teethPeriods, + "Teeth lookback periods must be greater than Lips lookback periods for Alligator."); + } + + if (lipsPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lipsPeriods), lipsPeriods, + "Lips lookback periods must be greater than 0 for Alligator."); + } + + if (jawOffset <= 0) + { + throw new ArgumentOutOfRangeException(nameof(jawOffset), jawOffset, + "Jaw offset periods must be greater than 0 for Alligator."); + } + + if (teethOffset <= 0) + { + throw new ArgumentOutOfRangeException(nameof(teethOffset), teethOffset, + "Jaw offset periods must be greater than 0 for Alligator."); + } + + if (lipsOffset <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lipsOffset), lipsOffset, + "Jaw offset periods must be greater than 0 for Alligator."); + } + + if (jawPeriods + jawOffset <= teethPeriods + teethOffset) + { + throw new ArgumentOutOfRangeException(nameof(jawPeriods), jawPeriods, + "Jaw lookback + offset are too small for Alligator."); + } + + if (teethPeriods + teethOffset <= lipsPeriods + lipsOffset) + { + throw new ArgumentOutOfRangeException(nameof(teethPeriods), teethPeriods, + "Teeth lookback + offset are too small for Alligator."); + } + } } diff --git a/src/a-d/Alligator/info.xml b/src/a-d/Alligator/info.xml deleted file mode 100644 index fff1c5de1..000000000 --- a/src/a-d/Alligator/info.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - Williams Alligator is an indicator that transposes multiple moving averages, - showing chart patterns that creator Bill Williams compared to an alligator's - feeding habits when describing market movement. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Lookback periods for the Jaw line. - Offset periods for the Jaw line. - Lookback periods for the Teeth line. - Offset periods for the Teeth line. - Lookback periods for the Lips line. - Offset periods for the Lips line. - Time series of Alligator values. - Invalid parameter value provided. - diff --git a/src/a-d/Alma/Alma.Api.cs b/src/a-d/Alma/Alma.Api.cs deleted file mode 100644 index ba18029a1..000000000 --- a/src/a-d/Alma/Alma.Api.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ARNAUD LEGOUX MOVING AVERAGE (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetAlma( - this IEnumerable quotes, - int lookbackPeriods = 9, - double offset = 0.85, - double sigma = 6) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcAlma(lookbackPeriods, offset, sigma); - - // SERIES, from CHAIN - public static IEnumerable GetAlma( - this IEnumerable results, - int lookbackPeriods = 9, - double offset = 0.85, - double sigma = 6) => results - .ToTuple() - .CalcAlma(lookbackPeriods, offset, sigma) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetAlma( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods = 9, - double offset = 0.85, - double sigma = 6) => priceTuples - .ToSortedList() - .CalcAlma(lookbackPeriods, offset, sigma); -} diff --git a/src/a-d/Alma/Alma.Models.cs b/src/a-d/Alma/Alma.Models.cs index 1ce7e3570..419a23d56 100644 --- a/src/a-d/Alma/Alma.Models.cs +++ b/src/a-d/Alma/Alma.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class AlmaResult : ResultBase, IReusableResult +public record AlmaResult +( + DateTime Timestamp, + double? Alma +) : IReusable { - public AlmaResult(DateTime date) - { - Date = date; - } - - public double? Alma { get; set; } - - double? IReusableResult.Value => Alma; + public double Value => Alma.Null2NaN(); } diff --git a/src/a-d/Alma/Alma.Series.cs b/src/a-d/Alma/Alma.Series.cs deleted file mode 100644 index 6644e1caf..000000000 --- a/src/a-d/Alma/Alma.Series.cs +++ /dev/null @@ -1,84 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ARNAUD LEGOUX MOVING AVERAGE (SERIES) -public static partial class Indicator -{ - internal static List CalcAlma( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - double offset, - double sigma) - { - // check parameter arguments - ValidateAlma(lookbackPeriods, offset, sigma); - - // initialize - List results = new(tpList.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 < tpList.Count; i++) - { - (DateTime date, double _) = tpList[i]; - - AlmaResult r = new(date); - results.Add(r); - - if (i + 1 >= lookbackPeriods) - { - double? weightedSum = 0; - int n = 0; - - for (int p = i + 1 - lookbackPeriods; p <= i; p++) - { - (DateTime _, double pValue) = tpList[p]; - weightedSum += weight[n] * pValue; - n++; - } - - r.Alma = (weightedSum / norm).NaN2Null(); - } - } - - return results; - } - - // parameter validation - private static void ValidateAlma( - int lookbackPeriods, - double offset, - double sigma) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for ALMA."); - } - - if (offset is < 0 or > 1) - { - throw new ArgumentOutOfRangeException(nameof(offset), offset, - "Offset must be between 0 and 1 for ALMA."); - } - - if (sigma <= 0) - { - throw new ArgumentOutOfRangeException(nameof(sigma), sigma, - "Sigma must be greater than 0 for ALMA."); - } - } -} diff --git a/src/a-d/Alma/Alma.StaticSeries.cs b/src/a-d/Alma/Alma.StaticSeries.cs new file mode 100644 index 000000000..372f68c9e --- /dev/null +++ b/src/a-d/Alma/Alma.StaticSeries.cs @@ -0,0 +1,63 @@ +namespace Skender.Stock.Indicators; + +// ARNAUD LEGOUX MOVING AVERAGE (SERIES) + +public static partial class Alma +{ + public static IReadOnlyList ToAlma( + this IReadOnlyList source, + int lookbackPeriods = 9, + double offset = 0.85, + double sigma = 6) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods, offset, sigma); + + // initialize + int length = source.Count; + List results = new(length); + + // determine price weight constants + 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 source values + for (int i = 0; i < length; i++) + { + double alma = double.NaN; + + if (i + 1 >= lookbackPeriods) + { + double weightedSum = 0; + int n = 0; + + for (int p = i + 1 - lookbackPeriods; p <= i; p++) + { + T ps = source[p]; + weightedSum += weight[n] * ps.Value; + n++; + } + + alma = weightedSum / norm; + } + + results.Add( + new(Timestamp: source[i].Timestamp, + Alma: alma.NaN2Null())); + } + + return results; + } +} diff --git a/src/a-d/Alma/Alma.Utilities.cs b/src/a-d/Alma/Alma.Utilities.cs index 305067db9..da7f11eb8 100644 --- a/src/a-d/Alma/Alma.Utilities.cs +++ b/src/a-d/Alma/Alma.Utilities.cs @@ -1,17 +1,32 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ARNAUD LEGOUX MOVING AVERAGE (UTILITIES) + +public static partial class Alma { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods, + double offset, + double sigma) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Alma != null); + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for ALMA."); + } + + if (offset is < 0 or > 1) + { + throw new ArgumentOutOfRangeException(nameof(offset), offset, + "Offset must be between 0 and 1 for ALMA."); + } - return results.Remove(removePeriods); + if (sigma <= 0) + { + throw new ArgumentOutOfRangeException(nameof(sigma), sigma, + "Sigma must be greater than 0 for ALMA."); + } } } diff --git a/src/a-d/Aroon/Aroon.Api.cs b/src/a-d/Aroon/Aroon.Api.cs deleted file mode 100644 index ca259c74b..000000000 --- a/src/a-d/Aroon/Aroon.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// AROON OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetAroon( - this IEnumerable quotes, - int lookbackPeriods = 25) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcAroon(lookbackPeriods); -} diff --git a/src/a-d/Aroon/Aroon.Models.cs b/src/a-d/Aroon/Aroon.Models.cs index 50fdc2ddf..5c4adcdda 100644 --- a/src/a-d/Aroon/Aroon.Models.cs +++ b/src/a-d/Aroon/Aroon.Models.cs @@ -1,16 +1,13 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class AroonResult : ResultBase, IReusableResult +public record AroonResult +( + DateTime Timestamp, + double? AroonUp, + double? AroonDown, + double? Oscillator +) : IReusable { - public AroonResult(DateTime date) - { - Date = date; - } - - public double? AroonUp { get; set; } - public double? AroonDown { get; set; } - public double? Oscillator { get; set; } - - double? IReusableResult.Value => Oscillator; + public double Value => Oscillator.Null2NaN(); } diff --git a/src/a-d/Aroon/Aroon.Series.cs b/src/a-d/Aroon/Aroon.Series.cs deleted file mode 100644 index 4af941670..000000000 --- a/src/a-d/Aroon/Aroon.Series.cs +++ /dev/null @@ -1,69 +0,0 @@ -namespace Skender.Stock.Indicators; - -// AROON OSCILLATOR (SERIES) -public static partial class Indicator -{ - internal static List CalcAroon( - this List qdList, - int lookbackPeriods) - { - // check parameter arguments - ValidateAroon(lookbackPeriods); - - // initialize - List results = new(qdList.Count); - - // roll through quotes - for (int i = 0; i < qdList.Count; i++) - { - QuoteD q = qdList[i]; - - AroonResult r = new(q.Date); - results.Add(r); - - // add aroons - if (i + 1 > lookbackPeriods) - { - double? lastHighPrice = 0; - double? lastLowPrice = double.MaxValue; - int lastHighIndex = 0; - int lastLowIndex = 0; - - for (int p = i + 1 - lookbackPeriods - 1; p <= i; p++) - { - QuoteD d = qdList[p]; - - if (d.High > lastHighPrice) - { - lastHighPrice = d.High; - lastHighIndex = p + 1; - } - - if (d.Low < lastLowPrice) - { - lastLowPrice = d.Low; - lastLowIndex = p + 1; - } - } - - r.AroonUp = 100d * (lookbackPeriods - (i + 1 - lastHighIndex)) / lookbackPeriods; - r.AroonDown = 100d * (lookbackPeriods - (i + 1 - lastLowIndex)) / lookbackPeriods; - r.Oscillator = r.AroonUp - r.AroonDown; - } - } - - return results; - } - - // parameter validation - private static void ValidateAroon( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Aroon."); - } - } -} diff --git a/src/a-d/Aroon/Aroon.StaticSeries.cs b/src/a-d/Aroon/Aroon.StaticSeries.cs new file mode 100644 index 000000000..4fcdbe3be --- /dev/null +++ b/src/a-d/Aroon/Aroon.StaticSeries.cs @@ -0,0 +1,73 @@ +namespace Skender.Stock.Indicators; + +// AROON OSCILLATOR (SERIES) + +public static partial class Aroon +{ + public static IReadOnlyList ToAroon( + this IReadOnlyList quotes, + int lookbackPeriods = 25) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcAroon(lookbackPeriods); + + private static List CalcAroon( + this IReadOnlyList source, + int lookbackPeriods) + { + // check parameter arguments + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + // roll through source values + for (int i = 0; i < length; i++) + { + QuoteD q = source[i]; + double? aroonUp = null; + double? aroonDown = null; + + // add aroons + if (i + 1 > lookbackPeriods) + { + double? lastHighPrice = 0; + double? lastLowPrice = double.MaxValue; + int lastHighIndex = 0; + int lastLowIndex = 0; + + for (int p = i + 1 - lookbackPeriods - 1; p <= i; p++) + { + QuoteD d = source[p]; + + if (d.High > lastHighPrice) + { + lastHighPrice = d.High; + lastHighIndex = p + 1; + } + + if (d.Low < lastLowPrice) + { + lastLowPrice = d.Low; + lastLowIndex = p + 1; + } + } + + aroonUp = 100d * (lookbackPeriods - (i + 1 - lastHighIndex)) / lookbackPeriods; + aroonDown = 100d * (lookbackPeriods - (i + 1 - lastLowIndex)) / lookbackPeriods; + } + + AroonResult r = new( + Timestamp: q.Timestamp, + AroonUp: aroonUp, + AroonDown: aroonDown, + Oscillator: aroonUp - aroonDown); + + results.Add(r); + + } + + return results; + } +} diff --git a/src/a-d/Aroon/Aroon.Utilities.cs b/src/a-d/Aroon/Aroon.Utilities.cs index 619c2ac3b..32f51769d 100644 --- a/src/a-d/Aroon/Aroon.Utilities.cs +++ b/src/a-d/Aroon/Aroon.Utilities.cs @@ -1,17 +1,16 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +public static partial class Aroon { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Oscillator != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Aroon."); + } } } diff --git a/src/a-d/Atr/Atr.Api.cs b/src/a-d/Atr/Atr.Api.cs deleted file mode 100644 index 82bd946f5..000000000 --- a/src/a-d/Atr/Atr.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// AVERAGE TRUE RANGE (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetAtr( - this IEnumerable quotes, - int lookbackPeriods = 14) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcAtr(lookbackPeriods); -} diff --git a/src/a-d/Atr/Atr.Models.cs b/src/a-d/Atr/Atr.Models.cs index d2980015c..67097be0f 100644 --- a/src/a-d/Atr/Atr.Models.cs +++ b/src/a-d/Atr/Atr.Models.cs @@ -1,16 +1,13 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class AtrResult : ResultBase, IReusableResult +public record AtrResult +( + DateTime Timestamp, + double? Tr = null, + double? Atr = null, + double? Atrp = null +) : IReusable { - public AtrResult(DateTime date) - { - Date = date; - } - - public double? Tr { get; set; } - public double? Atr { get; set; } - public double? Atrp { get; set; } - - double? IReusableResult.Value => Atrp; + public double Value => Atrp.Null2NaN(); } diff --git a/src/a-d/Atr/Atr.Series.cs b/src/a-d/Atr/Atr.Series.cs deleted file mode 100644 index d04a72d2b..000000000 --- a/src/a-d/Atr/Atr.Series.cs +++ /dev/null @@ -1,84 +0,0 @@ -namespace Skender.Stock.Indicators; - -// AVERAGE TRUE RANGE (SERIES) -public static partial class Indicator -{ - // calculate series - internal static List CalcAtr( - this List qdList, - int lookbackPeriods) - { - // check parameter arguments - ValidateAtr(lookbackPeriods); - - // initialize - List results = new(qdList.Count); - double prevAtr = double.NaN; - double prevClose = double.NaN; - double sumTr = 0; - - // roll through quotes - for (int i = 0; i < qdList.Count; i++) - { - double hmpc; - double lmpc; - QuoteD q = qdList[i]; - - AtrResult r = new(q.Date); - results.Add(r); - - if (i > 0) - { - hmpc = Math.Abs(q.High - prevClose); - lmpc = Math.Abs(q.Low - prevClose); - } - else - { - prevClose = q.Close; - continue; - } - - double tr = Math.Max(q.High - q.Low, Math.Max(hmpc, lmpc)); - r.Tr = tr; - - if (i > lookbackPeriods) - { - // calculate ATR - double atr = ((prevAtr * (lookbackPeriods - 1)) + tr) / lookbackPeriods; - r.Atr = atr; - r.Atrp = (q.Close == 0) ? null : atr / q.Close * 100; - prevAtr = atr; - } - else if (i == lookbackPeriods) - { - // initialize ATR - sumTr += tr; - double atr = sumTr / lookbackPeriods; - r.Atr = atr; - r.Atrp = (q.Close == 0) ? null : atr / q.Close * 100; - prevAtr = atr; - } - else - { - // only used for periods before ATR initialization - sumTr += tr; - } - - prevClose = q.Close; - } - - return results; - } - - // parameter validation - private static void ValidateAtr( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for Average True Range."); - } - } -} diff --git a/src/a-d/Atr/Atr.StaticSeries.cs b/src/a-d/Atr/Atr.StaticSeries.cs new file mode 100644 index 000000000..10db80315 --- /dev/null +++ b/src/a-d/Atr/Atr.StaticSeries.cs @@ -0,0 +1,90 @@ +namespace Skender.Stock.Indicators; + +// AVERAGE TRUE RANGE (SERIES) + +public static partial class Atr +{ + public static IReadOnlyList ToAtr( + this IReadOnlyList quotes, + int lookbackPeriods = 14) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcAtr(lookbackPeriods); + + internal static List CalcAtr( + this IReadOnlyList source, + int lookbackPeriods) + { + // check parameter arguments + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + double prevAtr = double.NaN; + double prevClose = double.NaN; + double sumTr = 0; + + // skip first period + if (length > 0) + { + QuoteD q = source[0]; + results.Add(new(Timestamp: q.Timestamp)); + prevClose = q.Close; + } + + // roll through source values + for (int i = 1; i < length; i++) + { + QuoteD q = source[i]; + + double hmpc = Math.Abs(q.High - prevClose); + double lmpc = Math.Abs(q.Low - prevClose); + + double tr = Math.Max(q.High - q.Low, Math.Max(hmpc, lmpc)); + + double atr; + double? atrp; + + if (i > lookbackPeriods) + { + // calculate ATR + atr = ((prevAtr * (lookbackPeriods - 1)) + tr) / lookbackPeriods; + atrp = q.Close == 0 ? null : atr / q.Close * 100; + prevAtr = atr; + } + + // TODO: update healing, without requiring specific indexing, + // have had trouble gettng this one to work when evaluating previous ATR values + else if (i == lookbackPeriods) + { + // initialize ATR + sumTr += tr; + atr = sumTr / lookbackPeriods; + atrp = q.Close == 0 ? null : atr / q.Close * 100; + prevAtr = atr; + } + + // only used for initialization periods + else + { + sumTr += tr; + + atr = double.NaN; + atrp = null; + } + + AtrResult r = new( + Timestamp: q.Timestamp, + Tr: tr.NaN2Null(), + Atr: atr.NaN2Null(), + Atrp: atrp); + + results.Add(r); + + prevClose = q.Close; + } + + return results; + } +} diff --git a/src/a-d/Atr/Atr.StreamHub.cs b/src/a-d/Atr/Atr.StreamHub.cs new file mode 100644 index 000000000..d37d45062 --- /dev/null +++ b/src/a-d/Atr/Atr.StreamHub.cs @@ -0,0 +1,98 @@ +namespace Skender.Stock.Indicators; + +// AVERAGE TRUE RANGE (STREAM HUB) + +#region hub interface and initializer + +public interface IAtrHub +{ + int LookbackPeriods { get; } +} + +public static partial class Atr +{ + public static AtrHub ToAtr( + this IQuoteProvider quoteProvider, + int lookbackPeriods = 14) + where TIn : IQuote + => new(quoteProvider, lookbackPeriods); +} +#endregion + +public class AtrHub + : ChainProvider, IAtrHub + where TIn : IQuote +{ + #region constructors + + private readonly string hubName; + + internal AtrHub(IQuoteProvider provider, + int lookbackPeriods) + : base(provider) + { + Atr.Validate(lookbackPeriods); + LookbackPeriods = lookbackPeriods; + hubName = $"ATR({lookbackPeriods})"; + + Reinitialize(); + } + #endregion + + public int LookbackPeriods { get; init; } + + // METHODS + + public override string ToString() => hubName; + + protected override (AtrResult result, int index) + ToIndicator(TIn item, int? indexHint) + { + int i = indexHint ?? ProviderCache.GetIndex(item, true); + + // skip incalculable periods + if (i == 0) + { + return (new AtrResult(item.Timestamp), i); + } + + AtrResult r; + + // re-initialize as average TR, if necessary + if (Cache[i - 1].Atr is null && i >= LookbackPeriods) + { + double sumTr = 0; + double tr = double.NaN; + + for (int p = i - LookbackPeriods + 1; p <= i; p++) + { + tr = Tr.Increment( + (double)ProviderCache[p].High, + (double)ProviderCache[p].Low, + (double)ProviderCache[p - 1].Close); + + sumTr += tr; + } + + double atr = sumTr / LookbackPeriods; + + r = new AtrResult( + item.Timestamp, + tr, + atr, + atr / (double)item.Close * 100); + } + + // calculate ATR (normally) + else + { + r = Atr.Increment( + LookbackPeriods, + item, + (double)ProviderCache[i - 1].Close, + Cache[i - 1].Atr); + } + + return (r, i); + } +} diff --git a/src/a-d/Atr/Atr.Utilities.cs b/src/a-d/Atr/Atr.Utilities.cs index 700f6f584..febeb0aa6 100644 --- a/src/a-d/Atr/Atr.Utilities.cs +++ b/src/a-d/Atr/Atr.Utilities.cs @@ -1,17 +1,53 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +public static partial class Atr { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // increment + internal static double Increment( + int lookbackPeriods, + double high, + double low, + double prevClose, + double prevAtr) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Atr != null); + double tr = Tr.Increment(high, low, prevClose); + return ((prevAtr * (lookbackPeriods - 1)) + tr) / lookbackPeriods; - return results.Remove(removePeriods); + // TODO: this may be unused, verify before making public + } + + // increment + public static AtrResult Increment( + int lookbackPeriods, + TQuote quote, + double prevClose, + double? prevAtr) + where TQuote : IQuote + { + double high = (double)quote.High; + double low = (double)quote.Low; + double close = (double)quote.Close; + + double tr = Tr.Increment(high, low, prevClose); + double atr = (((prevAtr ?? double.NaN) * (lookbackPeriods - 1)) + tr) / lookbackPeriods; + double atrp = close == 0 ? double.NaN : atr / close * 100; + + return new AtrResult( + quote.Timestamp, + tr, + atr.NaN2Null(), + atrp.NaN2Null()); + } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for Average True Range."); + } } } diff --git a/src/a-d/AtrStop/AtrStop.Api.cs b/src/a-d/AtrStop/AtrStop.Api.cs deleted file mode 100644 index ce988ee85..000000000 --- a/src/a-d/AtrStop/AtrStop.Api.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ATR TRAILING STOP (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetAtrStop( - this IEnumerable quotes, - int lookbackPeriods = 21, - double multiplier = 3, - EndType endType = EndType.Close) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcAtrStop(lookbackPeriods, multiplier, endType); -} diff --git a/src/a-d/AtrStop/AtrStop.Models.cs b/src/a-d/AtrStop/AtrStop.Models.cs index 13366f719..35e4938e1 100644 --- a/src/a-d/AtrStop/AtrStop.Models.cs +++ b/src/a-d/AtrStop/AtrStop.Models.cs @@ -1,14 +1,28 @@ namespace Skender.Stock.Indicators; +/// +/// ATR Trailing Stop result +/// +/// +/// Date corresponding to evaluated price data +/// +/// +/// Trailing stop line (includes both buy and sell stops) +/// +/// +/// Stop line (buy to close) short position +/// +/// +/// Stop line (sell to close) long position +/// +/// +/// Average True Range (ATR) +/// [Serializable] -public sealed class AtrStopResult : ResultBase -{ - public AtrStopResult(DateTime date) - { - Date = date; - } - - public decimal? AtrStop { get; set; } - public decimal? BuyStop { get; set; } - public decimal? SellStop { get; set; } -} +public record AtrStopResult( + DateTime Timestamp, + double? AtrStop = null, + double? BuyStop = null, + double? SellStop = null, + double? Atr = null +) : ISeries; diff --git a/src/a-d/AtrStop/AtrStop.Series.cs b/src/a-d/AtrStop/AtrStop.Series.cs deleted file mode 100644 index 9da4ababf..000000000 --- a/src/a-d/AtrStop/AtrStop.Series.cs +++ /dev/null @@ -1,111 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ATR TRAILING STOP (SERIES) -public static partial class Indicator -{ - internal static List CalcAtrStop( - this List qdList, - int lookbackPeriods, - double multiplier, - EndType endType) - { - // check parameter arguments - ValidateAtrStop(lookbackPeriods, multiplier); - - // initialize - List results = new(qdList.Count); - List atrResults = qdList.CalcAtr(lookbackPeriods); - - bool isBullish = true; - double? upperBand = null; - double? lowerBand = null; - - // roll through quotes - for (int i = 0; i < qdList.Count; i++) - { - QuoteD q = qdList[i]; - - AtrStopResult r = new(q.Date); - results.Add(r); - - if (i >= lookbackPeriods) - { - double? atr = atrResults[i].Atr; - QuoteD p = qdList[i - 1]; - - double? upperEval; - double? lowerEval; - - // potential bands for CLOSE - if (endType == EndType.Close) - { - upperEval = q.Close + (multiplier * atr); - lowerEval = q.Close - (multiplier * atr); - } - - // potential bands for HIGH/LOW - else - { - upperEval = q.High + (multiplier * atr); - lowerEval = q.Low - (multiplier * atr); - } - - // initial values - if (i == lookbackPeriods) - { - isBullish = q.Close >= p.Close; - - upperBand = upperEval; - lowerBand = lowerEval; - } - - // new upper band - if (upperEval < upperBand || p.Close > upperBand) - { - upperBand = upperEval; - } - - // new lower band - if (lowerEval > lowerBand || p.Close < lowerBand) - { - lowerBand = lowerEval; - } - - // trailing stop - if (q.Close <= (isBullish ? lowerBand : upperBand)) - { - r.AtrStop = (decimal?)upperBand; - r.BuyStop = (decimal?)upperBand; - isBullish = false; - } - else - { - r.AtrStop = (decimal?)lowerBand; - r.SellStop = (decimal?)lowerBand; - isBullish = true; - } - } - } - - return results; - } - - // parameter validation - private static void ValidateAtrStop( - int lookbackPeriods, - double multiplier) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for ATR Trailing Stop."); - } - - if (multiplier <= 0) - { - throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, - "Multiplier must be greater than 0 for ATR Trailing Stop."); - } - } -} diff --git a/src/a-d/AtrStop/AtrStop.StaticSeries.cs b/src/a-d/AtrStop/AtrStop.StaticSeries.cs new file mode 100644 index 000000000..b4b840f48 --- /dev/null +++ b/src/a-d/AtrStop/AtrStop.StaticSeries.cs @@ -0,0 +1,120 @@ +namespace Skender.Stock.Indicators; + +// ATR TRAILING STOP (SERIES) + +public static partial class AtrStop +{ + public static IReadOnlyList ToAtrStop( + this IReadOnlyList quotes, + int lookbackPeriods = 21, + double multiplier = 3, + EndType endType = EndType.Close) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcAtrStop(lookbackPeriods, multiplier, endType); + + private static List CalcAtrStop( + this IReadOnlyList source, + int lookbackPeriods, + double multiplier, + EndType endType) + { + // check parameter arguments + Validate(lookbackPeriods, multiplier); + + // initialize + int length = source.Count; + List results = new(length); + List atrResults = source.CalcAtr(lookbackPeriods); + + // prevailing direction and bands + bool isBullish = true; + double upperBand = double.MaxValue; + double lowerBand = double.MinValue; + + // roll through source values + for (int i = 0; i < length; i++) + { + // handle warmup periods + if (i < lookbackPeriods) + { + results.Add(new(Timestamp: source[i].Timestamp)); + continue; + } + + QuoteD q = source[i]; + QuoteD p = source[i - 1]; + + // initialize direction on first evaluation + if (i == lookbackPeriods) + { + isBullish = q.Close >= p.Close; + } + + // evaluate bands + double upperEval; + double lowerEval; + double atr = atrResults[i].Atr ?? double.NaN; + + // potential bands for CLOSE + if (endType == EndType.Close) + { + upperEval = q.Close + (multiplier * atr); + lowerEval = q.Close - (multiplier * atr); + } + + // potential bands for HIGH/LOW + else + { + upperEval = q.High + (multiplier * atr); + lowerEval = q.Low - (multiplier * atr); + } + + // new upper band: can only go down, or reverse + if (upperEval < upperBand || p.Close > upperBand) + { + upperBand = upperEval; + } + + // new lower band: can only go up, or reverse + if (lowerEval > lowerBand || p.Close < lowerBand) + { + lowerBand = lowerEval; + } + + // trailing stop: based on direction + + AtrStopResult r; + + // the upper band (short / buy-to-stop) + if (q.Close <= (isBullish ? lowerBand : upperBand)) + { + isBullish = false; + + r = new( + Timestamp: q.Timestamp, + AtrStop: upperBand, + BuyStop: upperBand, + SellStop: null, + Atr: atr); + } + + // the lower band (long / sell-to-stop) + else + { + isBullish = true; + + r = new( + Timestamp: q.Timestamp, + AtrStop: lowerBand, + BuyStop: null, + SellStop: lowerBand, + Atr: atr); + } + + results.Add(r); + } + + return results; + } +} diff --git a/src/a-d/AtrStop/AtrStop.StreamHub.cs b/src/a-d/AtrStop/AtrStop.StreamHub.cs new file mode 100644 index 000000000..20c0e7d0e --- /dev/null +++ b/src/a-d/AtrStop/AtrStop.StreamHub.cs @@ -0,0 +1,205 @@ +namespace Skender.Stock.Indicators; + +// ATR TRAILING STOP (STREAM HUB) + +#region hub interface and initializer + +public interface IAtrStopHub +{ + int LookbackPeriods { get; } + double Multiplier { get; } + EndType EndType { get; } +} + +public static partial class AtrStop +{ + public static AtrStopHub ToAtrStop( + this IQuoteProvider quoteProvider, + int lookbackPeriods = 21, + double multiplier = 3, + EndType endType = EndType.Close) + where TIn : IQuote + => new(quoteProvider, lookbackPeriods, multiplier, endType); +} +#endregion + +public class AtrStopHub + : StreamHub, IAtrStopHub + where TIn : IQuote +{ + #region constructors + + private readonly string hubName; + + internal AtrStopHub( + IQuoteProvider provider, + int lookbackPeriods, + double multiplier, + EndType endType) : base(provider) + { + AtrStop.Validate(lookbackPeriods, multiplier); + + LookbackPeriods = lookbackPeriods; + Multiplier = multiplier; + EndType = endType; + hubName = $"ATR-STOP({lookbackPeriods},{multiplier},{endType.ToString().ToUpperInvariant()})"; + + Reinitialize(); + } + #endregion + + public int LookbackPeriods { get; init; } + public double Multiplier { get; init; } + public EndType EndType { get; init; } + + // prevailing direction and band thresholds + private bool IsBullish { get; set; } = true; + private double UpperBand { get; set; } = double.MaxValue; + private double LowerBand { get; set; } = double.MinValue; + + // METHODS + + public override string ToString() => hubName; + + protected override (AtrStopResult result, int index) + ToIndicator(TIn item, int? indexHint) + { + // reminder: should only process "new" instructions + + int i = indexHint ?? ProviderCache.GetIndex(item, true); + + // handle warmup periods + if (i < LookbackPeriods) + { + return (new AtrStopResult(item.Timestamp), i); + } + + QuoteD newQ = item.ToQuoteD(); + double prevClose = (double)ProviderCache[i - 1].Close; + + // initialize direction on first evaluation + if (i == LookbackPeriods) + { + IsBullish = newQ.Close >= prevClose; + } + + // calculate ATR + double atr; + + if (Cache[i - 1].Atr is not null) + { + atr = Atr.Increment( + LookbackPeriods, + newQ.High, + newQ.Low, + prevClose, + Cache[i - 1].Atr ?? double.NaN); + } + + // initialize ATR + else + { + double sumTr = 0; + + for (int p = i - LookbackPeriods + 1; p <= i; p++) + { + sumTr += Tr.Increment( + (double)ProviderCache[p].High, + (double)ProviderCache[p].Low, + (double)ProviderCache[p - 1].Close); + } + + atr = sumTr / LookbackPeriods; + } + + // evaluate bands + double upperEval; + double lowerEval; + + // potential bands for CLOSE + if (EndType == EndType.Close) + { + upperEval = newQ.Close + (Multiplier * atr); + lowerEval = newQ.Close - (Multiplier * atr); + } + + // potential bands for HIGH/LOW + else + { + upperEval = newQ.High + (Multiplier * atr); + lowerEval = newQ.Low - (Multiplier * atr); + } + + // new upper band: can only go down, or reverse + if (upperEval < UpperBand || prevClose > UpperBand) + { + UpperBand = upperEval; + } + + // new lower band: can only go up, or reverse + if (lowerEval > LowerBand || prevClose < LowerBand) + { + LowerBand = lowerEval; + } + + // trailing stop: based on direction + + AtrStopResult r; + + // the upper band (short / buy-to-stop) + if (newQ.Close <= (IsBullish ? LowerBand : UpperBand)) + { + IsBullish = false; + + r = new AtrStopResult( + Timestamp: newQ.Timestamp, + AtrStop: UpperBand, + BuyStop: UpperBand, + SellStop: null, + Atr: atr); + } + + // the lower band (long / sell-to-stop) + else + { + IsBullish = true; + + r = new AtrStopResult( + Timestamp: newQ.Timestamp, + AtrStop: LowerBand, + BuyStop: null, + SellStop: LowerBand, + Atr: atr); + } + + return (r, i); + } + + /// + /// Restore prior ATR Stop + /// + /// + protected override void RollbackState(DateTime timestamp) + { + int i = ProviderCache.GetIndexGte(timestamp); + + // restore prior stop point + if (i > LookbackPeriods) + { + AtrStopResult resetStop = Cache[i - 1]; + + // prevailing direction and bands + IsBullish = resetStop.AtrStop >= resetStop.SellStop; + UpperBand = resetStop.BuyStop ?? default; + LowerBand = resetStop.SellStop ?? default; + } + + // or reset if no prior stop found + else + { + IsBullish = default; + UpperBand = default; + LowerBand = default; + } + } +} diff --git a/src/a-d/AtrStop/AtrStop.Utilities.cs b/src/a-d/AtrStop/AtrStop.Utilities.cs index 62d910708..be667403f 100644 --- a/src/a-d/AtrStop/AtrStop.Utilities.cs +++ b/src/a-d/AtrStop/AtrStop.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ATR TRAILING STOP (UTILITIES) + +public static partial class AtrStop { // CONDENSE (REMOVE null results) - /// - /// - public static IEnumerable Condense( - this IEnumerable results) + /// + public static IReadOnlyList Condense( + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -17,10 +18,9 @@ public static IEnumerable Condense( } // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -28,4 +28,23 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods, + double multiplier) + { + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for ATR Trailing Stop."); + } + + if (multiplier <= 0) + { + throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, + "Multiplier must be greater than 0 for ATR Trailing Stop."); + } + } } diff --git a/src/a-d/Awesome/Awesome.Api.cs b/src/a-d/Awesome/Awesome.Api.cs deleted file mode 100644 index bc1af8ec4..000000000 --- a/src/a-d/Awesome/Awesome.Api.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// AWESOME OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetAwesome( - this IEnumerable quotes, - int fastPeriods = 5, - int slowPeriods = 34) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.HL2) - .CalcAwesome(fastPeriods, slowPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetAwesome( - this IEnumerable results, - int fastPeriods = 5, - int slowPeriods = 34) => results - .ToTuple() - .CalcAwesome(fastPeriods, slowPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetAwesome( - this IEnumerable<(DateTime, double)> priceTuples, - int fastPeriods = 5, - int slowPeriods = 34) => priceTuples - .ToSortedList() - .CalcAwesome(fastPeriods, slowPeriods); -} diff --git a/src/a-d/Awesome/Awesome.Models.cs b/src/a-d/Awesome/Awesome.Models.cs index a0dd84a03..ed1dd78de 100644 --- a/src/a-d/Awesome/Awesome.Models.cs +++ b/src/a-d/Awesome/Awesome.Models.cs @@ -1,15 +1,12 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class AwesomeResult : ResultBase, IReusableResult +public record AwesomeResult +( + DateTime Timestamp, + double? Oscillator, + double? Normalized +) : IReusable { - public AwesomeResult(DateTime date) - { - Date = date; - } - - public double? Oscillator { get; set; } - public double? Normalized { get; set; } - - double? IReusableResult.Value => Oscillator; + public double Value => Oscillator.Null2NaN(); } diff --git a/src/a-d/Awesome/Awesome.Series.cs b/src/a-d/Awesome/Awesome.Series.cs deleted file mode 100644 index 6cdda1620..000000000 --- a/src/a-d/Awesome/Awesome.Series.cs +++ /dev/null @@ -1,69 +0,0 @@ -namespace Skender.Stock.Indicators; - -// AWESOME OSCILLATOR (SERIES) -public static partial class Indicator -{ - internal static List CalcAwesome( - this List<(DateTime, double)> tpList, - int fastPeriods, - int slowPeriods) - { - // check parameter arguments - ValidateAwesome(fastPeriods, slowPeriods); - - // initialize - int length = tpList.Count; - List results = new(length); - double[] pr = new double[length]; - - // roll through quotes - for (int i = 0; i < length; i++) - { - (DateTime date, double value) = tpList[i]; - pr[i] = value; - - AwesomeResult r = new(date); - results.Add(r); - - if (i + 1 >= slowPeriods) - { - double sumSlow = 0; - double sumFast = 0; - - for (int p = i + 1 - slowPeriods; p <= i; p++) - { - sumSlow += pr[p]; - - if (p >= i + 1 - fastPeriods) - { - sumFast += pr[p]; - } - } - - r.Oscillator = ((sumFast / fastPeriods) - (sumSlow / slowPeriods)).NaN2Null(); - r.Normalized = (pr[i] != 0) ? 100 * r.Oscillator / pr[i] : null; - } - } - - return results; - } - - // parameter validation - private static void ValidateAwesome( - int fastPeriods, - int slowPeriods) - { - // check parameter arguments - if (fastPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, - "Fast periods must be greater than 0 for Awesome Oscillator."); - } - - if (slowPeriods <= fastPeriods) - { - throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, - "Slow periods must be larger than Fast Periods for Awesome Oscillator."); - } - } -} diff --git a/src/a-d/Awesome/Awesome.StaticSeries.cs b/src/a-d/Awesome/Awesome.StaticSeries.cs new file mode 100644 index 000000000..b8f38f542 --- /dev/null +++ b/src/a-d/Awesome/Awesome.StaticSeries.cs @@ -0,0 +1,63 @@ +namespace Skender.Stock.Indicators; + +// AWESOME OSCILLATOR (SERIES) + +public static partial class Awesome +{ + public static IReadOnlyList ToAwesome( + this IReadOnlyList source, + int fastPeriods = 5, + int slowPeriods = 34) + where T : IReusable + { + // check parameter arguments + Validate(fastPeriods, slowPeriods); + + // prefer HL2 when IQuote + IReadOnlyList values + = source.ToPreferredList(CandlePart.HL2); + + // initialize + int length = values.Count; + List results = new(length); + double[] pr = new double[length]; + + // roll through source values + for (int i = 0; i < length; i++) + { + IReusable s = values[i]; + pr[i] = s.Value; + + double? oscillator = null; + double? normalized = null; + + if (i >= slowPeriods - 1) + { + double sumSlow = 0; + double sumFast = 0; + + for (int p = i + 1 - slowPeriods; p <= i; p++) + { + sumSlow += pr[p]; + + if (p >= i + 1 - fastPeriods) + { + sumFast += pr[p]; + } + } + + oscillator = ((sumFast / fastPeriods) - (sumSlow / slowPeriods)).NaN2Null(); + normalized = pr[i] != 0 ? 100 * oscillator / pr[i] : null; + } + + AwesomeResult r = new( + Timestamp: s.Timestamp, + Oscillator: oscillator, + Normalized: normalized); + + results.Add(r); + } + + return results; + } +} diff --git a/src/a-d/Awesome/Awesome.Utilities.cs b/src/a-d/Awesome/Awesome.Utilities.cs index 8a19fc3fa..95396cb3d 100644 --- a/src/a-d/Awesome/Awesome.Utilities.cs +++ b/src/a-d/Awesome/Awesome.Utilities.cs @@ -1,17 +1,23 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +public static partial class Awesome { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int fastPeriods, + int slowPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Oscillator != null); + // check parameter arguments + if (fastPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, + "Fast periods must be greater than 0 for Awesome Oscillator."); + } - return results.Remove(removePeriods); + if (slowPeriods <= fastPeriods) + { + throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, + "Slow periods must be larger than Fast Periods for Awesome Oscillator."); + } } } diff --git a/src/a-d/BasicQuote/BasicData.Models.cs b/src/a-d/BasicQuote/BasicData.Models.cs deleted file mode 100644 index 2c09980cc..000000000 --- a/src/a-d/BasicQuote/BasicData.Models.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -public interface IBasicData -{ - public DateTime Date { get; } - public double Value { get; } -} - -public class BasicData : ISeries, IBasicData, IReusableResult -{ - public DateTime Date { get; set; } - public double Value { get; set; } - - double? IReusableResult.Value => Value; -} diff --git a/src/a-d/BasicQuote/BasicQuote.Api.cs b/src/a-d/BasicQuote/BasicQuote.Api.cs deleted file mode 100644 index ddd3ab978..000000000 --- a/src/a-d/BasicQuote/BasicQuote.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -public static partial class Indicator -{ - // BASE QUOTE (simplified quote) - /// - /// - public static IEnumerable GetBaseQuote( - this IEnumerable quotes, - CandlePart candlePart = CandlePart.Close) - where TQuote : IQuote => quotes - .Select(q => q.ToBasicData(candlePart)) - .OrderBy(x => x.Date); -} diff --git a/src/a-d/BasicQuote/info.xml b/src/a-d/BasicQuote/info.xml deleted file mode 100644 index 60943f9e8..000000000 --- a/src/a-d/BasicQuote/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - A simple quote transform. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - The OHLCV element or simply calculated value type. - Time series of Basic Quote values. - Invalid candle part provided. - \ No newline at end of file diff --git a/src/a-d/Beta/Beta.Api.cs b/src/a-d/Beta/Beta.Api.cs deleted file mode 100644 index 34d379e79..000000000 --- a/src/a-d/Beta/Beta.Api.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace Skender.Stock.Indicators; - -// BETA COEFFICIENT (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetBeta( - this IEnumerable quotesEval, - IEnumerable quotesMarket, - int lookbackPeriods, - BetaType type = BetaType.Standard) - where TQuote : IQuote - { - List<(DateTime, double)> tpListEval - = quotesEval.ToTuple(CandlePart.Close); - - List<(DateTime, double)> tpListMrkt - = quotesMarket.ToTuple(CandlePart.Close); - - // to enable typical 'this' extension - return CalcBeta(tpListEval, tpListMrkt, lookbackPeriods, type); - } - - // SERIES, from CHAINS (both inputs reusable) - public static IEnumerable GetBeta( - this IEnumerable evalResults, - IEnumerable mrktResults, - int lookbackPeriods, - BetaType type = BetaType.Standard) - { - List<(DateTime Date, double Value)> tpListEval - = evalResults.ToTuple(); - - List<(DateTime Date, double Value)> tpListMrkt - = mrktResults.ToTuple(); - - return CalcBeta(tpListEval, tpListMrkt, lookbackPeriods, type) - .SyncIndex(evalResults, SyncType.Prepend); - } - - // SERIES, from TUPLE - public static IEnumerable GetBeta( - this IEnumerable<(DateTime, double)> evalTuple, - IEnumerable<(DateTime, double)> mrktTuple, - int lookbackPeriods, - BetaType type = BetaType.Standard) - { - List<(DateTime, double)> tpListEval - = evalTuple.ToSortedList(); - - List<(DateTime, double)> tpListMrkt - = mrktTuple.ToSortedList(); - - return CalcBeta(tpListEval, tpListMrkt, lookbackPeriods, type); - } -} diff --git a/src/a-d/Beta/Beta.Models.cs b/src/a-d/Beta/Beta.Models.cs index 2a1dc5257..5d62fd0eb 100644 --- a/src/a-d/Beta/Beta.Models.cs +++ b/src/a-d/Beta/Beta.Models.cs @@ -1,22 +1,18 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class BetaResult : ResultBase, IReusableResult +public record BetaResult( + DateTime Timestamp, + double? Beta = null, + double? BetaUp = null, + double? BetaDown = null, + double? Ratio = null, + double? Convexity = null, + double? ReturnsEval = null, + double? ReturnsMrkt = null +) : IReusable { - public BetaResult(DateTime date) - { - Date = date; - } - - public double? Beta { get; set; } - public double? BetaUp { get; set; } - public double? BetaDown { get; set; } - public double? Ratio { get; set; } - public double? Convexity { get; set; } - public double? ReturnsEval { get; set; } - public double? ReturnsMrkt { get; set; } - - double? IReusableResult.Value => Beta; + public double Value => Beta.Null2NaN(); } public enum BetaType diff --git a/src/a-d/Beta/Beta.Series.cs b/src/a-d/Beta/Beta.Series.cs deleted file mode 100644 index e36854367..000000000 --- a/src/a-d/Beta/Beta.Series.cs +++ /dev/null @@ -1,173 +0,0 @@ -namespace Skender.Stock.Indicators; - -// BETA COEFFICIENT (SERIES) -public static partial class Indicator -{ - // NOTE: sequence swapped from API - internal static List CalcBeta( - List<(DateTime, double)> tpListEval, - List<(DateTime, double)> tpListMrkt, - int lookbackPeriods, - BetaType type = BetaType.Standard) - { - // check parameter arguments - ValidateBeta(tpListEval, tpListMrkt, lookbackPeriods); - - // initialize - int length = tpListEval.Count; - List results = new(length); - - 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; - - // convert quotes to returns - double[] evalReturns = new double[length]; - double[] mrktReturns = new double[length]; - double prevE = 0; - double prevM = 0; - - for (int i = 0; i < length; i++) - { - (DateTime eDate, double eValue) = tpListEval[i]; - (DateTime mDate, double mValue) = tpListMrkt[i]; - - if (eDate != mDate) - { - throw new InvalidQuotesException(nameof(tpListEval), eDate, - "Date sequence does not match. Beta requires matching dates in provided quotes."); - } - - evalReturns[i] = prevE != 0 ? (eValue / prevE) - 1d : 0; - mrktReturns[i] = prevM != 0 ? (mValue / prevM) - 1d : 0; - - prevE = eValue; - prevM = mValue; - } - - // roll through quotes - for (int i = 0; i < length; i++) - { - (DateTime date, double _) = tpListEval[i]; - - BetaResult r = new(date) { - ReturnsEval = evalReturns[i], - ReturnsMrkt = mrktReturns[i] - }; - results.Add(r); - - // skip warmup periods - if (i < lookbackPeriods) - { - continue; - } - - // calculate beta variants - if (calcSd) - { - r.CalcBetaWindow( - i, lookbackPeriods, mrktReturns, evalReturns, BetaType.Standard); - } - - if (calcDn) - { - r.CalcBetaWindow( - i, lookbackPeriods, mrktReturns, evalReturns, BetaType.Down); - } - - if (calcUp) - { - r.CalcBetaWindow( - i, lookbackPeriods, mrktReturns, evalReturns, 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; - } - - // calculate beta - private static void CalcBetaWindow( - this BetaResult r, - int i, - int lookbackPeriods, - double[] mrktReturns, - double[] evalReturns, - BetaType type) - { - // note: BetaType.All is ineligible for this method - - // initialize - CorrResult c = new(r.Date); - - List dataA = new(lookbackPeriods); - List dataB = new(lookbackPeriods); - - for (int p = i - lookbackPeriods + 1; p <= i; p++) - { - double a = mrktReturns[p]; - double b = evalReturns[p]; - - if (type is BetaType.Standard - || (type is BetaType.Down && a < 0) - || (type is BetaType.Up && a > 0)) - { - dataA.Add(a); - dataB.Add(b); - } - } - - if (dataA.Count > 0) - { - // calculate correlation, covariance, and variance - c.PeriodCorrelation(dataA.ToArray(), dataB.ToArray()); - - // calculate beta - if (c.Covariance != null && c.VarianceA != null && c.VarianceA != 0) - { - double? beta = (c.Covariance / c.VarianceA).NaN2Null(); - - if (type == BetaType.Standard) - { - r.Beta = beta; - } - else if (type == BetaType.Down) - { - r.BetaDown = beta; - } - else if (type == BetaType.Up) - { - r.BetaUp = beta; - } - } - } - } - - // parameter validation - private static void ValidateBeta( - List<(DateTime, double)> tpListEval, - List<(DateTime, double)> tpListMrkt, - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Beta."); - } - - // check quotes - if (tpListEval.Count != tpListMrkt.Count) - { - throw new InvalidQuotesException( - nameof(tpListEval), - "Eval quotes should have the same number of Market quotes for Beta."); - } - } -} diff --git a/src/a-d/Beta/Beta.StaticSeries.cs b/src/a-d/Beta/Beta.StaticSeries.cs new file mode 100644 index 000000000..d8e3280a6 --- /dev/null +++ b/src/a-d/Beta/Beta.StaticSeries.cs @@ -0,0 +1,163 @@ +namespace Skender.Stock.Indicators; + +// BETA COEFFICIENT (SERIES) + +public static partial class Beta +{ + public static IReadOnlyList ToBeta( + this IReadOnlyList sourceEval, + IReadOnlyList sourceMrkt, + int lookbackPeriods, + BetaType type = BetaType.Standard) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(sourceEval); + ArgumentNullException.ThrowIfNull(sourceMrkt); + Validate(sourceEval, sourceMrkt, lookbackPeriods); + + // initialize + int length = sourceEval.Count; + List results = new(length); + + 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; + + // convert quotes to returns + double[] evalReturns = new double[length]; + double[] mrktReturns = new double[length]; + double prevE = 0; + double prevM = 0; + + for (int i = 0; i < length; i++) + { + T eval = sourceEval[i]; + T mrkt = sourceMrkt[i]; + + if (eval.Timestamp != mrkt.Timestamp) + { + throw new InvalidQuotesException( + nameof(sourceEval), eval.Timestamp, + "Timestamp sequence does not match. " + + "Beta requires matching dates in provided quotes."); + } + + evalReturns[i] = prevE != 0 ? (eval.Value / prevE) - 1d : 0; + mrktReturns[i] = prevM != 0 ? (mrkt.Value / prevM) - 1d : 0; + + prevE = eval.Value; + prevM = mrkt.Value; + } + + // roll through source values + for (int i = 0; i < length; i++) + { + T eval = sourceEval[i]; + + // skip warmup periods + if (i < lookbackPeriods) + { + results.Add(new( + Timestamp: eval.Timestamp, + ReturnsEval: evalReturns[i], + ReturnsMrkt: mrktReturns[i])); + + continue; + } + + double? beta = null; + double? betaUp = null; + double? betaDown = null; + double? ratio = null; + double? convexity = null; + + // calculate beta variants + if (calcSd) + { + beta = CalcBetaWindow(i, lookbackPeriods, + mrktReturns, evalReturns, BetaType.Standard); + } + + if (calcDn) + { + betaDown = CalcBetaWindow(i, lookbackPeriods, + mrktReturns, evalReturns, BetaType.Down); + } + + if (calcUp) + { + betaUp = CalcBetaWindow(i, lookbackPeriods, + mrktReturns, evalReturns, BetaType.Up); + } + + // ratio and convexity + if (type == BetaType.All && betaUp != null && betaDown != null) + { + ratio = betaDown != 0 ? betaUp / betaDown : null; + convexity = (betaUp - betaDown) * (betaUp - betaDown); + } + + results.Add( + new(Timestamp: eval.Timestamp, + ReturnsEval: evalReturns[i], + ReturnsMrkt: mrktReturns[i], + Beta: beta, + BetaUp: betaUp, + BetaDown: betaDown, + Ratio: ratio, + Convexity: convexity)); + } + + return results; + } + + // calculate beta + private static double? CalcBetaWindow( + int i, + int lookbackPeriods, + double[] mrktReturns, + double[] evalReturns, + BetaType type) + { + // note: BetaType.All is ineligible for this method + + // initialize + double? beta = null; + List dataA = new(lookbackPeriods); + List dataB = new(lookbackPeriods); + + for (int p = i - lookbackPeriods + 1; p <= i; p++) + { + double a = mrktReturns[p]; + double b = evalReturns[p]; + + if (type is BetaType.Standard + || (type is BetaType.Down && a < 0) + || (type is BetaType.Up && a > 0)) + { + dataA.Add(a); + dataB.Add(b); + } + } + + if (dataA.Count <= 0) + { + return beta; + } + + // calculate correlation, covariance, and variance + CorrResult c = Correlation.PeriodCorrelation( + default, + [.. dataA], + [.. dataB]); + + // calculate beta + if (c.VarianceA != 0) + { + beta = (c.Covariance / c.VarianceA).NaN2Null(); + } + + return beta; + } +} diff --git a/src/a-d/Beta/Beta.Utilities.cs b/src/a-d/Beta/Beta.Utilities.cs index ca341e6c2..45a096dcf 100644 --- a/src/a-d/Beta/Beta.Utilities.cs +++ b/src/a-d/Beta/Beta.Utilities.cs @@ -1,17 +1,27 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +public static partial class Beta { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + IReadOnlyList sourceEval, + IReadOnlyList sourceMrkt, + int lookbackPeriods) + where T : ISeries { - int removePeriods = results - .ToList() - .FindIndex(x => x.Beta != null); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Beta."); + } - return results.Remove(removePeriods); + // check quotes + if (sourceEval.Count != sourceMrkt.Count) + { + throw new InvalidQuotesException( + nameof(sourceEval), + "Eval quotes should have the same number of Market quotes for Beta."); + } } } diff --git a/src/a-d/BollingerBands/BollingerBands.Api.cs b/src/a-d/BollingerBands/BollingerBands.Api.cs deleted file mode 100644 index 5ad5ca75e..000000000 --- a/src/a-d/BollingerBands/BollingerBands.Api.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// BOLLINGER BANDS (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetBollingerBands( - this IEnumerable quotes, - int lookbackPeriods = 20, - double standardDeviations = 2) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcBollingerBands(lookbackPeriods, standardDeviations); - - // SERIES, from CHAIN - public static IEnumerable GetBollingerBands( - this IEnumerable results, - int lookbackPeriods = 20, - double standardDeviations = 2) => results - .ToTuple() - .CalcBollingerBands(lookbackPeriods, standardDeviations) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetBollingerBands( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods = 20, - double standardDeviations = 2) => priceTuples - .ToSortedList() - .CalcBollingerBands(lookbackPeriods, standardDeviations); -} diff --git a/src/a-d/BollingerBands/BollingerBands.Models.cs b/src/a-d/BollingerBands/BollingerBands.Models.cs index 9260a0618..7f577f160 100644 --- a/src/a-d/BollingerBands/BollingerBands.Models.cs +++ b/src/a-d/BollingerBands/BollingerBands.Models.cs @@ -1,20 +1,16 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class BollingerBandsResult : ResultBase, IReusableResult +public record BollingerBandsResult +( + DateTime Timestamp, + double? Sma = null, + double? UpperBand = null, + double? LowerBand = null, + double? PercentB = null, + double? ZScore = null, + double? Width = null +) : IReusable { - public BollingerBandsResult(DateTime date) - { - Date = date; - } - - public double? Sma { get; set; } - public double? UpperBand { get; set; } - public double? LowerBand { get; set; } - - public double? PercentB { get; set; } - public double? ZScore { get; set; } - public double? Width { get; set; } - - double? IReusableResult.Value => PercentB; + public double Value => PercentB.Null2NaN(); } diff --git a/src/a-d/BollingerBands/BollingerBands.Series.cs b/src/a-d/BollingerBands/BollingerBands.Series.cs deleted file mode 100644 index 26802686a..000000000 --- a/src/a-d/BollingerBands/BollingerBands.Series.cs +++ /dev/null @@ -1,75 +0,0 @@ -namespace Skender.Stock.Indicators; - -// BOLLINGER BANDS (SERIES) -public static partial class Indicator -{ - internal static List CalcBollingerBands( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - double standardDeviations) - { - // check parameter arguments - ValidateBollingerBands(lookbackPeriods, standardDeviations); - - // initialize - List results = new(tpList.Count); - - // roll through quotes - for (int i = 0; i < tpList.Count; i++) - { - (DateTime date, double value) = tpList[i]; - - BollingerBandsResult r = new(date); - results.Add(r); - - if (i + 1 >= lookbackPeriods) - { - double[] window = new double[lookbackPeriods]; - double sum = 0; - int n = 0; - - for (int p = i + 1 - lookbackPeriods; p <= i; p++) - { - (DateTime _, double pValue) = tpList[p]; - window[n] = pValue; - sum += pValue; - n++; - } - - double? periodAvg = (sum / lookbackPeriods).NaN2Null(); - double? stdDev = window.StdDev().NaN2Null(); - - r.Sma = periodAvg; - r.UpperBand = periodAvg + (standardDeviations * stdDev); - r.LowerBand = periodAvg - (standardDeviations * stdDev); - - r.PercentB = (r.UpperBand == r.LowerBand) ? null - : (value - r.LowerBand) / (r.UpperBand - r.LowerBand); - - r.ZScore = (stdDev == 0) ? null : (value - r.Sma) / stdDev; - r.Width = (periodAvg == 0) ? null : (r.UpperBand - r.LowerBand) / periodAvg; - } - } - - return results; - } - - // parameter validation - private static void ValidateBollingerBands( - int lookbackPeriods, - double standardDeviations) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for Bollinger Bands."); - } - - if (standardDeviations <= 0) - { - throw new ArgumentOutOfRangeException(nameof(standardDeviations), standardDeviations, - "Standard Deviations must be greater than 0 for Bollinger Bands."); - } - } -} diff --git a/src/a-d/BollingerBands/BollingerBands.StaticSeries.cs b/src/a-d/BollingerBands/BollingerBands.StaticSeries.cs new file mode 100644 index 000000000..8945cb590 --- /dev/null +++ b/src/a-d/BollingerBands/BollingerBands.StaticSeries.cs @@ -0,0 +1,71 @@ +namespace Skender.Stock.Indicators; + +// BOLLINGER BANDS (SERIES) + +public static partial class BollingerBands +{ + public static IReadOnlyList ToBollingerBands( + this IReadOnlyList source, + int lookbackPeriods = 20, + double standardDeviations = 2) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods, standardDeviations); + + // initialize + int length = source.Count; + List results = new(length); + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + if (i >= lookbackPeriods - 1) + { + double[] window = new double[lookbackPeriods]; + double sum = 0; + int n = 0; + + for (int p = i + 1 - lookbackPeriods; p <= i; p++) + { + T ps = source[p]; + window[n] = ps.Value; + sum += ps.Value; + n++; + } + + double? sma = (sum / lookbackPeriods).NaN2Null(); + double? stdDev = window.StdDev().NaN2Null(); + + double? upperBand = sma + (standardDeviations * stdDev); + double? lowerBand = sma - (standardDeviations * stdDev); + + results.Add(new BollingerBandsResult( + + Timestamp: s.Timestamp, + + Sma: sma, + UpperBand: upperBand, + LowerBand: lowerBand, + + PercentB: upperBand - lowerBand == 0 ? null + : (s.Value - lowerBand) / (upperBand - lowerBand), + + ZScore: stdDev == 0 ? null : (s.Value - sma) / stdDev, + Width: sma == 0 ? null : (upperBand - lowerBand) / sma + )); + } + + // initization period + else + { + results.Add(new(s.Timestamp)); + } + } + + return results; + } +} diff --git a/src/a-d/BollingerBands/BollingerBands.Utilities.cs b/src/a-d/BollingerBands/BollingerBands.Utilities.cs index a7f86e769..a90db9fe1 100644 --- a/src/a-d/BollingerBands/BollingerBands.Utilities.cs +++ b/src/a-d/BollingerBands/BollingerBands.Utilities.cs @@ -1,12 +1,11 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +public static partial class BollingerBands { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -14,4 +13,23 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods, + double standardDeviations) + { + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for Bollinger Bands."); + } + + if (standardDeviations <= 0) + { + throw new ArgumentOutOfRangeException(nameof(standardDeviations), standardDeviations, + "Standard Deviations must be greater than 0 for Bollinger Bands."); + } + } } diff --git a/src/a-d/Bop/Bop.Api.cs b/src/a-d/Bop/Bop.Api.cs deleted file mode 100644 index ef788b3db..000000000 --- a/src/a-d/Bop/Bop.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// BALANCE OF POWER (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetBop( - this IEnumerable quotes, - int smoothPeriods = 14) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcBop(smoothPeriods); -} diff --git a/src/a-d/Bop/Bop.Models.cs b/src/a-d/Bop/Bop.Models.cs index 74df18931..2abc7e6b1 100644 --- a/src/a-d/Bop/Bop.Models.cs +++ b/src/a-d/Bop/Bop.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class BopResult : ResultBase, IReusableResult +public record BopResult +( + DateTime Timestamp, + double? Bop +) : IReusable { - public BopResult(DateTime date) - { - Date = date; - } - - public double? Bop { get; set; } - - double? IReusableResult.Value => Bop; + public double Value => Bop.Null2NaN(); } diff --git a/src/a-d/Bop/Bop.Series.cs b/src/a-d/Bop/Bop.Series.cs deleted file mode 100644 index fb75f1988..000000000 --- a/src/a-d/Bop/Bop.Series.cs +++ /dev/null @@ -1,54 +0,0 @@ -namespace Skender.Stock.Indicators; - -// BALANCE OF POWER (SERIES) -public static partial class Indicator -{ - internal static List CalcBop( - this List qdList, - int smoothPeriods) - { - // check parameter arguments - ValidateBop(smoothPeriods); - - // initialize - int length = qdList.Count; - List results = new(length); - - double[] raw = qdList - .Select(x => (x.High != x.Low) ? - ((x.Close - x.Open) / (x.High - x.Low)) : double.NaN) - .ToArray(); - - // roll through quotes - for (int i = 0; i < length; i++) - { - BopResult r = new(qdList[i].Date); - results.Add(r); - - if (i >= smoothPeriods - 1) - { - double sum = 0; - for (int p = i - smoothPeriods + 1; p <= i; p++) - { - sum += raw[p]; - } - - r.Bop = (sum / smoothPeriods).NaN2Null(); - } - } - - return results; - } - - // parameter validation - private static void ValidateBop( - int smoothPeriods) - { - // check parameter arguments - if (smoothPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, - "Smoothing periods must be greater than 0 for BOP."); - } - } -} diff --git a/src/a-d/Bop/Bop.StaticSeries.cs b/src/a-d/Bop/Bop.StaticSeries.cs new file mode 100644 index 000000000..37a1c7b7c --- /dev/null +++ b/src/a-d/Bop/Bop.StaticSeries.cs @@ -0,0 +1,53 @@ +namespace Skender.Stock.Indicators; + +// BALANCE OF POWER (SERIES) + +public static partial class Bop +{ + public static IReadOnlyList ToBop( + this IReadOnlyList quotes, + int smoothPeriods = 14) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcBop(smoothPeriods); + + private static List CalcBop( + this IReadOnlyList source, + int smoothPeriods) + { + // check parameter arguments + Validate(smoothPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + double[] raw = source + .Select(x => x.High - x.Low != 0 ? + (x.Close - x.Open) / (x.High - x.Low) : double.NaN) + .ToArray(); + + // roll through source values + for (int i = 0; i < length; i++) + { + double? bop = null; + + if (i >= smoothPeriods - 1) + { + double sum = 0; + for (int p = i - smoothPeriods + 1; p <= i; p++) + { + sum += raw[p]; + } + + bop = (sum / smoothPeriods).NaN2Null(); + } + + results.Add(new( + Timestamp: source[i].Timestamp, + Bop: bop)); + } + + return results; + } +} diff --git a/src/a-d/Bop/Bop.Utilities.cs b/src/a-d/Bop/Bop.Utilities.cs index 7ccb26c47..488440947 100644 --- a/src/a-d/Bop/Bop.Utilities.cs +++ b/src/a-d/Bop/Bop.Utilities.cs @@ -1,17 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// BALANCE OF POWER (UTILITIES) + +public static partial class Bop { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int smoothPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Bop != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (smoothPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, + "Smoothing periods must be greater than 0 for BOP."); + } } } diff --git a/src/a-d/Cci/Cci.Api.cs b/src/a-d/Cci/Cci.Api.cs deleted file mode 100644 index a7ec674ef..000000000 --- a/src/a-d/Cci/Cci.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// COMMODITY CHANNEL INDEX (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetCci( - this IEnumerable quotes, - int lookbackPeriods = 20) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcCci(lookbackPeriods); -} diff --git a/src/a-d/Cci/Cci.Models.cs b/src/a-d/Cci/Cci.Models.cs index 2f3f7a449..f2a819eb3 100644 --- a/src/a-d/Cci/Cci.Models.cs +++ b/src/a-d/Cci/Cci.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class CciResult : ResultBase, IReusableResult +public record CciResult +( + DateTime Timestamp, + double? Cci +) : IReusable { - public CciResult(DateTime date) - { - Date = date; - } - - public double? Cci { get; set; } - - double? IReusableResult.Value => Cci; + public double Value => Cci.Null2NaN(); } diff --git a/src/a-d/Cci/Cci.Series.cs b/src/a-d/Cci/Cci.StaticSeries.cs similarity index 59% rename from src/a-d/Cci/Cci.Series.cs rename to src/a-d/Cci/Cci.StaticSeries.cs index 93bad6f21..e854ce386 100644 --- a/src/a-d/Cci/Cci.Series.cs +++ b/src/a-d/Cci/Cci.StaticSeries.cs @@ -1,28 +1,35 @@ namespace Skender.Stock.Indicators; // COMMODITY CHANNEL INDEX (SERIES) -public static partial class Indicator + +public static partial class Cci { - internal static List CalcCci( - this List qdList, + public static IReadOnlyList ToCci( + this IReadOnlyList quotes, + int lookbackPeriods = 20) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcCci(lookbackPeriods); + + private static List CalcCci( + this IReadOnlyList source, int lookbackPeriods) { // check parameter arguments - ValidateCci(lookbackPeriods); + Validate(lookbackPeriods); // initialize - int length = qdList.Count; + int length = source.Count; List results = new(length); double[] tp = new double[length]; - // roll through quotes + // roll through source values for (int i = 0; i < length; i++) { - QuoteD q = qdList[i]; + QuoteD q = source[i]; tp[i] = (q.High + q.Low + q.Close) / 3d; - CciResult r = new(q.Date); - results.Add(r); + double? cci = null; if (i + 1 >= lookbackPeriods) { @@ -44,23 +51,15 @@ internal static List CalcCci( avgDv /= lookbackPeriods; - r.Cci = (avgDv == 0) ? null + cci = avgDv == 0 ? null : ((tp[i] - avgTp) / (0.015 * avgDv)).NaN2Null(); } + + results.Add(new( + Timestamp: q.Timestamp, + Cci: cci)); } return results; } - - // parameter validation - private static void ValidateCci( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Commodity Channel Index."); - } - } } diff --git a/src/a-d/Cci/Cci.Utilities.cs b/src/a-d/Cci/Cci.Utilities.cs index ff33b1556..34bce451c 100644 --- a/src/a-d/Cci/Cci.Utilities.cs +++ b/src/a-d/Cci/Cci.Utilities.cs @@ -1,17 +1,19 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// COMMODITY CHANNEL INDEX (UTILITIES) + +public static partial class Cci { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Cci != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Commodity Channel Index."); + } } + } diff --git a/src/a-d/ChaikinOsc/ChaikinOsc.Api.cs b/src/a-d/ChaikinOsc/ChaikinOsc.Api.cs deleted file mode 100644 index 0f1973bf8..000000000 --- a/src/a-d/ChaikinOsc/ChaikinOsc.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHAIKIN OSCILLATOR -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetChaikinOsc( - this IEnumerable quotes, - int fastPeriods = 3, - int slowPeriods = 10) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcChaikinOsc(fastPeriods, slowPeriods); -} diff --git a/src/a-d/ChaikinOsc/ChaikinOsc.Models.cs b/src/a-d/ChaikinOsc/ChaikinOsc.Models.cs index 686e2ed0b..d30df99b6 100644 --- a/src/a-d/ChaikinOsc/ChaikinOsc.Models.cs +++ b/src/a-d/ChaikinOsc/ChaikinOsc.Models.cs @@ -1,17 +1,14 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class ChaikinOscResult : ResultBase, IReusableResult +public record ChaikinOscResult +( + DateTime Timestamp, + double? MoneyFlowMultiplier, + double? MoneyFlowVolume, + double? Adl, + double? Oscillator +) : IReusable { - public ChaikinOscResult(DateTime date) - { - Date = date; - } - - public double? MoneyFlowMultiplier { get; set; } - public double? MoneyFlowVolume { get; set; } - public double? Adl { get; set; } - public double? Oscillator { get; set; } - - double? IReusableResult.Value => Oscillator; + public double Value => Oscillator.Null2NaN(); } diff --git a/src/a-d/ChaikinOsc/ChaikinOsc.Series.cs b/src/a-d/ChaikinOsc/ChaikinOsc.Series.cs deleted file mode 100644 index 77b9e27c7..000000000 --- a/src/a-d/ChaikinOsc/ChaikinOsc.Series.cs +++ /dev/null @@ -1,64 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHAIKIN OSCILLATOR (SERIES) -public static partial class Indicator -{ - internal static List CalcChaikinOsc( - this List qdList, - int fastPeriods, - int slowPeriods) - { - // check parameter arguments - ValidateChaikinOsc(fastPeriods, slowPeriods); - - // money flow - List results = qdList.CalcAdl(null) - .Select(r => new ChaikinOscResult(r.Date) { - MoneyFlowMultiplier = r.MoneyFlowMultiplier, - MoneyFlowVolume = r.MoneyFlowVolume, - Adl = r.Adl - }) - .ToList(); - - // EMA of ADL - List<(DateTime Date, double)> tpAdl = results - .Select(x => ( - x.Date, (double)(x.Adl ?? double.NaN))) - .ToList(); - - List adlEmaSlow = tpAdl.CalcEma(slowPeriods); - List adlEmaFast = tpAdl.CalcEma(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 = f.Ema - s.Ema; - } - - return results; - } - - // parameter validation - private static void ValidateChaikinOsc( - int fastPeriods, - int slowPeriods) - { - // check parameter arguments - if (fastPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, - "Fast lookback periods must be greater than 0 for Chaikin Oscillator."); - } - - if (slowPeriods <= fastPeriods) - { - throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, - "Slow lookback periods must be greater than Fast lookback period for Chaikin Oscillator."); - } - } -} diff --git a/src/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.cs b/src/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.cs new file mode 100644 index 000000000..f3d1b3b6c --- /dev/null +++ b/src/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.cs @@ -0,0 +1,46 @@ +namespace Skender.Stock.Indicators; + +// CHAIKIN OSCILLATOR (SERIES) + +public static partial class ChaikinOsc +{ + public static IReadOnlyList ToChaikinOsc( + this IReadOnlyList quotes, + int fastPeriods = 3, + int slowPeriods = 10) + where TQuote : IQuote + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(quotes); + Validate(fastPeriods, slowPeriods); + + // initialize + int length = quotes.Count; + List results = new(length); + + // money flow + IReadOnlyList adlResults = quotes.ToAdl(); + + // fast/slow EMA of ADL + IReadOnlyList adlEmaSlow = adlResults.ToEma(slowPeriods); + IReadOnlyList adlEmaFast = adlResults.ToEma(fastPeriods); + + // roll through source values + for (int i = 0; i < length; i++) + { + AdlResult a = adlResults[i]; + EmaResult f = adlEmaFast[i]; + EmaResult s = adlEmaSlow[i]; + + results.Add(new( + Timestamp: a.Timestamp, + MoneyFlowMultiplier: a.MoneyFlowMultiplier, + MoneyFlowVolume: a.MoneyFlowVolume, + Adl: a.Adl, + Oscillator: f.Ema - s.Ema + )); + } + + return results; + } +} diff --git a/src/a-d/ChaikinOsc/ChaikinOsc.Utilities.cs b/src/a-d/ChaikinOsc/ChaikinOsc.Utilities.cs index d63404607..3e21a64c0 100644 --- a/src/a-d/ChaikinOsc/ChaikinOsc.Utilities.cs +++ b/src/a-d/ChaikinOsc/ChaikinOsc.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// CHAIKIN OSCILLATOR (UTILITIES) + +public static partial class ChaikinOsc { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int s = results .ToList() @@ -14,4 +15,23 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(s + 100); } + + // parameter validation + internal static void Validate( + int fastPeriods, + int slowPeriods) + { + // check parameter arguments + if (fastPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, + "Fast lookback periods must be greater than 0 for Chaikin Oscillator."); + } + + if (slowPeriods <= fastPeriods) + { + throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, + "Slow lookback periods must be greater than Fast lookback period for Chaikin Oscillator."); + } + } } diff --git a/src/a-d/Chandelier/Chandelier.Api.cs b/src/a-d/Chandelier/Chandelier.Api.cs deleted file mode 100644 index 5aba2b735..000000000 --- a/src/a-d/Chandelier/Chandelier.Api.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHANDELIER EXIT (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetChandelier( - this IEnumerable quotes, - int lookbackPeriods = 22, - double multiplier = 3, - ChandelierType type = ChandelierType.Long) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcChandelier(lookbackPeriods, multiplier, type); -} diff --git a/src/a-d/Chandelier/Chandelier.Models.cs b/src/a-d/Chandelier/Chandelier.Models.cs index 2239680cb..8fc6b4209 100644 --- a/src/a-d/Chandelier/Chandelier.Models.cs +++ b/src/a-d/Chandelier/Chandelier.Models.cs @@ -1,16 +1,13 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class ChandelierResult : ResultBase, IReusableResult +public record ChandelierResult +( + DateTime Timestamp, + double? ChandelierExit +) : IReusable { - public ChandelierResult(DateTime date) - { - Date = date; - } - - public double? ChandelierExit { get; set; } - - double? IReusableResult.Value => ChandelierExit; + public double Value => ChandelierExit.Null2NaN(); } public enum ChandelierType diff --git a/src/a-d/Chandelier/Chandelier.Series.cs b/src/a-d/Chandelier/Chandelier.StaticSeries.cs similarity index 53% rename from src/a-d/Chandelier/Chandelier.Series.cs rename to src/a-d/Chandelier/Chandelier.StaticSeries.cs index bdfe189fe..8f13ebeb8 100644 --- a/src/a-d/Chandelier/Chandelier.Series.cs +++ b/src/a-d/Chandelier/Chandelier.StaticSeries.cs @@ -1,31 +1,40 @@ namespace Skender.Stock.Indicators; // CHANDELIER EXIT (SERIES) -public static partial class Indicator + +public static partial class Chandelier { - internal static List CalcChandelier( - this List qdList, + public static IReadOnlyList ToChandelier( + this IReadOnlyList quotes, + int lookbackPeriods = 22, + double multiplier = 3, + ChandelierType type = ChandelierType.Long) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcChandelier(lookbackPeriods, multiplier, type); + + private static List CalcChandelier( + this IReadOnlyList source, int lookbackPeriods, double multiplier, ChandelierType type) { // check parameter arguments - ValidateChandelier(lookbackPeriods, multiplier); + Validate(lookbackPeriods, multiplier); // initialize - int length = qdList.Count; + int length = source.Count; List results = new(length); - List atrResult = qdList - .CalcAtr(lookbackPeriods) - .ToList(); - // roll through quotes + IReadOnlyList atrResult + = source.CalcAtr(lookbackPeriods); + + // roll through source values for (int i = 0; i < length; i++) { - QuoteD q = qdList[i]; + QuoteD q = source[i]; - ChandelierResult r = new(q.Date); - results.Add(r); + double? exit = null; // add exit values if (i >= lookbackPeriods) @@ -39,14 +48,14 @@ internal static List CalcChandelier( double maxHigh = 0; for (int p = i + 1 - lookbackPeriods; p <= i; p++) { - QuoteD d = qdList[p]; + QuoteD d = source[p]; if (d.High > maxHigh) { maxHigh = d.High; } } - r.ChandelierExit = maxHigh - (atr * multiplier); + exit = maxHigh - (atr * multiplier); break; case ChandelierType.Short: @@ -54,41 +63,26 @@ internal static List CalcChandelier( double minLow = double.MaxValue; for (int p = i + 1 - lookbackPeriods; p <= i; p++) { - QuoteD d = qdList[p]; + QuoteD d = source[p]; if (d.Low < minLow) { minLow = d.Low; } } - r.ChandelierExit = minLow + (atr * multiplier); + exit = minLow + (atr * multiplier); break; default: throw new ArgumentOutOfRangeException(nameof(type)); } } - } - - return results; - } - // parameter validation - private static void ValidateChandelier( - int lookbackPeriods, - double multiplier) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Chandelier Exit."); + results.Add(new( + Timestamp: q.Timestamp, + ChandelierExit: exit)); } - if (multiplier <= 0) - { - throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, - "Multiplier must be greater than 0 for Chandelier Exit."); - } + return results; } } diff --git a/src/a-d/Chandelier/Chandelier.Utilities.cs b/src/a-d/Chandelier/Chandelier.Utilities.cs index a98e81b0e..f61fe73ec 100644 --- a/src/a-d/Chandelier/Chandelier.Utilities.cs +++ b/src/a-d/Chandelier/Chandelier.Utilities.cs @@ -1,17 +1,25 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// CHANDELIER EXIT (UTILITIES) + +public static partial class Chandelier { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods, + double multiplier) { - int removePeriods = results - .ToList() - .FindIndex(x => x.ChandelierExit != null); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Chandelier Exit."); + } - return results.Remove(removePeriods); + if (multiplier <= 0) + { + throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, + "Multiplier must be greater than 0 for Chandelier Exit."); + } } } diff --git a/src/a-d/Chop/Chop.Api.cs b/src/a-d/Chop/Chop.Api.cs deleted file mode 100644 index 6535d5928..000000000 --- a/src/a-d/Chop/Chop.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHOPPINESS INDEX (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetChop( - this IEnumerable quotes, - int lookbackPeriods = 14) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcChop(lookbackPeriods); -} diff --git a/src/a-d/Chop/Chop.Models.cs b/src/a-d/Chop/Chop.Models.cs index a444acc58..27aeb760a 100644 --- a/src/a-d/Chop/Chop.Models.cs +++ b/src/a-d/Chop/Chop.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class ChopResult : ResultBase, IReusableResult +public record ChopResult +( + DateTime Timestamp, + double? Chop +) : IReusable { - public ChopResult(DateTime date) - { - Date = date; - } - - public double? Chop { get; set; } - - double? IReusableResult.Value => Chop; + public double Value => Chop.Null2NaN(); } diff --git a/src/a-d/Chop/Chop.Series.cs b/src/a-d/Chop/Chop.Series.cs deleted file mode 100644 index 1329d2e23..000000000 --- a/src/a-d/Chop/Chop.Series.cs +++ /dev/null @@ -1,79 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHOPPINESS INDEX (SERIES) -public static partial class Indicator -{ - internal static List CalcChop( - this List qdList, - int lookbackPeriods) - { - // check parameter arguments - ValidateChop(lookbackPeriods); - - // initialize - double sum; - double high; - double low; - double range; - - int length = qdList.Count; - List results = new(length); - double[] trueHigh = new double[length]; - double[] trueLow = new double[length]; - double[] trueRange = new double[length]; - - // roll through quotes - for (int i = 0; i < qdList.Count; i++) - { - ChopResult r = new(qdList[i].Date); - results.Add(r); - - if (i > 0) - { - trueHigh[i] = Math.Max(qdList[i].High, qdList[i - 1].Close); - trueLow[i] = Math.Min(qdList[i].Low, qdList[i - 1].Close); - trueRange[i] = trueHigh[i] - trueLow[i]; - - // calculate CHOP - - if (i >= lookbackPeriods) - { - // reset measurements - sum = trueRange[i]; - high = trueHigh[i]; - low = trueLow[i]; - - // iterate over lookback window - for (int j = 1; j < lookbackPeriods; j++) - { - sum += trueRange[i - j]; - high = Math.Max(high, trueHigh[i - j]); - low = Math.Min(low, trueLow[i - j]); - } - - range = high - low; - - // calculate CHOP - if (range != 0) - { - r.Chop = 100 * (Math.Log(sum / range) / Math.Log(lookbackPeriods)); - } - } - } - } - - return results; - } - - // parameter validation - private static void ValidateChop( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for CHOP."); - } - } -} diff --git a/src/a-d/Chop/Chop.StaticSeries.cs b/src/a-d/Chop/Chop.StaticSeries.cs new file mode 100644 index 000000000..2237f86c1 --- /dev/null +++ b/src/a-d/Chop/Chop.StaticSeries.cs @@ -0,0 +1,73 @@ +namespace Skender.Stock.Indicators; + +// CHOPPINESS INDEX (SERIES) + +public static partial class Chop +{ + public static IReadOnlyList ToChop( + this IReadOnlyList quotes, + int lookbackPeriods = 14) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcChop(lookbackPeriods); + + private static List CalcChop( + this IReadOnlyList source, + int lookbackPeriods) + { + // check parameter arguments + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + double[] trueHigh = new double[length]; + double[] trueLow = new double[length]; + double[] trueRange = new double[length]; + + // roll through source values + for (int i = 0; i < length; i++) + { + double? chop = null; + + if (i > 0) + { + trueHigh[i] = Math.Max(source[i].High, source[i - 1].Close); + trueLow[i] = Math.Min(source[i].Low, source[i - 1].Close); + trueRange[i] = trueHigh[i] - trueLow[i]; + + // calculate CHOP + + if (i >= lookbackPeriods) + { + // reset measurements + double sum = trueRange[i]; + double high = trueHigh[i]; + double low = trueLow[i]; + + // iterate over lookback window + for (int j = 1; j < lookbackPeriods; j++) + { + sum += trueRange[i - j]; + high = Math.Max(high, trueHigh[i - j]); + low = Math.Min(low, trueLow[i - j]); + } + + double range = high - low; + + // calculate CHOP + if (range != 0) + { + chop = 100 * (Math.Log(sum / range) / Math.Log(lookbackPeriods)); + } + } + } + + results.Add(new( + Timestamp: source[i].Timestamp, + Chop: chop)); + } + + return results; + } +} diff --git a/src/a-d/Chop/Chop.Utilities.cs b/src/a-d/Chop/Chop.Utilities.cs index 3fed5751b..08bdb70dc 100644 --- a/src/a-d/Chop/Chop.Utilities.cs +++ b/src/a-d/Chop/Chop.Utilities.cs @@ -1,17 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// CHOPPINESS INDEX (UTILITIES) + +public static partial class Chop { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Chop != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for CHOP."); + } } } diff --git a/src/a-d/Cmf/Cmf.Api.cs b/src/a-d/Cmf/Cmf.Api.cs deleted file mode 100644 index 02933f9ab..000000000 --- a/src/a-d/Cmf/Cmf.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHAIKIN MONEY FLOW (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetCmf( - this IEnumerable quotes, - int lookbackPeriods = 20) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcCmf(lookbackPeriods); -} diff --git a/src/a-d/Cmf/Cmf.Models.cs b/src/a-d/Cmf/Cmf.Models.cs index 914ac667a..6159a061f 100644 --- a/src/a-d/Cmf/Cmf.Models.cs +++ b/src/a-d/Cmf/Cmf.Models.cs @@ -1,16 +1,13 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class CmfResult : ResultBase, IReusableResult +public record CmfResult +( + DateTime Timestamp, + double? MoneyFlowMultiplier, + double? MoneyFlowVolume, + double? Cmf +) : IReusable { - public CmfResult(DateTime date) - { - Date = date; - } - - public double? MoneyFlowMultiplier { get; set; } - public double? MoneyFlowVolume { get; set; } - public double? Cmf { get; set; } - - double? IReusableResult.Value => Cmf; + public double Value => Cmf.Null2NaN(); } diff --git a/src/a-d/Cmf/Cmf.Series.cs b/src/a-d/Cmf/Cmf.Series.cs deleted file mode 100644 index d3eb8e391..000000000 --- a/src/a-d/Cmf/Cmf.Series.cs +++ /dev/null @@ -1,70 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHAIKIN MONEY FLOW (SERIES) -public static partial class Indicator -{ - internal static List CalcCmf( - this List qdList, - int lookbackPeriods) - { - // convert quotes - List<(DateTime, double)> tpList = qdList.ToTuple(CandlePart.Volume); - - // check parameter arguments - ValidateCmf(lookbackPeriods); - - // initialize - int length = tpList.Count; - List results = new(length); - List adlResults = qdList.CalcAdl(null).ToList(); - - // roll through quotes - for (int i = 0; i < length; i++) - { - AdlResult adl = adlResults[i]; - - CmfResult r = new(adl.Date) { - MoneyFlowMultiplier = adl.MoneyFlowMultiplier, - MoneyFlowVolume = adl.MoneyFlowVolume - }; - results.Add(r); - - if (i >= lookbackPeriods - 1) - { - double? sumMfv = 0; - double? sumVol = 0; - - for (int p = i + 1 - lookbackPeriods; p <= i; p++) - { - (DateTime _, double pValue) = tpList[p]; - sumVol += pValue; - - AdlResult d = adlResults[p]; - sumMfv += d.MoneyFlowVolume; - } - - double? avgMfv = sumMfv / lookbackPeriods; - double? avgVol = sumVol / lookbackPeriods; - - if (avgVol != 0) - { - r.Cmf = avgMfv / avgVol; - } - } - } - - return results; - } - - // parameter validation - private static void ValidateCmf( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Chaikin Money Flow."); - } - } -} diff --git a/src/a-d/Cmf/Cmf.StaticSeries.cs b/src/a-d/Cmf/Cmf.StaticSeries.cs new file mode 100644 index 000000000..063d60b5b --- /dev/null +++ b/src/a-d/Cmf/Cmf.StaticSeries.cs @@ -0,0 +1,68 @@ +namespace Skender.Stock.Indicators; + +// CHAIKIN MONEY FLOW (SERIES) + +public static partial class Cmf +{ + public static IReadOnlyList ToCmf( + this IReadOnlyList quotes, + int lookbackPeriods = 20) + where TQuote : IQuote => quotes + .ToSortedList() + .CalcCmf(lookbackPeriods); + + private static List CalcCmf( + this IReadOnlyList source, + int lookbackPeriods) + where TQuote : IQuote + { + // get volume array + double[] volume + = source.Select(v => (double)v.Volume).ToArray(); + + // check parameter arguments + Validate(lookbackPeriods); + + // initialize + int length = volume.Length; + List results = new(length); + IReadOnlyList adlResults = source.ToAdl(); + + // roll through source values + for (int i = 0; i < length; i++) + { + AdlResult adl = adlResults[i]; + double? cmf = null; + + if (i >= lookbackPeriods - 1) + { + double? sumMfv = 0; + double? sumVol = 0; + + for (int p = i + 1 - lookbackPeriods; p <= i; p++) + { + sumVol += volume[p]; + + AdlResult d = adlResults[p]; + sumMfv += d.MoneyFlowVolume; + } + + double? avgMfv = sumMfv / lookbackPeriods; + double? avgVol = sumVol / lookbackPeriods; + + if (avgVol != 0) + { + cmf = avgMfv / avgVol; + } + } + + results.Add(new( + Timestamp: adl.Timestamp, + MoneyFlowMultiplier: adl.MoneyFlowMultiplier, + MoneyFlowVolume: adl.MoneyFlowVolume, + Cmf: cmf)); + } + + return results; + } +} diff --git a/src/a-d/Cmf/Cmf.Utilities.cs b/src/a-d/Cmf/Cmf.Utilities.cs index 69a6f1ac6..360263a04 100644 --- a/src/a-d/Cmf/Cmf.Utilities.cs +++ b/src/a-d/Cmf/Cmf.Utilities.cs @@ -1,17 +1,19 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// CHAIKIN MONEY FLOW (UTILITIES) + +public static partial class Cmf { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Cmf != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Chaikin Money Flow."); + } } + } diff --git a/src/a-d/Cmo/Cmo.Api.cs b/src/a-d/Cmo/Cmo.Api.cs deleted file mode 100644 index 68aa1f18c..000000000 --- a/src/a-d/Cmo/Cmo.Api.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHANDE MOMENTUM OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetCmo( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcCmo(lookbackPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetCmo( - this IEnumerable results, - int lookbackPeriods) => results - .ToTuple() - .CalcCmo(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetCmo( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods) => priceTuples - .ToSortedList() - .CalcCmo(lookbackPeriods); -} diff --git a/src/a-d/Cmo/Cmo.Models.cs b/src/a-d/Cmo/Cmo.Models.cs index b7caef49e..2a9053965 100644 --- a/src/a-d/Cmo/Cmo.Models.cs +++ b/src/a-d/Cmo/Cmo.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class CmoResult : ResultBase, IReusableResult +public record CmoResult +( + DateTime Timestamp, + double? Cmo = null +) : IReusable { - public CmoResult(DateTime date) - { - Date = date; - } - - public double? Cmo { get; set; } - - double? IReusableResult.Value => Cmo; + public double Value => Cmo.Null2NaN(); } diff --git a/src/a-d/Cmo/Cmo.Series.cs b/src/a-d/Cmo/Cmo.Series.cs deleted file mode 100644 index a45f8545c..000000000 --- a/src/a-d/Cmo/Cmo.Series.cs +++ /dev/null @@ -1,85 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHANDE MOMENTUM OSCILLATOR (SERIES) -public static partial class Indicator -{ - internal static List CalcCmo( - this List<(DateTime, double)> tpList, - int lookbackPeriods) - { - // check parameter arguments - ValidateCmo(lookbackPeriods); - - // initialize - int length = tpList.Count; - List results = new(length); - List<(bool? isUp, double value)> ticks = new(length); - double prevValue = double.NaN; - - // add initial record - if (length > 0) - { - results.Add(new CmoResult(tpList[0].Item1)); - ticks.Add((null, double.NaN)); - - prevValue = tpList[0].Item2; - } - - // roll through quotes - for (int i = 1; i < length; i++) - { - (DateTime date, double value) = tpList[i]; - - CmoResult r = new(date); - results.Add(r); - ticks.Add(( - value > prevValue ? true - : value < prevValue ? false - : null, Math.Abs(value - prevValue))); - - if (i >= lookbackPeriods) - { - double sH = 0; - double sL = 0; - - for (int p = i - lookbackPeriods + 1; p <= i; p++) - { - (bool? isUp, double pDiff) = ticks[p]; - - if (isUp is null) - { - continue; - } - else if (isUp == true) - { - sH += pDiff; - } - else - { - sL += pDiff; - } - } - - r.Cmo = (sH + sL != 0) - ? (100 * (sH - sL) / (sH + sL)).NaN2Null() - : null; - } - - prevValue = value; - } - - return results; - } - - // parameter validation - private static void ValidateCmo( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for CMO."); - } - } -} diff --git a/src/a-d/Cmo/Cmo.StaticSeries.cs b/src/a-d/Cmo/Cmo.StaticSeries.cs new file mode 100644 index 000000000..8cd1d448f --- /dev/null +++ b/src/a-d/Cmo/Cmo.StaticSeries.cs @@ -0,0 +1,93 @@ +namespace Skender.Stock.Indicators; + +// CHANDE MOMENTUM OSCILLATOR (SERIES) + +public static partial class Cmo +{ + public static IReadOnlyList ToCmo( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + List<(bool? isUp, double value)> ticks = new(length); + + // discontinue of empty + if (length == 0) + { + return results; + } + + // initialize, add first records + double prevValue = source[0].Value; + + results.Add(new(source[0].Timestamp)); + ticks.Add((null, double.NaN)); + + // roll through remaining values + for (int i = 1; i < length; i++) + { + T s = source[i]; + double? cmo = null; + + // determine tick direction and size + (bool? isUp, double value) tick = (null, Math.Abs(s.Value - prevValue)); + + tick.isUp = double.IsNaN(tick.value) || s.Value == prevValue + ? null + : s.Value > prevValue; + + ticks.Add(tick); + + // calculate CMO + if (i >= lookbackPeriods) + { + double sH = 0; + double sL = 0; + + for (int p = i - lookbackPeriods + 1; p <= i; p++) + { + (bool? isUp, double pDiff) = ticks[p]; + + if (double.IsNaN(pDiff)) + { + sH = double.NaN; + sL = double.NaN; + break; + } + + // up + + if (isUp == true) + { + sH += pDiff; + } + + // down + else + { + sL += pDiff; + } + } + + cmo = sH + sL != 0 + ? (100 * (sH - sL) / (sH + sL)).NaN2Null() + : null; + } + + results.Add(new( + Timestamp: s.Timestamp, + Cmo: cmo)); + + prevValue = s.Value; + } + + return results; + } +} diff --git a/src/a-d/Cmo/Cmo.Utilities.cs b/src/a-d/Cmo/Cmo.Utilities.cs index 936073359..5ed18a20a 100644 --- a/src/a-d/Cmo/Cmo.Utilities.cs +++ b/src/a-d/Cmo/Cmo.Utilities.cs @@ -1,17 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// CHANDE MOMENTUM OSCILLATOR (UTILITIES) + +public static partial class Cmo { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Cmo != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for CMO."); + } } } diff --git a/src/a-d/ConnorsRsi/ConnorsRsi.Api.cs b/src/a-d/ConnorsRsi/ConnorsRsi.Api.cs deleted file mode 100644 index f7eba8633..000000000 --- a/src/a-d/ConnorsRsi/ConnorsRsi.Api.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CONNORS RSI (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetConnorsRsi( - this IEnumerable quotes, - int rsiPeriods = 3, - int streakPeriods = 2, - int rankPeriods = 100) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcConnorsRsi(rsiPeriods, streakPeriods, rankPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetConnorsRsi( - this IEnumerable results, - int rsiPeriods = 3, - int streakPeriods = 2, - int rankPeriods = 100) => results - .ToTuple() - .CalcConnorsRsi(rsiPeriods, streakPeriods, rankPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetConnorsRsi( - this IEnumerable<(DateTime, double)> priceTuples, - int rsiPeriods = 3, - int streakPeriods = 2, - int rankPeriods = 100) => priceTuples - .ToSortedList() - .CalcConnorsRsi(rsiPeriods, streakPeriods, rankPeriods); -} diff --git a/src/a-d/ConnorsRsi/ConnorsRsi.Models.cs b/src/a-d/ConnorsRsi/ConnorsRsi.Models.cs index 8f7556e57..9a8f29a08 100644 --- a/src/a-d/ConnorsRsi/ConnorsRsi.Models.cs +++ b/src/a-d/ConnorsRsi/ConnorsRsi.Models.cs @@ -1,19 +1,15 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class ConnorsRsiResult : ResultBase, IReusableResult +public record ConnorsRsiResult +( + DateTime Timestamp, + double Streak, + double? Rsi = null, + double? RsiStreak = null, + double? PercentRank = null, + double? ConnorsRsi = null +) : IReusable { - public ConnorsRsiResult(DateTime date) - { - Date = date; - } - - public double? Rsi { get; set; } - public double? RsiStreak { get; set; } - public double? PercentRank { get; set; } - public double? ConnorsRsi { get; set; } - - // internal use only - internal int Streak { get; set; } - double? IReusableResult.Value => ConnorsRsi; + public double Value => ConnorsRsi.Null2NaN(); } diff --git a/src/a-d/ConnorsRsi/ConnorsRsi.Series.cs b/src/a-d/ConnorsRsi/ConnorsRsi.Series.cs deleted file mode 100644 index 918df784c..000000000 --- a/src/a-d/ConnorsRsi/ConnorsRsi.Series.cs +++ /dev/null @@ -1,157 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CONNORS RSI (SERIES) -public static partial class Indicator -{ - internal static List CalcConnorsRsi( - this List<(DateTime, double)> tpList, - int rsiPeriods, - int streakPeriods, - int rankPeriods) - { - // check parameter arguments - ValidateConnorsRsi(rsiPeriods, streakPeriods, rankPeriods); - - // initialize - List results = tpList.CalcStreak(rsiPeriods, rankPeriods); - int startPeriod = Math.Max(rsiPeriods, Math.Max(streakPeriods, rankPeriods)) + 2; - int length = results.Count; - - // RSI of streak - List<(DateTime Date, double Streak)> bdStreak = results - .Remove(Math.Min(length, 1)) - .Select(x => (x.Date, (double)x.Streak)) - .ToList(); - - List rsiStreak = CalcRsi(bdStreak, streakPeriods); - - // compose final results - for (int p = streakPeriods + 2; p < length; p++) - { - ConnorsRsiResult r = results[p]; - RsiResult k = rsiStreak[p - 1]; - - r.RsiStreak = k.Rsi; - - if (p + 1 >= startPeriod) - { - r.ConnorsRsi = (r.Rsi + r.RsiStreak + r.PercentRank) / 3; - } - } - - return results; - } - - // calculate baseline streak and rank - private static List CalcStreak( - this List<(DateTime Date, double Streak)> tpList, - int rsiPeriods, - int rankPeriods) - { - // initialize - List rsiResults = CalcRsi(tpList, rsiPeriods); - - int length = tpList.Count; - List results = new(length); - double[] gain = new double[length]; - - double lastClose = double.NaN; - int streak = 0; - - // compose interim results - for (int i = 0; i < length; i++) - { - (DateTime date, double value) = tpList[i]; - - ConnorsRsiResult r = new(date) { - Rsi = rsiResults[i].Rsi - }; - results.Add(r); - - // bypass for first record - if (i == 0) - { - lastClose = value; - continue; - } - - // streak of up or down - if (value == lastClose) - { - streak = 0; - } - else if (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) ? double.NaN - : (value - lastClose) / lastClose; - - if (i + 1 > rankPeriods) - { - int qty = 0; - for (int p = i - rankPeriods; p <= i; p++) - { - if (gain[p] < gain[i]) - { - qty++; - } - } - - r.PercentRank = 100 * qty / rankPeriods; - } - - lastClose = value; - } - - return results; - } - - // parameter validation - private static void ValidateConnorsRsi( - int rsiPeriods, - int streakPeriods, - int rankPeriods) - { - // check parameter arguments - if (rsiPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(rsiPeriods), rsiPeriods, - "RSI period for Close price must be greater than 1 for ConnorsRsi."); - } - - if (streakPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(streakPeriods), streakPeriods, - "RSI period for Streak must be greater than 1 for ConnorsRsi."); - } - - if (rankPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(rankPeriods), rankPeriods, - "Percent Rank periods must be greater than 1 for ConnorsRsi."); - } - } -} diff --git a/src/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.cs b/src/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.cs new file mode 100644 index 000000000..9f224edca --- /dev/null +++ b/src/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.cs @@ -0,0 +1,168 @@ +namespace Skender.Stock.Indicators; + +// CONNORS RSI (SERIES) + +public static partial class ConnorsRsi +{ + public static IReadOnlyList ToConnorsRsi( + this IReadOnlyList source, + int rsiPeriods = 3, + int streakPeriods = 2, + int rankPeriods = 100) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(rsiPeriods, streakPeriods, rankPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + int startPeriod + = Math.Max(rsiPeriods, Math.Max(streakPeriods, rankPeriods)) + 2; + + IReadOnlyList streakInfo + = source.CalcStreak(rsiPeriods, rankPeriods); + + // RSI of streak + IReadOnlyList rsiStreak = streakInfo + .Select(si => new QuotePart(si.Timestamp, si.Streak)) + .ToList() + .ToRsi(streakPeriods); + + // compose final results + for (int i = 0; i < length; i++) + { + if (i >= streakPeriods + 2) + { + ConnorsRsiResult sInfo = streakInfo[i]; + RsiResult sRsi = rsiStreak[i]; + + double? crsi = null; + + if (i >= startPeriod - 1) + { + crsi = (sInfo.Rsi + sRsi.Rsi + sInfo.PercentRank) / 3; + } + + results.Add(sInfo with { + ConnorsRsi = crsi, + RsiStreak = sRsi.Rsi + }); + } + + // warmup periods + else + { + ConnorsRsiResult sInfo = streakInfo[i]; + results.Add(sInfo); + } + } + + return results; + } + + // calculate baseline streak and rank + private static List CalcStreak( + this IReadOnlyList source, + int rsiPeriods, + int rankPeriods) + where T : IReusable + { + // initialize + IReadOnlyList rsiResults = source.ToRsi(rsiPeriods); + + int length = source.Count; + List results = new(length); + double[] gain = new double[length]; + + double streak = 0; + double prevPrice = double.NaN; + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + double? percentRank = null; + + // bypass for first record + if (i == 0) + { + prevPrice = s.Value; + results.Add(new(s.Timestamp, 0)); + continue; + } + + // streak of up or down + if (double.IsNaN(s.Value) || double.IsNaN(prevPrice)) + { + streak = double.NaN; + } + else if (s.Value > prevPrice) + { + if (streak >= 0) + { + streak++; + } + else + { + streak = 1; + } + } + else if (s.Value < prevPrice) + { + if (streak <= 0) + { + streak--; + } + else + { + streak = -1; + } + } + else + { + streak = 0; + } + + // percentile rank + gain[i] = double.IsNaN(s.Value) || double.IsNaN(prevPrice) || prevPrice <= 0 + ? double.NaN + : (s.Value - prevPrice) / prevPrice; + + if (i > rankPeriods - 1 && !double.IsNaN(gain[i])) + { + int qty = 0; + bool isViableRank = true; + for (int p = i - rankPeriods; p <= i; p++) + { + // rank is not viable if there + // are incalculable gain values + if (double.IsNaN(gain[p])) + { + isViableRank = false; + break; + } + + if (gain[p] < gain[i]) + { + qty++; + } + } + + percentRank = isViableRank ? 100 * qty / rankPeriods : null; + } + + results.Add(new ConnorsRsiResult( + Timestamp: s.Timestamp, + Streak: streak, + Rsi: rsiResults[i].Rsi, + PercentRank: percentRank)); + + prevPrice = s.Value; + } + + return results; + } +} diff --git a/src/a-d/ConnorsRsi/ConnorsRsi.Utilities.cs b/src/a-d/ConnorsRsi/ConnorsRsi.Utilities.cs index be0489840..6fe17591f 100644 --- a/src/a-d/ConnorsRsi/ConnorsRsi.Utilities.cs +++ b/src/a-d/ConnorsRsi/ConnorsRsi.Utilities.cs @@ -1,17 +1,32 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// CONNORS RSI (UTILITIES) + +public static partial class ConnorsRsi { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int rsiPeriods, + int streakPeriods, + int rankPeriods) { - int n = results - .ToList() - .FindIndex(x => x.ConnorsRsi != null); + // check parameter arguments + if (rsiPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(rsiPeriods), rsiPeriods, + "RSI period for Close price must be greater than 1 for ConnorsRsi."); + } + + if (streakPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(streakPeriods), streakPeriods, + "RSI period for Streak must be greater than 1 for ConnorsRsi."); + } - return results.Remove(n); + if (rankPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(rankPeriods), rankPeriods, + "Percent Rank periods must be greater than 1 for ConnorsRsi."); + } } } diff --git a/src/a-d/Correlation/Correlation.Api.cs b/src/a-d/Correlation/Correlation.Api.cs deleted file mode 100644 index 709e77697..000000000 --- a/src/a-d/Correlation/Correlation.Api.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CORRELATION COEFFICIENT (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetCorrelation( - this IEnumerable quotesA, - IEnumerable quotesB, - int lookbackPeriods) - where TQuote : IQuote - { - List<(DateTime, double)> tpListA - = quotesA.ToTuple(CandlePart.Close); - - List<(DateTime, double)> tpListB - = quotesB.ToTuple(CandlePart.Close); - - return CalcCorrelation(tpListA, tpListB, lookbackPeriods); - } - - // SERIES, from CHAINS (both inputs reusable) - public static IEnumerable GetCorrelation( - this IEnumerable quotesA, - IEnumerable quotesB, - int lookbackPeriods) - { - List<(DateTime Date, double Value)> tpListA - = quotesA.ToTuple(); - - List<(DateTime Date, double Value)> tpListB - = quotesB.ToTuple(); - - return CalcCorrelation(tpListA, tpListB, lookbackPeriods) - .SyncIndex(quotesA, SyncType.Prepend); - } - - // SERIES, from TUPLE - public static IEnumerable GetCorrelation( - this IEnumerable<(DateTime, double)> tuplesA, - IEnumerable<(DateTime, double)> tuplesB, - int lookbackPeriods) - { - List<(DateTime, double)> tpListA = tuplesA.ToSortedList(); - List<(DateTime, double)> tpListB = tuplesB.ToSortedList(); - - return CalcCorrelation(tpListA, tpListB, lookbackPeriods); - } -} diff --git a/src/a-d/Correlation/Correlation.Models.cs b/src/a-d/Correlation/Correlation.Models.cs index 04ef2516b..08f4f0808 100644 --- a/src/a-d/Correlation/Correlation.Models.cs +++ b/src/a-d/Correlation/Correlation.Models.cs @@ -1,18 +1,15 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class CorrResult : ResultBase, IReusableResult +public record CorrResult +( + DateTime Timestamp, + double? VarianceA = null, + double? VarianceB = null, + double? Covariance = null, + double? Correlation = null, + double? RSquared = null +) : IReusable { - public CorrResult(DateTime date) - { - Date = date; - } - - public double? VarianceA { get; set; } - public double? VarianceB { get; set; } - public double? Covariance { get; set; } - public double? Correlation { get; set; } - public double? RSquared { get; set; } - - double? IReusableResult.Value => Correlation; + public double Value => Correlation.Null2NaN(); } diff --git a/src/a-d/Correlation/Correlation.Series.cs b/src/a-d/Correlation/Correlation.Series.cs deleted file mode 100644 index bd60e26a6..000000000 --- a/src/a-d/Correlation/Correlation.Series.cs +++ /dev/null @@ -1,119 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CORRELATION COEFFICIENT (SERIES) -public static partial class Indicator -{ - internal static List CalcCorrelation( - this List<(DateTime, double)> tpListA, - List<(DateTime, double)> tpListB, - int lookbackPeriods) - { - // check parameter arguments - ValidateCorrelation(tpListA, tpListB, lookbackPeriods); - - // initialize - int length = tpListA.Count; - List results = new(length); - - // roll through quotes - for (int i = 0; i < length; i++) - { - (DateTime aDate, double _) = tpListA[i]; - (DateTime bDate, double _) = tpListB[i]; - - if (aDate != bDate) - { - throw new InvalidQuotesException(nameof(tpListA), aDate, - "Date sequence does not match. Correlation requires matching dates in provided histories."); - } - - CorrResult r = new(aDate); - results.Add(r); - - // calculate correlation - if (i >= lookbackPeriods - 1) - { - double[] dataA = new double[lookbackPeriods]; - double[] dataB = new double[lookbackPeriods]; - int z = 0; - - for (int p = i + 1 - lookbackPeriods; p <= i; p++) - { - dataA[z] = tpListA[p].Item2; - dataB[z] = tpListB[p].Item2; - - z++; - } - - r.PeriodCorrelation(dataA, dataB); - } - } - - return results; - } - - // calculate correlation - private static void PeriodCorrelation( - this CorrResult r, - double[] dataA, - double[] dataB) - { - int length = dataA.Length; - double sumA = 0; - double sumB = 0; - double sumA2 = 0; - double sumB2 = 0; - double sumAB = 0; - - for (int i = 0; i < length; i++) - { - double a = dataA[i]; - double b = dataB[i]; - - sumA += a; - sumB += b; - sumA2 += a * a; - sumB2 += b * b; - sumAB += a * b; - } - - double avgA = sumA / length; - double avgB = sumB / length; - double avgA2 = sumA2 / length; - double avgB2 = sumB2 / length; - double avgAB = sumAB / length; - - double varA = avgA2 - (avgA * avgA); - double varB = avgB2 - (avgB * avgB); - double cov = avgAB - (avgA * avgB); - double divisor = Math.Sqrt(varA * varB); - - r.VarianceA = varA.NaN2Null(); - r.VarianceB = varB.NaN2Null(); - r.Covariance = cov.NaN2Null(); - r.Correlation = (divisor == 0) ? null : (cov / divisor).NaN2Null(); - r.RSquared = r.Correlation * r.Correlation; - } - - // parameter validation - private static void ValidateCorrelation( - List<(DateTime, double)> quotesA, - List<(DateTime, double)> quotesB, - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Correlation."); - } - - // check quotes - if (quotesA.Count != quotesB.Count) - { - throw new InvalidQuotesException( - nameof(quotesB), - "B quotes should have at least as many records as A quotes for Correlation."); - } - } -} diff --git a/src/a-d/Correlation/Correlation.StaticSeries.cs b/src/a-d/Correlation/Correlation.StaticSeries.cs new file mode 100644 index 000000000..e82a4df70 --- /dev/null +++ b/src/a-d/Correlation/Correlation.StaticSeries.cs @@ -0,0 +1,114 @@ +namespace Skender.Stock.Indicators; + +// CORRELATION COEFFICIENT (SERIES) + +public static partial class Correlation +{ + public static IReadOnlyList ToCorrelation( + this IReadOnlyList sourceA, + IReadOnlyList sourceB, + int lookbackPeriods) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(sourceA); + ArgumentNullException.ThrowIfNull(sourceB); + Validate(sourceA, sourceB, lookbackPeriods); + + // initialize + int length = sourceA.Count; + List results = new(length); + + // roll through source values + for (int i = 0; i < length; i++) + { + T a = sourceA[i]; + T b = sourceB[i]; + + if (a.Timestamp != b.Timestamp) + { + throw new InvalidQuotesException( + nameof(sourceA), a.Timestamp, + "Timestamp sequence does not match. " + + "Correlation requires matching dates in provided histories."); + } + + CorrResult r; + + // calculate correlation + if (i >= lookbackPeriods - 1) + { + double[] dataA = new double[lookbackPeriods]; + double[] dataB = new double[lookbackPeriods]; + int z = 0; + + for (int p = i + 1 - lookbackPeriods; p <= i; p++) + { + dataA[z] = sourceA[p].Value; + dataB[z] = sourceB[p].Value; + + z++; + } + + r = PeriodCorrelation(a.Timestamp, dataA, dataB); + } + else + { + r = new(Timestamp: a.Timestamp); + } + + results.Add(r); + } + + return results; + } + + // calculate correlation + internal static CorrResult PeriodCorrelation( + DateTime timestamp, + double[] dataA, + double[] dataB) + { + int length = dataA.Length; + double sumA = 0; + double sumB = 0; + double sumA2 = 0; + double sumB2 = 0; + double sumAb = 0; + + for (int i = 0; i < length; i++) + { + double a = dataA[i]; + double b = dataB[i]; + + sumA += a; + sumB += b; + sumA2 += a * a; + sumB2 += b * b; + sumAb += a * b; + } + + double avgA = sumA / length; + double avgB = sumB / length; + double avgA2 = sumA2 / length; + double avgB2 = sumB2 / length; + double avgAb = sumAb / length; + + double varA = avgA2 - avgA * avgA; + double varB = avgB2 - avgB * avgB; + double cov = avgAb - avgA * avgB; + double divisor = Math.Sqrt(varA * varB); + + double? corr = divisor == 0 + ? null + : (cov / divisor).NaN2Null(); + + return new( + Timestamp: timestamp, + VarianceA: varA.NaN2Null(), + VarianceB: varB.NaN2Null(), + Covariance: cov.NaN2Null(), + Correlation: corr, + RSquared: corr * corr); + } +} diff --git a/src/a-d/Correlation/Correlation.Utilities.cs b/src/a-d/Correlation/Correlation.Utilities.cs index c5071fd63..00df00337 100644 --- a/src/a-d/Correlation/Correlation.Utilities.cs +++ b/src/a-d/Correlation/Correlation.Utilities.cs @@ -1,17 +1,27 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +public static partial class Correlation { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + IReadOnlyList sourceA, + IReadOnlyList sourceB, + int lookbackPeriods) + where T : ISeries { - int removePeriods = results - .ToList() - .FindIndex(x => x.Correlation != null); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Correlation."); + } - return results.Remove(removePeriods); + // check quotes + if (sourceA.Count != sourceB.Count) + { + throw new InvalidQuotesException( + nameof(sourceB), + "B quotes should have at least as many records as A quotes for Correlation."); + } } } diff --git a/src/a-d/Dema/Dema.Api.cs b/src/a-d/Dema/Dema.Api.cs deleted file mode 100644 index ebd5a475e..000000000 --- a/src/a-d/Dema/Dema.Api.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Skender.Stock.Indicators; - -// DOUBLE EXPONENTIAL MOVING AVERAGE - DEMA (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetDema( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcDema(lookbackPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetDema( - this IEnumerable results, - int lookbackPeriods) => results - .ToTuple() - .CalcDema(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetDema( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods) => priceTuples - .ToSortedList() - .CalcDema(lookbackPeriods); -} diff --git a/src/a-d/Dema/Dema.Models.cs b/src/a-d/Dema/Dema.Models.cs index 9d8fe3379..3dc5047f1 100644 --- a/src/a-d/Dema/Dema.Models.cs +++ b/src/a-d/Dema/Dema.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class DemaResult : ResultBase, IReusableResult +public record DemaResult +( + DateTime Timestamp, + double? Dema = null +) : IReusable { - public DemaResult(DateTime date) - { - Date = date; - } - - public double? Dema { get; set; } - - double? IReusableResult.Value => Dema; + public double Value => Dema.Null2NaN(); } diff --git a/src/a-d/Dema/Dema.Series.cs b/src/a-d/Dema/Dema.Series.cs deleted file mode 100644 index 624d013c2..000000000 --- a/src/a-d/Dema/Dema.Series.cs +++ /dev/null @@ -1,69 +0,0 @@ -namespace Skender.Stock.Indicators; - -// DOUBLE EXPONENTIAL MOVING AVERAGE - DEMA (SERIES) -public static partial class Indicator -{ - internal static List CalcDema( - this List<(DateTime, double)> tpList, - int lookbackPeriods) - { - // check parameter arguments - ValidateDema(lookbackPeriods); - - // initialize - int length = tpList.Count; - List results = new(length); - - double k = 2d / (lookbackPeriods + 1); - double? lastEma1 = 0; - double? lastEma2; - int initPeriods = Math.Min(lookbackPeriods, length); - - for (int i = 0; i < initPeriods; i++) - { - (DateTime _, double value) = tpList[i]; - lastEma1 += value; - } - - lastEma1 /= lookbackPeriods; - lastEma2 = lastEma1; - - // roll through quotes - for (int i = 0; i < length; i++) - { - (DateTime date, double value) = tpList[i]; - - DemaResult r = new(date); - results.Add(r); - - if (i > lookbackPeriods - 1) - { - double? ema1 = lastEma1 + (k * (value - lastEma1)); - double? ema2 = lastEma2 + (k * (ema1 - lastEma2)); - - r.Dema = ((2d * ema1) - ema2).NaN2Null(); - - lastEma1 = ema1; - lastEma2 = ema2; - } - else if (i == lookbackPeriods - 1) - { - r.Dema = (2d * lastEma1) - lastEma2; - } - } - - return results; - } - - // parameter validation - private static void ValidateDema( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for DEMA."); - } - } -} diff --git a/src/a-d/Dema/Dema.StaticSeries.cs b/src/a-d/Dema/Dema.StaticSeries.cs new file mode 100644 index 000000000..0a10cb5a9 --- /dev/null +++ b/src/a-d/Dema/Dema.StaticSeries.cs @@ -0,0 +1,69 @@ +namespace Skender.Stock.Indicators; + +// DOUBLE EXPONENTIAL MOVING AVERAGE (SERIES) + +public static partial class Dema +{ + public static IReadOnlyList ToDema( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + double k = 2d / (lookbackPeriods + 1); + double lastEma1 = double.NaN; + double lastEma2 = double.NaN; + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + // skip incalculable periods + if (i < lookbackPeriods - 1) + { + results.Add(new(s.Timestamp)); + continue; + } + + double ema1; + double ema2; + + // when no prior EMA, reset as SMA + if (double.IsNaN(lastEma2)) + { + double sum = 0; + for (int p = i - lookbackPeriods + 1; p <= i; p++) + { + T ps = source[p]; + sum += ps.Value; + } + + ema1 = ema2 = sum / lookbackPeriods; + } + + // normal DEMA + else + { + ema1 = lastEma1 + (k * (s.Value - lastEma1)); + ema2 = lastEma2 + (k * (ema1 - lastEma2)); + } + + results.Add(new DemaResult( + Timestamp: s.Timestamp, + Dema: ((2d * ema1) - ema2).NaN2Null())); + + lastEma1 = ema1; + lastEma2 = ema2; + } + + return results; + } +} diff --git a/src/a-d/Dema/Dema.Utilities.cs b/src/a-d/Dema/Dema.Utilities.cs index 34641fa54..e04ddcead 100644 --- a/src/a-d/Dema/Dema.Utilities.cs +++ b/src/a-d/Dema/Dema.Utilities.cs @@ -1,17 +1,30 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// DOUBLE EXPONENTIAL MOVING AVERAGE (UTILITIES) + +public static partial class Dema { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int n = results .ToList() .FindIndex(x => x.Dema != null) + 1; - return results.Remove((2 * n) + 100); + return results.Remove(2 * n + 100); + } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for DEMA."); + } } } diff --git a/src/a-d/Doji/Doji.Api.cs b/src/a-d/Doji/Doji.Api.cs deleted file mode 100644 index 991a04349..000000000 --- a/src/a-d/Doji/Doji.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// DOJI (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetDoji( - this IEnumerable quotes, - double maxPriceChangePercent = 0.1) - where TQuote : IQuote => quotes - .CalcDoji(maxPriceChangePercent); -} diff --git a/src/a-d/Doji/Doji.Series.cs b/src/a-d/Doji/Doji.Series.cs deleted file mode 100644 index 3fa081375..000000000 --- a/src/a-d/Doji/Doji.Series.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace Skender.Stock.Indicators; - -// DOJI (SERIES) -public static partial class Indicator -{ - /// - /// - internal static List CalcDoji( - this IEnumerable quotes, - double maxPriceChangePercent) - where TQuote : IQuote - { - // check parameter arguments - ValidateDoji(maxPriceChangePercent); - - // initialize - List results = quotes.ToCandleResults(); - maxPriceChangePercent /= 100; - int length = results.Count; - - // roll through candles - for (int i = 0; i < length; i++) - { - CandleResult r = results[i]; - - // check for current signal - if (r.Candle.Open != 0 - && Math.Abs((double)(r.Candle.Close / r.Candle.Open) - 1d) <= maxPriceChangePercent) - { - r.Price = r.Candle.Close; - r.Match = Match.Neutral; - } - } - - return results; - } - - // parameter validation - private static void ValidateDoji( - double maxPriceChangePercent) - { - // check parameter arguments - if (maxPriceChangePercent is < 0 or > 0.5) - { - throw new ArgumentOutOfRangeException(nameof(maxPriceChangePercent), maxPriceChangePercent, - "Maximum Percent Change must be between 0 and 0.5 for Doji (0% to 0.5%)."); - } - } -} diff --git a/src/a-d/Doji/Doji.StaticSeries.cs b/src/a-d/Doji/Doji.StaticSeries.cs new file mode 100644 index 000000000..982d4f3eb --- /dev/null +++ b/src/a-d/Doji/Doji.StaticSeries.cs @@ -0,0 +1,60 @@ +namespace Skender.Stock.Indicators; + +// DOJI (SERIES) + +public static partial class Doji +{ + /// + /// Doji is a single candlestick pattern where open and close price + /// are virtually identical, representing market indecision. + /// + /// Configurable Quote type. + /// See Guide for more information. + /// Historical price quotes. + /// + /// Optional.Maximum absolute percent difference in open and close price. + /// + /// Time series of Doji values. + /// + /// Invalid parameter value provided. + /// + public static IReadOnlyList ToDoji( + this IReadOnlyList quotes, + double maxPriceChangePercent = 0.1) + where TQuote : IQuote + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(quotes); + Validate(maxPriceChangePercent); + + // initialize + int length = quotes.Count; + List results = new(length); + + maxPriceChangePercent /= 100; + + // roll through candles + for (int i = 0; i < length; i++) + { + TQuote q = quotes[i]; + decimal? matchPrice = null; + Match matchType = Match.None; + + // check for current signal + if (q.Open != 0 + && Math.Abs((double)(q.Close / q.Open) - 1d) <= maxPriceChangePercent) + { + matchPrice = q.Close; + matchType = Match.Neutral; + } + + results.Add(new CandleResult( + timestamp: q.Timestamp, + quote: q, + match: matchType, + price: matchPrice)); + } + + return results; + } +} diff --git a/src/a-d/Doji/Doji.Utilities.cs b/src/a-d/Doji/Doji.Utilities.cs new file mode 100644 index 000000000..eb8b3988c --- /dev/null +++ b/src/a-d/Doji/Doji.Utilities.cs @@ -0,0 +1,18 @@ +namespace Skender.Stock.Indicators; + +// DOJI (UTILITIES) + +public static partial class Doji +{ + // parameter validation + internal static void Validate( + double maxPriceChangePercent) + { + // check parameter arguments + if (maxPriceChangePercent is < 0 or > 0.5) + { + throw new ArgumentOutOfRangeException(nameof(maxPriceChangePercent), maxPriceChangePercent, + "Maximum Percent Change must be between 0 and 0.5 for Doji (0% to 0.5%)."); + } + } +} diff --git a/src/a-d/Doji/info.xml b/src/a-d/Doji/info.xml deleted file mode 100644 index c063a5d45..000000000 --- a/src/a-d/Doji/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Doji is a single candlestick pattern where open and close price are virtually identical, representing market indecision. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Optional. Maximum absolute percent difference in open and close price. - Time series of Doji values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/Donchian/Donchian.Api.cs b/src/a-d/Donchian/Donchian.Api.cs deleted file mode 100644 index d303e68f8..000000000 --- a/src/a-d/Donchian/Donchian.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// DONCHIAN CHANNEL (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetDonchian( - this IEnumerable quotes, - int lookbackPeriods = 20) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcDonchian(lookbackPeriods); -} diff --git a/src/a-d/Donchian/Donchian.Models.cs b/src/a-d/Donchian/Donchian.Models.cs index 33594a24e..f20ad4ba5 100644 --- a/src/a-d/Donchian/Donchian.Models.cs +++ b/src/a-d/Donchian/Donchian.Models.cs @@ -1,15 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class DonchianResult : ResultBase -{ - public DonchianResult(DateTime date) - { - Date = date; - } - - public decimal? UpperBand { get; set; } - public decimal? Centerline { get; set; } - public decimal? LowerBand { get; set; } - public decimal? Width { get; set; } -} +public record DonchianResult +( + DateTime Timestamp, + decimal? UpperBand = null, + decimal? Centerline = null, + decimal? LowerBand = null, + decimal? Width = null +) : ISeries; diff --git a/src/a-d/Donchian/Donchian.Series.cs b/src/a-d/Donchian/Donchian.Series.cs deleted file mode 100644 index 4e7a87194..000000000 --- a/src/a-d/Donchian/Donchian.Series.cs +++ /dev/null @@ -1,69 +0,0 @@ -namespace Skender.Stock.Indicators; - -// DONCHIAN CHANNEL (SERIES) -public static partial class Indicator -{ - internal static List CalcDonchian( - this List quotesList, - int lookbackPeriods) - where TQuote : IQuote - { - // check parameter arguments - ValidateDonchian(lookbackPeriods); - - // initialize - int length = quotesList.Count; - List results = new(length); - - // roll through quotes - for (int i = 0; i < length; i++) - { - TQuote q = quotesList[i]; - - DonchianResult r = new(q.Date); - results.Add(r); - - if (i >= lookbackPeriods) - { - decimal highHigh = 0; - decimal lowLow = decimal.MaxValue; - - // high/low over prior periods - for (int p = i - lookbackPeriods; p < i; p++) - { - TQuote d = quotesList[p]; - - if (d.High > highHigh) - { - highHigh = d.High; - } - - if (d.Low < lowLow) - { - lowLow = d.Low; - } - } - - r.UpperBand = highHigh; - r.LowerBand = lowLow; - r.Centerline = (r.UpperBand + r.LowerBand) / 2m; - r.Width = (r.Centerline == 0) ? null - : (r.UpperBand - r.LowerBand) / r.Centerline; - } - } - - return results; - } - - // parameter validation - private static void ValidateDonchian( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Donchian Channel."); - } - } -} diff --git a/src/a-d/Donchian/Donchian.StaticSeries.cs b/src/a-d/Donchian/Donchian.StaticSeries.cs new file mode 100644 index 000000000..483ac4a34 --- /dev/null +++ b/src/a-d/Donchian/Donchian.StaticSeries.cs @@ -0,0 +1,66 @@ +namespace Skender.Stock.Indicators; + +// DONCHIAN CHANNEL (SERIES) + +public static partial class Donchian +{ + public static IReadOnlyList ToDonchian( + this IReadOnlyList quotes, + int lookbackPeriods = 20) + where TQuote : IQuote + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(quotes); + Validate(lookbackPeriods); + + // initialize + int length = quotes.Count; + List results = new(length); + + // roll through source values + for (int i = 0; i < length; i++) + { + TQuote q = quotes[i]; + + if (i >= lookbackPeriods) + { + decimal highHigh = 0; + decimal lowLow = decimal.MaxValue; + + // high/low over prior periods + for (int p = i - lookbackPeriods; p < i; p++) + { + TQuote d = quotes[p]; + + if (d.High > highHigh) + { + highHigh = d.High; + } + + if (d.Low < lowLow) + { + lowLow = d.Low; + } + } + + decimal u = highHigh; + decimal l = lowLow; + decimal c = (u + l) / 2m; + + results.Add(new DonchianResult( + Timestamp: q.Timestamp, + UpperBand: u, + LowerBand: l, + Centerline: c, + Width: c == 0 ? null : (u - l) / c)); + } + else + { + results.Add(new(q.Timestamp)); + + } + } + + return results; + } +} diff --git a/src/a-d/Donchian/Donchian.Utilities.cs b/src/a-d/Donchian/Donchian.Utilities.cs index d5806064a..5cd392a2e 100644 --- a/src/a-d/Donchian/Donchian.Utilities.cs +++ b/src/a-d/Donchian/Donchian.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// DONCHIAN CHANNEL (UTILITIES) + +public static partial class Donchian { // CONDENSE (REMOVE null results) - /// - /// - public static IEnumerable Condense( - this IEnumerable results) + /// + public static IReadOnlyList Condense( + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -19,10 +20,9 @@ public static IEnumerable Condense( } // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -30,4 +30,16 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Donchian Channel."); + } + } } diff --git a/src/a-d/Dpo/Dpo.Api.cs b/src/a-d/Dpo/Dpo.Api.cs deleted file mode 100644 index 83b245ea6..000000000 --- a/src/a-d/Dpo/Dpo.Api.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Skender.Stock.Indicators; - -// DETRENDED PRICE OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetDpo( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcDpo(lookbackPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetDpo( - this IEnumerable results, - int lookbackPeriods) => results - .ToTuple() - .CalcDpo(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetDpo( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods) => priceTuples - .ToSortedList() - .CalcDpo(lookbackPeriods); -} diff --git a/src/a-d/Dpo/Dpo.Models.cs b/src/a-d/Dpo/Dpo.Models.cs index 0526c7ae6..a6150372e 100644 --- a/src/a-d/Dpo/Dpo.Models.cs +++ b/src/a-d/Dpo/Dpo.Models.cs @@ -1,15 +1,12 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class DpoResult : ResultBase, IReusableResult +public record DpoResult +( + DateTime Timestamp, + double? Dpo = null, + double? Sma = null + ) : IReusable { - public DpoResult(DateTime date) - { - Date = date; - } - - public double? Sma { get; set; } - public double? Dpo { get; set; } - - double? IReusableResult.Value => Dpo; + public double Value => Dpo.Null2NaN(); } diff --git a/src/a-d/Dpo/Dpo.Series.cs b/src/a-d/Dpo/Dpo.Series.cs deleted file mode 100644 index b63e00ccb..000000000 --- a/src/a-d/Dpo/Dpo.Series.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace Skender.Stock.Indicators; - -// DETRENDED PRICE OSCILLATOR (SERIES) -public static partial class Indicator -{ - // calculate series - internal static List CalcDpo( - this List<(DateTime, double)> tpList, - int lookbackPeriods) - { - // check parameter arguments - ValidateDpo(lookbackPeriods); - - // initialize - int length = tpList.Count; - int offset = (lookbackPeriods / 2) + 1; - List sma = tpList.GetSma(lookbackPeriods).ToList(); - List results = new(length); - - // roll through quotes - for (int i = 0; i < length; i++) - { - (DateTime date, double value) = tpList[i]; - - DpoResult r = new(date); - results.Add(r); - - if (i >= lookbackPeriods - offset - 1 && i < length - offset) - { - SmaResult s = sma[i + offset]; - r.Sma = s.Sma; - r.Dpo = s.Sma is null ? null : (value - s.Sma).NaN2Null(); - } - } - - return results; - } - - // parameter validation - private static void ValidateDpo( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for DPO."); - } - } -} diff --git a/src/a-d/Dpo/Dpo.StaticSeries.cs b/src/a-d/Dpo/Dpo.StaticSeries.cs new file mode 100644 index 000000000..1298baca3 --- /dev/null +++ b/src/a-d/Dpo/Dpo.StaticSeries.cs @@ -0,0 +1,50 @@ +namespace Skender.Stock.Indicators; + +// DETRENDED PRICE OSCILLATOR (SERIES) + +public static partial class Dpo +{ + public static IReadOnlyList ToDpo( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + int offset = (lookbackPeriods / 2) + 1; + + IReadOnlyList sma + = source.ToSma(lookbackPeriods); + + // roll through source values + for (int i = 0; i < length; i++) + { + T src = source[i]; + + double? dpoSma = null; + double? dpoVal = null; + + if (i >= lookbackPeriods - offset - 1 && i < length - offset) + { + SmaResult s = sma[i + offset]; + dpoSma = s.Sma; + dpoVal = s.Sma is null ? null : src.Value - s.Sma; + } + + DpoResult r = new( + Timestamp: src.Timestamp, + Dpo: dpoVal, + Sma: dpoSma); + + results.Add(r); + } + + return results; + } +} diff --git a/src/a-d/Dpo/Dpo.Utilities.cs b/src/a-d/Dpo/Dpo.Utilities.cs new file mode 100644 index 000000000..d6170d70c --- /dev/null +++ b/src/a-d/Dpo/Dpo.Utilities.cs @@ -0,0 +1,19 @@ +namespace Skender.Stock.Indicators; + +// DETRENDED PRICE OSCILLATOR (UTILITIES) + +public static partial class Dpo +{ + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for DPO."); + } + } + +} diff --git a/src/a-d/Dynamic/Dynamic.Api.cs b/src/a-d/Dynamic/Dynamic.Api.cs deleted file mode 100644 index 73ca72dc8..000000000 --- a/src/a-d/Dynamic/Dynamic.Api.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// McGINLEY DYNAMIC -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetDynamic( - this IEnumerable quotes, - int lookbackPeriods, - double kFactor = 0.6) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcDynamic(lookbackPeriods, kFactor); - - // SERIES, from CHAIN - public static IEnumerable GetDynamic( - this IEnumerable results, - int lookbackPeriods, - double kFactor = 0.6) => results - .ToTuple() - .CalcDynamic(lookbackPeriods, kFactor) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetDynamic( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods, - double kFactor = 0.6) => priceTuples - .ToSortedList() - .CalcDynamic(lookbackPeriods, kFactor); -} diff --git a/src/a-d/Dynamic/Dynamic.Models.cs b/src/a-d/Dynamic/Dynamic.Models.cs index 9fd3d6294..f120c7156 100644 --- a/src/a-d/Dynamic/Dynamic.Models.cs +++ b/src/a-d/Dynamic/Dynamic.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class DynamicResult : ResultBase, IReusableResult +public record DynamicResult +( + DateTime Timestamp, + double? Dynamic +) : IReusable { - public DynamicResult(DateTime date) - { - Date = date; - } - - public double? Dynamic { get; set; } - - double? IReusableResult.Value => Dynamic; + public double Value => Dynamic.Null2NaN(); } diff --git a/src/a-d/Dynamic/Dynamic.Series.cs b/src/a-d/Dynamic/Dynamic.Series.cs deleted file mode 100644 index 9d0f47e01..000000000 --- a/src/a-d/Dynamic/Dynamic.Series.cs +++ /dev/null @@ -1,75 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MONEY FLOW INDEX (SERIES) -public static partial class Indicator -{ - internal static List CalcDynamic( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - double kFactor) - { - // check parameter arguments - ValidateDynamic(lookbackPeriods, kFactor); - - // initialize - int iStart = 1; - int length = tpList.Count; - List results = new(length); - - if (length == 0) - { - return results; - } - - double prevMD = tpList[0].Item2; - - // roll through quotes, to get preliminary data - for (int i = 0; i < length; i++) - { - (DateTime date, double value) = tpList[i]; - - DynamicResult r = new(date); - results.Add(r); - - // re-initialize if value is NaN - if (double.IsNaN(value) || prevMD == 0) - { - prevMD = value; - iStart = i + lookbackPeriods; - } - else - { - double md = prevMD + ((value - prevMD) / - (kFactor * lookbackPeriods * Math.Pow(value / prevMD, 4))); - - if (i >= iStart) - { - r.Dynamic = md.NaN2Null(); - } - - prevMD = md; - } - } - - return results; - } - - // parameter validation - private static void ValidateDynamic( - int lookbackPeriods, - double kFactor) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for DYNAMIC."); - } - - if (kFactor <= 0) - { - throw new ArgumentOutOfRangeException(nameof(kFactor), kFactor, - "K-Factor range adjustment must be greater than 0 for DYNAMIC."); - } - } -} diff --git a/src/a-d/Dynamic/Dynamic.StaticSeries.cs b/src/a-d/Dynamic/Dynamic.StaticSeries.cs new file mode 100644 index 000000000..03c56c40e --- /dev/null +++ b/src/a-d/Dynamic/Dynamic.StaticSeries.cs @@ -0,0 +1,44 @@ +namespace Skender.Stock.Indicators; + +// McGINLEY DYNAMIC (SERIES) + +public static partial class MgDynamic +{ + public static IReadOnlyList ToDynamic( + this IReadOnlyList source, + int lookbackPeriods, + double kFactor = 0.6) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods, kFactor); + + // initialize + int length = source.Count; + List results = new(length); + + // skip first period + if (length > 0) + { + results.Add(new(source[0].Timestamp, null)); + } + + // roll through source values + for (int i = 1; i < length; i++) + { + double? dyn = Increment( + lookbackPeriods, + kFactor, + newVal: source[i].Value, + prevDyn: results[i - 1].Dynamic ?? source[i - 1].Value + ).NaN2Null(); + + results.Add(new DynamicResult( + Timestamp: source[i].Timestamp, + Dynamic: dyn)); + } + + return results; + } +} diff --git a/src/a-d/Dynamic/Dynamic.Utilities.cs b/src/a-d/Dynamic/Dynamic.Utilities.cs new file mode 100644 index 000000000..5228d3c4d --- /dev/null +++ b/src/a-d/Dynamic/Dynamic.Utilities.cs @@ -0,0 +1,36 @@ +namespace Skender.Stock.Indicators; + +// McGINLEY DYNAMIC (UTILITIES) + +public static partial class MgDynamic +{ + // increment calculation + public static double Increment( + int lookbackPeriods, + double kFactor, + double newVal, + double prevDyn) + => prevDyn + ( + (newVal - prevDyn) + / (kFactor * lookbackPeriods * Math.Pow(newVal / prevDyn, 4))); + + // parameter validation + internal static void Validate( + int lookbackPeriods, + double kFactor) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for DYNAMIC."); + } + + if (kFactor <= 0) + { + throw new ArgumentOutOfRangeException(nameof(kFactor), kFactor, + "K-Factor range adjustment must be greater than 0 for DYNAMIC."); + } + } + +} diff --git a/src/e-k/ElderRay/ElderRay.Api.cs b/src/e-k/ElderRay/ElderRay.Api.cs deleted file mode 100644 index 6721637af..000000000 --- a/src/e-k/ElderRay/ElderRay.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ELDER-RAY (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetElderRay( - this IEnumerable quotes, - int lookbackPeriods = 13) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcElderRay(lookbackPeriods); -} diff --git a/src/e-k/ElderRay/ElderRay.Models.cs b/src/e-k/ElderRay/ElderRay.Models.cs index 1ffd29543..275bf9580 100644 --- a/src/e-k/ElderRay/ElderRay.Models.cs +++ b/src/e-k/ElderRay/ElderRay.Models.cs @@ -1,16 +1,13 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class ElderRayResult : ResultBase, IReusableResult +public record ElderRayResult +( + DateTime Timestamp, + double? Ema, + double? BullPower, + double? BearPower +) : IReusable { - public ElderRayResult(DateTime date) - { - Date = date; - } - - public double? Ema { get; set; } - public double? BullPower { get; set; } - public double? BearPower { get; set; } - - double? IReusableResult.Value => BullPower + BearPower; + public double Value => (BullPower + BearPower).Null2NaN(); } diff --git a/src/e-k/ElderRay/ElderRay.Series.cs b/src/e-k/ElderRay/ElderRay.Series.cs deleted file mode 100644 index ba8f366d4..000000000 --- a/src/e-k/ElderRay/ElderRay.Series.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ELDER-RAY (SERIES) -public static partial class Indicator -{ - internal static List CalcElderRay( - this List qdList, - int lookbackPeriods) - { - // check parameter arguments - ValidateElderRay(lookbackPeriods); - - // initialize with EMA - List results = qdList - .ToTuple(CandlePart.Close) - .CalcEma(lookbackPeriods) - .Select(x => new ElderRayResult(x.Date) { - Ema = x.Ema - }) - .ToList(); - - // roll through quotes - for (int i = lookbackPeriods - 1; i < qdList.Count; i++) - { - QuoteD q = qdList[i]; - ElderRayResult r = results[i]; - - r.BullPower = q.High - r.Ema; - r.BearPower = q.Low - r.Ema; - } - - return results; - } - - // parameter validation - private static void ValidateElderRay( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Elder-ray Index."); - } - } -} diff --git a/src/e-k/ElderRay/ElderRay.StaticSeries.cs b/src/e-k/ElderRay/ElderRay.StaticSeries.cs new file mode 100644 index 000000000..785223db6 --- /dev/null +++ b/src/e-k/ElderRay/ElderRay.StaticSeries.cs @@ -0,0 +1,44 @@ +namespace Skender.Stock.Indicators; + +// ELDER-RAY (SERIES) + +public static partial class ElderRay +{ + public static IReadOnlyList ToElderRay( + this IReadOnlyList quotes, + int lookbackPeriods = 13) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcElderRay(lookbackPeriods); + + private static List CalcElderRay( + this IReadOnlyList source, + int lookbackPeriods) + { + // check parameter arguments + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + // EMA + IReadOnlyList emaResults + = source.ToEma(lookbackPeriods); + + // roll through source values + for (int i = 0; i < length; i++) + { + QuoteD q = source[i]; + EmaResult e = emaResults[i]; + + results.Add(new( + Timestamp: e.Timestamp, + Ema: e.Ema, + BullPower: q.High - e.Ema, + BearPower: q.Low - e.Ema)); + } + + return results; + } +} diff --git a/src/e-k/ElderRay/ElderRay.Utilities.cs b/src/e-k/ElderRay/ElderRay.Utilities.cs index d98fa8fa7..5e789103b 100644 --- a/src/e-k/ElderRay/ElderRay.Utilities.cs +++ b/src/e-k/ElderRay/ElderRay.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ELDER-RAY (UTILITIES) + +public static partial class ElderRay { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int n = results .ToList() @@ -14,4 +15,16 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(n + 100); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Elder-ray Index."); + } + } } diff --git a/src/e-k/Ema/Ema.Api.cs b/src/e-k/Ema/Ema.Api.cs deleted file mode 100644 index 765314080..000000000 --- a/src/e-k/Ema/Ema.Api.cs +++ /dev/null @@ -1,52 +0,0 @@ -namespace Skender.Stock.Indicators; - -// EXPONENTIAL MOVING AVERAGE (API) - -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetEma( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcEma(lookbackPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetEma( - this IEnumerable results, - int lookbackPeriods) => results - .ToTuple() - .CalcEma(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetEma( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods) => priceTuples - .ToSortedList() - .CalcEma(lookbackPeriods); - - // OBSERVER, from Quote Provider - /// - /// - public static EmaObserver GetEma( - this QuoteProvider provider, - int lookbackPeriods) - { - UseObserver useObserver = provider - .Use(CandlePart.Close); - - return new(useObserver, lookbackPeriods); - } - - // OBSERVER, from Chain Provider - /// - /// - public static EmaObserver GetEma( - this TupleProvider tupleProvider, - int lookbackPeriods) - => new(tupleProvider, lookbackPeriods); -} diff --git a/src/e-k/Ema/Ema.Increments.cs b/src/e-k/Ema/Ema.Increments.cs new file mode 100644 index 000000000..0df4706d5 --- /dev/null +++ b/src/e-k/Ema/Ema.Increments.cs @@ -0,0 +1,90 @@ +namespace Skender.Stock.Indicators; + +// EXPONENTIAL MOVING AVERAGE (INCREMENTING LIST) + +/// +/// Exponential Moving Average (EMA) +/// from incremental reusable values. +/// +public class EmaList : List, IEma, IAddQuote, IAddReusable +{ + private readonly Queue _buffer; + private double _bufferSum; + + public EmaList(int lookbackPeriods) + { + Ema.Validate(lookbackPeriods); + LookbackPeriods = lookbackPeriods; + K = 2d / (lookbackPeriods + 1); + + _buffer = new(lookbackPeriods); + _bufferSum = 0; + } + + public int LookbackPeriods { get; init; } + public double K { get; init; } + + public void Add(DateTime timestamp, double value) + { + // update buffer + if (_buffer.Count == LookbackPeriods) + { + _bufferSum -= _buffer.Dequeue(); + } + _buffer.Enqueue(value); + _bufferSum += value; + + // add nulls for incalculable periods + if (Count < LookbackPeriods - 1) + { + base.Add(new EmaResult(timestamp)); + return; + } + + // re/initialize as SMA + if (this[^1].Ema is null) + { + base.Add(new EmaResult( + timestamp, + _bufferSum / LookbackPeriods)); + return; + } + + // calculate EMA normally + base.Add(new EmaResult( + timestamp, + Ema.Increment(K, this[^1].Ema, value))); + } + + public void Add(IReusable value) + { + ArgumentNullException.ThrowIfNull(value); + Add(value.Timestamp, value.Value); + } + + public void Add(IReadOnlyList values) + { + ArgumentNullException.ThrowIfNull(values); + + for (int i = 0; i < values.Count; i++) + { + Add(values[i].Timestamp, values[i].Value); + } + } + + public void Add(IQuote quote) + { + ArgumentNullException.ThrowIfNull(quote); + Add(quote.Timestamp, quote.Value); + } + + public void Add(IReadOnlyList quotes) + { + ArgumentNullException.ThrowIfNull(quotes); + + for (int i = 0; i < quotes.Count; i++) + { + Add(quotes[i]); + } + } +} diff --git a/src/e-k/Ema/Ema.Models.cs b/src/e-k/Ema/Ema.Models.cs index 9d94b7d9f..7b5fca5c0 100644 --- a/src/e-k/Ema/Ema.Models.cs +++ b/src/e-k/Ema/Ema.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class EmaResult : ResultBase, IReusableResult +public record EmaResult +( + DateTime Timestamp, + double? Ema = null +) : IReusable { - public EmaResult(DateTime date) - { - Date = date; - } - - public double? Ema { get; set; } - - double? IReusableResult.Value => Ema; + public double Value => Ema.Null2NaN(); } diff --git a/src/e-k/Ema/Ema.Observer.cs b/src/e-k/Ema/Ema.Observer.cs deleted file mode 100644 index 88367dcc9..000000000 --- a/src/e-k/Ema/Ema.Observer.cs +++ /dev/null @@ -1,144 +0,0 @@ -namespace Skender.Stock.Indicators; - -// EXPONENTIAL MOVING AVERAGE (STREAMING) - -public class EmaObserver : ChainProvider -{ - public EmaObserver( - TupleProvider provider, - int lookbackPeriods) - { - Supplier = provider; - ProtectedResults = []; - - LookbackPeriods = lookbackPeriods; - K = 2d / (lookbackPeriods + 1); - - Initialize(); - } - - // PROPERTIES - - public IEnumerable Results => ProtectedResults; - internal List ProtectedResults { get; set; } - - private double WarmupValue { get; set; } - private int LookbackPeriods { get; set; } - private double K { get; set; } - - // STATIC METHODS - - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for EMA."); - } - } - - // incremental calculation - internal static double Increment(double newValue, double lastEma, double k) - => lastEma + (k * (newValue - lastEma)); - - // NON-STATIC METHODS - - // handle quote arrival - public override void OnNext((DateTime Date, double Value) value) => Add(value); - - // add new tuple quote - internal void Add((DateTime Date, double Value) tuple) - { - // candidate result (empty) - EmaResult r = new(tuple.Date); - - // initialize - int length = ProtectedResults.Count; - - if (length == 0) - { - ProtectedResults.Add(r); - WarmupValue += tuple.Value; - SendToChain(r); - return; - } - - // check against last entry - EmaResult last = ProtectedResults[length - 1]; - - // initialization periods - if (length < LookbackPeriods - 1) - { - // add if not duplicate - if (last.Date != r.Date) - { - ProtectedResults.Add(r); - WarmupValue += tuple.Value; - } - - return; - } - - // initialize with SMA - if (length == LookbackPeriods - 1) - { - WarmupValue += tuple.Value; - r.Ema = (WarmupValue / LookbackPeriods).NaN2Null(); - ProtectedResults.Add(r); - SendToChain(r); - return; - } - - // add new - if (r.Date > last.Date) - { - // calculate incremental value - double lastEma = (last.Ema == null) ? double.NaN : (double)last.Ema; - double newEma = Increment(tuple.Value, lastEma, K); - - r.Ema = newEma.NaN2Null(); - ProtectedResults.Add(r); - SendToChain(r); - return; - } - - // update last - else if (r.Date == last.Date) - { - // get prior last EMA - EmaResult prior = ProtectedResults[length - 2]; - - double priorEma = (prior.Ema == null) ? double.NaN : (double)prior.Ema; - last.Ema = Increment(tuple.Value, priorEma, K); - SendToChain(last); - return; - } - - // late arrival - else - { - // heal - throw new NotImplementedException(); - } - } - - // calculate with provider cache - private void Initialize() - { - if (Supplier != null) - { - List<(DateTime, double)> tuples = Supplier - .ProtectedTuples; - - for (int i = 0; i < tuples.Count; i++) - { - Add(tuples[i]); - } - - Subscribe(); - } - } -} diff --git a/src/e-k/Ema/Ema.Series.cs b/src/e-k/Ema/Ema.Series.cs deleted file mode 100644 index 77952750d..000000000 --- a/src/e-k/Ema/Ema.Series.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace Skender.Stock.Indicators; - -// EXPONENTIAL MOVING AVERAGE (SERIES) -public static partial class Indicator -{ - internal static List CalcEma( - this List<(DateTime, double)> tpList, - int lookbackPeriods) - { - // check parameter arguments - EmaObserver.Validate(lookbackPeriods); - - // initialize - int length = tpList.Count; - List results = new(length); - - double lastEma = 0; - double k = 2d / (lookbackPeriods + 1); - int initPeriods = Math.Min(lookbackPeriods, length); - - for (int i = 0; i < initPeriods; i++) - { - (DateTime _, double value) = tpList[i]; - lastEma += value; - } - - lastEma /= lookbackPeriods; - - // roll through quotes - for (int i = 0; i < length; i++) - { - (DateTime date, double value) = tpList[i]; - EmaResult r = new(date); - results.Add(r); - - if (i + 1 > lookbackPeriods) - { - double ema = EmaObserver.Increment(value, lastEma, k); - r.Ema = ema.NaN2Null(); - lastEma = ema; - } - else if (i == lookbackPeriods - 1) - { - r.Ema = lastEma.NaN2Null(); - } - } - - return results; - } -} diff --git a/src/e-k/Ema/Ema.StaticSeries.cs b/src/e-k/Ema/Ema.StaticSeries.cs new file mode 100644 index 000000000..49ad66637 --- /dev/null +++ b/src/e-k/Ema/Ema.StaticSeries.cs @@ -0,0 +1,52 @@ +namespace Skender.Stock.Indicators; + +// EXPONENTIAL MOVING AVERAGE (SERIES) + +public static partial class Ema +{ + public static IReadOnlyList ToEma( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + EmaResult[] results = new EmaResult[length]; + + double lastEma = double.NaN; + double k = 2d / (lookbackPeriods + 1); + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + // skip incalculable periods + if (i < lookbackPeriods - 1) + { + results[i] = new EmaResult(Timestamp: s.Timestamp); + continue; + } + + double ema = !double.IsNaN(lastEma) + + // calculate EMA (normally) + ? Ema.Increment(k, lastEma, s.Value) + + // when no prior EMA, reset as SMA + : Sma.Increment(source, lookbackPeriods, i); + + results[i] = new EmaResult( + Timestamp: s.Timestamp, + Ema: ema.NaN2Null()); + + lastEma = ema; + } + + return new List(results); + } +} diff --git a/src/e-k/Ema/Ema.StreamHub.cs b/src/e-k/Ema/Ema.StreamHub.cs new file mode 100644 index 000000000..c66697259 --- /dev/null +++ b/src/e-k/Ema/Ema.StreamHub.cs @@ -0,0 +1,76 @@ +namespace Skender.Stock.Indicators; + +// EXPONENTIAL MOVING AVERAGE (STREAM HUB) + +#region hub interface and initializer + +public interface IEma +{ + int LookbackPeriods { get; } + double K { get; } +} + +public static partial class Ema +{ + // HUB, from Chain Provider + public static EmaHub ToEma( + this IChainProvider chainProvider, + int lookbackPeriods) + where T : IReusable + => new(chainProvider, lookbackPeriods); +} +#endregion + +public class EmaHub + : ChainProvider, IEma + where TIn : IReusable +{ + #region constructors + + private readonly string hubName; + + internal EmaHub( + IChainProvider provider, + int lookbackPeriods) : base(provider) + { + Ema.Validate(lookbackPeriods); + LookbackPeriods = lookbackPeriods; + K = 2d / (lookbackPeriods + 1); + hubName = $"EMA({lookbackPeriods})"; + + Reinitialize(); + } + #endregion + + public int LookbackPeriods { get; init; } + public double K { get; init; } + + // METHODS + + public override string ToString() => hubName; + + protected override (EmaResult result, int index) + ToIndicator(TIn item, int? indexHint) + { + int i = indexHint ?? ProviderCache.GetIndex(item, true); + + double ema = i >= LookbackPeriods - 1 + ? Cache[i - 1].Ema is not null + + // normal + ? Ema.Increment(K, Cache[i - 1].Value, item.Value) + + // re/initialize as SMA + : Sma.Increment(ProviderCache, LookbackPeriods, i) + + // warmup periods are never calculable + : double.NaN; + + // candidate result + EmaResult r = new( + Timestamp: item.Timestamp, + Ema: ema.NaN2Null()); + + return (r, i); + } +} diff --git a/src/e-k/Ema/Ema.Utilities.cs b/src/e-k/Ema/Ema.Utilities.cs index 50beab90a..6a8f8f1f0 100644 --- a/src/e-k/Ema/Ema.Utilities.cs +++ b/src/e-k/Ema/Ema.Utilities.cs @@ -1,12 +1,33 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// EXPONENTIAL MOVING AVERAGE (UTILITIES) + +public static partial class Ema { + public static double Increment( + double k, + double lastEma, + double newPrice) + => lastEma + k * (newPrice - lastEma); + + public static double Increment( + int lookbackPeriods, + double lastEma, + double newPrice) + { + double k = 2d / (lookbackPeriods + 1); + return Increment(k, lastEma, newPrice); + } + + public static double? Increment( + double k, + double? lastEma, + double newPrice) + => lastEma + k * (newPrice - lastEma); + // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int n = results .ToList() @@ -14,4 +35,17 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(n + 100); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for EMA."); + } + } } diff --git a/src/e-k/Ema/info.xml b/src/e-k/Ema/info.xml index 5e3e3be73..a0b526913 100644 --- a/src/e-k/Ema/info.xml +++ b/src/e-k/Ema/info.xml @@ -1,6 +1,7 @@ + Exponential Moving Average (EMA) of price or any other specified OHLCV element. @@ -10,38 +11,10 @@ for more information. - Configurable Quote type. See Guide for more information. + Configurable Quote type. See Guide for more information. Historical price quotes. Number of periods in the lookback window. Time series of EMA values. Invalid parameter value provided. - - - Establish an observable streaming Exponential Moving Average (EMA). - - See - documentation - for more information. - - - Observable quote provider. - Number of periods in the lookback window. - Observable EMA instance. - Invalid parameter value provided. - - - - Chain from an observable streaming Exponential Moving Average (EMA). - - See - documentation - for more information. - - - Observable from chained indicator. - Number of periods in the lookback window. - Observable EMA instance. - Invalid parameter value provided. - - \ No newline at end of file + diff --git a/src/e-k/Epma/Epma.Api.cs b/src/e-k/Epma/Epma.Api.cs deleted file mode 100644 index b999b8a4f..000000000 --- a/src/e-k/Epma/Epma.Api.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ENDPOINT MOVING AVERAGE (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetEpma( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcEpma(lookbackPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetEpma( - this IEnumerable results, - int lookbackPeriods) => results - .ToTuple() - .CalcEpma(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetEpma( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods) => priceTuples - .ToSortedList() - .CalcEpma(lookbackPeriods); -} diff --git a/src/e-k/Epma/Epma.Models.cs b/src/e-k/Epma/Epma.Models.cs index eeabaabf6..afd3c6e3c 100644 --- a/src/e-k/Epma/Epma.Models.cs +++ b/src/e-k/Epma/Epma.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class EpmaResult : ResultBase, IReusableResult +public record EpmaResult +( + DateTime Timestamp, + double? Epma +) : IReusable { - public EpmaResult(DateTime date) - { - Date = date; - } - - public double? Epma { get; set; } - - double? IReusableResult.Value => Epma; + public double Value => Epma.Null2NaN(); } diff --git a/src/e-k/Epma/Epma.Series.cs b/src/e-k/Epma/Epma.Series.cs deleted file mode 100644 index 76269778c..000000000 --- a/src/e-k/Epma/Epma.Series.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ENDPOINT MOVING AVERAGE (SERIES) -public static partial class Indicator -{ - // calculate series - internal static List CalcEpma( - this List<(DateTime, double)> tpList, - int lookbackPeriods) - { - // check parameter arguments - ValidateEpma(lookbackPeriods); - - // initialize - List slopeResults = tpList - .CalcSlope(lookbackPeriods) - .ToList(); - - int length = slopeResults.Count; - List results = new(length); - - // roll through quotes - for (int i = 0; i < length; i++) - { - SlopeResult s = slopeResults[i]; - - EpmaResult r = new(s.Date) { - Epma = ((s.Slope * (i + 1)) + s.Intercept).NaN2Null() - }; - - results.Add(r); - } - - return results; - } - - // parameter validation - private static void ValidateEpma( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Epma."); - } - } -} diff --git a/src/e-k/Epma/Epma.StaticSeries.cs b/src/e-k/Epma/Epma.StaticSeries.cs new file mode 100644 index 000000000..131d601be --- /dev/null +++ b/src/e-k/Epma/Epma.StaticSeries.cs @@ -0,0 +1,33 @@ +namespace Skender.Stock.Indicators; + +// ENDPOINT MOVING AVERAGE (SERIES) + +public static partial class Epma +{ + public static IReadOnlyList ToEpma( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + IReadOnlyList slope + = source.ToSlope(lookbackPeriods); + + // roll through source values + for (int i = 0; i < length; i++) + { + results.Add(new EpmaResult( + Timestamp: slope[i].Timestamp, + Epma: ((slope[i].Slope * (i + 1)) + slope[i].Intercept).NaN2Null())); + } + + return results; + } +} diff --git a/src/e-k/Epma/Epma.Utilities.cs b/src/e-k/Epma/Epma.Utilities.cs index 76b47b966..49bfc8370 100644 --- a/src/e-k/Epma/Epma.Utilities.cs +++ b/src/e-k/Epma/Epma.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ENDPOINT MOVING AVERAGE (UTILITIES) + +public static partial class Epma { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -14,4 +15,16 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Epma."); + } + } } diff --git a/src/e-k/Fcb/Fcb.Api.cs b/src/e-k/Fcb/Fcb.Api.cs deleted file mode 100644 index ca60aa0a3..000000000 --- a/src/e-k/Fcb/Fcb.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// FRACTAL CHAOS BANDS (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetFcb( - this IEnumerable quotes, - int windowSpan = 2) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcFcb(windowSpan); -} diff --git a/src/e-k/Fcb/Fcb.Models.cs b/src/e-k/Fcb/Fcb.Models.cs index ba4c20d4f..6dead53fd 100644 --- a/src/e-k/Fcb/Fcb.Models.cs +++ b/src/e-k/Fcb/Fcb.Models.cs @@ -1,13 +1,9 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class FcbResult : ResultBase -{ - public FcbResult(DateTime date) - { - Date = date; - } - - public decimal? UpperBand { get; set; } - public decimal? LowerBand { get; set; } -} +public record FcbResult +( + DateTime Timestamp, + decimal? UpperBand, + decimal? LowerBand +) : ISeries; diff --git a/src/e-k/Fcb/Fcb.Series.cs b/src/e-k/Fcb/Fcb.Series.cs deleted file mode 100644 index 6689bc1d7..000000000 --- a/src/e-k/Fcb/Fcb.Series.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace Skender.Stock.Indicators; - -// FRACTAL CHAOS BANDS (SERIES) -public static partial class Indicator -{ - internal static List CalcFcb( - this List quotesList, - int windowSpan) - where TQuote : IQuote - { - // check parameter arguments - ValidateFcb(windowSpan); - - // initialize - List fractals = quotesList - .CalcFractal(windowSpan, windowSpan, EndType.HighLow) - .ToList(); - - int length = fractals.Count; - List results = new(length); - decimal? upperLine = null; - decimal? lowerLine = null; - - // roll through quotes - for (int i = 0; i < length; i++) - { - FractalResult f = fractals[i]; - - FcbResult r = new(f.Date); - results.Add(r); - - if (i >= 2 * windowSpan) - { - FractalResult fp = fractals[i - windowSpan]; - - upperLine = fp.FractalBear ?? upperLine; - lowerLine = fp.FractalBull ?? lowerLine; - - r.UpperBand = upperLine; - r.LowerBand = lowerLine; - } - } - - return results; - } - - // parameter validation - private static void ValidateFcb( - int windowSpan) - { - // check parameter arguments - if (windowSpan < 2) - { - throw new ArgumentOutOfRangeException(nameof(windowSpan), windowSpan, - "Window span must be at least 2 for FCB."); - } - } -} diff --git a/src/e-k/Fcb/Fcb.StaticSeries.cs b/src/e-k/Fcb/Fcb.StaticSeries.cs new file mode 100644 index 000000000..36ca6553e --- /dev/null +++ b/src/e-k/Fcb/Fcb.StaticSeries.cs @@ -0,0 +1,47 @@ +namespace Skender.Stock.Indicators; + +// FRACTAL CHAOS BANDS (SERIES) + +public static partial class Fcb +{ + public static IReadOnlyList ToFcb( + this IReadOnlyList quotes, + int windowSpan = 2) + where TQuote : IQuote + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(quotes); + Validate(windowSpan); + + // initialize + int length = quotes.Count; + List results = new(length); + + IReadOnlyList fractals = quotes + .ToFractal(windowSpan, windowSpan, EndType.HighLow); + + decimal? upperLine = null; + decimal? lowerLine = null; + + // roll through source values + for (int i = 0; i < length; i++) + { + FractalResult f = fractals[i]; + + if (i >= 2 * windowSpan) + { + FractalResult fp = fractals[i - windowSpan]; + + upperLine = fp.FractalBear ?? upperLine; + lowerLine = fp.FractalBull ?? lowerLine; + } + + results.Add(new( + Timestamp: f.Timestamp, + UpperBand: upperLine, + LowerBand: lowerLine)); + } + + return results; + } +} diff --git a/src/e-k/Fcb/Fcb.Utilities.cs b/src/e-k/Fcb/Fcb.Utilities.cs index 386420a5f..6a3dc52b4 100644 --- a/src/e-k/Fcb/Fcb.Utilities.cs +++ b/src/e-k/Fcb/Fcb.Utilities.cs @@ -1,12 +1,12 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// FRACTAL CHAOS BANDS (UTILITIES) + +public static partial class Fcb { - // CONDENSE (REMOVE null results) - /// - /// - public static IEnumerable Condense( - this IEnumerable results) + /// + public static IReadOnlyList Condense( + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -18,11 +18,9 @@ public static IEnumerable Condense( return resultsList.ToSortedList(); } - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -30,4 +28,16 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int windowSpan) + { + // check parameter arguments + if (windowSpan < 2) + { + throw new ArgumentOutOfRangeException(nameof(windowSpan), windowSpan, + "Window span must be at least 2 for FCB."); + } + } } diff --git a/src/e-k/FisherTransform/FisherTransform.Api.cs b/src/e-k/FisherTransform/FisherTransform.Api.cs deleted file mode 100644 index 34c2db32a..000000000 --- a/src/e-k/FisherTransform/FisherTransform.Api.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Skender.Stock.Indicators; - -// FISHER TRANSFORM (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetFisherTransform( - this IEnumerable quotes, - int lookbackPeriods = 10) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.HL2) - .CalcFisherTransform(lookbackPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetFisherTransform( - this IEnumerable results, - int lookbackPeriods) => results - .ToTuple() - .CalcFisherTransform(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetFisherTransform( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods) => priceTuples - .ToSortedList() - .CalcFisherTransform(lookbackPeriods); -} diff --git a/src/e-k/FisherTransform/FisherTransform.Models.cs b/src/e-k/FisherTransform/FisherTransform.Models.cs index 379d958be..a02dc5ea3 100644 --- a/src/e-k/FisherTransform/FisherTransform.Models.cs +++ b/src/e-k/FisherTransform/FisherTransform.Models.cs @@ -1,15 +1,12 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class FisherTransformResult : ResultBase, IReusableResult +public record FisherTransformResult +( + DateTime Timestamp, + double? Fisher, + double? Trigger +) : IReusable { - public FisherTransformResult(DateTime date) - { - Date = date; - } - - public double? Fisher { get; set; } - public double? Trigger { get; set; } - - double? IReusableResult.Value => Fisher; + public double Value => Fisher.Null2NaN(); } diff --git a/src/e-k/FisherTransform/FisherTransform.Series.cs b/src/e-k/FisherTransform/FisherTransform.Series.cs deleted file mode 100644 index c7862ba9d..000000000 --- a/src/e-k/FisherTransform/FisherTransform.Series.cs +++ /dev/null @@ -1,73 +0,0 @@ -namespace Skender.Stock.Indicators; - -// FISHER TRANSFORM (SERIES) -public static partial class Indicator -{ - internal static List CalcFisherTransform( - this List<(DateTime, double)> tpList, - int lookbackPeriods) - { - // check parameter arguments - ValidateFisherTransform(lookbackPeriods); - - // initialize - int length = tpList.Count; - double[] pr = new double[length]; // median price - double[] xv = new double[length]; // price transform "value" - List results = new(length); - - // roll through quotes - for (int i = 0; i < tpList.Count; i++) - { - (DateTime date, double value) = tpList[i]; - pr[i] = value; - - double minPrice = pr[i]; - double maxPrice = pr[i]; - - for (int p = Math.Max(i - lookbackPeriods + 1, 0); p <= i; p++) - { - minPrice = Math.Min(pr[p], minPrice); - maxPrice = Math.Max(pr[p], maxPrice); - } - - FisherTransformResult r = new(date); - results.Add(r); - - if (i > 0) - { - xv[i] = maxPrice != minPrice - ? (0.33 * 2 * (((pr[i] - minPrice) / (maxPrice - minPrice)) - 0.5)) - + (0.67 * xv[i - 1]) - : 0; - - xv[i] = (xv[i] > 0.99) ? 0.999 : xv[i]; - xv[i] = (xv[i] < -0.99) ? -0.999 : xv[i]; - - r.Fisher = ((0.5 * Math.Log((1 + xv[i]) / (1 - xv[i]))) - + (0.5 * results[i - 1].Fisher)).NaN2Null(); - - r.Trigger = results[i - 1].Fisher; - } - else - { - xv[i] = 0; - r.Fisher = 0; - } - } - - return results; - } - - // parameter validation - private static void ValidateFisherTransform( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Fisher Transform."); - } - } -} diff --git a/src/e-k/FisherTransform/FisherTransform.StaticSeries.cs b/src/e-k/FisherTransform/FisherTransform.StaticSeries.cs new file mode 100644 index 000000000..a9f542ba8 --- /dev/null +++ b/src/e-k/FisherTransform/FisherTransform.StaticSeries.cs @@ -0,0 +1,72 @@ +namespace Skender.Stock.Indicators; + +// FISHER TRANSFORM (SERIES) + +public static partial class FisherTransform +{ + public static IReadOnlyList ToFisherTransform( + this IReadOnlyList source, + int lookbackPeriods = 10) + where T : IReusable + { + // check parameter arguments + Validate(lookbackPeriods); + + // prefer HL2 when IQuote + IReadOnlyList values + = source.ToPreferredList(CandlePart.HL2); + + // initialize + int length = values.Count; + double[] pr = new double[length]; // median price + double[] xv = new double[length]; // price transform "value" + List results = new(length); + + // roll through source values + for (int i = 0; i < length; i++) + { + IReusable s = values[i]; + pr[i] = s.Value; + + double minPrice = pr[i]; + double maxPrice = pr[i]; + + for (int p = Math.Max(i - lookbackPeriods + 1, 0); p <= i; p++) + { + minPrice = Math.Min(pr[p], minPrice); + maxPrice = Math.Max(pr[p], maxPrice); + } + + double? fisher; + double? trigger = null; + + if (i > 0) + { + xv[i] = maxPrice - minPrice != 0 + ? (0.33 * 2 * (((pr[i] - minPrice) / (maxPrice - minPrice)) - 0.5)) + + (0.67 * xv[i - 1]) + : 0; + + xv[i] = xv[i] > 0.99 ? 0.999 : xv[i]; + xv[i] = xv[i] < -0.99 ? -0.999 : xv[i]; + + fisher = ((0.5 * Math.Log((1 + xv[i]) / (1 - xv[i]))) + + (0.5 * results[i - 1].Fisher)).NaN2Null(); + + trigger = results[i - 1].Fisher; + } + else + { + xv[i] = 0; + fisher = 0; + } + + results.Add(new( + Timestamp: s.Timestamp, + Trigger: trigger, + Fisher: fisher)); + } + + return results; + } +} diff --git a/src/e-k/FisherTransform/FisherTransform.Utilities.cs b/src/e-k/FisherTransform/FisherTransform.Utilities.cs new file mode 100644 index 000000000..8d192b5db --- /dev/null +++ b/src/e-k/FisherTransform/FisherTransform.Utilities.cs @@ -0,0 +1,18 @@ +namespace Skender.Stock.Indicators; + +// FISHER TRANSFORM (UTILITIES) + +public static partial class FisherTransform +{ + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Fisher Transform."); + } + } +} diff --git a/src/e-k/ForceIndex/ForceIndex.Api.cs b/src/e-k/ForceIndex/ForceIndex.Api.cs deleted file mode 100644 index 9d5e0c7e4..000000000 --- a/src/e-k/ForceIndex/ForceIndex.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// FORCE INDEX (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetForceIndex( - this IEnumerable quotes, - int lookbackPeriods = 2) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcForceIndex(lookbackPeriods); -} diff --git a/src/e-k/ForceIndex/ForceIndex.Models.cs b/src/e-k/ForceIndex/ForceIndex.Models.cs index 06f061c4a..0a02fcac7 100644 --- a/src/e-k/ForceIndex/ForceIndex.Models.cs +++ b/src/e-k/ForceIndex/ForceIndex.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class ForceIndexResult : ResultBase, IReusableResult +public record ForceIndexResult +( + DateTime Timestamp, + double? ForceIndex = null +) : IReusable { - public ForceIndexResult(DateTime date) - { - Date = date; - } - - public double? ForceIndex { get; set; } - - double? IReusableResult.Value => ForceIndex; + public double Value => ForceIndex.Null2NaN(); } diff --git a/src/e-k/ForceIndex/ForceIndex.Series.cs b/src/e-k/ForceIndex/ForceIndex.Series.cs deleted file mode 100644 index fa1c23515..000000000 --- a/src/e-k/ForceIndex/ForceIndex.Series.cs +++ /dev/null @@ -1,75 +0,0 @@ -namespace Skender.Stock.Indicators; - -// FORCE INDEX (SERIES) -public static partial class Indicator -{ - internal static List CalcForceIndex( - this List qdList, - int lookbackPeriods) - { - // check parameter arguments - ValidateForceIndex(lookbackPeriods); - - // initialize - int length = qdList.Count; - List results = new(length); - double? prevClose = null; - double? prevFI = null; - double? sumRawFI = 0; - double k = 2d / (lookbackPeriods + 1); - - // roll through quotes - for (int i = 0; i < length; i++) - { - QuoteD q = qdList[i]; - - ForceIndexResult r = new(q.Date); - results.Add(r); - - // skip first period - if (i == 0) - { - prevClose = q.Close; - continue; - } - - // raw Force Index - double? rawFI = q.Volume * (q.Close - prevClose); - prevClose = q.Close; - - // calculate EMA - if (i > lookbackPeriods) - { - r.ForceIndex = prevFI + (k * (rawFI - prevFI)); - } - - // initialization period - else - { - sumRawFI += rawFI; - - // first EMA value - if (i == lookbackPeriods) - { - r.ForceIndex = sumRawFI / lookbackPeriods; - } - } - - prevFI = r.ForceIndex; - } - - return results; - } - - // parameter validation - private static void ValidateForceIndex( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Force Index."); - } - } -} diff --git a/src/e-k/ForceIndex/ForceIndex.StaticSeries.cs b/src/e-k/ForceIndex/ForceIndex.StaticSeries.cs new file mode 100644 index 000000000..1bea16987 --- /dev/null +++ b/src/e-k/ForceIndex/ForceIndex.StaticSeries.cs @@ -0,0 +1,71 @@ +namespace Skender.Stock.Indicators; + +// FORCE INDEX (SERIES) + +public static partial class ForceIndex +{ + public static IReadOnlyList ToForceIndex( + this IReadOnlyList quotes, + int lookbackPeriods = 2) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcForceIndex(lookbackPeriods); + + private static List CalcForceIndex( + this IReadOnlyList source, + int lookbackPeriods) + { + // check parameter arguments + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + double? prevFi = null; + double? sumRawFi = 0; + double k = 2d / (lookbackPeriods + 1); + + // skip first period + if (length > 0) + { + results.Add(new(source[0].Timestamp)); + } + + // roll through source values + for (int i = 1; i < length; i++) + { + QuoteD q = source[i]; + double? fi = null; + + // raw Force Index + double? rawFi = q.Volume * (q.Close - source[i - 1].Close); + + // calculate EMA + if (i > lookbackPeriods) + { + fi = prevFi + (k * (rawFi - prevFi)); + } + + // initialization period + // TODO: update healing, without requiring specific indexing + else + { + sumRawFi += rawFi; + + // first EMA value + if (i == lookbackPeriods) + { + fi = sumRawFi / lookbackPeriods; + } + } + + results.Add(new ForceIndexResult( + Timestamp: q.Timestamp, + ForceIndex: fi)); + + prevFi = fi; + } + + return results; + } +} diff --git a/src/e-k/ForceIndex/ForceIndex.Utilities.cs b/src/e-k/ForceIndex/ForceIndex.Utilities.cs index b5143c529..025c244ca 100644 --- a/src/e-k/ForceIndex/ForceIndex.Utilities.cs +++ b/src/e-k/ForceIndex/ForceIndex.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// FORCE INDEX (UTILITIES) + +public static partial class ForceIndex { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int n = results .ToList() @@ -14,4 +15,16 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(n + 100); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Force Index."); + } + } } diff --git a/src/e-k/Fractal/Fractal.Api.cs b/src/e-k/Fractal/Fractal.Api.cs deleted file mode 100644 index 169e06146..000000000 --- a/src/e-k/Fractal/Fractal.Api.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Skender.Stock.Indicators; - -// WILLIAMS FRACTAL (API) -public static partial class Indicator -{ - /// - /// - public static IEnumerable GetFractal( - this IEnumerable quotes, - int windowSpan = 2, - EndType endType = EndType.HighLow) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcFractal(windowSpan, windowSpan, endType); - - // more configurable version (undocumented) - /// - /// - public static IEnumerable GetFractal( - this IEnumerable quotes, - int leftSpan, - int rightSpan, - EndType endType = EndType.HighLow) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcFractal(leftSpan, rightSpan, endType); -} diff --git a/src/e-k/Fractal/Fractal.Models.cs b/src/e-k/Fractal/Fractal.Models.cs index 406feb5e6..23fb6e239 100644 --- a/src/e-k/Fractal/Fractal.Models.cs +++ b/src/e-k/Fractal/Fractal.Models.cs @@ -1,13 +1,9 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class FractalResult : ResultBase -{ - public FractalResult(DateTime date) - { - Date = date; - } - - public decimal? FractalBear { get; set; } - public decimal? FractalBull { get; set; } -} +public record FractalResult +( + DateTime Timestamp, + decimal? FractalBear, + decimal? FractalBull +) : ISeries; diff --git a/src/e-k/Fractal/Fractal.Series.cs b/src/e-k/Fractal/Fractal.Series.cs deleted file mode 100644 index 66abf1e3c..000000000 --- a/src/e-k/Fractal/Fractal.Series.cs +++ /dev/null @@ -1,95 +0,0 @@ -namespace Skender.Stock.Indicators; - -// WILLIAMS FRACTAL (SERIES) -public static partial class Indicator -{ - internal static List CalcFractal( - this List quotesList, - int leftSpan, - int rightSpan, - EndType endType) - where TQuote : IQuote - { - // check parameter arguments - ValidateFractal(Math.Min(leftSpan, rightSpan)); - - // initialize - List results = new(quotesList.Count); - - // roll through quotes - for (int i = 0; i < quotesList.Count; i++) - { - TQuote q = quotesList[i]; - - FractalResult r = new(q.Date); - results.Add(r); - - if (i + 1 > leftSpan && i + 1 <= quotesList.Count - rightSpan) - { - bool isHigh = true; - bool isLow = true; - - decimal evalHigh = (endType == EndType.Close) ? - q.Close : q.High; - - decimal evalLow = (endType == EndType.Close) ? - q.Close : q.Low; - - // compare today with wings - for (int p = i - leftSpan; p <= i + rightSpan; p++) - { - // skip center eval period - if (p == i) - { - continue; - } - - // evaluate wing periods - TQuote wing = quotesList[p]; - - decimal wingHigh = (endType == EndType.Close) ? - wing.Close : wing.High; - - decimal wingLow = (endType == EndType.Close) ? - wing.Close : wing.Low; - - if (evalHigh <= wingHigh) - { - isHigh = false; - } - - if (evalLow >= wingLow) - { - isLow = false; - } - } - - // bearish signal - if (isHigh) - { - r.FractalBear = evalHigh; - } - - // bullish signal - if (isLow) - { - r.FractalBull = evalLow; - } - } - } - - return results; - } - - // parameter validation - private static void ValidateFractal( - int windowSpan) - { - // check parameter arguments - if (windowSpan < 2) - { - throw new ArgumentOutOfRangeException(nameof(windowSpan), windowSpan, - "Window span must be at least 2 for Fractal."); - } - } -} diff --git a/src/e-k/Fractal/Fractal.StaticSeries.cs b/src/e-k/Fractal/Fractal.StaticSeries.cs new file mode 100644 index 000000000..2dd3b1615 --- /dev/null +++ b/src/e-k/Fractal/Fractal.StaticSeries.cs @@ -0,0 +1,97 @@ +namespace Skender.Stock.Indicators; + +// WILLIAMS FRACTAL (SERIES) + +public static partial class Fractal +{ + public static IReadOnlyList ToFractal( + this IReadOnlyList quotes, + int windowSpan = 2, + EndType endType = EndType.HighLow) + where TQuote : IQuote => quotes + .ToFractal(windowSpan, windowSpan, endType); + + public static IReadOnlyList ToFractal( + this IReadOnlyList quotes, + int leftSpan, + int rightSpan, + EndType endType = EndType.HighLow) + where TQuote : IQuote + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(quotes); + Validate(Math.Min(leftSpan, rightSpan)); + + // initialize + int length = quotes.Count; + List results = new(length); + + // roll through source values + for (int i = 0; i < length; i++) + { + TQuote q = quotes[i]; + decimal? fractalBear = null; + decimal? fractalBull = null; + + if (i + 1 > leftSpan && i + 1 <= length - rightSpan) + { + bool isHigh = true; + bool isLow = true; + + decimal evalHigh = endType == EndType.Close ? + q.Close : q.High; + + decimal evalLow = endType == EndType.Close ? + q.Close : q.Low; + + // compare today with wings + for (int p = i - leftSpan; p <= i + rightSpan; p++) + { + // skip center eval period + if (p == i) + { + continue; + } + + // evaluate wing periods + TQuote wing = quotes[p]; + + decimal wingHigh = endType == EndType.Close ? + wing.Close : wing.High; + + decimal wingLow = endType == EndType.Close ? + wing.Close : wing.Low; + + if (evalHigh <= wingHigh) + { + isHigh = false; + } + + if (evalLow >= wingLow) + { + isLow = false; + } + } + + // bearish signal + if (isHigh) + { + fractalBear = evalHigh; + } + + // bullish signal + if (isLow) + { + fractalBull = evalLow; + } + } + + results.Add(new( + Timestamp: q.Timestamp, + FractalBear: fractalBear, + FractalBull: fractalBull)); + } + + return results; + } +} diff --git a/src/e-k/Fractal/Fractal.Utilities.cs b/src/e-k/Fractal/Fractal.Utilities.cs index b7146d215..9e36b3105 100644 --- a/src/e-k/Fractal/Fractal.Utilities.cs +++ b/src/e-k/Fractal/Fractal.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// WILLIAMS FRACTAL (UTILITIES) + +public static partial class Fractal { // CONDENSE (REMOVE null results) - /// - /// - public static IEnumerable Condense( - this IEnumerable results) + /// + public static IReadOnlyList Condense( + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -17,4 +18,16 @@ public static IEnumerable Condense( return resultsList.ToSortedList(); } + + // parameter validation + internal static void Validate( + int windowSpan) + { + // check parameter arguments + if (windowSpan < 2) + { + throw new ArgumentOutOfRangeException(nameof(windowSpan), windowSpan, + "Window span must be at least 2 for Fractal."); + } + } } diff --git a/src/e-k/Gator/Gator.Api.cs b/src/e-k/Gator/Gator.Api.cs deleted file mode 100644 index a45991cdc..000000000 --- a/src/e-k/Gator/Gator.Api.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace Skender.Stock.Indicators; - -// GATOR OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetGator( - this IEnumerable quotes) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.HL2) - .GetAlligator() - .ToList() - .CalcGator(); - - // SERIES, from [custom] Alligator - public static IEnumerable GetGator( - this IEnumerable alligator) => alligator - .ToList() - .CalcGator(); - - // SERIES, from CHAIN - public static IEnumerable GetGator( - this IEnumerable results) => results - .ToTuple() - .GetAlligator() - .ToList() - .CalcGator() - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetGator( - this IEnumerable<(DateTime, double)> priceTuples) => priceTuples - .ToSortedList() - .GetAlligator() - .ToList() - .CalcGator(); -} diff --git a/src/e-k/Gator/Gator.Models.cs b/src/e-k/Gator/Gator.Models.cs index 3cf27e8eb..8e7311cf3 100644 --- a/src/e-k/Gator/Gator.Models.cs +++ b/src/e-k/Gator/Gator.Models.cs @@ -1,16 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public class GatorResult : ResultBase -{ - public GatorResult(DateTime date) - { - Date = date; - } - - public double? Upper { get; set; } - public double? Lower { get; set; } - - public bool? UpperIsExpanding { get; set; } - public bool? LowerIsExpanding { get; set; } -} +public record GatorResult +( + DateTime Timestamp, + double? Upper = null, + double? Lower = null, + bool? UpperIsExpanding = null, + bool? LowerIsExpanding = null +) : ISeries; diff --git a/src/e-k/Gator/Gator.Series.cs b/src/e-k/Gator/Gator.Series.cs deleted file mode 100644 index 2c308f322..000000000 --- a/src/e-k/Gator/Gator.Series.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Skender.Stock.Indicators; - -// GATOR OSCILLATOR (SERIES) -public static partial class Indicator -{ - internal static List CalcGator( - this List alligator) - { - List results = alligator - .Select(x => new GatorResult(x.Date) { - Upper = NullMath.Abs(x.Jaw - x.Teeth), - Lower = -NullMath.Abs(x.Teeth - x.Lips) - }) - .ToList(); - - // roll through quotes - for (int i = 1; i < results.Count; i++) - { - GatorResult r = results[i]; - GatorResult p = results[i - 1]; - - // directional information - r.UpperIsExpanding = p.Upper is not null ? (r.Upper > p.Upper) : null; - r.LowerIsExpanding = p.Lower is not null ? (r.Lower < p.Lower) : null; - } - - return results; - } -} diff --git a/src/e-k/Gator/Gator.StaticSeries.cs b/src/e-k/Gator/Gator.StaticSeries.cs new file mode 100644 index 000000000..66fe5353b --- /dev/null +++ b/src/e-k/Gator/Gator.StaticSeries.cs @@ -0,0 +1,68 @@ +namespace Skender.Stock.Indicators; + +// GATOR OSCILLATOR (SERIES) + +public static partial class Gator +{ + /// + /// Gator Oscillator is an expanded view of Williams Alligator. + /// + /// + /// + /// + /// T must be or type + /// + /// Time-series values to transform. + /// Time series of Gator values. + public static IReadOnlyList ToGator( + this IReadOnlyList source) + where T : IReusable + => source + .ToAlligator() + .ToGator(); + + // from [custom] Alligator + public static IReadOnlyList ToGator( + this IReadOnlyList alligator) + { + ArgumentNullException.ThrowIfNull(alligator); + + // initialize + int length = alligator.Count; + List results = []; + + if (length > 0) + { + results.Add(new(alligator[0].Timestamp)); + } + + // roll through source values + for (int i = 1; i < length; i++) + { + AlligatorResult a = alligator[i]; + GatorResult p = results[i - 1]; + + double? upper = (a.Jaw - a.Teeth).Abs(); + double? lower = -(a.Teeth - a.Lips).Abs(); + + results.Add(new GatorResult( + + Timestamp: a.Timestamp, + + // gator + Upper: upper, + Lower: lower, + + // directional information + UpperIsExpanding: p.Upper is not null + ? upper > p.Upper + : null, + + LowerIsExpanding: p.Lower is not null + ? lower < p.Lower + : null)); + } + + return results; + } +} diff --git a/src/e-k/Gator/Gator.Utilities.cs b/src/e-k/Gator/Gator.Utilities.cs index a9ea87295..a9b97545a 100644 --- a/src/e-k/Gator/Gator.Utilities.cs +++ b/src/e-k/Gator/Gator.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// GATOR OSCILLATOR (UTILITIES) + +public static partial class Gator { // CONDENSE (REMOVE null results) - /// - /// - public static IEnumerable Condense( - this IEnumerable results) + /// + public static IReadOnlyList Condense( + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -19,8 +20,7 @@ public static IEnumerable Condense( } // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) => results.Remove(150); + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) => results.Remove(150); } diff --git a/src/e-k/Gator/info.xml b/src/e-k/Gator/info.xml deleted file mode 100644 index 19f3416d9..000000000 --- a/src/e-k/Gator/info.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - Gator Oscillator is an expanded view of Williams Alligator. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Time series of Gator values. - \ No newline at end of file diff --git a/src/e-k/HeikinAshi/HeikinAshi.Api.cs b/src/e-k/HeikinAshi/HeikinAshi.Api.cs deleted file mode 100644 index 8a0efd725..000000000 --- a/src/e-k/HeikinAshi/HeikinAshi.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// HEIKIN-ASHI (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetHeikinAshi( - this IEnumerable quotes) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcHeikinAshi(); -} diff --git a/src/e-k/HeikinAshi/HeikinAshi.Models.cs b/src/e-k/HeikinAshi/HeikinAshi.Models.cs index 4cb917ac5..13e1d047a 100644 --- a/src/e-k/HeikinAshi/HeikinAshi.Models.cs +++ b/src/e-k/HeikinAshi/HeikinAshi.Models.cs @@ -1,16 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class HeikinAshiResult : ResultBase, IQuote -{ - public HeikinAshiResult(DateTime date) - { - Date = date; - } - - public decimal Open { get; set; } - public decimal High { get; set; } - public decimal Low { get; set; } - public decimal Close { get; set; } - public decimal Volume { get; set; } -} +public record HeikinAshiResult( + DateTime Timestamp, + decimal Open, + decimal High, + decimal Low, + decimal Close, + decimal Volume +) : Quote(Timestamp, Open, High, Low, Close, Volume); diff --git a/src/e-k/HeikinAshi/HeikinAshi.Series.cs b/src/e-k/HeikinAshi/HeikinAshi.StaticSeries.cs similarity index 62% rename from src/e-k/HeikinAshi/HeikinAshi.Series.cs rename to src/e-k/HeikinAshi/HeikinAshi.StaticSeries.cs index ecc83d4eb..946cb94eb 100644 --- a/src/e-k/HeikinAshi/HeikinAshi.Series.cs +++ b/src/e-k/HeikinAshi/HeikinAshi.StaticSeries.cs @@ -1,14 +1,17 @@ namespace Skender.Stock.Indicators; // HEIKIN-ASHI (SERIES) -public static partial class Indicator + +public static class HeikinAshi { - internal static List CalcHeikinAshi( - this List quotesList) + public static IReadOnlyList ToHeikinAshi( + this IReadOnlyList quotes) where TQuote : IQuote { + ArgumentNullException.ThrowIfNull(quotes); + // initialize - int length = quotesList.Count; + int length = quotes.Count; List results = new(length); decimal prevOpen = decimal.MinValue; @@ -16,15 +19,15 @@ internal static List CalcHeikinAshi( if (length > 0) { - TQuote q = quotesList[0]; + TQuote q = quotes[0]; prevOpen = q.Open; prevClose = q.Close; } - // roll through quotes + // roll through source values for (int i = 0; i < length; i++) { - TQuote q = quotesList[i]; + TQuote q = quotes[i]; // close decimal close = (q.Open + q.High + q.Low + q.Close) / 4; @@ -40,14 +43,13 @@ internal static List CalcHeikinAshi( decimal[] arrL = [q.Low, open, close]; decimal low = arrL.Min(); - HeikinAshiResult r = new(q.Date) { - Open = open, - High = high, - Low = low, - Close = close, - Volume = q.Volume - }; - results.Add(r); + results.Add(new HeikinAshiResult( + Timestamp: q.Timestamp, + Open: open, + High: high, + Low: low, + Close: close, + Volume: q.Volume)); // save for next iteration prevOpen = open; diff --git a/src/e-k/HeikinAshi/HeikinAshi.Utilities.cs b/src/e-k/HeikinAshi/HeikinAshi.Utilities.cs deleted file mode 100644 index 096e44982..000000000 --- a/src/e-k/HeikinAshi/HeikinAshi.Utilities.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -public static partial class Indicator -{ - // convert results to quotes - public static IEnumerable ToQuotes( - this IEnumerable results) - => results - .Select(x => new Quote { - Date = x.Date, - Open = x.Open, - High = x.High, - Low = x.Low, - Close = x.Close, - Volume = x.Volume - }) - .OrderBy(x => x.Date) - .ToList(); -} diff --git a/src/e-k/Hma/Hma.Api.cs b/src/e-k/Hma/Hma.Api.cs deleted file mode 100644 index c60107ee4..000000000 --- a/src/e-k/Hma/Hma.Api.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Skender.Stock.Indicators; - -// HULL MOVING AVERAGE (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetHma( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcHma(lookbackPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetHma( - this IEnumerable results, - int lookbackPeriods) => results - .ToTuple() - .CalcHma(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetHma( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods) => priceTuples - .ToSortedList() - .CalcHma(lookbackPeriods); -} diff --git a/src/e-k/Hma/Hma.Models.cs b/src/e-k/Hma/Hma.Models.cs index f63a82743..0bc754fa6 100644 --- a/src/e-k/Hma/Hma.Models.cs +++ b/src/e-k/Hma/Hma.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class HmaResult : ResultBase, IReusableResult +public record HmaResult +( + DateTime Timestamp, + double? Hma = null +) : IReusable { - public HmaResult(DateTime date) - { - Date = date; - } - - public double? Hma { get; set; } - - double? IReusableResult.Value => Hma; + public double Value => Hma.Null2NaN(); } diff --git a/src/e-k/Hma/Hma.Series.cs b/src/e-k/Hma/Hma.Series.cs deleted file mode 100644 index a895f64e3..000000000 --- a/src/e-k/Hma/Hma.Series.cs +++ /dev/null @@ -1,70 +0,0 @@ -namespace Skender.Stock.Indicators; - -// HULL MOVING AVERAGE (SERIES) -public static partial class Indicator -{ - // calculate series - internal static List CalcHma( - this List<(DateTime, double)> tpList, - int lookbackPeriods) - { - // check parameter arguments - ValidateHma(lookbackPeriods); - - // initialize - int shiftQty = lookbackPeriods - 1; - List<(DateTime, double)> synthHistory = []; - - List wmaN1 = tpList.GetWma(lookbackPeriods).ToList(); - List wmaN2 = tpList.GetWma(lookbackPeriods / 2).ToList(); - - // roll through quotes, to get interim synthetic quotes - for (int i = 0; i < tpList.Count; i++) - { - (DateTime date, double _) = tpList[i]; - - WmaResult w1 = wmaN1[i]; - WmaResult w2 = wmaN2[i]; - - if (i >= shiftQty) - { - (DateTime, double) sh - = new(date, (w2.Wma.Null2NaN() * 2d) - w1.Wma.Null2NaN()); - - synthHistory.Add(sh); - } - } - - // add back truncated null results - int sqN = (int)Math.Sqrt(lookbackPeriods); - - List results = tpList - .Take(shiftQty) - .Select(x => new HmaResult(x.Item1)) - .ToList(); - - // calculate final HMA = WMA with period SQRT(n) - List hmaResults = synthHistory.CalcWma(sqN) - .Select(x => new HmaResult(x.Date) { - Hma = x.Wma - }) - .ToList(); - - // add WMA to results - results.AddRange(hmaResults); - - return results.ToSortedList(); - } - - // parameter validation - private static void ValidateHma( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for HMA."); - } - } -} diff --git a/src/e-k/Hma/Hma.StaticSeries.cs b/src/e-k/Hma/Hma.StaticSeries.cs new file mode 100644 index 000000000..a4c02ba66 --- /dev/null +++ b/src/e-k/Hma/Hma.StaticSeries.cs @@ -0,0 +1,68 @@ +namespace Skender.Stock.Indicators; + +// HULL MOVING AVERAGE (SERIES) + +public static partial class Hma +{ + public static IReadOnlyList ToHma( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + int shiftQty = lookbackPeriods - 1; + List synthHistory = []; + + IReadOnlyList wmaN1 + = source.ToWma(lookbackPeriods); + + IReadOnlyList wmaN2 + = source.ToWma(lookbackPeriods / 2); + + // roll through source values, to get interim synthetic quotes + for (int i = 0; i < length; i++) + { + T s = source[i]; + + WmaResult w1 = wmaN1[i]; + WmaResult w2 = wmaN2[i]; + + if (i < shiftQty) + { + continue; + } + + QuotePart sh = new( + s.Timestamp, + (w2.Wma.Null2NaN() * 2d) - w1.Wma.Null2NaN()); + + synthHistory.Add(sh); + } + + // add back truncated null results + int sqN = (int)Math.Sqrt(lookbackPeriods); + + List results = source + .Take(shiftQty) + .Select(x => new HmaResult(x.Timestamp)) + .ToList(); + + // calculate final HMA = WMA with period SQRT(n) + List hmaResults = synthHistory.ToWma(sqN) + .Select(x => new HmaResult( + Timestamp: x.Timestamp, + Hma: x.Wma + )) + .ToList(); + + // add WMA to results + results.AddRange(hmaResults); + + return results.ToSortedList(); + } +} diff --git a/src/e-k/Hma/Hma.Utilities.cs b/src/e-k/Hma/Hma.Utilities.cs index fc5e0be5b..2cb778625 100644 --- a/src/e-k/Hma/Hma.Utilities.cs +++ b/src/e-k/Hma/Hma.Utilities.cs @@ -1,17 +1,16 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +public static partial class Hma { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Hma != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for HMA."); + } } } diff --git a/src/e-k/HtTrendline/HtTrendline.Api.cs b/src/e-k/HtTrendline/HtTrendline.Api.cs deleted file mode 100644 index 2ca410491..000000000 --- a/src/e-k/HtTrendline/HtTrendline.Api.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Skender.Stock.Indicators; - -// HILBERT TRANSFORM - INSTANTANEOUS TRENDLINE (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetHtTrendline( - this IEnumerable quotes) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.HL2) - .CalcHtTrendline(); - - // SERIES, from CHAIN - public static IEnumerable GetHtTrendline( - this IEnumerable results) => results - .ToTuple() - .CalcHtTrendline() - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetHtTrendline( - this IEnumerable<(DateTime, double)> priceTuples) => priceTuples - .ToSortedList() - .CalcHtTrendline(); -} diff --git a/src/e-k/HtTrendline/HtTrendline.Models.cs b/src/e-k/HtTrendline/HtTrendline.Models.cs index a4e9913c1..0c73dd30d 100644 --- a/src/e-k/HtTrendline/HtTrendline.Models.cs +++ b/src/e-k/HtTrendline/HtTrendline.Models.cs @@ -1,16 +1,13 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class HtlResult : ResultBase, IReusableResult +public record HtlResult +( + DateTime Timestamp, + int? DcPeriods, + double? Trendline, + double? SmoothPrice +) : IReusable { - public HtlResult(DateTime date) - { - Date = date; - } - - public int? DcPeriods { get; set; } - public double? Trendline { get; set; } - public double? SmoothPrice { get; set; } - - double? IReusableResult.Value => Trendline; + public double Value => Trendline.Null2NaN(); } diff --git a/src/e-k/HtTrendline/HtTrendline.Series.cs b/src/e-k/HtTrendline/HtTrendline.StaticSeries.cs similarity index 63% rename from src/e-k/HtTrendline/HtTrendline.Series.cs rename to src/e-k/HtTrendline/HtTrendline.StaticSeries.cs index fc1c4e4e6..0788b5181 100644 --- a/src/e-k/HtTrendline/HtTrendline.Series.cs +++ b/src/e-k/HtTrendline/HtTrendline.StaticSeries.cs @@ -1,13 +1,29 @@ namespace Skender.Stock.Indicators; // HILBERT TRANSFORM - INSTANTANEOUS TRENDLINE (SERIES) -public static partial class Indicator + +public static partial class HtTrendline { - internal static List CalcHtTrendline( - this List<(DateTime, double)> tpList) + // SERIES, from CHAIN + /// + /// Hilbert Transform Instantaneous Trendline(HTL) is a 5-period trendline + /// of high/low price that uses signal processing to reduce noise. + /// + /// + /// T must be type + /// + /// Time-series values to transform. + /// Time series of HTL values and smoothed price. + public static IReadOnlyList ToHtTrendline( + this IReadOnlyList source) + where T : IReusable { + // prefer HL2 when IQuote + IReadOnlyList values + = source.ToPreferredList(CandlePart.HL2); + // initialize - int length = tpList.Count; + int length = values.Count; List results = new(length); double[] pr = new double[length]; // price @@ -18,9 +34,6 @@ internal static List CalcHtTrendline( 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 @@ -30,14 +43,11 @@ internal static List CalcHtTrendline( double[] sd = new double[length]; // smooth period double[] it = new double[length]; // instantaneous trend (raw) - // roll through quotes + // roll through source values for (int i = 0; i < length; i++) { - (DateTime date, double value) = tpList[i]; - pr[i] = value; - - HtlResult r = new(date); - results.Add(r); + IReusable s = values[i]; + pr[i] = s.Value; if (i > 5) { @@ -52,8 +62,8 @@ internal static List CalcHtTrendline( 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; + double jI = ((0.0962 * i1[i]) + (0.5769 * i1[i - 2]) - (0.5769 * i1[i - 4]) - (0.0962 * i1[i - 6])) * adj; + double 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; @@ -75,10 +85,10 @@ internal static List CalcHtTrendline( : 0d; // 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]; + 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]); @@ -103,21 +113,27 @@ internal static List CalcHtTrendline( it[i] = dcPeriods > 0 ? sumPr / dcPeriods : pr[i]; - r.DcPeriods = dcPeriods > 0 ? dcPeriods : null; - // final indicators - r.Trendline = i >= 11 // 12th bar - ? (((4 * it[i]) + (3 * it[i - 1]) + (2 * it[i - 2]) + it[i - 3]) / 10d).NaN2Null() - : pr[i].NaN2Null(); + results.Add(new( + + Timestamp: s.Timestamp, + DcPeriods: dcPeriods > 0 ? dcPeriods : null, + + Trendline: i >= 11 // 12th bar + ? (((4 * it[i]) + (3 * it[i - 1]) + (2 * it[i - 2]) + it[i - 3]) / 10d).NaN2Null() + : pr[i].NaN2Null(), - r.SmoothPrice = (((4 * pr[i]) + (3 * pr[i - 1]) + (2 * pr[i - 2]) + pr[i - 3]) / 10d).NaN2Null(); + SmoothPrice: (((4 * pr[i]) + (3 * pr[i - 1]) + (2 * pr[i - 2]) + pr[i - 3]) / 10d).NaN2Null())); } // initialization period else { - r.Trendline = pr[i].NaN2Null(); - r.SmoothPrice = null; + results.Add(new( + Timestamp: s.Timestamp, + DcPeriods: null, + Trendline: pr[i].NaN2Null(), + SmoothPrice: null)); pd[i] = 0; sp[i] = 0; diff --git a/src/e-k/HtTrendline/HtTrendline.Utilities.cs b/src/e-k/HtTrendline/HtTrendline.Utilities.cs index 698505a1b..6ca015ee2 100644 --- a/src/e-k/HtTrendline/HtTrendline.Utilities.cs +++ b/src/e-k/HtTrendline/HtTrendline.Utilities.cs @@ -1,10 +1,12 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// HILBERT TRANSFORM - INSTANTANEOUS TRENDLINE (UTILITIES) + +public static partial class HtTrendline { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) => results.Remove(100); + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) + => results.Remove(100); } diff --git a/src/e-k/HtTrendline/info.xml b/src/e-k/HtTrendline/info.xml deleted file mode 100644 index 465a32f5a..000000000 --- a/src/e-k/HtTrendline/info.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - Hilbert Transform Instantaneous Trendline (HTL) is a 5-period trendline of high/low price that uses signal processing to reduce noise. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Time series of HTL values and smoothed price. - \ No newline at end of file diff --git a/src/e-k/Hurst/Hurst.Api.cs b/src/e-k/Hurst/Hurst.Api.cs deleted file mode 100644 index a8b33a1b6..000000000 --- a/src/e-k/Hurst/Hurst.Api.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Skender.Stock.Indicators; - -// HURST EXPONENT (API) -public static partial class Indicator -{ - /// - /// - public static IEnumerable GetHurst( - this IEnumerable quotes, - int lookbackPeriods = 100) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcHurst(lookbackPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetHurst( - this IEnumerable results, - int lookbackPeriods) => results - .ToTuple() - .CalcHurst(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetHurst( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods) => priceTuples - .ToSortedList() - .CalcHurst(lookbackPeriods); -} diff --git a/src/e-k/Hurst/Hurst.Models.cs b/src/e-k/Hurst/Hurst.Models.cs index 3c6b82b15..6b283a542 100644 --- a/src/e-k/Hurst/Hurst.Models.cs +++ b/src/e-k/Hurst/Hurst.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class HurstResult : ResultBase, IReusableResult +public record HurstResult +( + DateTime Timestamp, + double? HurstExponent +) : IReusable { - public HurstResult(DateTime date) - { - Date = date; - } - - public double? HurstExponent { get; set; } - - double? IReusableResult.Value => HurstExponent; + public double Value => HurstExponent.Null2NaN(); } diff --git a/src/e-k/Hurst/Hurst.Series.cs b/src/e-k/Hurst/Hurst.StaticSeries.cs similarity index 71% rename from src/e-k/Hurst/Hurst.Series.cs rename to src/e-k/Hurst/Hurst.StaticSeries.cs index d7685bb5d..3b2ff650d 100644 --- a/src/e-k/Hurst/Hurst.Series.cs +++ b/src/e-k/Hurst/Hurst.StaticSeries.cs @@ -1,26 +1,27 @@ namespace Skender.Stock.Indicators; // HURST EXPONENT (SERIES) -public static partial class Indicator + +public static partial class Hurst { - internal static List CalcHurst( - this List<(DateTime, double)> tpList, - int lookbackPeriods) + public static IReadOnlyList ToHurst( + this IReadOnlyList source, + int lookbackPeriods = 100) + where T : IReusable { // check parameter arguments - ValidateHurst(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize - int length = tpList.Count; + int length = source.Count; List results = new(length); - // roll through quotes + // roll through source values for (int i = 0; i < length; i++) { - (DateTime date, double _) = tpList[i]; - - HurstResult r = new(date); - results.Add(r); + T s = source[i]; + double? h = null; if (i + 1 > lookbackPeriods) { @@ -28,22 +29,26 @@ internal static List CalcHurst( double[] values = new double[lookbackPeriods]; int x = 0; - double l = tpList[i - lookbackPeriods].Item2; + double l = source[i - lookbackPeriods].Value; for (int p = i + 1 - lookbackPeriods; p <= i; p++) { - (DateTime _, double c) = tpList[p]; + T ps = source[p]; // return values - values[x] = l != 0 ? (c / l) - 1 : double.NaN; + values[x] = l != 0 ? ps.Value / l - 1 : double.NaN; - l = c; + l = ps.Value; x++; } // calculate hurst exponent - r.HurstExponent = CalcHurstWindow(values).NaN2Null(); + h = CalcHurstWindow(values).NaN2Null(); } + + results.Add(new( + Timestamp: s.Timestamp, + HurstExponent: h)); } return results; @@ -85,7 +90,7 @@ private static double CalcHurstWindow(double[] values) // starting index position used to skip // observations to enforce same-sized chunks - int startIndex = totalSize - (chunkSize * chunkQty); + int startIndex = totalSize - chunkSize * chunkQty; // analyze chunks in set for (int chunkNum = 1; chunkNum <= chunkQty; chunkNum++) @@ -108,8 +113,8 @@ private static double CalcHurstWindow(double[] values) { double y = values[i] - chunkMean; sumY += y; - minY = (sumY < minY) ? sumY : minY; - maxY = (sumY > maxY) ? sumY : maxY; + minY = sumY < minY ? sumY : minY; + maxY = sumY > maxY ? sumY : maxY; sumSq += y * y; } @@ -135,18 +140,6 @@ private static double CalcHurstWindow(double[] values) // hurst exponent // TODO: apply Anis-Lloyd corrected R/S Hurst? - return Numerix.Slope(logSize, logRs); - } - - // parameter validation - private static void ValidateHurst( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods < 20) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be at least 20 for Hurst Exponent."); - } + return Numerical.Slope(logSize, logRs); } } diff --git a/src/e-k/Hurst/Hurst.Utilities.cs b/src/e-k/Hurst/Hurst.Utilities.cs index 53f79595e..92fc16415 100644 --- a/src/e-k/Hurst/Hurst.Utilities.cs +++ b/src/e-k/Hurst/Hurst.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// HURST EXPONENT (UTILITIES) + +public static partial class Hurst { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -14,4 +15,16 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods < 20) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be at least 20 for Hurst Exponent."); + } + } } diff --git a/src/e-k/Ichimoku/Ichimoku.Api.cs b/src/e-k/Ichimoku/Ichimoku.Api.cs deleted file mode 100644 index 34184d4ed..000000000 --- a/src/e-k/Ichimoku/Ichimoku.Api.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ICHIMOKU CLOUD (API) -public static partial class Indicator -{ - /// - /// - public static IEnumerable GetIchimoku( - this IEnumerable quotes, - int tenkanPeriods = 9, - int kijunPeriods = 26, - int senkouBPeriods = 52) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcIchimoku( - tenkanPeriods, - kijunPeriods, - senkouBPeriods, - kijunPeriods, - kijunPeriods); - - /// - /// - public static IEnumerable GetIchimoku( - this IEnumerable quotes, - int tenkanPeriods, - int kijunPeriods, - int senkouBPeriods, - int offsetPeriods) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcIchimoku( - tenkanPeriods, - kijunPeriods, - senkouBPeriods, - offsetPeriods, - offsetPeriods); - - /// - /// - public static IEnumerable GetIchimoku( - this IEnumerable quotes, - int tenkanPeriods, - int kijunPeriods, - int senkouBPeriods, - int senkouOffset, - int chikouOffset) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcIchimoku( - tenkanPeriods, - kijunPeriods, - senkouBPeriods, - senkouOffset, - chikouOffset); -} diff --git a/src/e-k/Ichimoku/Ichimoku.Models.cs b/src/e-k/Ichimoku/Ichimoku.Models.cs index 5fb1692c3..512c5690d 100644 --- a/src/e-k/Ichimoku/Ichimoku.Models.cs +++ b/src/e-k/Ichimoku/Ichimoku.Models.cs @@ -1,16 +1,12 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class IchimokuResult : ResultBase -{ - public IchimokuResult(DateTime date) - { - Date = date; - } - - public decimal? TenkanSen { get; set; } // conversion line - public decimal? KijunSen { get; set; } // base line - public decimal? SenkouSpanA { get; set; } // leading span A - public decimal? SenkouSpanB { get; set; } // leading span B - public decimal? ChikouSpan { get; set; } // lagging span -} +public record IchimokuResult +( + DateTime Timestamp, + decimal? TenkanSen, // conversion line + decimal? KijunSen, // base line + decimal? SenkouSpanA, // leading span A + decimal? SenkouSpanB, // leading span B + decimal? ChikouSpan // lagging span +) : ISeries; diff --git a/src/e-k/Ichimoku/Ichimoku.Series.cs b/src/e-k/Ichimoku/Ichimoku.Series.cs deleted file mode 100644 index ab276f947..000000000 --- a/src/e-k/Ichimoku/Ichimoku.Series.cs +++ /dev/null @@ -1,192 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ICHIMOKU CLOUD (SERIES) -public static partial class Indicator -{ - internal static List CalcIchimoku( - this List quotesList, - int tenkanPeriods, - int kijunPeriods, - int senkouBPeriods, - int senkouOffset, - int chikouOffset) - where TQuote : IQuote - { - // check parameter arguments - ValidateIchimoku( - tenkanPeriods, - kijunPeriods, - senkouBPeriods, - senkouOffset, - chikouOffset); - - // initialize - int length = quotesList.Count; - List results = new(length); - int senkouStartPeriod = Math.Max( - 2 * senkouOffset, - Math.Max(tenkanPeriods, kijunPeriods)) - 1; - - // roll through quotes - for (int i = 0; i < length; i++) - { - TQuote q = quotesList[i]; - - IchimokuResult r = new(q.Date); - results.Add(r); - - // tenkan-sen conversion line - CalcIchimokuTenkanSen(i, quotesList, r, tenkanPeriods); - - // kijun-sen base line - CalcIchimokuKijunSen(i, quotesList, r, kijunPeriods); - - // senkou span A - if (i >= senkouStartPeriod) - { - IchimokuResult skq = results[i - senkouOffset]; - - if (skq != null && skq.TenkanSen != null && skq.KijunSen != null) - { - r.SenkouSpanA = (skq.TenkanSen + skq.KijunSen) / 2; - } - } - - // senkou span B - CalcIchimokuSenkouB(i, quotesList, r, senkouOffset, senkouBPeriods); - - // chikou line - if (i + chikouOffset < quotesList.Count) - { - r.ChikouSpan = quotesList[i + chikouOffset].Close; - } - } - - return results; - } - - private static void CalcIchimokuTenkanSen( - int i, List quotesList, IchimokuResult result, int tenkanPeriods) - where TQuote : IQuote - { - if (i >= tenkanPeriods - 1) - { - decimal max = 0; - decimal min = decimal.MaxValue; - - for (int p = i - tenkanPeriods + 1; p <= i; p++) - { - TQuote d = quotesList[p]; - - if (d.High > max) - { - max = d.High; - } - - if (d.Low < min) - { - min = d.Low; - } - } - - result.TenkanSen = (min == decimal.MaxValue) ? null : (min + max) / 2; - } - } - - private static void CalcIchimokuKijunSen( - int i, - List quotesList, - IchimokuResult result, - int kijunPeriods) - where TQuote : IQuote - { - if (i >= kijunPeriods - 1) - { - decimal max = 0; - decimal min = decimal.MaxValue; - - for (int p = i - kijunPeriods + 1; p <= i; p++) - { - TQuote d = quotesList[p]; - - if (d.High > max) - { - max = d.High; - } - - if (d.Low < min) - { - min = d.Low; - } - } - - result.KijunSen = (min == decimal.MaxValue) ? null : (min + max) / 2; - } - } - - private static void CalcIchimokuSenkouB( - int i, - List quotesList, - IchimokuResult result, - int senkouOffset, - int senkouBPeriods) - where TQuote : IQuote - { - if (i >= senkouOffset + senkouBPeriods - 1) - { - decimal max = 0; - decimal min = decimal.MaxValue; - - for (int p = i - senkouOffset - senkouBPeriods + 1; - p <= i - senkouOffset; p++) - { - TQuote d = quotesList[p]; - - if (d.High > max) - { - max = d.High; - } - - if (d.Low < min) - { - min = d.Low; - } - } - - result.SenkouSpanB = (min == decimal.MaxValue) ? null : (min + max) / 2; - } - } - - private static void ValidateIchimoku( - int tenkanPeriods, - int kijunPeriods, - int senkouBPeriods, - int senkouOffset, - int chikouOffset) - { - // check parameter arguments - if (tenkanPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(tenkanPeriods), tenkanPeriods, - "Tenkan periods must be greater than 0 for Ichimoku Cloud."); - } - - if (kijunPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(kijunPeriods), kijunPeriods, - "Kijun periods must be greater than 0 for Ichimoku Cloud."); - } - - if (senkouBPeriods <= kijunPeriods) - { - throw new ArgumentOutOfRangeException(nameof(senkouBPeriods), senkouBPeriods, - "Senkou B periods must be greater than Kijun periods for Ichimoku Cloud."); - } - - if (senkouOffset < 0 || chikouOffset < 0) - { - throw new ArgumentOutOfRangeException(nameof(senkouOffset), senkouOffset, - "Senkou and Chikou offset periods must be non-negative for Ichimoku Cloud."); - } - } -} diff --git a/src/e-k/Ichimoku/Ichimoku.StaticSeries.cs b/src/e-k/Ichimoku/Ichimoku.StaticSeries.cs new file mode 100644 index 000000000..f370ff6a2 --- /dev/null +++ b/src/e-k/Ichimoku/Ichimoku.StaticSeries.cs @@ -0,0 +1,226 @@ +namespace Skender.Stock.Indicators; + +// ICHIMOKU CLOUD (SERIES) + +public static partial class Ichimoku +{ + public static IReadOnlyList ToIchimoku( + this IReadOnlyList quotes, + int tenkanPeriods = 9, + int kijunPeriods = 26, + int senkouBPeriods = 52) + where TQuote : IQuote => quotes + .ToSortedList() + .CalcIchimoku( + tenkanPeriods, + kijunPeriods, + senkouBPeriods, + kijunPeriods, + kijunPeriods); + + public static IReadOnlyList GetIchimoku( + this IReadOnlyList quotes, + int tenkanPeriods, + int kijunPeriods, + int senkouBPeriods, + int offsetPeriods) + where TQuote : IQuote => quotes + .ToSortedList() + .CalcIchimoku( + tenkanPeriods, + kijunPeriods, + senkouBPeriods, + offsetPeriods, + offsetPeriods); + + public static IReadOnlyList GetIchimoku( + this IReadOnlyList quotes, + int tenkanPeriods, + int kijunPeriods, + int senkouBPeriods, + int senkouOffset, + int chikouOffset) + where TQuote : IQuote => quotes + .ToSortedList() + .CalcIchimoku( + tenkanPeriods, + kijunPeriods, + senkouBPeriods, + senkouOffset, + chikouOffset); + + private static List CalcIchimoku( + this IReadOnlyList quotes, + int tenkanPeriods, + int kijunPeriods, + int senkouBPeriods, + int senkouOffset, + int chikouOffset) + where TQuote : IQuote + { + // check parameter arguments + Validate( + tenkanPeriods, + kijunPeriods, + senkouBPeriods, + senkouOffset, + chikouOffset); + + // initialize + int length = quotes.Count; + List results = new(length); + + int senkouStartPeriod = Math.Max( + 2 * senkouOffset, + Math.Max(tenkanPeriods, kijunPeriods)) - 1; + + // roll through source values + for (int i = 0; i < length; i++) + { + TQuote q = quotes[i]; + + // tenkan-sen conversion line + decimal? tenkanSen = CalcIchimokuTenkanSen( + i, quotes, tenkanPeriods); + + // kijun-sen base line + decimal? kijunSen = CalcIchimokuKijunSen( + i, quotes, kijunPeriods); + + // senkou span A + decimal? senkouSpanA = null; + + if (i >= senkouStartPeriod) + { + if (senkouOffset == 0) + { + senkouSpanA = (tenkanSen + kijunSen) / 2; + } + else + { + IchimokuResult skq = results[i - senkouOffset]; + senkouSpanA = (skq.TenkanSen + skq.KijunSen) / 2; + } + } + + // senkou span B + decimal? senkouSpanB = CalcIchimokuSenkouB( + i, quotes, senkouOffset, senkouBPeriods); + + // chikou line + decimal? chikouSpan = null; + + if (i + chikouOffset < quotes.Count) + { + chikouSpan = quotes[i + chikouOffset].Close; + } + + results.Add(new( + Timestamp: q.Timestamp, + TenkanSen: tenkanSen, + KijunSen: kijunSen, + SenkouSpanA: senkouSpanA, + SenkouSpanB: senkouSpanB, + ChikouSpan: chikouSpan)); + } + + return results; + } + + private static decimal? CalcIchimokuTenkanSen( + int i, IReadOnlyList quotes, int tenkanPeriods) + where TQuote : IQuote + { + if (i < tenkanPeriods - 1) + { + return null; + } + + decimal max = 0; + decimal min = decimal.MaxValue; + + for (int p = i - tenkanPeriods + 1; p <= i; p++) + { + TQuote d = quotes[p]; + + if (d.High > max) + { + max = d.High; + } + + if (d.Low < min) + { + min = d.Low; + } + } + + return min == decimal.MaxValue ? null : (min + max) / 2; + + } + + private static decimal? CalcIchimokuKijunSen( + int i, + IReadOnlyList quotes, + int kijunPeriods) + where TQuote : IQuote + { + if (i < kijunPeriods - 1) + { + return null; + } + + decimal max = 0; + decimal min = decimal.MaxValue; + + for (int p = i - kijunPeriods + 1; p <= i; p++) + { + TQuote d = quotes[p]; + + if (d.High > max) + { + max = d.High; + } + + if (d.Low < min) + { + min = d.Low; + } + } + + return min == decimal.MaxValue ? null : (min + max) / 2; + } + + private static decimal? CalcIchimokuSenkouB( + int i, + IReadOnlyList quotes, + int senkouOffset, + int senkouBPeriods) + where TQuote : IQuote + { + if (i < senkouOffset + senkouBPeriods - 1) + { + return null; + } + + decimal max = 0; + decimal min = decimal.MaxValue; + + for (int p = i - senkouOffset - senkouBPeriods + 1; + p <= i - senkouOffset; p++) + { + TQuote d = quotes[p]; + + if (d.High > max) + { + max = d.High; + } + + if (d.Low < min) + { + min = d.Low; + } + } + + return min == decimal.MaxValue ? null : (min + max) / 2; + } +} diff --git a/src/e-k/Ichimoku/Ichimoku.Utilities.cs b/src/e-k/Ichimoku/Ichimoku.Utilities.cs index de63ef8a6..5a09101db 100644 --- a/src/e-k/Ichimoku/Ichimoku.Utilities.cs +++ b/src/e-k/Ichimoku/Ichimoku.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ICHIMOKU CLOUD (UTILITIES) + +public static partial class Ichimoku { - // CONDENSE (REMOVE null results) - /// - /// - public static IEnumerable Condense( - this IEnumerable results) + // remove null results + /// + public static IReadOnlyList Condense( + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -21,4 +22,37 @@ x.TenkanSen is null return resultsList.ToSortedList(); } + + // validate parameters + internal static void Validate( + int tenkanPeriods, + int kijunPeriods, + int senkouBPeriods, + int senkouOffset, + int chikouOffset) + { + if (tenkanPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(tenkanPeriods), tenkanPeriods, + "Tenkan periods must be greater than 0 for Ichimoku Cloud."); + } + + if (kijunPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(kijunPeriods), kijunPeriods, + "Kijun periods must be greater than 0 for Ichimoku Cloud."); + } + + if (senkouBPeriods <= kijunPeriods) + { + throw new ArgumentOutOfRangeException(nameof(senkouBPeriods), senkouBPeriods, + "Senkou B periods must be greater than Kijun periods for Ichimoku Cloud."); + } + + if (senkouOffset < 0 || chikouOffset < 0) + { + throw new ArgumentOutOfRangeException(nameof(senkouOffset), senkouOffset, + "Senkou and Chikou offset periods must be non-negative for Ichimoku Cloud."); + } + } } diff --git a/src/e-k/Kama/Kama.Api.cs b/src/e-k/Kama/Kama.Api.cs deleted file mode 100644 index 01a308a1c..000000000 --- a/src/e-k/Kama/Kama.Api.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace Skender.Stock.Indicators; - -// KAUFMAN's ADAPTIVE MOVING AVERAGE (API) -public static partial class Indicator -{ - /// - /// - public static IEnumerable GetKama( - this IEnumerable quotes, - int erPeriods = 10, - int fastPeriods = 2, - int slowPeriods = 30) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcKama(erPeriods, fastPeriods, slowPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetKama( - this IEnumerable results, - int erPeriods = 10, - int fastPeriods = 2, - int slowPeriods = 30) => results - .ToTuple() - .CalcKama(erPeriods, fastPeriods, slowPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetKama( - this IEnumerable<(DateTime, double)> priceTuples, - int erPeriods = 10, - int fastPeriods = 2, - int slowPeriods = 30) => priceTuples - .ToSortedList() - .CalcKama(erPeriods, fastPeriods, slowPeriods); -} diff --git a/src/e-k/Kama/Kama.Models.cs b/src/e-k/Kama/Kama.Models.cs index e5e3e0e23..27b62844c 100644 --- a/src/e-k/Kama/Kama.Models.cs +++ b/src/e-k/Kama/Kama.Models.cs @@ -1,15 +1,12 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class KamaResult : ResultBase, IReusableResult +public record KamaResult +( + DateTime Timestamp, + double? Er = null, + double? Kama = null +) : IReusable { - public KamaResult(DateTime date) - { - Date = date; - } - - public double? ER { get; set; } - public double? Kama { get; set; } - - double? IReusableResult.Value => Kama; + public double Value => Kama.Null2NaN(); } diff --git a/src/e-k/Kama/Kama.Series.cs b/src/e-k/Kama/Kama.Series.cs deleted file mode 100644 index d5b342b8a..000000000 --- a/src/e-k/Kama/Kama.Series.cs +++ /dev/null @@ -1,98 +0,0 @@ -namespace Skender.Stock.Indicators; - -// KAUFMAN's ADAPTIVE MOVING AVERAGE (SERIES) -public static partial class Indicator -{ - internal static List CalcKama( - this List<(DateTime, double)> tpList, - int erPeriods, - int fastPeriods, - int slowPeriods) - { - // check parameter arguments - ValidateKama(erPeriods, fastPeriods, slowPeriods); - - // initialize - int length = tpList.Count; - List results = new(length); - double scFast = 2d / (fastPeriods + 1); - double scSlow = 2d / (slowPeriods + 1); - - // roll through quotes - for (int i = 0; i < length; i++) - { - (DateTime date, double value) = tpList[i]; - - KamaResult r = new(date); - results.Add(r); - - if (i + 1 > erPeriods) - { - // ER period change - double change = Math.Abs(value - tpList[i - erPeriods].Item2); - - // volatility - double sumPV = 0; - for (int p = i - erPeriods + 1; p <= i; p++) - { - sumPV += Math.Abs(tpList[p].Item2 - tpList[p - 1].Item2); - } - - if (sumPV != 0) - { - // efficiency ratio - double er = change / sumPV; - r.ER = er.NaN2Null(); - - // smoothing constant - double sc = (er * (scFast - scSlow)) + scSlow; // squared later - - // kama calculation - double? pk = results[i - 1].Kama; // prior KAMA - r.Kama = (pk + (sc * sc * (value - pk))).NaN2Null(); - } - - // handle flatline case - else - { - r.ER = 0; - r.Kama = value.NaN2Null(); - } - } - - // initial value - else if (i + 1 == erPeriods) - { - r.Kama = value.NaN2Null(); - } - } - - return results; - } - - // parameter validation - private static void ValidateKama( - int erPeriods, - int fastPeriods, - int slowPeriods) - { - // check parameter arguments - if (erPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(erPeriods), erPeriods, - "Efficiency Ratio periods must be greater than 0 for KAMA."); - } - - if (fastPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, - "Fast EMA periods must be greater than 0 for KAMA."); - } - - if (slowPeriods <= fastPeriods) - { - throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, - "Slow EMA periods must be greater than Fast EMA period for KAMA."); - } - } -} diff --git a/src/e-k/Kama/Kama.StaticSeries.cs b/src/e-k/Kama/Kama.StaticSeries.cs new file mode 100644 index 000000000..b321d4cbf --- /dev/null +++ b/src/e-k/Kama/Kama.StaticSeries.cs @@ -0,0 +1,91 @@ +namespace Skender.Stock.Indicators; + +// KAUFMAN's ADAPTIVE MOVING AVERAGE (SERIES) + +public static partial class Kama +{ + public static IReadOnlyList ToKama( + this IReadOnlyList source, + int erPeriods = 10, + int fastPeriods = 2, + int slowPeriods = 30) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(erPeriods, fastPeriods, slowPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + double scFast = 2d / (fastPeriods + 1); + double scSlow = 2d / (slowPeriods + 1); + + double prevKama = double.NaN; + + // roll through source values + for (int i = 0; i < length; i++) + { + // skip incalculable periods + if (i < erPeriods - 1) + { + results.Add(new(source[i].Timestamp)); + continue; + } + + double er; + double kama; + + if (results[i - 1].Kama is not null) + { + double newVal = source[i].Value; + + // ER period change + double change = Math.Abs(newVal - source[i - erPeriods].Value); + + // volatility + double sumPv = 0; + for (int p = i - erPeriods + 1; p <= i; p++) + { + sumPv += Math.Abs(source[p].Value - source[p - 1].Value); + } + + if (sumPv != 0) + { + // efficiency ratio + er = change / sumPv; + + // smoothing constant + double sc = (er * (scFast - scSlow)) + scSlow; // squared later + + // kama calculation + kama = prevKama + (sc * sc * (newVal - prevKama)); + } + + // handle flatline case + else + { + er = 0; + kama = source[i].Value; + } + } + + // re/initialize + else + { + er = double.NaN; + kama = source[i].Value; + } + + results.Add(new KamaResult( + Timestamp: source[i].Timestamp, + Er: er.NaN2Null(), + Kama: kama.NaN2Null())); + + prevKama = kama; + } + + return results; + } +} diff --git a/src/e-k/Kama/Kama.Utilities.cs b/src/e-k/Kama/Kama.Utilities.cs index 2e3493944..93af9a74a 100644 --- a/src/e-k/Kama/Kama.Utilities.cs +++ b/src/e-k/Kama/Kama.Utilities.cs @@ -1,17 +1,44 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// KAUFMAN's ADAPTIVE MOVING AVERAGE (UTILITIES) + +public static partial class Kama { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int erPeriods = results .ToList() - .FindIndex(x => x.ER != null); + .FindIndex(x => x.Er != null); return results.Remove(Math.Max(erPeriods + 100, 10 * erPeriods)); } + + // parameter validation + internal static void Validate( + int erPeriods, + int fastPeriods, + int slowPeriods) + { + // check parameter arguments + if (erPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(erPeriods), erPeriods, + "Efficiency Ratio periods must be greater than 0 for KAMA."); + } + + if (fastPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, + "Fast EMA periods must be greater than 0 for KAMA."); + } + + if (slowPeriods <= fastPeriods) + { + throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, + "Slow EMA periods must be greater than Fast EMA period for KAMA."); + } + } } diff --git a/src/e-k/Keltner/Keltner.Api.cs b/src/e-k/Keltner/Keltner.Api.cs deleted file mode 100644 index 9df400951..000000000 --- a/src/e-k/Keltner/Keltner.Api.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Skender.Stock.Indicators; - -// KELTNER CHANNELS (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetKeltner( - this IEnumerable quotes, - int emaPeriods = 20, - double multiplier = 2, - int atrPeriods = 10) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcKeltner(emaPeriods, multiplier, atrPeriods); -} diff --git a/src/e-k/Keltner/Keltner.Models.cs b/src/e-k/Keltner/Keltner.Models.cs index ba9d79fbe..7acc1a4b6 100644 --- a/src/e-k/Keltner/Keltner.Models.cs +++ b/src/e-k/Keltner/Keltner.Models.cs @@ -1,15 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class KeltnerResult : ResultBase -{ - public KeltnerResult(DateTime date) - { - Date = date; - } - - public double? UpperBand { get; set; } - public double? Centerline { get; set; } - public double? LowerBand { get; set; } - public double? Width { get; set; } -} +public record KeltnerResult +( + DateTime Timestamp, + double? UpperBand = null, + double? Centerline = null, + double? LowerBand = null, + double? Width = null +) : ISeries; diff --git a/src/e-k/Keltner/Keltner.Series.cs b/src/e-k/Keltner/Keltner.Series.cs deleted file mode 100644 index b6dfcde83..000000000 --- a/src/e-k/Keltner/Keltner.Series.cs +++ /dev/null @@ -1,80 +0,0 @@ -namespace Skender.Stock.Indicators; - -// KELTNER CHANNELS (SERIES) -public static partial class Indicator -{ - internal static List CalcKeltner( - this List qdList, - int emaPeriods, - double multiplier, - int atrPeriods) - { - // check parameter arguments - ValidateKeltner(emaPeriods, multiplier, atrPeriods); - - // initialize - int length = qdList.Count; - List results = new(length); - - List emaResults = qdList - .ToTuple(CandlePart.Close) - .CalcEma(emaPeriods) - .ToList(); - - List atrResults = qdList - .CalcAtr(atrPeriods) - .ToList(); - - int lookbackPeriods = Math.Max(emaPeriods, atrPeriods); - - // roll through quotes - for (int i = 0; i < length; i++) - { - QuoteD q = qdList[i]; - - KeltnerResult r = new(q.Date); - results.Add(r); - - if (i + 1 >= lookbackPeriods) - { - EmaResult ema = emaResults[i]; - AtrResult atr = atrResults[i]; - double? atrSpan = atr.Atr * multiplier; - - r.UpperBand = ema.Ema + atrSpan; - r.LowerBand = ema.Ema - atrSpan; - r.Centerline = ema.Ema; - r.Width = (r.Centerline == 0) ? null - : (r.UpperBand - r.LowerBand) / r.Centerline; - } - } - - return results; - } - - // parameter validation - private static void ValidateKeltner( - int emaPeriods, - double multiplier, - int atrPeriods) - { - // check parameter arguments - if (emaPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(emaPeriods), emaPeriods, - "EMA periods must be greater than 1 for Keltner Channel."); - } - - if (atrPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(atrPeriods), atrPeriods, - "ATR periods must be greater than 1 for Keltner Channel."); - } - - if (multiplier <= 0) - { - throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, - "Multiplier must be greater than 0 for Keltner Channel."); - } - } -} diff --git a/src/e-k/Keltner/Keltner.StaticSeries.cs b/src/e-k/Keltner/Keltner.StaticSeries.cs new file mode 100644 index 000000000..baa1ba676 --- /dev/null +++ b/src/e-k/Keltner/Keltner.StaticSeries.cs @@ -0,0 +1,63 @@ +namespace Skender.Stock.Indicators; + +// KELTNER CHANNELS (SERIES) + +public static partial class Keltner +{ + public static IReadOnlyList ToKeltner( + this IReadOnlyList quotes, + int emaPeriods = 20, + double multiplier = 2, + int atrPeriods = 10) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcKeltner(emaPeriods, multiplier, atrPeriods); + + private static List CalcKeltner( + this IReadOnlyList source, + int emaPeriods, + double multiplier, + int atrPeriods) + { + // check parameter arguments + Validate(emaPeriods, multiplier, atrPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + IReadOnlyList emaResults + = source.ToEma(emaPeriods); + + IReadOnlyList atrResults + = source.CalcAtr(atrPeriods); + + int lookbackPeriods = Math.Max(emaPeriods, atrPeriods); + + // roll through source values + for (int i = 0; i < length; i++) + { + QuoteD q = source[i]; + + if (i >= lookbackPeriods - 1) + { + EmaResult ema = emaResults[i]; + AtrResult atr = atrResults[i]; + double? atrSpan = atr.Atr * multiplier; + + results.Add(new KeltnerResult( + Timestamp: q.Timestamp, + UpperBand: ema.Ema + atrSpan, + LowerBand: ema.Ema - atrSpan, + Centerline: ema.Ema, + Width: ema.Ema == 0 ? null : 2 * atrSpan / ema.Ema)); + } + else + { + results.Add(new(q.Timestamp)); + } + } + + return results; + } +} diff --git a/src/e-k/Keltner/Keltner.Utilities.cs b/src/e-k/Keltner/Keltner.Utilities.cs index 9223b3a24..992bf4f5e 100644 --- a/src/e-k/Keltner/Keltner.Utilities.cs +++ b/src/e-k/Keltner/Keltner.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// KELTNER CHANNELS (UTILITIES) + +public static partial class Keltner { // CONDENSE (REMOVE null results) - /// - /// - public static IEnumerable Condense( - this IEnumerable results) + /// + public static IReadOnlyList Condense( + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -19,10 +20,9 @@ public static IEnumerable Condense( } // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int n = results .ToList() @@ -30,4 +30,30 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(Math.Max(2 * n, n + 100)); } + + // parameter validation + internal static void Validate( + int emaPeriods, + double multiplier, + int atrPeriods) + { + // check parameter arguments + if (emaPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(emaPeriods), emaPeriods, + "EMA periods must be greater than 1 for Keltner Channel."); + } + + if (atrPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(atrPeriods), atrPeriods, + "ATR periods must be greater than 1 for Keltner Channel."); + } + + if (multiplier <= 0) + { + throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, + "Multiplier must be greater than 0 for Keltner Channel."); + } + } } diff --git a/src/e-k/Kvo/Kvo.Api.cs b/src/e-k/Kvo/Kvo.Api.cs deleted file mode 100644 index 03076766d..000000000 --- a/src/e-k/Kvo/Kvo.Api.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Skender.Stock.Indicators; - -// KLINGER VOLUME OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetKvo( - this IEnumerable quotes, - int fastPeriods = 34, - int slowPeriods = 55, - int signalPeriods = 13) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcKvo(fastPeriods, slowPeriods, signalPeriods); -} diff --git a/src/e-k/Kvo/Kvo.Models.cs b/src/e-k/Kvo/Kvo.Models.cs index 030bd1a31..9588ad1c8 100644 --- a/src/e-k/Kvo/Kvo.Models.cs +++ b/src/e-k/Kvo/Kvo.Models.cs @@ -1,15 +1,12 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class KvoResult : ResultBase, IReusableResult +public record KvoResult +( + DateTime Timestamp, + double? Oscillator = null, + double? Signal = null +) : IReusable { - internal KvoResult(DateTime date) - { - Date = date; - } - - public double? Oscillator { get; set; } - public double? Signal { get; set; } - - double? IReusableResult.Value => Oscillator; + public double Value => Oscillator.Null2NaN(); } diff --git a/src/e-k/Kvo/Kvo.Series.cs b/src/e-k/Kvo/Kvo.StaticSeries.cs similarity index 60% rename from src/e-k/Kvo/Kvo.Series.cs rename to src/e-k/Kvo/Kvo.StaticSeries.cs index b27ae2b49..1b735fe2d 100644 --- a/src/e-k/Kvo/Kvo.Series.cs +++ b/src/e-k/Kvo/Kvo.StaticSeries.cs @@ -1,19 +1,29 @@ namespace Skender.Stock.Indicators; // KLINGER VOLUME OSCILLATOR (SERIES) -public static partial class Indicator + +public static partial class Kvo { - internal static List CalcKvo( - this List qdList, + public static IReadOnlyList ToKvo( + this IReadOnlyList quotes, + int fastPeriods = 34, + int slowPeriods = 55, + int signalPeriods = 13) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcKvo(fastPeriods, slowPeriods, signalPeriods); + + private static List CalcKvo( + this IReadOnlyList source, int fastPeriods, int slowPeriods, int signalPeriods) { // check parameter arguments - ValidateKlinger(fastPeriods, slowPeriods, signalPeriods); + Validate(fastPeriods, slowPeriods, signalPeriods); // initialize - int length = qdList.Count; + int length = source.Count; List results = new(length); double[] t = new double[length]; // trend direction @@ -29,13 +39,13 @@ internal static List CalcKvo( double kSlow = 2d / (slowPeriods + 1); double kSignal = 2d / (signalPeriods + 1); - // roll through quotes + // roll through source values for (int i = 0; i < length; i++) { - QuoteD q = qdList[i]; + QuoteD q = source[i]; - KvoResult r = new(q.Date); - results.Add(r); + double? kvo = null; + double? sig = null; // trend basis comparator hlc[i] = q.High + q.Low + q.Close; @@ -45,26 +55,28 @@ internal static List CalcKvo( if (i <= 0) { + results.Add(new(q.Timestamp)); continue; } // trend direction - t[i] = (hlc[i] > hlc[i - 1]) ? 1 : -1; + t[i] = hlc[i] > hlc[i - 1] ? 1 : -1; if (i <= 1) { cm[i] = 0; + results.Add(new(q.Timestamp)); continue; } // cumulative measurement - cm[i] = (t[i] == t[i - 1]) ? - (cm[i - 1] + dm[i]) : (dm[i - 1] + dm[i]); + cm[i] = t[i] == t[i - 1] ? + cm[i - 1] + dm[i] : dm[i - 1] + dm[i]; // volume force (VF) - vf[i] = (dm[i] == cm[i] || q.Volume == 0) ? 0 - : (dm[i] == 0) ? q.Volume * 2d * t[i] * 100d - : (cm[i] != 0) ? q.Volume * Math.Abs(2d * ((dm[i] / cm[i]) - 1)) * t[i] * 100d + vf[i] = dm[i] == cm[i] || q.Volume == 0 ? 0 + : dm[i] == 0 ? q.Volume * 2d * t[i] * 100d + : cm[i] != 0 ? q.Volume * Math.Abs(2d * ((dm[i] / cm[i]) - 1)) * t[i] * 100d : vf[i - 1]; // fast-period EMA of VF @@ -72,6 +84,8 @@ internal static List CalcKvo( { vfFastEma[i] = (vf[i] * kFast) + (vfFastEma[i - 1] * (1 - kFast)); } + + // TODO: update healing, without requiring specific indexing else if (i == fastPeriods + 1) { double sum = 0; @@ -88,6 +102,8 @@ internal static List CalcKvo( { vfSlowEma[i] = (vf[i] * kSlow) + (vfSlowEma[i - 1] * (1 - kSlow)); } + + // TODO: update healing, without requiring specific indexing else if (i == slowPeriods + 1) { double sum = 0; @@ -102,53 +118,34 @@ internal static List CalcKvo( // Klinger Oscillator if (i >= slowPeriods + 1) { - r.Oscillator = vfFastEma[i] - vfSlowEma[i]; + kvo = vfFastEma[i] - vfSlowEma[i]; // Signal if (i > slowPeriods + signalPeriods) { - r.Signal = (r.Oscillator * kSignal) + sig = (kvo * kSignal) + (results[i - 1].Signal * (1 - kSignal)); } + + // TODO: update healing, without requiring specific indexing else if (i == slowPeriods + signalPeriods) { - double? sum = 0; - for (int p = slowPeriods + 1; p <= i; p++) + double? sum = kvo; + for (int p = slowPeriods + 1; p < i; p++) { sum += results[p].Oscillator; } - r.Signal = sum / signalPeriods; + sig = sum / signalPeriods; } } - } - - return results; - } - - // parameter validation - private static void ValidateKlinger( - int fastPeriods, - int slowPeriods, - int signalPeriods) - { - // check parameter arguments - if (fastPeriods <= 2) - { - throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, - "Fast (short) Periods must be greater than 2 for Klinger Oscillator."); - } - if (slowPeriods <= fastPeriods) - { - throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, - "Slow (long) Periods must be greater than Fast Periods for Klinger Oscillator."); + results.Add(new KvoResult( + Timestamp: q.Timestamp, + Oscillator: kvo, + Signal: sig)); } - if (signalPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, - "Signal Periods must be greater than 0 for Klinger Oscillator."); - } + return results; } } diff --git a/src/e-k/Kvo/Kvo.Utilities.cs b/src/e-k/Kvo/Kvo.Utilities.cs index fcc18e34d..022893924 100644 --- a/src/e-k/Kvo/Kvo.Utilities.cs +++ b/src/e-k/Kvo/Kvo.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// KLINGER VOLUME OSCILLATOR (UTILIITES) + +public static partial class Kvo { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int l = results .ToList() @@ -14,4 +15,30 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(l + 150); } + + // parameter validation + internal static void Validate( + int fastPeriods, + int slowPeriods, + int signalPeriods) + { + // check parameter arguments + if (fastPeriods <= 2) + { + throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, + "Fast (short) Periods must be greater than 2 for Klinger Oscillator."); + } + + if (slowPeriods <= fastPeriods) + { + throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, + "Slow (long) Periods must be greater than Fast Periods for Klinger Oscillator."); + } + + if (signalPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, + "Signal Periods must be greater than 0 for Klinger Oscillator."); + } + } } diff --git a/src/m-r/MaEnvelopes/MaEnvelopes.Api.cs b/src/m-r/MaEnvelopes/MaEnvelopes.Api.cs deleted file mode 100644 index 4d2035b5f..000000000 --- a/src/m-r/MaEnvelopes/MaEnvelopes.Api.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MOVING AVERAGE ENVELOPES (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetMaEnvelopes( - this IEnumerable quotes, - int lookbackPeriods, - double percentOffset = 2.5, - MaType movingAverageType = MaType.SMA) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcMaEnvelopes(lookbackPeriods, percentOffset, movingAverageType); - - // SERIES, from CHAIN - public static IEnumerable GetMaEnvelopes( - this IEnumerable results, - int lookbackPeriods, - double percentOffset = 2.5, - MaType movingAverageType = MaType.SMA) => results - .ToTuple() - .CalcMaEnvelopes(lookbackPeriods, percentOffset, movingAverageType) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetMaEnvelopes( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods, - double percentOffset = 2.5, - MaType movingAverageType = MaType.SMA) => priceTuples - .ToSortedList() - .CalcMaEnvelopes(lookbackPeriods, percentOffset, movingAverageType); -} diff --git a/src/m-r/MaEnvelopes/MaEnvelopes.Models.cs b/src/m-r/MaEnvelopes/MaEnvelopes.Models.cs index 399d39c12..c485dfe50 100644 --- a/src/m-r/MaEnvelopes/MaEnvelopes.Models.cs +++ b/src/m-r/MaEnvelopes/MaEnvelopes.Models.cs @@ -1,14 +1,10 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class MaEnvelopeResult : ResultBase -{ - public MaEnvelopeResult(DateTime date) - { - Date = date; - } - - public double? Centerline { get; set; } - public double? UpperEnvelope { get; set; } - public double? LowerEnvelope { get; set; } -} +public record MaEnvelopeResult +( + DateTime Timestamp, + double? Centerline, + double? UpperEnvelope, + double? LowerEnvelope +) : ISeries; diff --git a/src/m-r/MaEnvelopes/MaEnvelopes.Series.cs b/src/m-r/MaEnvelopes/MaEnvelopes.Series.cs deleted file mode 100644 index ce8776b33..000000000 --- a/src/m-r/MaEnvelopes/MaEnvelopes.Series.cs +++ /dev/null @@ -1,151 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MOVING AVERAGE ENVELOPES (SERIES) -public static partial class Indicator -{ - // calculate series - internal static IEnumerable CalcMaEnvelopes( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - double percentOffset, - MaType movingAverageType) - { - // check parameter arguments - // note: most validations are done in variant methods - ValidateMaEnvelopes(percentOffset); - - // initialize - double offsetRatio = percentOffset / 100d; - - // get envelopes variant - return movingAverageType switch { - MaType.ALMA => tpList.MaEnvAlma(lookbackPeriods, offsetRatio), - MaType.DEMA => tpList.MaEnvDema(lookbackPeriods, offsetRatio), - MaType.EMA => tpList.MaEnvEma(lookbackPeriods, offsetRatio), - MaType.EPMA => tpList.MaEnvEpma(lookbackPeriods, offsetRatio), - MaType.HMA => tpList.MaEnvHma(lookbackPeriods, offsetRatio), - MaType.SMA => tpList.MaEnvSma(lookbackPeriods, offsetRatio), - MaType.SMMA => tpList.MaEnvSmma(lookbackPeriods, offsetRatio), - MaType.TEMA => tpList.MaEnvTema(lookbackPeriods, offsetRatio), - MaType.WMA => tpList.MaEnvWma(lookbackPeriods, offsetRatio), - - _ => throw new ArgumentOutOfRangeException( - nameof(movingAverageType), movingAverageType, - string.Format( - invCulture, - "Moving Average Envelopes does not support {0}.", - Enum.GetName(typeof(MaType), movingAverageType))) - }; - } - - private static IEnumerable MaEnvAlma( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - double offsetRatio) - => tpList.GetAlma(lookbackPeriods) - .Select(x => new MaEnvelopeResult(x.Date) { - Centerline = x.Alma, - UpperEnvelope = x.Alma + (x.Alma * offsetRatio), - LowerEnvelope = x.Alma - (x.Alma * offsetRatio) - }); - - private static IEnumerable MaEnvDema( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - double offsetRatio) - => tpList.GetDema(lookbackPeriods) - .Select(x => new MaEnvelopeResult(x.Date) { - Centerline = x.Dema, - UpperEnvelope = x.Dema + (x.Dema * offsetRatio), - LowerEnvelope = x.Dema - (x.Dema * offsetRatio) - }); - - private static IEnumerable MaEnvEma( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - double offsetRatio) - => tpList.GetEma(lookbackPeriods) - .Select(x => new MaEnvelopeResult(x.Date) { - Centerline = x.Ema, - UpperEnvelope = x.Ema + (x.Ema * offsetRatio), - LowerEnvelope = x.Ema - (x.Ema * offsetRatio) - }); - - private static IEnumerable MaEnvEpma( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - double offsetRatio) - => tpList.GetEpma(lookbackPeriods) - .Select(x => new MaEnvelopeResult(x.Date) { - Centerline = x.Epma, - UpperEnvelope = x.Epma + (x.Epma * offsetRatio), - LowerEnvelope = x.Epma - (x.Epma * offsetRatio) - }); - - private static IEnumerable MaEnvHma( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - double offsetRatio) - => tpList.GetHma(lookbackPeriods) - .Select(x => new MaEnvelopeResult(x.Date) { - Centerline = x.Hma, - UpperEnvelope = x.Hma + (x.Hma * offsetRatio), - LowerEnvelope = x.Hma - (x.Hma * offsetRatio) - }); - - private static IEnumerable MaEnvSma( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - double offsetRatio) - => tpList.GetSma(lookbackPeriods) - .Select(x => new MaEnvelopeResult(x.Date) { - Centerline = x.Sma, - UpperEnvelope = x.Sma + (x.Sma * offsetRatio), - LowerEnvelope = x.Sma - (x.Sma * offsetRatio) - }); - - private static IEnumerable MaEnvSmma( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - double offsetRatio) - => tpList.GetSmma(lookbackPeriods) - .Select(x => new MaEnvelopeResult(x.Date) { - Centerline = x.Smma, - UpperEnvelope = x.Smma + (x.Smma * offsetRatio), - LowerEnvelope = x.Smma - (x.Smma * offsetRatio) - }); - - private static IEnumerable MaEnvTema( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - double offsetRatio) - => tpList.GetTema(lookbackPeriods) - .Select(x => new MaEnvelopeResult(x.Date) { - Centerline = x.Tema, - UpperEnvelope = x.Tema + (x.Tema * offsetRatio), - LowerEnvelope = x.Tema - (x.Tema * offsetRatio) - }); - - private static IEnumerable MaEnvWma( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - double offsetRatio) - => tpList.GetWma(lookbackPeriods) - .Select(x => new MaEnvelopeResult(x.Date) { - Centerline = x.Wma, - UpperEnvelope = x.Wma + (x.Wma * offsetRatio), - LowerEnvelope = x.Wma - (x.Wma * offsetRatio) - }); - - // parameter validation - private static void ValidateMaEnvelopes( - double percentOffset) - { - // check parameter arguments - if (percentOffset <= 0) - { - throw new ArgumentOutOfRangeException(nameof(percentOffset), percentOffset, - "Percent Offset must be greater than 0 for Moving Average Envelopes."); - } - } -} diff --git a/src/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.cs b/src/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.cs new file mode 100644 index 000000000..52942bbbd --- /dev/null +++ b/src/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.cs @@ -0,0 +1,155 @@ +using System.Globalization; + +namespace Skender.Stock.Indicators; + +// MOVING AVERAGE ENVELOPES (SERIES) + +public static partial class MaEnvelopes +{ + // calculate series + public static IReadOnlyList ToMaEnvelopes( + this IReadOnlyList source, + int lookbackPeriods, + double percentOffset = 2.5, + MaType movingAverageType = MaType.SMA) + where T : IReusable + { + // check parameter arguments + // note: most validations are done in variant methods + Validate(percentOffset); + + // initialize + double offsetRatio = percentOffset / 100d; + + // get envelopes variant + IEnumerable results = movingAverageType + switch { + MaType.ALMA => source.MaEnvAlma(lookbackPeriods, offsetRatio), + MaType.DEMA => source.MaEnvDema(lookbackPeriods, offsetRatio), + MaType.EMA => source.MaEnvEma(lookbackPeriods, offsetRatio), + MaType.EPMA => source.MaEnvEpma(lookbackPeriods, offsetRatio), + MaType.HMA => source.MaEnvHma(lookbackPeriods, offsetRatio), + MaType.SMA => source.MaEnvSma(lookbackPeriods, offsetRatio), + MaType.SMMA => source.MaEnvSmma(lookbackPeriods, offsetRatio), + MaType.TEMA => source.MaEnvTema(lookbackPeriods, offsetRatio), + MaType.WMA => source.MaEnvWma(lookbackPeriods, offsetRatio), + + _ => throw new ArgumentOutOfRangeException( + nameof(movingAverageType), movingAverageType, + string.Format( + CultureInfo.InvariantCulture, + "Moving Average Envelopes does not support {0}.", + Enum.GetName(typeof(MaType), movingAverageType))) + }; + + return results.ToList(); + } + + private static IEnumerable MaEnvAlma( + this IReadOnlyList source, + int lookbackPeriods, + double offsetRatio) + where T : IReusable + => source.ToAlma(lookbackPeriods, offset: 0.85, sigma: 6) + .Select(x => new MaEnvelopeResult( + Timestamp: x.Timestamp, + Centerline: x.Alma, + UpperEnvelope: x.Alma + (x.Alma * offsetRatio), + LowerEnvelope: x.Alma - (x.Alma * offsetRatio))); + + private static IEnumerable MaEnvDema( + this IReadOnlyList source, + int lookbackPeriods, + double offsetRatio) + where T : IReusable + => source.ToDema(lookbackPeriods) + .Select(x => new MaEnvelopeResult( + Timestamp: x.Timestamp, + Centerline: x.Dema, + UpperEnvelope: x.Dema + (x.Dema * offsetRatio), + LowerEnvelope: x.Dema - (x.Dema * offsetRatio))); + + private static IEnumerable MaEnvEma( + this IReadOnlyList source, + int lookbackPeriods, + double offsetRatio) + where T : IReusable + => source.ToEma(lookbackPeriods) + .Select(x => new MaEnvelopeResult( + Timestamp: x.Timestamp, + Centerline: x.Ema, + UpperEnvelope: x.Ema + (x.Ema * offsetRatio), + LowerEnvelope: x.Ema - (x.Ema * offsetRatio))); + + private static IEnumerable MaEnvEpma( + this IReadOnlyList source, + int lookbackPeriods, + double offsetRatio) + where T : IReusable + => source.ToEpma(lookbackPeriods) + .Select(x => new MaEnvelopeResult( + Timestamp: x.Timestamp, + Centerline: x.Epma, + UpperEnvelope: x.Epma + (x.Epma * offsetRatio), + LowerEnvelope: x.Epma - (x.Epma * offsetRatio))); + + private static IEnumerable MaEnvHma( + this IReadOnlyList source, + int lookbackPeriods, + double offsetRatio) + where T : IReusable + => source.ToHma(lookbackPeriods) + .Select(x => new MaEnvelopeResult( + Timestamp: x.Timestamp, + Centerline: x.Hma, + UpperEnvelope: x.Hma + (x.Hma * offsetRatio), + LowerEnvelope: x.Hma - (x.Hma * offsetRatio))); + + private static IEnumerable MaEnvSma( + this IReadOnlyList source, + int lookbackPeriods, + double offsetRatio) + where T : IReusable + => source.ToSma(lookbackPeriods) + .Select(x => new MaEnvelopeResult( + Timestamp: x.Timestamp, + Centerline: x.Sma, + UpperEnvelope: x.Sma + (x.Sma * offsetRatio), + LowerEnvelope: x.Sma - (x.Sma * offsetRatio))); + + private static IEnumerable MaEnvSmma( + this IReadOnlyList source, + int lookbackPeriods, + double offsetRatio) + where T : IReusable + => source.ToSmma(lookbackPeriods) + .Select(x => new MaEnvelopeResult( + Timestamp: x.Timestamp, + Centerline: x.Smma, + UpperEnvelope: x.Smma + (x.Smma * offsetRatio), + LowerEnvelope: x.Smma - (x.Smma * offsetRatio))); + + private static IEnumerable MaEnvTema( + this IReadOnlyList source, + int lookbackPeriods, + double offsetRatio) + where T : IReusable + => source.ToTema(lookbackPeriods) + .Select(x => new MaEnvelopeResult( + Timestamp: x.Timestamp, + Centerline: x.Tema, + UpperEnvelope: x.Tema + (x.Tema * offsetRatio), + LowerEnvelope: x.Tema - (x.Tema * offsetRatio))); + + private static IEnumerable MaEnvWma( + this IReadOnlyList source, + int lookbackPeriods, + double offsetRatio) + where T : IReusable + => source.ToWma(lookbackPeriods) + .Select(x => new MaEnvelopeResult( + Timestamp: x.Timestamp, + Centerline: x.Wma, + UpperEnvelope: x.Wma + (x.Wma * offsetRatio), + LowerEnvelope: x.Wma - (x.Wma * offsetRatio))); +} diff --git a/src/m-r/MaEnvelopes/MaEnvelopes.Utilities.cs b/src/m-r/MaEnvelopes/MaEnvelopes.Utilities.cs index 9f2f3ed4b..07b314c46 100644 --- a/src/m-r/MaEnvelopes/MaEnvelopes.Utilities.cs +++ b/src/m-r/MaEnvelopes/MaEnvelopes.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// MOVING AVERAGE ENVELOPES (UTILITIES) + +public static partial class MaEnvelopes { // CONDENSE (REMOVE null results) - /// - /// - public static IEnumerable Condense( - this IEnumerable results) + /// + public static IReadOnlyList Condense( + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -17,4 +18,16 @@ public static IEnumerable Condense( return resultsList.ToSortedList(); } + + // parameter validation + internal static void Validate( + double percentOffset) + { + // check parameter arguments + if (percentOffset <= 0) + { + throw new ArgumentOutOfRangeException(nameof(percentOffset), percentOffset, + "Percent Offset must be greater than 0 for Moving Average Envelopes."); + } + } } diff --git a/src/m-r/Macd/Macd.Models.cs b/src/m-r/Macd/Macd.Models.cs index e989b1a5e..5af8fb0df 100644 --- a/src/m-r/Macd/Macd.Models.cs +++ b/src/m-r/Macd/Macd.Models.cs @@ -1,20 +1,18 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class MacdResult : ResultBase, IReusableResult -{ - public MacdResult(DateTime date) - { - Date = date; - } - - public double? Macd { get; set; } - public double? Signal { get; set; } - public double? Histogram { get; set; } +public record MacdResult +( + DateTime Timestamp, + double? Macd, + double? Signal, + double? Histogram, - // extra interim data - public double? FastEma { get; set; } - public double? SlowEma { get; set; } + // extra/interim data + double? FastEma, + double? SlowEma - double? IReusableResult.Value => Macd; +) : IReusable +{ + public double Value => Macd.Null2NaN(); } diff --git a/src/m-r/Macd/Macd.Series.cs b/src/m-r/Macd/Macd.Series.cs deleted file mode 100644 index d5660d9c3..000000000 --- a/src/m-r/Macd/Macd.Series.cs +++ /dev/null @@ -1,88 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MOVING AVERAGE CONVERGENCE/DIVERGENCE (MACD) OSCILLATOR (SERIES) -public static partial class Indicator -{ - internal static List CalcMacd( - this List<(DateTime, double)> tpList, - int fastPeriods, - int slowPeriods, - int signalPeriods) - { - // check parameter arguments - ValidateMacd(fastPeriods, slowPeriods, signalPeriods); - - // initialize - List emaFast = tpList.CalcEma(fastPeriods); - List emaSlow = tpList.CalcEma(slowPeriods); - - int length = tpList.Count; - List<(DateTime, double)> emaDiff = []; - List results = new(length); - - // roll through quotes - for (int i = 0; i < length; i++) - { - (DateTime date, double _) = tpList[i]; - EmaResult df = emaFast[i]; - EmaResult ds = emaSlow[i]; - - MacdResult r = new(date) { - FastEma = df.Ema, - SlowEma = ds.Ema - }; - results.Add(r); - - if (i >= slowPeriods - 1) - { - double macd = (df.Ema - ds.Ema).Null2NaN(); - r.Macd = macd.NaN2Null(); - - // temp data for interim EMA of macd - (DateTime, double) diff = (date, macd); - - emaDiff.Add(diff); - } - } - - // add signal and histogram to result - List 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.NaN2Null(); - r.Histogram = (r.Macd - r.Signal).NaN2Null(); - } - - return results; - } - - // parameter validation - private static void ValidateMacd( - int fastPeriods, - int slowPeriods, - int signalPeriods) - { - // check parameter arguments - if (fastPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, - "Fast periods must be greater than 0 for MACD."); - } - - if (signalPeriods < 0) - { - throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, - "Signal periods must be greater than or equal to 0 for MACD."); - } - - if (slowPeriods <= fastPeriods) - { - throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, - "Slow periods must be greater than the fast period for MACD."); - } - } -} diff --git a/src/m-r/Macd/Macd.StaticSeries.cs b/src/m-r/Macd/Macd.StaticSeries.cs new file mode 100644 index 000000000..45c4628d6 --- /dev/null +++ b/src/m-r/Macd/Macd.StaticSeries.cs @@ -0,0 +1,82 @@ +namespace Skender.Stock.Indicators; + +// MOVING AVERAGE CONVERGENCE/DIVERGENCE (MACD) OSCILLATOR (SERIES) + +public static partial class Macd +{ + public static IReadOnlyList ToMacd( + this IReadOnlyList source, + int fastPeriods = 12, + int slowPeriods = 26, + int signalPeriods = 9) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(fastPeriods, slowPeriods, signalPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + double lastEmaFast = double.NaN; + double lastEmaSlow = double.NaN; + double lastEmaMacd = double.NaN; + + double kFast = 2d / (fastPeriods + 1); + double kSlow = 2d / (slowPeriods + 1); + double kMacd = 2d / (signalPeriods + 1); + + // roll through source values + for (int i = 0; i < length; i++) + { + // Fast EMA + double emaFast + = i >= fastPeriods - 1 && results[i - 1].FastEma is null + ? Sma.Increment(source, fastPeriods, i) + : Ema.Increment(kFast, lastEmaFast, source[i].Value); + + // Slow EMA + double emaSlow + = i >= slowPeriods - 1 && results[i - 1].SlowEma is null + ? Sma.Increment(source, slowPeriods, i) + : Ema.Increment(kSlow, lastEmaSlow, source[i].Value); + + // MACD + double macd = emaFast - emaSlow; + + // Signal + double signal; + + if (i >= signalPeriods + slowPeriods - 2 && results[i - 1].Signal is null) + { + double sum = macd; + for (int p = i - signalPeriods + 1; p < i; p++) + { + sum += results[p].Value; + } + + signal = sum / signalPeriods; + } + else + { + signal = Ema.Increment(kMacd, lastEmaMacd, macd); + } + + // results + results.Add(new MacdResult( + Timestamp: source[i].Timestamp, + Macd: macd.NaN2Null(), + Signal: signal.NaN2Null(), + Histogram: (macd - signal).NaN2Null(), + FastEma: emaFast.NaN2Null(), + SlowEma: emaSlow.NaN2Null())); + + lastEmaMacd = signal; + lastEmaFast = emaFast; + lastEmaSlow = emaSlow; + } + + return results; + } +} diff --git a/src/m-r/Macd/Macd.Utilities.cs b/src/m-r/Macd/Macd.Utilities.cs index a09ff5480..4326aac2d 100644 --- a/src/m-r/Macd/Macd.Utilities.cs +++ b/src/m-r/Macd/Macd.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// MOVING AVERAGE CONVERGENCE/DIVERGENCE (MACD) OSCILLATOR (UTILITIES) + +public static partial class Macd { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int n = results .ToList() @@ -14,4 +15,30 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(n + 250); } + + // parameter validation + internal static void Validate( + int fastPeriods, + int slowPeriods, + int signalPeriods) + { + // check parameter arguments + if (fastPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, + "Fast periods must be greater than 0 for MACD."); + } + + if (signalPeriods < 0) + { + throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, + "Signal periods must be greater than or equal to 0 for MACD."); + } + + if (slowPeriods <= fastPeriods) + { + throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, + "Slow periods must be greater than the fast period for MACD."); + } + } } diff --git a/src/m-r/Macd/MacdApi.cs b/src/m-r/Macd/MacdApi.cs deleted file mode 100644 index eeefb3827..000000000 --- a/src/m-r/Macd/MacdApi.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MOVING AVERAGE CONVERGENCE/DIVERGENCE (MACD) OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetMacd( - this IEnumerable quotes, - int fastPeriods = 12, - int slowPeriods = 26, - int signalPeriods = 9) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcMacd(fastPeriods, slowPeriods, signalPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetMacd( - this IEnumerable results, - int fastPeriods = 12, - int slowPeriods = 26, - int signalPeriods = 9) => results - .ToTuple() - .CalcMacd(fastPeriods, slowPeriods, signalPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetMacd( - this IEnumerable<(DateTime, double)> priceTuples, - int fastPeriods = 12, - int slowPeriods = 26, - int signalPeriods = 9) => priceTuples - .ToSortedList() - .CalcMacd(fastPeriods, slowPeriods, signalPeriods); -} diff --git a/src/m-r/Mama/Mama.Api.cs b/src/m-r/Mama/Mama.Api.cs deleted file mode 100644 index 8d30b7fab..000000000 --- a/src/m-r/Mama/Mama.Api.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MOTHER of ADAPTIVE MOVING AVERAGES - MAMA (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetMama( - this IEnumerable quotes, - double fastLimit = 0.5, - double slowLimit = 0.05) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.HL2) - .CalcMama(fastLimit, slowLimit); - - // SERIES, from CHAIN - public static IEnumerable GetMama( - this IEnumerable results, - double fastLimit = 0.5, - double slowLimit = 0.05) => results - .ToTuple() - .CalcMama(fastLimit, slowLimit) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetMama( - this IEnumerable<(DateTime, double)> priceTuples, - double fastLimit = 0.5, - double slowLimit = 0.05) => priceTuples - .ToSortedList() - .CalcMama(fastLimit, slowLimit); -} diff --git a/src/m-r/Mama/Mama.Models.cs b/src/m-r/Mama/Mama.Models.cs index 7b18f3b78..4a3f0f949 100644 --- a/src/m-r/Mama/Mama.Models.cs +++ b/src/m-r/Mama/Mama.Models.cs @@ -1,15 +1,12 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class MamaResult : ResultBase, IReusableResult +public record MamaResult +( + DateTime Timestamp, + double? Mama = null, + double? Fama = null +) : IReusable { - public MamaResult(DateTime date) - { - Date = date; - } - - public double? Mama { get; set; } - public double? Fama { get; set; } - - double? IReusableResult.Value => Mama; + public double Value => Mama.Null2NaN(); } diff --git a/src/m-r/Mama/Mama.Series.cs b/src/m-r/Mama/Mama.StaticSeries.cs similarity index 51% rename from src/m-r/Mama/Mama.Series.cs rename to src/m-r/Mama/Mama.StaticSeries.cs index ebbeaac73..3110c5702 100644 --- a/src/m-r/Mama/Mama.Series.cs +++ b/src/m-r/Mama/Mama.StaticSeries.cs @@ -1,21 +1,28 @@ namespace Skender.Stock.Indicators; -// MOTHER of ADAPTIVE MOVING AVERAGES - MAMA (SERIES) -public static partial class Indicator +// MOTHER of ADAPTIVE MOVING AVERAGES (SERIES) + +public static partial class Mama { - internal static List CalcMama( - this List<(DateTime, double)> tpList, - double fastLimit, - double slowLimit) + public static IReadOnlyList ToMama( + this IReadOnlyList source, + double fastLimit = 0.5, + double slowLimit = 0.05) + where T : IReusable { // check parameter arguments - ValidateMama(fastLimit, slowLimit); + Validate(fastLimit, slowLimit); + + // prefer HL2 when IQuote + IReadOnlyList values + = source.ToPreferredList(CandlePart.HL2); // initialize - int length = tpList.Count; + int length = values.Count; List results = new(length); - double sumPr = 0d; + double prevMama = double.NaN; + double prevFama = double.NaN; double[] pr = new double[length]; // price double[] sm = new double[length]; // smooth @@ -25,9 +32,6 @@ internal static List CalcMama( 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 @@ -36,16 +40,50 @@ internal static List CalcMama( double[] ph = new double[length]; // phase - // roll through quotes + // roll through source values for (int i = 0; i < length; i++) { - (DateTime date, double value) = tpList[i]; - pr[i] = value; + IReusable s = values[i]; + pr[i] = s.Value; + + // skip incalculable periods + if (i < 5) + { + results.Add(new(s.Timestamp)); + continue; + } + + double mama; + double fama; + + // initialization + if (double.IsNaN(prevMama)) + { + double sum = 0; + for (int p = i - 5; p <= i; p++) + { + pd[p] = 0; + sm[p] = 0; + dt[p] = 0; + + i1[p] = 0; + q1[p] = 0; + i2[p] = 0; + q2[p] = 0; + + re[p] = 0; + im[p] = 0; - MamaResult r = new(date); - results.Add(r); + ph[p] = 0; + + sum += pr[p]; + } - if (i > 5) + mama = fama = sum / 6; + } + + // normal MAMA + else { double adj = (0.075 * pd[i - 1]) + 0.54; @@ -58,8 +96,8 @@ internal static List CalcMama( 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; + double jI = ((0.0962 * i1[i]) + (0.5769 * i1[i - 2]) - (0.5769 * i1[i - 4]) - (0.0962 * i1[i - 6])) * adj; + double 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; @@ -78,77 +116,40 @@ internal static List CalcMama( // calculate period pd[i] = im[i] != 0 && re[i] != 0 ? 2 * Math.PI / Math.Atan(im[i] / re[i]) - : 0d; + : 0; // 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]; + 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] < 6 ? 6 : pd[i]; + pd[i] = pd[i] > 50 ? 50 : 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; + 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); + double delta = Math.Max(ph[i - 1] - ph[i], 1); // adaptive alpha value double alpha = Math.Max(fastLimit / delta, slowLimit); // final indicators - r.Mama = ((alpha * pr[i]) + ((1d - alpha) * results[i - 1].Mama)).NaN2Null(); - r.Fama = ((0.5d * alpha * r.Mama) + ((1d - (0.5d * alpha)) * results[i - 1].Fama)).NaN2Null(); + mama = (alpha * pr[i]) + ((1d - alpha) * prevMama); + fama = (0.5 * alpha * mama) + ((1d - (0.5 * alpha)) * prevFama); } - // initialization period - else - { - sumPr += pr[i]; - - if (i == 5) - { - r.Mama = (sumPr / 6d).NaN2Null(); - r.Fama = r.Mama; - } - - pd[i] = 0; - sm[i] = 0; - dt[i] = 0; + results.Add(new MamaResult( + Timestamp: s.Timestamp, + Mama: mama.NaN2Null(), + Fama: fama.NaN2Null())); - i1[i] = 0; - q1[i] = 0; - i2[i] = 0; - q2[i] = 0; - - re[i] = 0; - im[i] = 0; - - ph[i] = 0; - } + prevMama = mama; + prevFama = fama; } return results; } - - // parameter validation - private static void ValidateMama( - double fastLimit, - double slowLimit) - { - // check parameter arguments - if (fastLimit <= slowLimit || fastLimit >= 1) - { - throw new ArgumentOutOfRangeException(nameof(fastLimit), fastLimit, - "Fast Limit must be greater than Slow Limit and less than 1 for MAMA."); - } - - if (slowLimit <= 0) - { - throw new ArgumentOutOfRangeException(nameof(slowLimit), slowLimit, - "Slow Limit must be greater than 0 for MAMA."); - } - } } diff --git a/src/m-r/Mama/Mama.Utilities.cs b/src/m-r/Mama/Mama.Utilities.cs index adae6c5ec..2b9a21126 100644 --- a/src/m-r/Mama/Mama.Utilities.cs +++ b/src/m-r/Mama/Mama.Utilities.cs @@ -1,10 +1,30 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// MOTHER of ADAPTIVE MOVING AVERAGES (UTILITIES) + +public static partial class Mama { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) => results.Remove(50); + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) => results.Remove(50); + + // parameter validation + internal static void Validate( + double fastLimit, + double slowLimit) + { + // check parameter arguments + if (fastLimit <= slowLimit || fastLimit >= 1) + { + throw new ArgumentOutOfRangeException(nameof(fastLimit), fastLimit, + "Fast Limit must be greater than Slow Limit and less than 1 for MAMA."); + } + + if (slowLimit <= 0) + { + throw new ArgumentOutOfRangeException(nameof(slowLimit), slowLimit, + "Slow Limit must be greater than 0 for MAMA."); + } + } } diff --git a/src/m-r/Marubozu/Marubozu.Api.cs b/src/m-r/Marubozu/Marubozu.Api.cs deleted file mode 100644 index bec3d47da..000000000 --- a/src/m-r/Marubozu/Marubozu.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MARUBOZU (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetMarubozu( - this IEnumerable quotes, - double minBodyPercent = 95) - where TQuote : IQuote => quotes - .CalcMarubozu(minBodyPercent); -} diff --git a/src/m-r/Marubozu/Marubozu.Series.cs b/src/m-r/Marubozu/Marubozu.Series.cs deleted file mode 100644 index 6126d2010..000000000 --- a/src/m-r/Marubozu/Marubozu.Series.cs +++ /dev/null @@ -1,54 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MARUBOZU (SERIES) -public static partial class Indicator -{ - /// - /// - internal static List CalcMarubozu( - this IEnumerable quotes, - double minBodyPercent) - where TQuote : IQuote - { - // check parameter arguments - ValidateMarubozu(minBodyPercent); - - // initialize - List results = quotes.ToCandleResults(); - minBodyPercent /= 100; - int length = results.Count; - - // roll through candles - for (int i = 0; i < length; i++) - { - CandleResult r = results[i]; - - // check for current signal - if (r.Candle.BodyPct >= minBodyPercent) - { - r.Price = r.Candle.Close; - r.Match = r.Candle.IsBullish ? Match.BullSignal : Match.BearSignal; - } - } - - return results; - } - - // parameter validation - private static void ValidateMarubozu( - double minBodyPercent) - { - // check parameter arguments - if (minBodyPercent > 100) - { - throw new ArgumentOutOfRangeException(nameof(minBodyPercent), minBodyPercent, - "Minimum Body Percent must be less than 100 for Marubozu (<=100%)."); - } - - if (minBodyPercent < 80) - { - throw new ArgumentOutOfRangeException(nameof(minBodyPercent), minBodyPercent, - "Minimum Body Percent must at least 80 (80%) for Marubozu and is usually greater than 90 (90%)."); - } - } -} diff --git a/src/m-r/Marubozu/Marubozu.StaticSeries.cs b/src/m-r/Marubozu/Marubozu.StaticSeries.cs new file mode 100644 index 000000000..6cf34e122 --- /dev/null +++ b/src/m-r/Marubozu/Marubozu.StaticSeries.cs @@ -0,0 +1,46 @@ +namespace Skender.Stock.Indicators; + +// MARUBOZU (SERIES) + +public static partial class Marubozu +{ + public static IReadOnlyList ToMarubozu( + this IReadOnlyList quotes, + double minBodyPercent = 95) + where TQuote : IQuote + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(quotes); + Validate(minBodyPercent); + + // initialize + int length = quotes.Count; + List results = new(length); + + minBodyPercent /= 100; + + // roll through candles + for (int i = 0; i < length; i++) + { + TQuote q = quotes[i]; + decimal? matchPrice = null; + Match matchType = Match.None; + CandleProperties candle = q.ToCandle(); + + // check for current signal + if (candle.BodyPct >= minBodyPercent) + { + matchPrice = q.Close; + matchType = candle.IsBullish ? Match.BullSignal : Match.BearSignal; + } + + results.Add(new( + timestamp: q.Timestamp, + candle: candle, + match: matchType, + price: matchPrice)); + } + + return results; + } +} diff --git a/src/m-r/Marubozu/Marubozu.Utilities.cs b/src/m-r/Marubozu/Marubozu.Utilities.cs new file mode 100644 index 000000000..7d859c14c --- /dev/null +++ b/src/m-r/Marubozu/Marubozu.Utilities.cs @@ -0,0 +1,28 @@ +namespace Skender.Stock.Indicators; + +// MARUBOZU (UTILITIES) + +public static partial class Marubozu +{ + // parameter validation + internal static void Validate( + double minBodyPercent) + { + // check parameter arguments + if (minBodyPercent > 100) + { + throw new ArgumentOutOfRangeException( + nameof(minBodyPercent), minBodyPercent, + "Minimum Body Percent must be less than 100 " + + "for Marubozu (<=100%)."); + } + + if (minBodyPercent < 80) + { + throw new ArgumentOutOfRangeException( + nameof(minBodyPercent), minBodyPercent, + "Minimum Body Percent must at least 80 (80%) " + + "for Marubozu and is usually greater than 90 (90%)."); + } + } +} diff --git a/src/m-r/Marubozu/info.xml b/src/m-r/Marubozu/info.xml index 531da5f7d..5a7e7a20c 100644 --- a/src/m-r/Marubozu/info.xml +++ b/src/m-r/Marubozu/info.xml @@ -5,7 +5,7 @@ Marubozu is a single candlestick pattern that has no wicks, representing consistent directional movement. See - documentation + documentation for more information. @@ -14,4 +14,4 @@ Optional. Minimum candle body size as percentage. Time series of Marubozu values. Invalid parameter value provided. - \ No newline at end of file + diff --git a/src/m-r/Mfi/Mfi.Api.cs b/src/m-r/Mfi/Mfi.Api.cs deleted file mode 100644 index db59c6350..000000000 --- a/src/m-r/Mfi/Mfi.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MONEY FLOW INDEX (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetMfi( - this IEnumerable quotes, - int lookbackPeriods = 14) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcMfi(lookbackPeriods); -} diff --git a/src/m-r/Mfi/Mfi.Models.cs b/src/m-r/Mfi/Mfi.Models.cs index e4d133454..0a0c8f29b 100644 --- a/src/m-r/Mfi/Mfi.Models.cs +++ b/src/m-r/Mfi/Mfi.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class MfiResult : ResultBase, IReusableResult +public record MfiResult +( + DateTime Timestamp, + double? Mfi +) : IReusable { - public MfiResult(DateTime date) - { - Date = date; - } - - public double? Mfi { get; set; } - - double? IReusableResult.Value => Mfi; + public double Value => Mfi.Null2NaN(); } diff --git a/src/m-r/Mfi/Mfi.Series.cs b/src/m-r/Mfi/Mfi.Series.cs deleted file mode 100644 index f47115438..000000000 --- a/src/m-r/Mfi/Mfi.Series.cs +++ /dev/null @@ -1,101 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MONEY FLOW INDEX (SERIES) -public static partial class Indicator -{ - internal static List CalcMfi( - this List qdList, - int lookbackPeriods) - { - // check parameter arguments - ValidateMfi(lookbackPeriods); - - // initialize - int length = qdList.Count; - List results = new(length); - double[] tp = new double[length]; // true price - double[] mf = new double[length]; // raw MF value - int[] direction = new int[length]; // direction - - double? prevTP = null; - - // roll through quotes, to get preliminary data - for (int i = 0; i < qdList.Count; i++) - { - QuoteD q = qdList[i]; - - MfiResult r = new(q.Date); - results.Add(r); - - // true price - tp[i] = (q.High + q.Low + q.Close) / 3; - - // raw money flow - mf[i] = tp[i] * q.Volume; - - // direction - if (prevTP == null || tp[i] == prevTP) - { - direction[i] = 0; - } - else if (tp[i] > prevTP) - { - direction[i] = 1; - } - else if (tp[i] < prevTP) - { - direction[i] = -1; - } - - prevTP = tp[i]; - } - - // add money flow index - for (int i = lookbackPeriods; i < results.Count; i++) - { - MfiResult r = results[i]; - - double sumPosMFs = 0; - double sumNegMFs = 0; - - for (int p = i + 1 - lookbackPeriods; p <= i; p++) - { - if (direction[p] == 1) - { - sumPosMFs += mf[p]; - } - else if (direction[p] == -1) - { - sumNegMFs += mf[p]; - } - } - - // calculate MFI normally - if (sumNegMFs != 0) - { - double? mfRatio = sumPosMFs / sumNegMFs; - r.Mfi = 100 - (100 / (1 + mfRatio)); - } - - // handle no negative case - else - { - r.Mfi = 100; - } - } - - return results; - } - - // parameter validation - private static void ValidateMfi( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for MFI."); - } - } -} diff --git a/src/m-r/Mfi/Mfi.StaticSeries.cs b/src/m-r/Mfi/Mfi.StaticSeries.cs new file mode 100644 index 000000000..2d9043a7c --- /dev/null +++ b/src/m-r/Mfi/Mfi.StaticSeries.cs @@ -0,0 +1,102 @@ +namespace Skender.Stock.Indicators; + +// MONEY FLOW INDEX (SERIES) + +public static partial class Mfi +{ + public static IReadOnlyList ToMfi( + this IReadOnlyList quotes, + int lookbackPeriods = 14) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcMfi(lookbackPeriods); + + private static List CalcMfi( + this IReadOnlyList source, + int lookbackPeriods) + { + // check parameter arguments + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + double[] tp = new double[length]; // true price + double[] mf = new double[length]; // raw MF value + int[] direction = new int[length]; // direction + + double? prevTp = null; + + // roll through source values, to get preliminary data + for (int i = 0; i < length; i++) + { + QuoteD q = source[i]; + double mfi; + + // true price + tp[i] = (q.High + q.Low + q.Close) / 3; + + // raw money flow + mf[i] = tp[i] * q.Volume; + + // direction + if (prevTp == null || tp[i] == prevTp) + { + direction[i] = 0; + } + else if (tp[i] > prevTp) + { + direction[i] = 1; + } + else if (tp[i] < prevTp) + { + direction[i] = -1; + } + + // add money flow index + if (i >= lookbackPeriods) + { + double sumPosMFs = 0; + double sumNegMFs = 0; + + for (int p = i + 1 - lookbackPeriods; p <= i; p++) + { + if (direction[p] == 1) + { + sumPosMFs += mf[p]; + } + else if (direction[p] == -1) + { + sumNegMFs += mf[p]; + } + } + + // calculate MFI normally + if (sumNegMFs != 0) + { + double mfRatio = sumPosMFs / sumNegMFs; + mfi = 100 - 100 / (1 + mfRatio); + } + + // handle no negative case + else + { + mfi = 100; + } + } + else + { + mfi = double.NaN; + } + + results.Add(new( + Timestamp: q.Timestamp, + Mfi: mfi.NaN2Null())); + + prevTp = tp[i]; + } + + return results; + } +} diff --git a/src/m-r/Mfi/Mfi.Utilities.cs b/src/m-r/Mfi/Mfi.Utilities.cs index 8177422d0..e4768b450 100644 --- a/src/m-r/Mfi/Mfi.Utilities.cs +++ b/src/m-r/Mfi/Mfi.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// MONEY FLOW INDEX (UTILITIES) + +public static partial class Mfi { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -14,4 +15,16 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for MFI."); + } + } } diff --git a/src/m-r/Obv/Obv.Api.cs b/src/m-r/Obv/Obv.Api.cs deleted file mode 100644 index 099233100..000000000 --- a/src/m-r/Obv/Obv.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ON-BALANCE VOLUME (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetObv( - this IEnumerable quotes, - int? smaPeriods = null) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcObv(smaPeriods); -} diff --git a/src/m-r/Obv/Obv.Models.cs b/src/m-r/Obv/Obv.Models.cs index 2f315ae4a..458caa43a 100644 --- a/src/m-r/Obv/Obv.Models.cs +++ b/src/m-r/Obv/Obv.Models.cs @@ -1,15 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class ObvResult : ResultBase, IReusableResult +public record ObvResult +( + DateTime Timestamp, + double Obv +) : IReusable { - public ObvResult(DateTime date) - { - Date = date; - } - - public double Obv { get; set; } - public double? ObvSma { get; set; } - - double? IReusableResult.Value => Obv; + public double Value => Obv; } diff --git a/src/m-r/Obv/Obv.Series.cs b/src/m-r/Obv/Obv.Series.cs deleted file mode 100644 index d50ba1007..000000000 --- a/src/m-r/Obv/Obv.Series.cs +++ /dev/null @@ -1,71 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ON-BALANCE VOLUME (SERIES) -public static partial class Indicator -{ - internal static List CalcObv( - this List qdList, - int? smaPeriods) - { - // check parameter arguments - ValidateObv(smaPeriods); - - // initialize - List results = new(qdList.Count); - - double prevClose = double.NaN; - double obv = 0; - - // roll through quotes - for (int i = 0; i < qdList.Count; i++) - { - QuoteD q = qdList[i]; - - if (double.IsNaN(prevClose) || q.Close == prevClose) - { - // no change to OBV - } - else if (q.Close > prevClose) - { - obv += q.Volume; - } - else if (q.Close < prevClose) - { - obv -= q.Volume; - } - - ObvResult r = new(q.Date) { - Obv = obv - }; - results.Add(r); - - prevClose = q.Close; - - // optional SMA - if (smaPeriods != null && i + 1 > smaPeriods) - { - double? sumSma = 0; - for (int p = i + 1 - (int)smaPeriods; p <= i; p++) - { - sumSma += results[p].Obv; - } - - r.ObvSma = sumSma / smaPeriods; - } - } - - return results; - } - - // parameter validation - private static void ValidateObv( - int? smaPeriods) - { - // check parameter arguments - if (smaPeriods is not null and <= 0) - { - throw new ArgumentOutOfRangeException(nameof(smaPeriods), smaPeriods, - "SMA periods must be greater than 0 for OBV."); - } - } -} diff --git a/src/m-r/Obv/Obv.StaticSeries.cs b/src/m-r/Obv/Obv.StaticSeries.cs new file mode 100644 index 000000000..48456b11d --- /dev/null +++ b/src/m-r/Obv/Obv.StaticSeries.cs @@ -0,0 +1,50 @@ +namespace Skender.Stock.Indicators; + +// ON-BALANCE VOLUME (SERIES) + +public static partial class Obv +{ + public static IReadOnlyList ToObv( + this IReadOnlyList quotes) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcObv(); + + private static List CalcObv( + this IReadOnlyList source) + { + // initialize + int length = source.Count; + List results = new(length); + + double prevClose = double.NaN; + double obv = 0; + + // roll through source values + for (int i = 0; i < length; i++) + { + QuoteD q = source[i]; + + if (double.IsNaN(prevClose) || q.Close == prevClose) + { + // no change to OBV + } + else if (q.Close > prevClose) + { + obv += q.Volume; + } + else if (q.Close < prevClose) + { + obv -= q.Volume; + } + + results.Add(new ObvResult( + Timestamp: q.Timestamp, + Obv: obv)); + + prevClose = q.Close; + } + + return results; + } +} diff --git a/src/m-r/Obv/info.xml b/src/m-r/Obv/info.xml index 7ce02375d..378ee0b7f 100644 --- a/src/m-r/Obv/info.xml +++ b/src/m-r/Obv/info.xml @@ -11,7 +11,6 @@ Configurable Quote type. See Guide for more information. Historical price quotes. - Optional. Number of periods for an SMA of the OBV line. Time series of OBV values. Invalid parameter value provided. \ No newline at end of file diff --git a/src/m-r/ParabolicSar/ParabolicSar.Api.cs b/src/m-r/ParabolicSar/ParabolicSar.Api.cs deleted file mode 100644 index 6bec07060..000000000 --- a/src/m-r/ParabolicSar/ParabolicSar.Api.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PARABOLIC SAR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetParabolicSar( - this IEnumerable quotes, - double accelerationStep = 0.02, - double maxAccelerationFactor = 0.2) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcParabolicSar( - accelerationStep, - maxAccelerationFactor, - accelerationStep); - - // SERIES, from TQuote (alt) - /// - /// - public static IEnumerable GetParabolicSar( - this IEnumerable quotes, - double accelerationStep, - double maxAccelerationFactor, - double initialFactor) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcParabolicSar( - accelerationStep, - maxAccelerationFactor, - initialFactor); -} diff --git a/src/m-r/ParabolicSar/ParabolicSar.Models.cs b/src/m-r/ParabolicSar/ParabolicSar.Models.cs index 6b70d76b7..8fad331a8 100644 --- a/src/m-r/ParabolicSar/ParabolicSar.Models.cs +++ b/src/m-r/ParabolicSar/ParabolicSar.Models.cs @@ -1,15 +1,12 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class ParabolicSarResult : ResultBase, IReusableResult +public record ParabolicSarResult +( + DateTime Timestamp, + double? Sar = null, + bool? IsReversal = null +) : IReusable { - public ParabolicSarResult(DateTime date) - { - Date = date; - } - - public double? Sar { get; set; } - public bool? IsReversal { get; set; } - - double? IReusableResult.Value => Sar; + public double Value => Sar.Null2NaN(); } diff --git a/src/m-r/ParabolicSar/ParabolicSar.Series.cs b/src/m-r/ParabolicSar/ParabolicSar.StaticSeries.cs similarity index 55% rename from src/m-r/ParabolicSar/ParabolicSar.Series.cs rename to src/m-r/ParabolicSar/ParabolicSar.StaticSeries.cs index 67e7cf10c..53eca2d91 100644 --- a/src/m-r/ParabolicSar/ParabolicSar.Series.cs +++ b/src/m-r/ParabolicSar/ParabolicSar.StaticSeries.cs @@ -1,48 +1,70 @@ namespace Skender.Stock.Indicators; // PARABOLIC SAR (SERIES) -public static partial class Indicator + +public static partial class ParabolicSar { - internal static List CalcParabolicSar( - this List qdList, + public static IReadOnlyList ToParabolicSar( + this IReadOnlyList quotes, + double accelerationStep = 0.02, + double maxAccelerationFactor = 0.2) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcParabolicSar( + accelerationStep, + maxAccelerationFactor, + accelerationStep); + + public static IReadOnlyList GetParabolicSar( + this IReadOnlyList quotes, + double accelerationStep, + double maxAccelerationFactor, + double initialFactor) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcParabolicSar( + accelerationStep, + maxAccelerationFactor, + initialFactor); + + private static List CalcParabolicSar( + this IReadOnlyList source, double accelerationStep, double maxAccelerationFactor, double initialFactor) { // check parameter arguments - ValidateParabolicSar( + Validate( accelerationStep, maxAccelerationFactor, initialFactor); // initialize - int length = qdList.Count; + int length = source.Count; List results = new(length); - QuoteD q0; if (length == 0) { return results; } - else - { - q0 = qdList[0]; - } + + QuoteD q0 = source[0]; double accelerationFactor = initialFactor; double extremePoint = q0.High; double priorSar = q0.Low; bool isRising = true; // initial guess - // roll through quotes + // roll through source values for (int i = 0; i < length; i++) { - QuoteD q = qdList[i]; + QuoteD q = source[i]; - ParabolicSarResult r = new(q.Date); - results.Add(r); + bool? isReversal; + double psar; // skip first one if (i == 0) { + results.Add(new(q.Timestamp)); continue; } @@ -57,8 +79,8 @@ internal static List CalcParabolicSar( { double minLastTwo = Math.Min( - qdList[i - 1].Low, - qdList[i - 2].Low); + source[i - 1].Low, + source[i - 2].Low); sar = Math.Min(sar, minLastTwo); } @@ -66,8 +88,8 @@ internal static List CalcParabolicSar( // turn down if (q.Low < sar) { - r.IsReversal = true; - r.Sar = extremePoint; + isReversal = true; + psar = extremePoint; isRising = false; accelerationFactor = initialFactor; @@ -77,8 +99,8 @@ internal static List CalcParabolicSar( // continue rising else { - r.IsReversal = false; - r.Sar = sar; + isReversal = false; + psar = sar; // new high extreme point if (q.High > extremePoint) @@ -102,8 +124,8 @@ double sar if (i >= 2) { double maxLastTwo = Math.Max( - qdList[i - 1].High, - qdList[i - 2].High); + source[i - 1].High, + source[i - 2].High); sar = Math.Max(sar, maxLastTwo); } @@ -111,8 +133,8 @@ double sar // turn up if (q.High > sar) { - r.IsReversal = true; - r.Sar = extremePoint; + isReversal = true; + psar = extremePoint; isRising = true; accelerationFactor = initialFactor; @@ -122,8 +144,8 @@ double sar // continue falling else { - r.IsReversal = false; - r.Sar = sar; + isReversal = false; + psar = sar; // new low extreme point if (q.Low < extremePoint) @@ -137,62 +159,29 @@ double sar } } - priorSar = (double)r.Sar; + results.Add(new ParabolicSarResult( + Timestamp: q.Timestamp, + Sar: psar.NaN2Null(), + IsReversal: isReversal)); + + priorSar = psar; } // remove first trendline since it is an invalid guess - ParabolicSarResult? firstReversal = results - .Where(x => x.IsReversal == true) - .OrderBy(x => x.Date) - .FirstOrDefault(); + int cutIndex = results.FindIndex(x => x.IsReversal ?? false); - int cutIndex = (firstReversal != null) - ? results.IndexOf(firstReversal) - : length - 1; + cutIndex = cutIndex < 0 ? length - 1 : cutIndex; for (int d = 0; d <= cutIndex; d++) { - ParabolicSarResult r = results[d]; - r.Sar = null; - r.IsReversal = null; - } - - return results; - } - - // parameter validation - private static void ValidateParabolicSar( - double accelerationStep, - double maxAccelerationFactor, - double initialFactor) - { - // check parameter arguments - if (accelerationStep <= 0) - { - throw new ArgumentOutOfRangeException(nameof(accelerationStep), accelerationStep, - "Acceleration Step must be greater than 0 for Parabolic SAR."); - } - - if (maxAccelerationFactor <= 0) - { - throw new ArgumentOutOfRangeException(nameof(maxAccelerationFactor), maxAccelerationFactor, - "Max Acceleration Factor must be greater than 0 for Parabolic SAR."); - } - - if (accelerationStep > maxAccelerationFactor) - { - string message = string.Format( - invCulture, - "Acceleration Step cannot be larger than the Max Acceleration Factor ({0}) for Parabolic SAR.", - maxAccelerationFactor); + ParabolicSarResult r = results[d] with { + Sar = null, + IsReversal = null + }; - throw new ArgumentOutOfRangeException(nameof(accelerationStep), accelerationStep, message); + results[d] = r; } - if (initialFactor <= 0 || initialFactor > maxAccelerationFactor) - { - throw new ArgumentOutOfRangeException(nameof(initialFactor), initialFactor, - "Initial Factor must be greater than 0 and not larger than Max Acceleration Factor for Parabolic SAR."); - } + return results; } } diff --git a/src/m-r/ParabolicSar/ParabolicSar.Utilities.cs b/src/m-r/ParabolicSar/ParabolicSar.Utilities.cs index 850844880..afa732021 100644 --- a/src/m-r/ParabolicSar/ParabolicSar.Utilities.cs +++ b/src/m-r/ParabolicSar/ParabolicSar.Utilities.cs @@ -1,17 +1,46 @@ +using System.Globalization; + namespace Skender.Stock.Indicators; -public static partial class Indicator +// PARABOLIC SAR (UTILITIES) + +public static partial class ParabolicSar { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + private static readonly CultureInfo invariantCulture = CultureInfo.InvariantCulture; + + // parameter validation + internal static void Validate( + double accelerationStep, + double maxAccelerationFactor, + double initialFactor) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Sar != null); + // check parameter arguments + if (accelerationStep <= 0) + { + throw new ArgumentOutOfRangeException(nameof(accelerationStep), accelerationStep, + "Acceleration Step must be greater than 0 for Parabolic SAR."); + } + + if (maxAccelerationFactor <= 0) + { + throw new ArgumentOutOfRangeException(nameof(maxAccelerationFactor), maxAccelerationFactor, + "Max Acceleration Factor must be greater than 0 for Parabolic SAR."); + } + + if (accelerationStep > maxAccelerationFactor) + { + string message = string.Format( + invariantCulture, + "Acceleration Step cannot be larger than the Max Acceleration Factor ({0}) for Parabolic SAR.", + maxAccelerationFactor); + + throw new ArgumentOutOfRangeException(nameof(accelerationStep), accelerationStep, message); + } - return results.Remove(removePeriods); + if (initialFactor <= 0 || initialFactor > maxAccelerationFactor) + { + throw new ArgumentOutOfRangeException(nameof(initialFactor), initialFactor, + "Initial Factor must be greater than 0 and not larger than Max Acceleration Factor for Parabolic SAR."); + } } } diff --git a/src/m-r/PivotPoints/PivotPoints.Api.cs b/src/m-r/PivotPoints/PivotPoints.Api.cs deleted file mode 100644 index 95e7d0fe9..000000000 --- a/src/m-r/PivotPoints/PivotPoints.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PIVOT POINTS (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetPivotPoints( - this IEnumerable quotes, - PeriodSize windowSize, - PivotPointType pointType = PivotPointType.Standard) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcPivotPoints(windowSize, pointType); -} diff --git a/src/m-r/PivotPoints/PivotPoints.Models.cs b/src/m-r/PivotPoints/PivotPoints.Models.cs index 5a8a6de8f..f14c31cdc 100644 --- a/src/m-r/PivotPoints/PivotPoints.Models.cs +++ b/src/m-r/PivotPoints/PivotPoints.Models.cs @@ -2,31 +2,51 @@ namespace Skender.Stock.Indicators; internal interface IPivotPoint { - public decimal? R4 { get; set; } - public decimal? R3 { get; set; } - public decimal? R2 { get; set; } - public decimal? R1 { get; set; } - public decimal? PP { get; set; } - public decimal? S1 { get; set; } - public decimal? S2 { get; set; } - public decimal? S3 { get; set; } - public decimal? S4 { get; set; } + decimal? R4 { get; } + decimal? R3 { get; } + decimal? R2 { get; } + decimal? R1 { get; } + decimal? PP { get; } + decimal? S1 { get; } + decimal? S2 { get; } + decimal? S3 { get; } + decimal? S4 { get; } } [Serializable] -public sealed class PivotPointsResult : ResultBase, IPivotPoint +public record PivotPointsResult : ISeries, IPivotPoint { - public decimal? R4 { get; set; } - public decimal? R3 { get; set; } - public decimal? R2 { get; set; } - public decimal? R1 { get; set; } - public decimal? PP { get; set; } - public decimal? S1 { get; set; } - public decimal? S2 { get; set; } - public decimal? S3 { get; set; } - public decimal? S4 { get; set; } + public DateTime Timestamp { get; init; } + + public decimal? PP { get; init; } + + public decimal? S1 { get; init; } + public decimal? S2 { get; init; } + public decimal? S3 { get; init; } + public decimal? S4 { get; init; } + + public decimal? R1 { get; init; } + public decimal? R2 { get; init; } + public decimal? R3 { get; init; } + public decimal? R4 { get; init; } } +internal record WindowPoint : IPivotPoint +{ + public decimal? PP { get; init; } + + public decimal? S1 { get; init; } + public decimal? S2 { get; init; } + public decimal? S3 { get; init; } + public decimal? S4 { get; init; } + + public decimal? R1 { get; init; } + public decimal? R2 { get; init; } + public decimal? R3 { get; init; } + public decimal? R4 { get; init; } +} + + public enum PivotPointType { // do not modify numbers, diff --git a/src/m-r/PivotPoints/PivotPoints.Series.cs b/src/m-r/PivotPoints/PivotPoints.StaticSeries.cs similarity index 52% rename from src/m-r/PivotPoints/PivotPoints.Series.cs rename to src/m-r/PivotPoints/PivotPoints.StaticSeries.cs index 98b8f5e85..7b540b12f 100644 --- a/src/m-r/PivotPoints/PivotPoints.Series.cs +++ b/src/m-r/PivotPoints/PivotPoints.StaticSeries.cs @@ -1,32 +1,32 @@ namespace Skender.Stock.Indicators; // PIVOT POINTS (SERIES) -public static partial class Indicator + +public static partial class PivotPoints { - internal static List CalcPivotPoints( - this List quotesList, + public static IReadOnlyList ToPivotPoints( + this IReadOnlyList quotes, PeriodSize windowSize, - PivotPointType pointType) + PivotPointType pointType = PivotPointType.Standard) where TQuote : IQuote { + ArgumentNullException.ThrowIfNull(quotes); + // initialize - int length = quotesList.Count; + int length = quotes.Count; List results = new(length); - PivotPointsResult? windowPoint = new(); - TQuote h0; + + WindowPoint windowPoint = new(); if (length == 0) { return results; } - else - { - h0 = quotesList[0]; - } - int windowId = GetWindowNumber(h0.Date, windowSize); + TQuote h0 = quotes[0]; + + int windowId = GetWindowNumber(h0.Timestamp, windowSize); - int windowEval; bool firstWindow = true; decimal windowHigh = h0.High; @@ -34,17 +34,13 @@ internal static List CalcPivotPoints( decimal windowOpen = h0.Open; decimal windowClose = h0.Close; - // roll through quotes + // roll through source values for (int i = 0; i < length; i++) { - TQuote q = quotesList[i]; - - PivotPointsResult r = new() { - Date = q.Date - }; + TQuote q = quotes[i]; // new window evaluation - windowEval = GetWindowNumber(q.Date, windowSize); + int windowEval = GetWindowNumber(q.Timestamp, windowSize); if (windowEval != windowId) { @@ -57,7 +53,7 @@ internal static List CalcPivotPoints( windowOpen = q.Open; } - windowPoint = GetPivotPoint( + windowPoint = GetPivotPoint( pointType, windowOpen, windowHigh, windowLow, windowClose); // reset window min/max thresholds @@ -67,43 +63,85 @@ internal static List CalcPivotPoints( } // add levels - if (!firstWindow) - { - // pivot point - r.PP = windowPoint?.PP; - - // support - r.S1 = windowPoint?.S1; - r.S2 = windowPoint?.S2; - r.S3 = windowPoint?.S3; - r.S4 = windowPoint?.S4; - - // resistance - r.R1 = windowPoint?.R1; - r.R2 = windowPoint?.R2; - r.R3 = windowPoint?.R3; - r.R4 = windowPoint?.R4; - } + PivotPointsResult r + = !firstWindow + ? new() { + + Timestamp = q.Timestamp, + + // pivot point + PP = windowPoint.PP, + + // support + S1 = windowPoint.S1, + S2 = windowPoint.S2, + S3 = windowPoint.S3, + S4 = windowPoint.S4, + + // resistance + R1 = windowPoint.R1, + R2 = windowPoint.R2, + R3 = windowPoint.R3, + R4 = windowPoint.R4 + } + : new PivotPointsResult { + Timestamp = q.Timestamp + }; results.Add(r); // capture window threholds (for next iteration) - windowHigh = (q.High > windowHigh) ? q.High : windowHigh; - windowLow = (q.Low < windowLow) ? q.Low : windowLow; + windowHigh = q.High > windowHigh ? q.High : windowHigh; + windowLow = q.Low < windowLow ? q.Low : windowLow; windowClose = q.Close; } return results; } - // internals - internal static TPivotPoint GetPivotPointStandard( + // pivot point lookup + internal static WindowPoint GetPivotPoint( + PivotPointType pointType, decimal open, decimal high, decimal low, decimal close) + => pointType switch { + + PivotPointType.Standard => GetPivotPointStandard(high, low, close), + PivotPointType.Camarilla => GetPivotPointCamarilla(high, low, close), + PivotPointType.Demark => GetPivotPointDemark(open, high, low, close), + PivotPointType.Fibonacci => GetPivotPointFibonacci(high, low, close), + PivotPointType.Woodie => GetPivotPointWoodie(open, high, low), + + _ => throw new ArgumentOutOfRangeException( + nameof(pointType), pointType, "Invalid pointType provided.") + }; + + // window size lookup + private static int GetWindowNumber(DateTime d, PeriodSize windowSize) + => windowSize switch { + + PeriodSize.Month => d.Month, + + PeriodSize.Week => calendar.GetWeekOfYear( + d, calendarWeekRule, firstDayOfWeek), + + PeriodSize.Day => d.Day, + PeriodSize.OneHour => d.Hour, + + _ => throw new ArgumentOutOfRangeException( + nameof(windowSize), windowSize, + string.Format( + invariantCulture, + "Pivot Points does not support PeriodSize of {0}. " + + "See documentation for valid options.", + Enum.GetName(typeof(PeriodSize), windowSize))) + }; + + // pivot point variants + private static WindowPoint GetPivotPointStandard( decimal high, decimal low, decimal close) - where TPivotPoint : IPivotPoint, new() { decimal pp = (high + low + close) / 3; - return new TPivotPoint { + return new() { PP = pp, S1 = (pp * 2) - high, S2 = pp - (high - low), @@ -114,9 +152,8 @@ internal static TPivotPoint GetPivotPointStandard( }; } - internal static TPivotPoint GetPivotPointCamarilla( + private static WindowPoint GetPivotPointCamarilla( decimal high, decimal low, decimal close) - where TPivotPoint : IPivotPoint, new() => new() { PP = close, S1 = close - (1.1m / 12 * (high - low)), @@ -129,39 +166,28 @@ internal static TPivotPoint GetPivotPointCamarilla( R4 = close + (1.1m / 2 * (high - low)) }; - internal static TPivotPoint GetPivotPointDemark( + internal static WindowPoint GetPivotPointDemark( decimal open, decimal high, decimal low, decimal close) - where TPivotPoint : IPivotPoint, new() { - decimal? x; + decimal x = close < open + ? high + (2 * low) + close + : close > open + ? (2 * high) + low + close + : high + low + (2 * close); - if (close < open) - { - x = high + (2 * low) + close; - } - else if (close > open) - { - x = (2 * high) + low + close; - } - else // close == open - { - x = high + low + (2 * close); - } - - return new TPivotPoint { + return new() { PP = x / 4, S1 = (x / 2) - high, R1 = (x / 2) - low }; } - internal static TPivotPoint GetPivotPointFibonacci( + private static WindowPoint GetPivotPointFibonacci( decimal high, decimal low, decimal close) - where TPivotPoint : IPivotPoint, new() { decimal pp = (high + low + close) / 3; - return new TPivotPoint { + return new() { PP = pp, S1 = pp - (0.382m * (high - low)), S2 = pp - (0.618m * (high - low)), @@ -172,47 +198,19 @@ internal static TPivotPoint GetPivotPointFibonacci( }; } - internal static TPivotPoint GetPivotPointWoodie( + private static WindowPoint GetPivotPointWoodie( decimal currentOpen, decimal high, decimal low) - where TPivotPoint : IPivotPoint, new() { decimal pp = (high + low + (2 * currentOpen)) / 4; - return new TPivotPoint { + return new() { PP = pp, S1 = (pp * 2) - high, S2 = pp - high + low, S3 = low - (2 * (high - pp)), R1 = (pp * 2) - low, R2 = pp + high - low, - R3 = high + (2 * (pp - low)), + R3 = high + (2 * (pp - low)) }; } - - // pivot type lookup - internal static TPivotPoint GetPivotPoint( - PivotPointType pointType, decimal open, decimal high, decimal low, decimal close) - where TPivotPoint : IPivotPoint, new() - => pointType switch { - PivotPointType.Standard => GetPivotPointStandard(high, low, close), - PivotPointType.Camarilla => GetPivotPointCamarilla(high, low, close), - PivotPointType.Demark => GetPivotPointDemark(open, high, low, close), - PivotPointType.Fibonacci => GetPivotPointFibonacci(high, low, close), - PivotPointType.Woodie => GetPivotPointWoodie(open, high, low), - _ => throw new ArgumentOutOfRangeException(nameof(pointType), pointType, "Invalid pointType provided.") - }; - - // window size lookup - private static int GetWindowNumber(DateTime d, PeriodSize windowSize) - => windowSize switch { - PeriodSize.Month => d.Month, - PeriodSize.Week => invCalendar.GetWeekOfYear(d, invCalendarWeekRule, invFirstDayOfWeek), - PeriodSize.Day => d.Day, - PeriodSize.OneHour => d.Hour, - _ => throw new ArgumentOutOfRangeException(nameof(windowSize), windowSize, - string.Format( - invCulture, - "Pivot Points does not support PeriodSize of {0}. See documentation for valid options.", - Enum.GetName(typeof(PeriodSize), windowSize))) - }; } diff --git a/src/m-r/PivotPoints/PivotPoints.Utilities.cs b/src/m-r/PivotPoints/PivotPoints.Utilities.cs index bbd81b5c2..c562b0da9 100644 --- a/src/m-r/PivotPoints/PivotPoints.Utilities.cs +++ b/src/m-r/PivotPoints/PivotPoints.Utilities.cs @@ -1,16 +1,29 @@ +using System.Globalization; + namespace Skender.Stock.Indicators; // PIVOT POINTS (UTILITIES) -public static partial class Indicator + +public static partial class PivotPoints { + private static readonly CultureInfo invariantCulture = CultureInfo.InvariantCulture; + + private static readonly Calendar calendar = invariantCulture.Calendar; + + // Gets the DTFI properties required by GetWeekOfYear. + private static readonly CalendarWeekRule calendarWeekRule + = invariantCulture.DateTimeFormat.CalendarWeekRule; + + private static readonly DayOfWeek firstDayOfWeek + = invariantCulture.DateTimeFormat.FirstDayOfWeek; + // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results - .ToList() + .ToList() // TODO: is there a no-copy way to do this? Many places. .FindIndex(x => x.PP != null); return results.Remove(removePeriods); diff --git a/src/m-r/Pivots/Pivots.Api.cs b/src/m-r/Pivots/Pivots.Api.cs deleted file mode 100644 index 6dc75d34e..000000000 --- a/src/m-r/Pivots/Pivots.Api.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PIVOTS (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetPivots( - this IEnumerable quotes, - int leftSpan = 2, - int rightSpan = 2, - int maxTrendPeriods = 20, - EndType endType = EndType.HighLow) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcPivots(leftSpan, rightSpan, maxTrendPeriods, endType); -} diff --git a/src/m-r/Pivots/Pivots.Models.cs b/src/m-r/Pivots/Pivots.Models.cs index d8231c267..8561851c7 100644 --- a/src/m-r/Pivots/Pivots.Models.cs +++ b/src/m-r/Pivots/Pivots.Models.cs @@ -1,25 +1,21 @@ namespace Skender.Stock.Indicators; [Serializable] -public class PivotsResult : ResultBase -{ - public PivotsResult(DateTime date) - { - Date = date; - } - - public decimal? HighPoint { get; set; } - public decimal? LowPoint { get; set; } - public decimal? HighLine { get; set; } - public decimal? LowLine { get; set; } - public PivotTrend? HighTrend { get; set; } - public PivotTrend? LowTrend { get; set; } -} +public record struct PivotsResult +( + DateTime Timestamp, + decimal? HighPoint, + decimal? LowPoint, + decimal? HighLine, + decimal? LowLine, + PivotTrend? HighTrend, + PivotTrend? LowTrend +) : ISeries; public enum PivotTrend { - HH, // higher high - LH, // lower high - HL, // higher low - LL // lower low + Hh, // higher high + Lh, // lower high + Hl, // higher low + Ll // lower low } diff --git a/src/m-r/Pivots/Pivots.Series.cs b/src/m-r/Pivots/Pivots.Series.cs deleted file mode 100644 index b19f7e0f3..000000000 --- a/src/m-r/Pivots/Pivots.Series.cs +++ /dev/null @@ -1,135 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PIVOTS (SERIES) -public static partial class Indicator -{ - internal static List CalcPivots( - this List quotesList, - int leftSpan, - int rightSpan, - int maxTrendPeriods, - EndType endType) - where TQuote : IQuote - { - // check parameter arguments - ValidatePivots(leftSpan, rightSpan, maxTrendPeriods); - - // initialize - - List results - = quotesList - .CalcFractal(leftSpan, rightSpan, endType) - .Select(x => new PivotsResult(x.Date) { - HighPoint = x.FractalBear, - LowPoint = x.FractalBull - }) - .ToList(); - - int? lastHighIndex = null; - decimal? lastHighValue = null; - int? lastLowIndex = null; - decimal? lastLowValue = null; - - // roll through results - for (int i = leftSpan; i <= results.Count - rightSpan; i++) - { - PivotsResult r = results[i]; - - // reset expired indexes - if (lastHighIndex < i - maxTrendPeriods) - { - lastHighIndex = null; - lastHighValue = null; - } - - if (lastLowIndex < i - maxTrendPeriods) - { - lastLowIndex = null; - lastLowValue = null; - } - - // evaluate high trend - if (r.HighPoint != null) - { - // repaint trend - if (lastHighIndex != null && r.HighPoint != lastHighValue) - { - PivotTrend trend = (r.HighPoint > lastHighValue) - ? PivotTrend.HH - : PivotTrend.LH; - - results[(int)lastHighIndex].HighLine = lastHighValue; - - decimal? incr = (r.HighPoint - lastHighValue) - / (i - lastHighIndex); - - for (int t = (int)lastHighIndex + 1; t <= i; t++) - { - results[t].HighTrend = trend; - results[t].HighLine = r.HighPoint + (incr * (t - i)); - } - } - - // reset starting position - lastHighIndex = i; - lastHighValue = r.HighPoint; - } - - // evaluate low trend - if (r.LowPoint != null) - { - // repaint trend - if (lastLowIndex != null && r.LowPoint != lastLowValue) - { - PivotTrend trend = (r.LowPoint > lastLowValue) - ? PivotTrend.HL - : PivotTrend.LL; - - results[(int)lastLowIndex].LowLine = lastLowValue; - - decimal? incr = (r.LowPoint - lastLowValue) - / (i - lastLowIndex); - - for (int t = (int)lastLowIndex + 1; t <= i; t++) - { - results[t].LowTrend = trend; - results[t].LowLine = r.LowPoint + (incr * (t - i)); - } - } - - // reset starting position - lastLowIndex = i; - lastLowValue = r.LowPoint; - } - } - - return results; - } - - // parameter validation - internal static void ValidatePivots( - int leftSpan, - int rightSpan, - int maxTrendPeriods, - string caller = "Pivots") - { - // check parameter arguments - if (rightSpan < 2) - { - throw new ArgumentOutOfRangeException(nameof(rightSpan), rightSpan, - $"Right span must be at least 2 for {caller}."); - } - - if (leftSpan < 2) - { - throw new ArgumentOutOfRangeException(nameof(leftSpan), leftSpan, - $"Left span must be at least 2 for {caller}."); - } - - if (maxTrendPeriods <= leftSpan) - { - throw new ArgumentOutOfRangeException(nameof(leftSpan), leftSpan, - $"Lookback periods must be greater than the Left window span for {caller}."); - } - } -} diff --git a/src/m-r/Pivots/Pivots.StaticSeries.cs b/src/m-r/Pivots/Pivots.StaticSeries.cs new file mode 100644 index 000000000..00a833b10 --- /dev/null +++ b/src/m-r/Pivots/Pivots.StaticSeries.cs @@ -0,0 +1,141 @@ +namespace Skender.Stock.Indicators; + +// PIVOTS (SERIES) + +public static partial class Pivots +{ + public static IReadOnlyList ToPivots( + this IReadOnlyList quotes, + int leftSpan = 2, + int rightSpan = 2, + int maxTrendPeriods = 20, + EndType endType = EndType.HighLow) + where TQuote : IQuote + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(quotes); + Validate(leftSpan, rightSpan, maxTrendPeriods); + + // initialize + int length = quotes.Count; + + decimal?[] highLine = new decimal?[length]; + PivotTrend?[] highTrend = new PivotTrend?[length]; + + decimal?[] lowLine = new decimal?[length]; + PivotTrend?[] lowTrend = new PivotTrend?[length]; + + IReadOnlyList<(decimal? highPoint, decimal? lowPoint)> fractals + = quotes + .ToFractal(leftSpan, rightSpan, endType) + .Select(f => (f.FractalBear, f.FractalBull)) + .ToList(); + + int? lastHighIndex = null; + decimal? lastHighValue = null; + int? lastLowIndex = null; + decimal? lastLowValue = null; + + // roll through results + for (int i = leftSpan; i <= length - rightSpan; i++) + { + (decimal? highPoint, decimal? lowPoint) = fractals[i]; + + // reset expired indexes + if (lastHighIndex < i - maxTrendPeriods) + { + lastHighIndex = null; + lastHighValue = null; + } + + if (lastLowIndex < i - maxTrendPeriods) + { + lastLowIndex = null; + lastLowValue = null; + } + + // evaluate high trend + if (highPoint != null) + { + // repaint trend + if (lastHighIndex != null && highPoint != lastHighValue) + { + PivotTrend trend = highPoint > lastHighValue + ? PivotTrend.Hh + : PivotTrend.Lh; + + highLine[(int)lastHighIndex] = lastHighValue; + + decimal? incr = (highPoint - lastHighValue) + / (i - lastHighIndex); + + for (int t = (int)lastHighIndex + 1; t <= i; t++) + { + highTrend[t] = trend; + highLine[t] = highPoint + incr * (t - i); + } + } + + // reset starting position + lastHighIndex = i; + lastHighValue = highPoint; + } + + // evaluate low trend + if (lowPoint != null) + { + // repaint trend + if (lastLowIndex != null && lowPoint != lastLowValue) + { + PivotTrend trend = lowPoint > lastLowValue + ? PivotTrend.Hl + : PivotTrend.Ll; + + lowLine[(int)lastLowIndex] = lastLowValue; + + decimal? incr = (lowPoint - lastLowValue) + / (i - lastLowIndex); + + for (int t = (int)lastLowIndex + 1; t <= i; t++) + { + lowTrend[t] = trend; + lowLine[t] = lowPoint + incr * (t - i); + } + } + + // reset starting position + lastLowIndex = i; + lastLowValue = lowPoint; + } + } + + // write results + + // TODO: this may need to be re-writes (with) for streaming + // or even here, since it still may be better than 2 full passes + + List results = new(length); + + for (int i = 0; i < length; i++) + { + TQuote q = quotes[i]; + (decimal? highPoint, decimal? lowPoint) = fractals[i]; + + decimal? hl = highLine[i]; + decimal? ll = lowLine[i]; + PivotTrend? ht = highTrend[i]; + PivotTrend? lt = lowTrend[i]; + + results.Add(new( + Timestamp: q.Timestamp, + HighPoint: highPoint, + LowPoint: lowPoint, + HighLine: hl, + LowLine: ll, + HighTrend: ht, + LowTrend: lt)); + } + + return results; + } +} diff --git a/src/m-r/Pivots/Pivots.Utilities.cs b/src/m-r/Pivots/Pivots.Utilities.cs index d791b91ee..4030ac0a0 100644 --- a/src/m-r/Pivots/Pivots.Utilities.cs +++ b/src/m-r/Pivots/Pivots.Utilities.cs @@ -1,12 +1,12 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// PIVOTS (UTILITIES) + +public static partial class Pivots { - // CONDENSE (REMOVE null results) - /// - /// - public static IEnumerable Condense( - this IEnumerable results) + /// + public static IReadOnlyList Condense( + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -17,4 +17,31 @@ public static IEnumerable Condense( return resultsList.ToSortedList(); } + + // parameter validation + internal static void Validate( + int leftSpan, + int rightSpan, + int maxTrendPeriods, + string caller = "Pivots") + { + // check parameter arguments + if (rightSpan < 2) + { + throw new ArgumentOutOfRangeException(nameof(rightSpan), rightSpan, + $"Right span must be at least 2 for {caller}."); + } + + if (leftSpan < 2) + { + throw new ArgumentOutOfRangeException(nameof(leftSpan), leftSpan, + $"Left span must be at least 2 for {caller}."); + } + + if (maxTrendPeriods <= leftSpan) + { + throw new ArgumentOutOfRangeException(nameof(leftSpan), leftSpan, + $"Lookback periods must be greater than the Left window span for {caller}."); + } + } } diff --git a/src/m-r/Pmo/Pmo.Api.cs b/src/m-r/Pmo/Pmo.Api.cs deleted file mode 100644 index f4b990ba4..000000000 --- a/src/m-r/Pmo/Pmo.Api.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PRICE MOMENTUM OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetPmo( - this IEnumerable quotes, - int timePeriods = 35, - int smoothPeriods = 20, - int signalPeriods = 10) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcPmo(timePeriods, smoothPeriods, signalPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetPmo( - this IEnumerable results, - int timePeriods = 35, - int smoothPeriods = 20, - int signalPeriods = 10) => results - .ToTuple() - .CalcPmo(timePeriods, smoothPeriods, signalPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetPmo( - this IEnumerable<(DateTime, double)> priceTuples, - int timePeriods = 35, - int smoothPeriods = 20, - int signalPeriods = 10) => priceTuples - .ToSortedList() - .CalcPmo(timePeriods, smoothPeriods, signalPeriods); -} diff --git a/src/m-r/Pmo/Pmo.Models.cs b/src/m-r/Pmo/Pmo.Models.cs index c46c40236..422add37b 100644 --- a/src/m-r/Pmo/Pmo.Models.cs +++ b/src/m-r/Pmo/Pmo.Models.cs @@ -1,18 +1,12 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class PmoResult : ResultBase, IReusableResult +public record PmoResult +( + DateTime Timestamp, + double? Pmo, + double? Signal +) : IReusable { - public PmoResult(DateTime date) - { - Date = date; - } - - public double? Pmo { get; set; } - public double? Signal { get; set; } - - // internal use only - internal double? RocEma { get; set; } - - double? IReusableResult.Value => Pmo; + public double Value => Pmo.Null2NaN(); } diff --git a/src/m-r/Pmo/Pmo.Series.cs b/src/m-r/Pmo/Pmo.Series.cs deleted file mode 100644 index f2fdc94bf..000000000 --- a/src/m-r/Pmo/Pmo.Series.cs +++ /dev/null @@ -1,155 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PRICE MOMENTUM OSCILLATOR (SERIES) -public static partial class Indicator -{ - internal static List CalcPmo( - this List<(DateTime, double)> tpList, - int timePeriods, - int smoothPeriods, - int signalPeriods) - { - // check parameter arguments - ValidatePmo(timePeriods, smoothPeriods, signalPeriods); - - // initialize - List results = tpList.CalcPmoRocEma(timePeriods); - double smoothingConstant = 2d / smoothPeriods; - double? lastPmo = null; - - // calculate PMO - int startIndex = timePeriods + smoothPeriods; - - for (int i = startIndex - 1; i < results.Count; i++) - { - PmoResult pr = results[i]; - - if (i + 1 > startIndex) - { - pr.Pmo = ((pr.RocEma - lastPmo) * smoothingConstant) + lastPmo; - } - else if (i + 1 == startIndex) - { - double? sumRocEma = 0; - for (int p = i + 1 - smoothPeriods; p <= i; p++) - { - PmoResult d = results[p]; - sumRocEma += d.RocEma; - } - - pr.Pmo = sumRocEma / smoothPeriods; - } - - lastPmo = pr.Pmo; - } - - // add Signal - CalcPmoSignal(results, timePeriods, smoothPeriods, signalPeriods); - - return results; - } - - // internals - private static List CalcPmoRocEma( - this List<(DateTime, double)> tpList, - int timePeriods) - { - // initialize - double smoothingMultiplier = 2d / timePeriods; - double? lastRocEma = null; - List roc = tpList.CalcRoc(1, null).ToList(); - List results = []; - - int startIndex = timePeriods + 1; - - for (int i = 0; i < roc.Count; i++) - { - RocResult rocResult = roc[i]; - - PmoResult r = new(rocResult.Date); - results.Add(r); - - if (i + 1 > startIndex) - { - r.RocEma = (rocResult.Roc * smoothingMultiplier) + (lastRocEma * (1 - smoothingMultiplier)); - } - else if (i + 1 == startIndex) - { - double? sumRoc = 0; - for (int p = i + 1 - timePeriods; p <= i; p++) - { - RocResult d = roc[p]; - sumRoc += d.Roc; - } - - r.RocEma = sumRoc / timePeriods; - } - - lastRocEma = r.RocEma; - r.RocEma *= 10; - } - - return results; - } - - private static void CalcPmoSignal( - List results, - int timePeriods, - int smoothPeriods, - int signalPeriods) - { - double signalConstant = 2d / (signalPeriods + 1); - double? lastSignal = null; - - int startIndex = timePeriods + smoothPeriods + signalPeriods - 1; - - for (int i = startIndex - 1; i < results.Count; i++) - { - PmoResult pr = results[i]; - - if (i + 1 > startIndex) - { - pr.Signal = ((pr.Pmo - lastSignal) * signalConstant) + lastSignal; - } - else if (i + 1 == startIndex) - { - double? sumPmo = 0; - for (int p = i + 1 - signalPeriods; p <= i; p++) - { - PmoResult d = results[p]; - sumPmo += d.Pmo; - } - - pr.Signal = sumPmo / signalPeriods; - } - - lastSignal = pr.Signal; - } - } - - // parameter validation - private static void ValidatePmo( - int timePeriods, - int smoothPeriods, - int signalPeriods) - { - // check parameter arguments - if (timePeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(timePeriods), timePeriods, - "Time periods must be greater than 1 for PMO."); - } - - if (smoothPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, - "Smoothing periods must be greater than 0 for PMO."); - } - - if (signalPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, - "Signal periods must be greater than 0 for PMO."); - } - } -} diff --git a/src/m-r/Pmo/Pmo.StaticSeries.cs b/src/m-r/Pmo/Pmo.StaticSeries.cs new file mode 100644 index 000000000..feaea3a64 --- /dev/null +++ b/src/m-r/Pmo/Pmo.StaticSeries.cs @@ -0,0 +1,112 @@ +namespace Skender.Stock.Indicators; + +// PRICE MOMENTUM OSCILLATOR (SERIES) + +public static partial class Pmo +{ + public static IReadOnlyList ToPmo( + this IReadOnlyList source, + int timePeriods = 35, + int smoothPeriods = 20, + int signalPeriods = 10) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(timePeriods, smoothPeriods, signalPeriods); + + // initialize + int length = source.Count; + List results = new(length); + double smoothingConstant1 = 2d / smoothPeriods; + double smoothingConstant2 = 2d / timePeriods; + double smoothingConstant3 = 2d / (signalPeriods + 1); + + double prevPrice = double.NaN; + double prevPmo = double.NaN; + double prevRocEma = double.NaN; + double prevSignal = double.NaN; + + double[] rc = new double[length]; // roc + double[] re = new double[length]; // roc ema + double[] pm = new double[length]; // pmo + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + // rate of change (ROC) + rc[i] = prevPrice == 0 ? double.NaN : 100 * ((s.Value / prevPrice) - 1); + prevPrice = s.Value; + + // ROC smoothed moving average + double rocEma; + + if (double.IsNaN(prevRocEma) && i >= timePeriods) + { + double sum = 0; + for (int p = i - timePeriods + 1; p <= i; p++) + { + sum += rc[p]; + } + rocEma = sum / timePeriods; + } + else + { + rocEma = prevRocEma + (smoothingConstant2 * (rc[i] - prevRocEma)); + } + + re[i] = rocEma * 10; + prevRocEma = rocEma; + + // price momentum oscillator + double pmo; + + if (double.IsNaN(prevPmo) && i >= smoothPeriods) + { + double sum = 0; + for (int p = i - smoothPeriods + 1; p <= i; p++) + { + sum += re[p]; + } + pmo = sum / smoothPeriods; + } + else + { + pmo = prevPmo + (smoothingConstant1 * (re[i] - prevPmo)); + } + + prevPmo = pm[i] = pmo; + + // add signal (EMA of PMO) + double signal; + + if (double.IsNaN(prevSignal) && i >= signalPeriods) + { + double sum = 0; + for (int p = i - signalPeriods + 1; p <= i; p++) + { + sum += pm[p]; + } + + signal = sum / signalPeriods; + } + else + { + signal = Ema.Increment(smoothingConstant3, prevSignal, pm[i]); + } + + PmoResult r = new( + Timestamp: s.Timestamp, + Pmo: pmo.NaN2Null(), + Signal: signal.NaN2Null()); + + results.Add(r); + + prevSignal = signal; + } + + return results; + } +} diff --git a/src/m-r/Pmo/Pmo.Utilities.cs b/src/m-r/Pmo/Pmo.Utilities.cs index ac67f96b7..3ca068f94 100644 --- a/src/m-r/Pmo/Pmo.Utilities.cs +++ b/src/m-r/Pmo/Pmo.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// PRICE MOMENTUM OSCILLATOR (UTILITIES) + +public static partial class Pmo { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int ts = results .ToList() @@ -14,4 +15,30 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(ts + 250); } + + // parameter validation + internal static void Validate( + int timePeriods, + int smoothPeriods, + int signalPeriods) + { + // check parameter arguments + if (timePeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(timePeriods), timePeriods, + "Time periods must be greater than 1 for PMO."); + } + + if (smoothPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, + "Smoothing periods must be greater than 0 for PMO."); + } + + if (signalPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, + "Signal periods must be greater than 0 for PMO."); + } + } } diff --git a/src/m-r/Prs/Prs.Api.cs b/src/m-r/Prs/Prs.Api.cs deleted file mode 100644 index 16bd36901..000000000 --- a/src/m-r/Prs/Prs.Api.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PRICE RELATIVE STRENGTH (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetPrs( - this IEnumerable quotesEval, - IEnumerable quotesBase, - int? lookbackPeriods = null, - int? smaPeriods = null) - where TQuote : IQuote - { - List<(DateTime, double)> tpListBase = quotesBase - .ToTuple(CandlePart.Close); - List<(DateTime, double)> tpListEval = quotesEval - .ToTuple(CandlePart.Close); - - return CalcPrs(tpListEval, tpListBase, lookbackPeriods, smaPeriods); - } - - // SERIES, from CHAINS (both inputs reusable) - public static IEnumerable GetPrs( - this IEnumerable quotesEval, - IEnumerable quotesBase, - int? lookbackPeriods = null, - int? smaPeriods = null) - { - List<(DateTime Date, double Value)> tpListEval - = quotesEval.ToTuple(); - - List<(DateTime Date, double Value)> tpListBase - = quotesBase.ToTuple(); - - return CalcPrs(tpListEval, tpListBase, lookbackPeriods, smaPeriods) - .SyncIndex(quotesEval, SyncType.Prepend); - } - - // SERIES, from TUPLE - public static IEnumerable GetPrs( - this IEnumerable<(DateTime, double)> tupleEval, - IEnumerable<(DateTime, double)> tupleBase, - int? lookbackPeriods = null, - int? smaPeriods = null) - { - List<(DateTime, double)> tpListBase = tupleBase.ToSortedList(); - List<(DateTime, double)> tpListEval = tupleEval.ToSortedList(); - - return CalcPrs(tpListEval, tpListBase, lookbackPeriods, smaPeriods); - } -} diff --git a/src/m-r/Prs/Prs.Models.cs b/src/m-r/Prs/Prs.Models.cs index 20c884fef..e565d7e56 100644 --- a/src/m-r/Prs/Prs.Models.cs +++ b/src/m-r/Prs/Prs.Models.cs @@ -1,16 +1,12 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class PrsResult : ResultBase, IReusableResult +public record PrsResult +( + DateTime Timestamp, + double? Prs, + double? PrsPercent +) : IReusable { - public PrsResult(DateTime date) - { - Date = date; - } - - public double? Prs { get; set; } - public double? PrsSma { get; set; } - public double? PrsPercent { get; set; } - - double? IReusableResult.Value => Prs; + public double Value => Prs.Null2NaN(); } diff --git a/src/m-r/Prs/Prs.Series.cs b/src/m-r/Prs/Prs.Series.cs deleted file mode 100644 index c5aa072cc..000000000 --- a/src/m-r/Prs/Prs.Series.cs +++ /dev/null @@ -1,109 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PRICE RELATIVE STRENGTH (SERIES) -public static partial class Indicator -{ - internal static List CalcPrs( - List<(DateTime, double)> tpListEval, - List<(DateTime, double)> tpListBase, - int? lookbackPeriods = null, - int? smaPeriods = null) - { - // check parameter arguments - ValidatePriceRelative(tpListEval, tpListBase, lookbackPeriods, smaPeriods); - - // initialize - List results = new(tpListEval.Count); - - // roll through quotes - for (int i = 0; i < tpListEval.Count; i++) - { - (DateTime bDate, double bValue) = tpListBase[i]; - (DateTime eDate, double eValue) = tpListEval[i]; - - if (eDate != bDate) - { - throw new InvalidQuotesException(nameof(tpListEval), eDate, - "Date sequence does not match. Price Relative requires matching dates in provided histories."); - } - - PrsResult r = new(eDate) { - Prs = (bValue == 0) ? null : (eValue / bValue).NaN2Null() // relative strength ratio - }; - results.Add(r); - - if (lookbackPeriods != null && i + 1 > lookbackPeriods) - { - (DateTime _, double boValue) = tpListBase[i - (int)lookbackPeriods]; - (DateTime _, double eoValue) = tpListEval[i - (int)lookbackPeriods]; - - if (boValue != 0 && eoValue != 0) - { - double? pctB = (bValue - boValue) / boValue; - double? pctE = (eValue - eoValue) / eoValue; - - r.PrsPercent = (pctE - pctB).NaN2Null(); - } - } - - // optional moving average of PRS - if (smaPeriods != null && i + 1 >= smaPeriods) - { - double? sumRs = 0; - for (int p = i + 1 - (int)smaPeriods; p <= i; p++) - { - PrsResult d = results[p]; - sumRs += d.Prs; - } - - r.PrsSma = (sumRs / smaPeriods).NaN2Null(); - } - } - - return results; - } - - // parameter validation - private static void ValidatePriceRelative( - List<(DateTime, double)> quotesEval, - List<(DateTime, double)> quotesBase, - int? lookbackPeriods, - int? smaPeriods) - { - // check parameter arguments - if (lookbackPeriods is not null and <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Price Relative Strength."); - } - - if (smaPeriods is not null and <= 0) - { - throw new ArgumentOutOfRangeException(nameof(smaPeriods), smaPeriods, - "SMA periods must be greater than 0 for Price Relative Strength."); - } - - // check quotes - int qtyHistoryEval = quotesEval.Count; - int qtyHistoryBase = quotesBase.Count; - - int? minHistory = lookbackPeriods; - if (minHistory != null && qtyHistoryEval < minHistory) - { - string message = "Insufficient quotes provided for Price Relative Strength. " + - string.Format( - invCulture, - "You provided {0} periods of quotes when at least {1} are required.", - qtyHistoryEval, minHistory); - - throw new InvalidQuotesException(nameof(quotesEval), message); - } - - if (qtyHistoryBase != qtyHistoryEval) - { - throw new InvalidQuotesException( - nameof(quotesBase), - "Base quotes should have at least as many records as Eval quotes for PRS."); - } - } -} diff --git a/src/m-r/Prs/Prs.StaticSeries.cs b/src/m-r/Prs/Prs.StaticSeries.cs new file mode 100644 index 000000000..40a6ab70d --- /dev/null +++ b/src/m-r/Prs/Prs.StaticSeries.cs @@ -0,0 +1,66 @@ +namespace Skender.Stock.Indicators; + +// PRICE RELATIVE STRENGTH (SERIES) + +public static partial class Prs +{ + public static IReadOnlyList ToPrs( + this IReadOnlyList sourceEval, + IReadOnlyList sourceBase, + int? lookbackPeriods = null) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(sourceEval); + ArgumentNullException.ThrowIfNull(sourceBase); + Validate(sourceEval, sourceBase, lookbackPeriods); + + // initialize + int length = sourceEval.Count; + List results = new(length); + + // roll through source values + for (int i = 0; i < length; i++) + { + T b = sourceBase[i]; + T e = sourceEval[i]; + + if (e.Timestamp != b.Timestamp) + { + throw new InvalidQuotesException( + nameof(sourceEval), e.Timestamp, + "Timestamp sequence does not match. " + + "Price Relative requires matching dates in provided histories."); + } + + double? prsPercent = null; + + if (lookbackPeriods is not null && i > lookbackPeriods - 1) + { + T bo = sourceBase[i - (int)lookbackPeriods]; + T eo = sourceEval[i - (int)lookbackPeriods]; + + if (bo.Value != 0 && eo.Value != 0) + { + double? pctB = (b.Value - bo.Value) / bo.Value; + double? pctE = (e.Value - eo.Value) / eo.Value; + + prsPercent = (pctE - pctB).NaN2Null(); + } + } + + PrsResult r = new( + Timestamp: e.Timestamp, + + Prs: b.Value == 0 + ? null + : (e.Value / b.Value).NaN2Null(), // relative strength ratio + + PrsPercent: prsPercent); + + results.Add(r); + } + + return results; + } +} diff --git a/src/m-r/Prs/Prs.Utilities.cs b/src/m-r/Prs/Prs.Utilities.cs new file mode 100644 index 000000000..497f2e649 --- /dev/null +++ b/src/m-r/Prs/Prs.Utilities.cs @@ -0,0 +1,49 @@ +using System.Globalization; + +namespace Skender.Stock.Indicators; + +// PRICE RELATIVE STRENGTH (UTILITIES) + +public static partial class Prs +{ + private static readonly CultureInfo invariantCulture = CultureInfo.InvariantCulture; + + // parameter validation + internal static void Validate( + IReadOnlyList quotesEval, + IReadOnlyList quotesBase, + int? lookbackPeriods) + where T : IReusable + { + // check parameter arguments + if (lookbackPeriods is <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Price Relative Strength."); + } + + // check quotes + int qtyHistoryEval = quotesEval.Count; + int qtyHistoryBase = quotesBase.Count; + + if (qtyHistoryEval < lookbackPeriods) + { + string message = "Insufficient quotes provided for Price Relative Strength. " + + string.Format( + invariantCulture, + "You provided {0} periods of quotes when at least {1} are required.", + qtyHistoryEval, lookbackPeriods); + + throw new InvalidQuotesException(nameof(quotesEval), message); + } + + if (qtyHistoryBase != qtyHistoryEval) + { + throw new InvalidQuotesException( + nameof(quotesBase), + "Base quotes should have at least as many records as Eval quotes for PRS."); + } + } + +} diff --git a/src/m-r/Prs/info.xml b/src/m-r/Prs/info.xml index e83fc1f16..e9a4e9e7c 100644 --- a/src/m-r/Prs/info.xml +++ b/src/m-r/Prs/info.xml @@ -16,7 +16,6 @@ Historical price quotes for evaluation. This is usually market index data, but could be any baseline data that you might use for comparison. Optional. Number of periods for % difference. - Optional. Number of periods for a PRS SMA signal line. Time series of PRS values. Invalid parameter value provided. Invalid quotes provided. diff --git a/src/m-r/Pvo/Pvo.Api.cs b/src/m-r/Pvo/Pvo.Api.cs deleted file mode 100644 index 158e82376..000000000 --- a/src/m-r/Pvo/Pvo.Api.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PRICE VOLUME OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetPvo( - this IEnumerable quotes, - int fastPeriods = 12, - int slowPeriods = 26, - int signalPeriods = 9) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Volume) - .CalcPvo(fastPeriods, slowPeriods, signalPeriods); - - // given that this is volume-based, other chaining is moot -} diff --git a/src/m-r/Pvo/Pvo.Models.cs b/src/m-r/Pvo/Pvo.Models.cs index 98e6966a8..39fb9966b 100644 --- a/src/m-r/Pvo/Pvo.Models.cs +++ b/src/m-r/Pvo/Pvo.Models.cs @@ -1,16 +1,13 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class PvoResult : ResultBase, IReusableResult +public record PvoResult +( + DateTime Timestamp, + double? Pvo, + double? Signal, + double? Histogram +) : IReusable { - public PvoResult(DateTime date) - { - Date = date; - } - - public double? Pvo { get; set; } - public double? Signal { get; set; } - public double? Histogram { get; set; } - - double? IReusableResult.Value => Pvo; + public double Value => Pvo.Null2NaN(); } diff --git a/src/m-r/Pvo/Pvo.Series.cs b/src/m-r/Pvo/Pvo.Series.cs deleted file mode 100644 index 29f831dce..000000000 --- a/src/m-r/Pvo/Pvo.Series.cs +++ /dev/null @@ -1,87 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PRICE VOLUME OSCILLATOR (SERIES) -public static partial class Indicator -{ - internal static List CalcPvo( - this List<(DateTime, double)> tpList, - int fastPeriods, - int slowPeriods, - int signalPeriods) - { - // check parameter arguments - ValidatePvo(fastPeriods, slowPeriods, signalPeriods); - - // initialize - List emaFast = tpList.CalcEma(fastPeriods); - List emaSlow = tpList.CalcEma(slowPeriods); - - int length = tpList.Count; - List<(DateTime, double)> emaDiff = []; - List results = new(length); - - // roll through quotes - for (int i = 0; i < length; i++) - { - (DateTime date, double _) = tpList[i]; - EmaResult df = emaFast[i]; - EmaResult ds = emaSlow[i]; - - PvoResult r = new(date); - results.Add(r); - - if (i >= slowPeriods - 1) - { - double? pvo = (ds.Ema != 0) ? - 100 * ((df.Ema - ds.Ema) / ds.Ema) : null; - - r.Pvo = pvo; - - // temp data for interim EMA of PVO - (DateTime, double) diff = (date, (pvo == null) ? 0 : (double)pvo); - - emaDiff.Add(diff); - } - } - - // add signal and histogram to result - List 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; - } - - // parameter validation - private static void ValidatePvo( - int fastPeriods, - int slowPeriods, - int signalPeriods) - { - // check parameter arguments - if (fastPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, - "Fast periods must be greater than 0 for PVO."); - } - - if (signalPeriods < 0) - { - throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, - "Signal periods must be greater than or equal to 0 for PVO."); - } - - if (slowPeriods <= fastPeriods) - { - throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, - "Slow periods must be greater than the fast period for PVO."); - } - } -} diff --git a/src/m-r/Pvo/Pvo.StaticSeries.cs b/src/m-r/Pvo/Pvo.StaticSeries.cs new file mode 100644 index 000000000..4f02630e7 --- /dev/null +++ b/src/m-r/Pvo/Pvo.StaticSeries.cs @@ -0,0 +1,120 @@ +namespace Skender.Stock.Indicators; + +// PRICE VOLUME OSCILLATOR (SERIES) + +public static partial class Pvo +{ + public static IReadOnlyList ToPvo( + this IReadOnlyList quotes, + int fastPeriods = 12, + int slowPeriods = 26, + int signalPeriods = 9) + where TQuote : IQuote => quotes + .Use(CandlePart.Volume) + .CalcPvo(fastPeriods, slowPeriods, signalPeriods); + + private static List CalcPvo( + this IReadOnlyList source, // volume + int fastPeriods, + int slowPeriods, + int signalPeriods) + where T : IReusable + { + // check parameter arguments + Validate(fastPeriods, slowPeriods, signalPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + double lastEmaFast = double.NaN; + double lastEmaSlow = double.NaN; + double lastEmaPvo = double.NaN; + + double kFast = 2d / (fastPeriods + 1); + double kSlow = 2d / (slowPeriods + 1); + double kPvo = 2d / (signalPeriods + 1); + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + // re-initialize Fast EMA + double emaFast; + + if (double.IsNaN(lastEmaFast) && i >= fastPeriods - 1) + { + double sum = 0; + for (int p = i - fastPeriods + 1; p <= i; p++) + { + T ps = source[p]; + sum += ps.Value; + } + + emaFast = sum / fastPeriods; + } + else + { + emaFast = Ema.Increment(kFast, lastEmaFast, s.Value); + } + + // re-initialize Slow EMA + double emaSlow; + + if (double.IsNaN(lastEmaSlow) && i >= slowPeriods - 1) + { + double sum = 0; + for (int p = i - slowPeriods + 1; p <= i; p++) + { + T ps = source[p]; + sum += ps.Value; + } + + emaSlow = sum / slowPeriods; + } + else + { + emaSlow = Ema.Increment(kSlow, lastEmaSlow, s.Value); + } + + double pvo = emaSlow != 0 ? + 100 * ((emaFast - emaSlow) / emaSlow) : double.NaN; + + // re-initialize Signal EMA + double signal; + + if (double.IsNaN(lastEmaPvo) && i >= signalPeriods + slowPeriods - 2) + { + double sum = pvo; + for (int p = i - signalPeriods + 1; p < i; p++) + { + sum += ((IReusable)results[p]).Value; + } + + signal = sum / signalPeriods; + } + else + { + signal = Ema.Increment(kPvo, lastEmaPvo, pvo); + } + + // write results + results.Add(new( + Timestamp: s.Timestamp, + Pvo: pvo.NaN2Null(), + Signal: signal.NaN2Null(), + Histogram: (pvo - signal).NaN2Null())); + + lastEmaPvo = signal; + lastEmaFast = emaFast; + lastEmaSlow = emaSlow; + } + + return results; + } + + /* DESIGN NOTE: this is exactly like MACD, except for: + * a) it uses Volume instead of Price (see API) + * b) the PVO calculation slightly different */ +} diff --git a/src/m-r/Pvo/Pvo.Utilities.cs b/src/m-r/Pvo/Pvo.Utilities.cs index 639d42d90..3731ccb5d 100644 --- a/src/m-r/Pvo/Pvo.Utilities.cs +++ b/src/m-r/Pvo/Pvo.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// PRICE VOLUME OSCILLATOR (UTILITIES) + +public static partial class Pvo { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int n = results .ToList() @@ -14,4 +15,30 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(n + 250); } + + // parameter validation + internal static void Validate( + int fastPeriods, + int slowPeriods, + int signalPeriods) + { + // check parameter arguments + if (fastPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, + "Fast periods must be greater than 0 for PVO."); + } + + if (signalPeriods < 0) + { + throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, + "Signal periods must be greater than or equal to 0 for PVO."); + } + + if (slowPeriods <= fastPeriods) + { + throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, + "Slow periods must be greater than the fast period for PVO."); + } + } } diff --git a/src/m-r/Renko/Renko.Api.cs b/src/m-r/Renko/Renko.Api.cs deleted file mode 100644 index 07ea95f0c..000000000 --- a/src/m-r/Renko/Renko.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RENKO CHART - STANDARD (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetRenko( - this IEnumerable quotes, - decimal brickSize, - EndType endType = EndType.Close) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcRenko(brickSize, endType); -} diff --git a/src/m-r/Renko/Renko.Models.cs b/src/m-r/Renko/Renko.Models.cs index a85e45c78..d8fe874d3 100644 --- a/src/m-r/Renko/Renko.Models.cs +++ b/src/m-r/Renko/Renko.Models.cs @@ -1,17 +1,14 @@ namespace Skender.Stock.Indicators; +/// [Serializable] -public sealed class RenkoResult : ResultBase, IQuote -{ - public RenkoResult(DateTime date) - { - Date = date; - } - - public decimal Open { get; set; } - public decimal High { get; set; } - public decimal Low { get; set; } - public decimal Close { get; set; } - public decimal Volume { get; set; } - public bool IsUp { get; set; } -} +public record RenkoResult +( + DateTime Timestamp, + decimal Open, + decimal High, + decimal Low, + decimal Close, + decimal Volume, + bool IsUp +) : Quote(Timestamp, Open, High, Low, Close, Volume); diff --git a/src/m-r/Renko/Renko.Series.cs b/src/m-r/Renko/Renko.Series.cs deleted file mode 100644 index 0d27477b8..000000000 --- a/src/m-r/Renko/Renko.Series.cs +++ /dev/null @@ -1,156 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RENKO CHART - STANDARD (SERIES) -public static partial class Indicator -{ - internal static List CalcRenko( - this List quotesList, - decimal brickSize, - EndType endType) - where TQuote : IQuote - { - // check parameter arguments - ValidateRenko(brickSize); - - // initialize - int length = quotesList.Count; - List results = new(length); - TQuote q0; - - if (length == 0) - { - return results; - } - else - { - q0 = quotesList[0]; - } - - bool resetHLV = true; - int decimals = brickSize.GetDecimalPlaces(); - decimal baseline = Math.Round(q0.Close, Math.Max(decimals - 1, 0)); - - decimal h = decimal.MinValue; - decimal l = decimal.MaxValue; - decimal v = 0; - - RenkoResult lastBrick = new(q0.Date) { - Open = baseline, - Close = baseline - }; - - // roll through quotes - for (int i = 1; i < length; i++) - { - TQuote q = quotesList[i]; - - // accumulate brick info - if (resetHLV) - { - // reset - h = q.High; - l = q.Low; - v = q.Volume; - } - else - { - h = q.High > h ? q.High : h; - l = q.Low < l ? q.Low : l; - v += q.Volume; - } - - // determine if new brick threshold is met - int newBrickQty = GetNewBricks(endType, q, lastBrick, brickSize); - int absQty = Math.Abs(newBrickQty); - - // add new brick(s) - // can add more than one brick! - for (int b = 0; b < absQty; b++) - { - decimal c; - bool isUp = newBrickQty >= 0; - - if (newBrickQty > 0) - { - baseline = Math.Max(lastBrick.Open, lastBrick.Close); - c = baseline + brickSize; - } - else - { - baseline = Math.Min(lastBrick.Open, lastBrick.Close); - c = baseline - brickSize; - } - - RenkoResult r = new(q.Date) { - Open = baseline, - High = h, - Low = l, - Close = c, - Volume = v / absQty, - IsUp = isUp - }; - results.Add(r); - lastBrick = r; - } - - // init next brick(s) - resetHLV = absQty != 0; - } - - return results; - } - - // calculate brick size - private static int GetNewBricks( - EndType endType, - TQuote q, - RenkoResult lastBrick, - decimal brickSize) - where TQuote : IQuote - { - int bricks; - decimal upper = Math.Max(lastBrick.Open, lastBrick.Close); - decimal lower = Math.Min(lastBrick.Open, lastBrick.Close); - - switch (endType) - { - case EndType.Close: - - bricks = q.Close > upper - ? (int)((q.Close - upper) / brickSize) - : q.Close < lower - ? (int)((q.Close - lower) / brickSize) - : 0; - - break; - - case EndType.HighLow: - - // high/low assumption: absolute greater diff wins - // --> does not consider close direction - - decimal hQty = (q.High - upper) / brickSize; - decimal lQty = (lower - q.Low) / brickSize; - - bricks = (int)((hQty >= lQty) ? hQty : -lQty); - break; - - default: - throw new ArgumentOutOfRangeException(nameof(endType)); - } - - return bricks; - } - - // parameter validation - private static void ValidateRenko( - decimal brickSize) - { - // check parameter arguments - if (brickSize <= 0) - { - throw new ArgumentOutOfRangeException(nameof(brickSize), brickSize, - "Brick size must be greater than 0 for Renko Charts."); - } - } -} diff --git a/src/m-r/Renko/Renko.StaticSeries.cs b/src/m-r/Renko/Renko.StaticSeries.cs new file mode 100644 index 000000000..6aaa45bc5 --- /dev/null +++ b/src/m-r/Renko/Renko.StaticSeries.cs @@ -0,0 +1,94 @@ +namespace Skender.Stock.Indicators; + +// RENKO CHART (SERIES) + +public static partial class Renko +{ + public static IReadOnlyList ToRenko( + this IReadOnlyList quotes, + decimal brickSize, + EndType endType = EndType.Close) + where TQuote : IQuote + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(quotes); + Validate(brickSize); + + // initialize + int length = quotes.Count; + List results = new(length); + + if (length == 0) + { + return results; + } + + // first brick baseline + TQuote q0 = quotes[0]; + + int decimals = brickSize.GetDecimalPlaces(); + decimal baseline = Math.Round(q0.Close, Math.Max(decimals - 1, 0)); + + RenkoResult lastBrick = new( + q0.Timestamp, + Open: baseline, 0, 0, + Close: baseline, 0, false); + + // initialize high/low/volume tracking + decimal h = decimal.MinValue; + decimal l = decimal.MaxValue; + decimal sumV = 0; // cumulative + + // roll through source values + for (int i = 1; i < length; i++) + { + TQuote q = quotes[i]; + + // track high/low/volume between bricks + h = Math.Max(h, q.High); + l = Math.Min(l, q.Low); + sumV += q.Volume; + + // determine new brick quantity + int newBrickQty = GetNewBrickQuantity(q, lastBrick, brickSize, endType); + int absBrickQty = Math.Abs(newBrickQty); + bool isUp = newBrickQty >= 0; + + // add new brick(s) + // can add more than one brick! + for (int b = 0; b < absBrickQty; b++) + { + decimal o; + decimal c; + decimal v = sumV / absBrickQty; + + if (isUp) + { + o = Math.Max(lastBrick.Open, lastBrick.Close); + c = o + brickSize; + } + else + { + o = Math.Min(lastBrick.Open, lastBrick.Close); + c = o - brickSize; + } + + RenkoResult r + = new(q.Timestamp, o, h, l, c, v, isUp); + + results.Add(r); + lastBrick = r; + } + + // reset high/low/volume tracking + if (absBrickQty != 0) + { + h = decimal.MinValue; + l = decimal.MaxValue; + sumV = 0; + } + } + + return results; + } +} diff --git a/src/m-r/Renko/Renko.StreamHub.cs b/src/m-r/Renko/Renko.StreamHub.cs new file mode 100644 index 000000000..8486bf51f --- /dev/null +++ b/src/m-r/Renko/Renko.StreamHub.cs @@ -0,0 +1,200 @@ +namespace Skender.Stock.Indicators; + +// RENKO CHART (STREAM HUB) + +#region hub interface and initializer + +public interface IRenkoHub +{ + decimal BrickSize { get; } + EndType EndType { get; } +} + +public static partial class Renko +{ + public static RenkoHub ToRenko( + this IQuoteProvider quoteProvider, + decimal brickSize, + EndType endType = EndType.Close) + where TIn : IQuote + => new(quoteProvider, brickSize, endType); +} +#endregion + +public class RenkoHub + : QuoteProvider, IRenkoHub + where TIn : IQuote +{ + #region constructors + + private readonly string hubName; + + private RenkoResult lastBrick + = new(default, default, default, + default, default, default, default); + + internal RenkoHub( + IQuoteProvider provider, + decimal brickSize, + EndType endType) : base(provider) + { + Renko.Validate(brickSize); + BrickSize = brickSize; + EndType = endType; + hubName = $"RENKO({brickSize},{endType.ToString().ToUpperInvariant()})"; + + Reinitialize(); + } + #endregion + + /// + /// Renko hub settings. Since it can produce 0 or many bricks per quote, + /// the default 1:1 in/out is not used and must be skipped to prevent + /// same-date triggerred rebuilds when caching. + /// + public override BinarySettings Properties { get; init; } = new(0b00000010); // custom + + /// + /// Standard brick size for Renko chart. + /// + public decimal BrickSize { get; } + + /// + /// Close or High/Low price used to determine when threshold + /// is met to generate new bricks. + /// + public EndType EndType { get; } + + // METHODS + + public override string ToString() => hubName; + + public override void OnAdd(TIn item, bool notify, int? indexHint) + => ToIndicator(item, notify, indexHint); + + protected override (RenkoResult result, int index) + ToIndicator(TIn item, int? indexHint) + => throw new InvalidOperationException(); // not used + + // TODO: see if returning array of results is possible ^^ + // for all indicators, so we don't have to do this goofy override + + /// + /// Restore last brick marker. + /// + /// + protected override void RollbackState(DateTime timestamp) + { + // restore last brick marker + if (Cache.Count != 0) + { + lastBrick = Cache + .Last(c => c.Timestamp <= timestamp); + + return; + } + + // skip first quote + if (ProviderCache.Count <= 1) + { + return; + } + + SetBaselineBrick(); + } + + // re/initialize last brick marker + private void SetBaselineBrick() + { + int decimals = BrickSize.GetDecimalPlaces(); + + TIn q0 = ProviderCache[0]; + + decimal baseline + = Math.Round(q0.Close, + Math.Max(decimals - 1, 0)); + + lastBrick = new( + q0.Timestamp, + Open: baseline, + High: 0, + Low: 0, + Close: baseline, + Volume: 0, + IsUp: false); + } + + // custom: build 0 to many bricks per quote + private void ToIndicator(TIn item, bool notify, int? indexHint) + { + int providerIndex = indexHint + ?? throw new InvalidOperationException($"{nameof(indexHint)} cannot be empty"); + + // nothing to do + if (providerIndex <= 0) + { + return; + } + + // establish baseline brick + if (providerIndex == 1) + { + SetBaselineBrick(); + } + + // determine new brick quantity + int newBrickQty + = Renko.GetNewBrickQuantity( + item, lastBrick, BrickSize, EndType); + + int absBrickQty = Math.Abs(newBrickQty); + bool isUp = newBrickQty >= 0; + + // add new brick(s) ... can add more than one! + if (absBrickQty > 0) + { + // get high/low/volume between bricks + decimal h = decimal.MinValue; + decimal l = decimal.MaxValue; + decimal sumV = 0; // cumulative + + // by aggregating provider cache range + int lastBrickIndex = ProviderCache.GetIndex(lastBrick.Timestamp, true); + + for (int w = lastBrickIndex + 1; w <= providerIndex; w++) + { + TIn pq = ProviderCache[w]; + + h = Math.Max(h, pq.High); + l = Math.Min(l, pq.Low); + sumV += pq.Volume; + } + + decimal v = sumV / absBrickQty; + + for (int b = 0; b < absBrickQty; b++) + { + decimal o = isUp + ? Math.Max(lastBrick.Open, lastBrick.Close) + : Math.Min(lastBrick.Open, lastBrick.Close); + + decimal c = isUp + ? o + BrickSize + : o - BrickSize; + + // candidate result + RenkoResult r + = new(item.Timestamp, o, h, l, c, v, isUp); + + lastBrick = r; + + // save and send + AppendCache(r, notify); + + // note: bypass rebuild bit set in Properties to allow + // sequential bricks with duplicate dates that would + // normally trigger rebuild, causing stack overflow. + } + } + } +} diff --git a/src/m-r/Renko/Renko.Utilities.cs b/src/m-r/Renko/Renko.Utilities.cs new file mode 100644 index 000000000..d8463910b --- /dev/null +++ b/src/m-r/Renko/Renko.Utilities.cs @@ -0,0 +1,63 @@ +namespace Skender.Stock.Indicators; + +// RENKO CHART (UTILITIES) + +public static partial class Renko +{ + // calculate brick size + internal static int GetNewBrickQuantity( + TQuote q, + RenkoResult lastBrick, + decimal brickSize, + EndType endType) + where TQuote : IQuote + { + int brickQuantity; + decimal upper = Math.Max(lastBrick.Open, lastBrick.Close); + decimal lower = Math.Min(lastBrick.Open, lastBrick.Close); + + switch (endType) + { + case EndType.Close: + + brickQuantity + = q.Close > upper + ? (int)((q.Close - upper) / brickSize) + : q.Close < lower + ? (int)((q.Close - lower) / brickSize) + : 0; + + break; + + case EndType.HighLow: + + // high/low assumption: absolute greater diff wins + // --> does not consider close direction + + decimal hQty = (q.High - upper) / brickSize; + decimal lQty = (lower - q.Low) / brickSize; + + brickQuantity = (int)(hQty >= lQty ? hQty : -lQty); + break; + + default: + throw new ArgumentOutOfRangeException(nameof(endType)); + } + + return brickQuantity; + } + + // parameter validation + internal static void Validate( + decimal brickSize) + { + // check parameter arguments + if (brickSize <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(brickSize), brickSize, + "Brick size must be greater than 0 for Renko Charts."); + } + } + +} diff --git a/src/m-r/Renko/RenkoAtr.Api.cs b/src/m-r/Renko/RenkoAtr.Api.cs deleted file mode 100644 index 38f9e894e..000000000 --- a/src/m-r/Renko/RenkoAtr.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RENKO CHART - ATR (API) -public static partial class Indicator -{ - /// - /// - public static IEnumerable GetRenkoAtr( - this IEnumerable quotes, - int atrPeriods, - EndType endType = EndType.Close) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcRenkoAtr(atrPeriods, endType); -} diff --git a/src/m-r/Renko/RenkoAtr.Series.cs b/src/m-r/Renko/RenkoAtr.Series.cs deleted file mode 100644 index 73323db2f..000000000 --- a/src/m-r/Renko/RenkoAtr.Series.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Skender.Stock.Indicators; - -public static partial class Indicator -{ - // RENKO CHART - ATR (SERIES) - internal static List CalcRenkoAtr( - this List quotesList, - int atrPeriods, - EndType endType = EndType.Close) - where TQuote : IQuote - { - // initialize - List atrResults = quotesList - .ToQuoteD() - .CalcAtr(atrPeriods); - - double? atr = atrResults.LastOrDefault()?.Atr; - decimal brickSize = (atr == null) ? 0 : (decimal)atr; - - return brickSize is 0 ? - [] - : quotesList.CalcRenko(brickSize, endType); - } -} diff --git a/src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs b/src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs new file mode 100644 index 000000000..479278122 --- /dev/null +++ b/src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs @@ -0,0 +1,25 @@ +namespace Skender.Stock.Indicators; + +// RENKO CHART - ATR (SERIES) + +public static partial class Renko +{ + public static IReadOnlyList GetRenkoAtr( + this IReadOnlyList quotes, + int atrPeriods, + EndType endType = EndType.Close) + where TQuote : IQuote + { + // initialize + List atrResults = quotes + .ToQuoteDList() + .CalcAtr(atrPeriods); + + AtrResult? last = atrResults.LastOrDefault(); + decimal brickSize = (decimal?)last?.Atr ?? 0; + + return brickSize == 0 + ? [] + : quotes.ToRenko(brickSize, endType); + } +} diff --git a/src/m-r/Roc/Roc.Api.cs b/src/m-r/Roc/Roc.Api.cs deleted file mode 100644 index 86bdd081f..000000000 --- a/src/m-r/Roc/Roc.Api.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RATE OF CHANGE (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetRoc( - this IEnumerable quotes, - int lookbackPeriods, - int? smaPeriods = null) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcRoc(lookbackPeriods, smaPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetRoc( - this IEnumerable results, - int lookbackPeriods, - int? smaPeriods = null) => results - .ToTuple() - .CalcRoc(lookbackPeriods, smaPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetRoc( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods, - int? smaPeriods = null) => priceTuples - .ToSortedList() - .CalcRoc(lookbackPeriods, smaPeriods); -} diff --git a/src/m-r/Roc/Roc.Models.cs b/src/m-r/Roc/Roc.Models.cs index 8a060ff3a..c7040df8d 100644 --- a/src/m-r/Roc/Roc.Models.cs +++ b/src/m-r/Roc/Roc.Models.cs @@ -1,16 +1,12 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class RocResult : ResultBase, IReusableResult +public record RocResult +( + DateTime Timestamp, + double? Momentum, + double? Roc +) : IReusable { - public RocResult(DateTime date) - { - Date = date; - } - - public double? Momentum { get; set; } - public double? Roc { get; set; } - public double? RocSma { get; set; } - - double? IReusableResult.Value => Roc; + public double Value => Roc.Null2NaN(); } diff --git a/src/m-r/Roc/Roc.Series.cs b/src/m-r/Roc/Roc.Series.cs deleted file mode 100644 index d6781bacd..000000000 --- a/src/m-r/Roc/Roc.Series.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RATE OF CHANGE (SERIES) -public static partial class Indicator -{ - internal static List CalcRoc( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - int? smaPeriods) - { - // check parameter arguments - ValidateRoc(lookbackPeriods, smaPeriods); - - // initialize - List results = new(tpList.Count); - - // roll through quotes - for (int i = 0; i < tpList.Count; i++) - { - (DateTime date, double value) = tpList[i]; - - RocResult r = new(date); - results.Add(r); - - if (i + 1 > lookbackPeriods) - { - (DateTime _, double backValue) = tpList[i - lookbackPeriods]; - - r.Momentum = (value - backValue).NaN2Null(); - r.Roc = (backValue == 0) ? null - : (100d * r.Momentum / backValue).NaN2Null(); - } - - // optional SMA - if (smaPeriods != null && i >= lookbackPeriods + smaPeriods - 1) - { - double? sumSma = 0; - for (int p = i + 1 - (int)smaPeriods; p <= i; p++) - { - sumSma += results[p].Roc; - } - - r.RocSma = sumSma / smaPeriods; - } - } - - return results; - } - - // parameter validation - private static void ValidateRoc( - int lookbackPeriods, - int? smaPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for ROC."); - } - - if (smaPeriods is not null and <= 0) - { - throw new ArgumentOutOfRangeException(nameof(smaPeriods), smaPeriods, - "SMA periods must be greater than 0 for ROC."); - } - } -} diff --git a/src/m-r/Roc/Roc.StaticSeries.cs b/src/m-r/Roc/Roc.StaticSeries.cs new file mode 100644 index 000000000..aacfbf368 --- /dev/null +++ b/src/m-r/Roc/Roc.StaticSeries.cs @@ -0,0 +1,54 @@ +namespace Skender.Stock.Indicators; + +// RATE OF CHANGE (SERIES) + +public static partial class Roc +{ + public static IReadOnlyList ToRoc( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + double roc; + double momentum; + + if (i + 1 > lookbackPeriods) + { + T back = source[i - lookbackPeriods]; + + momentum = s.Value - back.Value; + + roc = back.Value == 0 + ? double.NaN + : 100d * momentum / back.Value; + } + else + { + momentum = double.NaN; + roc = double.NaN; + } + + RocResult r = new( + Timestamp: s.Timestamp, + Momentum: momentum.NaN2Null(), + Roc: roc.NaN2Null()); + + results.Add(r); + } + + return results; + } +} diff --git a/src/m-r/Roc/Roc.Utilities.cs b/src/m-r/Roc/Roc.Utilities.cs index 473586d9d..2256c42ec 100644 --- a/src/m-r/Roc/Roc.Utilities.cs +++ b/src/m-r/Roc/Roc.Utilities.cs @@ -1,17 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// RATE OF CHANGE (UTILITIES) + +public static partial class Roc { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Roc != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for ROC."); + } } } diff --git a/src/m-r/Roc/info.xml b/src/m-r/Roc/info.xml index b9a05cf9f..c1021b2dd 100644 --- a/src/m-r/Roc/info.xml +++ b/src/m-r/Roc/info.xml @@ -14,7 +14,6 @@ Configurable Quote type. See Guide for more information. Historical price quotes. Number of periods in the lookback window. - Optional. Number of periods for an ROC SMA signal line. Time series of ROC values. Invalid parameter value provided. diff --git a/src/m-r/RocWb/Roc.Models.cs b/src/m-r/RocWb/Roc.Models.cs deleted file mode 100644 index 3b46dd0b5..000000000 --- a/src/m-r/RocWb/Roc.Models.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Skender.Stock.Indicators; - -[Serializable] -public sealed class RocWbResult : ResultBase, IReusableResult -{ - public RocWbResult(DateTime date) - { - Date = date; - } - - public double? Roc { get; set; } - public double? RocEma { get; set; } - public double? UpperBand { get; set; } - public double? LowerBand { get; set; } - - double? IReusableResult.Value => Roc; -} diff --git a/src/m-r/RocWb/RocWb.Api.cs b/src/m-r/RocWb/RocWb.Api.cs deleted file mode 100644 index 5b95ea1b3..000000000 --- a/src/m-r/RocWb/RocWb.Api.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RATE OF CHANGE (ROC) WITH BANDS (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetRocWb( - this IEnumerable quotes, - int lookbackPeriods, - int emaPeriods, - int stdDevPeriods) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcRocWb(lookbackPeriods, emaPeriods, stdDevPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetRocWb( - this IEnumerable results, - int lookbackPeriods, - int emaPeriods, - int stdDevPeriods) => results - .ToTuple() - .CalcRocWb(lookbackPeriods, emaPeriods, stdDevPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetRocWb( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods, - int emaPeriods, - int stdDevPeriods) => priceTuples - .ToSortedList() - .CalcRocWb(lookbackPeriods, emaPeriods, stdDevPeriods); -} diff --git a/src/m-r/RocWb/RocWb.Models.cs b/src/m-r/RocWb/RocWb.Models.cs new file mode 100644 index 000000000..6ed030dbb --- /dev/null +++ b/src/m-r/RocWb/RocWb.Models.cs @@ -0,0 +1,14 @@ +namespace Skender.Stock.Indicators; + +[Serializable] +public record RocWbResult +( + DateTime Timestamp, + double? Roc, + double? RocEma, + double? UpperBand, + double? LowerBand +) : IReusable +{ + public double Value => Roc.Null2NaN(); +} diff --git a/src/m-r/RocWb/RocWb.Series.cs b/src/m-r/RocWb/RocWb.Series.cs deleted file mode 100644 index 864c7bf8b..000000000 --- a/src/m-r/RocWb/RocWb.Series.cs +++ /dev/null @@ -1,107 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RATE OF CHANGE (ROC) WITH BANDS (SERIES) -public static partial class Indicator -{ - internal static List CalcRocWb( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - int emaPeriods, - int stdDevPeriods) - { - // check parameter arguments - ValidateRocWb(lookbackPeriods, emaPeriods, stdDevPeriods); - - // initialize - List results = tpList - .CalcRoc(lookbackPeriods, null) - .Select(x => new RocWbResult(x.Date) { - Roc = x.Roc - }) - .ToList(); - - double k = 2d / (emaPeriods + 1); - double? lastEma = 0; - - int length = results.Count; - - if (length > lookbackPeriods) - { - int initPeriods = Math.Min(lookbackPeriods + emaPeriods, length); - - for (int i = lookbackPeriods; i < initPeriods; i++) - { - lastEma += results[i].Roc; - } - - lastEma /= emaPeriods; - } - - double?[] rocSq = results - .Select(x => x.Roc * x.Roc) - .ToArray(); - - // roll through quotes - for (int i = lookbackPeriods; i < length; i++) - { - RocWbResult r = results[i]; - - // exponential moving average - if (i + 1 > lookbackPeriods + emaPeriods) - { - r.RocEma = lastEma + (k * (r.Roc - lastEma)); - lastEma = r.RocEma; - } - else if (i + 1 == lookbackPeriods + emaPeriods) - { - r.RocEma = lastEma; - } - - // ROC deviation - if (i + 1 >= lookbackPeriods + stdDevPeriods) - { - double? sumSq = 0; - for (int p = i - stdDevPeriods + 1; p <= i; p++) - { - sumSq += rocSq[p]; - } - - if (sumSq is not null) - { - double? rocDev = Math.Sqrt((double)sumSq / stdDevPeriods); - - r.UpperBand = rocDev; - r.LowerBand = -rocDev; - } - } - } - - return results; - } - - // parameter validation - private static void ValidateRocWb( - int lookbackPeriods, - int emaPeriods, - int stdDevPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for ROC with Bands."); - } - - if (emaPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(emaPeriods), emaPeriods, - "EMA periods must be greater than 0 for ROC."); - } - - if (stdDevPeriods <= 0 || stdDevPeriods > lookbackPeriods) - { - throw new ArgumentOutOfRangeException(nameof(stdDevPeriods), stdDevPeriods, - "Standard Deviation periods must be greater than 0 and less than lookback period for ROC with Bands."); - } - } -} diff --git a/src/m-r/RocWb/RocWb.StaticSeries.cs b/src/m-r/RocWb/RocWb.StaticSeries.cs new file mode 100644 index 000000000..5fa46b88f --- /dev/null +++ b/src/m-r/RocWb/RocWb.StaticSeries.cs @@ -0,0 +1,83 @@ +namespace Skender.Stock.Indicators; + +// RATE OF CHANGE (ROC) WITH BANDS (SERIES) + +public static partial class RocWb +{ + public static IReadOnlyList ToRocWb( + this IReadOnlyList source, + int lookbackPeriods, + int emaPeriods, + int stdDevPeriods) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods, emaPeriods, stdDevPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + double k = 2d / (emaPeriods + 1); + double prevEma = double.NaN; + + IReadOnlyList ogRoc = source + .ToRoc(lookbackPeriods); + + double[] rocSq = ogRoc + .Select(x => x.Value * x.Value) + .ToArray(); + + double[] ema = new double[length]; + + // roll through results + for (int i = 0; i < length; i++) + { + IReusable roc = ogRoc[i]; + + // exponential moving average + if (double.IsNaN(prevEma) && i >= emaPeriods) + { + double sum = 0; + for (int p = i - emaPeriods + 1; p <= i; p++) + { + sum += ogRoc[p].Value; + } + + ema[i] = sum / emaPeriods; + } + + // normal EMA + else + { + ema[i] = Ema.Increment(k, prevEma, roc.Value); + } + + prevEma = ema[i]; + + // ROC deviation + double? rocDev = null; + + if (i >= stdDevPeriods) + { + double sum = 0; + for (int p = i - stdDevPeriods + 1; p <= i; p++) + { + sum += rocSq[p]; + } + + rocDev = Math.Sqrt(sum / stdDevPeriods).NaN2Null(); + } + + results.Add(new( + Timestamp: roc.Timestamp, + Roc: roc.Value.NaN2Null(), + RocEma: ema[i].NaN2Null(), + UpperBand: rocDev, + LowerBand: -rocDev)); + } + + return results; + } +} diff --git a/src/m-r/RocWb/RocWb.Utilities.cs b/src/m-r/RocWb/RocWb.Utilities.cs index be2323824..3b94d7257 100644 --- a/src/m-r/RocWb/RocWb.Utilities.cs +++ b/src/m-r/RocWb/RocWb.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// RATE OF CHANGE (ROC) WITH BANDS (UTILITIES) + +public static partial class RocWb { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int n = results .ToList() @@ -14,4 +15,30 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(n + 100); } + + // parameter validation + internal static void Validate( + int lookbackPeriods, + int emaPeriods, + int stdDevPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for ROC with Bands."); + } + + if (emaPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(emaPeriods), emaPeriods, + "EMA periods must be greater than 0 for ROC."); + } + + if (stdDevPeriods <= 0 || stdDevPeriods > lookbackPeriods) + { + throw new ArgumentOutOfRangeException(nameof(stdDevPeriods), stdDevPeriods, + "Standard Deviation periods must be greater than 0 and less than lookback period for ROC with Bands."); + } + } } diff --git a/src/m-r/RollingPivots/RollingPivots.Api.cs b/src/m-r/RollingPivots/RollingPivots.Api.cs deleted file mode 100644 index 0c0a4807f..000000000 --- a/src/m-r/RollingPivots/RollingPivots.Api.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ROLLING PIVOT POINTS (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetRollingPivots( - this IEnumerable quotes, - int windowPeriods, - int offsetPeriods, - PivotPointType pointType = PivotPointType.Standard) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcRollingPivots(windowPeriods, offsetPeriods, pointType); -} diff --git a/src/m-r/RollingPivots/RollingPivots.Models.cs b/src/m-r/RollingPivots/RollingPivots.Models.cs index b1b49434c..c5782f236 100644 --- a/src/m-r/RollingPivots/RollingPivots.Models.cs +++ b/src/m-r/RollingPivots/RollingPivots.Models.cs @@ -1,15 +1,19 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class RollingPivotsResult : ResultBase, IPivotPoint +public record RollingPivotsResult : ISeries, IPivotPoint { - public decimal? R4 { get; set; } - public decimal? R3 { get; set; } - public decimal? R2 { get; set; } - public decimal? R1 { get; set; } - public decimal? PP { get; set; } - public decimal? S1 { get; set; } - public decimal? S2 { get; set; } - public decimal? S3 { get; set; } - public decimal? S4 { get; set; } + public DateTime Timestamp { get; init; } + + public decimal? PP { get; init; } + + public decimal? S1 { get; init; } + public decimal? S2 { get; init; } + public decimal? S3 { get; init; } + public decimal? S4 { get; init; } + + public decimal? R1 { get; init; } + public decimal? R2 { get; init; } + public decimal? R3 { get; init; } + public decimal? R4 { get; init; } } diff --git a/src/m-r/RollingPivots/RollingPivots.Series.cs b/src/m-r/RollingPivots/RollingPivots.Series.cs deleted file mode 100644 index 2b3b7d138..000000000 --- a/src/m-r/RollingPivots/RollingPivots.Series.cs +++ /dev/null @@ -1,85 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PIVOT POINTS (SERIES) -public static partial class Indicator -{ - internal static List CalcRollingPivots( - this List quotesList, - int windowPeriods, - int offsetPeriods, - PivotPointType pointType) - where TQuote : IQuote - { - // check parameter arguments - ValidateRollingPivots(windowPeriods, offsetPeriods); - - // initialize - int length = quotesList.Count; - List results = new(length); - - // roll through quotes - for (int i = 0; i < length; i++) - { - TQuote q = quotesList[i]; - - RollingPivotsResult r = new() { - Date = q.Date - }; - - if (i >= windowPeriods + offsetPeriods) - { - // window values - int s = i - windowPeriods - offsetPeriods; - TQuote hi = quotesList[s]; - - decimal windowHigh = hi.High; - decimal windowLow = hi.Low; - decimal windowClose = quotesList[i - offsetPeriods - 1].Close; - - for (int p = s; p <= i - offsetPeriods - 1; p++) - { - TQuote d = quotesList[p]; - windowHigh = (d.High > windowHigh) ? d.High : windowHigh; - windowLow = (d.Low < windowLow) ? d.Low : windowLow; - } - - // pivot points - RollingPivotsResult wp = GetPivotPoint( - pointType, q.Open, windowHigh, windowLow, windowClose); - - r.PP = wp.PP; - r.S1 = wp.S1; - r.S2 = wp.S2; - r.S3 = wp.S3; - r.S4 = wp.S4; - r.R1 = wp.R1; - r.R2 = wp.R2; - r.R3 = wp.R3; - r.R4 = wp.R4; - } - - results.Add(r); - } - - return results; - } - - // parameter validation - private static void ValidateRollingPivots( - int windowPeriods, - int offsetPeriods) - { - // check parameter arguments - if (windowPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(windowPeriods), windowPeriods, - "Window periods must be greater than 0 for Rolling Pivot Points."); - } - - if (offsetPeriods < 0) - { - throw new ArgumentOutOfRangeException(nameof(offsetPeriods), offsetPeriods, - "Offset periods must be greater than or equal to 0 for Rolling Pivot Points."); - } - } -} diff --git a/src/m-r/RollingPivots/RollingPivots.StaticSeries.cs b/src/m-r/RollingPivots/RollingPivots.StaticSeries.cs new file mode 100644 index 000000000..efbc07fde --- /dev/null +++ b/src/m-r/RollingPivots/RollingPivots.StaticSeries.cs @@ -0,0 +1,80 @@ +namespace Skender.Stock.Indicators; + +// ROLLING PIVOT POINTS (SERIES) + +public static partial class RollingPivots +{ + public static IReadOnlyList ToRollingPivots( + this IReadOnlyList quotes, + int windowPeriods, + int offsetPeriods, + PivotPointType pointType = PivotPointType.Standard) + where TQuote : IQuote + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(quotes); + Validate(windowPeriods, offsetPeriods); + + // initialize + int length = quotes.Count; + List results = new(length); + + // roll through source values + for (int i = 0; i < length; i++) + { + TQuote q = quotes[i]; + + RollingPivotsResult r; + + if (i >= windowPeriods + offsetPeriods) + { + // window values + int s = i - windowPeriods - offsetPeriods; + TQuote hi = quotes[s]; + + decimal windowHigh = hi.High; + decimal windowLow = hi.Low; + decimal windowClose = quotes[i - offsetPeriods - 1].Close; + + for (int p = s; p <= i - offsetPeriods - 1; p++) + { + TQuote d = quotes[p]; + windowHigh = d.High > windowHigh ? d.High : windowHigh; + windowLow = d.Low < windowLow ? d.Low : windowLow; + } + + // pivot points + WindowPoint wp = PivotPoints.GetPivotPoint( + pointType, q.Open, windowHigh, windowLow, windowClose); + + r = new() { + + Timestamp = q.Timestamp, + + // pivot point + PP = wp.PP, + + // support + S1 = wp.S1, + S2 = wp.S2, + S3 = wp.S3, + S4 = wp.S4, + + // resistance + R1 = wp.R1, + R2 = wp.R2, + R3 = wp.R3, + R4 = wp.R4 + }; + } + else + { + r = new() { Timestamp = q.Timestamp }; + } + + results.Add(r); + } + + return results; + } +} diff --git a/src/m-r/RollingPivots/RollingPivots.Utilities.cs b/src/m-r/RollingPivots/RollingPivots.Utilities.cs index 6c897032f..95a88b44b 100644 --- a/src/m-r/RollingPivots/RollingPivots.Utilities.cs +++ b/src/m-r/RollingPivots/RollingPivots.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ROLLING PIVOT POINTS (UTILITIES) + +public static partial class RollingPivots { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -14,4 +15,23 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int windowPeriods, + int offsetPeriods) + { + // check parameter arguments + if (windowPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(windowPeriods), windowPeriods, + "Window periods must be greater than 0 for Rolling Pivot Points."); + } + + if (offsetPeriods < 0) + { + throw new ArgumentOutOfRangeException(nameof(offsetPeriods), offsetPeriods, + "Offset periods must be greater than or equal to 0 for Rolling Pivot Points."); + } + } } diff --git a/src/m-r/Rsi/Rsi.Api.cs b/src/m-r/Rsi/Rsi.Api.cs deleted file mode 100644 index 31900a55a..000000000 --- a/src/m-r/Rsi/Rsi.Api.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RELATIVE STRENGTH INDEX (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetRsi( - this IEnumerable quotes, - int lookbackPeriods = 14) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcRsi(lookbackPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetRsi( - this IEnumerable results, - int lookbackPeriods = 14) => results - .ToTuple() - .CalcRsi(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetRsi( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods = 14) => priceTuples - .ToSortedList() - .CalcRsi(lookbackPeriods); -} diff --git a/src/m-r/Rsi/Rsi.Models.cs b/src/m-r/Rsi/Rsi.Models.cs index d0d2268e6..2689789e5 100644 --- a/src/m-r/Rsi/Rsi.Models.cs +++ b/src/m-r/Rsi/Rsi.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class RsiResult : ResultBase, IReusableResult +public record RsiResult +( + DateTime Timestamp, + double? Rsi = null +) : IReusable { - public RsiResult(DateTime date) - { - Date = date; - } - - public double? Rsi { get; set; } - - double? IReusableResult.Value => Rsi; + public double Value => Rsi.Null2NaN(); } diff --git a/src/m-r/Rsi/Rsi.Series.cs b/src/m-r/Rsi/Rsi.Series.cs deleted file mode 100644 index 41b1dafb2..000000000 --- a/src/m-r/Rsi/Rsi.Series.cs +++ /dev/null @@ -1,94 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RELATIVE STRENGTH INDEX (SERIES) -public static partial class Indicator -{ - internal static List CalcRsi( - this List<(DateTime Date, double Value)> tpList, - int lookbackPeriods) - { - // check parameter arguments - ValidateRsi(lookbackPeriods); - - // initialize - int length = tpList.Count; - double avgGain = 0; - double avgLoss = 0; - - List results = new(length); - double[] gain = new double[length]; // gain - double[] loss = new double[length]; // loss - double lastValue; - - if (length == 0) - { - return results; - } - else - { - lastValue = tpList[0].Value; - } - - // roll through quotes - for (int i = 0; i < length; i++) - { - (DateTime date, double value) = tpList[i]; - - RsiResult r = new(date); - results.Add(r); - - gain[i] = (value > lastValue) ? value - lastValue : 0; - loss[i] = (value < lastValue) ? lastValue - value : 0; - lastValue = value; - - // calculate RSI - if (i > lookbackPeriods) - { - 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 (i == lookbackPeriods) - { - 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; - } - - // parameter validation - private static void ValidateRsi( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods < 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for RSI."); - } - } -} diff --git a/src/m-r/Rsi/Rsi.StaticSeries.cs b/src/m-r/Rsi/Rsi.StaticSeries.cs new file mode 100644 index 000000000..fe0163cdf --- /dev/null +++ b/src/m-r/Rsi/Rsi.StaticSeries.cs @@ -0,0 +1,96 @@ +namespace Skender.Stock.Indicators; + +// RELATIVE STRENGTH INDEX (SERIES) + +public static partial class Rsi +{ + public static IReadOnlyList ToRsi( + this IReadOnlyList source, + int lookbackPeriods = 14) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + double avgGain = double.NaN; + double avgLoss = double.NaN; + + List results = new(length); + double[] gain = new double[length]; // gain + double[] loss = new double[length]; // loss + + if (length == 0) + { + return results; + } + + double prevValue = source[0].Value; + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + if (double.IsNaN(s.Value) || double.IsNaN(prevValue)) + { + gain[i] = loss[i] = double.NaN; + } + else + { + gain[i] = s.Value > prevValue ? s.Value - prevValue : 0; + loss[i] = s.Value < prevValue ? prevValue - s.Value : 0; + } + + double? rsi = null; + prevValue = s.Value; + + // re/initialize average gain + if (i >= lookbackPeriods && (double.IsNaN(avgGain) || double.IsNaN(avgLoss))) + { + double sumGain = 0; + double sumLoss = 0; + + for (int p = i - lookbackPeriods + 1; p <= i; p++) + { + sumGain += gain[p]; + sumLoss += loss[p]; + } + + avgGain = sumGain / lookbackPeriods; + avgLoss = sumLoss / lookbackPeriods; + + rsi = !double.IsNaN(avgGain / avgLoss) + ? avgLoss > 0 ? 100 - 100 / (1 + avgGain / avgLoss) : 100 + : null; + } + + // calculate RSI normally + else if (i > lookbackPeriods) + { + avgGain = (avgGain * (lookbackPeriods - 1) + gain[i]) / lookbackPeriods; + avgLoss = (avgLoss * (lookbackPeriods - 1) + loss[i]) / lookbackPeriods; + + if (avgLoss > 0) + { + double rs = avgGain / avgLoss; + rsi = 100 - 100 / (1 + rs); + } + else + { + rsi = 100; + } + } + + RsiResult r = new( + Timestamp: s.Timestamp, + Rsi: rsi); + + results.Add(r); + } + + return results; + } +} diff --git a/src/m-r/Rsi/Rsi.Utilities.cs b/src/m-r/Rsi/Rsi.Utilities.cs index a6112933e..c82bab854 100644 --- a/src/m-r/Rsi/Rsi.Utilities.cs +++ b/src/m-r/Rsi/Rsi.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// RELATIVE STRENGTH INDEX (UTILITIES) + +public static partial class Rsi { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int n = results .ToList() @@ -14,4 +15,16 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(10 * n); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods < 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for RSI."); + } + } } diff --git a/src/s-z/Slope/Slope.Api.cs b/src/s-z/Slope/Slope.Api.cs deleted file mode 100644 index c915e9e8b..000000000 --- a/src/s-z/Slope/Slope.Api.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SLOPE AND LINEAR REGRESSION (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetSlope( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcSlope(lookbackPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetSlope( - this IEnumerable results, - int lookbackPeriods) => results - .ToTuple() - .CalcSlope(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetSlope( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods) => priceTuples - .ToSortedList() - .CalcSlope(lookbackPeriods); -} diff --git a/src/s-z/Slope/Slope.Models.cs b/src/s-z/Slope/Slope.Models.cs index 202dd32db..a9488d2c7 100644 --- a/src/s-z/Slope/Slope.Models.cs +++ b/src/s-z/Slope/Slope.Models.cs @@ -1,18 +1,15 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class SlopeResult : ResultBase, IReusableResult +public record SlopeResult +( + DateTime Timestamp, + double? Slope = null, + double? Intercept = null, + double? StdDev = null, + double? RSquared = null, + decimal? Line = null // last line segment only +) : IReusable { - public SlopeResult(DateTime date) - { - Date = date; - } - - public double? Slope { get; set; } - public double? Intercept { get; set; } - public double? StdDev { get; set; } - public double? RSquared { get; set; } - public decimal? Line { get; set; } // last line segment only - - double? IReusableResult.Value => Slope; + public double Value => Slope.Null2NaN(); } diff --git a/src/s-z/Slope/Slope.Series.cs b/src/s-z/Slope/Slope.Series.cs deleted file mode 100644 index 52553f7ca..000000000 --- a/src/s-z/Slope/Slope.Series.cs +++ /dev/null @@ -1,104 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SLOPE AND LINEAR REGRESSION (SERIES) -public static partial class Indicator -{ - // calculate series - internal static List CalcSlope( - this List<(DateTime, double)> tpList, - int lookbackPeriods) - { - // check parameter arguments - ValidateSlope(lookbackPeriods); - - // initialize - int length = tpList.Count; - List results = new(length); - - // roll through quotes - for (int i = 0; i < length; i++) - { - (DateTime date, double _) = tpList[i]; - - SlopeResult r = new(date); - results.Add(r); - - // skip initialization period - if (i + 1 < lookbackPeriods) - { - continue; - } - - // get averages for period - double sumX = 0; - double sumY = 0; - - for (int p = i - lookbackPeriods + 1; p <= i; p++) - { - (DateTime _, double pValue) = tpList[p]; - - sumX += p + 1d; - sumY += pValue; - } - - double avgX = sumX / lookbackPeriods; - double avgY = sumY / lookbackPeriods; - - // least squares method - double sumSqX = 0; - double sumSqY = 0; - double sumSqXY = 0; - - for (int p = i - lookbackPeriods + 1; p <= i; p++) - { - (DateTime _, double pValue) = tpList[p]; - - double devX = p + 1d - avgX; - double devY = pValue - avgY; - - sumSqX += devX * devX; - sumSqY += devY * devY; - sumSqXY += devX * devY; - } - - r.Slope = (sumSqXY / sumSqX).NaN2Null(); - r.Intercept = (avgY - (r.Slope * avgX)).NaN2Null(); - - // calculate Standard Deviation and R-Squared - double stdDevX = Math.Sqrt(sumSqX / lookbackPeriods); - double stdDevY = Math.Sqrt(sumSqY / lookbackPeriods); - r.StdDev = stdDevY.NaN2Null(); - - if (stdDevX * stdDevY != 0) - { - double arrr = sumSqXY / (stdDevX * stdDevY) / lookbackPeriods; - r.RSquared = (arrr * arrr).NaN2Null(); - } - } - - // 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).NaN2Null(); - } - } - - return results; - } - - // parameter validation - private static void ValidateSlope( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for Slope/Linear Regression."); - } - } -} diff --git a/src/s-z/Slope/Slope.StaticSeries.cs b/src/s-z/Slope/Slope.StaticSeries.cs new file mode 100644 index 000000000..6082405ed --- /dev/null +++ b/src/s-z/Slope/Slope.StaticSeries.cs @@ -0,0 +1,111 @@ +namespace Skender.Stock.Indicators; + +// SLOPE AND LINEAR REGRESSION (SERIES) + +public static partial class Slope +{ + public static IReadOnlyList ToSlope( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + // skip initialization period + if (i < lookbackPeriods - 1) + { + results.Add(new(s.Timestamp)); + continue; + } + + // get averages for period + double sumX = 0; + double sumY = 0; + + for (int p = i - lookbackPeriods + 1; p <= i; p++) + { + T ps = source[p]; + + sumX += p + 1d; + sumY += ps.Value; + } + + double avgX = sumX / lookbackPeriods; + double avgY = sumY / lookbackPeriods; + + // least squares method + double sumSqX = 0; + double sumSqY = 0; + double sumSqXy = 0; + + for (int p = i - lookbackPeriods + 1; p <= i; p++) + { + T ps = source[p]; + + double devX = p + 1d - avgX; + double devY = ps.Value - avgY; + + sumSqX += devX * devX; + sumSqY += devY * devY; + sumSqXy += devX * devY; + } + + double? slope = (sumSqXy / sumSqX).NaN2Null(); + double? intercept = (avgY - (slope * avgX)).NaN2Null(); + + // calculate Standard Deviation and R-Squared + double stdDevX = Math.Sqrt(sumSqX / lookbackPeriods); + double stdDevY = Math.Sqrt(sumSqY / lookbackPeriods); + + double? rSquared = null; + + if (stdDevX * stdDevY != 0) + { + double arrr = sumSqXy / (stdDevX * stdDevY) / lookbackPeriods; + rSquared = (arrr * arrr).NaN2Null(); + } + + // write results + SlopeResult r = new( + Timestamp: s.Timestamp, + Slope: slope, + Intercept: intercept, + StdDev: stdDevY.NaN2Null(), + RSquared: rSquared, + Line: null); // re-written below + + results.Add(r); + } + + // insufficient length for last line + if (length < lookbackPeriods) + { + return results; + } + + // add last Line (y = mx + b) + SlopeResult last = results.Last(); + + for (int p = length - lookbackPeriods; p < length; p++) + { + SlopeResult d = results[p]; + + results[p] = d with { + Line = (decimal?)((last.Slope * (p + 1)) + last.Intercept).NaN2Null() + }; + } + + return results; + } +} diff --git a/src/s-z/Slope/Slope.Utilities.cs b/src/s-z/Slope/Slope.Utilities.cs index c41549edb..f04ed9b5e 100644 --- a/src/s-z/Slope/Slope.Utilities.cs +++ b/src/s-z/Slope/Slope.Utilities.cs @@ -1,17 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// SLOPE AND LINEAR REGRESSION (UTILITIES) + +public static partial class Slope { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Slope != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for Slope/Linear Regression."); + } } } diff --git a/src/s-z/Sma/Sma.Analysis.cs b/src/s-z/Sma/Sma.Analysis.cs deleted file mode 100644 index a4cddf4ee..000000000 --- a/src/s-z/Sma/Sma.Analysis.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SIMPLE MOVING AVERAGE (ANALYSIS) -public static partial class Indicator -{ - internal static IEnumerable CalcSmaAnalysis( - this List<(DateTime, double)> tpList, - int lookbackPeriods) - { - // initialize - List results = tpList - .CalcSma(lookbackPeriods) - .Select(x => new SmaAnalysis(x.Date) { Sma = x.Sma }) - .ToList(); - - // roll through quotes - for (int i = lookbackPeriods - 1; i < results.Count; i++) - { - SmaAnalysis r = results[i]; - double sma = (r.Sma == null) ? double.NaN : (double)r.Sma; - - double sumMad = 0; - double sumMse = 0; - double sumMape = 0; - - for (int p = i + 1 - lookbackPeriods; p <= i; p++) - { - (DateTime _, double value) = tpList[p]; - - sumMad += Math.Abs(value - sma); - sumMse += (value - sma) * (value - sma); - - sumMape += (value == 0) ? double.NaN - : Math.Abs(value - sma) / value; - } - - // mean absolute deviation - r.Mad = (sumMad / lookbackPeriods).NaN2Null(); - - // mean squared error - r.Mse = (sumMse / lookbackPeriods).NaN2Null(); - - // mean absolute percent error - r.Mape = (sumMape / lookbackPeriods).NaN2Null(); - } - - return results; - } -} diff --git a/src/s-z/Sma/Sma.Api.cs b/src/s-z/Sma/Sma.Api.cs deleted file mode 100644 index 5e21293dd..000000000 --- a/src/s-z/Sma/Sma.Api.cs +++ /dev/null @@ -1,77 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SIMPLE MOVING AVERAGE (API) - -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetSma( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcSma(lookbackPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetSma( - this IEnumerable results, - int lookbackPeriods) => results - .ToTuple() - .CalcSma(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetSma( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods) => priceTuples - .ToSortedList() - .CalcSma(lookbackPeriods); - - // OBSERVER, from Quote Provider - /// - /// - public static SmaObserver GetSma( - this QuoteProvider provider, - int lookbackPeriods) - { - UseObserver useObserver = provider - .Use(CandlePart.Close); - - return new(useObserver, lookbackPeriods); - } - - // OBSERVER, from Chain Provider - /// - /// - public static SmaObserver GetSma( - this TupleProvider tupleProvider, - int lookbackPeriods) - => new(tupleProvider, lookbackPeriods); - - /// - /// - // ANALYSIS, from TQuote - public static IEnumerable GetSmaAnalysis( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcSmaAnalysis(lookbackPeriods); - - // ANALYSIS, from CHAIN - public static IEnumerable GetSmaAnalysis( - this IEnumerable results, - int lookbackPeriods) => results - .ToTuple() - .CalcSmaAnalysis(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // ANALYSIS, from TUPLE - public static IEnumerable GetSmaAnalysis( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods) => priceTuples - .ToSortedList() - .CalcSmaAnalysis(lookbackPeriods); -} diff --git a/src/s-z/Sma/Sma.Models.cs b/src/s-z/Sma/Sma.Models.cs index 04f6371f5..f0dfcd857 100644 --- a/src/s-z/Sma/Sma.Models.cs +++ b/src/s-z/Sma/Sma.Models.cs @@ -1,30 +1,10 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class SmaResult : ResultBase, IReusableResult +public record SmaResult( + DateTime Timestamp, + double? Sma +) : IReusable { - public SmaResult(DateTime date) - { - Date = date; - } - - public double? Sma { get; set; } - - double? IReusableResult.Value => Sma; -} - -[Serializable] -public sealed class SmaAnalysis : ResultBase, IReusableResult -{ - public SmaAnalysis(DateTime date) - { - Date = date; - } - - public double? Sma { get; set; } // simple moving average - public double? Mad { get; set; } // mean absolute deviation - public double? Mse { get; set; } // mean square error - public double? Mape { get; set; } // mean absolute percentage error - - double? IReusableResult.Value => Sma; + public double Value => Sma.Null2NaN(); } diff --git a/src/s-z/Sma/Sma.Observer.cs b/src/s-z/Sma/Sma.Observer.cs deleted file mode 100644 index f0227f4cd..000000000 --- a/src/s-z/Sma/Sma.Observer.cs +++ /dev/null @@ -1,139 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SIMPLE MOVING AVERAGE (STREAMING) - -public class SmaObserver : ChainProvider -{ - public SmaObserver( - TupleProvider provider, - int lookbackPeriods) - { - Supplier = provider; - ProtectedResults = []; - - LookbackPeriods = lookbackPeriods; - - Initialize(); - } - - // PROPERTIES - - public IEnumerable Results => ProtectedResults; - internal List ProtectedResults { get; set; } - - private int LookbackPeriods { get; set; } - - // STATIC METHODS - - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for SMA."); - } - } - - // incremental calculation - internal static double Increment( - List<(DateTime Date, double Value)> values, - int index, - int lookbackPeriods) - { - if (index < lookbackPeriods - 1) - { - return double.NaN; - } - - double sum = 0; - for (int i = index - lookbackPeriods + 1; i <= index; i++) - { - sum += values[i].Value; - } - - return sum / lookbackPeriods; - } - - // NON-STATIC METHODS - - // handle quote arrival - public override void OnNext((DateTime Date, double Value) value) => Add(value); - - // add new tuple quote - internal void Add((DateTime Date, double Value) tp) - { - // candidate result - SmaResult r = new(tp.Date); - - // initialize - int lengthRes = ProtectedResults.Count; - int lengthSrc = Supplier!.ProtectedTuples.Count; // merge fix, okay to replace - - // handle first value - if (lengthRes == 0) - { - ProtectedResults.Add(r); - SendToChain(r); - return; - } - - SmaResult lastResult = ProtectedResults[lengthRes - 1]; - (DateTime lastSrcDate, double _) = Supplier.ProtectedTuples[lengthSrc - 1]; - - if (r.Date == lastSrcDate) - { - r.Sma = Increment( - Supplier.ProtectedTuples, - lengthSrc - 1, - LookbackPeriods) - .NaN2Null(); - } - - // add new - if (r.Date > lastResult.Date) - { - ProtectedResults.Add(r); - SendToChain(r); - } - - // update last - else if (r.Date == lastResult.Date) - { - lastResult.Sma = r.Sma; - SendToChain(lastResult); - } - - // late arrival - else - { - // heal - throw new NotImplementedException(); - - // existing and index in sync? - - // new and index otherwise in sync? - - // all other scenarios: unsubscribe from provider and end transmission to others? - } - } - - // calculate with provider cache - private void Initialize() - { - if (Supplier != null) - { - List<(DateTime, double)> tuples = Supplier - .ProtectedTuples; - - for (int i = 0; i < tuples.Count; i++) - { - Add(tuples[i]); - } - - Subscribe(); - } - } -} diff --git a/src/s-z/Sma/Sma.Series.cs b/src/s-z/Sma/Sma.Series.cs deleted file mode 100644 index 2541de516..000000000 --- a/src/s-z/Sma/Sma.Series.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SIMPLE MOVING AVERAGE (SERIES) -public static partial class Indicator -{ - internal static List CalcSma( - this List<(DateTime, double)> tpList, - int lookbackPeriods) - { - // check parameter arguments - SmaObserver.Validate(lookbackPeriods); - - // initialize - List results = new(tpList.Count); - - // roll through quotes - for (int i = 0; i < tpList.Count; i++) - { - (DateTime date, double _) = tpList[i]; - - SmaResult result = new(date); - results.Add(result); - - result.Sma = SmaObserver - .Increment(tpList, i, lookbackPeriods) - .NaN2Null(); - } - - return results; - } -} diff --git a/src/s-z/Sma/Sma.StaticSeries.cs b/src/s-z/Sma/Sma.StaticSeries.cs new file mode 100644 index 000000000..a0b7a5c39 --- /dev/null +++ b/src/s-z/Sma/Sma.StaticSeries.cs @@ -0,0 +1,58 @@ +namespace Skender.Stock.Indicators; + +// SIMPLE MOVING AVERAGE (SERIES) + +public static partial class Sma +{ + public static IReadOnlyList ToSma( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + SmaResult[] results = new SmaResult[length]; + double[] values = new double[length]; + + for (int i = 0; i < length; i++) + { + values[i] = source[i].Value; + } + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + double sma; + + if (i >= lookbackPeriods - 1) + { + double sum = 0; + int end = i + 1; + int start = end - lookbackPeriods; + + for (int p = start; p < end; p++) + { + sum += source[p].Value; + } + + sma = sum / lookbackPeriods; + } + else + { + sma = double.NaN; + } + + results[i] = new SmaResult( + Timestamp: s.Timestamp, + Sma: sma.NaN2Null()); + } + + return new List(results); + } +} diff --git a/src/s-z/Sma/Sma.StreamHub.cs b/src/s-z/Sma/Sma.StreamHub.cs new file mode 100644 index 000000000..150609bb2 --- /dev/null +++ b/src/s-z/Sma/Sma.StreamHub.cs @@ -0,0 +1,60 @@ +namespace Skender.Stock.Indicators; + +// SIMPLE MOVING AVERAGE (STREAM HUB) + +#region hub interface and initializer + +public interface ISmaHub +{ + int LookbackPeriods { get; } +} + +public static partial class Sma +{ + public static SmaHub ToSma( + this IChainProvider chainProvider, + int lookbackPeriods) + where TIn : IReusable + => new(chainProvider, lookbackPeriods); +} +#endregion + +public class SmaHub + : ChainProvider, ISmaHub + where TIn : IReusable +{ + #region constructors + + private readonly string hubName; + + internal SmaHub( + IChainProvider provider, + int lookbackPeriods) : base(provider) + { + Sma.Validate(lookbackPeriods); + LookbackPeriods = lookbackPeriods; + hubName = $"SMA({lookbackPeriods})"; + + Reinitialize(); + } + #endregion + + public int LookbackPeriods { get; init; } + + // METHODS + + public override string ToString() => hubName; + + protected override (SmaResult result, int index) + ToIndicator(TIn item, int? indexHint) + { + int i = indexHint ?? ProviderCache.GetIndex(item, true); + + // candidate result + SmaResult r = new( + Timestamp: item.Timestamp, + Sma: Sma.Increment(ProviderCache, LookbackPeriods, i).NaN2Null()); + + return (r, i); + } +} diff --git a/src/s-z/Sma/Sma.Utilities.cs b/src/s-z/Sma/Sma.Utilities.cs index 4a14403eb..f8700d185 100644 --- a/src/s-z/Sma/Sma.Utilities.cs +++ b/src/s-z/Sma/Sma.Utilities.cs @@ -1,30 +1,116 @@ +using System.Numerics; + namespace Skender.Stock.Indicators; -public static partial class Indicator +// SIMPLE MOVING AVERAGE (UTILITIES) + +public static partial class Sma { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + /// Simple moving average calculation + /// + /// List of chainable values + /// + /// Window to evaluate, prior to 'endIndex' + /// + /// + /// Index position to evaluate or last position when . + /// + /// IReusable (chainable) type + /// + /// Simple moving average or + /// if incalculable + /// values are in range. + /// + internal static double? Average( + this IReadOnlyList values, + int lookbackPeriods, + int? endIndex = null) + where T : IReusable + + // TODO: unused SMA utility, either make public or remove + + => Increment( + values, + lookbackPeriods, + endIndex ?? values.Count - 1) + .NaN2Null(); + + /// + /// Simple moving average calculation + /// + /// List of chainable values + /// Window to evaluate, prior to 'endIndex' + /// Index position to evaluate. + /// IReusable (chainable) type + /// + /// Simple moving average or + /// when incalculable. + /// + internal static double Increment( + IReadOnlyList source, + int lookbackPeriods, + int endIndex) + where T : IReusable { - int removePeriods = results - .ToList() - .FindIndex(x => x.Sma != null); + if (endIndex < lookbackPeriods - 1 || endIndex >= source.Count) + { + return double.NaN; + } + + double sum = 0; + for (int i = endIndex - lookbackPeriods + 1; i <= endIndex; i++) + { + sum += source[i].Value; + } - return results.Remove(removePeriods); + return sum / lookbackPeriods; + + // TODO: apply this SMA increment method more widely in other indicators (see EMA example) } - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + internal static double[] Increment(this double[] prices, int period) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Sma != null); + // TODO: is this used (probably just an experiment, has rounding errors) + + int count = prices.Length - period + 1; + double[] sma = new double[count]; - return results.Remove(removePeriods); + int simdWidth = Vector.Count; + for (int i = 0; i < count; i++) + { + Vector sumVector = Vector.Zero; + + int j; + for (j = 0; j <= period - simdWidth; j += simdWidth) + { + Vector priceVector = new(prices, i + j); + sumVector += priceVector; + } + + double sum = 0; + for (; j < period; j++) // remainder loop + { + sum += prices[i + j]; + } + sum += Vector.Dot(sumVector, Vector.One); + + sma[i] = sum / period; + } + + return sma; + } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for SMA."); + } } } diff --git a/src/s-z/Sma/info.xml b/src/s-z/Sma/info.xml index 386418d41..2e257ef90 100644 --- a/src/s-z/Sma/info.xml +++ b/src/s-z/Sma/info.xml @@ -2,7 +2,7 @@ - + Simple Moving Average (SMA) of the price. @@ -17,35 +17,8 @@ Time series of SMA values. Invalid parameter value provided. - - - Establish an observable streaming Exponential Moving Average (EMA). - - See - documentation - for more information. - - - Observable quote provider. - Number of periods in the lookback window. - Observable EMA instance. - Invalid parameter value provided. - - - - Chain from an observable streaming Simple Moving Average (SMA). - - See - documentation - for more information. - - - Observable from chained indicator. - Number of periods in the lookback window. - Observable SMA instance. - Invalid parameter value provided. - - + + Simple Moving Average (SMA) is the average of price over a lookback window. This extended variant includes mean absolute deviation (MAD), mean square error (MSE), and mean absolute percentage error (MAPE). @@ -61,4 +34,4 @@ Invalid parameter value provided. - \ No newline at end of file + diff --git a/src/s-z/SmaAnalysis/SmaAnalysis.Models.cs b/src/s-z/SmaAnalysis/SmaAnalysis.Models.cs new file mode 100644 index 000000000..055f5c004 --- /dev/null +++ b/src/s-z/SmaAnalysis/SmaAnalysis.Models.cs @@ -0,0 +1,19 @@ +namespace Skender.Stock.Indicators; + +/// +/// SMA with extended analysis. +/// +/// Timestamp +/// Simple moving average +/// Mean absolute deviation +/// Mean square error +/// Mean absolute percentage error +[Serializable] +public record SmaAnalysis +( + DateTime Timestamp, + double? Sma = null, + double? Mad = null, + double? Mse = null, + double? Mape = null +) : SmaResult(Timestamp, Sma); diff --git a/src/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.cs b/src/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.cs new file mode 100644 index 000000000..d43770795 --- /dev/null +++ b/src/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.cs @@ -0,0 +1,54 @@ +namespace Skender.Stock.Indicators; + +// SIMPLE MOVING AVERAGE (ANALYSIS) + +public static partial class Sma +{ + public static IReadOnlyList ToSmaAnalysis( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + { + // initialize + List results = source + .ToSma(lookbackPeriods) + .Select(s => new SmaAnalysis(s.Timestamp, s.Sma)) + .ToList(); + + // roll through source values + for (int i = lookbackPeriods - 1; i < results.Count; i++) + { + SmaAnalysis r = results[i]; + double sma = r.Sma ?? double.NaN; + + double sumMad = 0; + double sumMse = 0; + double sumMape = 0; + + for (int p = i + 1 - lookbackPeriods; p <= i; p++) + { + T s = source[p]; + + sumMad += Math.Abs(s.Value - sma); + sumMse += (s.Value - sma) * (s.Value - sma); + + sumMape += s.Value == 0 ? double.NaN + : Math.Abs(s.Value - sma) / s.Value; + } + + results[i] = r with { + + // mean absolute deviation + Mad = (sumMad / lookbackPeriods).NaN2Null(), + + // mean squared error + Mse = (sumMse / lookbackPeriods).NaN2Null(), + + // mean absolute percent error + Mape = (sumMape / lookbackPeriods).NaN2Null() + }; + } + + return results; + } +} diff --git a/src/s-z/Smi/Smi.Api.cs b/src/s-z/Smi/Smi.Api.cs deleted file mode 100644 index 924ab5c84..000000000 --- a/src/s-z/Smi/Smi.Api.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STOCHASTIC MOMENTUM INDEX (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetSmi( - this IEnumerable quotes, - int lookbackPeriods = 13, - int firstSmoothPeriods = 25, - int secondSmoothPeriods = 2, - int signalPeriods = 3) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcSmi( - lookbackPeriods, - firstSmoothPeriods, - secondSmoothPeriods, - signalPeriods); -} diff --git a/src/s-z/Smi/Smi.Models.cs b/src/s-z/Smi/Smi.Models.cs index d0fc2018e..739c082fc 100644 --- a/src/s-z/Smi/Smi.Models.cs +++ b/src/s-z/Smi/Smi.Models.cs @@ -1,15 +1,12 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class SmiResult : ResultBase, IReusableResult +public record SmiResult +( + DateTime Timestamp, + double? Smi, + double? Signal +) : IReusable { - public SmiResult(DateTime date) - { - Date = date; - } - - public double? Smi { get; set; } - public double? Signal { get; set; } - - double? IReusableResult.Value => Smi; + public double Value => Smi.Null2NaN(); } diff --git a/src/s-z/Smi/Smi.Series.cs b/src/s-z/Smi/Smi.Series.cs deleted file mode 100644 index 7eb6f3148..000000000 --- a/src/s-z/Smi/Smi.Series.cs +++ /dev/null @@ -1,140 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STOCHASTIC MOMENTUM INDEX (SERIES) -public static partial class Indicator -{ - internal static List CalcSmi( - this List qdList, - int lookbackPeriods, - int firstSmoothPeriods, - int secondSmoothPeriods, - int signalPeriods) - { - // check parameter arguments - ValidateSmi( - lookbackPeriods, - firstSmoothPeriods, - secondSmoothPeriods, - signalPeriods); - - // initialize - int length = qdList.Count; - List results = new(length); - - double k1 = 2d / (firstSmoothPeriods + 1); - double k2 = 2d / (secondSmoothPeriods + 1); - double kS = 2d / (signalPeriods + 1); - - double lastSmEma1 = 0; - double lastSmEma2 = 0; - double lastHlEma1 = 0; - double lastHlEma2 = 0; - double lastSignal = 0; - - // roll through quotes - for (int i = 0; i < length; i++) - { - QuoteD q = qdList[i]; - - SmiResult r = new(q.Date); - results.Add(r); - - if (i + 1 >= lookbackPeriods) - { - double hH = double.MinValue; - double lL = double.MaxValue; - - for (int p = i + 1 - lookbackPeriods; p <= i; p++) - { - QuoteD x = qdList[p]; - - if (x.High > hH) - { - hH = x.High; - } - - if (x.Low < lL) - { - lL = x.Low; - } - } - - double sm = q.Close - (0.5d * (hH + lL)); - double hl = hH - lL; - - // initialize last EMA values - if (i + 1 == lookbackPeriods) - { - lastSmEma1 = sm; - lastSmEma2 = lastSmEma1; - lastHlEma1 = hl; - lastHlEma2 = lastHlEma1; - } - - // first smoothing - double smEma1 = lastSmEma1 + (k1 * (sm - lastSmEma1)); - double hlEma1 = lastHlEma1 + (k1 * (hl - lastHlEma1)); - - // second smoothing - double smEma2 = lastSmEma2 + (k2 * (smEma1 - lastSmEma2)); - double hlEma2 = lastHlEma2 + (k2 * (hlEma1 - lastHlEma2)); - - // stochastic momentum index - double smi = 100 * (smEma2 / (0.5 * hlEma2)); - r.Smi = smi; - - // initialize signal line - if (i + 1 == lookbackPeriods) - { - lastSignal = smi; - } - - // signal line - double signal = lastSignal + (kS * (smi - lastSignal)); - r.Signal = signal; - - // carryover values - lastSmEma1 = smEma1; - lastSmEma2 = smEma2; - lastHlEma1 = hlEma1; - lastHlEma2 = hlEma2; - lastSignal = signal; - } - } - - return results; - } - - // parameter validation - private static void ValidateSmi( - int lookbackPeriods, - int firstSmoothPeriods, - int secondSmoothPeriods, - int signalPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for SMI."); - } - - if (firstSmoothPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(firstSmoothPeriods), firstSmoothPeriods, - "Smoothing periods must be greater than 0 for SMI."); - } - - if (secondSmoothPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(secondSmoothPeriods), secondSmoothPeriods, - "Smoothing periods must be greater than 0 for SMI."); - } - - if (signalPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, - "Signal periods must be greater than 0 for SMI."); - } - } -} diff --git a/src/s-z/Smi/Smi.StaticSeries.cs b/src/s-z/Smi/Smi.StaticSeries.cs new file mode 100644 index 000000000..9d80f0be2 --- /dev/null +++ b/src/s-z/Smi/Smi.StaticSeries.cs @@ -0,0 +1,132 @@ +namespace Skender.Stock.Indicators; + +// STOCHASTIC MOMENTUM INDEX (SERIES) + +public static partial class Smi +{ + public static IReadOnlyList ToSmi( + this IReadOnlyList quotes, + int lookbackPeriods = 13, + int firstSmoothPeriods = 25, + int secondSmoothPeriods = 2, + int signalPeriods = 3) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcSmi( + lookbackPeriods, + firstSmoothPeriods, + secondSmoothPeriods, + signalPeriods); + + private static List CalcSmi( + this IReadOnlyList source, + int lookbackPeriods, + int firstSmoothPeriods, + int secondSmoothPeriods, + int signalPeriods) + { + // check parameter arguments + Validate( + lookbackPeriods, + firstSmoothPeriods, + secondSmoothPeriods, + signalPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + double k1 = 2d / (firstSmoothPeriods + 1); + double k2 = 2d / (secondSmoothPeriods + 1); + double kS = 2d / (signalPeriods + 1); + + double lastSmEma1 = 0; + double lastSmEma2 = 0; + double lastHlEma1 = 0; + double lastHlEma2 = 0; + double lastSignal = 0; + + // roll through source values + for (int i = 0; i < length; i++) + { + QuoteD q = source[i]; + + double smi; + double signal; + + if (i >= lookbackPeriods - 1) + { + double hH = double.MinValue; + double lL = double.MaxValue; + + for (int p = i + 1 - lookbackPeriods; p <= i; p++) + { + QuoteD x = source[p]; + + if (x.High > hH) + { + hH = x.High; + } + + if (x.Low < lL) + { + lL = x.Low; + } + } + + double sm = q.Close - 0.5d * (hH + lL); + double hl = hH - lL; + + // initialize last EMA values + // TODO: update healing, without requiring specific indexing + if (i == lookbackPeriods - 1) + { + lastSmEma1 = sm; + lastSmEma2 = lastSmEma1; + lastHlEma1 = hl; + lastHlEma2 = lastHlEma1; + } + + // first smoothing + double smEma1 = lastSmEma1 + k1 * (sm - lastSmEma1); + double hlEma1 = lastHlEma1 + k1 * (hl - lastHlEma1); + + // second smoothing + double smEma2 = lastSmEma2 + k2 * (smEma1 - lastSmEma2); + double hlEma2 = lastHlEma2 + k2 * (hlEma1 - lastHlEma2); + + // stochastic momentum index + smi = 100 * (smEma2 / (0.5 * hlEma2)); + + // initialize signal line + // TODO: update healing, without requiring specific indexing + if (i == lookbackPeriods - 1) + { + lastSignal = smi; + } + + // signal line + signal = lastSignal + kS * (smi - lastSignal); + + // carryover values + lastSmEma1 = smEma1; + lastSmEma2 = smEma2; + lastHlEma1 = hlEma1; + lastHlEma2 = hlEma2; + lastSignal = signal; + } + else + { + smi = double.NaN; + signal = double.NaN; + } + + results.Add(new( + Timestamp: q.Timestamp, + Smi: smi.NaN2Null(), + Signal: signal.NaN2Null())); + } + + return results; + } +} diff --git a/src/s-z/Smi/Smi.Utilities.cs b/src/s-z/Smi/Smi.Utilities.cs index 5aca22aef..b22632495 100644 --- a/src/s-z/Smi/Smi.Utilities.cs +++ b/src/s-z/Smi/Smi.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// STOCHASTIC MOMENTUM INDEX (UTILITIES) + +public static partial class Smi { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -14,4 +15,37 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(removePeriods + 2 + 100); } + + // parameter validation + internal static void Validate( + int lookbackPeriods, + int firstSmoothPeriods, + int secondSmoothPeriods, + int signalPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for SMI."); + } + + if (firstSmoothPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(firstSmoothPeriods), firstSmoothPeriods, + "Smoothing periods must be greater than 0 for SMI."); + } + + if (secondSmoothPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(secondSmoothPeriods), secondSmoothPeriods, + "Smoothing periods must be greater than 0 for SMI."); + } + + if (signalPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, + "Signal periods must be greater than 0 for SMI."); + } + } } diff --git a/src/s-z/Smma/Smma.Api.cs b/src/s-z/Smma/Smma.Api.cs deleted file mode 100644 index ada941da1..000000000 --- a/src/s-z/Smma/Smma.Api.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SMOOTHED MOVING AVERAGE (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetSmma( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcSmma(lookbackPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetSmma( - this IEnumerable results, - int lookbackPeriods) => results - .ToTuple() - .CalcSmma(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetSmma( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods) => priceTuples - .ToSortedList() - .CalcSmma(lookbackPeriods); -} diff --git a/src/s-z/Smma/Smma.Models.cs b/src/s-z/Smma/Smma.Models.cs index d685792b4..3ea1c2e02 100644 --- a/src/s-z/Smma/Smma.Models.cs +++ b/src/s-z/Smma/Smma.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class SmmaResult : ResultBase, IReusableResult +public record SmmaResult +( + DateTime Timestamp, + double? Smma = null +) : IReusable { - public SmmaResult(DateTime date) - { - Date = date; - } - - public double? Smma { get; set; } - - double? IReusableResult.Value => Smma; + public double Value => Smma.Null2NaN(); } diff --git a/src/s-z/Smma/Smma.Series.cs b/src/s-z/Smma/Smma.Series.cs deleted file mode 100644 index 3181ebb33..000000000 --- a/src/s-z/Smma/Smma.Series.cs +++ /dev/null @@ -1,66 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SMOOTHED MOVING AVERAGE (SERIES) -public static partial class Indicator -{ - // calculate series - internal static List CalcSmma( - this List<(DateTime, double)> tpList, - int lookbackPeriods) - { - // check parameter arguments - ValidateSmma(lookbackPeriods); - - // initialize - int length = tpList.Count; - List results = new(length); - double prevValue = double.NaN; - - // roll through quotes - for (int i = 0; i < length; i++) - { - double smma = double.NaN; - (DateTime date, double value) = tpList[i]; - - SmmaResult r = new(date); - results.Add(r); - - // calculate SMMA - if (i + 1 > lookbackPeriods) - { - smma = ((prevValue * (lookbackPeriods - 1)) + value) / lookbackPeriods; - r.Smma = smma.NaN2Null(); - } - - // first SMMA calculated as simple SMA - else if (i + 1 == lookbackPeriods) - { - double sumClose = 0; - for (int p = i + 1 - lookbackPeriods; p <= i; p++) - { - (DateTime _, double pValue) = tpList[p]; - sumClose += pValue; - } - - smma = sumClose / lookbackPeriods; - r.Smma = smma.NaN2Null(); - } - - prevValue = smma; - } - - return results; - } - - // parameter validation - private static void ValidateSmma( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for SMMA."); - } - } -} diff --git a/src/s-z/Smma/Smma.StaticSeries.cs b/src/s-z/Smma/Smma.StaticSeries.cs new file mode 100644 index 000000000..e2503ef6c --- /dev/null +++ b/src/s-z/Smma/Smma.StaticSeries.cs @@ -0,0 +1,63 @@ +namespace Skender.Stock.Indicators; + +// SMOOTHED MOVING AVERAGE (SERIES) + +public static partial class Smma +{ + public static IReadOnlyList ToSmma( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + double prevSmma = double.NaN; + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + // skip incalculable periods + if (i < lookbackPeriods - 1) + { + results.Add(new(s.Timestamp)); + continue; + } + + double smma; + + // when no prior SMMA, reset as SMA + if (double.IsNaN(prevSmma)) + { + double sum = 0; + for (int p = i + 1 - lookbackPeriods; p <= i; p++) + { + T ps = source[p]; + sum += ps.Value; + } + + smma = sum / lookbackPeriods; + } + + // normal SMMA + else + { + smma = ((prevSmma * (lookbackPeriods - 1)) + s.Value) / lookbackPeriods; + } + + results.Add(new SmmaResult( + Timestamp: s.Timestamp, + Smma: smma.NaN2Null())); + + prevSmma = smma; + } + + return results; + } +} diff --git a/src/s-z/Smma/Smma.Utilities.cs b/src/s-z/Smma/Smma.Utilities.cs index 8a3ea18dc..5b5336023 100644 --- a/src/s-z/Smma/Smma.Utilities.cs +++ b/src/s-z/Smma/Smma.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// SMOOTHED MOVING AVERAGE (UTILITIES) + +public static partial class Smma { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int n = results .ToList() @@ -14,4 +15,16 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(n + 100); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for SMMA."); + } + } } diff --git a/src/s-z/StarcBands/StarcBands.Api.cs b/src/s-z/StarcBands/StarcBands.Api.cs deleted file mode 100644 index ece5d9af0..000000000 --- a/src/s-z/StarcBands/StarcBands.Api.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STARC BANDS (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetStarcBands( - this IEnumerable quotes, - int smaPeriods, - double multiplier = 2, - int atrPeriods = 10) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcStarcBands(smaPeriods, multiplier, atrPeriods); -} diff --git a/src/s-z/StarcBands/StarcBands.Models.cs b/src/s-z/StarcBands/StarcBands.Models.cs index 09c275abf..aafb95c00 100644 --- a/src/s-z/StarcBands/StarcBands.Models.cs +++ b/src/s-z/StarcBands/StarcBands.Models.cs @@ -1,14 +1,10 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class StarcBandsResult : ResultBase -{ - public StarcBandsResult(DateTime date) - { - Date = date; - } - - public double? UpperBand { get; set; } - public double? Centerline { get; set; } - public double? LowerBand { get; set; } -} +public record StarcBandsResult +( + DateTime Timestamp, + double? UpperBand, + double? Centerline, + double? LowerBand +) : ISeries; diff --git a/src/s-z/StarcBands/StarcBands.Series.cs b/src/s-z/StarcBands/StarcBands.Series.cs deleted file mode 100644 index 5d1318abd..000000000 --- a/src/s-z/StarcBands/StarcBands.Series.cs +++ /dev/null @@ -1,67 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STARC BANDS (SERIES) -public static partial class Indicator -{ - internal static List CalcStarcBands( - this List qdList, - int smaPeriods, - double multiplier, - int atrPeriods) - { - // check parameter arguments - ValidateStarcBands(smaPeriods, multiplier, atrPeriods); - - // initialize - List atrResults = qdList.CalcAtr(atrPeriods); - - List results = qdList - .ToTuple(CandlePart.Close) - .CalcSma(smaPeriods) - .Select(x => new StarcBandsResult(x.Date) { - Centerline = x.Sma - }) - .ToList(); - - int lookbackPeriods = Math.Max(smaPeriods, atrPeriods); - - // roll through quotes - for (int i = lookbackPeriods - 1; i < results.Count; i++) - { - StarcBandsResult r = results[i]; - - AtrResult a = atrResults[i]; - - r.UpperBand = r.Centerline + (multiplier * a.Atr); - r.LowerBand = r.Centerline - (multiplier * a.Atr); - } - - return results; - } - - // parameter validation - private static void ValidateStarcBands( - int smaPeriods, - double multiplier, - int atrPeriods) - { - // check parameter arguments - if (smaPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(smaPeriods), smaPeriods, - "EMA periods must be greater than 1 for STARC Bands."); - } - - if (atrPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(atrPeriods), atrPeriods, - "ATR periods must be greater than 1 for STARC Bands."); - } - - if (multiplier <= 0) - { - throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, - "Multiplier must be greater than 0 for STARC Bands."); - } - } -} diff --git a/src/s-z/StarcBands/StarcBands.StaticSeries.cs b/src/s-z/StarcBands/StarcBands.StaticSeries.cs new file mode 100644 index 000000000..3a28837e4 --- /dev/null +++ b/src/s-z/StarcBands/StarcBands.StaticSeries.cs @@ -0,0 +1,46 @@ +namespace Skender.Stock.Indicators; + +// STARC BANDS (SERIES) + +public static partial class StarcBands +{ + public static IReadOnlyList ToStarcBands( + this IReadOnlyList quotes, + int smaPeriods, + double multiplier = 2, + int atrPeriods = 10) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcStarcBands(smaPeriods, multiplier, atrPeriods); + + private static List CalcStarcBands( + this IReadOnlyList source, + int smaPeriods, + double multiplier, + int atrPeriods) + { + // check parameter arguments + Validate(smaPeriods, multiplier, atrPeriods); + + // initialize + int length = source.Count; + List results = new(length); + List atrResults = source.CalcAtr(atrPeriods); + IReadOnlyList smaResults = source.ToSma(smaPeriods); + + // roll through source values + for (int i = 0; i < length; i++) + { + SmaResult s = smaResults[i]; + AtrResult a = atrResults[i]; + + results.Add(new( + Timestamp: s.Timestamp, + Centerline: s.Sma, + UpperBand: s.Sma + (multiplier * a.Atr), + LowerBand: s.Sma - (multiplier * a.Atr))); + } + + return results; + } +} diff --git a/src/s-z/StarcBands/StarcBands.Utilities.cs b/src/s-z/StarcBands/StarcBands.Utilities.cs index 382fb23f8..c30e4d801 100644 --- a/src/s-z/StarcBands/StarcBands.Utilities.cs +++ b/src/s-z/StarcBands/StarcBands.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// STARC BANDS (UTILITIES) + +public static partial class StarcBands { // CONDENSE (REMOVE null results) - /// - /// - public static IEnumerable Condense( - this IEnumerable results) + /// + public static IReadOnlyList Condense( + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -19,10 +20,9 @@ public static IEnumerable Condense( } // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int n = results .ToList() @@ -30,4 +30,30 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(n + 150); } + + // parameter validation + internal static void Validate( + int smaPeriods, + double multiplier, + int atrPeriods) + { + // check parameter arguments + if (smaPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(smaPeriods), smaPeriods, + "EMA periods must be greater than 1 for STARC Bands."); + } + + if (atrPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(atrPeriods), atrPeriods, + "ATR periods must be greater than 1 for STARC Bands."); + } + + if (multiplier <= 0) + { + throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, + "Multiplier must be greater than 0 for STARC Bands."); + } + } } diff --git a/src/s-z/Stc/Stc.Api.cs b/src/s-z/Stc/Stc.Api.cs deleted file mode 100644 index aad669cfc..000000000 --- a/src/s-z/Stc/Stc.Api.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SCHAFF TREND CYCLE (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetStc( - this IEnumerable quotes, - int cyclePeriods = 10, - int fastPeriods = 23, - int slowPeriods = 50) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcStc(cyclePeriods, fastPeriods, slowPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetStc( - this IEnumerable results, - int cyclePeriods = 10, - int fastPeriods = 23, - int slowPeriods = 50) => results - .ToTuple() - .CalcStc(cyclePeriods, fastPeriods, slowPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetStc( - this IEnumerable<(DateTime, double)> priceTuples, - int cyclePeriods = 10, - int fastPeriods = 23, - int slowPeriods = 50) => priceTuples - .ToSortedList() - .CalcStc(cyclePeriods, fastPeriods, slowPeriods); -} diff --git a/src/s-z/Stc/Stc.Models.cs b/src/s-z/Stc/Stc.Models.cs index 51a781e9c..c60915501 100644 --- a/src/s-z/Stc/Stc.Models.cs +++ b/src/s-z/Stc/Stc.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class StcResult : ResultBase, IReusableResult +public record StcResult +( + DateTime Timestamp, + double? Stc +) : IReusable { - public StcResult(DateTime date) - { - Date = date; - } - - public double? Stc { get; set; } - - double? IReusableResult.Value => Stc; + public double Value => Stc.Null2NaN(); } diff --git a/src/s-z/Stc/Stc.Series.cs b/src/s-z/Stc/Stc.Series.cs deleted file mode 100644 index 8fc170176..000000000 --- a/src/s-z/Stc/Stc.Series.cs +++ /dev/null @@ -1,75 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SCHAFF TREND CYCLE (SERIES) -public static partial class Indicator -{ - internal static List CalcStc( - this List<(DateTime, double)> tpList, - int cyclePeriods, - int fastPeriods, - int slowPeriods) - { - // check parameter arguments - ValidateStc(cyclePeriods, fastPeriods, slowPeriods); - - // initialize results - int length = tpList.Count; - int initPeriods = Math.Min(slowPeriods - 1, length); - List results = new(length); - - // add back auto-pruned results - for (int i = 0; i < initPeriods; i++) - { - (DateTime date, double _) = tpList[i]; - results.Add(new StcResult(date)); - } - - // get stochastic of macd - List stochMacd = tpList - .CalcMacd(fastPeriods, slowPeriods, 1) - .Remove(initPeriods) - .Select(x => new QuoteD { - Date = x.Date, - High = x.Macd.Null2NaN(), - Low = x.Macd.Null2NaN(), - Close = x.Macd.Null2NaN() - }) - .ToList() - .CalcStoch(cyclePeriods, 1, 3, 3, 2, MaType.SMA); - - // add stoch results - for (int i = 0; i < stochMacd.Count; i++) - { - StochResult r = stochMacd[i]; - results.Add(new StcResult(r.Date) { Stc = r.Oscillator }); - } - - return results; - } - - // parameter validation - private static void ValidateStc( - int cyclePeriods, - int fastPeriods, - int slowPeriods) - { - // check parameter arguments - if (cyclePeriods < 0) - { - throw new ArgumentOutOfRangeException(nameof(cyclePeriods), cyclePeriods, - "Trend Cycle periods must be greater than or equal to 0 for STC."); - } - - if (fastPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, - "Fast periods must be greater than 0 for STC."); - } - - if (slowPeriods <= fastPeriods) - { - throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, - "Slow periods must be greater than the fast period for STC."); - } - } -} diff --git a/src/s-z/Stc/Stc.StaticSeries.cs b/src/s-z/Stc/Stc.StaticSeries.cs new file mode 100644 index 000000000..bb103d5bb --- /dev/null +++ b/src/s-z/Stc/Stc.StaticSeries.cs @@ -0,0 +1,45 @@ +namespace Skender.Stock.Indicators; + +// SCHAFF TREND CYCLE (SERIES) + +public static partial class Stc +{ + public static IReadOnlyList ToStc( + this IReadOnlyList source, + int cyclePeriods = 10, + int fastPeriods = 23, + int slowPeriods = 50) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(cyclePeriods, fastPeriods, slowPeriods); + + // initialize results + int length = source.Count; + List results = new(length); + + // get stochastic of macd + IReadOnlyList stochMacd = source + .ToMacd(fastPeriods, slowPeriods, 1) + .Select(x => new QuoteD( + x.Timestamp, 0, + x.Macd.Null2NaN(), + x.Macd.Null2NaN(), + x.Macd.Null2NaN(), 0)) + .ToList() + .CalcStoch(cyclePeriods, 1, 3, 3, 2, MaType.SMA); + + // add stoch results + for (int i = 0; i < length; i++) + { + StochResult r = stochMacd[i]; + + results.Add(new StcResult( + Timestamp: r.Timestamp, + Stc: r.Oscillator)); + } + + return results; + } +} diff --git a/src/s-z/Stc/Stc.Utilities.cs b/src/s-z/Stc/Stc.Utilities.cs index c2de9b594..ecc8a0004 100644 --- a/src/s-z/Stc/Stc.Utilities.cs +++ b/src/s-z/Stc/Stc.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// SCHAFF TREND CYCLE (UTILITIES) + +public static partial class Stc { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int n = results .ToList() @@ -14,4 +15,30 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(n + 250); } + + // parameter validation + internal static void Validate( + int cyclePeriods, + int fastPeriods, + int slowPeriods) + { + // check parameter arguments + if (cyclePeriods < 0) + { + throw new ArgumentOutOfRangeException(nameof(cyclePeriods), cyclePeriods, + "Trend Cycle periods must be greater than or equal to 0 for STC."); + } + + if (fastPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, + "Fast periods must be greater than 0 for STC."); + } + + if (slowPeriods <= fastPeriods) + { + throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, + "Slow periods must be greater than the fast period for STC."); + } + } } diff --git a/src/s-z/StdDev/StdDev.Api.cs b/src/s-z/StdDev/StdDev.Api.cs deleted file mode 100644 index a4dc29e37..000000000 --- a/src/s-z/StdDev/StdDev.Api.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STANDARD DEVIATION (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetStdDev( - this IEnumerable quotes, - int lookbackPeriods, - int? smaPeriods = null) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcStdDev(lookbackPeriods, smaPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetStdDev( - this IEnumerable results, - int lookbackPeriods, - int? smaPeriods = null) => results - .ToTuple() - .CalcStdDev(lookbackPeriods, smaPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetStdDev( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods, - int? smaPeriods = null) => priceTuples - .ToSortedList() - .CalcStdDev(lookbackPeriods, smaPeriods); -} diff --git a/src/s-z/StdDev/StdDev.Models.cs b/src/s-z/StdDev/StdDev.Models.cs index 78768abfd..5d4dbb6ee 100644 --- a/src/s-z/StdDev/StdDev.Models.cs +++ b/src/s-z/StdDev/StdDev.Models.cs @@ -1,17 +1,13 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class StdDevResult : ResultBase, IReusableResult +public record StdDevResult +( + DateTime Timestamp, + double? StdDev, + double? Mean, + double? ZScore +) : IReusable { - public StdDevResult(DateTime date) - { - Date = date; - } - - public double? StdDev { get; set; } - public double? Mean { get; set; } - public double? ZScore { get; set; } - public double? StdDevSma { get; set; } - - double? IReusableResult.Value => StdDev; + public double Value => StdDev.Null2NaN(); } diff --git a/src/s-z/StdDev/StdDev.Series.cs b/src/s-z/StdDev/StdDev.Series.cs deleted file mode 100644 index b3d2c1264..000000000 --- a/src/s-z/StdDev/StdDev.Series.cs +++ /dev/null @@ -1,83 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STANDARD DEVIATION (SERIES) -public static partial class Indicator -{ - internal static List CalcStdDev( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - int? smaPeriods) - { - // check parameter arguments - ValidateStdDev(lookbackPeriods, smaPeriods); - - // initialize - int length = tpList.Count; - List results = new(length); - - // roll through quotes - for (int i = 0; i < length; i++) - { - (DateTime date, double value) = tpList[i]; - - StdDevResult r = new(date); - results.Add(r); - - if (i + 1 >= lookbackPeriods) - { - double[] values = new double[lookbackPeriods]; - double sum = 0; - int n = 0; - - for (int p = i + 1 - lookbackPeriods; p <= i; p++) - { - (DateTime _, double v) = tpList[p]; - values[n] = v; - sum += v; - n++; - } - - double avg = sum / lookbackPeriods; - - r.StdDev = values.StdDev().NaN2Null(); - r.Mean = avg.NaN2Null(); - - r.ZScore = (r.StdDev == 0) ? null - : (value - avg) / r.StdDev; - } - - // optional SMA - if (smaPeriods != null && i >= lookbackPeriods + smaPeriods - 2) - { - double? sumSma = 0; - for (int p = i + 1 - (int)smaPeriods; p <= i; p++) - { - sumSma += results[p].StdDev; - } - - r.StdDevSma = (sumSma / smaPeriods).NaN2Null(); - } - } - - return results; - } - - // parameter validation - private static void ValidateStdDev( - int lookbackPeriods, - int? smaPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for Standard Deviation."); - } - - if (smaPeriods is not null and <= 0) - { - throw new ArgumentOutOfRangeException(nameof(smaPeriods), smaPeriods, - "SMA periods must be greater than 0 for Standard Deviation."); - } - } -} diff --git a/src/s-z/StdDev/StdDev.StaticSeries.cs b/src/s-z/StdDev/StdDev.StaticSeries.cs new file mode 100644 index 000000000..f1a9aba47 --- /dev/null +++ b/src/s-z/StdDev/StdDev.StaticSeries.cs @@ -0,0 +1,68 @@ +namespace Skender.Stock.Indicators; + +// STANDARD DEVIATION (SERIES) + +public static partial class StdDev +{ + public static IReadOnlyList ToStdDev( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + double mean; + double stdDev; + double zScore; + + if (i >= lookbackPeriods - 1) + { + double[] values = new double[lookbackPeriods]; + double sum = 0; + int n = 0; + + for (int p = i + 1 - lookbackPeriods; p <= i; p++) + { + T ps = source[p]; + values[n] = ps.Value; + sum += ps.Value; + n++; + } + + mean = sum / lookbackPeriods; + + stdDev = values.StdDev(); + + zScore = stdDev == 0 ? double.NaN + : (s.Value - mean) / stdDev; + } + else + { + mean = double.NaN; + stdDev = double.NaN; + zScore = double.NaN; + } + + StdDevResult r = new( + Timestamp: s.Timestamp, + StdDev: stdDev.NaN2Null(), + Mean: mean.NaN2Null(), + ZScore: zScore.NaN2Null()); + + results.Add(r); + } + + return results; + } +} diff --git a/src/s-z/StdDev/StdDev.Utilities.cs b/src/s-z/StdDev/StdDev.Utilities.cs index 2d9e9f9aa..bd42630e0 100644 --- a/src/s-z/StdDev/StdDev.Utilities.cs +++ b/src/s-z/StdDev/StdDev.Utilities.cs @@ -1,17 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// STANDARD DEVIATION (UTILITIES) + +public static partial class StdDev { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.StdDev != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for Standard Deviation."); + } } } diff --git a/src/s-z/StdDev/info.xml b/src/s-z/StdDev/info.xml index 6bab67d38..71c861cf5 100644 --- a/src/s-z/StdDev/info.xml +++ b/src/s-z/StdDev/info.xml @@ -12,7 +12,6 @@ Configurable Quote type. See Guide for more information. Historical price quotes. Number of periods in the lookback window. - Optional. Number of periods in the Standard Deviation SMA signal line. Time series of Standard Deviations values. Invalid parameter value provided. \ No newline at end of file diff --git a/src/s-z/StdDevChannels/StdDevChannels.Api.cs b/src/s-z/StdDevChannels/StdDevChannels.Api.cs deleted file mode 100644 index 5d7818128..000000000 --- a/src/s-z/StdDevChannels/StdDevChannels.Api.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STANDARD DEVIATION CHANNELS (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetStdDevChannels( - this IEnumerable quotes, - int? lookbackPeriods = 20, - double stdDeviations = 2) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcStdDevChannels(lookbackPeriods, stdDeviations); - - // SERIES, from CHAIN - public static IEnumerable GetStdDevChannels( - this IEnumerable results, - int? lookbackPeriods = 20, - double stdDeviations = 2) => results - .ToTuple() - .CalcStdDevChannels(lookbackPeriods, stdDeviations) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetStdDevChannels( - this IEnumerable<(DateTime, double)> priceTuples, - int? lookbackPeriods = 20, - double stdDeviations = 2) => priceTuples - .ToSortedList() - .CalcStdDevChannels(lookbackPeriods, stdDeviations); -} diff --git a/src/s-z/StdDevChannels/StdDevChannels.Models.cs b/src/s-z/StdDevChannels/StdDevChannels.Models.cs index 00da6a900..34be76c47 100644 --- a/src/s-z/StdDevChannels/StdDevChannels.Models.cs +++ b/src/s-z/StdDevChannels/StdDevChannels.Models.cs @@ -1,15 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class StdDevChannelsResult : ResultBase -{ - public StdDevChannelsResult(DateTime date) - { - Date = date; - } - - public double? Centerline { get; set; } - public double? UpperChannel { get; set; } - public double? LowerChannel { get; set; } - public bool BreakPoint { get; set; } -} +public record StdDevChannelsResult +( + DateTime Timestamp, + double? Centerline = null, + double? UpperChannel = null, + double? LowerChannel = null, + bool BreakPoint = false +) : ISeries; diff --git a/src/s-z/StdDevChannels/StdDevChannels.Series.cs b/src/s-z/StdDevChannels/StdDevChannels.Series.cs deleted file mode 100644 index 85fe19a16..000000000 --- a/src/s-z/StdDevChannels/StdDevChannels.Series.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STANDARD DEVIATION CHANNELS -public static partial class Indicator -{ - internal static List CalcStdDevChannels( - this List<(DateTime, double)> tpList, - int? lookbackPeriods, - double stdDeviations) - { - // assume whole quotes when lookback is null - lookbackPeriods ??= tpList.Count; - - // check parameter arguments - ValidateStdDevChannels(lookbackPeriods, stdDeviations); - - // initialize - List slopeResults = tpList - .CalcSlope((int)lookbackPeriods); - - int length = slopeResults.Count; - List results = slopeResults - .Select(x => new StdDevChannelsResult(x.Date)) - .ToList(); - - // roll through quotes in reverse - for (int w = length - 1; w >= lookbackPeriods - 1; w -= (int)lookbackPeriods) - { - SlopeResult s = slopeResults[w]; - double? width = stdDeviations * s.StdDev; - - // add regression line (y = mx + b) and channels - for (int p = w - (int)lookbackPeriods + 1; p <= w; p++) - { - if (p >= 0) - { - StdDevChannelsResult d = results[p]; - d.Centerline = (s.Slope * (p + 1)) + s.Intercept; - d.UpperChannel = d.Centerline + width; - d.LowerChannel = d.Centerline - width; - - d.BreakPoint = p == w - lookbackPeriods + 1; - } - } - } - - return results; - } - - // parameter validation - private static void ValidateStdDevChannels( - int? lookbackPeriods, - double stdDeviations) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for Standard Deviation Channels."); - } - - if (stdDeviations <= 0) - { - throw new ArgumentOutOfRangeException(nameof(stdDeviations), stdDeviations, - "Standard Deviations must be greater than 0 for Standard Deviation Channels."); - } - } -} diff --git a/src/s-z/StdDevChannels/StdDevChannels.StaticSeries.cs b/src/s-z/StdDevChannels/StdDevChannels.StaticSeries.cs new file mode 100644 index 000000000..067c32127 --- /dev/null +++ b/src/s-z/StdDevChannels/StdDevChannels.StaticSeries.cs @@ -0,0 +1,58 @@ +namespace Skender.Stock.Indicators; + +// STANDARD DEVIATION CHANNELS (SERIES) + +public static partial class StdDevChannels +{ + public static IReadOnlyList ToStdDevChannels( + this IReadOnlyList source, + int? lookbackPeriods = 20, + double stdDeviations = 2) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods, stdDeviations); + + // initialize + lookbackPeriods ??= source.Count; // assume whole quotes when null + int length = source.Count; + + IReadOnlyList slopeResults = source + .ToSlope((int)lookbackPeriods); + + List results = slopeResults + .Select(x => new StdDevChannelsResult(x.Timestamp)) + .ToList(); + + // roll through source values in reverse + for (int i = length - 1; i >= lookbackPeriods - 1; i -= (int)lookbackPeriods) + { + SlopeResult s = slopeResults[i]; + double? width = stdDeviations * s.StdDev; + + // add regression line (y = mx + b) and channels + for (int p = i - (int)lookbackPeriods + 1; p <= i; p++) + { + if (p < 0) + { + continue; + } + + StdDevChannelsResult d = results[p]; + + double? c = (s.Slope * (p + 1)) + s.Intercept; + + // re-write record + results[p] = d with { + Centerline = c, + UpperChannel = c + width, + LowerChannel = c - width, + BreakPoint = p == i - lookbackPeriods + 1 + }; + } + } + + return results; + } +} diff --git a/src/s-z/StdDevChannels/StdDevChannels.Utilities.cs b/src/s-z/StdDevChannels/StdDevChannels.Utilities.cs index f00a074c1..70d382c48 100644 --- a/src/s-z/StdDevChannels/StdDevChannels.Utilities.cs +++ b/src/s-z/StdDevChannels/StdDevChannels.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// STANDARD DEVIATION CHANNELS (UTILITIES) + +public static partial class StdDevChannels { // CONDENSE (REMOVE null results) - /// - /// - public static IEnumerable Condense( - this IEnumerable results) + /// + public static IReadOnlyList Condense( + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -16,16 +17,15 @@ public static IEnumerable Condense( x.UpperChannel is null && x.LowerChannel is null && x.Centerline is null - && x.BreakPoint is false); + && !x.BreakPoint); return resultsList.ToSortedList(); } // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -33,4 +33,23 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int? lookbackPeriods, + double stdDeviations) + { + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for Standard Deviation Channels."); + } + + if (stdDeviations <= 0) + { + throw new ArgumentOutOfRangeException(nameof(stdDeviations), stdDeviations, + "Standard Deviations must be greater than 0 for Standard Deviation Channels."); + } + } } diff --git a/src/s-z/Stoch/Stoch.Api.cs b/src/s-z/Stoch/Stoch.Api.cs deleted file mode 100644 index 0119bad4b..000000000 --- a/src/s-z/Stoch/Stoch.Api.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STOCHASTIC OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote (standard) - /// - /// - public static IEnumerable GetStoch( - this IEnumerable quotes, - int lookbackPeriods = 14, - int signalPeriods = 3, - int smoothPeriods = 3) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcStoch( - lookbackPeriods, - signalPeriods, - smoothPeriods, 3, 2, MaType.SMA); - - // SERIES, from TQuote (extended) - /// - /// - public static IEnumerable GetStoch( - this IEnumerable quotes, - int lookbackPeriods, - int signalPeriods, - int smoothPeriods, - double kFactor, - double dFactor, - MaType movingAverageType) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcStoch( - lookbackPeriods, - signalPeriods, - smoothPeriods, - kFactor, - dFactor, - movingAverageType); -} diff --git a/src/s-z/Stoch/Stoch.Models.cs b/src/s-z/Stoch/Stoch.Models.cs index c1099e1df..ee9c63f99 100644 --- a/src/s-z/Stoch/Stoch.Models.cs +++ b/src/s-z/Stoch/Stoch.Models.cs @@ -1,23 +1,18 @@ namespace Skender.Stock.Indicators; -/// -/// [Serializable] -public sealed class StochResult : ResultBase, IReusableResult +public record StochResult +( + DateTime Timestamp, + double? Oscillator, + double? Signal, + double? PercentJ +) : IReusable { - public StochResult(DateTime date) - { - Date = date; - } - - public double? Oscillator { get; set; } - public double? Signal { get; set; } - public double? PercentJ { get; set; } + public double Value => Oscillator.Null2NaN(); // aliases public double? K => Oscillator; public double? D => Signal; public double? J => PercentJ; - - double? IReusableResult.Value => Oscillator; } diff --git a/src/s-z/Stoch/Stoch.Series.cs b/src/s-z/Stoch/Stoch.Series.cs deleted file mode 100644 index 3f0aaa22e..000000000 --- a/src/s-z/Stoch/Stoch.Series.cs +++ /dev/null @@ -1,213 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STOCHASTIC OSCILLATOR (SERIES) -public static partial class Indicator -{ - internal static List CalcStoch( - this List qdList, - int lookbackPeriods, - int signalPeriods, - int smoothPeriods, - double kFactor, - double dFactor, - MaType movingAverageType) - { - // check parameter arguments - ValidateStoch( - lookbackPeriods, signalPeriods, smoothPeriods, - kFactor, dFactor, movingAverageType); - - // initialize - int length = qdList.Count; - List results = new(length); - - // roll through quotes - for (int i = 0; i < length; i++) - { - QuoteD q = qdList[i]; - - StochResult r = new(q.Date); - results.Add(r); - - if (i + 1 >= lookbackPeriods) - { - double highHigh = double.MinValue; - double lowLow = double.MaxValue; - - for (int p = i + 1 - lookbackPeriods; p <= i; p++) - { - QuoteD x = qdList[p]; - - if (x.High > highHigh) - { - highHigh = x.High; - } - - if (x.Low < lowLow) - { - lowLow = x.Low; - } - } - - r.Oscillator = lowLow != highHigh - ? 100 * (q.Close - lowLow) / (highHigh - lowLow) - : 0; - - // reclaim nulls from NaNs - r.Oscillator = r.Oscillator.NaN2Null(); - } - } - - // smooth the oscillator - if (smoothPeriods > 1) - { - results = SmoothOscillator( - results, length, lookbackPeriods, smoothPeriods, movingAverageType); - } - - // handle insufficient length - if (length < lookbackPeriods - 1) - { - return results; - } - - // signal (%D) and %J - int signalIndex = lookbackPeriods + smoothPeriods + signalPeriods - 2; - double? s = null; - - for (int i = lookbackPeriods - 1; i < length; i++) - { - StochResult r = results[i]; - - // add signal - - if (signalPeriods <= 1) - { - r.Signal = r.Oscillator; - } - - // SMA case - else if (i + 1 >= signalIndex && movingAverageType is MaType.SMA) - { - double? sumOsc = 0; - for (int p = i + 1 - signalPeriods; p <= i; p++) - { - StochResult x = results[p]; - sumOsc += x.Oscillator; - } - - r.Signal = sumOsc / signalPeriods; - } - - // SMMA case - else if (i >= lookbackPeriods - 1 && movingAverageType is MaType.SMMA) - { - s ??= results[i].Oscillator; // set initial or reset if null - - s = ((s * (signalPeriods - 1)) + results[i].Oscillator) / signalPeriods; - r.Signal = s; - } - - // %J - r.PercentJ = (kFactor * r.Oscillator) - (dFactor * r.Signal); - } - - return results; - } - - // internals - private static List SmoothOscillator( - List results, - int length, - int lookbackPeriods, - int smoothPeriods, - MaType movingAverageType) - { - // temporarily store interim smoothed oscillator - double?[] smooth = new double?[length]; // smoothed value - - if (movingAverageType is MaType.SMA) - { - int smoothIndex = lookbackPeriods + smoothPeriods - 2; - - for (int i = smoothIndex; i < length; i++) - { - double? sumOsc = 0; - for (int p = i + 1 - smoothPeriods; p <= i; p++) - { - sumOsc += results[p].Oscillator; - } - - smooth[i] = sumOsc / smoothPeriods; - } - } - else if (movingAverageType is MaType.SMMA) - { - // initialize with unsmoothed value - double? k = results[lookbackPeriods - 1].Oscillator; - - for (int i = lookbackPeriods - 1; i < length; i++) - { - k ??= results[i].Oscillator; // reset if null - - k = ((k * (smoothPeriods - 1)) + results[i].Oscillator) / smoothPeriods; - smooth[i] = k; - } - } - - // replace oscillator - for (int i = 0; i < length; i++) - { - results[i].Oscillator = smooth[i]; - } - - return results; - } - - // parameter validation - private static void ValidateStoch( - int lookbackPeriods, - int signalPeriods, - int smoothPeriods, - double kFactor, - double dFactor, - MaType movingAverageType) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Stochastic."); - } - - if (signalPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, - "Signal periods must be greater than 0 for Stochastic."); - } - - if (smoothPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, - "Smooth periods must be greater than 0 for Stochastic."); - } - - if (kFactor <= 0) - { - throw new ArgumentOutOfRangeException(nameof(kFactor), kFactor, - "kFactor must be greater than 0 for Stochastic."); - } - - if (dFactor <= 0) - { - throw new ArgumentOutOfRangeException(nameof(dFactor), dFactor, - "dFactor must be greater than 0 for Stochastic."); - } - - if (movingAverageType is not MaType.SMA and not MaType.SMMA) - { - throw new ArgumentOutOfRangeException(nameof(dFactor), dFactor, - "Stochastic only supports SMA and SMMA moving average types."); - } - } -} diff --git a/src/s-z/Stoch/Stoch.StaticSeries.cs b/src/s-z/Stoch/Stoch.StaticSeries.cs new file mode 100644 index 000000000..b272dfb4a --- /dev/null +++ b/src/s-z/Stoch/Stoch.StaticSeries.cs @@ -0,0 +1,216 @@ +namespace Skender.Stock.Indicators; + +// STOCHASTIC OSCILLATOR (SERIES) + +public static partial class Stoch +{ + public static IReadOnlyList ToStoch( + this IReadOnlyList quotes, + int lookbackPeriods = 14, + int signalPeriods = 3, + int smoothPeriods = 3) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcStoch( + lookbackPeriods, + signalPeriods, + smoothPeriods, 3, 2, MaType.SMA); + + public static IReadOnlyList ToStoch( + this IReadOnlyList quotes, + int lookbackPeriods, + int signalPeriods, + int smoothPeriods, + double kFactor, + double dFactor, + MaType movingAverageType) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcStoch( + lookbackPeriods, + signalPeriods, + smoothPeriods, + kFactor, + dFactor, + movingAverageType); + + internal static List CalcStoch( + this IReadOnlyList source, + int lookbackPeriods, + int signalPeriods, + int smoothPeriods, + double kFactor, + double dFactor, + MaType movingAverageType) + { + // check parameter arguments + Validate( + lookbackPeriods, signalPeriods, smoothPeriods, + kFactor, dFactor, movingAverageType); + + // initialize + int length = source.Count; + List results = new(length); + + double[] o = new double[length]; // %K oscillator (initial) + double[] k = new double[length]; // %K oscillator (final) + + double prevK = double.NaN; + double prevD = double.NaN; + + // roll through source values + for (int i = 0; i < length; i++) + { + QuoteD q = source[i]; + + // initial %K oscillator + if (i >= lookbackPeriods - 1) + { + double highHigh = double.MinValue; + double lowLow = double.MaxValue; + bool isViable = true; + + for (int p = i - lookbackPeriods + 1; p <= i; p++) + { + QuoteD x = source[p]; + + if (double.IsNaN(x.High) + || double.IsNaN(x.Low) + || double.IsNaN(x.Close)) + { + isViable = false; + break; + } + + if (x.High > highHigh) + { + highHigh = x.High; + } + + if (x.Low < lowLow) + { + lowLow = x.Low; + } + } + + o[i] = !isViable + ? double.NaN + : highHigh - lowLow != 0 + ? 100 * (q.Close - lowLow) / (highHigh - lowLow) + : 0; + } + else + { + o[i] = double.NaN; + } + + // final %K oscillator, keep original + if (smoothPeriods <= 1) + { + k[i] = o[i]; + } + + // final %K oscillator, if smoothed + else if (i >= smoothPeriods) + { + k[i] = double.NaN; + + switch (movingAverageType) + { + // SMA case + case MaType.SMA: + { + double sum = 0; + for (int p = i - smoothPeriods + 1; p <= i; p++) + { + sum += o[p]; + } + + k[i] = sum / smoothPeriods; + break; + } + + // SMMA case + case MaType.SMMA: + { + // re/initialize + if (double.IsNaN(prevK)) + { + prevK = o[i]; + } + + k[i] = ((prevK * (smoothPeriods - 1)) + o[i]) / smoothPeriods; + prevK = k[i]; + break; + } + + default: + throw new InvalidOperationException( + "Invalid Stochastic moving average type."); + } + } + else + { + k[i] = double.NaN; + } + + double oscillator = k[i]; + double signal; + + + // %D signal line + if (signalPeriods <= 1) + { + signal = oscillator; + } + else if (i >= signalPeriods) + { + switch (movingAverageType) + { + // SMA case + // TODO: || double.IsNaN(prevD) to re/initialize SMMA? + case MaType.SMA: + { + double sum = 0; + for (int p = i - signalPeriods + 1; p <= i; p++) + { + sum += k[p]; + } + + signal = sum / signalPeriods; + break; + } + + // SMMA case + case MaType.SMMA: + { + // re/initialize + if (double.IsNaN(prevD)) + { + prevD = k[i]; + } + + double d = ((prevD * (signalPeriods - 1)) + k[i]) / signalPeriods; + signal = d; + prevD = d; + break; + } + + default: + throw new InvalidOperationException("Invalid Stochastic moving average type."); + } + } + else + { + signal = double.NaN; + } + + results.Add(new( + Timestamp: q.Timestamp, + Oscillator: oscillator.NaN2Null(), + Signal: signal.NaN2Null(), + PercentJ: ((kFactor * oscillator) - (dFactor * signal)).NaN2Null())); + } + return results; + } +} diff --git a/src/s-z/Stoch/Stoch.Utilities.cs b/src/s-z/Stoch/Stoch.Utilities.cs index feaf9867f..bb1fcc708 100644 --- a/src/s-z/Stoch/Stoch.Utilities.cs +++ b/src/s-z/Stoch/Stoch.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// STOCHASTIC OSCILLATOR (UTILITIES) + +public static partial class Stoch { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -14,4 +15,51 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods, + int signalPeriods, + int smoothPeriods, + double kFactor, + double dFactor, + MaType movingAverageType) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Stochastic."); + } + + if (signalPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, + "Signal periods must be greater than 0 for Stochastic."); + } + + if (smoothPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, + "Smooth periods must be greater than 0 for Stochastic."); + } + + if (kFactor <= 0) + { + throw new ArgumentOutOfRangeException(nameof(kFactor), kFactor, + "kFactor must be greater than 0 for Stochastic."); + } + + if (dFactor <= 0) + { + throw new ArgumentOutOfRangeException(nameof(dFactor), dFactor, + "dFactor must be greater than 0 for Stochastic."); + } + + if (movingAverageType is not MaType.SMA and not MaType.SMMA) + { + throw new ArgumentOutOfRangeException(nameof(dFactor), dFactor, + "Stochastic only supports SMA and SMMA moving average types."); + } + } } diff --git a/src/s-z/StochRsi/StochRsi.Api.cs b/src/s-z/StochRsi/StochRsi.Api.cs deleted file mode 100644 index 1cf710fc1..000000000 --- a/src/s-z/StochRsi/StochRsi.Api.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STOCHASTIC RSI (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetStochRsi( - this IEnumerable quotes, - int rsiPeriods, - int stochPeriods, - int signalPeriods, - int smoothPeriods = 1) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcStochRsi( - rsiPeriods, - stochPeriods, - signalPeriods, - smoothPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetStochRsi( - this IEnumerable results, - int rsiPeriods, - int stochPeriods, - int signalPeriods, - int smoothPeriods) => results - .ToTuple() - .CalcStochRsi( - rsiPeriods, - stochPeriods, - signalPeriods, - smoothPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetStochRsi( - this IEnumerable<(DateTime, double)> priceTuples, - int rsiPeriods, - int stochPeriods, - int signalPeriods, - int smoothPeriods) => priceTuples - .ToSortedList() - .CalcStochRsi( - rsiPeriods, - stochPeriods, - signalPeriods, - smoothPeriods); -} diff --git a/src/s-z/StochRsi/StochRsi.Models.cs b/src/s-z/StochRsi/StochRsi.Models.cs index 96c2b4f04..f365f5748 100644 --- a/src/s-z/StochRsi/StochRsi.Models.cs +++ b/src/s-z/StochRsi/StochRsi.Models.cs @@ -1,15 +1,12 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class StochRsiResult : ResultBase, IReusableResult +public record StochRsiResult +( + DateTime Timestamp, + double? StochRsi = null, + double? Signal = null +) : IReusable { - public StochRsiResult(DateTime date) - { - Date = date; - } - - public double? StochRsi { get; set; } - public double? Signal { get; set; } - - double? IReusableResult.Value => StochRsi; + public double Value => StochRsi.Null2NaN(); } diff --git a/src/s-z/StochRsi/StochRsi.Series.cs b/src/s-z/StochRsi/StochRsi.Series.cs deleted file mode 100644 index 087099ee0..000000000 --- a/src/s-z/StochRsi/StochRsi.Series.cs +++ /dev/null @@ -1,91 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STOCHASTIC RSI (SERIES) -public static partial class Indicator -{ - internal static List CalcStochRsi( - this List<(DateTime, double)> tpList, - int rsiPeriods, - int stochPeriods, - int signalPeriods, - int smoothPeriods) - { - // check parameter arguments - ValidateStochRsi(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods); - - // initialize results - int length = tpList.Count; - int initPeriods = Math.Min(rsiPeriods + stochPeriods - 1, length); - List results = new(length); - - // add back auto-pruned results - for (int i = 0; i < initPeriods; i++) - { - (DateTime date, double _) = tpList[i]; - results.Add(new StochRsiResult(date)); - } - - // get Stochastic of RSI - List stoResults = - tpList - .CalcRsi(rsiPeriods) - .Remove(Math.Min(rsiPeriods, length)) - .Select(x => new QuoteD { - Date = x.Date, - High = x.Rsi.Null2NaN(), - Low = x.Rsi.Null2NaN(), - Close = x.Rsi.Null2NaN() - }) - .ToList() - .CalcStoch( - stochPeriods, - signalPeriods, - smoothPeriods, 3, 2, MaType.SMA) - .ToList(); - - // add stoch results - for (int i = rsiPeriods + stochPeriods - 1; i < length; i++) - { - StochResult r = stoResults[i - rsiPeriods]; - results.Add(new StochRsiResult(r.Date) { - StochRsi = r.Oscillator, - Signal = r.Signal - }); - } - - return results; - } - - // parameter validation - private static void ValidateStochRsi( - int rsiPeriods, - int stochPeriods, - int signalPeriods, - int smoothPeriods) - { - // check parameter arguments - if (rsiPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(rsiPeriods), rsiPeriods, - "RSI periods must be greater than 0 for Stochastic RSI."); - } - - if (stochPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(stochPeriods), stochPeriods, - "STOCH periods must be greater than 0 for Stochastic RSI."); - } - - if (signalPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, - "Signal periods must be greater than 0 for Stochastic RSI."); - } - - if (smoothPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, - "Smooth periods must be greater than 0 for Stochastic RSI."); - } - } -} diff --git a/src/s-z/StochRsi/StochRsi.StaticSeries.cs b/src/s-z/StochRsi/StochRsi.StaticSeries.cs new file mode 100644 index 000000000..85837f5bf --- /dev/null +++ b/src/s-z/StochRsi/StochRsi.StaticSeries.cs @@ -0,0 +1,64 @@ +namespace Skender.Stock.Indicators; + +// STOCHASTIC RSI (SERIES) + +public static partial class StochRsi +{ + public static IReadOnlyList ToStochRsi( + this IReadOnlyList source, + int rsiPeriods, + int stochPeriods, + int signalPeriods, + int smoothPeriods = 1) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods); + + // initialize results + int length = source.Count; + int initPeriods = Math.Min(rsiPeriods + stochPeriods - 1, length); + List results = new(length); + + // add back auto-pruned results + for (int i = 0; i < initPeriods; i++) + { + T s = source[i]; + results.Add(new(s.Timestamp)); + } + + // get Stochastic of RSI + List stoResults = + source + .ToRsi(rsiPeriods) + .Remove(Math.Min(rsiPeriods, length)) // TODO: still need to Remove() here, or auto-healing? + .Select(x => new QuoteD( + Timestamp: x.Timestamp, + Open: 0, + High: x.Rsi.Null2NaN(), + Low: x.Rsi.Null2NaN(), + Close: x.Rsi.Null2NaN(), + Volume: 0 + )) + .ToList() + .CalcStoch( + stochPeriods, + signalPeriods, + smoothPeriods, 3, 2, MaType.SMA) + .ToList(); + + // add stoch results + for (int i = rsiPeriods + stochPeriods - 1; i < length; i++) + { + StochResult r = stoResults[i - rsiPeriods]; + + results.Add(new StochRsiResult( + Timestamp: r.Timestamp, + StochRsi: r.Oscillator, + Signal: r.Signal)); + } + + return results; + } +} diff --git a/src/s-z/StochRsi/StochRsi.Utilities.cs b/src/s-z/StochRsi/StochRsi.Utilities.cs index 52170655f..d7d71e061 100644 --- a/src/s-z/StochRsi/StochRsi.Utilities.cs +++ b/src/s-z/StochRsi/StochRsi.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// STOCHASTIC RSI (UTILITIES) + +public static partial class StochRsi { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int n = results .ToList() @@ -14,4 +15,37 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(n + 100); } + + // parameter validation + internal static void Validate( + int rsiPeriods, + int stochPeriods, + int signalPeriods, + int smoothPeriods) + { + // check parameter arguments + if (rsiPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(rsiPeriods), rsiPeriods, + "RSI periods must be greater than 0 for Stochastic RSI."); + } + + if (stochPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(stochPeriods), stochPeriods, + "STOCH periods must be greater than 0 for Stochastic RSI."); + } + + if (signalPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, + "Signal periods must be greater than 0 for Stochastic RSI."); + } + + if (smoothPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, + "Smooth periods must be greater than 0 for Stochastic RSI."); + } + } } diff --git a/src/s-z/SuperTrend/SuperTrend.Api.cs b/src/s-z/SuperTrend/SuperTrend.Api.cs deleted file mode 100644 index d36ad4638..000000000 --- a/src/s-z/SuperTrend/SuperTrend.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SUPERTREND (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetSuperTrend( - this IEnumerable quotes, - int lookbackPeriods = 10, - double multiplier = 3) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcSuperTrend(lookbackPeriods, multiplier); -} diff --git a/src/s-z/SuperTrend/SuperTrend.Models.cs b/src/s-z/SuperTrend/SuperTrend.Models.cs index cd8063612..f50d8dceb 100644 --- a/src/s-z/SuperTrend/SuperTrend.Models.cs +++ b/src/s-z/SuperTrend/SuperTrend.Models.cs @@ -1,14 +1,10 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class SuperTrendResult : ResultBase -{ - public SuperTrendResult(DateTime date) - { - Date = date; - } - - public decimal? SuperTrend { get; set; } - public decimal? UpperBand { get; set; } - public decimal? LowerBand { get; set; } -} +public record SuperTrendResult +( + DateTime Timestamp, + decimal? SuperTrend, + decimal? UpperBand, + decimal? LowerBand +) : ISeries; diff --git a/src/s-z/SuperTrend/SuperTrend.Series.cs b/src/s-z/SuperTrend/SuperTrend.Series.cs deleted file mode 100644 index 99b2bd9c9..000000000 --- a/src/s-z/SuperTrend/SuperTrend.Series.cs +++ /dev/null @@ -1,98 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SUPERTREND (SERIES) -public static partial class Indicator -{ - internal static List CalcSuperTrend( - this List qdList, - int lookbackPeriods, - double multiplier) - { - // check parameter arguments - ValidateSuperTrend(lookbackPeriods, multiplier); - - // initialize - List results = new(qdList.Count); - List atrResults = qdList.CalcAtr(lookbackPeriods); - - bool isBullish = true; - double? upperBand = null; - double? lowerBand = null; - - // roll through quotes - for (int i = 0; i < qdList.Count; i++) - { - QuoteD q = qdList[i]; - - SuperTrendResult r = new(q.Date); - results.Add(r); - - if (i >= lookbackPeriods) - { - double? mid = (q.High + q.Low) / 2; - double? atr = atrResults[i].Atr; - double? prevClose = qdList[i - 1].Close; - - // potential bands - double? upperEval = mid + (multiplier * atr); - double? lowerEval = mid - (multiplier * atr); - - // initial values - if (i == lookbackPeriods) - { - isBullish = q.Close >= mid; - - upperBand = upperEval; - lowerBand = lowerEval; - } - - // new upper band - if (upperEval < upperBand || prevClose > upperBand) - { - upperBand = upperEval; - } - - // new lower band - if (lowerEval > lowerBand || prevClose < lowerBand) - { - lowerBand = lowerEval; - } - - // supertrend - if (q.Close <= (isBullish ? lowerBand : upperBand)) - { - r.SuperTrend = (decimal?)upperBand; - r.UpperBand = (decimal?)upperBand; - isBullish = false; - } - else - { - r.SuperTrend = (decimal?)lowerBand; - r.LowerBand = (decimal?)lowerBand; - isBullish = true; - } - } - } - - return results; - } - - // parameter validation - private static void ValidateSuperTrend( - int lookbackPeriods, - double multiplier) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for SuperTrend."); - } - - if (multiplier <= 0) - { - throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, - "Multiplier must be greater than 0 for SuperTrend."); - } - } -} diff --git a/src/s-z/SuperTrend/SuperTrend.StaticSeries.cs b/src/s-z/SuperTrend/SuperTrend.StaticSeries.cs new file mode 100644 index 000000000..98e6fbee6 --- /dev/null +++ b/src/s-z/SuperTrend/SuperTrend.StaticSeries.cs @@ -0,0 +1,105 @@ +namespace Skender.Stock.Indicators; + +// SUPERTREND (SERIES) + +public static partial class SuperTrend +{ + public static IReadOnlyList ToSuperTrend( + this IReadOnlyList quotes, + int lookbackPeriods = 10, + double multiplier = 3) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcSuperTrend(lookbackPeriods, multiplier); + + private static List CalcSuperTrend( + this IReadOnlyList source, + int lookbackPeriods, + double multiplier) + { + // check parameter arguments + Validate(lookbackPeriods, multiplier); + + // initialize + int length = source.Count; + List results = new(length); + List atrResults = source.CalcAtr(lookbackPeriods); + + bool isBullish = true; + double? upperBand = null; + double? lowerBand = null; + + // roll through source values + for (int i = 0; i < length; i++) + { + QuoteD q = source[i]; + + double? superTrend; + double? upperOnly; + double? lowerOnly; + + if (i >= lookbackPeriods) + { + double? mid = (q.High + q.Low) / 2; + double? atr = atrResults[i].Atr; + double? prevClose = source[i - 1].Close; + + // potential bands + double? upperEval = mid + multiplier * atr; + double? lowerEval = mid - multiplier * atr; + + // initial values + // TODO: update healing, without requiring specific indexing + if (i == lookbackPeriods) + { + isBullish = q.Close >= mid; + + upperBand = upperEval; + lowerBand = lowerEval; + } + + // new upper band + if (upperEval < upperBand || prevClose > upperBand) + { + upperBand = upperEval; + } + + // new lower band + if (lowerEval > lowerBand || prevClose < lowerBand) + { + lowerBand = lowerEval; + } + + // supertrend + if (q.Close <= (isBullish ? lowerBand : upperBand)) + { + superTrend = upperBand; + upperOnly = upperBand; + lowerOnly = null; + isBullish = false; + } + else + { + superTrend = lowerBand; + lowerOnly = lowerBand; + upperOnly = null; + isBullish = true; + } + } + else + { + superTrend = null; + upperOnly = null; + lowerOnly = null; + } + + results.Add(new( + Timestamp: q.Timestamp, + SuperTrend: (decimal?)superTrend, + UpperBand: (decimal?)upperOnly, + LowerBand: (decimal?)lowerOnly)); + } + + return results; + } +} diff --git a/src/s-z/SuperTrend/SuperTrend.Utilities.cs b/src/s-z/SuperTrend/SuperTrend.Utilities.cs index 99efe4d80..052d097b9 100644 --- a/src/s-z/SuperTrend/SuperTrend.Utilities.cs +++ b/src/s-z/SuperTrend/SuperTrend.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// SUPERTREND (UTILITIES) + +public static partial class SuperTrend { // CONDENSE (REMOVE null results) - /// - /// - public static IEnumerable Condense( - this IEnumerable results) + /// + public static IReadOnlyList Condense( + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -19,10 +20,9 @@ public static IEnumerable Condense( } // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -30,4 +30,23 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods, + double multiplier) + { + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for SuperTrend."); + } + + if (multiplier <= 0) + { + throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, + "Multiplier must be greater than 0 for SuperTrend."); + } + } } diff --git a/src/s-z/T3/T3.Api.cs b/src/s-z/T3/T3.Api.cs deleted file mode 100644 index f1c5e5eaf..000000000 --- a/src/s-z/T3/T3.Api.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TILLSON T3 MOVING AVERAGE (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetT3( - this IEnumerable quotes, - int lookbackPeriods = 5, - double volumeFactor = 0.7) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcT3(lookbackPeriods, volumeFactor); - - // SERIES, from CHAIN - public static IEnumerable GetT3( - this IEnumerable results, - int lookbackPeriods = 5, - double volumeFactor = 0.7) => results - .ToTuple() - .CalcT3(lookbackPeriods, volumeFactor) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetT3( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods = 5, - double volumeFactor = 0.7) => priceTuples - .ToSortedList() - .CalcT3(lookbackPeriods, volumeFactor); -} diff --git a/src/s-z/T3/T3.Models.cs b/src/s-z/T3/T3.Models.cs index be3814a92..de026e889 100644 --- a/src/s-z/T3/T3.Models.cs +++ b/src/s-z/T3/T3.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class T3Result : ResultBase, IReusableResult +public record T3Result +( + DateTime Timestamp, + double? T3 +) : IReusable { - public T3Result(DateTime date) - { - Date = date; - } - - public double? T3 { get; set; } - - double? IReusableResult.Value => T3; + public double Value => T3.Null2NaN(); } diff --git a/src/s-z/T3/T3.Series.cs b/src/s-z/T3/T3.Series.cs deleted file mode 100644 index 11d0ad331..000000000 --- a/src/s-z/T3/T3.Series.cs +++ /dev/null @@ -1,82 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TILLSON T3 MOVING AVERAGE (SERIES) -public static partial class Indicator -{ - internal static List CalcT3( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - double volumeFactor) - { - // check parameter arguments - ValidateT3(lookbackPeriods, volumeFactor); - - // initialize - int length = tpList.Count; - List results = new(length); - - if (length == 0) - { - return results; - } - - 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; - double? e2; - double? e3; - double? e4; - double? e5; - double? e6; - - // add initial value - (DateTime date, double value) r0 = tpList[0]; - e1 = e2 = e3 = e4 = e5 = e6 = r0.value; - results.Add(new T3Result(r0.date) { T3 = r0.value }); - - // roll through quotes - for (int i = 1; i < length; i++) - { - (DateTime date, double value) = tpList[i]; - T3Result r = new(date); - results.Add(r); - - // first smoothing - e1 += k * (value - e1); - e2 += k * (e1 - e2); - e3 += k * (e2 - e3); - e4 += k * (e3 - e4); - e5 += k * (e4 - e5); - e6 += k * (e5 - e6); - - // T3 moving average - r.T3 = ((c1 * e6) + (c2 * e5) + (c3 * e4) + (c4 * e3)).NaN2Null(); - } - - return results; - } - - // parameter validation - private static void ValidateT3( - int lookbackPeriods, - double volumeFactor) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for T3."); - } - - if (volumeFactor <= 0) - { - throw new ArgumentOutOfRangeException(nameof(volumeFactor), volumeFactor, - "Volume Factor must be greater than 0 for T3."); - } - } -} diff --git a/src/s-z/T3/T3.StaticSeries.cs b/src/s-z/T3/T3.StaticSeries.cs new file mode 100644 index 000000000..a85bf64ac --- /dev/null +++ b/src/s-z/T3/T3.StaticSeries.cs @@ -0,0 +1,63 @@ +namespace Skender.Stock.Indicators; + +// TILLSON T3 MOVING AVERAGE (SERIES) + +public static partial class T3 +{ + public static IReadOnlyList ToT3( + this IReadOnlyList source, + int lookbackPeriods = 5, + double volumeFactor = 0.7) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods, volumeFactor); + + // initialize + int length = source.Count; + List 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 = double.NaN; + double e2 = double.NaN; + double e3 = double.NaN; + double e4 = double.NaN; + double e5 = double.NaN; + double e6 = double.NaN; + + // roll through remaining quotes + for (int i = 0; i < length; i++) + { + T s = source[i]; + + // re/seed values + if (double.IsNaN(e6)) + { + e1 = e2 = e3 = e4 = e5 = e6 = s.Value; + } + + // first smoothing + e1 += k * (s.Value - e1); + e2 += k * (e1 - e2); + e3 += k * (e2 - e3); + e4 += k * (e3 - e4); + e5 += k * (e4 - e5); + e6 += k * (e5 - e6); + + // T3 moving average + results.Add(new( + Timestamp: s.Timestamp, + T3: (c1 * e6 + c2 * e5 + c3 * e4 + c4 * e3).NaN2Null())); + } + + return results; + } +} diff --git a/src/s-z/T3/T3.Utilities.cs b/src/s-z/T3/T3.Utilities.cs new file mode 100644 index 000000000..70c4264aa --- /dev/null +++ b/src/s-z/T3/T3.Utilities.cs @@ -0,0 +1,25 @@ +namespace Skender.Stock.Indicators; + +// TILLSON T3 MOVING AVERAGE (UTILITIES) + +public static partial class T3 +{ + // parameter validation + internal static void Validate( + int lookbackPeriods, + double volumeFactor) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for T3."); + } + + if (volumeFactor <= 0) + { + throw new ArgumentOutOfRangeException(nameof(volumeFactor), volumeFactor, + "Volume Factor must be greater than 0 for T3."); + } + } +} diff --git a/src/s-z/Tema/Tema.Api.cs b/src/s-z/Tema/Tema.Api.cs deleted file mode 100644 index 1955004f3..000000000 --- a/src/s-z/Tema/Tema.Api.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TRIPLE EXPONENTIAL MOVING AVERAGE - TEMA (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetTema( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcTema(lookbackPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetTema( - this IEnumerable results, - int lookbackPeriods) => results - .ToTuple() - .CalcTema(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetTema( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods) => priceTuples - .ToSortedList() - .CalcTema(lookbackPeriods); -} diff --git a/src/s-z/Tema/Tema.Models.cs b/src/s-z/Tema/Tema.Models.cs index 145a7548a..825eecffe 100644 --- a/src/s-z/Tema/Tema.Models.cs +++ b/src/s-z/Tema/Tema.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class TemaResult : ResultBase, IReusableResult +public record TemaResult +( + DateTime Timestamp, + double? Tema = null +) : IReusable { - public TemaResult(DateTime date) - { - Date = date; - } - - public double? Tema { get; set; } - - double? IReusableResult.Value => Tema; + public double Value => Tema.Null2NaN(); } diff --git a/src/s-z/Tema/Tema.Series.cs b/src/s-z/Tema/Tema.Series.cs deleted file mode 100644 index 2a564f2c5..000000000 --- a/src/s-z/Tema/Tema.Series.cs +++ /dev/null @@ -1,73 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TRIPLE EXPONENTIAL MOVING AVERAGE - TEMA (SERIES) -public static partial class Indicator -{ - // calculate series - internal static List CalcTema( - this List<(DateTime, double)> tpList, - int lookbackPeriods) - { - // check parameter arguments - ValidateTema(lookbackPeriods); - - // initialize - int length = tpList.Count; - List results = new(length); - - double k = 2d / (lookbackPeriods + 1); - double? lastEma1 = 0; - double? lastEma2; - double? lastEma3; - int initPeriods = Math.Min(lookbackPeriods, length); - - for (int i = 0; i < initPeriods; i++) - { - (DateTime _, double value) = tpList[i]; - lastEma1 += value; - } - - lastEma1 /= lookbackPeriods; - lastEma2 = lastEma3 = lastEma1; - - // roll through quotes - for (int i = 0; i < length; i++) - { - (DateTime date, double value) = tpList[i]; - - TemaResult r = new(date); - results.Add(r); - - if (i > lookbackPeriods - 1) - { - double? ema1 = lastEma1 + (k * (value - lastEma1)); - double? ema2 = lastEma2 + (k * (ema1 - lastEma2)); - double? ema3 = lastEma3 + (k * (ema2 - lastEma3)); - - r.Tema = ((3 * ema1) - (3 * ema2) + ema3).NaN2Null(); - - lastEma1 = ema1; - lastEma2 = ema2; - lastEma3 = ema3; - } - else if (i == lookbackPeriods - 1) - { - r.Tema = ((3 * lastEma1) - (3 * lastEma2) + lastEma3).NaN2Null(); - } - } - - return results; - } - - // parameter validation - private static void ValidateTema( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for TEMA."); - } - } -} diff --git a/src/s-z/Tema/Tema.StaticSeries.cs b/src/s-z/Tema/Tema.StaticSeries.cs new file mode 100644 index 000000000..f14a28512 --- /dev/null +++ b/src/s-z/Tema/Tema.StaticSeries.cs @@ -0,0 +1,74 @@ +namespace Skender.Stock.Indicators; + +// TRIPLE EXPONENTIAL MOVING AVERAGE (SERIES) + +public static partial class Tema +{ + // calculate series + public static IReadOnlyList ToTema( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + double k = 2d / (lookbackPeriods + 1); + double lastEma1 = double.NaN; + double lastEma2 = double.NaN; + double lastEma3 = double.NaN; + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + // skip incalculable periods + if (i < lookbackPeriods - 1) + { + results.Add(new(s.Timestamp)); + continue; + } + + double ema1; + double ema2; + double ema3; + + // when no prior EMA, reset as SMA + if (double.IsNaN(lastEma3)) + { + double sum = 0; + for (int p = i - lookbackPeriods + 1; p <= i; p++) + { + T ps = source[p]; + sum += ps.Value; + } + + ema1 = ema2 = ema3 = sum / lookbackPeriods; + } + + // normal TEMA + else + { + ema1 = lastEma1 + (k * (s.Value - lastEma1)); + ema2 = lastEma2 + (k * (ema1 - lastEma2)); + ema3 = lastEma3 + (k * (ema2 - lastEma3)); + } + + results.Add(new TemaResult( + Timestamp: s.Timestamp, + Tema: ((3 * ema1) - (3 * ema2) + ema3).NaN2Null())); + + lastEma1 = ema1; + lastEma2 = ema2; + lastEma3 = ema3; + } + + return results; + } +} diff --git a/src/s-z/Tema/Tema.Utilities.cs b/src/s-z/Tema/Tema.Utilities.cs index ad438bec5..963479cb1 100644 --- a/src/s-z/Tema/Tema.Utilities.cs +++ b/src/s-z/Tema/Tema.Utilities.cs @@ -1,17 +1,30 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// TRIPLE EXPONENTIAL MOVING AVERAGE (UTILITIES) + +public static partial class Tema { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int n = results .ToList() .FindIndex(x => x.Tema != null) + 1; - return results.Remove((3 * n) + 100); + return results.Remove(3 * n + 100); + } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for TEMA."); + } } } diff --git a/src/s-z/Tr/Tr.Api.cs b/src/s-z/Tr/Tr.Api.cs deleted file mode 100644 index d3cf0ac23..000000000 --- a/src/s-z/Tr/Tr.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TRUE RANGE (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetTr( - this IEnumerable quotes) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcTr(); -} diff --git a/src/s-z/Tr/Tr.Models.cs b/src/s-z/Tr/Tr.Models.cs index 2cb02a6aa..49dff2582 100644 --- a/src/s-z/Tr/Tr.Models.cs +++ b/src/s-z/Tr/Tr.Models.cs @@ -1,14 +1,10 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class TrResult : ResultBase, IReusableResult +public record TrResult( + DateTime Timestamp, + double? Tr +) : IReusable { - public TrResult(DateTime date) - { - Date = date; - } - - public double? Tr { get; set; } - - double? IReusableResult.Value => Tr; + public double Value => Tr.Null2NaN(); } diff --git a/src/s-z/Tr/Tr.Series.cs b/src/s-z/Tr/Tr.Series.cs deleted file mode 100644 index 617928bfe..000000000 --- a/src/s-z/Tr/Tr.Series.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TRUE RANGE (SERIES) -public static partial class Indicator -{ - // calculate series - internal static List CalcTr( - this List qdList) - { - // initialize - List results = new(qdList.Count); - double prevClose = double.NaN; - - // roll through quotes - for (int i = 0; i < qdList.Count; i++) - { - QuoteD q = qdList[i]; - - TrResult r = new(q.Date); - results.Add(r); - - if (i is 0) - { - prevClose = q.Close; - continue; - } - - double hmpc = Math.Abs(q.High - prevClose); - double lmpc = Math.Abs(q.Low - prevClose); - - r.Tr = Math.Max(q.High - q.Low, Math.Max(hmpc, lmpc)); - - prevClose = q.Close; - } - - return results; - } -} diff --git a/src/s-z/Tr/Tr.StaticSeries.cs b/src/s-z/Tr/Tr.StaticSeries.cs new file mode 100644 index 000000000..79fa0dbaf --- /dev/null +++ b/src/s-z/Tr/Tr.StaticSeries.cs @@ -0,0 +1,38 @@ +namespace Skender.Stock.Indicators; + +// TRUE RANGE (SERIES) + +public static partial class Tr +{ + public static IReadOnlyList ToTr( + this IReadOnlyList quotes) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcTr(); + + private static List CalcTr( + this IReadOnlyList source) + { + // initialize + int length = source.Count; + TrResult[] results = new TrResult[length]; + + // skip first period + if (length > 0) + { + results[0] = new TrResult(source[0].Timestamp, null); + } + + // roll through source values + for (int i = 1; i < length; i++) + { + QuoteD q = source[i]; + + results[i] = new TrResult( + Timestamp: q.Timestamp, + Tr: Increment(q.High, q.Low, source[i - 1].Close)); + } + + return new List(results); + } +} diff --git a/src/s-z/Tr/Tr.StreamHub.cs b/src/s-z/Tr/Tr.StreamHub.cs new file mode 100644 index 000000000..ea0803e9b --- /dev/null +++ b/src/s-z/Tr/Tr.StreamHub.cs @@ -0,0 +1,58 @@ +namespace Skender.Stock.Indicators; + +// TRUE RANGE (STREAM HUB) + +#region initializer + +public static partial class Tr +{ + public static TrHub ToTr( + this IQuoteProvider quoteProvider) + where TIn : IQuote + => new(quoteProvider); +} +#endregion + +public class TrHub + : ChainProvider + where TIn : IQuote +{ + #region constructors + + private const string hubName = "TRUE RANGE"; + + internal TrHub(IQuoteProvider provider) + : base(provider) + { + Reinitialize(); + } + #endregion + + // METHODS + + public override string ToString() => hubName; + + protected override (TrResult result, int index) + ToIndicator(TIn item, int? indexHint) + { + int i = indexHint ?? ProviderCache.GetIndex(item, true); + + // skip first period + if (i == 0) + { + return (new TrResult(item.Timestamp, null), i); + } + + TIn prev = ProviderCache[i - 1]; + + // candidate result + TrResult r = new( + item.Timestamp, + Tr.Increment( + (double)item.High, + (double)item.Low, + (double)prev.Close)); + + return (r, i); + } +} diff --git a/src/s-z/Tr/Tr.Utilities.cs b/src/s-z/Tr/Tr.Utilities.cs new file mode 100644 index 000000000..d6f9f4d09 --- /dev/null +++ b/src/s-z/Tr/Tr.Utilities.cs @@ -0,0 +1,17 @@ +namespace Skender.Stock.Indicators; + +// TRUE RANGE (UTILITIES) + +public static partial class Tr +{ + public static double Increment( + double high, + double low, + double prevClose) + { + double hmpc = Math.Abs(high - prevClose); + double lmpc = Math.Abs(low - prevClose); + + return Math.Max(high - low, Math.Max(hmpc, lmpc)); + } +} diff --git a/src/s-z/Tr/info.xml b/src/s-z/Tr/info.xml index 407b2b550..cacf17b58 100644 --- a/src/s-z/Tr/info.xml +++ b/src/s-z/Tr/info.xml @@ -1,16 +1,30 @@ - - True Range (TR) is a measure of volatility that captures gaps and limits between periods. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Time series of True Range (TR) values. - Invalid parameter value provided. + + + True Range (TR) is a measure of volatility that captures gaps and limits between periods. + + See + documentation + for more information. + + + Configurable Quote type. See Guide for more information. + Historical price quotes. + Time series of True Range (TR) values. + Invalid parameter value provided. + + + Get the next incremental True Range (TR) value. + + See + documentation + for more information. + + Last Close price, from prior period. + High price, current period. + Low price, current period. + New TR value. + \ No newline at end of file diff --git a/src/s-z/Trix/Trix.Api.cs b/src/s-z/Trix/Trix.Api.cs deleted file mode 100644 index 88914cd69..000000000 --- a/src/s-z/Trix/Trix.Api.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TRIPLE EMA OSCILLATOR - TRIX (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetTrix( - this IEnumerable quotes, - int lookbackPeriods, - int? signalPeriods = null) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcTrix(lookbackPeriods, signalPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetTrix( - this IEnumerable results, - int lookbackPeriods, - int? signalPeriods = null) => results - .ToTuple() - .CalcTrix(lookbackPeriods, signalPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetTrix( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods, - int? signalPeriods = null) => priceTuples - .ToSortedList() - .CalcTrix(lookbackPeriods, signalPeriods); -} diff --git a/src/s-z/Trix/Trix.Models.cs b/src/s-z/Trix/Trix.Models.cs index de69555be..ad12f3937 100644 --- a/src/s-z/Trix/Trix.Models.cs +++ b/src/s-z/Trix/Trix.Models.cs @@ -1,16 +1,12 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class TrixResult : ResultBase, IReusableResult +public record TrixResult +( + DateTime Timestamp, + double? Ema3 = null, + double? Trix = null +) : IReusable { - public TrixResult(DateTime date) - { - Date = date; - } - - public double? Ema3 { get; set; } - public double? Trix { get; set; } - public double? Signal { get; set; } - - double? IReusableResult.Value => Trix; + public double Value => Trix.Null2NaN(); } diff --git a/src/s-z/Trix/Trix.Series.cs b/src/s-z/Trix/Trix.Series.cs deleted file mode 100644 index 14ec09e59..000000000 --- a/src/s-z/Trix/Trix.Series.cs +++ /dev/null @@ -1,88 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TRIPLE EMA OSCILLATOR - TRIX (SERIES) -public static partial class Indicator -{ - internal static List CalcTrix( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - int? signalPeriods) - { - // check parameter arguments - ValidateTrix(lookbackPeriods); - - // initialize - int length = tpList.Count; - List results = new(length); - - double k = 2d / (lookbackPeriods + 1); - double? lastEma1 = 0; - double? lastEma2; - double? lastEma3; - int initPeriods = Math.Min(lookbackPeriods, length); - - for (int i = 0; i < initPeriods; i++) - { - lastEma1 += tpList[i].Item2; - } - - lastEma1 /= initPeriods; - lastEma2 = lastEma3 = lastEma1; - - // compose final results - for (int i = 0; i < length; i++) - { - (DateTime date, double value) = tpList[i]; - - TrixResult r = new(date); - results.Add(r); - - if (i >= lookbackPeriods) - { - double? ema1 = lastEma1 + (k * (value - lastEma1)); - double? ema2 = lastEma2 + (k * (ema1 - lastEma2)); - double? ema3 = lastEma3 + (k * (ema2 - lastEma3)); - - r.Ema3 = ema3.NaN2Null(); - r.Trix = (100d * (ema3 - lastEma3) / lastEma3).NaN2Null(); - - lastEma1 = ema1; - lastEma2 = ema2; - lastEma3 = ema3; - } - - // optional SMA signal - CalcTrixSignal(signalPeriods, i, lookbackPeriods, results); - } - - return results; - } - - // internals - private static void CalcTrixSignal( - int? signalPeriods, int i, int lookbackPeriods, List results) - { - if (signalPeriods != null && i >= (lookbackPeriods + signalPeriods - 1)) - { - double? sumSma = 0; - for (int p = i + 1 - (int)signalPeriods; p <= i; p++) - { - sumSma += results[p].Trix; - } - - results[i].Signal = sumSma / signalPeriods; - } - } - - // parameter validation - private static void ValidateTrix( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for TRIX."); - } - } -} diff --git a/src/s-z/Trix/Trix.StaticSeries.cs b/src/s-z/Trix/Trix.StaticSeries.cs new file mode 100644 index 000000000..a94818133 --- /dev/null +++ b/src/s-z/Trix/Trix.StaticSeries.cs @@ -0,0 +1,78 @@ +namespace Skender.Stock.Indicators; + +// TRIPLE EMA OSCILLATOR - TRIX (SERIES) + +public static partial class Trix +{ + public static IReadOnlyList ToTrix( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + double k = 2d / (lookbackPeriods + 1); + double lastEma1 = double.NaN; + double lastEma2 = double.NaN; + double lastEma3 = double.NaN; + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + // skip incalculable periods + if (i < lookbackPeriods - 1) + { + results.Add(new(s.Timestamp)); + continue; + } + + double ema1; + double ema2; + double ema3; + + // when no prior EMA, reset as SMA + if (double.IsNaN(lastEma3)) + { + double sum = 0; + for (int p = i - lookbackPeriods + 1; p <= i; p++) + { + T ps = source[p]; + sum += ps.Value; + } + + ema1 = ema2 = ema3 = sum / lookbackPeriods; + + results.Add(new(s.Timestamp)); + } + + // normal TRIX + else + { + ema1 = lastEma1 + (k * (s.Value - lastEma1)); + ema2 = lastEma2 + (k * (ema1 - lastEma2)); + ema3 = lastEma3 + (k * (ema2 - lastEma3)); + + double trix = 100 * (ema3 - lastEma3) / lastEma3; + + results.Add(new TrixResult( + Timestamp: s.Timestamp, + Ema3: ema3.NaN2Null(), + Trix: trix.NaN2Null())); + } + + lastEma1 = ema1; + lastEma2 = ema2; + lastEma3 = ema3; + } + + return results; + } +} diff --git a/src/s-z/Trix/Trix.Utilities.cs b/src/s-z/Trix/Trix.Utilities.cs index 2849feb50..ce869af82 100644 --- a/src/s-z/Trix/Trix.Utilities.cs +++ b/src/s-z/Trix/Trix.Utilities.cs @@ -1,17 +1,30 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// TRIPLE EMA OSCILLATOR - TRIX (UTILITIES) + +public static partial class Trix { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int n = results .ToList() .FindIndex(x => x.Trix != null); - return results.Remove((3 * n) + 100); + return results.Remove(3 * n + 100); + } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for TRIX."); + } } } diff --git a/src/s-z/Trix/info.xml b/src/s-z/Trix/info.xml index 67c8c5884..00f9697cb 100644 --- a/src/s-z/Trix/info.xml +++ b/src/s-z/Trix/info.xml @@ -12,7 +12,6 @@ Configurable Quote type. See Guide for more information. Historical price quotes. Number of periods in the lookback window. - Optional. Number of periods for a TRIX SMA signal line. Time series of TRIX values. Invalid parameter value provided. \ No newline at end of file diff --git a/src/s-z/Tsi/Tsi.Api.cs b/src/s-z/Tsi/Tsi.Api.cs deleted file mode 100644 index 9cbb03e3d..000000000 --- a/src/s-z/Tsi/Tsi.Api.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TRUE STRENGTH INDEX (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetTsi( - this IEnumerable quotes, - int lookbackPeriods = 25, - int smoothPeriods = 13, - int signalPeriods = 7) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcTsi(lookbackPeriods, smoothPeriods, signalPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetTsi( - this IEnumerable results, - int lookbackPeriods = 25, - int smoothPeriods = 13, - int signalPeriods = 7) => results - .ToTuple() - .CalcTsi(lookbackPeriods, smoothPeriods, signalPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetTsi( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods = 25, - int smoothPeriods = 13, - int signalPeriods = 7) => priceTuples - .ToSortedList() - .CalcTsi(lookbackPeriods, smoothPeriods, signalPeriods); -} diff --git a/src/s-z/Tsi/Tsi.Models.cs b/src/s-z/Tsi/Tsi.Models.cs index c01f12784..35a2caf2c 100644 --- a/src/s-z/Tsi/Tsi.Models.cs +++ b/src/s-z/Tsi/Tsi.Models.cs @@ -1,15 +1,12 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class TsiResult : ResultBase, IReusableResult +public record TsiResult +( + DateTime Timestamp, + double? Tsi = null, + double? Signal = null +) : IReusable { - public TsiResult(DateTime date) - { - Date = date; - } - - public double? Tsi { get; set; } - public double? Signal { get; set; } - - double? IReusableResult.Value => Tsi; + public double Value => Tsi.Null2NaN(); } diff --git a/src/s-z/Tsi/Tsi.Series.cs b/src/s-z/Tsi/Tsi.Series.cs deleted file mode 100644 index c89f62891..000000000 --- a/src/s-z/Tsi/Tsi.Series.cs +++ /dev/null @@ -1,161 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TRUE STRENGTH INDEX (SERIES) -public static partial class Indicator -{ - internal static List CalcTsi( - this List<(DateTime, double)> tpList, - int lookbackPeriods, - int smoothPeriods, - int signalPeriods) - { - // check parameter arguments - ValidateTsi(lookbackPeriods, smoothPeriods, signalPeriods); - - // initialize - int length = tpList.Count; - double mult1 = 2d / (lookbackPeriods + 1); - double mult2 = 2d / (smoothPeriods + 1); - double multS = 2d / (signalPeriods + 1); - double sumS = 0; - - List 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++) - { - (DateTime date, double value) = tpList[i]; - - TsiResult r = new(date); - results.Add(r); - - // skip first period - if (i == 0) - { - continue; - } - - // price change - c[i] = value - tpList[i - 1].Item2; - a[i] = Math.Abs(c[i]); - - // smoothing - if (i > lookbackPeriods) - { - // 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 (i + 1 > lookbackPeriods + smoothPeriods) - { - cs2[i] = ((cs1[i] - cs2[i - 1]) * mult2) + cs2[i - 1]; - as2[i] = ((as1[i] - as2[i - 1]) * mult2) + as2[i - 1]; - - double tsi = (as2[i] != 0) ? 100d * (cs2[i] / as2[i]) : double.NaN; - r.Tsi = tsi.NaN2Null(); - - // signal line - if (signalPeriods > 0) - { - int startSignal = lookbackPeriods + smoothPeriods + signalPeriods - 1; - - if (i >= startSignal) - { - r.Signal = ((tsi - results[i - 1].Signal) * multS).NaN2Null() - + results[i - 1].Signal; - } - - // initialize signal - else if (i == startSignal - 1) - { - sumS += tsi; - r.Signal = sumS / signalPeriods; - } - - // warmup signal - else - { - sumS += tsi; - } - } - } - - // prepare second smoothing - else - { - sumC1 += cs1[i]; - sumA1 += as1[i]; - - // inialize second smoothing - if (i + 1 == lookbackPeriods + smoothPeriods) - { - cs2[i] = sumC1 / smoothPeriods; - as2[i] = sumA1 / smoothPeriods; - - double tsi = (as2[i] != 0) ? 100 * cs2[i] / as2[i] : double.NaN; - r.Tsi = tsi; - sumS = tsi; - } - } - } - - // prepare first smoothing - else - { - sumC += c[i]; - sumA += a[i]; - - // initialize first smoothing - if (i == lookbackPeriods) - { - cs1[i] = sumC / lookbackPeriods; - as1[i] = sumA / lookbackPeriods; - - sumC1 = cs1[i]; - sumA1 = as1[i]; - } - } - } - - return results; - } - - // parameter validation - private static void ValidateTsi( - int lookbackPeriods, - int smoothPeriods, - int signalPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for TSI."); - } - - if (smoothPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, - "Smoothing periods must be greater than 0 for TSI."); - } - - if (signalPeriods < 0) - { - throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, - "Signal periods must be greater than or equal to 0 for TSI."); - } - } -} diff --git a/src/s-z/Tsi/Tsi.StaticSeries.cs b/src/s-z/Tsi/Tsi.StaticSeries.cs new file mode 100644 index 000000000..578f6ab88 --- /dev/null +++ b/src/s-z/Tsi/Tsi.StaticSeries.cs @@ -0,0 +1,149 @@ +namespace Skender.Stock.Indicators; + +// TRUE STRENGTH INDEX (SERIES) + +public static partial class Tsi +{ + public static IReadOnlyList ToTsi( + this IReadOnlyList source, + int lookbackPeriods = 25, + int smoothPeriods = 13, + int signalPeriods = 7) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods, smoothPeriods, signalPeriods); + + // initialize + int length = source.Count; + double mult1 = 2d / (lookbackPeriods + 1); + double mult2 = 2d / (smoothPeriods + 1); + double multS = 2d / (signalPeriods + 1); + List 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[] a = new double[length]; // abs of price change + double[] as1 = new double[length]; // smooth 1 + double[] as2 = new double[length]; // smooth 2 + + double prevSignal = double.NaN; + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + // skip first period + if (i == 0) + { + a[i] = double.NaN; + c[i] = double.NaN; + cs1[i] = double.NaN; + as1[i] = double.NaN; + cs2[i] = double.NaN; + as2[i] = double.NaN; + + results.Add(new(s.Timestamp)); + continue; + } + + // price change + c[i] = s.Value - source[i - 1].Value; + a[i] = Math.Abs(c[i]); + + // re/initialize first smoothing + if (double.IsNaN(cs1[i - 1]) && i >= lookbackPeriods) + { + double sumC = 0; + double sumA = 0; + + for (int p = i - lookbackPeriods + 1; p <= i; p++) + { + sumC += c[p]; + sumA += a[p]; + } + + cs1[i] = sumC / lookbackPeriods; + as1[i] = sumA / lookbackPeriods; + } + + // normal first smoothing + else + { + cs1[i] = ((c[i] - cs1[i - 1]) * mult1) + cs1[i - 1]; + as1[i] = ((a[i] - as1[i - 1]) * mult1) + as1[i - 1]; + } + + // re/initialize second smoothing + if (double.IsNaN(cs2[i - 1]) && i >= smoothPeriods) + { + double sumCs = 0; + double sumAs = 0; + + for (int p = i - smoothPeriods + 1; p <= i; p++) + { + sumCs += cs1[p]; + sumAs += as1[p]; + } + + cs2[i] = sumCs / smoothPeriods; + as2[i] = sumAs / smoothPeriods; + } + + // normal second smoothing + else + { + cs2[i] = ((cs1[i] - cs2[i - 1]) * mult2) + cs2[i - 1]; + as2[i] = ((as1[i] - as2[i - 1]) * mult2) + as2[i - 1]; + } + + // true strength index + double tsi = as2[i] != 0 + ? 100d * (cs2[i] / as2[i]) + : double.NaN; + + // signal line + double signal; + + if (signalPeriods > 1) + { + // re/initialize signal + if (double.IsNaN(prevSignal) && i > signalPeriods) + { + double sum = tsi; + for (int p = i - signalPeriods + 1; p < i; p++) + { + sum += results[p].Tsi.Null2NaN(); + } + + signal = sum / signalPeriods; + } + + // normal signal + else + { + signal = ((tsi - prevSignal) * multS) + prevSignal; + } + } + else + { + signal = signalPeriods == 1 + ? tsi + : double.NaN; + } + + results.Add(new TsiResult( + Timestamp: s.Timestamp, + Tsi: tsi.NaN2Null(), + Signal: signal.NaN2Null())); + + prevSignal = signal; + } + + return results; + } +} diff --git a/src/s-z/Tsi/Tsi.Utilities.cs b/src/s-z/Tsi/Tsi.Utilities.cs index cc5efd837..16b08c8ef 100644 --- a/src/s-z/Tsi/Tsi.Utilities.cs +++ b/src/s-z/Tsi/Tsi.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// TRUE STRENGTH INDEX (UTILITIES) + +public static partial class Tsi { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int nm = results .ToList() @@ -14,4 +15,30 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(nm + 250); } + + // parameter validation + internal static void Validate( + int lookbackPeriods, + int smoothPeriods, + int signalPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for TSI."); + } + + if (smoothPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, + "Smoothing periods must be greater than 0 for TSI."); + } + + if (signalPeriods < 0) + { + throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, + "Signal periods must be greater than or equal to 0 for TSI."); + } + } } diff --git a/src/s-z/UlcerIndex/UlcerIndex.Api.cs b/src/s-z/UlcerIndex/UlcerIndex.Api.cs deleted file mode 100644 index 2ca2b6828..000000000 --- a/src/s-z/UlcerIndex/UlcerIndex.Api.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ULCER INDEX (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetUlcerIndex( - this IEnumerable quotes, - int lookbackPeriods = 14) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcUlcerIndex(lookbackPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetUlcerIndex( - this IEnumerable results, - int lookbackPeriods) => results - .ToTuple() - .CalcUlcerIndex(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetUlcerIndex( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods) => priceTuples - .ToSortedList() - .CalcUlcerIndex(lookbackPeriods); -} diff --git a/src/s-z/UlcerIndex/UlcerIndex.Models.cs b/src/s-z/UlcerIndex/UlcerIndex.Models.cs index 81064ae3b..72332f6a0 100644 --- a/src/s-z/UlcerIndex/UlcerIndex.Models.cs +++ b/src/s-z/UlcerIndex/UlcerIndex.Models.cs @@ -1,14 +1,14 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class UlcerIndexResult : ResultBase, IReusableResult +public record UlcerIndexResult +( + DateTime Timestamp, + double? UlcerIndex +) : IReusable { - public UlcerIndexResult(DateTime date) - { - Date = date; - } + public double Value => UlcerIndex.Null2NaN(); - public double? UI { get; set; } // ulcer index - - double? IReusableResult.Value => UI; + [Obsolete("Rename UI to UlcerIndex")] // v3.0.0 + public double? UI => UlcerIndex; } diff --git a/src/s-z/UlcerIndex/UlcerIndex.Series.cs b/src/s-z/UlcerIndex/UlcerIndex.Series.cs deleted file mode 100644 index 9d217a036..000000000 --- a/src/s-z/UlcerIndex/UlcerIndex.Series.cs +++ /dev/null @@ -1,66 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ULCER INDEX (SERIES) -public static partial class Indicator -{ - internal static List CalcUlcerIndex( - this List<(DateTime, double)> tpList, - int lookbackPeriods) - { - // check parameter arguments - ValidateUlcer(lookbackPeriods); - - // initialize - List results = new(tpList.Count); - - // roll through quotes - for (int i = 0; i < tpList.Count; i++) - { - (DateTime date, double _) = tpList[i]; - - UlcerIndexResult r = new(date); - results.Add(r); - - if (i + 1 >= lookbackPeriods) - { - double sumSquared = 0; - for (int p = i + 1 - lookbackPeriods; p <= i; p++) - { - (DateTime _, double pValue) = tpList[p]; - int dIndex = p + 1; - - double maxClose = 0; - for (int s = i + 1 - lookbackPeriods; s < dIndex; s++) - { - (DateTime _, double sValue) = tpList[s]; - if (sValue > maxClose) - { - maxClose = sValue; - } - } - - double percentDrawdown = (maxClose == 0) ? double.NaN - : 100 * ((pValue - maxClose) / maxClose); - - sumSquared += percentDrawdown * percentDrawdown; - } - - r.UI = Math.Sqrt(sumSquared / lookbackPeriods).NaN2Null(); - } - } - - return results; - } - - // parameter validation - private static void ValidateUlcer( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Ulcer Index."); - } - } -} diff --git a/src/s-z/UlcerIndex/UlcerIndex.StaticSeries.cs b/src/s-z/UlcerIndex/UlcerIndex.StaticSeries.cs new file mode 100644 index 000000000..29f7a3bc3 --- /dev/null +++ b/src/s-z/UlcerIndex/UlcerIndex.StaticSeries.cs @@ -0,0 +1,66 @@ +namespace Skender.Stock.Indicators; + +// ULCER INDEX (SERIES) + +public static partial class UlcerIndex +{ + public static IReadOnlyList ToUlcerIndex( + this IReadOnlyList source, + int lookbackPeriods = 14) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + double? ui; + + if (i + 1 >= lookbackPeriods) + { + double sumSquared = 0; + for (int p = i + 1 - lookbackPeriods; p <= i; p++) + { + T ps = source[p]; + int dIndex = p + 1; + + double maxClose = 0; + for (int z = i + 1 - lookbackPeriods; z < dIndex; z++) + { + T zs = source[z]; + if (zs.Value > maxClose) + { + maxClose = zs.Value; + } + } + + double percentDrawdown = maxClose == 0 ? double.NaN + : 100 * ((ps.Value - maxClose) / maxClose); + + sumSquared += percentDrawdown * percentDrawdown; + } + + ui = Math.Sqrt(sumSquared / lookbackPeriods).NaN2Null(); + } + else + { + ui = null; + } + + UlcerIndexResult r = new( + Timestamp: s.Timestamp, + UlcerIndex: ui); + results.Add(r); + } + + return results; + } +} diff --git a/src/s-z/UlcerIndex/UlcerIndex.Utilities.cs b/src/s-z/UlcerIndex/UlcerIndex.Utilities.cs index 57dff98a9..26679c7a6 100644 --- a/src/s-z/UlcerIndex/UlcerIndex.Utilities.cs +++ b/src/s-z/UlcerIndex/UlcerIndex.Utilities.cs @@ -1,17 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ULCER INDEX (UTILITIES) + +public static partial class UlcerIndex { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.UI != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Ulcer Index."); + } } } diff --git a/src/s-z/Ultimate/Ultimate.Api.cs b/src/s-z/Ultimate/Ultimate.Api.cs deleted file mode 100644 index 702908f00..000000000 --- a/src/s-z/Ultimate/Ultimate.Api.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ULTIMATE OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetUltimate( - this IEnumerable quotes, - int shortPeriods = 7, - int middlePeriods = 14, - int longPeriods = 28) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcUltimate(shortPeriods, middlePeriods, longPeriods); -} diff --git a/src/s-z/Ultimate/Ultimate.Models.cs b/src/s-z/Ultimate/Ultimate.Models.cs index 4238d731a..b8b82ba3f 100644 --- a/src/s-z/Ultimate/Ultimate.Models.cs +++ b/src/s-z/Ultimate/Ultimate.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class UltimateResult : ResultBase, IReusableResult +public record UltimateResult +( + DateTime Timestamp, + double? Ultimate +) : IReusable { - public UltimateResult(DateTime date) - { - Date = date; - } - - public double? Ultimate { get; set; } - - double? IReusableResult.Value => Ultimate; + public double Value => Ultimate.Null2NaN(); } diff --git a/src/s-z/Ultimate/Ultimate.Series.cs b/src/s-z/Ultimate/Ultimate.Series.cs deleted file mode 100644 index 4fc3bcba3..000000000 --- a/src/s-z/Ultimate/Ultimate.Series.cs +++ /dev/null @@ -1,102 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ULTIMATE OSCILLATOR (SERIES) -public static partial class Indicator -{ - internal static List CalcUltimate( - this List qdList, - int shortPeriods, - int middlePeriods, - int longPeriods) - { - // check parameter arguments - ValidateUltimate(shortPeriods, middlePeriods, longPeriods); - - // initialize - int length = qdList.Count; - List results = new(length); - double[] bp = new double[length]; // buying pressure - double[] tr = new double[length]; // true range - - double priorClose = 0; - - // roll through quotes - for (int i = 0; i < qdList.Count; i++) - { - QuoteD q = qdList[i]; - - UltimateResult r = new(q.Date); - results.Add(r); - - if (i > 0) - { - bp[i] = q.Close - Math.Min(q.Low, priorClose); - tr[i] = Math.Max(q.High, priorClose) - Math.Min(q.Low, priorClose); - } - - if (i >= longPeriods) - { - double sumBP1 = 0; - double sumBP2 = 0; - double sumBP3 = 0; - - double sumTR1 = 0; - double sumTR2 = 0; - double sumTR3 = 0; - - for (int p = i + 1 - longPeriods; p <= i; p++) - { - int pIndex = p + 1; - - // short aggregate - if (pIndex > i + 1 - shortPeriods) - { - sumBP1 += bp[p]; - sumTR1 += tr[p]; - } - - // middle aggregate - if (pIndex > i + 1 - middlePeriods) - { - sumBP2 += bp[p]; - sumTR2 += tr[p]; - } - - // long aggregate - sumBP3 += bp[p]; - sumTR3 += tr[p]; - } - - double avg1 = (sumTR1 == 0) ? double.NaN : sumBP1 / sumTR1; - double avg2 = (sumTR2 == 0) ? double.NaN : sumBP2 / sumTR2; - double avg3 = (sumTR3 == 0) ? double.NaN : sumBP3 / sumTR3; - - r.Ultimate = (100d * ((4d * avg1) + (2d * avg2) + avg3) / 7d).NaN2Null(); - } - - priorClose = q.Close; - } - - return results; - } - - // parameter validation - private static void ValidateUltimate( - int shortPeriods, - int middleAverage, - int longPeriods) - { - // check parameter arguments - if (shortPeriods <= 0 || middleAverage <= 0 || longPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(longPeriods), longPeriods, - "Average periods must be greater than 0 for Ultimate Oscillator."); - } - - if (shortPeriods >= middleAverage || middleAverage >= longPeriods) - { - throw new ArgumentOutOfRangeException(nameof(middleAverage), middleAverage, - "Average periods must be increasingly larger than each other for Ultimate Oscillator."); - } - } -} diff --git a/src/s-z/Ultimate/Ultimate.StaticSeries.cs b/src/s-z/Ultimate/Ultimate.StaticSeries.cs new file mode 100644 index 000000000..a4978492b --- /dev/null +++ b/src/s-z/Ultimate/Ultimate.StaticSeries.cs @@ -0,0 +1,98 @@ +namespace Skender.Stock.Indicators; + +// ULTIMATE OSCILLATOR (SERIES) + +public static partial class Ultimate +{ + public static IReadOnlyList ToUltimate( + this IReadOnlyList quotes, + int shortPeriods = 7, + int middlePeriods = 14, + int longPeriods = 28) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcUltimate(shortPeriods, middlePeriods, longPeriods); + + private static List CalcUltimate( + this IReadOnlyList source, + int shortPeriods, + int middlePeriods, + int longPeriods) + { + // check parameter arguments + Validate(shortPeriods, middlePeriods, longPeriods); + + // initialize + int length = source.Count; + List results = new(length); + double[] bp = new double[length]; // buying pressure + double[] tr = new double[length]; // true range + + double priorClose = 0; + + // roll through source values + for (int i = 0; i < length; i++) + { + QuoteD q = source[i]; + double? ultimate; + + if (i > 0) + { + bp[i] = q.Close - Math.Min(q.Low, priorClose); + tr[i] = Math.Max(q.High, priorClose) - Math.Min(q.Low, priorClose); + } + + if (i >= longPeriods) + { + double sumBp1 = 0; + double sumBp2 = 0; + double sumBp3 = 0; + + double sumTr1 = 0; + double sumTr2 = 0; + double sumTr3 = 0; + + for (int p = i + 1 - longPeriods; p <= i; p++) + { + int pIndex = p + 1; + + // short aggregate + if (pIndex > i + 1 - shortPeriods) + { + sumBp1 += bp[p]; + sumTr1 += tr[p]; + } + + // middle aggregate + if (pIndex > i + 1 - middlePeriods) + { + sumBp2 += bp[p]; + sumTr2 += tr[p]; + } + + // long aggregate + sumBp3 += bp[p]; + sumTr3 += tr[p]; + } + + double avg1 = sumTr1 == 0 ? double.NaN : sumBp1 / sumTr1; + double avg2 = sumTr2 == 0 ? double.NaN : sumBp2 / sumTr2; + double avg3 = sumTr3 == 0 ? double.NaN : sumBp3 / sumTr3; + + ultimate = (100d * (4d * avg1 + 2d * avg2 + avg3) / 7d).NaN2Null(); + } + else + { + ultimate = null; + } + + results.Add(new( + Timestamp: q.Timestamp, + Ultimate: ultimate)); + + priorClose = q.Close; + } + + return results; + } +} diff --git a/src/s-z/Ultimate/Ultimate.Utilities.cs b/src/s-z/Ultimate/Ultimate.Utilities.cs index 728d4967a..63c6da0ff 100644 --- a/src/s-z/Ultimate/Ultimate.Utilities.cs +++ b/src/s-z/Ultimate/Ultimate.Utilities.cs @@ -1,17 +1,26 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ULTIMATE OSCILLATOR (UTILITIES) + +public static partial class Ultimate { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int shortPeriods, + int middleAverage, + int longPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Ultimate != null); + // check parameter arguments + if (shortPeriods <= 0 || middleAverage <= 0 || longPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(longPeriods), longPeriods, + "Average periods must be greater than 0 for Ultimate Oscillator."); + } - return results.Remove(removePeriods); + if (shortPeriods >= middleAverage || middleAverage >= longPeriods) + { + throw new ArgumentOutOfRangeException(nameof(middleAverage), middleAverage, + "Average periods must be increasingly larger than each other for Ultimate Oscillator."); + } } } diff --git a/src/s-z/VolatilityStop/VolatilityStop.Api.cs b/src/s-z/VolatilityStop/VolatilityStop.Api.cs deleted file mode 100644 index 751f3b215..000000000 --- a/src/s-z/VolatilityStop/VolatilityStop.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// VOLATILITY SYSTEM/STOP (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetVolatilityStop( - this IEnumerable quotes, - int lookbackPeriods = 7, - double multiplier = 3) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcVolatilityStop(lookbackPeriods, multiplier); -} diff --git a/src/s-z/VolatilityStop/VolatilityStop.Models.cs b/src/s-z/VolatilityStop/VolatilityStop.Models.cs index 1382b24e4..2c729a914 100644 --- a/src/s-z/VolatilityStop/VolatilityStop.Models.cs +++ b/src/s-z/VolatilityStop/VolatilityStop.Models.cs @@ -1,19 +1,17 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class VolatilityStopResult : ResultBase, IReusableResult -{ - public VolatilityStopResult(DateTime date) - { - Date = date; - } - - public double? Sar { get; set; } - public bool? IsStop { get; set; } +public record VolatilityStopResult +( + DateTime Timestamp, + double? Sar = null, + bool? IsStop = null, // SAR values as long/short stop bands - public double? UpperBand { get; set; } - public double? LowerBand { get; set; } + double? UpperBand = null, + double? LowerBand = null - double? IReusableResult.Value => Sar; +) : IReusable +{ + public double Value => Sar.Null2NaN(); } diff --git a/src/s-z/VolatilityStop/VolatilityStop.Series.cs b/src/s-z/VolatilityStop/VolatilityStop.Series.cs deleted file mode 100644 index b49881305..000000000 --- a/src/s-z/VolatilityStop/VolatilityStop.Series.cs +++ /dev/null @@ -1,124 +0,0 @@ -namespace Skender.Stock.Indicators; - -// VOLATILITY SYSTEM/STOP (SERIES) -public static partial class Indicator -{ - internal static List CalcVolatilityStop( - this List qdList, - int lookbackPeriods, - double multiplier) - { - // convert quotes - List<(DateTime, double)> tpList = qdList - .ToTuple(CandlePart.Close); - - // check parameter arguments - ValidateVolatilityStop(lookbackPeriods, multiplier); - - // initialize - int length = tpList.Count; - List results = new(length); - - if (length == 0) - { - return results; - } - - List atrList = qdList.CalcAtr(lookbackPeriods); - - // initial trend (guess) - int initPeriods = Math.Min(length, lookbackPeriods); - double sic = tpList[0].Item2; - bool isLong = tpList[initPeriods - 1].Item2 > sic; - - for (int i = 0; i < initPeriods; i++) - { - (DateTime date, double value) = tpList[i]; - sic = isLong ? Math.Max(sic, value) : Math.Min(sic, value); - results.Add(new VolatilityStopResult(date)); - } - - // roll through quotes - for (int i = lookbackPeriods; i < length; i++) - { - (DateTime date, double value) = tpList[i]; - - // average true range × multiplier constant - double? arc = atrList[i - 1].Atr * multiplier; - - VolatilityStopResult r = new(date) { - // stop and reverse threshold - Sar = 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 && value < r.Sar) - || (!isLong && value > r.Sar)) - { - r.IsStop = true; - sic = value; - isLong = !isLong; - } - else - { - r.IsStop = false; - - // significant close adjustment - // extreme favorable close (value) while in trade - sic = isLong ? Math.Max(sic, value) : Math.Min(sic, value); - } - } - - // 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; - } - - // parameter validation - private static void ValidateVolatilityStop( - int lookbackPeriods, - double multiplier) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for Volatility Stop."); - } - - if (multiplier <= 0) - { - throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, - "ATR Multiplier must be greater than 0 for Volatility Stop."); - } - } -} diff --git a/src/s-z/VolatilityStop/VolatilityStop.StaticSeries.cs b/src/s-z/VolatilityStop/VolatilityStop.StaticSeries.cs new file mode 100644 index 000000000..6693ca1a4 --- /dev/null +++ b/src/s-z/VolatilityStop/VolatilityStop.StaticSeries.cs @@ -0,0 +1,119 @@ +namespace Skender.Stock.Indicators; + +// VOLATILITY SYSTEM/STOP (SERIES) + +public static partial class VolatilityStop +{ + public static IReadOnlyList ToVolatilityStop( + this IReadOnlyList quotes, + int lookbackPeriods = 7, + double multiplier = 3) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcVolatilityStop(lookbackPeriods, multiplier); + + private static List CalcVolatilityStop( + this IReadOnlyList source, + int lookbackPeriods, + double multiplier) + { + //convert quotes + List reList = source + .Cast() + .ToList(); + + // check parameter arguments + Validate(lookbackPeriods, multiplier); + + // initialize + int length = source.Count; + List results = new(length); + + if (length == 0) + { + return results; + } + + List atrList = source.CalcAtr(lookbackPeriods); + + // initial trend (guess) + int initPeriods = Math.Min(length, lookbackPeriods); + double sic = reList[0].Value; + bool isLong = reList[initPeriods - 1].Value > sic; + + for (int i = 0; i < initPeriods; i++) + { + IReusable init = reList[i]; + sic = isLong ? Math.Max(sic, init.Value) : Math.Min(sic, init.Value); + results.Add(new(init.Timestamp)); + } + + // roll through source values + for (int i = lookbackPeriods; i < length; i++) + { + IReusable s = reList[i]; + + // average true range × multiplier constant + double? arc = atrList[i - 1].Atr * multiplier; + + // stop and reverse threshold + double? sar = isLong ? sic - arc : sic + arc; + + // add SAR as separate bands + double? lowerBand = null; + double? upperBand = null; + + if (isLong) + { + lowerBand = sar; + } + else + { + upperBand = sar; + } + + // evaluate stop and reverse + bool? isStop; + + if ((isLong && s.Value < sar) + || (!isLong && s.Value > sar)) + { + isStop = true; + sic = s.Value; + isLong = !isLong; + } + else + { + isStop = false; + + // significant close adjustment + // extreme favorable close (value) while in trade + sic = isLong ? Math.Max(sic, s.Value) : Math.Min(sic, s.Value); + } + + results.Add(new VolatilityStopResult( + Timestamp: s.Timestamp, + Sar: sar, + IsStop: isStop, + UpperBand: upperBand, + LowerBand: lowerBand)); + } + + // remove trend to first stop, since it is a guess + int cutIndex = results.FindIndex(x => x.IsStop ?? false); + + for (int d = 0; d <= cutIndex; d++) + { + VolatilityStopResult r = results[d]; + + results[d] = r with { + Sar = null, + UpperBand = null, + LowerBand = null, + IsStop = null + }; + } + + return results; + } +} diff --git a/src/s-z/VolatilityStop/VolatilityStop.Utilities.cs b/src/s-z/VolatilityStop/VolatilityStop.Utilities.cs index cb0fb830d..ec65d0404 100644 --- a/src/s-z/VolatilityStop/VolatilityStop.Utilities.cs +++ b/src/s-z/VolatilityStop/VolatilityStop.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// VOLATILITY SYSTEM/STOP (UTILITIES) + +public static partial class VolatilityStop { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -16,4 +17,23 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods, + double multiplier) + { + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for Volatility Stop."); + } + + if (multiplier <= 0) + { + throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, + "ATR Multiplier must be greater than 0 for Volatility Stop."); + } + } } diff --git a/src/s-z/Vortex/Vortex.Api.cs b/src/s-z/Vortex/Vortex.Api.cs deleted file mode 100644 index d6e30a2b3..000000000 --- a/src/s-z/Vortex/Vortex.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// VORTEX INDICATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetVortex( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcVortex(lookbackPeriods); -} diff --git a/src/s-z/Vortex/Vortex.Models.cs b/src/s-z/Vortex/Vortex.Models.cs index 1d46cce2b..29fa3cf0a 100644 --- a/src/s-z/Vortex/Vortex.Models.cs +++ b/src/s-z/Vortex/Vortex.Models.cs @@ -1,13 +1,9 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class VortexResult : ResultBase -{ - public VortexResult(DateTime date) - { - Date = date; - } - - public double? Pvi { get; set; } - public double? Nvi { get; set; } -} +public record VortexResult +( + DateTime Timestamp, + double? Pvi = null, + double? Nvi = null +) : ISeries; diff --git a/src/s-z/Vortex/Vortex.Series.cs b/src/s-z/Vortex/Vortex.StaticSeries.cs similarity index 66% rename from src/s-z/Vortex/Vortex.Series.cs rename to src/s-z/Vortex/Vortex.StaticSeries.cs index c2eab9e30..2d6ce4549 100644 --- a/src/s-z/Vortex/Vortex.Series.cs +++ b/src/s-z/Vortex/Vortex.StaticSeries.cs @@ -1,17 +1,25 @@ namespace Skender.Stock.Indicators; // VORTEX INDICATOR (SERIES) -public static partial class Indicator + +public static partial class Vortex { - internal static List CalcVortex( - this List qdList, + public static IReadOnlyList ToVortex( + this IReadOnlyList quotes, + int lookbackPeriods) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcVortex(lookbackPeriods); + + private static List CalcVortex( + this IReadOnlyList source, int lookbackPeriods) { // check parameter arguments - ValidateVortex(lookbackPeriods); + Validate(lookbackPeriods); // initialize - int length = qdList.Count; + int length = source.Count; List results = new(length); double[] tr = new double[length]; @@ -22,13 +30,10 @@ internal static List CalcVortex( double prevLow = 0; double prevClose = 0; - // roll through quotes + // roll through source values for (int i = 0; i < length; i++) { - QuoteD q = qdList[i]; - - VortexResult r = new(q.Date); - results.Add(r); + QuoteD q = source[i]; // skip first period if (i == 0) @@ -36,6 +41,8 @@ internal static List CalcVortex( prevHigh = q.High; prevLow = q.Low; prevClose = q.Close; + + results.Add(new(q.Timestamp)); continue; } @@ -51,6 +58,9 @@ internal static List CalcVortex( prevLow = q.Low; prevClose = q.Close; + double pvi = double.NaN; + double nvi = double.NaN; + // vortex indicator if (i + 1 > lookbackPeriods) { @@ -67,24 +77,17 @@ internal static List CalcVortex( if (sumTr is not 0) { - r.Pvi = sumPvm / sumTr; - r.Nvi = sumNvm / sumTr; + pvi = sumPvm / sumTr; + nvi = sumNvm / sumTr; } } + + results.Add(new VortexResult( + Timestamp: q.Timestamp, + Pvi: pvi.NaN2Null(), + Nvi: nvi.NaN2Null())); } return results; } - - // parameter validation - private static void ValidateVortex( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for VI."); - } - } } diff --git a/src/s-z/Vortex/Vortex.Utilities.cs b/src/s-z/Vortex/Vortex.Utilities.cs index 91c9feac6..22e449b50 100644 --- a/src/s-z/Vortex/Vortex.Utilities.cs +++ b/src/s-z/Vortex/Vortex.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// VORTEX INDICATOR (UTILITIES) + +public static partial class Vortex { // CONDENSE (REMOVE null results) - /// - /// - public static IEnumerable Condense( - this IEnumerable results) + /// + public static IReadOnlyList Condense( + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -19,10 +20,9 @@ public static IEnumerable Condense( } // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -30,4 +30,16 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for VI."); + } + } } diff --git a/src/s-z/Vwap/Vwap.Api.cs b/src/s-z/Vwap/Vwap.Api.cs deleted file mode 100644 index e45502dd6..000000000 --- a/src/s-z/Vwap/Vwap.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// VOLUME WEIGHTED AVERAGE PRICE (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetVwap( - this IEnumerable quotes, - DateTime? startDate = null) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcVwap(startDate); -} diff --git a/src/s-z/Vwap/Vwap.Models.cs b/src/s-z/Vwap/Vwap.Models.cs index b6df0b221..5b4400319 100644 --- a/src/s-z/Vwap/Vwap.Models.cs +++ b/src/s-z/Vwap/Vwap.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class VwapResult : ResultBase, IReusableResult +public record VwapResult +( + DateTime Timestamp, + double? Vwap +) : IReusable { - public VwapResult(DateTime date) - { - Date = date; - } - - public double? Vwap { get; set; } - - double? IReusableResult.Value => Vwap; + public double Value => Vwap.Null2NaN(); } diff --git a/src/s-z/Vwap/Vwap.Series.cs b/src/s-z/Vwap/Vwap.Series.cs deleted file mode 100644 index c54de6b86..000000000 --- a/src/s-z/Vwap/Vwap.Series.cs +++ /dev/null @@ -1,69 +0,0 @@ -namespace Skender.Stock.Indicators; - -// VOLUME WEIGHTED AVERAGE PRICE (SERIES) -public static partial class Indicator -{ - internal static List CalcVwap( - this List qdList, - DateTime? startDate = null) - { - // check parameter arguments - ValidateVwap(qdList, startDate); - - // initialize - int length = qdList.Count; - List results = new(length); - - if (length == 0) - { - return results; - } - - startDate ??= qdList[0].Date; - - double? cumVolume = 0; - double? cumVolumeTP = 0; - - // roll through quotes - for (int i = 0; i < length; i++) - { - QuoteD q = qdList[i]; - double? v = q.Volume; - double? h = q.High; - double? l = q.Low; - double? c = q.Close; - - VwapResult r = new(q.Date); - results.Add(r); - - if (q.Date >= startDate) - { - cumVolume += v; - cumVolumeTP += v * (h + l + c) / 3; - - r.Vwap = (cumVolume != 0) ? (cumVolumeTP / cumVolume) : null; - } - } - - return results; - } - - // parameter validation - private static void ValidateVwap( - List quotesList, - DateTime? startDate) - { - // nothing to do for 0 length - if (quotesList.Count == 0) - { - return; - } - - // check parameter arguments (intentionally after quotes check) - if (startDate < quotesList[0].Date) - { - throw new ArgumentOutOfRangeException(nameof(startDate), startDate, - "Start Date must be within the quotes range for VWAP."); - } - } -} diff --git a/src/s-z/Vwap/Vwap.StaticSeries.cs b/src/s-z/Vwap/Vwap.StaticSeries.cs new file mode 100644 index 000000000..0f31b1505 --- /dev/null +++ b/src/s-z/Vwap/Vwap.StaticSeries.cs @@ -0,0 +1,66 @@ +namespace Skender.Stock.Indicators; + +// VOLUME WEIGHTED AVERAGE PRICE (SERIES) + +public static partial class Vwap +{ + public static IReadOnlyList ToVwap( + this IReadOnlyList quotes, + DateTime? startDate = null) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcVwap(startDate); + + private static List CalcVwap( + this IReadOnlyList source, + DateTime? startDate = null) + { + // check parameter arguments + Validate(source, startDate); + + // initialize + int length = source.Count; + List results = new(length); + + if (length == 0) + { + return results; + } + + startDate ??= source[0].Timestamp; + + double? cumVolume = 0; + double? cumVolumeTp = 0; + + // roll through source values + for (int i = 0; i < length; i++) + { + QuoteD q = source[i]; + + double? v = q.Volume; + double? h = q.High; + double? l = q.Low; + double? c = q.Close; + + double? vwap; + + if (q.Timestamp >= startDate) + { + cumVolume += v; + cumVolumeTp += v * (h + l + c) / 3; + + vwap = cumVolume != 0 ? cumVolumeTp / cumVolume : null; + } + else + { + vwap = null; + } + + results.Add(new( + Timestamp: q.Timestamp, + Vwap: vwap)); + } + + return results; + } +} diff --git a/src/s-z/Vwap/Vwap.Utilities.cs b/src/s-z/Vwap/Vwap.Utilities.cs index 66ec8a0bc..bf24c9fea 100644 --- a/src/s-z/Vwap/Vwap.Utilities.cs +++ b/src/s-z/Vwap/Vwap.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// VOLUME WEIGHTED AVERAGE PRICE (UTILITIES) + +public static partial class Vwap { // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + /// + public static IReadOnlyList RemoveWarmupPeriods( + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -14,4 +15,23 @@ public static IEnumerable RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + IReadOnlyList quotes, + DateTime? startDate) + { + // nothing to do for 0 length + if (quotes.Count == 0) + { + return; + } + + // check parameter arguments (intentionally after quotes check) + if (startDate < quotes[0].Timestamp) + { + throw new ArgumentOutOfRangeException(nameof(startDate), startDate, + "Start Timestamp must be within the quotes range for VWAP."); + } + } } diff --git a/src/s-z/Vwma/Vwma.Api.cs b/src/s-z/Vwma/Vwma.Api.cs deleted file mode 100644 index 603d2bbf6..000000000 --- a/src/s-z/Vwma/Vwma.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// VOLUME WEIGHTED MOVING AVERAGE (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetVwma( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcVwma(lookbackPeriods); -} diff --git a/src/s-z/Vwma/Vwma.Models.cs b/src/s-z/Vwma/Vwma.Models.cs index 45774b6c4..647136fbe 100644 --- a/src/s-z/Vwma/Vwma.Models.cs +++ b/src/s-z/Vwma/Vwma.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class VwmaResult : ResultBase, IReusableResult +public record VwmaResult +( + DateTime Timestamp, + double? Vwma +) : IReusable { - public VwmaResult(DateTime date) - { - Date = date; - } - - public double? Vwma { get; set; } - - double? IReusableResult.Value => Vwma; + public double Value => Vwma.Null2NaN(); } diff --git a/src/s-z/Vwma/Vwma.Series.cs b/src/s-z/Vwma/Vwma.Series.cs deleted file mode 100644 index 632c13098..000000000 --- a/src/s-z/Vwma/Vwma.Series.cs +++ /dev/null @@ -1,57 +0,0 @@ -namespace Skender.Stock.Indicators; - -// VOLUME WEIGHTED MOVING AVERAGE (SERIES) -public static partial class Indicator -{ - internal static List CalcVwma( - this List qdList, - int lookbackPeriods) - { - // check parameter arguments - ValidateVwma(lookbackPeriods); - - // initialize - int length = qdList.Count; - List results = new(length); - - // roll through quotes - for (int i = 0; i < length; i++) - { - QuoteD q = qdList[i]; - - VwmaResult r = new(q.Date); - results.Add(r); - - if (i + 1 >= lookbackPeriods) - { - double? sumCl = 0; - double? sumVl = 0; - for (int p = i + 1 - lookbackPeriods; p <= i; p++) - { - QuoteD d = qdList[p]; - double? c = d.Close; - double? v = d.Volume; - - sumCl += c * v; - sumVl += v; - } - - r.Vwma = sumVl != 0 ? (sumCl / sumVl) : null; - } - } - - return results; - } - - // parameter validation - private static void ValidateVwma( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Vwma."); - } - } -} diff --git a/src/s-z/Vwma/Vwma.StaticSeries.cs b/src/s-z/Vwma/Vwma.StaticSeries.cs new file mode 100644 index 000000000..20c4c689e --- /dev/null +++ b/src/s-z/Vwma/Vwma.StaticSeries.cs @@ -0,0 +1,60 @@ +namespace Skender.Stock.Indicators; + +// VOLUME WEIGHTED MOVING AVERAGE (SERIES) + +public static partial class Vwma +{ + public static IReadOnlyList ToVwma( + this IReadOnlyList quotes, + int lookbackPeriods) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcVwma(lookbackPeriods); + + private static List CalcVwma( + this IReadOnlyList source, + int lookbackPeriods) + { + // check parameter arguments + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + // roll through source values + for (int i = 0; i < length; i++) + { + QuoteD q = source[i]; + + double vwma; + + if (i + 1 >= lookbackPeriods) + { + double sumCl = 0; + double sumVl = 0; + for (int p = i + 1 - lookbackPeriods; p <= i; p++) + { + QuoteD d = source[p]; + double c = d.Close; + double v = d.Volume; + + sumCl += c * v; + sumVl += v; + } + + vwma = sumVl != 0 ? sumCl / sumVl : double.NaN; + } + else + { + vwma = double.NaN; + } + + results.Add(new( + Timestamp: q.Timestamp, + Vwma: vwma.NaN2Null())); + } + + return results; + } +} diff --git a/src/s-z/Vwma/Vwma.Utilities.cs b/src/s-z/Vwma/Vwma.Utilities.cs index f29154581..dd43909b3 100644 --- a/src/s-z/Vwma/Vwma.Utilities.cs +++ b/src/s-z/Vwma/Vwma.Utilities.cs @@ -1,17 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// VOLUME WEIGHTED MOVING AVERAGE (UTILITIES) + +public static partial class Vwma { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Vwma != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Vwma."); + } } } diff --git a/src/s-z/WilliamsR/WilliamsR.Api.cs b/src/s-z/WilliamsR/WilliamsR.Api.cs deleted file mode 100644 index c5b196bd8..000000000 --- a/src/s-z/WilliamsR/WilliamsR.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// WILLIAM %R OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetWilliamsR( - this IEnumerable quotes, - int lookbackPeriods = 14) - where TQuote : IQuote => quotes - .ToQuoteD() - .CalcWilliamsR(lookbackPeriods); -} diff --git a/src/s-z/WilliamsR/WilliamsR.Models.cs b/src/s-z/WilliamsR/WilliamsR.Models.cs index a00b47754..0fb8b05aa 100644 --- a/src/s-z/WilliamsR/WilliamsR.Models.cs +++ b/src/s-z/WilliamsR/WilliamsR.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class WilliamsResult : ResultBase, IReusableResult +public record WilliamsResult +( + DateTime Timestamp, + double? WilliamsR +) : IReusable { - public WilliamsResult(DateTime date) - { - Date = date; - } - - public double? WilliamsR { get; set; } - - double? IReusableResult.Value => WilliamsR; + public double Value => WilliamsR.Null2NaN(); } diff --git a/src/s-z/WilliamsR/WilliamsR.Series.cs b/src/s-z/WilliamsR/WilliamsR.Series.cs deleted file mode 100644 index 025bc2e90..000000000 --- a/src/s-z/WilliamsR/WilliamsR.Series.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Skender.Stock.Indicators; - -// WILLIAM %R OSCILLATOR (SERIES -public static partial class Indicator -{ - internal static List CalcWilliamsR( - this List qdList, - int lookbackPeriods) - { - // check parameter arguments - ValidateWilliam(lookbackPeriods); - - // convert Fast Stochastic to William %R - return qdList.CalcStoch(lookbackPeriods, 1, 1, 3, 2, MaType.SMA) - .Select(s => new WilliamsResult(s.Date) { - WilliamsR = s.Oscillator - 100 - }) - .ToList(); - } - - // parameter validation - private static void ValidateWilliam( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for William %R."); - } - } -} diff --git a/src/s-z/WilliamsR/WilliamsR.StaticSeries.cs b/src/s-z/WilliamsR/WilliamsR.StaticSeries.cs new file mode 100644 index 000000000..23becb293 --- /dev/null +++ b/src/s-z/WilliamsR/WilliamsR.StaticSeries.cs @@ -0,0 +1,29 @@ +namespace Skender.Stock.Indicators; + +// WILLIAM %R OSCILLATOR (SERIES) + +public static partial class WilliamsR +{ + public static IReadOnlyList ToWilliamsR( + this IReadOnlyList quotes, + int lookbackPeriods = 14) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcWilliamsR(lookbackPeriods); + + private static List CalcWilliamsR( + this IReadOnlyList source, + int lookbackPeriods) + { + // check parameter arguments + Validate(lookbackPeriods); + + // convert Fast Stochastic to William %R + return source.CalcStoch(lookbackPeriods, 1, 1, 3, 2, MaType.SMA) + .Select(s => new WilliamsResult( + Timestamp: s.Timestamp, + WilliamsR: s.Oscillator - 100 + )) + .ToList(); + } +} diff --git a/src/s-z/WilliamsR/WilliamsR.Utilities.cs b/src/s-z/WilliamsR/WilliamsR.Utilities.cs index 7f5563189..49f8d8af1 100644 --- a/src/s-z/WilliamsR/WilliamsR.Utilities.cs +++ b/src/s-z/WilliamsR/WilliamsR.Utilities.cs @@ -1,17 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// WILLIAM %R OSCILLATOR (UTILITIES) + +public static partial class WilliamsR { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.WilliamsR != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for William %R."); + } } } diff --git a/src/s-z/Wma/Wma.Api.cs b/src/s-z/Wma/Wma.Api.cs deleted file mode 100644 index a29f543a7..000000000 --- a/src/s-z/Wma/Wma.Api.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Skender.Stock.Indicators; - -// WEIGHTED MOVING AVERAGE (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetWma( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToTuple(CandlePart.Close) - .CalcWma(lookbackPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetWma( - this IEnumerable results, - int lookbackPeriods) => results - .ToTuple() - .CalcWma(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetWma( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods) => priceTuples - .ToSortedList() - .CalcWma(lookbackPeriods); -} diff --git a/src/s-z/Wma/Wma.Models.cs b/src/s-z/Wma/Wma.Models.cs index 551798d04..41b2e6638 100644 --- a/src/s-z/Wma/Wma.Models.cs +++ b/src/s-z/Wma/Wma.Models.cs @@ -1,14 +1,11 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class WmaResult : ResultBase, IReusableResult +public record WmaResult +( + DateTime Timestamp, + double? Wma +) : IReusable { - public WmaResult(DateTime date) - { - Date = date; - } - - public double? Wma { get; set; } - - double? IReusableResult.Value => Wma; + public double Value => Wma.Null2NaN(); } diff --git a/src/s-z/Wma/Wma.Series.cs b/src/s-z/Wma/Wma.Series.cs deleted file mode 100644 index 15c46f0b3..000000000 --- a/src/s-z/Wma/Wma.Series.cs +++ /dev/null @@ -1,52 +0,0 @@ -namespace Skender.Stock.Indicators; - -// WEIGHTED MOVING AVERAGE (SERIES) -public static partial class Indicator -{ - internal static List CalcWma( - this List<(DateTime, double)> tpList, - int lookbackPeriods) - { - // check parameter arguments - ValidateWma(lookbackPeriods); - - // initialize - List results = new(tpList.Count); - double divisor = (double)lookbackPeriods * (lookbackPeriods + 1) / 2d; - - // roll through quotes - for (int i = 0; i < tpList.Count; i++) - { - (DateTime date, double _) = tpList[i]; - - WmaResult r = new(date); - results.Add(r); - - if (i + 1 >= lookbackPeriods) - { - double wma = 0; - for (int p = i + 1 - lookbackPeriods; p <= i; p++) - { - (DateTime _, double pValue) = tpList[p]; - wma += pValue * (lookbackPeriods - (i + 1 - p - 1)) / divisor; - } - - r.Wma = wma.NaN2Null(); - } - } - - return results; - } - - // parameter validation - private static void ValidateWma( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for WMA."); - } - } -} diff --git a/src/s-z/Wma/Wma.StaticSeries.cs b/src/s-z/Wma/Wma.StaticSeries.cs new file mode 100644 index 000000000..95fcc8aac --- /dev/null +++ b/src/s-z/Wma/Wma.StaticSeries.cs @@ -0,0 +1,50 @@ +namespace Skender.Stock.Indicators; + +// WEIGHTED MOVING AVERAGE (SERIES) + +public static partial class Wma +{ + public static IReadOnlyList ToWma( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + { + // check parameter arguments + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); + + // initialize + int length = source.Count; + List results = new(length); + + double divisor = (double)lookbackPeriods * (lookbackPeriods + 1) / 2d; + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + double wma; + + if (i >= lookbackPeriods - 1) + { + wma = 0; + for (int p = i + 1 - lookbackPeriods; p <= i; p++) + { + T ps = source[p]; + wma += ps.Value * (lookbackPeriods - (i + 1 - p - 1)) / divisor; + } + } + else + { + wma = double.NaN; + } + + results.Add(new( + Timestamp: s.Timestamp, + Wma: wma.NaN2Null())); + } + + return results; + } +} diff --git a/src/s-z/Wma/Wma.Utilities.cs b/src/s-z/Wma/Wma.Utilities.cs index f6e9034e3..cd7b65c13 100644 --- a/src/s-z/Wma/Wma.Utilities.cs +++ b/src/s-z/Wma/Wma.Utilities.cs @@ -1,17 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// WEIGHTED MOVING AVERAGE (UTILITIES) + +public static partial class Wma { - // remove recommended periods - /// - /// - public static IEnumerable RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Wma != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for WMA."); + } } } diff --git a/src/s-z/ZigZag/ZigZag.Api.cs b/src/s-z/ZigZag/ZigZag.Api.cs deleted file mode 100644 index f5afcaf5c..000000000 --- a/src/s-z/ZigZag/ZigZag.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ZIG ZAG (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IEnumerable GetZigZag( - this IEnumerable quotes, - EndType endType = EndType.Close, - decimal percentChange = 5) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcZigZag(endType, percentChange); -} diff --git a/src/s-z/ZigZag/ZigZag.Models.cs b/src/s-z/ZigZag/ZigZag.Models.cs index 339b67d79..6e0a410a5 100644 --- a/src/s-z/ZigZag/ZigZag.Models.cs +++ b/src/s-z/ZigZag/ZigZag.Models.cs @@ -1,24 +1,21 @@ namespace Skender.Stock.Indicators; [Serializable] -public sealed class ZigZagResult : ResultBase, IReusableResult +public record ZigZagResult +( + DateTime Timestamp, + decimal? ZigZag = null, // zig zag line + string? PointType = null, // indicates a specific point and type e.g. H or L + decimal? RetraceHigh = null, // zig zag retrace high line + decimal? RetraceLow = null // zig zag retrace low line +) : IReusable { - public ZigZagResult(DateTime date) - { - Date = date; - } - - public decimal? ZigZag { get; set; } // zig zag line - public string? PointType { get; set; } // indicates a specific point and type e.g. H or L - public decimal? RetraceHigh { get; set; } // zig zag retrace high line - public decimal? RetraceLow { get; set; } // zig zag retrace low line - - double? IReusableResult.Value => (double?)ZigZag; + public double Value => ZigZag.Null2NaN(); } internal class ZigZagEval { - internal int Index { get; set; } + internal int Index { get; init; } internal decimal? High { get; set; } internal decimal? Low { get; set; } } diff --git a/src/s-z/ZigZag/ZigZag.Series.cs b/src/s-z/ZigZag/ZigZag.StaticSeries.cs similarity index 62% rename from src/s-z/ZigZag/ZigZag.Series.cs rename to src/s-z/ZigZag/ZigZag.StaticSeries.cs index 29d42485e..8d249e916 100644 --- a/src/s-z/ZigZag/ZigZag.Series.cs +++ b/src/s-z/ZigZag/ZigZag.StaticSeries.cs @@ -1,30 +1,29 @@ namespace Skender.Stock.Indicators; // ZIG ZAG (SERIES) -public static partial class Indicator + +public static partial class ZigZag { - internal static List CalcZigZag( - this List quotesList, + public static IReadOnlyList ToZigZag( + this IReadOnlyList quotes, EndType endType = EndType.Close, decimal percentChange = 5) where TQuote : IQuote { // check parameter arguments - ValidateZigZag(percentChange); + ArgumentNullException.ThrowIfNull(quotes); + Validate(percentChange); // initialize - int length = quotesList.Count; + int length = quotes.Count; List results = new(length); - TQuote q0; if (length == 0) { return results; } - else - { - q0 = quotesList[0]; - } + + TQuote q0 = quotes[0]; ZigZagEval eval = GetZigZagEval(endType, 1, q0); decimal changeThreshold = percentChange / 100m; @@ -47,21 +46,23 @@ internal static List CalcZigZag( PointType = "L" }; - int finalPointIndex = length; - - // roll through quotes, to find initial trend + // roll through source values, to find initial trend for (int i = 0; i < length; i++) { - TQuote q = quotesList[i]; + TQuote q = quotes[i]; int index = i + 1; eval = GetZigZagEval(endType, index, q); - decimal? changeUp = (lastLowPoint.Value == 0) ? null - : (eval.High - lastLowPoint.Value) / lastLowPoint.Value; + decimal? changeUp = lastLowPoint.Value == 0 + ? null + : (eval.High - lastLowPoint.Value) + / lastLowPoint.Value; - decimal? changeDn = (lastHighPoint.Value == 0) ? null - : (lastHighPoint.Value - eval.Low) / lastHighPoint.Value; + decimal? changeDn = lastHighPoint.Value == 0 + ? null + : (lastHighPoint.Value - eval.Low) + / lastHighPoint.Value; if (changeUp >= changeThreshold && changeUp > changeDn) { @@ -81,20 +82,23 @@ internal static List CalcZigZag( } // add first point to results - ZigZagResult firstResult = new(q0.Date); + ZigZagResult firstResult = new(q0.Timestamp); results.Add(firstResult); // find and draw lines - while (lastPoint.Index < finalPointIndex) + while (lastPoint.Index < length) { - ZigZagPoint nextPoint = EvaluateNextPoint(quotesList, endType, changeThreshold, lastPoint); + ZigZagPoint nextPoint = EvaluateNextPoint( + quotes, endType, changeThreshold, lastPoint); + string lastDirection = lastPoint.PointType; // draw line (and reset last point) - DrawZigZagLine(results, quotesList, lastPoint, nextPoint); + DrawZigZagLine(results, quotes, lastPoint, nextPoint); // draw retrace line (and reset last high/low point) - DrawRetraceLine(results, lastDirection, lastLowPoint, lastHighPoint, nextPoint); + DrawRetraceLine(results, lastDirection, lastLowPoint, + lastHighPoint, nextPoint); } return results; @@ -102,7 +106,7 @@ internal static List CalcZigZag( // internals private static ZigZagPoint EvaluateNextPoint( - List quotesList, + IReadOnlyList quotesList, EndType endType, decimal changeThreshold, ZigZagPoint lastPoint) @@ -138,8 +142,10 @@ private static ZigZagPoint EvaluateNextPoint( } else { - change = (extremePoint.Value == 0) ? null - : (extremePoint.Value - eval.Low) / extremePoint.Value; + change = extremePoint.Value == 0 + ? null + : (extremePoint.Value - eval.Low) + / extremePoint.Value; } } else @@ -152,8 +158,10 @@ private static ZigZagPoint EvaluateNextPoint( } else { - change = (extremePoint.Value == 0) ? null - : (eval.High - extremePoint.Value) / extremePoint.Value; + change = extremePoint.Value == 0 + ? null + : (eval.High - extremePoint.Value) + / extremePoint.Value; } } @@ -175,25 +183,31 @@ private static ZigZagPoint EvaluateNextPoint( return extremePoint; } - private static void DrawZigZagLine(List results, List quotesList, + private static void DrawZigZagLine( + List results, IReadOnlyList quotes, ZigZagPoint lastPoint, ZigZagPoint nextPoint) where TQuote : IQuote { if (nextPoint.Index != lastPoint.Index) { - decimal? increment = (nextPoint.Value - lastPoint.Value) / (nextPoint.Index - lastPoint.Index); + decimal? increment + = (nextPoint.Value - lastPoint.Value) + / (nextPoint.Index - lastPoint.Index); // add new line segment for (int i = lastPoint.Index; i < nextPoint.Index; i++) { - TQuote q = quotesList[i]; + TQuote q = quotes[i]; int index = i + 1; - ZigZagResult result = new(q.Date) { - ZigZag = (lastPoint.Index != 1 || index == nextPoint.Index) ? - lastPoint.Value + (increment * (index - lastPoint.Index)) : null, - PointType = (index == nextPoint.Index) ? nextPoint.PointType : null - }; + ZigZagResult result = new( + Timestamp: q.Timestamp, + ZigZag: lastPoint.Index != 1 || index == nextPoint.Index + ? lastPoint.Value + (increment * (index - lastPoint.Index)) + : null, + PointType: index == nextPoint.Index + ? nextPoint.PointType + : null); results.Add(result); } @@ -214,24 +228,27 @@ private static void DrawRetraceLine( { ZigZagPoint priorPoint = new(); - // handle type and reset last point - if (lastDirection == "L") + switch (lastDirection) { - priorPoint.Index = lastHighPoint.Index; - priorPoint.Value = lastHighPoint.Value; + // handle type and reset last point + case "L": + priorPoint.Index = lastHighPoint.Index; + priorPoint.Value = lastHighPoint.Value; - lastHighPoint.Index = nextPoint.Index; - lastHighPoint.Value = nextPoint.Value; - } + lastHighPoint.Index = nextPoint.Index; + lastHighPoint.Value = nextPoint.Value; + break; - // low line - else if (lastDirection == "H") - { - priorPoint.Index = lastLowPoint.Index; - priorPoint.Value = lastLowPoint.Value; + // low line + case "H": + priorPoint.Index = lastLowPoint.Index; + priorPoint.Value = lastLowPoint.Value; - lastLowPoint.Index = nextPoint.Index; - lastLowPoint.Value = nextPoint.Value; + lastLowPoint.Index = nextPoint.Index; + lastLowPoint.Value = nextPoint.Value; + break; + + default: break; // do nothing } // nothing to draw cases @@ -244,7 +261,9 @@ private static void DrawRetraceLine( } // narrow to period - decimal? increment = (nextPoint.Value - priorPoint.Value) / (nextPoint.Index - priorPoint.Index); + decimal? increment + = (nextPoint.Value - priorPoint.Value) + / (nextPoint.Index - priorPoint.Index); // add new line segment for (int i = priorPoint.Index - 1; i < nextPoint.Index; i++) @@ -252,16 +271,29 @@ private static void DrawRetraceLine( ZigZagResult r = results[i]; int index = i + 1; - // high line - if (lastDirection == "L") + switch (lastDirection) { - r.RetraceHigh = priorPoint.Value + (increment * (index - priorPoint.Index)); - } + // high line + case "L": - // low line - else if (lastDirection == "H") - { - r.RetraceLow = priorPoint.Value + (increment * (index - priorPoint.Index)); + decimal? retraceHigh + = priorPoint.Value + + (increment * (index - priorPoint.Index)); + + results[i] = r with { RetraceHigh = retraceHigh }; + break; + + // low line + case "H": + + decimal? retraceLow + = priorPoint.Value + + (increment * (index - priorPoint.Index)); + + results[i] = r with { RetraceLow = retraceLow }; + break; + + default: break; // do nothing } } } @@ -297,16 +329,4 @@ private static ZigZagEval GetZigZagEval( return eval; } - - // parameter validation - private static void ValidateZigZag( - decimal percentChange) - { - // check parameter arguments - if (percentChange <= 0) - { - throw new ArgumentOutOfRangeException(nameof(percentChange), percentChange, - "Percent change must be greater than 0 for ZIGZAG."); - } - } } diff --git a/src/s-z/ZigZag/ZigZag.Utilities.cs b/src/s-z/ZigZag/ZigZag.Utilities.cs index 6971abed2..b3af14695 100644 --- a/src/s-z/ZigZag/ZigZag.Utilities.cs +++ b/src/s-z/ZigZag/ZigZag.Utilities.cs @@ -1,12 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ZIG ZAG (UTILITIES) + +public static partial class ZigZag { // CONDENSE (REMOVE null results) - /// - /// - public static IEnumerable Condense( - this IEnumerable results) + /// + public static IReadOnlyList Condense( + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -17,4 +18,16 @@ public static IEnumerable Condense( return resultsList.ToSortedList(); } + + // parameter validation + internal static void Validate( + decimal percentChange) + { + // check parameter arguments + if (percentChange <= 0) + { + throw new ArgumentOutOfRangeException(nameof(percentChange), percentChange, + "Percent change must be greater than 0 for ZIGZAG."); + } + } } diff --git a/tests/application/GlobalUsings.cs b/tests/application/GlobalUsings.cs new file mode 100644 index 000000000..f72ae9eb8 --- /dev/null +++ b/tests/application/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Skender.Stock.Indicators; +global using Test.Data; diff --git a/tests/application/Program.cs b/tests/application/Program.cs new file mode 100644 index 000000000..620267b46 --- /dev/null +++ b/tests/application/Program.cs @@ -0,0 +1,216 @@ +namespace Test.Application; + +internal class Program +{ + private static void Main(string[] args) + { + if (args.Length != 0) + { + Console.WriteLine(args); + } + + string scenario = "C"; + + switch (scenario) + { + case "A": Do.QuoteHub(); break; + case "B": Do.EmaHub(); break; + case "C": Do.MultipleSubscribers(); break; + } + } +} + +public class Do +{ + private static readonly bool verbose = true; // turn this off when profiling + + private static readonly QuoteHub provider = new(); + + private static readonly IReadOnlyList quotesList = Data.Data.GetDefault(); + + private static readonly int quotesLength = quotesList.Count; + + internal Do() + { + if (!verbose) + { + Prefill(); + } + } + + private static void Prefill() + { + // prefill quotes to provider + for (int i = 0; i < quotesLength; i++) + { + provider.Add(quotesList[i]); + } + } + + internal static void QuoteHub() + { + EmaHub emaHub = provider.ToEma(14); + + if (!verbose) + { + return; + } + + // initialize console display + Console.WriteLine(""" + Date Close price + -------------------- + """); + + // add quotes to provider + for (int i = 0; i < quotesLength; i++) + { + Quote q = quotesList[i]; + provider.Add(q); + + // wait for next quote + Timewarp(); + + // send to console + SendToConsole(q); + } + } + + private static void SendToConsole(Quote q) + { + string m = $"{q.Timestamp:yyyy-MM-dd} ${q.Close:N2}"; + Console.WriteLine(m); + } + + + internal static void EmaHub() + { + EmaHub emaHub = provider.ToEma(14); + + if (!verbose) + { + return; + } + + // initialize console display + Console.WriteLine(""" + Date Close price EMA(14) + ------------------------------ + """); + + // add quotes to provider + for (int i = 0; i < quotesLength; i++) + { + Quote q = quotesList[i]; + provider.Add(q); + + // wait for next quote + Timewarp(); + + // send to console + SendToConsole(q, emaHub); + } + } + + private static void SendToConsole(Quote q, EmaHub emaHub) + where T : IReusable + { + string m = $"{q.Timestamp:yyyy-MM-dd} ${q.Close:N2}"; + + EmaResult e = emaHub.Results[^1]; + + if (e.Ema is not null) + { + m += $"{e.Ema,10:N3}"; + } + else + { + m += $"{"[null]",10}"; + } + + Console.WriteLine(m); + } + + internal static void MultipleSubscribers() + { + SmaHub smaHub = provider.ToSma(3); + EmaHub emaHub = provider.ToEma(5); + EmaHub useChain = provider.ToQuotePart(CandlePart.HL2).ToEma(7); + EmaHub emaChain = provider.ToSma(4).ToEma(4); + + if (!verbose) + { + return; + } + + // initialize console display + Console.WriteLine(""" + Date Close price SMA(3) EMA(5) EMA(7,HL2) SMA/EMA(8) + ------------------------------------------------------------- + """); + + // add quotes to provider + for (int i = 0; i < quotesLength; i++) + { + Quote q = quotesList[i]; + provider.Add(q); + + // wait for next quote + Timewarp(); + + // send to console + SendToConsole(q, smaHub, emaHub, useChain, emaChain); + } + } + + private static void SendToConsole( + Quote q, + SmaHub smaHub, + EmaHub emaHub, + EmaHub useChain, + EmaHub emaChain) + { + string m = $"{q.Timestamp:yyyy-MM-dd} ${q.Close:N2}"; + + SmaResult s = smaHub.Results[^1]; + EmaResult e = emaHub.Results[^1]; + EmaResult u = useChain.Results[^1]; + EmaResult c = emaChain.Results[^1]; + + if (s.Sma is not null) + { + m += $"{s.Sma,8:N1}"; + } + + if (e.Ema is not null) + { + m += $"{e.Ema,8:N1}"; + } + + if (u.Ema is not null) + { + m += $"{u.Ema,12:N1}"; + } + + if (c.Ema is not null) + { + m += $"{c.Ema,12:N1}"; + } + + Console.WriteLine(m); + } + + /// + /// Emulate quote arrival rate. + /// Use '0' to disable. + /// + private static void Timewarp(int quotesPerMinute = 0) + { + if (quotesPerMinute == 0) + { + return; + } + + Thread.Sleep(60000 / quotesPerMinute); + } +} diff --git a/tests/application/README.md b/tests/application/README.md new file mode 100644 index 000000000..9f30b7217 --- /dev/null +++ b/tests/application/README.md @@ -0,0 +1,3 @@ +# Test application + +This is only used for performance profiling and testing. diff --git a/tests/application/Test.Application.csproj b/tests/application/Test.Application.csproj new file mode 100644 index 000000000..a727a0290 --- /dev/null +++ b/tests/application/Test.Application.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + enable + + + + + + + + + Always + + + + diff --git a/tests/application/_testdata/TestData.Getter.cs b/tests/application/_testdata/TestData.Getter.cs new file mode 100644 index 000000000..dcc0a79bc --- /dev/null +++ b/tests/application/_testdata/TestData.Getter.cs @@ -0,0 +1,52 @@ +namespace Test.Data; + +// TEST QUOTE GETTERs + +internal static class Data +{ + // sorted by filename + + // DEFAULT: S&P 500 ~2 years of daily data + internal static IReadOnlyList GetDefault(int days = 502) + => File.ReadAllLines("_testdata/data/default.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderBy(x => x.Timestamp) + .Take(days) + .ToList(); + + // COMPARE DATA ~2 years of TSLA data (matches default time) + internal static IReadOnlyList GetCompare(int days = 502) + => File.ReadAllLines("_testdata/data/compare.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderBy(x => x.Timestamp) + .Take(days) + .ToList(); + + // INTRADAY DATA + internal static IReadOnlyList GetIntraday(int days = 1564) + => File.ReadAllLines("_testdata/data/intraday.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderBy(x => x.Timestamp) + .Take(days) + .ToList(); + + // LONGEST DATA ~62 years of S&P 500 daily data + internal static IReadOnlyList GetLongest() + => File.ReadAllLines("_testdata/data/longest.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderBy(x => x.Timestamp) + .ToList(); + + // LONGISH DATA ~20 years of S&P 500 daily data + internal static IReadOnlyList GetLongish(int days = 5285) + => File.ReadAllLines("_testdata/data/longish.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderBy(x => x.Timestamp) + .Take(days) + .ToList(); +} diff --git a/tests/application/_testdata/TestData.Imports.cs b/tests/application/_testdata/TestData.Imports.cs new file mode 100644 index 000000000..222ccd511 --- /dev/null +++ b/tests/application/_testdata/TestData.Imports.cs @@ -0,0 +1,46 @@ +using System.Globalization; + +namespace Test.Data; + +// TEST DATA IMPORT UTILITIES + +internal static class Imports +{ + private static readonly CultureInfo EnglishCulture = new("en-US", false); + + // importer / parser + internal static Quote QuoteFromCsv(string csvLine) + { + if (string.IsNullOrEmpty(csvLine)) + { + throw new InvalidDataException("CSV line was empty"); + } + + string[] csv = csvLine.Split(','); + + Quote quote = new( + Timestamp: DateTime.TryParse(csv[0], EnglishCulture, out DateTime d) ? d : default, + Open: csv[1].ToDecimalDefault(), + High: csv[2].ToDecimalDefault(), + Low: csv[3].ToDecimalDefault(), + Close: csv[4].ToDecimalDefault(), + Volume: csv[5].ToDecimalDefault() + ); + + return quote; + } + + internal static decimal ToDecimal(this string value) + => decimal.TryParse(value, out decimal d) ? d + : throw new NotFiniteNumberException( + $"Cannot convert `{value}`, it is not a number."); + + internal static decimal ToDecimalDefault(this string value) + => decimal.TryParse(value, out decimal d) ? d : default; + + internal static decimal? ToDecimalNull(this string value) + => decimal.TryParse(value, out decimal d) ? d : null; + + internal static double? ToDoubleNull(this string value) + => double.TryParse(value, out double d) ? d : null; +} diff --git a/tests/indicators/_common/data/compare.csv b/tests/application/_testdata/data/compare.csv similarity index 100% rename from tests/indicators/_common/data/compare.csv rename to tests/application/_testdata/data/compare.csv diff --git a/tests/indicators/_common/data/default.csv b/tests/application/_testdata/data/default.csv similarity index 100% rename from tests/indicators/_common/data/default.csv rename to tests/application/_testdata/data/default.csv diff --git a/tests/indicators/_common/data/intraday.csv b/tests/application/_testdata/data/intraday.csv similarity index 100% rename from tests/indicators/_common/data/intraday.csv rename to tests/application/_testdata/data/intraday.csv diff --git a/tests/indicators/_common/data/longest.csv b/tests/application/_testdata/data/longest.csv similarity index 100% rename from tests/indicators/_common/data/longest.csv rename to tests/application/_testdata/data/longest.csv diff --git a/tests/indicators/_common/data/longish.csv b/tests/application/_testdata/data/longish.csv similarity index 100% rename from tests/indicators/_common/data/longish.csv rename to tests/application/_testdata/data/longish.csv diff --git a/tests/indicators/GlobalSuppressions.cs b/tests/indicators/GlobalSuppressions.cs index b63914909..400f328ca 100644 --- a/tests/indicators/GlobalSuppressions.cs +++ b/tests/indicators/GlobalSuppressions.cs @@ -1,31 +1,6 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - using System.Diagnostics.CodeAnalysis; -[assembly: SuppressMessage( - "StyleCop.CSharp.NamingRules", - "SA1304:Non-private readonly fields should begin with upper-case letter", - Justification = "Acceptable for test project.")] - -[assembly: SuppressMessage( - "StyleCop.CSharp.NamingRules", - "SA1307:Accessible fields should begin with upper-case letter", - Justification = "Acceptable for test project.")] - -[assembly: SuppressMessage( - "StyleCop.CSharp.NamingRules", - "SA1311:Static readonly fields should begin with upper-case letter", - Justification = "Acceptable for test project.")] - [assembly: SuppressMessage( "Security", "CA5394:Do not use insecure randomness", Justification = "Okay for test rig, non-production code.")] - -[assembly: SuppressMessage( - "StyleCop.CSharp.SpacingRules", - "SA1010:Opening square brackets should be spaced correctly", - Justification = "Invalid for new C# 12 [ collection ] syntax.")] diff --git a/tests/indicators/GlobalUsings.cs b/tests/indicators/GlobalUsings.cs index bafd87a1b..fc2afe48a 100644 --- a/tests/indicators/GlobalUsings.cs +++ b/tests/indicators/GlobalUsings.cs @@ -1,4 +1,4 @@ global using FluentAssertions; global using Microsoft.VisualStudio.TestTools.UnitTesting; global using Skender.Stock.Indicators; -global using Tests.Common; +global using Test.Data; diff --git a/tests/indicators/TestBase.cs b/tests/indicators/TestBase.cs new file mode 100644 index 000000000..5486c5f0e --- /dev/null +++ b/tests/indicators/TestBase.cs @@ -0,0 +1,81 @@ +using System.Globalization; +using System.Runtime.CompilerServices; + +// GLOBALS & INITIALIZATION OF TEST DATA + +[assembly: CLSCompliant(true)] +[assembly: InternalsVisibleTo("Tests.Other")] // these use test data +[assembly: InternalsVisibleTo("Tests.Performance")] +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] + +namespace Test.Data; + +public abstract class TestBase // base for all tests +{ + internal static readonly CultureInfo invariantCulture = CultureInfo.InvariantCulture; + + internal static readonly IReadOnlyList Quotes = Data.GetDefault(); + internal static readonly IReadOnlyList OtherQuotes = Data.GetCompare(); + internal static readonly IReadOnlyList BadQuotes = Data.GetBad(); + internal static readonly IReadOnlyList BigQuotes = Data.GetTooBig(); + internal static readonly IReadOnlyList LongishQuotes = Data.GetLongish(); + internal static readonly IReadOnlyList LongestQuotes = Data.GetLongest(); + internal static readonly IReadOnlyList MismatchQuotes = Data.GetMismatch(); + internal static readonly IReadOnlyList Noquotes = []; + internal static readonly IReadOnlyList Onequote = Data.GetDefault(1); + internal static readonly IReadOnlyList RandomQuotes = Data.GetRandom(1000); + internal static readonly IReadOnlyList ZeroesQuotes = Data.GetZeros(); + + protected static readonly double DoublePrecision = 1E-13; + + protected static readonly DateTime EvalDate + = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", invariantCulture); +} + +/// +/// Base tests that all series indicators should have. +/// +public abstract class StaticSeriesTestBase : TestBase +{ + public abstract void Standard(); + + public abstract void BadData(); + + public abstract void NoQuotes(); +} + +/// +/// Base tests that all static indicators (series) should have. +/// +public abstract class IncrementsTestBase : TestBase +{ + public abstract void FromQuote(); + + public abstract void FromQuoteBatch(); +} + +/// +/// Base tests that all streamed indicators should have. +/// +public abstract class StreamHubTestBase : TestBase // default: quote observer +{ + public abstract void QuoteObserver(); + + public abstract void CustomToString(); +} + +/// +/// Add this to stream chainee indicator tests. +/// +public interface ITestChainObserver +{ + void ChainObserver(); +} + +/// +/// Add this to all stream chainor indicator tests. +/// +public interface ITestChainProvider +{ + void ChainProvider(); +} diff --git a/tests/indicators/Tests.Indicators.csproj b/tests/indicators/Tests.Indicators.csproj index b4d8d0d07..7f6a08c48 100644 --- a/tests/indicators/Tests.Indicators.csproj +++ b/tests/indicators/Tests.Indicators.csproj @@ -18,6 +18,10 @@ + + all + runtime; build; native; contentfiles; analyzers + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -29,7 +33,8 @@ - + + Always diff --git a/tests/indicators/_Initialize.cs b/tests/indicators/_Initialize.cs deleted file mode 100644 index 8fdecf061..000000000 --- a/tests/indicators/_Initialize.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Globalization; -using System.Runtime.CompilerServices; - -// GLOBALS & INITIALIZATION OF TEST DATA - -[assembly: CLSCompliant(true)] -[assembly: InternalsVisibleTo("Tests.Other")] -[assembly: InternalsVisibleTo("Tests.Performance")] -[assembly: InternalsVisibleTo("Observe.Streaming")] -namespace Tests.Common; - -[TestClass] -public abstract class TestBase -{ - internal static readonly CultureInfo EnglishCulture = new("en-US", false); - - internal static readonly IEnumerable quotes = TestData.GetDefault(); - internal static readonly IEnumerable otherQuotes = TestData.GetCompare(); - internal static readonly IEnumerable badQuotes = TestData.GetBad(); - internal static readonly IEnumerable bigQuotes = TestData.GetTooBig(); - internal static readonly IEnumerable maxQuotes = TestData.GetMax(); - internal static readonly IEnumerable longishQuotes = TestData.GetLongish(); - internal static readonly IEnumerable longestQuotes = TestData.GetLongest(); - internal static readonly IEnumerable mismatchQuotes = TestData.GetMismatch(); - internal static readonly IEnumerable noquotes = new List(); - internal static readonly IEnumerable onequote = TestData.GetDefault(1); - internal static readonly IEnumerable randomQuotes = TestData.GetRandom(1000); - internal static readonly IEnumerable zeroesQuotes = TestData.GetZeros(); - internal static readonly IEnumerable<(DateTime, double)> tupleNanny = TestData.GetTupleNaN(); -} diff --git a/tests/indicators/_common/Candles/Candles.Tests.cs b/tests/indicators/_common/Candles/Candles.Tests.cs index 2f5dbf94e..a23c94202 100644 --- a/tests/indicators/_common/Candles/Candles.Tests.cs +++ b/tests/indicators/_common/Candles/Candles.Tests.cs @@ -1,4 +1,4 @@ -namespace Tests.Common; +namespace Utilities; [TestClass] public class Candles : TestBase @@ -6,73 +6,77 @@ public class Candles : TestBase [TestMethod] public void SortCandles() { - IEnumerable quotes = TestData.GetMismatch(); + IReadOnlyList quotes = Data.GetMismatch(); // sort - List candles = quotes.ToCandleResults(); + IReadOnlyList candles = quotes + .ToCandles(); // not sorted // proper quantities Assert.AreEqual(502, candles.Count); // sample values - DateTime firstDate = DateTime.ParseExact("01/18/2016", "MM/dd/yyyy", EnglishCulture); - Assert.AreEqual(firstDate, candles[0].Date); + DateTime firstDate = DateTime.ParseExact("01/18/2016", "MM/dd/yyyy", invariantCulture); + Assert.AreEqual(firstDate, candles[0].Timestamp); - DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", EnglishCulture); - Assert.AreEqual(lastDate, candles.LastOrDefault().Date); + DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", invariantCulture); + Assert.AreEqual(lastDate, candles[^1].Timestamp); - DateTime spotDate = DateTime.ParseExact("03/16/2017", "MM/dd/yyyy", EnglishCulture); - Assert.AreEqual(spotDate, candles[50].Date); + DateTime spotDate = DateTime.ParseExact("03/16/2017", "MM/dd/yyyy", invariantCulture); + Assert.AreEqual(spotDate, candles[50].Timestamp); } [TestMethod] public void CandleValues() { - List candles = quotes.ToCandleResults(); + IReadOnlyList candles = Quotes + .ToCandles(); // proper quantities Assert.AreEqual(502, candles.Count); // sample values - CandleResult r0 = candles[0]; - Assert.AreEqual(212.8m, r0.Candle.Close); - Assert.AreEqual(1.83m, r0.Candle.Size); - Assert.AreEqual(0.19m, r0.Candle.Body); - Assert.AreEqual(0.55m, r0.Candle.UpperWick); - Assert.AreEqual(1.09m, r0.Candle.LowerWick); - Assert.AreEqual(0.10383, r0.Candle.BodyPct.Round(5)); - Assert.AreEqual(0.30055, r0.Candle.UpperWickPct.Round(5)); - Assert.AreEqual(0.59563, r0.Candle.LowerWickPct.Round(5)); - Assert.IsTrue(r0.Candle.IsBullish); - Assert.IsFalse(r0.Candle.IsBearish); + CandleProperties r0 = candles[0]; + Assert.AreEqual(212.8m, r0.Close); + Assert.AreEqual(1.83m, r0.Size); + Assert.AreEqual(0.19m, r0.Body); + Assert.AreEqual(0.55m, r0.UpperWick); + Assert.AreEqual(1.09m, r0.LowerWick); + Assert.AreEqual(0.10383, r0.BodyPct.Round(5)); + Assert.AreEqual(0.30055, r0.UpperWickPct.Round(5)); + Assert.AreEqual(0.59563, r0.LowerWickPct.Round(5)); + Assert.IsTrue(r0.IsBullish); + Assert.IsFalse(r0.IsBearish); - CandleResult r351 = candles[351]; - Assert.AreEqual(1.24m, r351.Candle.Size); - Assert.AreEqual(0m, r351.Candle.Body); - Assert.AreEqual(0.69m, r351.Candle.UpperWick); - Assert.AreEqual(0.55m, r351.Candle.LowerWick); - Assert.AreEqual(0, r351.Candle.BodyPct.Round(5)); - Assert.AreEqual(0.55645, r351.Candle.UpperWickPct.Round(5)); - Assert.AreEqual(0.44355, r351.Candle.LowerWickPct.Round(5)); - Assert.IsFalse(r351.Candle.IsBullish); - Assert.IsFalse(r351.Candle.IsBearish); + CandleProperties r351 = candles[351]; + Assert.AreEqual(1.24m, r351.Size); + Assert.AreEqual(0m, r351.Body); + Assert.AreEqual(0.69m, r351.UpperWick); + Assert.AreEqual(0.55m, r351.LowerWick); + Assert.AreEqual(0, r351.BodyPct.Round(5)); + Assert.AreEqual(0.55645, r351.UpperWickPct.Round(5)); + Assert.AreEqual(0.44355, r351.LowerWickPct.Round(5)); + Assert.IsFalse(r351.IsBullish); + Assert.IsFalse(r351.IsBearish); - CandleResult r501 = candles[501]; - Assert.AreEqual(2.67m, r501.Candle.Size); - Assert.AreEqual(0.36m, r501.Candle.Body); - Assert.AreEqual(0.26m, r501.Candle.UpperWick); - Assert.AreEqual(2.05m, r501.Candle.LowerWick); - Assert.AreEqual(0.13483, r501.Candle.BodyPct.Round(5)); - Assert.AreEqual(0.09738, r501.Candle.UpperWickPct.Round(5)); - Assert.AreEqual(0.76779, r501.Candle.LowerWickPct.Round(5)); - Assert.IsTrue(r501.Candle.IsBullish); - Assert.IsFalse(r501.Candle.IsBearish); + CandleProperties r501 = candles[501]; + Assert.AreEqual(2.67m, r501.Size); + Assert.AreEqual(0.36m, r501.Body); + Assert.AreEqual(0.26m, r501.UpperWick); + Assert.AreEqual(2.05m, r501.LowerWick); + Assert.AreEqual(0.13483, r501.BodyPct.Round(5)); + Assert.AreEqual(0.09738, r501.UpperWickPct.Round(5)); + Assert.AreEqual(0.76779, r501.LowerWickPct.Round(5)); + Assert.IsTrue(r501.IsBullish); + Assert.IsFalse(r501.IsBearish); } [TestMethod] public void ToCandles() { - IEnumerable candles = quotes.ToCandles(); - Assert.AreEqual(quotes.Count(), candles.Count()); + IReadOnlyList candles + = Quotes.ToCandles(); + + Assert.AreEqual(Quotes.Count, candles.Count); } } diff --git a/tests/indicators/_common/Generics/BinarySettingsTests.cs b/tests/indicators/_common/Generics/BinarySettingsTests.cs new file mode 100644 index 000000000..aa2b01b78 --- /dev/null +++ b/tests/indicators/_common/Generics/BinarySettingsTests.cs @@ -0,0 +1,73 @@ +namespace Utilities; + +[TestClass] +public class BinarySettingsTests : TestBase +{ + // see Renko Hub tests for inheritance + + [TestMethod] + public void InitializationDefault() + { + BinarySettings sut = new(); + sut.Settings.Should().Be(0); + sut.Mask.Should().Be(0b11111111); + } + + [TestMethod] + public void InitializationPartial() + { + BinarySettings sut = new(0); + sut.Settings.Should().Be(0); + sut.Mask.Should().Be(0b11111111); + } + + [TestMethod] + public void InitializationCustom() + { + BinarySettings sut = new(0b10101010, 0b11001100); + sut.Settings.Should().Be(0b10101010); + sut.Mask.Should().Be(0b11001100); + } + + [TestMethod] + public void AccessBit() + { + BinarySettings sut = new(0b00010001); + + // positions: 76543210 + sut[0].Should().BeTrue(); + sut[1].Should().BeFalse(); + sut[2].Should().BeFalse(); + sut[3].Should().BeFalse(); + sut[4].Should().BeTrue(); + sut[5].Should().BeFalse(); + sut[6].Should().BeFalse(); + sut[7].Should().BeFalse(); + } + + [TestMethod] + public void CombineDefaultMask() + { + BinarySettings srcSettings = new(0b01101001); + BinarySettings defSettings = new(0b00000010); + BinarySettings newSettings = defSettings.Combine(srcSettings); + newSettings.Settings.Should().Be(0b01101011); + } + + [TestMethod] + public void CombineCustomMask() + { + BinarySettings srcSettings = new(0b01101001, 0b11111110); + BinarySettings defSettings = new(0b00000010); + BinarySettings newSettings = defSettings.Combine(srcSettings); + newSettings.Settings.Should().Be(0b01101010); + } + + [TestMethod] + public void Equality() + { + BinarySettings sut = new(); + Assert.AreEqual(0b00000000, sut.Settings); + Assert.AreEqual(0b11111111, sut.Mask); + } +} diff --git a/tests/indicators/_common/Generics/Pruning.Tests.cs b/tests/indicators/_common/Generics/Pruning.Tests.cs deleted file mode 100644 index 5edd0265c..000000000 --- a/tests/indicators/_common/Generics/Pruning.Tests.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Tests.Common; - -[TestClass] -public class Pruning : TestBase -{ - [TestMethod] - public void Remove() - { - // specific periods - IEnumerable results = - quotes.GetHeikinAshi() - .RemoveWarmupPeriods(102); - - Assert.AreEqual(400, results.Count()); - - // bad remove period - Assert.ThrowsException(() - => quotes.GetAdx(14).RemoveWarmupPeriods(-1)); - } - - [TestMethod] - public void RemoveTooMany() - { - // more than available - IEnumerable results = - quotes.GetHeikinAshi() - .RemoveWarmupPeriods(600); - - Assert.AreEqual(0, results.Count()); - } -} diff --git a/tests/indicators/_common/Generics/RemoveWarmup.Tests.cs b/tests/indicators/_common/Generics/RemoveWarmup.Tests.cs new file mode 100644 index 000000000..99abb398b --- /dev/null +++ b/tests/indicators/_common/Generics/RemoveWarmup.Tests.cs @@ -0,0 +1,31 @@ +namespace Utilities; + +[TestClass] +public class RemoveWarmup : TestBase +{ + [TestMethod] + public void Standard() + { + // specific periods + IReadOnlyList results = Quotes + .ToHeikinAshi() + .RemoveWarmupPeriods(102); + + Assert.AreEqual(400, results.Count); + + // bad remove period + Assert.ThrowsException(() + => Quotes.ToAdx().RemoveWarmupPeriods(-1)); + } + + [TestMethod] + public void TooMany() + { + // more than available + IReadOnlyList results = Quotes + .ToHeikinAshi() + .RemoveWarmupPeriods(600); + + Assert.AreEqual(0, results.Count); + } +} diff --git a/tests/indicators/_common/Generics/Seek.Tests.cs b/tests/indicators/_common/Generics/Seek.Tests.cs index cb312d597..bcd1a673e 100644 --- a/tests/indicators/_common/Generics/Seek.Tests.cs +++ b/tests/indicators/_common/Generics/Seek.Tests.cs @@ -4,42 +4,14 @@ namespace Tests.Common; public class Seeking : TestBase { [TestMethod] - public void FindSeries() + public void Find() { - IEnumerable quotes = TestData.GetDefault(); - IEnumerable emaResults = quotes.GetEma(20); + var emaResults = Quotes.ToEma(20); // find specific date - DateTime findDate = DateTime.ParseExact("2018-12-31", "yyyy-MM-dd", EnglishCulture); + DateTime findDate = DateTime.ParseExact("2018-12-31", "yyyy-MM-dd", invariantCulture); EmaResult r = emaResults.Find(findDate); Assert.AreEqual(249.3519, r.Ema.Round(4)); } - - [TestMethod] - public void FindSeriesNone() - { - IEnumerable quotes = TestData.GetDefault(); - IEnumerable emaResults = quotes.GetEma(20); - - // find specific date - DateTime findDate = DateTime.ParseExact("1928-10-29", "yyyy-MM-dd", EnglishCulture); - - EmaResult r = emaResults.Find(findDate); - Assert.IsNull(r); - } - - [TestMethod] - public void FindSeriesIndex() - { - List quotes = TestData - .GetDefault() - .ToSortedList(); - - // find specific date - DateTime findDate = DateTime.ParseExact("2018-12-31", "yyyy-MM-dd", EnglishCulture); - - int i = quotes.FindIndex(findDate); - Assert.AreEqual(501, i); - } } diff --git a/tests/indicators/_common/Generics/Sort.Tests.cs b/tests/indicators/_common/Generics/Sort.Tests.cs deleted file mode 100644 index 9445c0426..000000000 --- a/tests/indicators/_common/Generics/Sort.Tests.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Collections.ObjectModel; - -namespace Tests.Common; - -[TestClass] -public class Sorting : TestBase -{ - [TestMethod] - public void ToSortedCollection() - { - // baseline for comparison - List baseline = - [ - new SmaResult(DateTime.Parse("1/1/2000", EnglishCulture)) { Sma = null }, - new SmaResult(DateTime.Parse("1/2/2000", EnglishCulture)) { Sma = null }, - new SmaResult(DateTime.Parse("1/9/2000", EnglishCulture)) { Sma = null }, - new SmaResult(DateTime.Parse("1/3/2000", EnglishCulture)) { Sma = 3 }, - new SmaResult(DateTime.Parse("1/4/2000", EnglishCulture)) { Sma = 4 }, - new SmaResult(DateTime.Parse("1/5/2000", EnglishCulture)) { Sma = 5 }, - new SmaResult(DateTime.Parse("1/6/2000", EnglishCulture)) { Sma = 6 }, - new SmaResult(DateTime.Parse("1/7/2000", EnglishCulture)) { Sma = 7 }, - new SmaResult(DateTime.Parse("1/8/2000", EnglishCulture)) { Sma = double.NaN }, - ]; - - // PUBLIC VARIANT, generic sorted Collection - Collection sortResults = baseline - .ToSortedCollection(); - - Assert.AreEqual(5, sortResults[4].Sma); - Assert.AreEqual(DateTime.Parse("1/9/2000", EnglishCulture), sortResults.LastOrDefault().Date); - } -} diff --git a/tests/indicators/_common/Generics/Sorting.Tests.cs b/tests/indicators/_common/Generics/Sorting.Tests.cs new file mode 100644 index 000000000..4bb16536b --- /dev/null +++ b/tests/indicators/_common/Generics/Sorting.Tests.cs @@ -0,0 +1,32 @@ +using System.Collections.ObjectModel; + +namespace Utilities; + +[TestClass] +public class Sorting : TestBase +{ + [TestMethod] + public void ToSortedList() + { + // baseline for comparison + IReadOnlyList baseline = + [ + new(Timestamp: DateTime.Parse("1/1/2000", invariantCulture), Sma: null), + new(Timestamp: DateTime.Parse("1/2/2000", invariantCulture), Sma: null), + new(Timestamp: DateTime.Parse("1/9/2000", invariantCulture), Sma: null), + new(Timestamp: DateTime.Parse("1/3/2000", invariantCulture), Sma: 3), + new(Timestamp: DateTime.Parse("1/4/2000", invariantCulture), Sma: 4), + new(Timestamp: DateTime.Parse("1/5/2000", invariantCulture), Sma: 5), + new(Timestamp: DateTime.Parse("1/6/2000", invariantCulture), Sma: 6), + new(Timestamp: DateTime.Parse("1/7/2000", invariantCulture), Sma: 7), + new(Timestamp: DateTime.Parse("1/8/2000", invariantCulture), Sma: double.NaN) + ]; + + // PUBLIC VARIANT, generic sorted list + IReadOnlyList sortResults = baseline + .ToSortedList(); + + Assert.AreEqual(5, sortResults[4].Sma); + Assert.AreEqual(DateTime.Parse("1/9/2000", invariantCulture), sortResults[^1].Timestamp); + } +} diff --git a/tests/indicators/_common/Generics/Transforms.Tests.cs b/tests/indicators/_common/Generics/Transforms.Tests.cs index 8c398518f..60a8da66b 100644 --- a/tests/indicators/_common/Generics/Transforms.Tests.cs +++ b/tests/indicators/_common/Generics/Transforms.Tests.cs @@ -1,27 +1,27 @@ using System.Collections.ObjectModel; -namespace Tests.Common; +namespace Utilities; [TestClass] -public class TransformTests : TestBase +public class Transforms : TestBase { [TestMethod] public void ToCollection() { - Collection collection = quotes + Collection collection = Quotes .ToSortedList() .ToCollection(); Assert.IsNotNull(collection); Assert.AreEqual(502, collection.Count); - Assert.AreEqual(collection.LastOrDefault().Close, 245.28m); + Assert.AreEqual(245.28m, collection.LastOrDefault().Close); } // null ToCollection [TestMethod] - public void Exceptions() + public void ToCollectionNullExceptions() { - List nullQuotes = null; + IReadOnlyList nullQuotes = null; Assert.ThrowsException(() => nullQuotes.ToCollection()); diff --git a/tests/indicators/_common/Helper.Getter.cs b/tests/indicators/_common/Helper.Getter.cs deleted file mode 100644 index 7a2daa11a..000000000 --- a/tests/indicators/_common/Helper.Getter.cs +++ /dev/null @@ -1,212 +0,0 @@ -namespace Tests.Common; - -// IMPORT TEST DATA -internal static class TestData -{ - // DEFAULT: S&P 500 ~2 years of daily data - internal static IEnumerable GetDefault(int days = 502) - => File.ReadAllLines("_common/data/default.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date) - .Take(days) - .ToList(); - - // RANDOM: gaussian brownaian motion - internal static IEnumerable GetRandom(int days = 502) - => new RandomGbm(bars: days); - - // ZEROS (200) - internal static IEnumerable GetZeros(int days = 200) - => File.ReadAllLines("_common/data/zeros.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date) - .Take(days) - .ToList(); - - // BAD DATA - internal static IEnumerable GetBad(int days = 502) - => File.ReadAllLines("_common/data/bad.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date) - .Take(days) - .ToList(); - - // TOO BIG DATA - internal static IEnumerable GetTooBig(int days = 1246) - => File.ReadAllLines("_common/data/toobig.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date) - .Take(days) - .ToList(); - - // MAX SIZE DATA - internal static IEnumerable GetMax(int days = 502) - => File.ReadAllLines("_common/data/toobig.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date) - .Take(days) - .ToList(); - - // BITCOIN DATA - internal static IEnumerable GetBitcoin(int days = 1246) - => File.ReadAllLines("_common/data/bitcoin.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date) - .Take(days) - .ToList(); - - // COMPARE DATA ~2 years of TSLA data (matches default time) - internal static IEnumerable GetCompare(int days = 502) - => File.ReadAllLines("_common/data/compare.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date) - .Take(days) - .ToList(); - - // INTRADAY DATA - internal static IEnumerable GetIntraday(int days = 1564) - => File.ReadAllLines("_common/data/intraday.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date) - .Take(days) - .ToList(); - - // LONGISH DATA ~20 years of S&P 500 daily data - internal static IEnumerable GetLongish(int days = 5285) - => File.ReadAllLines("_common/data/longish.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date) - .Take(days) - .ToList(); - - // LONGEST DATA ~62 years of S&P 500 daily data - internal static IEnumerable GetLongest() - => File.ReadAllLines("_common/data/longest.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .ToList(); - - // PENNY DATA - internal static IEnumerable GetPenny() - => File.ReadAllLines("_common/data/penny.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .ToList(); - - // MISMATCH DATA is in incorrect sequence - internal static IEnumerable GetMismatch() - => File.ReadAllLines("_common/data/mismatch.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .ToList(); - - // SPX, 30 years, daily - internal static IEnumerable GetSpx(int days = 8111) - => File.ReadAllLines("_common/data/spx.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date) - .Take(days) - .ToList(); - - // MSFT, 30 years, daily - internal static IEnumerable GetMsft(int days = 8111) - => File.ReadAllLines("_common/data/msft.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date) - .Take(days) - .ToList(); - - // BTCUSD, 69288 records, 15-minute bars - internal static IEnumerable GetBtcUsdNan(int bars = 69288) - => File.ReadAllLines("_common/data/btcusd15x69k.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date) - .Take(bars) - .ToList(); - - // TUPLE with NaNs - internal static IEnumerable<(DateTime, double)> GetTupleNaN() - { - List<(DateTime, double)> tpList = new(200); - double timeFactor = 10000000d; - - DateTime date = DateTime.UtcNow; - - // sequential - for (int i = 0; i < 25; i++) - { - date = date.AddDays(1); - double value = date.ToFileTime() / timeFactor; - - tpList.Add(new(date, value)); - } - - // sequential negative - for (int i = 0; i < 25; i++) - { - date = date.AddDays(1); - double value = -date.ToFileTime() / timeFactor; - - tpList.Add(new(date, value)); - } - - // sequential 0 - for (int i = 0; i < 25; i++) - { - date = date.AddDays(1); - double value = 0; - - tpList.Add(new(date, value)); - } - - // sequential -10 - for (int i = 0; i < 25; i++) - { - date = date.AddDays(1); - double value = -10; - - tpList.Add(new(date, value)); - } - - // sequential 10 - for (int i = 0; i < 25; i++) - { - date = date.AddDays(1); - double value = 10; - - tpList.Add(new(date, value)); - } - - // some NaNs - for (int i = 0; i < 25; i++) - { - date = date.AddDays(1); - double value = double.NaN; - - tpList.Add(new(date, value)); - } - - // more sequential - for (int i = 0; i < 50; i++) - { - date = date.AddDays(1); - double value = date.ToFileTime() / timeFactor; - - tpList.Add(new(date, value)); - } - - return tpList; - } -} diff --git a/tests/indicators/_common/Helper.Importer.cs b/tests/indicators/_common/Helper.Importer.cs deleted file mode 100644 index b88006ca4..000000000 --- a/tests/indicators/_common/Helper.Importer.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Globalization; - -namespace Tests.Common; - -// TEST QUOTE IMPORTER -internal static class Importer -{ - private static readonly CultureInfo EnglishCulture = new("en-US", false); - - // importer / parser - internal static Quote QuoteFromCsv(string csvLine) - { - if (string.IsNullOrEmpty(csvLine)) - { - return new Quote(); - } - - string[] values = csvLine.Split(','); - Quote quote = new(); - - HandleOHLCV(quote, "D", values[0]); - HandleOHLCV(quote, "O", values[1]); - HandleOHLCV(quote, "H", values[2]); - HandleOHLCV(quote, "L", values[3]); - HandleOHLCV(quote, "C", values[4]); - HandleOHLCV(quote, "V", values[5]); - - return quote; - } - - internal static decimal ToDecimal(this string value) - => decimal.TryParse(value, out decimal d) ? d - : throw new NotFiniteNumberException( - $"Cannot convert `{value}`, it is not a number."); - - internal static decimal? ToDecimalNull(this string value) - => decimal.TryParse(value, out decimal d) ? d : null; - - internal static double? ToDoubleNull(this string value) - => double.TryParse(value, out double d) ? d : null; - - private static void HandleOHLCV(Quote quote, string position, string value) - { - if (string.IsNullOrEmpty(value)) - { - return; - } - - switch (position) - { - case "D": - quote.Date = Convert.ToDateTime(value, EnglishCulture); - break; - case "O": - quote.Open = Convert.ToDecimal(value, EnglishCulture); - break; - case "H": - quote.High = Convert.ToDecimal(value, EnglishCulture); - break; - case "L": - quote.Low = Convert.ToDecimal(value, EnglishCulture); - break; - case "C": - quote.Close = Convert.ToDecimal(value, EnglishCulture); - break; - case "V": - quote.Volume = Convert.ToDecimal(value, EnglishCulture); - break; - default: - throw new ArgumentOutOfRangeException(nameof(position)); - } - } -} diff --git a/tests/indicators/_common/Helper.Random.cs b/tests/indicators/_common/Helper.Random.cs deleted file mode 100644 index cc11b90b8..000000000 --- a/tests/indicators/_common/Helper.Random.cs +++ /dev/null @@ -1,77 +0,0 @@ -namespace Tests.Common; -/** - -Geometric Brownian Motion (GMB) is a random simulator of market movement. -GBM can be used for testing indicators, validation and Monte Carlo simulations of strategies. - -Sample usage: -RandomGbm data = new(); // generates 1 year (252) list of bars -RandomGbm data = new(Bars: 1000); // generates 1,000 bars -RandomGbm data = new(Bars: 252, Volatility: 0.05, Drift: 0.0005, Seed: 100.0) - -Parameters -Bars: number of bars (quotes) requested -Volatility: how dymamic/volatile the series should be; default is 1 -Drift: incremental drift due to annual interest rate; default is 5% -Seed: starting value of the random series; should not be 0. - -**/ -internal class RandomGbm : List -{ - private readonly double volatility; - private readonly double drift; - private double seed; - - public RandomGbm( - int bars = 250, - double volatility = 1.0, - double drift = 0.01, - double seed = 1000.0) - { - this.seed = seed; - this.volatility = volatility * 0.01; - this.drift = drift * 0.001; - for (int i = 0; i < bars; i++) - { - DateTime date = DateTime.Today.AddMinutes(i - bars); - Add(date); - } - } - - public void Add(DateTime timestamp) - { - double open = Price(seed, volatility * volatility, drift); - double close = Price(open, volatility, drift); - - double ocMax = Math.Max(open, close); - double high = Price(seed, volatility * 0.5, 0); - high = (high < ocMax) ? (2 * ocMax) - high : high; - - double ocMin = Math.Min(open, close); - double low = Price(seed, volatility * 0.5, 0); - low = (low > ocMin) ? (2 * ocMin) - low : low; - - double volume = Price(seed * 10, volatility * 2, drift: 0); - - Quote quote = new() { - Date = timestamp, - Open = (decimal)open, - High = (decimal)high, - Low = (decimal)low, - Close = (decimal)close, - Volume = (decimal)volume - }; - - Add(quote); - seed = close; - } - - private static double Price(double seed, double volatility, double drift) - { - Random rnd = new((int)DateTime.UtcNow.Ticks); - double u1 = 1.0 - rnd.NextDouble(); - double u2 = 1.0 - rnd.NextDouble(); - double z = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); - return seed * Math.Exp(drift - (volatility * volatility * 0.5) + (volatility * z)); - } -} diff --git a/tests/indicators/_common/Math/Numerix.Tests.cs b/tests/indicators/_common/Math/Numerical.Tests.cs similarity index 67% rename from tests/indicators/_common/Math/Numerix.Tests.cs rename to tests/indicators/_common/Math/Numerical.Tests.cs index a8cc81498..e116fe11f 100644 --- a/tests/indicators/_common/Math/Numerix.Tests.cs +++ b/tests/indicators/_common/Math/Numerical.Tests.cs @@ -1,55 +1,63 @@ -namespace Tests.Common; +namespace Utilities; [TestClass] -public class NumerixTests : TestBase +public class Numericals : TestBase { - private readonly double[] closePrice = longishQuotes + private readonly double[] _closePrice = LongishQuotes .Select(x => (double)x.Close) .ToArray(); - private readonly double[] x = [1, 2, 3, 4, 5]; - private readonly double[] y = [0, 0, 0, 0]; + private readonly double[] _x = { 1, 2, 3, 4, 5 }; + private readonly double[] _y = { 0, 0, 0, 0 }; [TestMethod] public void StdDev() { - double sd = closePrice.StdDev(); + double sd = _closePrice.StdDev(); Assert.AreEqual(633.932098287, Math.Round(sd, 9)); } [TestMethod] - [ExpectedException(typeof(ArgumentNullException), "Null parameter.")] - public void StdDevNull() => Numerix.StdDev(null); + public void StdDevNull() + { + Assert.ThrowsException(() => Numerical.StdDev(null)); + } [TestMethod] public void Slope() { - double s = Numerix.Slope(x, x); + double s = Numerical.Slope(_x, _x); Assert.AreEqual(1d, s); } [TestMethod] - [ExpectedException(typeof(ArgumentNullException), "Null X parameter.")] - public void SlopeXnull() => Numerix.Slope(null, x); + public void SlopeXnull() + { + Assert.ThrowsException(() => Numerical.Slope(null, _x)); + } [TestMethod] - [ExpectedException(typeof(ArgumentNullException), "Null Y parameter.")] - public void SlopeYnull() => Numerix.Slope(x, null); + public void SlopeYnull() + { + Assert.ThrowsException(() => Numerical.Slope(_x, null)); + } [TestMethod] - [ExpectedException(typeof(ArgumentException), "X and Y different lengths.")] - public void SlopeMismatch() => Numerix.Slope(x, y); + public void SlopeMismatch() + { + Assert.ThrowsException(() => Numerical.Slope(_x, _y)); + } [TestMethod] public void RoundDownDate() { TimeSpan interval = PeriodSize.OneHour.ToTimeSpan(); - DateTime evDate = DateTime.Parse("2020-12-15 09:35:45", EnglishCulture); + DateTime evDate = DateTime.Parse("2020-12-15 09:35:45", invariantCulture); DateTime rnDate = evDate.RoundDown(interval); - DateTime exDate = DateTime.Parse("2020-12-15 09:00:00", EnglishCulture); + DateTime exDate = DateTime.Parse("2020-12-15 09:00:00", invariantCulture); Assert.AreEqual(exDate, rnDate); } diff --git a/tests/indicators/_common/Observables/StreamHub.CacheMgmt.Tests.cs b/tests/indicators/_common/Observables/StreamHub.CacheMgmt.Tests.cs new file mode 100644 index 000000000..2ebd45acf --- /dev/null +++ b/tests/indicators/_common/Observables/StreamHub.CacheMgmt.Tests.cs @@ -0,0 +1,159 @@ +namespace Observables; + +[TestClass] +public class CacheManagement : TestBase +{ + [TestMethod] + public void ModifyWithAnalysis() => Assert.Inconclusive("test not implemented"); + + [TestMethod] + public void ModifyWithAct() => Assert.Inconclusive("test not implemented"); + + [TestMethod] + public void Remove() + { + QuoteHub provider = new(); + SmaHub observer = provider.ToSma(20); + provider.Add(Quotes.Take(21)); + + observer.Results[19].Sma.Should().BeApproximately(214.5250, precision: DoublePrecision); + + provider.Remove(Quotes[14]); + provider.EndTransmission(); + + observer.Results[19].Sma.Should().BeApproximately(214.5260, precision: DoublePrecision); + } + + [TestMethod] // TODO: tests should include all Act enum methods + public void ActInstructions() => Assert.Inconclusive("test not implemented"); + + [TestMethod] + public void ActAddOld() // late arrival + { + int length = Quotes.Count; + + // add base quotes + QuoteHub provider = new(); + + QuotePartHub observer = provider + .ToQuotePart(CandlePart.Close); + + // emulate incremental quotes + for (int i = 0; i < length; i++) + { + // skip one + if (i == 100) + { + continue; + } + + Quote q = Quotes[i]; + provider.Add(q); + } + + // add late + provider.Insert(Quotes[100]); + + // assert same as original + for (int i = 0; i < length; i++) + { + Quote q = Quotes[i]; + QuotePart r = observer.Cache[i]; + + // compare quote to result cache + r.Timestamp.Should().Be(q.Timestamp); + r.Value.Should().Be(q.Value); + } + + // close observations + provider.EndTransmission(); + } + + [TestMethod] + public void Overflowing() + { + // initialize + QuoteHub provider = new(); + + Quote dup = new( + Timestamp: DateTime.Now, + Open: 1.00m, + High: 2.00m, + Low: 0.50m, + Close: 1.75m, + Volume: 1000); + + QuotePartHub observer = provider + .ToQuotePart(CandlePart.Close); + + // overflowing, under threshold + for (int i = 0; i <= 100; i++) + { + provider.Add(dup); + } + + // assert: no fault, no overflow (yet) + + provider.Quotes.Should().HaveCount(1); + observer.Results.Should().HaveCount(1); + provider.IsFaulted.Should().BeFalse(); + provider.OverflowCount.Should().Be(100); + provider.HasObservers.Should().BeTrue(); + + provider.EndTransmission(); + } + + [TestMethod] + public void OverflowedAndReset() + { + // initialize + QuoteHub provider = new(); + + Quote dup = new( + Timestamp: DateTime.Now, + Open: 1.00m, + High: 2.00m, + Low: 0.50m, + Close: 1.75m, + Volume: 1000); + + QuotePartHub observer = provider + .ToQuotePart(CandlePart.Close); + + // overflowed, over threshold + Assert.ThrowsException(() => { + + for (int i = 0; i <= 101; i++) + { + provider.Add(dup); + } + }); + + // assert: faulted + + provider.Quotes.Should().HaveCount(1); + observer.Results.Should().HaveCount(1); + provider.IsFaulted.Should().BeTrue(); + provider.OverflowCount.Should().Be(101); + provider.HasObservers.Should().BeTrue(); + + // act: reset + + provider.ResetFault(); + + for (int i = 0; i < 100; i++) + { + provider.Add(dup); + } + + // assert: no fault, no overflow (yet) + + provider.Quotes.Should().HaveCount(1); + observer.Results.Should().HaveCount(1); + provider.IsFaulted.Should().BeFalse(); + provider.OverflowCount.Should().Be(100); + provider.HasObservers.Should().BeTrue(); // not lost + + provider.EndTransmission(); + } +} diff --git a/tests/indicators/_common/Observables/StreamHub.Observable.Tests.cs b/tests/indicators/_common/Observables/StreamHub.Observable.Tests.cs new file mode 100644 index 000000000..8dcb6f73c --- /dev/null +++ b/tests/indicators/_common/Observables/StreamHub.Observable.Tests.cs @@ -0,0 +1,112 @@ +namespace Observables; + +[TestClass] +public class StreamObservables : TestBase, ITestChainProvider +{ + [TestMethod] + public void Prefill() + { + IReadOnlyList quotesList = Quotes + .Take(50) + .ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // prefill quotes to provider + provider.Add(quotesList); + + // initialize observer + QuotePartHub observer = provider + .ToQuotePart(CandlePart.Close); + + // assert: prefilled + provider.Cache.Should().HaveCount(50); + observer.Cache.Should().HaveCount(50); + + // assert: same dates + for (int i = 0; i < 50; i++) + { + IReusable r = observer.Cache[i]; + IReusable q = provider.Cache[i]; + + r.Timestamp.Should().Be(q.Timestamp); + } + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public void Subscription() + { + // setup quote provider, observer + QuoteHub provider = new(); + + QuotePartHub observer + = provider.ToQuotePart(CandlePart.OHLC4); + + // assert: subscribed + provider.ObserverCount.Should().Be(1); + provider.HasObservers.Should().BeTrue(); + observer.IsSubscribed.Should().BeTrue(); + + // act: unsubscribe + observer.Unsubscribe(); + + // assert: not subscribed + provider.ObserverCount.Should().Be(0); + provider.HasObservers.Should().BeFalse(); + observer.IsSubscribed.Should().BeFalse(); + + // act: resubscribe + provider.Subscribe(observer); + + // assert: subscribed + provider.ObserverCount.Should().Be(1); + provider.HasObservers.Should().BeTrue(); + observer.IsSubscribed.Should().BeTrue(); + + // act: end all subscriptions + provider.EndTransmission(); + + // assert: not subscribed + provider.ObserverCount.Should().Be(0); + provider.HasObservers.Should().BeFalse(); + observer.IsSubscribed.Should().BeFalse(); + } + + [TestMethod] + public void ChainProvider() + { + // setup quote provider + QuoteHub provider = new(); + + // initialize observer + EmaHub observer = provider + .ToQuotePart(CandlePart.HL2) + .ToEma(11); + + // emulate adding quotes to provider + provider.Add(Quotes); + provider.EndTransmission(); + + // stream results + IReadOnlyList streamList + = observer.Results; + + // time-series, for comparison + IReadOnlyList seriesList = Quotes + .Use(CandlePart.HL2) + .ToEma(11); + + // assert, should equal series + streamList.Should().HaveCount(Quotes.Count); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } +} diff --git a/tests/indicators/_common/Observables/StreamHub.Observer.Tests.cs b/tests/indicators/_common/Observables/StreamHub.Observer.Tests.cs new file mode 100644 index 000000000..794749b3b --- /dev/null +++ b/tests/indicators/_common/Observables/StreamHub.Observer.Tests.cs @@ -0,0 +1,62 @@ +namespace Observables; + +[TestClass] +public class StreamObservers : TestBase +{ + [TestMethod] + public void RebuildCache() + { + int qtyQuotes = 5000; + + // setup: many random quotes (massive) + IReadOnlyList quotesList + = Data.GetRandom(qtyQuotes).ToList(); + + int length = quotesList.Count; + + length.Should().Be(qtyQuotes); // check rando + + QuoteHub provider = new(); + + QuotePartHub observer = provider + .ToQuotePart(CandlePart.Close); + + for (int i = 0; i < length; i++) + { + provider.Add(quotesList[i]); + } + + // original results + IReadOnlyList original = observer.Results.ToList(); + + // quotes to replace + Quote q1000original = quotesList[1000] with { /* copy */ }; + QuotePart r1000original = observer.Cache[1000] with { /* copy */ }; + + // modify results (keeping provider intact) + Quote q1000modified = quotesList[1000] with { Close = 12345m }; + QuotePart r1000modified = q1000modified.ToQuotePart(CandlePart.Close); + + observer.Cache.Insert(1000, r1000modified); // add directly to cache + + IReadOnlyList modified = observer.Results.ToList(); + + // precondition: prefilled, modified + provider.Cache.Should().HaveCount(length); + observer.Cache.Should().HaveCount(length + 1); + + observer.Cache[1000].Value.Should().Be(12345); + observer.Cache.Should().NotBeEquivalentTo(original); + observer.Cache.Should().BeEquivalentTo(modified); + + // act: Rebuild() + observer.Rebuild(); + + // assert: restored to original + observer.Results.Should().HaveCount(length); + observer.Results.Should().BeEquivalentTo(original); + + observer.Cache[1000].Value.Should().NotBe(12345); + observer.Cache[1000].Value.Should().Be((double)quotesList[1000].Close); + } +} diff --git a/tests/indicators/_common/Observables/StreamHub.Stackoverflow.Tests.cs b/tests/indicators/_common/Observables/StreamHub.Stackoverflow.Tests.cs new file mode 100644 index 000000000..0e61b8fcc --- /dev/null +++ b/tests/indicators/_common/Observables/StreamHub.Stackoverflow.Tests.cs @@ -0,0 +1,260 @@ +namespace Observables; + +[TestClass] +public class Stackoverflow : TestBase +{ + [TestMethod] + public void FatLongStack() + { + // goal: about ~10 subscribers, with really long + // quote history, checking for stack overflow + + int qtyQuotes = 20000; + + // setup: many random quotes (massive) + IReadOnlyList quotesList = Data.GetRandom(qtyQuotes); + + QuoteHub provider = new(); + + // setup: define ~10 subscribers (flat) + List<(string label, IReadOnlyList results, bool irregular)> subscribers = new() + { + HubRef(provider.ToAdl()), + HubRef(provider.ToEma(14)) + }; + + // all USEs + foreach (CandlePart candlePart in Enum.GetValues()) + { + subscribers.Add(HubRef(provider.ToQuotePart(candlePart))); + } + + // act: add quotes + for (int i = 0; i < qtyQuotes; i++) + { + provider.Add(quotesList[i]); + } + + subscribers.Insert(0, new(provider.ToString(), provider.Quotes, false)); + + // assert: this just has to not fail, really + + Console.WriteLine($"Subscribers: {subscribers.Count}"); + Console.WriteLine("--------------------"); + + // assert: all non-irregular subscribers have the same count + foreach ((string label, IReadOnlyList results, bool irregular) in subscribers) + { + int resultQty = results.Count; + Console.WriteLine($"Hub: {resultQty} - {label}"); + if (irregular) { continue; } + resultQty.Should().Be(qtyQuotes); + } + + // assert: [last subscriber] has the same dates + IReadOnlyList lastSubscriber = subscribers[^1].results.ToList(); + for (int i = 0; i < qtyQuotes; i++) + { + Quote q = quotesList[i]; + ISeries r = lastSubscriber[i]; + r.Timestamp.Should().Be(q.Timestamp); + } + + // act: clear provider cache (cascades to subscribers) + int cutoff = qtyQuotes / 2; + provider.RemoveRange(cutoff, notify: true); + + provider.Quotes.Count.Should().Be(cutoff); + + Console.WriteLine("--------------------"); + + // assert: all have same count + foreach ((string label, IReadOnlyList results, bool irregular) in subscribers) + { + int resultQty = results.Count; + Console.WriteLine($"Cut: {resultQty} - {label}"); + if (irregular) { continue; } + resultQty.Should().Be(cutoff); + } + } + + [TestMethod] + public void ManyChainDepths() + { + // goal: test that a massive chain where each new subscriber + // subscribes to the next creating a really long chain + // of observers, without stack overflow + + int qtyQuotes = 10000; + int chainDepth = 500; + + // setup: many random quotes (massive) + IReadOnlyList quotesList = Data.GetRandom(qtyQuotes); + + QuoteHub provider = new(); + + // setup: subscribe a large chain depth + List<(string label, IReadOnlyList results, bool irregular)> subscribers = new(chainDepth + 2); + + SmaHub init = provider.ToSma(1); + SmaHub sma = init.ToSma(2); + + subscribers.Add(HubRef(init)); + subscribers.Add(HubRef(sma)); + + int lookbackPeriods = 1; + + // recursive providers + for (int i = 1; i <= chainDepth; i++) + { + sma = sma.ToSma(lookbackPeriods); + subscribers.Add(HubRef(sma)); + + lookbackPeriods = lookbackPeriods is 2 ? 1 : 2; + } + + // act: add quotes + for (int i = 0; i < qtyQuotes; i++) + { + provider.Add(quotesList[i]); + } + + subscribers.Insert(0, new(provider.ToString(), provider.Quotes, false)); + + Console.WriteLine($"Subscribers: {subscribers.Count}"); + Console.WriteLine("--------------------"); + + // assert: this just has to not fail, really + + // assert: all non-irregular subscribers have the same count + foreach ((string label, IReadOnlyList results, bool irregular) in subscribers) + { + int resultQty = results.Count; + Console.WriteLine($"Hub: {resultQty} - {label}"); + if (irregular) { continue; } + resultQty.Should().Be(qtyQuotes); + } + + // assert: [last subscriber] has the same dates + IReadOnlyList lastSubscriber = subscribers[^1].results.ToList(); + for (int i = 0; i < qtyQuotes; i++) + { + Quote q = quotesList[i]; + ISeries r = lastSubscriber[i]; + r.Timestamp.Should().Be(q.Timestamp); + } + + // act: clear provider cache (cascades to subscribers) + int cutoff = qtyQuotes / 2; + provider.RemoveRange(cutoff, notify: true); + + provider.Quotes.Count.Should().Be(cutoff); + + // assert: all have same count + foreach ((string label, IReadOnlyList results, bool irregular) in subscribers) + { + int resultQty = results.Count; + Console.WriteLine($"Cut: {resultQty} - {label}"); + if (irregular) { continue; } + resultQty.Should().Be(cutoff); + } + } + + [TestMethod] + public void ManySubscribers() + { + // goal: test that many indictors (all at once) + // can subscribe to the same quote provider + // without stack overflow; ~350 subscribers + + int qtyQuotes = 5000; + + // setup: many random quotes + IReadOnlyList quotesList = Data.GetRandom(qtyQuotes); + + QuoteHub provider = new(); + + // setup: define all possible subscribers + // TODO: add to this as more Hubs come online + List<(string label, IReadOnlyList results, bool irregular)> subscribers = new() + { + HubRef(provider.ToAdl()), + HubRef(provider.ToAlligator()), + HubRef(provider.ToEma(14)), + //HubRef(provider.ToRenko(2.1m), irregular: true), + HubRef(provider.ToQuote()) + }; + + // all QuoteParts + foreach (CandlePart candlePart in Enum.GetValues()) + { + subscribers.Add(HubRef(provider.ToQuotePart(candlePart))); + } + + // many SMAs + for (int i = 1; i <= 300; i++) + { + subscribers.Add(HubRef(provider.ToSma(i))); + } + + // act: add quotes + for (int i = 0; i < qtyQuotes; i++) + { + provider.Add(quotesList[i]); + } + + subscribers.Insert(0, new(provider.ToString(), provider.Quotes, false)); + + // assert: this just has to not fail, really + + Console.WriteLine($"Subscribers: {subscribers.Count}"); + Console.WriteLine("--------------------"); + + // assert: all non-irregular subscribers have the same count + foreach ((string label, IReadOnlyList results, bool irregular) in subscribers) + { + int resultQty = results.Count; + Console.WriteLine($"Hub: {resultQty} - {label}"); + if (irregular) { continue; } + resultQty.Should().Be(qtyQuotes); + } + + // assert: [last subscriber] has the same dates + IReadOnlyList lastSubscriber = subscribers[^1].results.ToList(); + for (int i = 0; i < qtyQuotes; i++) + { + Quote q = quotesList[i]; + ISeries r = lastSubscriber[i]; + r.Timestamp.Should().Be(q.Timestamp); + } + + // act: clear provider cache (cascades to subscribers) + int cutoff = qtyQuotes / 2; + provider.RemoveRange(cutoff, notify: true); + + provider.Quotes.Count.Should().Be(cutoff); + + Console.WriteLine("--------------------"); + + // assert: all have same count + foreach ((string label, IReadOnlyList results, bool irregular) in subscribers) + { + int resultQty = results.Count; + Console.WriteLine($"Cut: {resultQty} - {label}"); + if (irregular) { continue; } + resultQty.Should().Be(cutoff); + } + } + + /// + /// Utility to get references to a hub's results. + /// + private static (string, IReadOnlyList, bool) HubRef( + StreamHub hub, bool irregular = false) + where TIn : ISeries + where TOut : ISeries + { + IReadOnlyList results = hub.Cache; + return (hub.ToString(), results, irregular); + } +} diff --git a/tests/indicators/_common/Observables/StreamHub.Utilities.StaticSeries.Tests.cs b/tests/indicators/_common/Observables/StreamHub.Utilities.StaticSeries.Tests.cs new file mode 100644 index 000000000..17e87a1dd --- /dev/null +++ b/tests/indicators/_common/Observables/StreamHub.Utilities.StaticSeries.Tests.cs @@ -0,0 +1,189 @@ +namespace Observables; + +[TestClass] +public class CacheUtilities : TestBase +{ + [TestMethod] + public void ClearCacheByTimestamp() + { + + // setup quote provider + + IReadOnlyList quotesList = Quotes + .Take(10) + .ToList(); + + int length = quotesList.Count; + + QuoteHub provider = new(); + + QuotePartHub observer = provider + .ToQuotePart(CandlePart.Close); + + for (int i = 0; i < length; i++) + { + provider.Add(quotesList[i]); + } + + Quote q3 = quotesList[3]; + + // act: clear cache + observer.RemoveRange(q3.Timestamp, notify: false); + + // assert: cache is empty + observer.Cache.Should().HaveCount(3); + provider.Cache.Should().HaveCount(10); + + List cacheOver + = observer.Results + .Where(c => c.Timestamp >= q3.Timestamp).ToList(); + + List cacheUndr + = observer.Results + .Where(c => c.Timestamp <= q3.Timestamp).ToList(); + + cacheOver.Should().BeEmpty(); + cacheUndr.Should().HaveCount(3); + } + + [TestMethod] + public void ClearCacheByIndex() + { + // setup quote provider + + List quotesList = Quotes + .ToSortedList() + .Take(10) + .ToList(); + + int length = quotesList.Count; + + QuoteHub provider = new(); + + QuotePartHub observer = provider + .ToQuotePart(CandlePart.Close); + + for (int i = 0; i < length; i++) + { + provider.Add(quotesList[i]); + } + + Quote q3 = quotesList[3]; + + // act: clear cache + observer.RemoveRange(3, notify: true); + + // assert: cache is empty + observer.Cache.Should().HaveCount(3); + provider.Cache.Should().HaveCount(10); + + List cacheOver + = observer.Results + .Where(c => c.Timestamp >= q3.Timestamp).ToList(); + + List cacheUndr + = observer.Results + .Where(c => c.Timestamp <= q3.Timestamp).ToList(); + + cacheOver.Should().BeEmpty(); + cacheUndr.Should().HaveCount(3); + } + + [TestMethod] + public void GetIndex() + { + // setup quote provider + + IReadOnlyList quotesList = Quotes + .Take(10) + .ToList(); + + int length = quotesList.Count; + + QuoteHub provider = new(); + + for (int i = 0; i < length; i++) + { + provider.Add(quotesList[i]); + } + + // find position of quote + Quote q = quotesList[4]; + + int itemIndexEx = provider.Cache.GetIndex(q, true); + int timeIndexEx = provider.Cache.GetIndex(q.Timestamp, true); + + // assert: same index + itemIndexEx.Should().Be(4); + timeIndexEx.Should().Be(4); + + // out of range (exceptions) + Quote o = Quotes[10]; + + Assert.ThrowsException(() => { + provider.Cache.GetIndex(o, true); + }); + + Assert.ThrowsException(() => { + provider.Cache.GetIndex(o.Timestamp, true); + }); + + // out of range (no exceptions) + int itemIndexNo = provider.Cache.GetIndex(o, false); + int timeIndexNo = provider.Cache.GetIndex(o.Timestamp, false); + + itemIndexNo.Should().Be(-1); + timeIndexNo.Should().Be(-1); + + int timeInsertOut = provider.Cache.GetIndexGte(o.Timestamp); + int timeInsertIn = provider.Cache.GetIndexGte(quotesList[2].Timestamp); + + timeInsertOut.Should().Be(-1); + timeInsertIn.Should().Be(2); + } + + [TestMethod] + public void TryFindIndex() + { + + // setup quote provider + + List quotesList = Quotes + .ToSortedList() + .Take(10) + .ToList(); + + int length = quotesList.Count; + + QuoteHub provider = new(); + + for (int i = 0; i < length; i++) + { + provider.Add(quotesList[i]); + } + + Quote q = quotesList[4]; + + // act: find index of quote + + // assert: correct index + if (provider.Cache.TryFindIndex(q.Timestamp, out int goodIndex)) + { + goodIndex.Should().Be(4); + } + else + { + Assert.Fail("index not found"); + } + + // assert: out of range + if (provider.Cache.TryFindIndex(DateTime.MaxValue, out int badIndex)) + { + Assert.Fail("unexpected index found"); + } + else + { + badIndex.Should().Be(-1); + } + } +} diff --git a/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs b/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs index 44a79bd47..8fa697629 100644 --- a/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs @@ -1,24 +1,24 @@ -namespace Tests.Common; +namespace Utilities; -[TestClass] -public class QuoteAggregateTests : TestBase +// quote aggregates + +public partial class Quotes : TestBase { [TestMethod] public void Aggregate() { - IEnumerable quotes = TestData.GetIntraday(); + IReadOnlyList quotes = Data.GetIntraday(); // aggregate - List results = quotes - .Aggregate(PeriodSize.FifteenMinutes) - .ToList(); + IReadOnlyList results = quotes + .Aggregate(PeriodSize.FifteenMinutes); // proper quantities Assert.AreEqual(108, results.Count); // sample values Quote r0 = results[0]; - Assert.AreEqual(DateTime.Parse("2020-12-15 09:30", EnglishCulture), r0.Date); + Assert.AreEqual(DateTime.Parse("2020-12-15 09:30", TestBase.invariantCulture), r0.Timestamp); Assert.AreEqual(367.40m, r0.Open); Assert.AreEqual(367.775m, r0.High); Assert.AreEqual(367.02m, r0.Low); @@ -26,7 +26,7 @@ public void Aggregate() Assert.AreEqual(2401786m, r0.Volume); Quote r1 = results[1]; - Assert.AreEqual(DateTime.Parse("2020-12-15 09:45", EnglishCulture), r1.Date); + Assert.AreEqual(DateTime.Parse("2020-12-15 09:45", TestBase.invariantCulture), r1.Timestamp); Assert.AreEqual(367.25m, r1.Open); Assert.AreEqual(367.44m, r1.High); Assert.AreEqual(366.69m, r1.Low); @@ -34,7 +34,7 @@ public void Aggregate() Assert.AreEqual(1669983m, r1.Volume); Quote r2 = results[2]; - Assert.AreEqual(DateTime.Parse("2020-12-15 10:00", EnglishCulture), r2.Date); + Assert.AreEqual(DateTime.Parse("2020-12-15 10:00", TestBase.invariantCulture), r2.Timestamp); Assert.AreEqual(366.85m, r2.Open); Assert.AreEqual(367.17m, r2.High); Assert.AreEqual(366.57m, r2.Low); @@ -42,27 +42,26 @@ public void Aggregate() Assert.AreEqual(1396993m, r2.Volume); // no history scenario - List noQuotes = []; - IEnumerable noResults = noQuotes.Aggregate(PeriodSize.Day); + IReadOnlyList noQuotes = []; + IReadOnlyList noResults = noQuotes.Aggregate(PeriodSize.Day); Assert.IsFalse(noResults.Any()); } [TestMethod] public void AggregateTimeSpan() { - IEnumerable quotes = TestData.GetIntraday(); + IReadOnlyList quotes = Data.GetIntraday(); // aggregate - List results = quotes - .Aggregate(TimeSpan.FromMinutes(15)) - .ToList(); + IReadOnlyList results = quotes + .Aggregate(TimeSpan.FromMinutes(15)); // proper quantities Assert.AreEqual(108, results.Count); // sample values Quote r0 = results[0]; - Assert.AreEqual(DateTime.Parse("2020-12-15 09:30", EnglishCulture), r0.Date); + Assert.AreEqual(DateTime.Parse("2020-12-15 09:30", TestBase.invariantCulture), r0.Timestamp); Assert.AreEqual(367.40m, r0.Open); Assert.AreEqual(367.775m, r0.High); Assert.AreEqual(367.02m, r0.Low); @@ -70,7 +69,7 @@ public void AggregateTimeSpan() Assert.AreEqual(2401786m, r0.Volume); Quote r1 = results[1]; - Assert.AreEqual(DateTime.Parse("2020-12-15 09:45", EnglishCulture), r1.Date); + Assert.AreEqual(DateTime.Parse("2020-12-15 09:45", TestBase.invariantCulture), r1.Timestamp); Assert.AreEqual(367.25m, r1.Open); Assert.AreEqual(367.44m, r1.High); Assert.AreEqual(366.69m, r1.Low); @@ -78,7 +77,7 @@ public void AggregateTimeSpan() Assert.AreEqual(1669983m, r1.Volume); Quote r2 = results[2]; - Assert.AreEqual(DateTime.Parse("2020-12-15 10:00", EnglishCulture), r2.Date); + Assert.AreEqual(DateTime.Parse("2020-12-15 10:00", TestBase.invariantCulture), r2.Timestamp); Assert.AreEqual(366.85m, r2.Open); Assert.AreEqual(367.17m, r2.High); Assert.AreEqual(366.57m, r2.Low); @@ -86,8 +85,8 @@ public void AggregateTimeSpan() Assert.AreEqual(1396993m, r2.Volume); // no history scenario - List noQuotes = []; - IEnumerable noResults = noQuotes.Aggregate(TimeSpan.FromDays(1)); + IReadOnlyList noQuotes = []; + IReadOnlyList noResults = noQuotes.Aggregate(TimeSpan.FromDays(1)); Assert.IsFalse(noResults.Any()); } @@ -95,16 +94,15 @@ public void AggregateTimeSpan() public void AggregateMonth() { // aggregate - List results = quotes - .Aggregate(PeriodSize.Month) - .ToList(); + IReadOnlyList results = Quotes + .Aggregate(PeriodSize.Month); // proper quantities Assert.AreEqual(24, results.Count); // sample values Quote r0 = results[0]; - Assert.AreEqual(DateTime.Parse("2017-01-01", EnglishCulture), r0.Date); + Assert.AreEqual(DateTime.Parse("2017-01-01", TestBase.invariantCulture), r0.Timestamp); Assert.AreEqual(212.61m, r0.Open); Assert.AreEqual(217.02m, r0.High); Assert.AreEqual(211.52m, r0.Low); @@ -112,7 +110,7 @@ public void AggregateMonth() Assert.AreEqual(1569087580m, r0.Volume); Quote r1 = results[1]; - Assert.AreEqual(DateTime.Parse("2017-02-01", EnglishCulture), r1.Date); + Assert.AreEqual(DateTime.Parse("2017-02-01", TestBase.invariantCulture), r1.Timestamp); Assert.AreEqual(215.65m, r1.Open); Assert.AreEqual(224.20m, r1.High); Assert.AreEqual(214.29m, r1.Low); @@ -120,7 +118,7 @@ public void AggregateMonth() Assert.AreEqual(1444958340m, r1.Volume); Quote r23 = results[23]; - Assert.AreEqual(DateTime.Parse("2018-12-01", EnglishCulture), r23.Date); + Assert.AreEqual(DateTime.Parse("2018-12-01", TestBase.invariantCulture), r23.Timestamp); Assert.AreEqual(273.47m, r23.Open); Assert.AreEqual(273.59m, r23.High); Assert.AreEqual(229.42m, r23.Low); @@ -128,10 +126,8 @@ public void AggregateMonth() Assert.AreEqual(3173255968m, r23.Volume); } - [TestMethod] - [ExpectedException(typeof(ArgumentOutOfRangeException), "Bad aggregation size.")] - public void BadAggregationSize() => - - // bad period size - quotes.Aggregate(TimeSpan.Zero); + [TestMethod] // bad period size + public void AggregateBadSize() + => Assert.ThrowsException(() + => Quotes.Aggregate(TimeSpan.Zero)); } diff --git a/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs b/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs index e0cb88630..8d837fe98 100644 --- a/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs @@ -1,265 +1,32 @@ using System.Collections.ObjectModel; -namespace Tests.Common; +// quote list converters + +namespace Utilities; [TestClass] -public class QuoteUtilityTests : TestBase +public partial class Quotes : TestBase { [TestMethod] - public void QuoteToSortedCollection() - { - IEnumerable quotes = TestData.GetMismatch(); - - Collection h = quotes.ToSortedCollection(); - - // proper quantities - Assert.AreEqual(502, h.Count); - - // check first date - DateTime firstDate = DateTime.ParseExact("01/18/2016", "MM/dd/yyyy", EnglishCulture); - Assert.AreEqual(firstDate, h[0].Date); - - // check last date - DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", EnglishCulture); - Assert.AreEqual(lastDate, h.LastOrDefault().Date); - - // spot check an out of sequence date - DateTime spotDate = DateTime.ParseExact("03/16/2017", "MM/dd/yyyy", EnglishCulture); - Assert.AreEqual(spotDate, h[50].Date); - } - - [TestMethod] - public void QuoteToSortedList() + public void ToSortedList() { - IEnumerable quotes = TestData.GetMismatch(); + IReadOnlyList quotes = Data.GetMismatch(); - List h = quotes.ToSortedList(); + IReadOnlyList h = quotes.ToSortedList(); // proper quantities Assert.AreEqual(502, h.Count); // check first date - DateTime firstDate = DateTime.ParseExact("01/18/2016", "MM/dd/yyyy", EnglishCulture); - Assert.AreEqual(firstDate, h[0].Date); + DateTime firstDate = DateTime.ParseExact("01/18/2016", "MM/dd/yyyy", TestBase.invariantCulture); + Assert.AreEqual(firstDate, h[0].Timestamp); // check last date - DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", EnglishCulture); - Assert.AreEqual(lastDate, h.LastOrDefault().Date); + DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", TestBase.invariantCulture); + Assert.AreEqual(lastDate, h[^1].Timestamp); // spot check an out of sequence date - DateTime spotDate = DateTime.ParseExact("03/16/2017", "MM/dd/yyyy", EnglishCulture); - Assert.AreEqual(spotDate, h[50].Date); - } - - [TestMethod] - public void QuoteToTuple() - { - DateTime d = DateTime.Parse("5/5/2055", EnglishCulture); - - decimal l = 111111111111111m; - decimal o = 222222222222222m; - decimal c = 333333333333333m; - decimal h = 444444444444444m; - decimal v = 555555555555555m; - decimal hl2 = (h + l) / 2m; - decimal hlc3 = (h + l + c) / 3m; - decimal oc2 = (o + c) / 2m; - decimal ohl3 = (o + h + l) / 3m; - decimal ohlc4 = (o + h + l + c) / 4m; - - Quote q = new() { - Date = d, - Open = o, - High = h, - Low = l, - Close = c, - Volume = v - }; - - Assert.AreEqual( - NullMath.Round((double)o, 10), - NullMath.Round(q.ToTuple(CandlePart.Open).value, 10)); - Assert.AreEqual( - NullMath.Round((double)h, 10), - NullMath.Round(q.ToTuple(CandlePart.High).value, 10)); - Assert.AreEqual( - NullMath.Round((double)l, 10), - NullMath.Round(q.ToTuple(CandlePart.Low).value, 10)); - Assert.AreEqual( - NullMath.Round((double)c, 10), - NullMath.Round(q.ToTuple(CandlePart.Close).value, 10)); - Assert.AreEqual( - NullMath.Round((double)v, 10), - NullMath.Round(q.ToTuple(CandlePart.Volume).value, 10)); - Assert.AreEqual( - NullMath.Round((double)hl2, 10), - NullMath.Round(q.ToTuple(CandlePart.HL2).value, 10)); - Assert.AreEqual( - NullMath.Round((double)hlc3, 10), - NullMath.Round(q.ToTuple(CandlePart.HLC3).value, 10)); - Assert.AreEqual( - NullMath.Round((double)oc2, 10), - NullMath.Round(q.ToTuple(CandlePart.OC2).value, 10)); - Assert.AreEqual( - NullMath.Round((double)ohl3, 10), - NullMath.Round(q.ToTuple(CandlePart.OHL3).value, 10)); - Assert.AreEqual( - NullMath.Round((double)ohlc4, 10), - NullMath.Round(q.ToTuple(CandlePart.OHLC4).value, 10)); - - // bad argument - Assert.ThrowsException(() - => q.ToTuple((CandlePart)999)); - - // bad argument - Assert.ThrowsException(() - => q.ToBasicData((CandlePart)999)); - } - - [TestMethod] - public void ToTupleCollection() - { - Collection<(DateTime, double)> collection = quotes - .OrderBy(x => x.Date) - .ToTupleCollection(CandlePart.Close); - - Assert.IsNotNull(collection); - Assert.AreEqual(502, collection.Count); - Assert.AreEqual(collection.LastOrDefault().Item2, 245.28d); - } - - [TestMethod] - public void ToSortedList() - { - Collection<(DateTime, double)> collection = quotes - .OrderBy(x => x.Date) - .ToTuple(CandlePart.Close) - .ToSortedCollection(); - - Assert.IsNotNull(collection); - Assert.AreEqual(502, collection.Count); - Assert.AreEqual(collection.LastOrDefault().Item2, 245.28d); - } - - [TestMethod] - public void QuoteToBasicData() - { - DateTime d = DateTime.Parse("5/5/2055", EnglishCulture); - - decimal l = 111111111111111m; - decimal o = 222222222222222m; - decimal c = 333333333333333m; - decimal h = 444444444444444m; - decimal v = 555555555555555m; - decimal hl2 = (h + l) / 2m; - decimal hlc3 = (h + l + c) / 3m; - decimal oc2 = (o + c) / 2m; - decimal ohl3 = (o + h + l) / 3m; - decimal ohlc4 = (o + h + l + c) / 4m; - - Quote q = new() { - Date = d, - Open = o, - High = h, - Low = l, - Close = c, - Volume = v - }; - - Assert.AreEqual( - NullMath.Round((double)o, 10), - NullMath.Round(q.ToBasicData(CandlePart.Open).Value, 10)); - Assert.AreEqual( - NullMath.Round((double)h, 10), - NullMath.Round(q.ToBasicData(CandlePart.High).Value, 10)); - Assert.AreEqual( - NullMath.Round((double)l, 10), - NullMath.Round(q.ToBasicData(CandlePart.Low).Value, 10)); - Assert.AreEqual( - NullMath.Round((double)c, 10), - NullMath.Round(q.ToBasicData(CandlePart.Close).Value, 10)); - Assert.AreEqual( - NullMath.Round((double)v, 10), - NullMath.Round(q.ToBasicData(CandlePart.Volume).Value, 10)); - Assert.AreEqual( - NullMath.Round((double)hl2, 10), - NullMath.Round(q.ToBasicData(CandlePart.HL2).Value, 10)); - Assert.AreEqual( - NullMath.Round((double)hlc3, 10), - NullMath.Round(q.ToBasicData(CandlePart.HLC3).Value, 10)); - Assert.AreEqual( - NullMath.Round((double)oc2, 10), - NullMath.Round(q.ToBasicData(CandlePart.OC2).Value, 10)); - Assert.AreEqual( - NullMath.Round((double)ohl3, 10), - NullMath.Round(q.ToBasicData(CandlePart.OHL3).Value, 10)); - Assert.AreEqual( - NullMath.Round((double)ohlc4, 10), - NullMath.Round(q.ToBasicData(CandlePart.OHLC4).Value, 10)); - - // bad argument - Assert.ThrowsException(() - => q.ToBasicData((CandlePart)999)); - } - - [TestMethod] - public void QuoteDToTuple() - { - DateTime d = DateTime.Parse("5/5/2055", EnglishCulture); - - double l = 111111111111111; - double o = 222222222222222; - double c = 333333333333333; - double h = 444444444444444; - double v = 555555555555555; - double hl2 = (h + l) / 2; - double hlc3 = (h + l + c) / 3; - double oc2 = (o + c) / 2; - double ohl3 = (o + h + l) / 3; - double ohlc4 = (o + h + l + c) / 4; - - QuoteD q = new() { - Date = d, - Open = o, - High = h, - Low = l, - Close = c, - Volume = v - }; - - Assert.AreEqual( - NullMath.Round((double)o, 10), - NullMath.Round(q.ToTuple(CandlePart.Open).Item2, 10)); - Assert.AreEqual( - NullMath.Round((double)h, 10), - NullMath.Round(q.ToTuple(CandlePart.High).Item2, 10)); - Assert.AreEqual( - NullMath.Round((double)l, 10), - NullMath.Round(q.ToTuple(CandlePart.Low).Item2, 10)); - Assert.AreEqual( - NullMath.Round((double)c, 10), - NullMath.Round(q.ToTuple(CandlePart.Close).Item2, 10)); - Assert.AreEqual( - NullMath.Round((double)v, 10), - NullMath.Round(q.ToTuple(CandlePart.Volume).Item2, 10)); - Assert.AreEqual( - NullMath.Round((double)hl2, 10), - NullMath.Round(q.ToTuple(CandlePart.HL2).Item2, 10)); - Assert.AreEqual( - NullMath.Round((double)hlc3, 10), - NullMath.Round(q.ToTuple(CandlePart.HLC3).Item2, 10)); - Assert.AreEqual( - NullMath.Round((double)oc2, 10), - NullMath.Round(q.ToTuple(CandlePart.OC2).Item2, 10)); - Assert.AreEqual( - NullMath.Round((double)ohl3, 10), - NullMath.Round(q.ToTuple(CandlePart.OHL3).Item2, 10)); - Assert.AreEqual( - NullMath.Round((double)ohlc4, 10), - NullMath.Round(q.ToTuple(CandlePart.OHLC4).Item2, 10)); - - // bad argument - Assert.ThrowsException(() - => q.ToTuple((CandlePart)999)); + DateTime spotDate = DateTime.ParseExact("03/16/2017", "MM/dd/yyyy", TestBase.invariantCulture); + Assert.AreEqual(spotDate, h[50].Timestamp); } } diff --git a/tests/indicators/_common/Quotes/Quote.Equality.Tests.cs b/tests/indicators/_common/Quotes/Quote.Equality.Tests.cs new file mode 100644 index 000000000..0ebbe8621 --- /dev/null +++ b/tests/indicators/_common/Quotes/Quote.Equality.Tests.cs @@ -0,0 +1,26 @@ +namespace Utilities; + +// quotes equality + +public partial class Quotes : TestBase +{ + [TestMethod] + public void Equality() + { + Quote q1 = new(EvalDate, 1m, 1m, 1m, 1m, 100); + Quote q2 = new(EvalDate, 1m, 1m, 1m, 1m, 100); + Quote q3 = new(EvalDate, 1m, 1m, 1m, 2m, 99); + + Assert.IsTrue(Equals(q1, q2)); + Assert.IsFalse(Equals(q1, q3)); + + Assert.IsTrue(q1.Equals(q2)); + Assert.IsFalse(q1.Equals(q3)); + + Assert.IsTrue(q1 == q2); + Assert.IsFalse(q1 == q3); + + Assert.IsFalse(q1 != q2); + Assert.IsTrue(q1 != q3); + } +} diff --git a/tests/indicators/_common/Quotes/Quote.Exceptions.Tests.cs b/tests/indicators/_common/Quotes/Quote.Exceptions.Tests.cs index 5a546b482..48ba52573 100644 --- a/tests/indicators/_common/Quotes/Quote.Exceptions.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Exceptions.Tests.cs @@ -1,21 +1,21 @@ -namespace Tests.Common; +namespace Utilities; -[TestClass] -public class ExceptionTests : TestBase +// invalid quotes exceptions + +public partial class Quotes : TestBase { - // bad quotes exceptions [TestMethod] - [ExpectedException(typeof(InvalidQuotesException), "Bad quotes without message.")] - public void BadHistory() - => throw new InvalidQuotesException(); + public void InvalidQuotesExceptionThrow() + => Assert.ThrowsException(() + => throw new InvalidQuotesException()); [TestMethod] - [ExpectedException(typeof(InvalidQuotesException), "Bad quotes with message.")] - public void BadHistoryWithMessage() - => throw new InvalidQuotesException("This is a quotes exception."); + public void InvalidQuotesExceptionThrowWithMessage() + => Assert.ThrowsException(() + => throw new InvalidQuotesException("This is a quotes exception.")); [TestMethod] - [ExpectedException(typeof(InvalidQuotesException), "Bad quotes with inner Exception.")] - public void BadHistoryWithInner() - => throw new InvalidQuotesException("This has an inner Exception.", new ArgumentException()); + public void InvalidQuotesExceptionThrowWithInner() + => Assert.ThrowsException(() + => throw new InvalidQuotesException("This has an inner Exception.", new ArgumentException())); } diff --git a/tests/indicators/_common/Quotes/Quote.StreamHub.Tests.cs b/tests/indicators/_common/Quotes/Quote.StreamHub.Tests.cs new file mode 100644 index 000000000..babf4c1c3 --- /dev/null +++ b/tests/indicators/_common/Quotes/Quote.StreamHub.Tests.cs @@ -0,0 +1,134 @@ +namespace StreamHub; + +// QUOTEHUB + +[TestClass] +public class QuoteHub : StreamHubTestBase, ITestChainProvider +{ + [TestMethod] + public override void QuoteObserver() + { + // tests quote redistribution + + List quotesList = Quotes.ToList(); + + int length = Quotes.Count; + + // add base quotes (batch) + QuoteHub provider = new(); + + provider.Add(quotesList.Take(200)); + + // add incremental quotes + for (int i = 200; i < length; i++) + { + Quote q = quotesList[i]; + provider.Add(q); + } + + QuoteHub observer + = provider.ToQuote(); + + // close observations + provider.EndTransmission(); + + // assert same as original + observer.Cache.Should().HaveCount(length); + observer.Cache.Should().BeEquivalentTo(provider.Cache); + } + + [TestMethod] + public void ChainProvider() + { + int smaPeriods = 8; + + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // initialize observer + SmaHub observer + = provider + .ToSma(smaPeriods); + + // emulate quote stream + for (int i = 0; i < length; i++) + { + provider.Add(quotesList[i]); + } + + // delete + provider.RemoveAt(400); + quotesList.RemoveAt(400); + + // final results + IReadOnlyList streamList + = observer.Results; + + // time-series, for comparison + IReadOnlyList seriesList + = quotesList + .ToSma(smaPeriods); + + // assert, should equal series + streamList.Should().HaveCount(length - 1); + streamList.Should().BeEquivalentTo(seriesList); + + // cleanup + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public override void CustomToString() + { + QuoteHub hub = new(); + + hub.ToString().Should().Be("QUOTES: 0 items"); + + hub.Add(Quotes[0]); + hub.Add(Quotes[1]); + + hub.ToString().Should().Be("QUOTES: 2 items"); + } + + [TestMethod] + public void AddQuote() + { + // covers both single and batch add + + List quotesList = Quotes.ToList(); + + int length = Quotes.Count; + + // add base quotes (batch) + QuoteHub provider = new(); + + provider.Add(quotesList.Take(200)); + + // add incremental quotes + for (int i = 200; i < length; i++) + { + Quote q = quotesList[i]; + provider.Add(q); + } + + // assert same as original + for (int i = 0; i < length; i++) + { + Quote o = quotesList[i]; + Quote q = provider.Cache[i]; + + Assert.AreEqual(o, q); // same ref + } + + // confirm public interfaces + Assert.AreEqual(provider.Cache.Count, provider.Quotes.Count); + + // close observations + provider.EndTransmission(); + } +} diff --git a/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs b/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs index 78d66cace..c1c54cac1 100644 --- a/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs @@ -1,37 +1,40 @@ -namespace Tests.Common; +using System.Globalization; -[TestClass] -public class QuoteValidationTests : TestBase +namespace Utilities; + +// quote validation + +public partial class Quotes : TestBase { [TestMethod] public void Validate() { - IEnumerable quotes = TestData.GetDefault(); + IReadOnlyList quotes = Data.GetDefault(); - List h = quotes.Validate().ToList(); + IReadOnlyList h = quotes.Validate(); // proper quantities Assert.AreEqual(502, h.Count); // sample values - DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", EnglishCulture); - Assert.AreEqual(lastDate, h[501].Date); + DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", invariantCulture); + Assert.AreEqual(lastDate, h[501].Timestamp); - DateTime spotDate = DateTime.ParseExact("02/01/2017", "MM/dd/yyyy", EnglishCulture); - Assert.AreEqual(spotDate, h[20].Date); + DateTime spotDate = DateTime.ParseExact("02/01/2017", "MM/dd/yyyy", invariantCulture); + Assert.AreEqual(spotDate, h[20].Timestamp); } [TestMethod] public void ValidateLong() { - List h = longishQuotes.Validate().ToList(); + IReadOnlyList h = LongishQuotes.Validate(); // proper quantities Assert.AreEqual(5285, h.Count); // sample values - DateTime lastDate = DateTime.ParseExact("09/04/2020", "MM/dd/yyyy", EnglishCulture); - Assert.AreEqual(lastDate, h[5284].Date); + DateTime lastDate = DateTime.ParseExact("09/04/2020", "MM/dd/yyyy", invariantCulture); + Assert.AreEqual(lastDate, h[5284].Timestamp); } [TestMethod] @@ -39,28 +42,29 @@ public void ValidateCut() { // if quotes post-cleaning, is cut down in size it should not corrupt the results - IEnumerable quotes = TestData.GetDefault(200); - List h = quotes.Validate().ToList(); + IReadOnlyList quotes = Data.GetDefault(200); + + IReadOnlyList h = quotes.Validate(); // should be 200 periods, initially Assert.AreEqual(200, h.Count); // should be 20 results and no index corruption - List r1 = Indicator.GetSma(h.TakeLast(20), 14).ToList(); + IReadOnlyList r1 = h.TakeLast(20).ToList().ToSma(14).ToList(); Assert.AreEqual(20, r1.Count); for (int i = 1; i < r1.Count; i++) { - Assert.IsTrue(r1[i].Date >= r1[i - 1].Date); + Assert.IsTrue(r1[i].Timestamp >= r1[i - 1].Timestamp); } // should be 50 results and no index corruption - List r2 = Indicator.GetSma(h.TakeLast(50), 14).ToList(); + IReadOnlyList r2 = h.TakeLast(50).ToList().ToSma(14).ToList(); Assert.AreEqual(50, r2.Count); for (int i = 1; i < r2.Count; i++) { - Assert.IsTrue(r2[i].Date >= r2[i - 1].Date); + Assert.IsTrue(r2[i].Timestamp >= r2[i - 1].Timestamp); } // should be original 200 periods and no index corruption, after temp mods @@ -68,26 +72,46 @@ public void ValidateCut() for (int i = 1; i < h.Count; i++) { - Assert.IsTrue(h[i].Date >= h[i - 1].Date); + Assert.IsTrue(h[i].Timestamp >= h[i - 1].Timestamp); } } [TestMethod] - public void DuplicateHistory() + public void ValidateDuplicates() { - List badHistory = - [ - new Quote { Date = DateTime.ParseExact("2017-01-03", "yyyy-MM-dd", EnglishCulture), Open = 214.86m, High = 220.33m, Low = 210.96m, Close = 216.99m, Volume = 5923254 }, - new Quote { Date = DateTime.ParseExact("2017-01-04", "yyyy-MM-dd", EnglishCulture), Open = 214.75m, High = 228.00m, Low = 214.31m, Close = 226.99m, Volume = 11213471 }, - new Quote { Date = DateTime.ParseExact("2017-01-05", "yyyy-MM-dd", EnglishCulture), Open = 226.42m, High = 227.48m, Low = 221.95m, Close = 226.75m, Volume = 5911695 }, - new Quote { Date = DateTime.ParseExact("2017-01-06", "yyyy-MM-dd", EnglishCulture), Open = 226.93m, High = 230.31m, Low = 225.45m, Close = 229.01m, Volume = 5527893 }, - new Quote { Date = DateTime.ParseExact("2017-01-06", "yyyy-MM-dd", EnglishCulture), Open = 228.97m, High = 231.92m, Low = 228.00m, Close = 231.28m, Volume = 3979484 } - ]; - - InvalidQuotesException ex = - Assert.ThrowsException(() - => badHistory.Validate()); - - ex.Message.Should().Contain("Duplicate date found on 2017-01-06T00:00:00.0000000."); + IReadOnlyList dupQuotes = new List + { + new(Timestamp: DateTime.ParseExact("2017-01-03", "yyyy-MM-dd", invariantCulture), Open: 214.86m, High: 220.33m, Low: 210.96m, Close: 216.99m, Volume: 5923254), + new(Timestamp: DateTime.ParseExact("2017-01-04", "yyyy-MM-dd", invariantCulture), Open: 214.75m, High: 228.00m, Low: 214.31m, Close: 226.99m, Volume: 11213471), + new(Timestamp: DateTime.ParseExact("2017-01-05", "yyyy-MM-dd", invariantCulture), Open: 226.42m, High: 227.48m, Low: 221.95m, Close: 226.75m, Volume: 5911695), + new(Timestamp: DateTime.ParseExact("2017-01-06", "yyyy-MM-dd", invariantCulture), Open: 226.93m, High: 230.31m, Low: 225.45m, Close: 229.01m, Volume: 5527893), + new(Timestamp: DateTime.ParseExact("2017-01-06", "yyyy-MM-dd", invariantCulture), Open: 228.97m, High: 231.92m, Low: 228.00m, Close: 231.28m, Volume: 3979484) + }; + + InvalidQuotesException dx + = Assert.ThrowsException( + () => dupQuotes.Validate()); + + dx.Message.Should().Contain("Duplicate date found on 2017-01-06T00:00:00.0000000."); + } + + [TestMethod] + public void ValidateOutOfSequence() + { + IReadOnlyList unorderedQuotes = new List + { + new(Timestamp: DateTime.ParseExact("2017-01-03", "yyyy-MM-dd", invariantCulture), Open: 214.86m, High: 220.33m, Low: 210.96m, Close: 216.99m, Volume: 5923254), + new(Timestamp: DateTime.ParseExact("2017-01-04", "yyyy-MM-dd", invariantCulture), Open: 214.75m, High: 228.00m, Low: 214.31m, Close: 226.99m, Volume: 11213471), + new(Timestamp: DateTime.ParseExact("2017-01-06", "yyyy-MM-dd", invariantCulture), Open: 228.97m, High: 231.92m, Low: 228.00m, Close: 231.28m, Volume: 3979484), + new(Timestamp: DateTime.ParseExact("2017-01-05", "yyyy-MM-dd", invariantCulture), Open: 226.42m, High: 227.48m, Low: 221.95m, Close: 226.75m, Volume: 5911695), + new(Timestamp: DateTime.ParseExact("2017-01-06", "yyyy-MM-dd", invariantCulture), Open: 226.93m, High: 230.31m, Low: 225.45m, Close: 229.01m, Volume: 5527893) + }; + + InvalidQuotesException dx + = Assert.ThrowsException( + () => unorderedQuotes.Validate()); + + dx.Message.Should() + .Contain("Quotes are out of sequence on 2017-01-05T00:00:00.0000000."); } } diff --git a/tests/indicators/_common/Quotes/Test.Quote.Provider.cs b/tests/indicators/_common/Quotes/Test.Quote.Provider.cs deleted file mode 100644 index 64d0a8a4d..000000000 --- a/tests/indicators/_common/Quotes/Test.Quote.Provider.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; - -namespace Tests.Common; - -[TestClass] -public class QuoteSourceTests : TestBase -{ - [TestMethod] - public void Standard() - { - List quotesList = quotes - .ToSortedList(); - - int length = quotes.Count(); - - // add base quotes - QuoteProvider provider = new(); - provider.Add(quotesList.Take(200)); - - // emulate incremental quotes - for (int i = 200; i < length; i++) - { - Quote q = quotesList[i]; - provider.Add(q); - } - - // assert same as original - for (int i = 0; i < length; i++) - { - Quote o = quotesList[i]; - Quote q = provider.ProtectedQuotes[i]; - - Assert.AreEqual(o, q); - } - - provider.EndTransmission(); - } - - [TestMethod] - public void Exceptions() - { - // null quote added - QuoteProvider provider = new(); - - Assert.ThrowsException(() => - { - Quote quote = new() - { - Date = DateTime.Now - }; - - for (int i = 0; i <= 101; i++) - { - provider.Add(quote); - } - }); - - provider.EndTransmission(); - } -} diff --git a/tests/indicators/_common/Quotes/Test.Use.Observer.cs b/tests/indicators/_common/Quotes/Test.Use.Observer.cs deleted file mode 100644 index bf52f9217..000000000 --- a/tests/indicators/_common/Quotes/Test.Use.Observer.cs +++ /dev/null @@ -1,99 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - -namespace Tests.Indicators; - -[TestClass] -public class UseStreamTests : TestBase -{ - [TestMethod] - public void Standard() - { - List quotesList = quotes - .ToSortedList(); - - int length = quotesList.Count; - - // time-series, for comparison - List<(DateTime Date, double Value)> seriesList = quotes - .ToTuple(CandlePart.Close); - - // setup quote provider - QuoteProvider provider = new(); - - // initialize EMA observer - UseObserver observer = provider - .Use(CandlePart.Close); - - // fetch initial results - IEnumerable<(DateTime Date, double Value)> results - = observer.Results; - - // emulate adding quotes to provider - for (int i = 0; i < length; i++) - { - Quote q = quotesList[i]; - provider.Add(q); - } - - // final results - List<(DateTime Date, double Value)> resultsList - = results.ToList(); - - // assert, should equal series - for (int i = 0; i < seriesList.Count; i++) - { - (DateTime sDate, double sValue) = seriesList[i]; - (DateTime rDate, double rValue) = resultsList[i]; - - Assert.AreEqual(sDate, rDate); - Assert.AreEqual(sValue, rValue); - } - - observer.Unsubscribe(); - provider.EndTransmission(); - } - - [TestMethod] - public void Chainor() - { - List quotesList = quotes - .ToSortedList(); - - int length = quotesList.Count; - - // time-series, for comparison - List staticEma = quotes - .Use(CandlePart.HL2) - .GetEma(11) - .ToList(); - - // setup quote provider - QuoteProvider provider = new(); - - // initialize EMA observer - List streamEma = provider - .Use(CandlePart.HL2) - .GetEma(11) - .ProtectedResults; - - // emulate adding quotes to provider - for (int i = 0; i < length; i++) - { - provider.Add(quotesList[i]); - } - - provider.EndTransmission(); - - // assert, should equal series - for (int i = 0; i < length; i++) - { - EmaResult t = staticEma[i]; - EmaResult s = streamEma[i]; - - Assert.AreEqual(t.Date, s.Date); - Assert.AreEqual(t.Ema, s.Ema); - } - } -} diff --git a/tests/indicators/_common/Results/Result.Syncing.Tests.cs b/tests/indicators/_common/Results/Result.Syncing.Tests.cs deleted file mode 100644 index e22906cdb..000000000 --- a/tests/indicators/_common/Results/Result.Syncing.Tests.cs +++ /dev/null @@ -1,101 +0,0 @@ -namespace Tests.Common; - -[TestClass] -public class Syncing : TestBase -{ - [TestMethod] - public void SyncIndex() - { - // baseline for comparison - List baseline = - [ - new SmaResult(DateTime.Parse("1/1/2000", EnglishCulture)) { Sma = null }, - new SmaResult(DateTime.Parse("1/2/2000", EnglishCulture)) { Sma = null }, - new SmaResult(DateTime.Parse("1/3/2000", EnglishCulture)) { Sma = 3 }, - new SmaResult(DateTime.Parse("1/4/2000", EnglishCulture)) { Sma = 4 }, - new SmaResult(DateTime.Parse("1/5/2000", EnglishCulture)) { Sma = 5 }, - new SmaResult(DateTime.Parse("1/6/2000", EnglishCulture)) { Sma = 6 }, - new SmaResult(DateTime.Parse("1/7/2000", EnglishCulture)) { Sma = 7 }, - new SmaResult(DateTime.Parse("1/8/2000", EnglishCulture)) { Sma = double.NaN }, - new SmaResult(DateTime.Parse("1/9/2000", EnglishCulture)) { Sma = null }, - ]; - - // to be synced - List eval = - [ - new EmaResult(DateTime.Parse("1/3/2000", EnglishCulture)) { Ema = 3 }, - new EmaResult(DateTime.Parse("1/4/2000", EnglishCulture)) { Ema = 4 }, - new EmaResult(DateTime.Parse("1/5/2000", EnglishCulture)) { Ema = 5 }, - new EmaResult(DateTime.Parse("1/6/2000", EnglishCulture)) { Ema = 6 }, - new EmaResult(DateTime.Parse("1/7/2000", EnglishCulture)) { Ema = 7 }, - new EmaResult(DateTime.Parse("1/9/2000", EnglishCulture)) { Ema = double.NaN }, - new EmaResult(DateTime.Parse("1/10/2000", EnglishCulture)) { Ema = null }, - ]; - - // prepend option - List prepend = eval.SyncIndex(baseline, SyncType.Prepend).ToList(); - - Assert.AreEqual(9, prepend.Count); - Assert.AreEqual(3, prepend.Count(x => x.Ema is null)); - - for (int i = 0; i < 6; i++) - { - SmaResult b = baseline[i]; - EmaResult r = prepend[i]; - - Assert.AreEqual(b.Date, r.Date); - } - - // append option - List append = eval.SyncIndex(baseline, SyncType.AppendOnly).ToList(); - - Assert.AreEqual(10, append.Count); - Assert.AreEqual(4, append.Count(x => x.Ema is null)); - - for (int i = 0; i < 8; i++) - { - SmaResult b = baseline[i]; - EmaResult r = append[i]; - - Assert.AreEqual(b.Date, r.Date); - } - - // remove option - List remove = eval.SyncIndex(baseline, SyncType.RemoveOnly).ToList(); - - Assert.AreEqual(6, remove.Count); - Assert.AreEqual(0, remove.Count(x => x.Ema is null)); - Assert.AreEqual(0, remove.Count(x => - x.Date == DateTime.Parse("1/10/2000", EnglishCulture))); - - // full option - List fullmatch = eval.SyncIndex(baseline, SyncType.FullMatch).ToList(); - - Assert.AreEqual(9, fullmatch.Count); - Assert.AreEqual(3, fullmatch.Count(x => x.Ema is null)); - Assert.AreEqual(0, fullmatch.Count(x => - x.Date == DateTime.Parse("1/10/2000", EnglishCulture))); - - for (int i = 0; i < baseline.Count; i++) - { - SmaResult b = baseline[i]; - EmaResult r = fullmatch[i]; - - Assert.AreEqual(b.Date, r.Date); - } - - // no results - List noBaseline = []; - List noEval = []; - - IEnumerable noBaseResults = eval.SyncIndex(noBaseline); - IEnumerable noEvalResults = noEval.SyncIndex(baseline); - - Assert.IsFalse(noBaseResults.Any()); - Assert.IsFalse(noEvalResults.Any()); - - // bad sync type - Assert.ThrowsException(() - => eval.SyncIndex(baseline, (SyncType)int.MaxValue)); - } -} diff --git a/tests/indicators/_common/Results/Result.Utilities.Tests.cs b/tests/indicators/_common/Results/Result.Utilities.Tests.cs deleted file mode 100644 index 555386720..000000000 --- a/tests/indicators/_common/Results/Result.Utilities.Tests.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.Collections.ObjectModel; - -namespace Tests.Common; - -[TestClass] -public class Results : TestBase -{ - [TestMethod] - public void Condense() - { - List x = quotes - .GetAdx(14) - .ToList(); - - // make a few more in the middle null and NaN - x[249].Adx = null; - x[345].Adx = double.NaN; - - List r = x.Condense().ToList(); - - // proper quantities - Assert.AreEqual(473, r.Count); - - // sample values - AdxResult last = r.LastOrDefault(); - Assert.AreEqual(17.7565, last.Pdi.Round(4)); - Assert.AreEqual(31.1510, last.Mdi.Round(4)); - Assert.AreEqual(34.2987, last.Adx.Round(4)); - } - - [TestMethod] - public void ToTuple() - { - // baseline for comparison - List baseline = - [ - new SmaResult(DateTime.Parse("1/1/2000", EnglishCulture)) { Sma = null }, - new SmaResult(DateTime.Parse("1/2/2000", EnglishCulture)) { Sma = null }, - new SmaResult(DateTime.Parse("1/3/2000", EnglishCulture)) { Sma = 3 }, - new SmaResult(DateTime.Parse("1/4/2000", EnglishCulture)) { Sma = 4 }, - new SmaResult(DateTime.Parse("1/5/2000", EnglishCulture)) { Sma = 5 }, - new SmaResult(DateTime.Parse("1/6/2000", EnglishCulture)) { Sma = 6 }, - new SmaResult(DateTime.Parse("1/7/2000", EnglishCulture)) { Sma = 7 }, - new SmaResult(DateTime.Parse("1/8/2000", EnglishCulture)) { Sma = double.NaN }, - new SmaResult(DateTime.Parse("1/9/2000", EnglishCulture)) { Sma = null }, - ]; - - // default chainable NaN with pruning (internal) - List<(DateTime Date, double Value)> chainableTuple = baseline - .ToTuple(); - - Assert.AreEqual(5, chainableTuple.Count(x => !double.IsNaN(x.Value))); - Assert.AreEqual(2, chainableTuple.Count(x => double.IsNaN(x.Value))); - - // PUBLIC VARIANT - - // default chainable NaN with pruning - Collection<(DateTime Date, double Value)> cnaNresults = baseline - .ToTupleChainable(); - - Assert.AreEqual(5, cnaNresults.Count(x => !double.IsNaN(x.Value))); - Assert.AreEqual(2, cnaNresults.Count(x => double.IsNaN(x.Value))); - - // with NaN option, no pruning - Collection<(DateTime Date, double Value)> nanResults = baseline - .ToTupleNaN(); - - Assert.AreEqual(4, nanResults.Count(x => x.Value is double.NaN)); - Assert.AreEqual(9, nanResults.Count); - } -} diff --git a/tests/indicators/_common/Reusable/Reusable.Utilities.Tests.cs b/tests/indicators/_common/Reusable/Reusable.Utilities.Tests.cs new file mode 100644 index 000000000..f8bba07d7 --- /dev/null +++ b/tests/indicators/_common/Reusable/Reusable.Utilities.Tests.cs @@ -0,0 +1,98 @@ +namespace Utilities; + +[TestClass] +public class Reusable : TestBase +{ + [TestMethod] + public void Condense() + { + List original = Quotes + .ToAdx() + .ToList(); + + // make a few more in the middle null and NaN + original[249] = original[249] with { Adx = null }; + original[345] = original[345] with { Adx = double.NaN }; + + IReadOnlyList results + = original.Condense(); + + // proper quantities + Assert.AreEqual(473, results.Count); + + // sample values + AdxResult last = results[^1]; + Assert.AreEqual(17.7565, last.Pdi.Round(4)); + Assert.AreEqual(31.1510, last.Mdi.Round(4)); + Assert.AreEqual(34.2987, last.Adx.Round(4)); + } + + [TestMethod] + public void ToReusableList() + { + IReadOnlyList reusableList = Quotes + .ToReusableList(CandlePart.Close); + + Assert.IsNotNull(reusableList); + Assert.AreEqual(502, reusableList.Count); + Assert.AreEqual(245.28d, reusableList[^1].Value); + } + + [TestMethod] + public void QuoteToReusable() + { + DateTime t = DateTime.Parse("5/5/2055", invariantCulture); + + decimal l = 111111111111111m; + decimal o = 222222222222222m; + decimal c = 333333333333333m; + decimal h = 444444444444444m; + decimal v = 555555555555555m; + decimal hl2 = (h + l) / 2m; + decimal hlc3 = (h + l + c) / 3m; + decimal oc2 = (o + c) / 2m; + decimal ohl3 = (o + h + l) / 3m; + decimal ohlc4 = (o + h + l + c) / 4m; + + Quote q = new(t, o, h, l, c, v); + + Assert.AreEqual( + ((double)o).Round(10), + q.ToReusable(CandlePart.Open).Value.Round(10)); + Assert.AreEqual( + ((double)h).Round(10), + q.ToReusable(CandlePart.High).Value.Round(10)); + Assert.AreEqual( + ((double)l).Round(10), + q.ToReusable(CandlePart.Low).Value.Round(10)); + Assert.AreEqual( + ((double)c).Round(10), + q.ToReusable(CandlePart.Close).Value.Round(10)); + Assert.AreEqual( + ((double)v).Round(10), + q.ToReusable(CandlePart.Volume).Value.Round(10)); + Assert.AreEqual( + ((double)hl2).Round(10), + q.ToReusable(CandlePart.HL2).Value.Round(10)); + Assert.AreEqual( + ((double)hlc3).Round(10), + q.ToReusable(CandlePart.HLC3).Value.Round(10)); + Assert.AreEqual( + ((double)oc2).Round(10), + q.ToReusable(CandlePart.OC2).Value.Round(10)); + Assert.AreEqual( + ((double)ohl3).Round(10), + q.ToReusable(CandlePart.OHL3).Value.Round(10)); + Assert.AreEqual( + ((double)ohlc4).Round(10), + q.ToReusable(CandlePart.OHLC4).Value.Round(10)); + + // bad argument + Assert.ThrowsException(() + => q.ToReusable((CandlePart)999)); + + // bad argument + Assert.ThrowsException(() + => q.ToReusable((CandlePart)999)); + } +} diff --git a/tests/indicators/_common/Use (QuotePart)/QuotePart.StaticSeries.Tests.cs b/tests/indicators/_common/Use (QuotePart)/QuotePart.StaticSeries.Tests.cs new file mode 100644 index 000000000..72c57b1a2 --- /dev/null +++ b/tests/indicators/_common/Use (QuotePart)/QuotePart.StaticSeries.Tests.cs @@ -0,0 +1,88 @@ +namespace StaticSeries; + +[TestClass] +public class QuoteParts : StaticSeriesTestBase +{ + [TestMethod] + public override void Standard() + { + // compose data + IReadOnlyList o = Quotes.Use(CandlePart.Open); + IReadOnlyList h = Quotes.Use(CandlePart.High); + IReadOnlyList l = Quotes.Use(CandlePart.Low); + IReadOnlyList c = Quotes.Use(CandlePart.Close); + IReadOnlyList v = Quotes.Use(CandlePart.Volume); + IReadOnlyList hl = Quotes.Use(CandlePart.HL2); + IReadOnlyList hlc = Quotes.Use(CandlePart.HLC3); + IReadOnlyList oc = Quotes.Use(CandlePart.OC2); + IReadOnlyList ohl = Quotes.Use(CandlePart.OHL3); + IReadOnlyList ohlc = Quotes.Use(CandlePart.OHLC4); + + // proper quantities + Assert.AreEqual(502, c.Count); + + // samples + QuotePart ro = o[501]; + QuotePart rh = h[501]; + QuotePart rl = l[501]; + QuotePart rc = c[501]; + QuotePart rv = v[501]; + QuotePart rhl = hl[501]; + QuotePart rhlc = hlc[501]; + QuotePart roc = oc[501]; + QuotePart rohl = ohl[501]; + QuotePart rohlc = ohlc[501]; + + // proper last date + DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", invariantCulture); + Assert.AreEqual(lastDate, rc.Timestamp); + + // last values should be correct + Assert.AreEqual(245.28, rc.Value); + 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, rhl.Value); + Assert.AreEqual(244.5633, rhlc.Value.Round(4)); + Assert.AreEqual(245.1, roc.Value); + Assert.AreEqual(244.4433, rohl.Value.Round(4)); + Assert.AreEqual(244.6525, rohlc.Value); + } + + [TestMethod] + public void Chainor() + { + IReadOnlyList results = Quotes + .Use(CandlePart.Close) + .ToSma(10); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(493, results.Count(x => x.Sma != null)); + } + + [TestMethod] + public override void BadData() + { + IReadOnlyList r = BadQuotes + .Use(CandlePart.Close); + + Assert.AreEqual(502, r.Count); + Assert.AreEqual(0, r.Count(x => x.Value is double.NaN)); + } + + [TestMethod] + public override void NoQuotes() + { + IReadOnlyList r0 = Noquotes + .Use(CandlePart.Close); + + Assert.AreEqual(0, r0.Count); + + IReadOnlyList r1 = Onequote + .Use(CandlePart.Close); + + Assert.AreEqual(1, r1.Count); + } +} diff --git a/tests/indicators/_common/Use (QuotePart)/QuotePart.StreamHub.Tests.cs b/tests/indicators/_common/Use (QuotePart)/QuotePart.StreamHub.Tests.cs new file mode 100644 index 000000000..9a5bbb284 --- /dev/null +++ b/tests/indicators/_common/Use (QuotePart)/QuotePart.StreamHub.Tests.cs @@ -0,0 +1,142 @@ +namespace StreamHub; + +[TestClass] +public class QuotePartHub : StreamHubTestBase, ITestChainProvider +{ + [TestMethod] + public override void QuoteObserver() + { + CandlePart candlePart = CandlePart.HLC3; + + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // prefill quotes to provider + for (int i = 0; i < 20; i++) + { + provider.Add(quotesList[i]); + } + + // initialize observer + QuotePartHub observer = provider + .ToQuotePart(candlePart); + + // fetch initial results (early) + IReadOnlyList streamList + = observer.Results; + + // emulate adding quotes to provider + for (int i = 20; i < length; i++) + { + // skip one (add later) + if (i == 80) + { + continue; + } + + Quote q = quotesList[i]; + provider.Add(q); + + // resend duplicate quotes + if (i is > 100 and < 105) + { + provider.Add(q); + } + } + + // late arrival + provider.Insert(quotesList[80]); + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // time-series, for comparison + IReadOnlyList seriesList + = quotesList + .Use(candlePart); + + // assert, should equal series + for (int i = 0; i < length - 1; i++) + { + Quote q = quotesList[i]; + QuotePart s = seriesList[i]; + QuotePart r = streamList[i]; + + r.Timestamp.Should().Be(q.Timestamp); + r.Timestamp.Should().Be(s.Timestamp); + r.Value.Should().Be(s.Value); + r.Should().Be(s); + } + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public void ChainProvider() + { + int smaPeriods = 8; + CandlePart candlePart = CandlePart.OHLC4; + + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // initialize observer + SmaHub observer + = provider + .ToQuotePart(candlePart) + .ToSma(smaPeriods); + + // emulate quote stream + for (int i = 0; i < length; i++) + { + provider.Add(quotesList[i]); + } + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // final results + IReadOnlyList streamList + = observer.Results; + + // time-series, for comparison + IReadOnlyList seriesList + = quotesList + .Use(candlePart) + .ToSma(smaPeriods); + + // assert, should equal series + for (int i = 0; i < length - 1; i++) + { + Quote q = quotesList[i]; + SmaResult s = seriesList[i]; + SmaResult r = streamList[i]; + + r.Timestamp.Should().Be(q.Timestamp); + r.Timestamp.Should().Be(s.Timestamp); + r.Sma.Should().Be(s.Sma); + r.Should().Be(s); + } + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public override void CustomToString() + { + QuotePartHub hub = new(new QuoteHub(), CandlePart.Close); + hub.ToString().Should().Be("QUOTE-PART(CLOSE)"); + } +} diff --git a/tests/indicators/_common/Use (QuotePart)/QuotePart.Utilities.Tests.cs b/tests/indicators/_common/Use (QuotePart)/QuotePart.Utilities.Tests.cs new file mode 100644 index 000000000..bbf643f4b --- /dev/null +++ b/tests/indicators/_common/Use (QuotePart)/QuotePart.Utilities.Tests.cs @@ -0,0 +1,89 @@ +namespace Utilities; + +[TestClass] +public class QuoteParts : TestBase +{ + // this is an alias of Quotes.Use() + // so we're only testing the base utilities here + + [TestMethod] + public void ConvertQuote() + { + // compose basic data + QuotePart o = Quotes[501].ToQuotePart(CandlePart.Open); + QuotePart h = Quotes[501].ToQuotePart(CandlePart.High); + QuotePart l = Quotes[501].ToQuotePart(CandlePart.Low); + QuotePart c = Quotes[501].ToQuotePart(CandlePart.Close); + QuotePart v = Quotes[501].ToQuotePart(CandlePart.Volume); + QuotePart hl = Quotes[501].ToQuotePart(CandlePart.HL2); + QuotePart hlc = Quotes[501].ToQuotePart(CandlePart.HLC3); + QuotePart oc = Quotes[501].ToQuotePart(CandlePart.OC2); + QuotePart ohl = Quotes[501].ToQuotePart(CandlePart.OHL3); + QuotePart ohlc = Quotes[501].ToQuotePart(CandlePart.OHLC4); + + // proper last date + DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", invariantCulture); + Assert.AreEqual(lastDate, c.Timestamp); + + // last values should be correct + Assert.AreEqual(245.28, c.Value); + Assert.AreEqual(244.92, o.Value); + Assert.AreEqual(245.54, h.Value); + Assert.AreEqual(242.87, l.Value); + Assert.AreEqual(245.28, c.Value); + Assert.AreEqual(147031456, v.Value); + Assert.AreEqual(244.205, hl.Value); + Assert.AreEqual(244.5633, hlc.Value.Round(4)); + Assert.AreEqual(245.1, oc.Value); + Assert.AreEqual(244.4433, ohl.Value.Round(4)); + Assert.AreEqual(244.6525, ohlc.Value); + } + + [TestMethod] + public void ConvertList() + { + // compose data + IReadOnlyList o = Quotes.ToQuotePart(CandlePart.Open); + IReadOnlyList h = Quotes.ToQuotePart(CandlePart.High); + IReadOnlyList l = Quotes.ToQuotePart(CandlePart.Low); + IReadOnlyList c = Quotes.ToQuotePart(CandlePart.Close); + IReadOnlyList v = Quotes.ToQuotePart(CandlePart.Volume); + IReadOnlyList hl = Quotes.ToQuotePart(CandlePart.HL2); + IReadOnlyList hlc = Quotes.ToQuotePart(CandlePart.HLC3); + IReadOnlyList oc = Quotes.ToQuotePart(CandlePart.OC2); + IReadOnlyList ohl = Quotes.ToQuotePart(CandlePart.OHL3); + IReadOnlyList ohlc = Quotes.ToQuotePart(CandlePart.OHLC4); + + // proper quantities + Assert.AreEqual(502, c.Count); + + // samples + QuotePart ro = o[501]; + QuotePart rh = h[501]; + QuotePart rl = l[501]; + QuotePart rc = c[501]; + QuotePart rv = v[501]; + QuotePart rhl = hl[501]; + QuotePart rhlc = hlc[501]; + QuotePart roc = oc[501]; + QuotePart rohl = ohl[501]; + QuotePart rohlc = ohlc[501]; + + // proper last date + DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", invariantCulture); + Assert.AreEqual(lastDate, rc.Timestamp); + + // last values should be correct + Assert.AreEqual(245.28, rc.Value); + 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, rhl.Value); + Assert.AreEqual(244.5633, rhlc.Value.Round(4)); + Assert.AreEqual(245.1, roc.Value); + Assert.AreEqual(244.4433, rohl.Value.Round(4)); + Assert.AreEqual(244.6525, rohlc.Value); + } +} diff --git a/tests/indicators/a-d/BasicQuote/BasicQuote.Calc.xlsx b/tests/indicators/_common/Use (QuotePart)/Use.Calc.xlsx similarity index 100% rename from tests/indicators/a-d/BasicQuote/BasicQuote.Calc.xlsx rename to tests/indicators/_common/Use (QuotePart)/Use.Calc.xlsx diff --git a/tests/indicators/_common/Data.Aggregate.xlsx b/tests/indicators/_testdata/Data.Aggregate.xlsx similarity index 100% rename from tests/indicators/_common/Data.Aggregate.xlsx rename to tests/indicators/_testdata/Data.Aggregate.xlsx diff --git a/tests/indicators/_common/Data.Candles.xlsx b/tests/indicators/_testdata/Data.Candles.xlsx similarity index 100% rename from tests/indicators/_common/Data.Candles.xlsx rename to tests/indicators/_testdata/Data.Candles.xlsx diff --git a/tests/indicators/_common/Data.Quotes.xlsx b/tests/indicators/_testdata/Data.Quotes.xlsx similarity index 100% rename from tests/indicators/_common/Data.Quotes.xlsx rename to tests/indicators/_testdata/Data.Quotes.xlsx diff --git a/tests/indicators/_testdata/TestData.Getter.cs b/tests/indicators/_testdata/TestData.Getter.cs new file mode 100644 index 000000000..69d763678 --- /dev/null +++ b/tests/indicators/_testdata/TestData.Getter.cs @@ -0,0 +1,141 @@ +namespace Test.Data; + +// TEST QUOTE GETTERs + +internal static class Data +{ + // DEFAULT: S&P 500 ~2 years of daily data + internal static IReadOnlyList GetDefault(int days = 502) + => File.ReadAllLines("_testdata/data/default.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderByDescending(x => x.Timestamp) + .Take(days) + .ToSortedList(); + + // RANDOM: gaussian brownaian motion + internal static IReadOnlyList GetRandom(int days = 502) + => new RandomGbm(bars: days); + + // sorted by filename + + // BAD DATA + internal static IReadOnlyList GetBad(int days = 502) + => File.ReadAllLines("_testdata/data/bad.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderByDescending(x => x.Timestamp) + .Take(days) + .ToSortedList(); + + // BITCOIN DATA + internal static IReadOnlyList GetBitcoin(int days = 1246) + => File.ReadAllLines("_testdata/data/bitcoin.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderByDescending(x => x.Timestamp) + .Take(days) + .ToSortedList(); + + // BTCUSD, 69288 records, 15-minute bars + internal static IReadOnlyList GetBtcUsdNan(int bars = 69288) + => File.ReadAllLines("_testdata/data/btcusd15x69k.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderByDescending(x => x.Timestamp) + .Take(bars) + .ToSortedList(); + + // COMPARE DATA ~2 years of TSLA data (matches default time) + internal static IReadOnlyList GetCompare(int days = 502) + => File.ReadAllLines("_testdata/data/compare.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderByDescending(x => x.Timestamp) + .Take(days) + .ToSortedList(); + + // INTRADAY DATA + internal static IReadOnlyList GetIntraday(int days = 1564) + => File.ReadAllLines("_testdata/data/intraday.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderByDescending(x => x.Timestamp) + .Take(days) + .ToSortedList(); + + // LONGEST DATA ~62 years of S&P 500 daily data (15,821) + internal static IReadOnlyList GetLongest() + => File.ReadAllLines("_testdata/data/longest.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .ToSortedList(); + + // LONGISH DATA ~20 years of S&P 500 daily data + internal static IReadOnlyList GetLongish(int days = 5285) + => File.ReadAllLines("_testdata/data/longish.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderByDescending(x => x.Timestamp) + .Take(days) + .ToSortedList(); + + // MISMATCH DATA is in incorrect sequence + internal static IReadOnlyList GetMismatch() + => File.ReadAllLines("_testdata/data/mismatch.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .ToList(); // not sorted + + // MSFT, 30 years, daily + internal static IReadOnlyList GetMsft(int days = 8111) + => File.ReadAllLines("_testdata/data/msft.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderByDescending(x => x.Timestamp) + .Take(days) + .ToSortedList(); + + // PENNY DATA + internal static IReadOnlyList GetPenny() + => File.ReadAllLines("_testdata/data/penny.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .ToSortedList(); + + // SPX, 30 years, daily + internal static IReadOnlyList GetSpx(int days = 8111) + => File.ReadAllLines("_testdata/data/spx.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderByDescending(x => x.Timestamp) + .Take(days) + .ToSortedList(); + + // TOO BIG DATA + internal static IReadOnlyList GetTooBig(int days = 1246) + => File.ReadAllLines("_testdata/data/toobig.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderByDescending(x => x.Timestamp) + .Take(days) + .ToSortedList(); + + // MAX SIZE DATA + internal static IReadOnlyList GetMax(int days = 502) + => File.ReadAllLines("_testdata/data/toobig.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderByDescending(x => x.Timestamp) + .Take(days) + .ToSortedList(); + + // ZEROS (200) + internal static IReadOnlyList GetZeros(int days = 200) + => File.ReadAllLines("_testdata/data/zeros.csv") + .Skip(1) + .Select(Imports.QuoteFromCsv) + .OrderByDescending(x => x.Timestamp) + .Take(days) + .ToSortedList(); +} diff --git a/tests/indicators/_testdata/TestData.Imports.cs b/tests/indicators/_testdata/TestData.Imports.cs new file mode 100644 index 000000000..222ccd511 --- /dev/null +++ b/tests/indicators/_testdata/TestData.Imports.cs @@ -0,0 +1,46 @@ +using System.Globalization; + +namespace Test.Data; + +// TEST DATA IMPORT UTILITIES + +internal static class Imports +{ + private static readonly CultureInfo EnglishCulture = new("en-US", false); + + // importer / parser + internal static Quote QuoteFromCsv(string csvLine) + { + if (string.IsNullOrEmpty(csvLine)) + { + throw new InvalidDataException("CSV line was empty"); + } + + string[] csv = csvLine.Split(','); + + Quote quote = new( + Timestamp: DateTime.TryParse(csv[0], EnglishCulture, out DateTime d) ? d : default, + Open: csv[1].ToDecimalDefault(), + High: csv[2].ToDecimalDefault(), + Low: csv[3].ToDecimalDefault(), + Close: csv[4].ToDecimalDefault(), + Volume: csv[5].ToDecimalDefault() + ); + + return quote; + } + + internal static decimal ToDecimal(this string value) + => decimal.TryParse(value, out decimal d) ? d + : throw new NotFiniteNumberException( + $"Cannot convert `{value}`, it is not a number."); + + internal static decimal ToDecimalDefault(this string value) + => decimal.TryParse(value, out decimal d) ? d : default; + + internal static decimal? ToDecimalNull(this string value) + => decimal.TryParse(value, out decimal d) ? d : null; + + internal static double? ToDoubleNull(this string value) + => double.TryParse(value, out double d) ? d : null; +} diff --git a/tests/indicators/_testdata/TestData.Random.cs b/tests/indicators/_testdata/TestData.Random.cs new file mode 100644 index 000000000..7a8d1cb5f --- /dev/null +++ b/tests/indicators/_testdata/TestData.Random.cs @@ -0,0 +1,79 @@ +namespace Test.Data; + +/// +/// Geometric Brownian Motion (GMB) is a random simulator of market movement. +/// GBM can be used for testing indicators, validation and Monte Carlo simulations of strategies. +/// +/// +/// Sample usage: +/// +/// RandomGbm data = new(); // generates 1 year (252) list of bars +/// RandomGbm data = new(Bars: 1000); // generates 1,000 bars +/// RandomGbm data = new(Bars: 252, Volatility: 0.05, Drift: 0.0005, Seed: 100.0) +/// +/// Parameters: +/// +/// Bars: number of bars (quotes) requested +/// Volatility: how dymamic/volatile the series should be; default is 1 +/// Drift: incremental drift due to annual interest rate; default is 5% +/// Seed: starting value of the random series; should not be 0. +/// + +internal class RandomGbm : List +{ + private readonly double _volatility; + private readonly double _drift; + private double _seed; + + public RandomGbm( + int bars = 250, + double volatility = 1.0, + double drift = 0.01, + double seed = 1000.0) + { + _seed = seed; + _volatility = volatility * 0.01; + _drift = drift * 0.001; + for (int i = 0; i < bars; i++) + { + DateTime date = DateTime.Today.AddMinutes(i - bars); + Add(date); + } + } + + public void Add(DateTime timestamp) + { + double open = Price(_seed, _volatility * _volatility, _drift); + double close = Price(open, _volatility, _drift); + + double ocMax = Math.Max(open, close); + double high = Price(_seed, _volatility * 0.5, 0); + high = high < ocMax ? (2 * ocMax) - high : high; + + double ocMin = Math.Min(open, close); + double low = Price(_seed, _volatility * 0.5, 0); + low = low > ocMin ? (2 * ocMin) - low : low; + + double volume = Price(_seed * 10, _volatility * 2, drift: 0); + + Quote quote = new( + Timestamp: timestamp, + Open: (decimal)open, + High: (decimal)high, + Low: (decimal)low, + Close: (decimal)close, + Volume: (decimal)volume); + + Add(quote); + _seed = close; + } + + private static double Price(double seed, double volatility, double drift) + { + Random rnd = new((int)DateTime.UtcNow.Ticks); + double u1 = 1.0 - rnd.NextDouble(); + double u2 = 1.0 - rnd.NextDouble(); + double z = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); + return seed * Math.Exp(drift - (volatility * volatility * 0.5) + (volatility * z)); + } +} diff --git a/tests/indicators/_testdata/TestData.Tests.cs b/tests/indicators/_testdata/TestData.Tests.cs new file mode 100644 index 000000000..7fcf9406f --- /dev/null +++ b/tests/indicators/_testdata/TestData.Tests.cs @@ -0,0 +1,77 @@ +namespace TestOfTests; + +[TestClass] +public class TestData : TestBase +{ + // Test the test data to + // ensure it meets the expected format + + [TestMethod] + public void QuotesIsValid() + { + Quotes.Should().HaveCount(502); + Quotes.Validate(); + } + + [TestMethod] + public void OtherQuotesIsValid() + { + OtherQuotes.Should().HaveCount(502); + OtherQuotes.Validate(); + } + + [TestMethod] + public void BadQuotesIsInvalid() + { + BadQuotes.Should().HaveCount(502); + + // duplicates + Assert.ThrowsException( + () => BadQuotes.Validate()); + } + + [TestMethod] + public void BigQuotesIsValid() + { + BigQuotes.Should().HaveCount(1246); + BigQuotes.Validate(); + } + + [TestMethod] + public void LongishQuotesIsValid() + { + LongishQuotes.Should().HaveCount(5285); + LongishQuotes.Validate(); + } + + [TestMethod] + public void LongestQuotesIsValid() + { + LongestQuotes.Should().HaveCount(15821); + LongestQuotes.Validate(); + } + + [TestMethod] + public void MismatchQuotesIsValid() + { + MismatchQuotes.Should().HaveCount(502); + + // out of sequence + Assert.ThrowsException( + () => MismatchQuotes.Validate()); + } + + [TestMethod] + public void RandomQuotesIsValid() + { + RandomQuotes.Should().HaveCount(1000); + RandomQuotes.Validate(); + } + + [TestMethod] + public void ZeroesQuotesIsValid() + { + ZeroesQuotes.Should().HaveCount(200); + ZeroesQuotes.Validate(); + } +} diff --git a/tests/indicators/_common/data/bad.csv b/tests/indicators/_testdata/data/bad.csv similarity index 100% rename from tests/indicators/_common/data/bad.csv rename to tests/indicators/_testdata/data/bad.csv diff --git a/tests/indicators/_common/data/bitcoin.csv b/tests/indicators/_testdata/data/bitcoin.csv similarity index 100% rename from tests/indicators/_common/data/bitcoin.csv rename to tests/indicators/_testdata/data/bitcoin.csv diff --git a/tests/indicators/_common/data/btcusd15x69k.csv b/tests/indicators/_testdata/data/btcusd15x69k.csv similarity index 100% rename from tests/indicators/_common/data/btcusd15x69k.csv rename to tests/indicators/_testdata/data/btcusd15x69k.csv diff --git a/tests/performance/helpers/data/compare.csv b/tests/indicators/_testdata/data/compare.csv similarity index 100% rename from tests/performance/helpers/data/compare.csv rename to tests/indicators/_testdata/data/compare.csv diff --git a/tests/performance/helpers/data/default.csv b/tests/indicators/_testdata/data/default.csv similarity index 100% rename from tests/performance/helpers/data/default.csv rename to tests/indicators/_testdata/data/default.csv diff --git a/tests/performance/helpers/data/intraday.csv b/tests/indicators/_testdata/data/intraday.csv similarity index 100% rename from tests/performance/helpers/data/intraday.csv rename to tests/indicators/_testdata/data/intraday.csv diff --git a/tests/performance/helpers/data/longest.csv b/tests/indicators/_testdata/data/longest.csv similarity index 99% rename from tests/performance/helpers/data/longest.csv rename to tests/indicators/_testdata/data/longest.csv index b753dcc33..5dd865443 100644 --- a/tests/performance/helpers/data/longest.csv +++ b/tests/indicators/_testdata/data/longest.csv @@ -1,4 +1,4 @@ -Date,Open,High,Low,Close,Volume +Timestamp,Open,High,Low,Close,Volume 2012-11-15,1355.41,1360.62,1348.05,1353.33,3928870000 2012-11-14,1374.64,1380.13,1352.5,1355.49,4109510000 2012-11-13,1380.03,1388.81,1371.39,1374.53,3455550000 diff --git a/tests/performance/helpers/data/longish.csv b/tests/indicators/_testdata/data/longish.csv similarity index 100% rename from tests/performance/helpers/data/longish.csv rename to tests/indicators/_testdata/data/longish.csv diff --git a/tests/indicators/_common/data/max.csv b/tests/indicators/_testdata/data/max.csv similarity index 100% rename from tests/indicators/_common/data/max.csv rename to tests/indicators/_testdata/data/max.csv diff --git a/tests/indicators/_common/data/mismatch.csv b/tests/indicators/_testdata/data/mismatch.csv similarity index 99% rename from tests/indicators/_common/data/mismatch.csv rename to tests/indicators/_testdata/data/mismatch.csv index 564ae3e48..cbcd48f8d 100644 --- a/tests/indicators/_common/data/mismatch.csv +++ b/tests/indicators/_testdata/data/mismatch.csv @@ -1,4 +1,4 @@ -Date,Open,High,Low,Close,Volume +Timestamp,Open,High,Low,Close,Volume 1/3/2017,212.61,213.35,211.52,212.8,96708880 1/4/2017,213.16,214.22,213.15,214.06,83348752 1/5/2017,213.77,214.06,213.02,213.89,82961968 diff --git a/tests/indicators/_common/data/msft.csv b/tests/indicators/_testdata/data/msft.csv similarity index 100% rename from tests/indicators/_common/data/msft.csv rename to tests/indicators/_testdata/data/msft.csv diff --git a/tests/indicators/_common/data/penny.csv b/tests/indicators/_testdata/data/penny.csv similarity index 100% rename from tests/indicators/_common/data/penny.csv rename to tests/indicators/_testdata/data/penny.csv diff --git a/tests/indicators/_common/data/spx.csv b/tests/indicators/_testdata/data/spx.csv similarity index 100% rename from tests/indicators/_common/data/spx.csv rename to tests/indicators/_testdata/data/spx.csv diff --git a/tests/indicators/_common/data/toobig.csv b/tests/indicators/_testdata/data/toobig.csv similarity index 100% rename from tests/indicators/_common/data/toobig.csv rename to tests/indicators/_testdata/data/toobig.csv diff --git a/tests/indicators/_common/data/zeros.csv b/tests/indicators/_testdata/data/zeros.csv similarity index 100% rename from tests/indicators/_common/data/zeros.csv rename to tests/indicators/_testdata/data/zeros.csv diff --git a/tests/indicators/a-d/Adl/Adl.StaticSeries.Tests.cs b/tests/indicators/a-d/Adl/Adl.StaticSeries.Tests.cs new file mode 100644 index 000000000..1c98e391d --- /dev/null +++ b/tests/indicators/a-d/Adl/Adl.StaticSeries.Tests.cs @@ -0,0 +1,82 @@ +namespace StaticSeries; + +[TestClass] +public class Adl : StaticSeriesTestBase +{ + [TestMethod] + public override void Standard() + { + IReadOnlyList results = Quotes + .ToAdl(); + + // proper quantities + Assert.AreEqual(502, results.Count); + + // sample values + AdlResult r1 = results[249]; + Assert.AreEqual(0.7778, r1.MoneyFlowMultiplier.Round(4)); + Assert.AreEqual(36433792.89, r1.MoneyFlowVolume.Round(2)); + Assert.AreEqual(3266400865.74, r1.Adl.Round(2)); + + AdlResult r2 = results[501]; + Assert.AreEqual(0.8052, r2.MoneyFlowMultiplier.Round(4)); + Assert.AreEqual(118396116.25, r2.MoneyFlowVolume.Round(2)); + Assert.AreEqual(3439986548.42, r2.Adl.Round(2)); + } + + [TestMethod] + public void Chainor() + { + IReadOnlyList results = Quotes + .ToAdl() + .ToSma(10); + + // assertions + + // proper quantities + Assert.AreEqual(502, results.Count); + Assert.AreEqual(493, results.Count(x => x.Sma != null)); + } + + [TestMethod] + public override void BadData() + { + IReadOnlyList r = BadQuotes + .ToAdl(); + + Assert.AreEqual(502, r.Count); + Assert.AreEqual(0, r.Count(x => double.IsNaN(x.Adl))); + } + + [TestMethod] + public void BigData() + { + IReadOnlyList r = BigQuotes + .ToAdl(); + + Assert.AreEqual(1246, r.Count); + } + + [TestMethod] + public void RandomData() + { + IReadOnlyList r = RandomQuotes + .ToAdl(); + + Assert.AreEqual(1000, r.Count); + } + + [TestMethod] + public override void NoQuotes() + { + IReadOnlyList r0 = Noquotes + .ToAdl(); + + Assert.AreEqual(0, r0.Count); + + IReadOnlyList r1 = Onequote + .ToAdl(); + + Assert.AreEqual(1, r1.Count); + } +} diff --git a/tests/indicators/a-d/Adl/Adl.StreamHub.Tests.cs b/tests/indicators/a-d/Adl/Adl.StreamHub.Tests.cs new file mode 100644 index 000000000..0b2d3e66b --- /dev/null +++ b/tests/indicators/a-d/Adl/Adl.StreamHub.Tests.cs @@ -0,0 +1,128 @@ +namespace StreamHub; + +[TestClass] +public class AdlHub : StreamHubTestBase, ITestChainProvider +{ + [TestMethod] + public override void QuoteObserver() + { + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // prefill quotes to provider + for (int i = 0; i < 20; i++) + { + provider.Add(quotesList[i]); + } + + // initialize observer + StreamHub observer = provider + .ToAdl(); + + // fetch initial results (early) + IReadOnlyList streamList + = observer.Results; + + // emulate adding quotes to provider + for (int i = 20; i < length; i++) + { + // skip one (add later) + if (i == 80) + { + continue; + } + + Quote q = quotesList[i]; + provider.Add(q); + + // resend duplicate quotes + if (i is > 100 and < 105) + { + provider.Add(q); + } + } + + // late arrival + provider.Insert(quotesList[80]); + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // time-series, for comparison + IReadOnlyList seriesList = quotesList + .ToAdl(); + + // assert, should equal series + streamList.Should().HaveCount(length - 1); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public void ChainProvider() + { + const int smaPeriods = 8; + + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // initialize observer + AdlHub adlHub = provider + .ToAdl(); + + SmaHub observer = adlHub + .ToSma(smaPeriods); + + // emulate quote stream + for (int i = 0; i < length; i++) + { + provider.Add(quotesList[i]); + } + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // final results + IReadOnlyList streamList + = observer.Results; + + // time-series, for comparison + IReadOnlyList seriesList = quotesList + .ToAdl() + .ToSma(smaPeriods); + + // assert, should equal series + streamList.Should().HaveCount(length - 1); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public override void CustomToString() + { + QuoteHub provider = new(); + + AdlHub hub = new(provider); + hub.ToString().Should().Be("ADL"); + + provider.Add(Quotes[0]); + provider.Add(Quotes[1]); + + string s = $"ADL({Quotes[0].Timestamp:d})"; + hub.ToString().Should().Be(s); + } +} diff --git a/tests/indicators/a-d/Adl/Adl.Tests.cs b/tests/indicators/a-d/Adl/Adl.Tests.cs deleted file mode 100644 index 3130641c5..000000000 --- a/tests/indicators/a-d/Adl/Adl.Tests.cs +++ /dev/null @@ -1,117 +0,0 @@ -namespace Tests.Indicators; - -[TestClass] -public class AdlTests : TestBase -{ - [TestMethod] - public void Standard() - { - List results = quotes - .GetAdl() - .ToList(); - - // proper quantities - Assert.AreEqual(502, results.Count); - Assert.AreEqual(502, results.Count(x => x.AdlSma == null)); - - // sample values - AdlResult r1 = results[249]; - Assert.AreEqual(0.7778, r1.MoneyFlowMultiplier.Round(4)); - Assert.AreEqual(36433792.89, r1.MoneyFlowVolume.Round(2)); - Assert.AreEqual(3266400865.74, r1.Adl.Round(2)); - Assert.AreEqual(null, r1.AdlSma); - - AdlResult r2 = results[501]; - Assert.AreEqual(0.8052, r2.MoneyFlowMultiplier.Round(4)); - Assert.AreEqual(118396116.25, r2.MoneyFlowVolume.Round(2)); - Assert.AreEqual(3439986548.42, r2.Adl.Round(2)); - Assert.AreEqual(null, r2.AdlSma); - } - - [TestMethod] - public void WithSma() - { - List results = quotes - .GetAdl(20) - .ToList(); - - // proper quantities - Assert.AreEqual(502, results.Count); - Assert.AreEqual(483, results.Count(x => x.AdlSma != null)); - - // sample value - AdlResult r = results[501]; - Assert.AreEqual(0.8052, r.MoneyFlowMultiplier.Round(4)); - Assert.AreEqual(118396116.25, r.MoneyFlowVolume.Round(2)); - Assert.AreEqual(3439986548.42, r.Adl.Round(2)); - Assert.AreEqual(3595352721.16, r.AdlSma.Round(2)); - } - - [TestMethod] - public void Chainor() - { - List results = quotes - .GetAdl() - .GetSma(10) - .ToList(); - - // assertions - - // proper quantities - Assert.AreEqual(502, results.Count); - Assert.AreEqual(493, results.Count(x => x.Sma != null)); - } - - [TestMethod] - public void BadData() - { - List r = badQuotes - .GetAdl() - .ToList(); - - Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => double.IsNaN(x.Adl))); - } - - [TestMethod] - public void BigData() - { - List r = bigQuotes - .GetAdl() - .ToList(); - - Assert.AreEqual(1246, r.Count); - } - - [TestMethod] - public void RandomData() - { - List r = randomQuotes - .GetAdl() - .ToList(); - - Assert.AreEqual(1000, r.Count); - } - - [TestMethod] - public void NoQuotes() - { - List r0 = noquotes - .GetAdl() - .ToList(); - - Assert.AreEqual(0, r0.Count); - - List r1 = onequote - .GetAdl() - .ToList(); - - Assert.AreEqual(1, r1.Count); - } - - // bad SMA period - [TestMethod] - public void Exceptions() - => Assert.ThrowsException(() - => quotes.GetAdl(0)); -} diff --git a/tests/indicators/a-d/Adx/Adx.Tests.cs b/tests/indicators/a-d/Adx/Adx.StaticSeries.Tests.cs similarity index 62% rename from tests/indicators/a-d/Adx/Adx.Tests.cs rename to tests/indicators/a-d/Adx/Adx.StaticSeries.Tests.cs index 81a4c4805..ca0a5c213 100644 --- a/tests/indicators/a-d/Adx/Adx.Tests.cs +++ b/tests/indicators/a-d/Adx/Adx.StaticSeries.Tests.cs @@ -1,14 +1,12 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class AdxTests : TestBase +public class Adx : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetAdx(14) - .ToList(); + IReadOnlyList results = Quotes.ToAdx(); // proper quantities Assert.AreEqual(502, results.Count); @@ -47,48 +45,39 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetAdx(14) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToAdx() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(466, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetAdx(20) - .ToList(); + IReadOnlyList r = BadQuotes.ToAdx(20); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Adx is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Adx is double.NaN)); } [TestMethod] public void BigData() { - List r = bigQuotes - .GetAdx(200) - .ToList(); + IReadOnlyList r = BigQuotes.ToAdx(200); Assert.AreEqual(1246, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetAdx(5) - .ToList(); + IReadOnlyList r0 = Noquotes.ToAdx(5); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetAdx(5) - .ToList(); + IReadOnlyList r1 = Onequote.ToAdx(5); Assert.AreEqual(1, r1.Count); } @@ -96,37 +85,38 @@ public void NoQuotes() [TestMethod] public void Issue859() { - IOrderedEnumerable test859 = File.ReadAllLines("a-d/Adx/issue859quotes.csv") + List test859 = File.ReadAllLines("a-d/Adx/issue859quotes.csv") .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date); + .Select(Imports.QuoteFromCsv) + .OrderByDescending(x => x.Timestamp) + .ToList(); - List r = test859.GetAdx(14).ToList(); + IReadOnlyList r = test859.ToAdx(); - Assert.AreEqual(0, r.Count(x => x.Adx is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Adx is double.NaN)); Assert.AreEqual(595, r.Count); } [TestMethod] public void Zeroes() { - List r = zeroesQuotes.GetAdx(14).ToList(); + IReadOnlyList r = ZeroesQuotes.ToAdx(); - Assert.AreEqual(0, r.Count(x => x.Adx is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Adx is double.NaN)); Assert.AreEqual(200, r.Count); } [TestMethod] public void Removed() { - List r = quotes.GetAdx(14) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToAdx() + .RemoveWarmupPeriods(); // assertions - Assert.AreEqual(502 - ((2 * 14) + 100), r.Count); + Assert.AreEqual(502 - ((2 * 14) + 100), results.Count); - AdxResult last = r.LastOrDefault(); + AdxResult last = results[^1]; Assert.AreEqual(17.7565, last.Pdi.Round(4)); Assert.AreEqual(31.1510, last.Mdi.Round(4)); Assert.AreEqual(34.2987, last.Adx.Round(4)); @@ -137,5 +127,5 @@ public void Exceptions() => // bad lookback period Assert.ThrowsException(() => - quotes.GetAdx(1)); + Quotes.ToAdx(1)); } diff --git a/tests/indicators/a-d/Adx/issue859quotes.csv b/tests/indicators/a-d/Adx/issue859quotes.csv index b5fe10488..e6fe253a4 100644 --- a/tests/indicators/a-d/Adx/issue859quotes.csv +++ b/tests/indicators/a-d/Adx/issue859quotes.csv @@ -1,4 +1,4 @@ -Date,Open,High,Low,Close,Volume +Timestamp,Open,High,Low,Close,Volume 2022-07-25T09:15:00+05:30,498.075,502.1,494.05,498.075,50 2022-07-25T09:18:00+05:30,498.075,498.075,494.05,494.05,0 2022-07-25T09:21:00+05:30,496.0625,496.0625,494.05,494.05,0 diff --git a/tests/indicators/a-d/Alligator/Alligator.Tests.cs b/tests/indicators/a-d/Alligator/Alligator.StaticSeries.Tests.cs similarity index 55% rename from tests/indicators/a-d/Alligator/Alligator.Tests.cs rename to tests/indicators/a-d/Alligator/Alligator.StaticSeries.Tests.cs index f74aaec15..e8c682b40 100644 --- a/tests/indicators/a-d/Alligator/Alligator.Tests.cs +++ b/tests/indicators/a-d/Alligator/Alligator.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class AlligatorTests : TestBase +public class Alligator : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetAlligator() - .ToList(); + IReadOnlyList results = Quotes + .ToAlligator(); // proper quantities Assert.AreEqual(502, results.Count); @@ -43,79 +42,37 @@ public void Standard() Assert.AreEqual(244.29591, results[501].Lips.Round(5)); } - [TestMethod] - public void UseTuple() - { - List results = quotes - .Use(CandlePart.HL2) - .GetAlligator() - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(482, results.Count(x => x.Jaw != null)); - - AlligatorResult last = results.LastOrDefault(); - Assert.AreEqual(244.29591, last.Lips.Round(5)); - } - - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetAlligator() - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Lips is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetAlligator() - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToAlligator(); Assert.AreEqual(502, results.Count); Assert.AreEqual(481, results.Count(x => x.Jaw != null)); } [TestMethod] - public void Sync() - { - List results = quotes - .GetSma(3) - .GetAlligator() - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(480, results.Count(x => x.Jaw != null)); - } - - [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetAlligator(3, 3, 2, 1, 1, 1) - .ToList(); + IReadOnlyList r = BadQuotes + .ToAlligator(3, 3, 2, 1, 1, 1); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Jaw is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Jaw is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetAlligator() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToAlligator(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetAlligator() - .ToList(); + IReadOnlyList r1 = Onequote + .ToAlligator(); Assert.AreEqual(1, r1.Count); } @@ -123,14 +80,13 @@ public void NoQuotes() [TestMethod] public void Condense() { - List r = quotes - .GetAlligator() - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToAlligator() + .Condense(); - Assert.AreEqual(495, r.Count); + Assert.AreEqual(495, results.Count); - AlligatorResult last = r.LastOrDefault(); + AlligatorResult last = results[^1]; Assert.AreEqual(260.98953, last.Jaw.Round(5)); Assert.AreEqual(253.53576, last.Teeth.Round(5)); Assert.AreEqual(244.29591, last.Lips.Round(5)); @@ -139,52 +95,62 @@ public void Condense() [TestMethod] public void Removed() { - List r = quotes - .GetAlligator(13, 8) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToAlligator() + .RemoveWarmupPeriods(); - Assert.AreEqual(502 - 21 - 250, r.Count); + Assert.AreEqual(502 - 21 - 250, results.Count); - AlligatorResult last = r.LastOrDefault(); + AlligatorResult last = results[^1]; Assert.AreEqual(260.98953, last.Jaw.Round(5)); Assert.AreEqual(253.53576, last.Teeth.Round(5)); Assert.AreEqual(244.29591, last.Lips.Round(5)); } + [TestMethod] + public void Equality() + { + AlligatorResult r1 = new(EvalDate, 1d, null, null); + AlligatorResult r2 = new(EvalDate, 1d, null, null); + AlligatorResult r3 = new(EvalDate, 2d, null, null); // abberent + + Assert.IsTrue(Equals(r1, r2)); + Assert.IsFalse(Equals(r1, r3)); + } + [TestMethod] public void Exceptions() { // bad jaw lookback periods Assert.ThrowsException(() => - quotes.GetAlligator(13, 8, 13, 5, 5, 3)); + Quotes.ToAlligator(13, 8, 13)); // bad teeth lookback periods Assert.ThrowsException(() => - quotes.GetAlligator(13, 8, 8, 5, 8, 3)); + Quotes.ToAlligator(13, 8, 8, 5, 8)); // bad lips lookback periods Assert.ThrowsException(() => - quotes.GetAlligator(13, 8, 8, 5, 0, 3)); + Quotes.ToAlligator(13, 8, 8, 5, 0)); // bad jaw offset periods Assert.ThrowsException(() => - quotes.GetAlligator(13, 0, 8, 5, 5, 3)); + Quotes.ToAlligator(13, 0)); // bad teeth offset periods Assert.ThrowsException(() => - quotes.GetAlligator(13, 8, 8, 0, 5, 3)); + Quotes.ToAlligator(13, 8, 8, 0)); // bad lips offset periods Assert.ThrowsException(() => - quotes.GetAlligator(13, 8, 8, 5, 5, 0)); + Quotes.ToAlligator(13, 8, 8, 5, 5, 0)); // bad jaw + offset periods Assert.ThrowsException(() => - quotes.GetAlligator(13, 8, 12, 11, 5, 3)); + Quotes.ToAlligator(13, 8, 12, 11)); // bad teeth + offset periods Assert.ThrowsException(() => - quotes.GetAlligator(13, 8, 8, 5, 7, 7)); + Quotes.ToAlligator(13, 8, 8, 5, 7, 7)); } } diff --git a/tests/indicators/a-d/Alligator/Alligator.StreamHub.Tests.cs b/tests/indicators/a-d/Alligator/Alligator.StreamHub.Tests.cs new file mode 100644 index 000000000..f6db7e617 --- /dev/null +++ b/tests/indicators/a-d/Alligator/Alligator.StreamHub.Tests.cs @@ -0,0 +1,135 @@ +namespace StreamHub; + +[TestClass] +public class AlligatorHub : StreamHubTestBase, ITestChainObserver +{ + [TestMethod] + public override void QuoteObserver() + { + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // prefill quotes to provider + for (int i = 0; i < 20; i++) + { + provider.Add(quotesList[i]); + } + + // initialize observer + AlligatorHub observer = provider + .ToAlligator(); + + // fetch initial results (early) + IReadOnlyList streamList + = observer.Results; + + // emulate adding quotes to provider + for (int i = 20; i < length; i++) + { + // skip one (add later) + if (i == 80) + { + continue; + } + + Quote q = quotesList[i]; + provider.Add(q); + + // resend duplicate quotes + if (i is > 100 and < 105) + { + provider.Add(q); + } + } + + // late arrival + provider.Insert(quotesList[80]); + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // time-series, for comparison + IReadOnlyList seriesList + = quotesList + .ToAlligator(); + + // assert, should equal series + streamList.Should().HaveCount(length - 1); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public void ChainObserver() + { + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // initialize observer + AlligatorHub observer = provider + .ToSma(10) + .ToAlligator(); + + // emulate adding quotes out of order + // note: this works when graceful order + for (int i = 0; i < length; i++) + { + // skip one (add later) + if (i == 80) + { + continue; + } + + Quote q = quotesList[i]; + provider.Add(q); + + // resend duplicate quotes + if (i is > 100 and < 105) + { + provider.Add(q); + } + } + + // late arrival + provider.Insert(quotesList[80]); + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // final results + IReadOnlyList streamList + = observer.Results; + + // time-series, for comparison + IReadOnlyList seriesList + = quotesList + .ToSma(10) + .ToAlligator(); + + // assert, should equal series + streamList.Should().HaveCount(length - 1); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public override void CustomToString() + { + AlligatorHub hub = new(new QuoteHub(), 13, 8, 7, 5, 4, 3); + hub.ToString().Should().Be("ALLIGATOR(13,8,7,5,4,3)"); + } +} diff --git a/tests/indicators/a-d/Alma/Alma.Tests.cs b/tests/indicators/a-d/Alma/Alma.StaticSeries.Tests.cs similarity index 53% rename from tests/indicators/a-d/Alma/Alma.Tests.cs rename to tests/indicators/a-d/Alma/Alma.StaticSeries.Tests.cs index e855c5b6f..d9ccb1884 100644 --- a/tests/indicators/a-d/Alma/Alma.Tests.cs +++ b/tests/indicators/a-d/Alma/Alma.StaticSeries.Tests.cs @@ -1,18 +1,17 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class Alma : TestBase +public class Alma : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { int lookbackPeriods = 10; double offset = 0.85; double sigma = 6; - List results = quotes - .GetAlma(lookbackPeriods, offset, sigma) - .ToList(); + IReadOnlyList results = Quotes + .ToAlma(lookbackPeriods, offset, sigma); // proper quantities Assert.AreEqual(502, results.Count); @@ -39,38 +38,25 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetAlma(10, 0.85, 6) - .ToList(); + .ToAlma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(493, results.Count(x => x.Alma != null)); - AlmaResult last = results.LastOrDefault(); + AlmaResult last = results[^1]; Assert.AreEqual(242.1871, last.Alma.Round(4)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetAlma() - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Alma is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetAlma(10, 0.85, 6) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToAlma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(492, results.Count(x => x.Alma != null)); @@ -83,10 +69,9 @@ public void Chainor() double offset = 0.85; double sigma = 6; - List results = quotes - .GetAlma(lookbackPeriods, offset, sigma) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToAlma(lookbackPeriods, offset, sigma) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(484, results.Count(x => x.Sma != null)); @@ -95,38 +80,34 @@ public void Chainor() [TestMethod] public void NaN() { - List r1 = TestData.GetBtcUsdNan().GetAlma(9, 0.85, 6).ToList(); + IReadOnlyList r1 + = Data.GetBtcUsdNan().ToAlma(); - Assert.AreEqual(0, r1.Count(x => x.Alma is double and double.NaN)); + Assert.AreEqual(0, r1.Count(x => x.Alma is double.NaN)); - List r2 = TestData.GetBtcUsdNan().GetAlma(20, 0.85, 6).ToList(); + IReadOnlyList r2 + = Data.GetBtcUsdNan().ToAlma(20); - Assert.AreEqual(0, r2.Count(x => x.Alma is double and double.NaN)); + Assert.AreEqual(0, r2.Count(x => x.Alma is double.NaN)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetAlma(14, 0.5, 3) - .ToList(); + IReadOnlyList r = BadQuotes.ToAlma(14, 0.5, 3); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Alma is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Alma is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetAlma() - .ToList(); + IReadOnlyList r0 = Noquotes.ToAlma(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetAlma() - .ToList(); + IReadOnlyList r1 = Onequote.ToAlma(); Assert.AreEqual(1, r1.Count); } @@ -134,15 +115,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetAlma(10, 0.85, 6) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToAlma(10) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 9, results.Count); - AlmaResult last = results.LastOrDefault(); + AlmaResult last = results[^1]; Assert.AreEqual(242.1871, last.Alma.Round(4)); } @@ -151,14 +131,14 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - quotes.GetAlma(0, 1, 5)); + Quotes.ToAlma(0, 1, 5)); // bad offset Assert.ThrowsException(() => - quotes.GetAlma(15, 1.1, 3)); + Quotes.ToAlma(15, 1.1, 3)); // bad sigma Assert.ThrowsException(() => - quotes.GetAlma(10, 0.5, 0)); + Quotes.ToAlma(10, 0.5, 0)); } } diff --git a/tests/indicators/a-d/Aroon/Aroon.Tests.cs b/tests/indicators/a-d/Aroon/Aroon.StaticSeries.Tests.cs similarity index 68% rename from tests/indicators/a-d/Aroon/Aroon.Tests.cs rename to tests/indicators/a-d/Aroon/Aroon.StaticSeries.Tests.cs index 50fe8f8e5..db6dcba18 100644 --- a/tests/indicators/a-d/Aroon/Aroon.Tests.cs +++ b/tests/indicators/a-d/Aroon/Aroon.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class Aroon : TestBase +public class Aroon : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetAroon(25) - .ToList(); + IReadOnlyList results = Quotes + .ToAroon(); // proper quantities Assert.AreEqual(502, results.Count); @@ -46,38 +45,34 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetAroon(25) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToAroon() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(468, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetAroon(20) - .ToList(); + IReadOnlyList r = BadQuotes + .ToAroon(20); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Oscillator is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Oscillator is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetAroon() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToAroon(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetAroon() - .ToList(); + IReadOnlyList r1 = Onequote + .ToAroon(); Assert.AreEqual(1, r1.Count); } @@ -85,15 +80,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetAroon(25) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToAroon() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 25, results.Count); - AroonResult last = results.LastOrDefault(); + AroonResult last = results[^1]; Assert.AreEqual(28, last.AroonUp); Assert.AreEqual(88, last.AroonDown); Assert.AreEqual(-60, last.Oscillator); @@ -103,5 +97,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetAroon(0)); + => Quotes.ToAroon(0)); } diff --git a/tests/indicators/a-d/Atr/Atr.Tests.cs b/tests/indicators/a-d/Atr/Atr.StaticSeries.Tests.cs similarity index 59% rename from tests/indicators/a-d/Atr/Atr.Tests.cs rename to tests/indicators/a-d/Atr/Atr.StaticSeries.Tests.cs index 23267d3ea..0682f0fc3 100644 --- a/tests/indicators/a-d/Atr/Atr.Tests.cs +++ b/tests/indicators/a-d/Atr/Atr.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class AtrTests : TestBase +public class Atr : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetAtr(14) - .ToList(); + IReadOnlyList results = Quotes + .ToAtr(); // proper quantities Assert.AreEqual(502, results.Count); @@ -41,41 +40,58 @@ public void Standard() Assert.AreEqual(2.5072, r501.Atrp.Round(4)); } + [TestMethod] + public void MatchingTrueRange() + { + IReadOnlyList resultsAtr = Quotes + .ToAtr(14); + + IReadOnlyList resultsTr = Quotes + .ToTr(); + + for (int i = 0; i < Quotes.Count; i++) + { + Quote q = Quotes[i]; + TrResult t = resultsTr[i]; + AtrResult r = resultsAtr[i]; + + r.Timestamp.Should().Be(q.Timestamp); + r.Timestamp.Should().Be(t.Timestamp); + r.Tr.Should().Be(t.Tr); + } + } + [TestMethod] public void Chainor() { - List results = quotes - .GetAtr(10) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToAtr(10) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(502 - 19, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetAtr(20) - .ToList(); + IReadOnlyList r = BadQuotes + .ToAtr(20); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Atr is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Atr is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetAtr() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToAtr(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetAtr() - .ToList(); + IReadOnlyList r1 = Onequote + .ToAtr(); Assert.AreEqual(1, r1.Count); } @@ -83,15 +99,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetAtr(14) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToAtr() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 14, results.Count); - AtrResult last = results.LastOrDefault(); + AtrResult last = results[^1]; Assert.AreEqual(2.67, last.Tr.Round(8)); Assert.AreEqual(6.1497, last.Atr.Round(4)); Assert.AreEqual(2.5072, last.Atrp.Round(4)); @@ -101,5 +116,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() => - quotes.GetAtr(1)); + Quotes.ToAtr(1)); } diff --git a/tests/indicators/a-d/Atr/Atr.StreamHub.Tests.cs b/tests/indicators/a-d/Atr/Atr.StreamHub.Tests.cs new file mode 100644 index 000000000..0f3e5e419 --- /dev/null +++ b/tests/indicators/a-d/Atr/Atr.StreamHub.Tests.cs @@ -0,0 +1,120 @@ +namespace StreamHub; + +[TestClass] +public class AtrHub : StreamHubTestBase, ITestChainProvider +{ + [TestMethod] + public override void QuoteObserver() + { + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // prefill quotes to provider + for (int i = 0; i < 20; i++) + { + provider.Add(quotesList[i]); + } + + // initialize observer + StreamHub observer = provider + .ToAtr(14); + + // fetch initial results (early) + IReadOnlyList streamList + = observer.Results; + + // emulate adding quotes to provider + for (int i = 20; i < length; i++) + { + // skip one (add later) + if (i == 80) + { + continue; + } + + Quote q = quotesList[i]; + provider.Add(q); + + // resend duplicate quotes + if (i is > 100 and < 105) + { + provider.Add(q); + } + } + + // late arrival + provider.Insert(quotesList[80]); + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // time-series, for comparison + IReadOnlyList seriesList = quotesList + .ToAtr(14); + + // assert, should equal series + streamList.Should().HaveCount(length - 1); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public void ChainProvider() + { + int smaPeriods = 8; + + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // initialize observer + IChainProvider adlHub = provider + .ToAtr(14); + + SmaHub observer = adlHub + .ToSma(smaPeriods); + + // emulate quote stream + for (int i = 0; i < length; i++) + { + provider.Add(quotesList[i]); + } + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // final results + IReadOnlyList streamList + = observer.Results; + + // time-series, for comparison + IReadOnlyList seriesList = quotesList + .ToAtr(14) + .ToSma(smaPeriods); + + // assert, should equal series + streamList.Should().HaveCount(length - 1); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public override void CustomToString() + { + AtrHub hub = new(new QuoteHub(), 20); + hub.ToString().Should().Be("ATR(20)"); + } +} diff --git a/tests/indicators/a-d/AtrStop/AtrStop.Tests.cs b/tests/indicators/a-d/AtrStop/AtrStop.StaticSeries.Tests.cs similarity index 63% rename from tests/indicators/a-d/AtrStop/AtrStop.Tests.cs rename to tests/indicators/a-d/AtrStop/AtrStop.StaticSeries.Tests.cs index 7d53fe6c5..091b89486 100644 --- a/tests/indicators/a-d/AtrStop/AtrStop.Tests.cs +++ b/tests/indicators/a-d/AtrStop/AtrStop.StaticSeries.Tests.cs @@ -1,17 +1,16 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class AtrStopTests : TestBase +public class AtrStop : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { int lookbackPeriods = 21; double multiplier = 3; - List results = quotes - .GetAtrStop(lookbackPeriods, multiplier, EndType.Close) - .ToList(); + IReadOnlyList results = Quotes + .ToAtrStop(lookbackPeriods, multiplier); // proper quantities Assert.AreEqual(502, results.Count); @@ -24,27 +23,27 @@ public void Standard() Assert.AreEqual(null, r20.SellStop); AtrStopResult r21 = results[21]; - Assert.AreEqual(211.13m, r21.AtrStop.Round(4)); + Assert.AreEqual(211.13, r21.AtrStop.Round(4)); Assert.AreEqual(null, r21.BuyStop); Assert.AreEqual(r21.AtrStop, r21.SellStop); AtrStopResult r151 = results[151]; - Assert.AreEqual(232.7861m, r151.AtrStop.Round(4)); + Assert.AreEqual(232.7861, r151.AtrStop.Round(4)); Assert.AreEqual(null, r151.BuyStop); Assert.AreEqual(r151.AtrStop, r151.SellStop); AtrStopResult r152 = results[152]; - Assert.AreEqual(236.3913m, r152.AtrStop.Round(4)); + Assert.AreEqual(236.3913, r152.AtrStop.Round(4)); Assert.AreEqual(r152.AtrStop, r152.BuyStop); Assert.AreEqual(null, r152.SellStop); AtrStopResult r249 = results[249]; - Assert.AreEqual(253.8863m, r249.AtrStop.Round(4)); + Assert.AreEqual(253.8863, r249.AtrStop.Round(4)); Assert.AreEqual(null, r249.BuyStop); Assert.AreEqual(r249.AtrStop, r249.SellStop); AtrStopResult r501 = results[501]; - Assert.AreEqual(246.3232m, r501.AtrStop.Round(4)); + Assert.AreEqual(246.3232, r501.AtrStop.Round(4)); Assert.AreEqual(r501.AtrStop, r501.BuyStop); Assert.AreEqual(null, r501.SellStop); } @@ -55,9 +54,8 @@ public void HighLow() int lookbackPeriods = 21; double multiplier = 3; - List results = quotes - .GetAtrStop(lookbackPeriods, multiplier, EndType.HighLow) - .ToList(); + IReadOnlyList results = Quotes + .ToAtrStop(lookbackPeriods, multiplier, EndType.HighLow); // proper quantities Assert.AreEqual(502, results.Count); @@ -70,53 +68,50 @@ public void HighLow() Assert.AreEqual(null, r20.SellStop); AtrStopResult r21 = results[21]; - Assert.AreEqual(210.23m, r21.AtrStop.Round(4)); + Assert.AreEqual(210.23, r21.AtrStop.Round(4)); Assert.AreEqual(null, r21.BuyStop); Assert.AreEqual(r21.AtrStop, r21.SellStop); AtrStopResult r69 = results[69]; - Assert.AreEqual(221.0594m, r69.AtrStop.Round(4)); + Assert.AreEqual(221.0594, r69.AtrStop.Round(4)); Assert.AreEqual(null, r69.BuyStop); Assert.AreEqual(r69.AtrStop, r69.SellStop); AtrStopResult r70 = results[70]; - Assert.AreEqual(226.4624m, r70.AtrStop.Round(4)); + Assert.AreEqual(226.4624, r70.AtrStop.Round(4)); Assert.AreEqual(r70.AtrStop, r70.BuyStop); Assert.AreEqual(null, r70.SellStop); AtrStopResult r249 = results[249]; - Assert.AreEqual(253.4863m, r249.AtrStop.Round(4)); + Assert.AreEqual(253.4863, r249.AtrStop.Round(4)); Assert.AreEqual(null, r249.BuyStop); Assert.AreEqual(r249.AtrStop, r249.SellStop); AtrStopResult r501 = results[501]; - Assert.AreEqual(252.6932m, r501.AtrStop.Round(4)); + Assert.AreEqual(252.6932, r501.AtrStop.Round(4)); Assert.AreEqual(r501.AtrStop, r501.BuyStop); Assert.AreEqual(null, r501.SellStop); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetAtrStop(7) - .ToList(); + IReadOnlyList r = BadQuotes + .ToAtrStop(7); Assert.AreEqual(502, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetAtrStop() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToAtrStop(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetAtrStop() - .ToList(); + IReadOnlyList r1 = Onequote + .ToAtrStop(); Assert.AreEqual(1, r1.Count); } @@ -127,16 +122,15 @@ public void Condense() int lookbackPeriods = 21; double multiplier = 3; - List results = - quotes.GetAtrStop(lookbackPeriods, multiplier) - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToAtrStop(lookbackPeriods, multiplier) + .Condense(); // assertions Assert.AreEqual(481, results.Count); - AtrStopResult last = results.LastOrDefault(); - Assert.AreEqual(246.3232m, last.AtrStop.Round(4)); + AtrStopResult last = results[^1]; + Assert.AreEqual(246.3232, last.AtrStop.Round(4)); Assert.AreEqual(last.AtrStop, last.BuyStop); Assert.AreEqual(null, last.SellStop); } @@ -147,16 +141,15 @@ public void Removed() int lookbackPeriods = 21; double multiplier = 3; - List results = - quotes.GetAtrStop(lookbackPeriods, multiplier) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToAtrStop(lookbackPeriods, multiplier) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(481, results.Count); - AtrStopResult last = results.LastOrDefault(); - Assert.AreEqual(246.3232m, last.AtrStop.Round(4)); + AtrStopResult last = results[^1]; + Assert.AreEqual(246.3232, last.AtrStop.Round(4)); Assert.AreEqual(last.AtrStop, last.BuyStop); Assert.AreEqual(null, last.SellStop); } @@ -166,10 +159,10 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() - => quotes.GetAtrStop(1)); + => Quotes.ToAtrStop(1)); // bad multiplier Assert.ThrowsException(() - => quotes.GetAtrStop(7, 0)); + => Quotes.ToAtrStop(7, 0)); } } diff --git a/tests/indicators/a-d/AtrStop/AtrStop.StreamHub.Tests.cs b/tests/indicators/a-d/AtrStop/AtrStop.StreamHub.Tests.cs new file mode 100644 index 000000000..539fc18ec --- /dev/null +++ b/tests/indicators/a-d/AtrStop/AtrStop.StreamHub.Tests.cs @@ -0,0 +1,107 @@ +namespace StreamHub; + +[TestClass] +public class AtrStop : StreamHubTestBase +{ + [TestMethod] + public override void QuoteObserver() + { + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // prefill quotes to provider (batch) + provider.Add(quotesList.Take(20)); + + // initialize observer + AtrStopHub observer = provider + .ToAtrStop(); + + observer.Results.Should().HaveCount(20); + + // fetch initial results (early) + IReadOnlyList streamList + = observer.Results; + + // emulate adding quotes to provider + for (int i = 20; i < length; i++) + { + // skip one (add later) + if (i is 30 or 80) + { + continue; + } + + Quote q = quotesList[i]; + provider.Add(q); + + // resend duplicate quotes + if (i is > 100 and < 105) + { + provider.Add(q); + } + } + + // late arrivals + provider.Insert(quotesList[30]); // rebuilds complete series + provider.Insert(quotesList[80]); // rebuilds from last reversal + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // time-series, for comparison + IReadOnlyList seriesList + = quotesList + .ToAtrStop(); + + // assert, should equal series + streamList.Should().HaveCount(length - 1); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public void QuoteObserverHighLow() + { + // simple test, just to check High/Low variant + + // setup quote provider + QuoteHub provider = new(); + + // initialize observer + AtrStopHub observer = provider + .ToAtrStop(endType: EndType.HighLow); + + // add quotes to provider + provider.Add(Quotes); + + // stream results + IReadOnlyList streamList + = observer.Results; + + // time-series, for comparison + IReadOnlyList seriesList + = Quotes + .ToAtrStop(endType: EndType.HighLow); + + // assert, should equal series + streamList.Should().HaveCount(Quotes.Count); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public override void CustomToString() + { + AtrStopHub hub = new(new QuoteHub(), 14, 3, EndType.Close); + hub.ToString().Should().Be("ATR-STOP(14,3,CLOSE)"); + } +} diff --git a/tests/indicators/a-d/Awesome/Awesome.Tests.cs b/tests/indicators/a-d/Awesome/Awesome.StaticSeries.Tests.cs similarity index 57% rename from tests/indicators/a-d/Awesome/Awesome.Tests.cs rename to tests/indicators/a-d/Awesome/Awesome.StaticSeries.Tests.cs index eab128bcc..92982f51e 100644 --- a/tests/indicators/a-d/Awesome/Awesome.Tests.cs +++ b/tests/indicators/a-d/Awesome/Awesome.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class AwesomeTests : TestBase +public class Awesome : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetAwesome(5, 34) - .ToList(); + IReadOnlyList results = Quotes + .ToAwesome(); // proper quantities Assert.AreEqual(502, results.Count); @@ -33,35 +32,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetAwesome() - .ToList(); + .ToAwesome(); Assert.AreEqual(502, results.Count); Assert.AreEqual(469, results.Count(x => x.Oscillator != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetAwesome() - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Oscillator is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetAwesome() - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToAwesome(); Assert.AreEqual(502, results.Count); Assert.AreEqual(468, results.Count(x => x.Oscillator != null)); @@ -70,38 +56,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetAwesome() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToAwesome() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(460, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetAwesome() - .ToList(); + IReadOnlyList r = BadQuotes + .ToAwesome(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Oscillator is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Oscillator is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetAwesome() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToAwesome(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetAwesome() - .ToList(); + IReadOnlyList r1 = Onequote + .ToAwesome(); Assert.AreEqual(1, r1.Count); } @@ -109,15 +91,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetAwesome(5, 34) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToAwesome() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 33, results.Count); - AwesomeResult last = results.LastOrDefault(); + AwesomeResult last = results[^1]; Assert.AreEqual(-17.7692, last.Oscillator.Round(4)); Assert.AreEqual(-7.2763, last.Normalized.Round(4)); } @@ -127,10 +108,10 @@ public void Exceptions() { // bad fast period Assert.ThrowsException(() => - quotes.GetAwesome(0, 34)); + Quotes.ToAwesome(0)); // bad slow period Assert.ThrowsException(() => - quotes.GetAwesome(25, 25)); + Quotes.ToAwesome(25, 25)); } } diff --git a/tests/indicators/a-d/BasicQuote/BasicQuote.Tests.cs b/tests/indicators/a-d/BasicQuote/BasicQuote.Tests.cs deleted file mode 100644 index 45a8472a1..000000000 --- a/tests/indicators/a-d/BasicQuote/BasicQuote.Tests.cs +++ /dev/null @@ -1,74 +0,0 @@ -namespace Tests.Indicators; - -[TestClass] -public class BaseQuoteTests : TestBase -{ - [TestMethod] - public void Standard() - { - // compose basic data - List o = quotes.GetBaseQuote(CandlePart.Open).ToList(); - List h = quotes.GetBaseQuote(CandlePart.High).ToList(); - List l = quotes.GetBaseQuote(CandlePart.Low).ToList(); - List c = quotes.GetBaseQuote(CandlePart.Close).ToList(); - List v = quotes.GetBaseQuote(CandlePart.Volume).ToList(); - List hl = quotes.GetBaseQuote(CandlePart.HL2).ToList(); - List hlc = quotes.GetBaseQuote(CandlePart.HLC3).ToList(); - List oc = quotes.GetBaseQuote(CandlePart.OC2).ToList(); - List ohl = quotes.GetBaseQuote(CandlePart.OHL3).ToList(); - List ohlc = quotes.GetBaseQuote(CandlePart.OHLC4).ToList(); - - // proper quantities - Assert.AreEqual(502, c.Count); - - // samples - BasicData ro = o[501]; - BasicData rh = h[501]; - BasicData rl = l[501]; - BasicData rc = c[501]; - BasicData rv = v[501]; - BasicData rhl = hl[501]; - BasicData rhlc = hlc[501]; - BasicData roc = oc[501]; - BasicData rohl = ohl[501]; - BasicData rohlc = ohlc[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, rhl.Value); - Assert.AreEqual(244.5633, rhlc.Value.Round(4)); - Assert.AreEqual(245.1, roc.Value); - Assert.AreEqual(244.4433, rohl.Value.Round(4)); - Assert.AreEqual(244.6525, rohlc.Value); - } - - [TestMethod] - public void Use() - { - List<(DateTime Date, double Value)> results = quotes - .Use(CandlePart.Close) - .ToList(); - - Assert.AreEqual(502, results.Count); - } - - [TestMethod] - public void Chainor() - { - List results = quotes - .GetBaseQuote(CandlePart.Close) - .GetSma(10) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(493, results.Count(x => x.Sma != null)); - } -} diff --git a/tests/indicators/a-d/Beta/Beta.Tests.cs b/tests/indicators/a-d/Beta/Beta.StaticSeries.Tests.cs similarity index 50% rename from tests/indicators/a-d/Beta/Beta.Tests.cs rename to tests/indicators/a-d/Beta/Beta.StaticSeries.Tests.cs index 67229ba06..c8214d024 100644 --- a/tests/indicators/a-d/Beta/Beta.Tests.cs +++ b/tests/indicators/a-d/Beta/Beta.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class BetaTests : TestBase +public class Beta : StaticSeriesTestBase { [TestMethod] public void All() { - List results = otherQuotes - .GetBeta(quotes, 20, BetaType.All) - .ToList(); + IReadOnlyList results = OtherQuotes + .ToBeta(Quotes, 20, BetaType.All); // proper quantities Assert.AreEqual(502, results.Count); @@ -49,11 +48,10 @@ public void All() } [TestMethod] - public void Standard() + public override void Standard() { - List results = Indicator - .GetBeta(otherQuotes, quotes, 20, BetaType.Standard) - .ToList(); + IReadOnlyList results = OtherQuotes + .ToBeta(Quotes, 20); // proper quantities Assert.AreEqual(502, results.Count); @@ -67,9 +65,8 @@ public void Standard() [TestMethod] public void Up() { - List results = otherQuotes - .GetBeta(quotes, 20, BetaType.Up) - .ToList(); + IReadOnlyList results = OtherQuotes + .ToBeta(Quotes, 20, BetaType.Up); // proper quantities Assert.AreEqual(502, results.Count); @@ -83,9 +80,8 @@ public void Up() [TestMethod] public void Down() { - List results = otherQuotes - .GetBeta(quotes, 20, BetaType.Down) - .ToList(); + IReadOnlyList results = OtherQuotes + .ToBeta(Quotes, 20, BetaType.Down); // proper quantities Assert.AreEqual(502, results.Count); @@ -97,35 +93,22 @@ public void Down() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = otherQuotes + IReadOnlyList results = OtherQuotes .Use(CandlePart.Close) - .GetBeta(quotes.Use(CandlePart.Close), 20) - .ToList(); + .ToBeta(Quotes.Use(CandlePart.Close), 20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Beta != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetBeta(tupleNanny, 6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Beta is double and double.NaN)); - } - [TestMethod] public void Chainor() { - List results = otherQuotes - .GetBeta(quotes, 20) - .GetSma(10) - .ToList(); + IReadOnlyList results = OtherQuotes + .ToBeta(Quotes, 20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(473, results.Count(x => x.Sma != null)); @@ -134,47 +117,42 @@ public void Chainor() [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetBeta(otherQuotes.GetSma(2), 20) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToBeta(OtherQuotes.ToSma(2), 20); Assert.AreEqual(502, results.Count); Assert.AreEqual(481, results.Count(x => x.Beta != null)); - Assert.AreEqual(0, results.Count(x => x.Beta is double and double.NaN)); + Assert.AreEqual(0, results.Count(x => x.Beta is double.NaN)); } [TestMethod] - public void BadData() + public override void BadData() { - List r1 = badQuotes - .GetBeta(badQuotes, 15, BetaType.Standard) - .ToList(); + IReadOnlyList r1 = BadQuotes + .ToBeta(BadQuotes, 15); Assert.AreEqual(502, r1.Count); - Assert.AreEqual(0, r1.Count(x => x.Beta is double and double.NaN)); + Assert.AreEqual(0, r1.Count(x => x.Beta is double.NaN)); - List r2 = badQuotes - .GetBeta(badQuotes, 15, BetaType.Up) - .ToList(); + IReadOnlyList r2 = BadQuotes + .ToBeta(BadQuotes, 15, BetaType.Up); Assert.AreEqual(502, r2.Count); - Assert.AreEqual(0, r2.Count(x => x.BetaUp is double and double.NaN)); + Assert.AreEqual(0, r2.Count(x => x.BetaUp is double.NaN)); - List r3 = badQuotes - .GetBeta(badQuotes, 15, BetaType.Down) - .ToList(); + IReadOnlyList r3 = BadQuotes + .ToBeta(BadQuotes, 15, BetaType.Down); Assert.AreEqual(502, r3.Count); - Assert.AreEqual(0, r3.Count(x => x.BetaDown is double and double.NaN)); + Assert.AreEqual(0, r3.Count(x => x.BetaDown is double.NaN)); } [TestMethod] public void BigData() { - List r = bigQuotes - .GetBeta(bigQuotes, 150, BetaType.All) - .ToList(); + IReadOnlyList r = BigQuotes + .ToBeta(BigQuotes, 150, BetaType.All); Assert.AreEqual(1246, r.Count); } @@ -190,15 +168,12 @@ public void BetaMsft() https://www.nasdaq.com/market-activity/stocks/msft */ - List evalQuotes = TestData.GetMsft().ToList(); - List mktQuotes = TestData.GetSpx().ToList(); + IReadOnlyList evalQuotes = Data.GetMsft(); + IReadOnlyList mktQuotes = Data.GetSpx(); - List results = Indicator - .GetBeta( - evalQuotes.Aggregate(PeriodSize.Month), - mktQuotes.Aggregate(PeriodSize.Month), - 60, BetaType.Standard) - .ToList(); + IReadOnlyList results = evalQuotes + .Aggregate(PeriodSize.Month) + .ToBeta(mktQuotes.Aggregate(PeriodSize.Month), 60); Assert.AreEqual(0.91, results[385].Beta.Round(2)); } @@ -206,15 +181,14 @@ public void BetaMsft() [TestMethod] public void Removed() { - List results = otherQuotes - .GetBeta(quotes, 20) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = OtherQuotes + .ToBeta(Quotes, 20) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 20, results.Count); - BetaResult last = results.LastOrDefault(); + BetaResult last = results[^1]; Assert.AreEqual(1.5123, last.Beta.Round(4)); } @@ -222,9 +196,8 @@ public void Removed() public void SameSame() { // Beta should be 1 if evaluating against self - List results = quotes - .GetBeta(quotes, 20) - .ToList(); + IReadOnlyList results = Quotes + .ToBeta(Quotes, 20); // proper quantities Assert.AreEqual(502, results.Count); @@ -236,49 +209,50 @@ public void SameSame() } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetBeta(noquotes, 5) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToBeta(Noquotes, 5); Assert.AreEqual(0, r0.Count); - List r1 = onequote.GetBeta(onequote, 5).ToList(); + IReadOnlyList r1 = Onequote + .ToBeta(Onequote, 5); + Assert.AreEqual(1, r1.Count); } [TestMethod] public void NoMatch() { - List quoteA = + IReadOnlyList quoteA = [ - new Quote { Date = DateTime.Parse("1/1/2020", EnglishCulture), Close = 1234 }, - new Quote { Date = DateTime.Parse("1/2/2020", EnglishCulture), Close = 1234 }, - new Quote { Date = DateTime.Parse("1/3/2020", EnglishCulture), Close = 1234 }, - new Quote { Date = DateTime.Parse("1/4/2020", EnglishCulture), Close = 1234 }, - new Quote { Date = DateTime.Parse("1/5/2020", EnglishCulture), Close = 1234 }, - new Quote { Date = DateTime.Parse("1/6/2020", EnglishCulture), Close = 1234 }, - new Quote { Date = DateTime.Parse("1/7/2020", EnglishCulture), Close = 1234 }, - new Quote { Date = DateTime.Parse("1/8/2020", EnglishCulture), Close = 1234 }, - new Quote { Date = DateTime.Parse("1/9/2020", EnglishCulture), Close = 1234 } + new(DateTime.Parse("1/1/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/2/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/3/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/4/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/5/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/6/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/7/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/8/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/9/2020", invariantCulture), 0, 0, 0, 1234, 0) ]; - List quoteB = + IReadOnlyList quoteB = [ - new Quote { Date = DateTime.Parse("1/1/2020", EnglishCulture), Close = 1234 }, - new Quote { Date = DateTime.Parse("1/2/2020", EnglishCulture), Close = 1234 }, - new Quote { Date = DateTime.Parse("1/3/2020", EnglishCulture), Close = 1234 }, - new Quote { Date = DateTime.Parse("2/4/2020", EnglishCulture), Close = 1234 }, // abberrant - new Quote { Date = DateTime.Parse("1/5/2020", EnglishCulture), Close = 1234 }, - new Quote { Date = DateTime.Parse("1/6/2020", EnglishCulture), Close = 1234 }, - new Quote { Date = DateTime.Parse("1/7/2020", EnglishCulture), Close = 1234 }, - new Quote { Date = DateTime.Parse("1/8/2020", EnglishCulture), Close = 1234 }, - new Quote { Date = DateTime.Parse("1/9/2020", EnglishCulture), Close = 1234 } + new(DateTime.Parse("1/1/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/2/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/3/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("2/4/2020", invariantCulture), 0, 0, 0, 1234, 0), // abberrant + new(DateTime.Parse("1/5/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/6/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/7/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/8/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/9/2020", invariantCulture), 0, 0, 0, 1234, 0) ]; Assert.ThrowsException(() - => quoteA.GetBeta(quoteB, 3)); + => quoteA.ToBeta(quoteB, 3)); } [TestMethod] @@ -286,12 +260,12 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() - => quotes.GetBeta(otherQuotes, 0)); + => Quotes.ToBeta(OtherQuotes, 0)); // bad evaluation quotes - List eval = TestData.GetCompare(300).ToList(); + IReadOnlyList eval = Data.GetCompare(300).ToList(); Assert.ThrowsException(() - => quotes.GetBeta(eval, 30)); + => Quotes.ToBeta(eval, 30)); } } diff --git a/tests/indicators/a-d/BollingerBands/BollingerBands.Tests.cs b/tests/indicators/a-d/BollingerBands/BollingerBands.StaticSeries.Tests.cs similarity index 62% rename from tests/indicators/a-d/BollingerBands/BollingerBands.Tests.cs rename to tests/indicators/a-d/BollingerBands/BollingerBands.StaticSeries.Tests.cs index abd4cecea..a0fa51e21 100644 --- a/tests/indicators/a-d/BollingerBands/BollingerBands.Tests.cs +++ b/tests/indicators/a-d/BollingerBands/BollingerBands.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class BollingerBandsTests : TestBase +public class BollingerBands : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = - quotes.GetBollingerBands(20, 2) - .ToList(); + IReadOnlyList results = + Quotes.ToBollingerBands(); // proper quantities Assert.AreEqual(502, results.Count); @@ -38,35 +37,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetBollingerBands() - .ToList(); + .ToBollingerBands(); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Sma != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetBollingerBands() - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.UpperBand is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetBollingerBands() - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToBollingerBands(); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.UpperBand != null)); @@ -75,38 +61,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetBollingerBands() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToBollingerBands() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetBollingerBands(15, 3) - .ToList(); + IReadOnlyList r = BadQuotes + .ToBollingerBands(15, 3); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.UpperBand is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.UpperBand is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetBollingerBands() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToBollingerBands(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetBollingerBands() - .ToList(); + IReadOnlyList r1 = Onequote + .ToBollingerBands(); Assert.AreEqual(1, r1.Count); } @@ -114,15 +96,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = - quotes.GetBollingerBands(20, 2) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToBollingerBands() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 19, results.Count); - BollingerBandsResult last = results.LastOrDefault(); + BollingerBandsResult last = results[^1]; Assert.AreEqual(251.8600, last.Sma.Round(4)); Assert.AreEqual(273.7004, last.UpperBand.Round(4)); Assert.AreEqual(230.0196, last.LowerBand.Round(4)); @@ -136,10 +117,10 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - quotes.GetBollingerBands(1)); + Quotes.ToBollingerBands(1)); // bad standard deviation Assert.ThrowsException(() => - quotes.GetBollingerBands(2, 0)); + Quotes.ToBollingerBands(2, 0)); } } diff --git a/tests/indicators/a-d/Bop/Bop.Tests.cs b/tests/indicators/a-d/Bop/Bop.StaticSeries.Tests.cs similarity index 57% rename from tests/indicators/a-d/Bop/Bop.Tests.cs rename to tests/indicators/a-d/Bop/Bop.StaticSeries.Tests.cs index 3f459108a..7169a2238 100644 --- a/tests/indicators/a-d/Bop/Bop.Tests.cs +++ b/tests/indicators/a-d/Bop/Bop.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class BopTests : TestBase +public class Bop : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetBop(14) - .ToList(); + IReadOnlyList results = Quotes + .ToBop(); // proper quantities Assert.AreEqual(502, results.Count); @@ -34,10 +33,9 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetBop(14) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToBop() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(480, results.Count(x => x.Sma != null)); @@ -46,49 +44,45 @@ public void Chainor() [TestMethod] public void NaN() { - IEnumerable r = TestData.GetBtcUsdNan() - .GetBop(50); + IReadOnlyList r = Data.GetBtcUsdNan() + .ToBop(50); - Assert.AreEqual(0, r.Count(x => x.Bop is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Bop is double.NaN)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetBop() - .ToList(); + IReadOnlyList r = BadQuotes + .ToBop(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Bop is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Bop is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetBop() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToBop(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetBop() - .ToList(); + IReadOnlyList r1 = Onequote + .ToBop(); Assert.AreEqual(1, r1.Count); } [TestMethod] public void Removed() { - List results = quotes - .GetBop(14) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToBop() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 13, results.Count); - BopResult last = results.LastOrDefault(); + BopResult last = results[^1]; Assert.AreEqual(-0.292788, last.Bop.Round(6)); } @@ -96,5 +90,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetBop(0)); + => Quotes.ToBop(0)); } diff --git a/tests/indicators/a-d/Cci/Cci.Tests.cs b/tests/indicators/a-d/Cci/Cci.StaticSeries.Tests.cs similarity index 52% rename from tests/indicators/a-d/Cci/Cci.Tests.cs rename to tests/indicators/a-d/Cci/Cci.StaticSeries.Tests.cs index 1154e6fb6..c4550ed6a 100644 --- a/tests/indicators/a-d/Cci/Cci.Tests.cs +++ b/tests/indicators/a-d/Cci/Cci.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class CciTests : TestBase +public class Cci : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetCci(20) - .ToList(); + IReadOnlyList results = Quotes + .ToCci(); // proper quantities Assert.AreEqual(502, results.Count); @@ -22,38 +21,34 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetCci(20) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToCci() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetCci(15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToCci(15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Cci is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Cci is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetCci() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToCci(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetCci() - .ToList(); + IReadOnlyList r1 = Onequote + .ToCci(); Assert.AreEqual(1, r1.Count); } @@ -61,15 +56,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetCci(20) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToCci() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 19, results.Count); - CciResult last = results.LastOrDefault(); + CciResult last = results[^1]; Assert.AreEqual(-52.9946, last.Cci.Round(4)); } @@ -77,5 +71,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetCci(0)); + => Quotes.ToCci(0)); } diff --git a/tests/indicators/a-d/ChaikinOsc/ChaikinOsc.Tests.cs b/tests/indicators/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.Tests.cs similarity index 60% rename from tests/indicators/a-d/ChaikinOsc/ChaikinOsc.Tests.cs rename to tests/indicators/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.Tests.cs index 89faa7093..613c34cb6 100644 --- a/tests/indicators/a-d/ChaikinOsc/ChaikinOsc.Tests.cs +++ b/tests/indicators/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.Tests.cs @@ -1,17 +1,16 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class ChaikinOscTests : TestBase +public class ChaikinOsc : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { int fastPeriods = 3; int slowPeriods = 10; - List results = quotes - .GetChaikinOsc(fastPeriods, slowPeriods) - .ToList(); + IReadOnlyList results = Quotes + .ToChaikinOsc(fastPeriods, slowPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -28,38 +27,34 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetChaikinOsc(3, 10) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToChaikinOsc() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(484, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetChaikinOsc(5, 15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToChaikinOsc(5, 15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Oscillator is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Oscillator is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetChaikinOsc() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToChaikinOsc(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetChaikinOsc() - .ToList(); + IReadOnlyList r1 = Onequote + .ToChaikinOsc(); Assert.AreEqual(1, r1.Count); } @@ -70,15 +65,14 @@ public void Removed() int fastPeriods = 3; int slowPeriods = 10; - List results = quotes - .GetChaikinOsc(fastPeriods, slowPeriods) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToChaikinOsc(fastPeriods, slowPeriods) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (slowPeriods + 100), results.Count); - ChaikinOscResult last = results.LastOrDefault(); + ChaikinOscResult last = results[^1]; Assert.AreEqual(3439986548.42, last.Adl.Round(2)); Assert.AreEqual(0.8052, last.MoneyFlowMultiplier.Round(4)); Assert.AreEqual(118396116.25, last.MoneyFlowVolume.Round(2)); @@ -90,10 +84,10 @@ public void Exceptions() { // bad fast lookback Assert.ThrowsException(() => - quotes.GetChaikinOsc(0)); + Quotes.ToChaikinOsc(0)); // bad slow lookback Assert.ThrowsException(() => - quotes.GetChaikinOsc(10, 5)); + Quotes.ToChaikinOsc(10, 5)); } } diff --git a/tests/indicators/a-d/Chandelier/Chandelier.Tests.cs b/tests/indicators/a-d/Chandelier/Chandelier.StaticSeries.Tests.cs similarity index 54% rename from tests/indicators/a-d/Chandelier/Chandelier.Tests.cs rename to tests/indicators/a-d/Chandelier/Chandelier.StaticSeries.Tests.cs index f100e2a7e..92b8663d0 100644 --- a/tests/indicators/a-d/Chandelier/Chandelier.Tests.cs +++ b/tests/indicators/a-d/Chandelier/Chandelier.StaticSeries.Tests.cs @@ -1,16 +1,15 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class ChandelierTests : TestBase +public class Chandelier : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { int lookbackPeriods = 22; - List longResult = - quotes.GetChandelier(lookbackPeriods, 3) - .ToList(); + IReadOnlyList longResult = + Quotes.ToChandelier(lookbackPeriods); // proper quantities Assert.AreEqual(502, longResult.Count); @@ -24,9 +23,8 @@ public void Standard() Assert.AreEqual(259.0480, b.ChandelierExit.Round(4)); // short - List shortResult = - quotes.GetChandelier(lookbackPeriods, 3, ChandelierType.Short) - .ToList(); + IReadOnlyList shortResult = + Quotes.ToChandelier(lookbackPeriods, 3, ChandelierType.Short); ChandelierResult c = shortResult[501]; Assert.AreEqual(246.4240, c.ChandelierExit.Round(4)); @@ -35,38 +33,34 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetChandelier(22) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToChandelier() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(471, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetChandelier(15, 2) - .ToList(); + IReadOnlyList r = BadQuotes + .ToChandelier(15, 2); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.ChandelierExit is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.ChandelierExit is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetChandelier() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToChandelier(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetChandelier() - .ToList(); + IReadOnlyList r1 = Onequote + .ToChandelier(); Assert.AreEqual(1, r1.Count); } @@ -74,15 +68,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List longResult = quotes - .GetChandelier(22, 3) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToChandelier() + .RemoveWarmupPeriods(); // assertions - Assert.AreEqual(502 - 22, longResult.Count); + Assert.AreEqual(502 - 22, results.Count); - ChandelierResult last = longResult.LastOrDefault(); + ChandelierResult last = results[^1]; Assert.AreEqual(256.5860, last.ChandelierExit.Round(4)); } @@ -91,14 +84,14 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - quotes.GetChandelier(0)); + Quotes.ToChandelier(0)); // bad multiplier Assert.ThrowsException(() => - quotes.GetChandelier(25, 0)); + Quotes.ToChandelier(25, 0)); // bad type Assert.ThrowsException(() => - quotes.GetChandelier(25, 2, (ChandelierType)int.MaxValue)); + Quotes.ToChandelier(25, 2, (ChandelierType)int.MaxValue)); } } diff --git a/tests/indicators/a-d/Chop/Chop.Tests.cs b/tests/indicators/a-d/Chop/Chop.StaticSeries.Tests.cs similarity index 59% rename from tests/indicators/a-d/Chop/Chop.Tests.cs rename to tests/indicators/a-d/Chop/Chop.StaticSeries.Tests.cs index cb316bf39..0563157d0 100644 --- a/tests/indicators/a-d/Chop/Chop.Tests.cs +++ b/tests/indicators/a-d/Chop/Chop.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class ChopTests : TestBase +public class Chop : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetChop(14) - .ToList(); + IReadOnlyList results = Quotes + .ToChop(); // proper quantities Assert.AreEqual(502, results.Count); @@ -31,10 +30,9 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetChop(14) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToChop() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(479, results.Count(x => x.Sma != null)); @@ -44,9 +42,8 @@ public void Chainor() public void SmallLookback() { int lookbackPeriods = 2; - List results = quotes - .GetChop(lookbackPeriods) - .ToList(); + IReadOnlyList results = Quotes + .ToChop(lookbackPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -54,28 +51,25 @@ public void SmallLookback() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetChop(20) - .ToList(); + IReadOnlyList r = BadQuotes + .ToChop(20); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Chop is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Chop is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetChop() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToChop(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetChop() - .ToList(); + IReadOnlyList r1 = Onequote + .ToChop(); Assert.AreEqual(1, r1.Count); } @@ -83,15 +77,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetChop(14) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToChop() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 14, results.Count); - ChopResult last = results.LastOrDefault(); + ChopResult last = results[^1]; Assert.AreEqual(38.6526, last.Chop.Round(4)); } @@ -99,5 +92,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetChop(1)); + => Quotes.ToChop(1)); } diff --git a/tests/indicators/a-d/Cmf/Cmf.Tests.cs b/tests/indicators/a-d/Cmf/Cmf.StaticSeries.Tests.cs similarity index 65% rename from tests/indicators/a-d/Cmf/Cmf.Tests.cs rename to tests/indicators/a-d/Cmf/Cmf.StaticSeries.Tests.cs index 53d1e23fd..f6c5814c0 100644 --- a/tests/indicators/a-d/Cmf/Cmf.Tests.cs +++ b/tests/indicators/a-d/Cmf/Cmf.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class CmfTests : TestBase +public class Cmf : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetCmf(20) - .ToList(); + IReadOnlyList results = Quotes + .ToCmf(); // proper quantities Assert.AreEqual(502, results.Count); @@ -34,48 +33,43 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetCmf(20) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToCmf() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetCmf(15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToCmf(15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Cmf is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Cmf is double.NaN)); } [TestMethod] public void BigData() { - List r = bigQuotes - .GetCmf(150) - .ToList(); + IReadOnlyList r = BigQuotes + .ToCmf(150); Assert.AreEqual(1246, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetCmf() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToCmf(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetCmf() - .ToList(); + IReadOnlyList r1 = Onequote + .ToCmf(); Assert.AreEqual(1, r1.Count); } @@ -83,15 +77,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetCmf(20) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToCmf() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 19, results.Count); - CmfResult last = results.LastOrDefault(); + CmfResult last = results[^1]; Assert.AreEqual(0.8052, last.MoneyFlowMultiplier.Round(4)); Assert.AreEqual(118396116.25, last.MoneyFlowVolume.Round(2)); Assert.AreEqual(-0.123754, last.Cmf.Round(6)); @@ -101,5 +94,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetCmf(0)); + => Quotes.ToCmf(0)); } diff --git a/tests/indicators/a-d/Cmo/Cmo.Tests.cs b/tests/indicators/a-d/Cmo/Cmo.StaticSeries.Tests.cs similarity index 51% rename from tests/indicators/a-d/Cmo/Cmo.Tests.cs rename to tests/indicators/a-d/Cmo/Cmo.StaticSeries.Tests.cs index 52b1b82bc..866f2b762 100644 --- a/tests/indicators/a-d/Cmo/Cmo.Tests.cs +++ b/tests/indicators/a-d/Cmo/Cmo.StaticSeries.Tests.cs @@ -1,19 +1,16 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class CmoTests : TestBase +public class Cmo : StaticSeriesTestBase { + // TODO: test for CMO isUp works as expected + // when there’s no price change + [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetCmo(14) - .ToList(); - - foreach (CmoResult r in results) - { - Console.WriteLine($"{r.Date:d},{r.Cmo:N4}"); - } + IReadOnlyList results = Quotes + .ToCmo(14); // proper quantities Assert.AreEqual(502, results.Count); @@ -34,35 +31,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetCmo(14) - .ToList(); + .ToCmo(14); Assert.AreEqual(502, results.Count); Assert.AreEqual(488, results.Count(x => x.Cmo != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetCmo(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Cmo is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetCmo(20) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToCmo(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(481, results.Count(x => x.Cmo != null)); @@ -71,38 +55,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetCmo(20) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToCmo(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(473, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetCmo(35) - .ToList(); + IReadOnlyList r = BadQuotes + .ToCmo(35); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Cmo is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Cmo is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetCmo(5) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToCmo(5); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetCmo(5) - .ToList(); + IReadOnlyList r1 = Onequote + .ToCmo(5); Assert.AreEqual(1, r1.Count); } @@ -110,15 +90,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetCmo(14) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToCmo(14) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(488, results.Count); - CmoResult last = results.LastOrDefault(); + CmoResult last = results[^1]; Assert.AreEqual(-26.7502, last.Cmo.Round(4)); } @@ -126,5 +105,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetCmo(0)); + => Quotes.ToCmo(0)); } diff --git a/tests/indicators/a-d/ConnorsRsi/ConnorsRsi.Tests.cs b/tests/indicators/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.Tests.cs similarity index 60% rename from tests/indicators/a-d/ConnorsRsi/ConnorsRsi.Tests.cs rename to tests/indicators/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.Tests.cs index e28b2b9c2..3e1c6e07c 100644 --- a/tests/indicators/a-d/ConnorsRsi/ConnorsRsi.Tests.cs +++ b/tests/indicators/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.Tests.cs @@ -1,19 +1,18 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class ConnorsRsiTests : TestBase +public class ConnorsRsi : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { int rsiPeriods = 3; int streakPeriods = 2; int rankPeriods = 100; int startPeriod = Math.Max(rsiPeriods, Math.Max(streakPeriods, rankPeriods)) + 2; - List results1 = - quotes.GetConnorsRsi(rsiPeriods, streakPeriods, rankPeriods) - .ToList(); + IReadOnlyList results1 = Quotes + .ToConnorsRsi(rsiPeriods, streakPeriods, rankPeriods); // proper quantities Assert.AreEqual(502, results1.Count); @@ -27,7 +26,7 @@ public void Standard() Assert.AreEqual(74.7662, r1.ConnorsRsi.Round(4)); // different parameters - List results2 = quotes.GetConnorsRsi(14, 20, 10).ToList(); + IReadOnlyList results2 = Quotes.ToConnorsRsi(14, 20, 10).ToList(); ConnorsRsiResult r2 = results2[501]; Assert.AreEqual(42.0773, r2.Rsi.Round(4)); Assert.AreEqual(52.7386, r2.RsiStreak.Round(4)); @@ -36,35 +35,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetConnorsRsi() - .ToList(); + .ToConnorsRsi(); Assert.AreEqual(502, results.Count); Assert.AreEqual(401, results.Count(x => x.ConnorsRsi != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetConnorsRsi() - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.ConnorsRsi is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetConnorsRsi() - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToConnorsRsi(); Assert.AreEqual(502, results.Count); Assert.AreEqual(400, results.Count(x => x.ConnorsRsi != null)); @@ -73,38 +59,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetConnorsRsi() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToConnorsRsi() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(392, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetConnorsRsi(4, 3, 25) - .ToList(); + IReadOnlyList r = BadQuotes + .ToConnorsRsi(4, 3, 25); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Rsi is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Rsi is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetConnorsRsi() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToConnorsRsi(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetConnorsRsi() - .ToList(); + IReadOnlyList r1 = Onequote + .ToConnorsRsi(); Assert.AreEqual(1, r1.Count); } @@ -119,15 +101,14 @@ public void Removed() // TODO: I don't think this is right, inconsistent int removePeriods = Math.Max(rsiPeriods, Math.Max(streakPeriods, rankPeriods)) + 2; - List results = - quotes.GetConnorsRsi(rsiPeriods, streakPeriods, rankPeriods) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToConnorsRsi(rsiPeriods, streakPeriods, rankPeriods) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - removePeriods + 1, results.Count); - ConnorsRsiResult last = results.LastOrDefault(); + ConnorsRsiResult last = results[^1]; Assert.AreEqual(68.8087, last.Rsi.Round(4)); Assert.AreEqual(67.4899, last.RsiStreak.Round(4)); Assert.AreEqual(88.0000, last.PercentRank.Round(4)); @@ -139,14 +120,14 @@ public void Exceptions() { // bad RSI period Assert.ThrowsException(() => - quotes.GetConnorsRsi(1, 2, 100)); + Quotes.ToConnorsRsi(1)); // bad Streak period Assert.ThrowsException(() => - quotes.GetConnorsRsi(3, 1, 100)); + Quotes.ToConnorsRsi(3, 1)); // bad Rank period Assert.ThrowsException(() => - quotes.GetConnorsRsi(3, 2, 1)); + Quotes.ToConnorsRsi(3, 2, 1)); } } diff --git a/tests/indicators/a-d/Correlation/Correlation.Tests.cs b/tests/indicators/a-d/Correlation/Correlation.StaticSeries.Tests.cs similarity index 54% rename from tests/indicators/a-d/Correlation/Correlation.Tests.cs rename to tests/indicators/a-d/Correlation/Correlation.StaticSeries.Tests.cs index 53c60c0a3..5502f29db 100644 --- a/tests/indicators/a-d/Correlation/Correlation.Tests.cs +++ b/tests/indicators/a-d/Correlation/Correlation.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class CorrelationTests : TestBase +public class Correlation : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = - quotes.GetCorrelation(otherQuotes, 20) - .ToList(); + IReadOnlyList results = Quotes + .ToCorrelation(OtherQuotes, 20); // proper quantities // should always be the same number of results as there is quotes @@ -34,35 +33,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetCorrelation(otherQuotes.Use(CandlePart.Close), 20) - .ToList(); + .ToCorrelation(OtherQuotes.Use(CandlePart.Close), 20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Correlation != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetCorrelation(tupleNanny, 6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Correlation is double and double.NaN)); - } - [TestMethod] public void Chainor() { - List results = quotes - .GetCorrelation(otherQuotes, 20) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToCorrelation(OtherQuotes, 20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); @@ -71,49 +57,44 @@ public void Chainor() [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetCorrelation(otherQuotes.GetSma(2), 20) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToCorrelation(OtherQuotes.ToSma(2), 20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Correlation != null)); - Assert.AreEqual(0, results.Count(x => x.Correlation is double and double.NaN)); + Assert.AreEqual(0, results.Count(x => x.Correlation is double.NaN)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetCorrelation(badQuotes, 15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToCorrelation(BadQuotes, 15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Correlation is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Correlation is double.NaN)); } [TestMethod] public void BigData() { - List r = bigQuotes - .GetCorrelation(bigQuotes, 150) - .ToList(); + IReadOnlyList r = BigQuotes + .ToCorrelation(BigQuotes, 150); Assert.AreEqual(1246, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetCorrelation(noquotes, 10) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToCorrelation(Noquotes, 10); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetCorrelation(onequote, 10) - .ToList(); + IReadOnlyList r1 = Onequote + .ToCorrelation(Onequote, 10); Assert.AreEqual(1, r1.Count); } @@ -121,15 +102,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetCorrelation(otherQuotes, 20) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToCorrelation(OtherQuotes, 20) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 19, results.Count); - CorrResult last = results.LastOrDefault(); + CorrResult last = results[^1]; Assert.AreEqual(0.8460, last.Correlation.Round(4)); Assert.AreEqual(0.7157, last.RSquared.Round(4)); } @@ -139,15 +119,15 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - quotes.GetCorrelation(otherQuotes, 0)); + Quotes.ToCorrelation(OtherQuotes, 0)); // bad eval quotes - IEnumerable eval = TestData.GetCompare(300); + IReadOnlyList eval = Data.GetCompare(300); Assert.ThrowsException(() => - quotes.GetCorrelation(eval, 30)); + Quotes.ToCorrelation(eval, 30)); // mismatched quotes Assert.ThrowsException(() => - mismatchQuotes.GetCorrelation(otherQuotes, 20)); + MismatchQuotes.ToCorrelation(OtherQuotes, 20)); } } diff --git a/tests/indicators/a-d/Dema/Dema.Tests.cs b/tests/indicators/a-d/Dema/Dema.StaticSeries.Tests.cs similarity index 53% rename from tests/indicators/a-d/Dema/Dema.Tests.cs rename to tests/indicators/a-d/Dema/Dema.StaticSeries.Tests.cs index b0a040b7c..b98a3b5f7 100644 --- a/tests/indicators/a-d/Dema/Dema.Tests.cs +++ b/tests/indicators/a-d/Dema/Dema.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class DemaTests : TestBase +public class Dema : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetDema(20) - .ToList(); + IReadOnlyList results = Quotes + .ToDema(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -29,35 +28,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetDema(20) - .ToList(); + .ToDema(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Dema != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetDema(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Dema is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetDema(20) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToDema(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Dema != null)); @@ -66,38 +52,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetDema(20) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToDema(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetDema(15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToDema(15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Dema is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Dema is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetDema(5) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToDema(5); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetDema(5) - .ToList(); + IReadOnlyList r1 = Onequote + .ToDema(5); Assert.AreEqual(1, r1.Count); } @@ -105,15 +87,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetDema(20) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToDema(20) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (40 + 100), results.Count); - DemaResult last = results.LastOrDefault(); + DemaResult last = results[^1]; Assert.AreEqual(241.1677, last.Dema.Round(4)); } @@ -121,5 +102,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetDema(0)); + => Quotes.ToDema(0)); } diff --git a/tests/indicators/a-d/Doji/Doji.Tests.cs b/tests/indicators/a-d/Doji/Doji.StaticSeries.Tests.cs similarity index 67% rename from tests/indicators/a-d/Doji/Doji.Tests.cs rename to tests/indicators/a-d/Doji/Doji.StaticSeries.Tests.cs index 4e43d65b2..6b0622535 100644 --- a/tests/indicators/a-d/Doji/Doji.Tests.cs +++ b/tests/indicators/a-d/Doji/Doji.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class DojiTests : TestBase +public class Doji : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetDoji(0.1) - .ToList(); + IReadOnlyList results = Quotes + .ToDoji(); // proper quantities Assert.AreEqual(502, results.Count); @@ -41,27 +40,24 @@ public void Standard() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetDoji() - .ToList(); + IReadOnlyList r = BadQuotes + .ToDoji(); Assert.AreEqual(502, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetDoji() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToDoji(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetDoji() - .ToList(); + IReadOnlyList r1 = Onequote + .ToDoji(); Assert.AreEqual(1, r1.Count); } @@ -69,12 +65,11 @@ public void NoQuotes() [TestMethod] public void Condense() { - List r = quotes - .GetDoji(0.1) - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToDoji() + .Condense(); - Assert.AreEqual(112, r.Count); + Assert.AreEqual(112, results.Count); } [TestMethod] @@ -82,9 +77,9 @@ public void Exceptions() { // bad maximum change value Assert.ThrowsException(() => - quotes.GetDoji(-0.00001)); + Quotes.ToDoji(-0.00001)); Assert.ThrowsException(() => - quotes.GetDoji(0.50001)); + Quotes.ToDoji(0.50001)); } } diff --git a/tests/indicators/a-d/Donchian/Donchian.Tests.cs b/tests/indicators/a-d/Donchian/Donchian.StaticSeries.Tests.cs similarity index 74% rename from tests/indicators/a-d/Donchian/Donchian.Tests.cs rename to tests/indicators/a-d/Donchian/Donchian.StaticSeries.Tests.cs index d41056dad..c16cee7b4 100644 --- a/tests/indicators/a-d/Donchian/Donchian.Tests.cs +++ b/tests/indicators/a-d/Donchian/Donchian.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class DonchianTests : TestBase +public class Donchian : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetDonchian(20) - .ToList(); + IReadOnlyList results = Quotes + .ToDonchian(); // proper quantities Assert.AreEqual(502, results.Count); @@ -50,27 +49,24 @@ public void Standard() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetDonchian(15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToDonchian(15); Assert.AreEqual(502, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetDonchian() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToDonchian(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetDonchian() - .ToList(); + IReadOnlyList r1 = Onequote + .ToDonchian(); Assert.AreEqual(1, r1.Count); } @@ -78,15 +74,14 @@ public void NoQuotes() [TestMethod] public void Condense() { - List r = quotes - .GetDonchian(20) - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToDonchian() + .Condense(); // assertions - Assert.AreEqual(502 - 20, r.Count); + Assert.AreEqual(502 - 20, results.Count); - DonchianResult last = r.LastOrDefault(); + DonchianResult last = results[^1]; Assert.AreEqual(251.5050m, last.Centerline.Round(4)); Assert.AreEqual(273.5900m, last.UpperBand.Round(4)); Assert.AreEqual(229.4200m, last.LowerBand.Round(4)); @@ -96,15 +91,14 @@ public void Condense() [TestMethod] public void Removed() { - List results = quotes - .GetDonchian(20) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToDonchian() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 20, results.Count); - DonchianResult last = results.LastOrDefault(); + DonchianResult last = results[^1]; Assert.AreEqual(251.5050m, last.Centerline.Round(4)); Assert.AreEqual(273.5900m, last.UpperBand.Round(4)); Assert.AreEqual(229.4200m, last.LowerBand.Round(4)); @@ -115,5 +109,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetDonchian(0)); + => Quotes.ToDonchian(0)); } diff --git a/tests/indicators/a-d/Dpo/Dpo.Tests.cs b/tests/indicators/a-d/Dpo/Dpo.StaticSeries.Tests.cs similarity index 51% rename from tests/indicators/a-d/Dpo/Dpo.Tests.cs rename to tests/indicators/a-d/Dpo/Dpo.StaticSeries.Tests.cs index 324b2ec69..08628d406 100644 --- a/tests/indicators/a-d/Dpo/Dpo.Tests.cs +++ b/tests/indicators/a-d/Dpo/Dpo.StaticSeries.Tests.cs @@ -1,10 +1,10 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class DpoTests : TestBase +public class Dpo : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { // get expected data List qot = []; @@ -17,22 +17,14 @@ public void Standard() for (int i = 0; i < csvData.Count; i++) { string[] csv = csvData[i].Split(","); - DateTime date = Convert.ToDateTime(csv[1], EnglishCulture); + DateTime date = Convert.ToDateTime(csv[1], invariantCulture); - qot.Add(new Quote { - Date = date, - Close = csv[5].ToDecimal() - }); - - exp.Add(new DpoResult(date) { - Sma = csv[6].ToDoubleNull(), - Dpo = csv[7].ToDoubleNull() - }); + qot.Add(new Quote(date, 0, 0, 0, Close: csv[5].ToDecimal(), 0)); + exp.Add(new(date, csv[7].ToDoubleNull(), csv[6].ToDoubleNull())); } // calculate actual data - List act = qot.GetDpo(14) - .ToList(); + IReadOnlyList act = qot.ToDpo(14); // assertions Assert.AreEqual(exp.Count, act.Count); @@ -43,42 +35,29 @@ public void Standard() DpoResult e = exp[i]; DpoResult a = act[i]; - Assert.AreEqual(e.Date, a.Date); + Assert.AreEqual(e.Timestamp, a.Timestamp); Assert.AreEqual(e.Sma, a.Sma.Round(5), $"at index {i}"); Assert.AreEqual(e.Dpo, a.Dpo.Round(5), $"at index {i}"); } } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetDpo(14) - .ToList(); + .ToDpo(14); Assert.AreEqual(502, results.Count); Assert.AreEqual(489, results.Count(x => x.Dpo != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetDpo(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Dpo is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetDpo(14) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToDpo(14); Assert.AreEqual(502, results.Count); Assert.AreEqual(488, results.Count(x => x.Dpo != null)); @@ -87,38 +66,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetDpo(14) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToDpo(14) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(480, results.Count(x => x.Sma is not null and not double.NaN)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetDpo(5) - .ToList(); + IReadOnlyList r = BadQuotes + .ToDpo(5); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Dpo is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Dpo is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetDpo(5) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToDpo(5); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetDpo(5) - .ToList(); + IReadOnlyList r1 = Onequote + .ToDpo(5); Assert.AreEqual(1, r1.Count); } @@ -127,5 +102,5 @@ public void NoQuotes() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetDpo(0)); + => Quotes.ToDpo(0)); } diff --git a/tests/indicators/a-d/Dynamic/Dynamic.Tests.cs b/tests/indicators/a-d/Dynamic/Dynamic.StaticSeries.Tests.cs similarity index 54% rename from tests/indicators/a-d/Dynamic/Dynamic.Tests.cs rename to tests/indicators/a-d/Dynamic/Dynamic.StaticSeries.Tests.cs index a3fb16f0f..6f2a47247 100644 --- a/tests/indicators/a-d/Dynamic/Dynamic.Tests.cs +++ b/tests/indicators/a-d/Dynamic/Dynamic.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class McGinleyDynamicTests : TestBase +public class McGinleyDynamic : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetDynamic(14) - .ToList(); + IReadOnlyList results = Quotes + .ToDynamic(14); // assertions Assert.AreEqual(502, results.Count); @@ -29,36 +28,23 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetDynamic(20) - .ToList(); + .ToDynamic(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(501, results.Count(x => x.Dynamic != null)); - Assert.AreEqual(0, results.Count(x => x.Dynamic is double and double.NaN)); - } - - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetDynamic(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Dynamic is double and double.NaN)); + Assert.AreEqual(0, results.Count(x => x.Dynamic is double.NaN)); } [TestMethod] public void Chainee() { - List results = quotes - .GetSma(10) - .GetDynamic(14) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(10) + .ToDynamic(14); Assert.AreEqual(502, results.Count); Assert.AreEqual(492, results.Count(x => x.Dynamic != null)); @@ -67,38 +53,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetDynamic(14) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToDynamic(14) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(492, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetDynamic(15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToDynamic(15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Dynamic is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Dynamic is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetDynamic(14) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToDynamic(14); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetDynamic(14) - .ToList(); + IReadOnlyList r1 = Onequote + .ToDynamic(14); Assert.AreEqual(1, r1.Count); } @@ -108,10 +90,10 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() - => quotes.GetDynamic(0)); + => Quotes.ToDynamic(0)); // bad k-factor Assert.ThrowsException(() - => quotes.GetDynamic(14, 0)); + => Quotes.ToDynamic(14, 0)); } } diff --git a/tests/indicators/e-k/ElderRay/ElderRay.Tests.cs b/tests/indicators/e-k/ElderRay/ElderRay.StaticSeries.Tests.cs similarity index 70% rename from tests/indicators/e-k/ElderRay/ElderRay.Tests.cs rename to tests/indicators/e-k/ElderRay/ElderRay.StaticSeries.Tests.cs index 34af7b32b..f857c4df9 100644 --- a/tests/indicators/e-k/ElderRay/ElderRay.Tests.cs +++ b/tests/indicators/e-k/ElderRay/ElderRay.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class ElderRayTests : TestBase +public class ElderRay : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetElderRay(13) - .ToList(); + IReadOnlyList results = Quotes + .ToElderRay(); // proper quantities Assert.AreEqual(502, results.Count); @@ -50,38 +49,34 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetElderRay(13) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToElderRay() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(481, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetElderRay() - .ToList(); + IReadOnlyList r = BadQuotes + .ToElderRay(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.BullPower is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.BullPower is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetElderRay() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToElderRay(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetElderRay() - .ToList(); + IReadOnlyList r1 = Onequote + .ToElderRay(); Assert.AreEqual(1, r1.Count); } @@ -89,15 +84,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetElderRay(13) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToElderRay() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (100 + 13), results.Count); - ElderRayResult last = results.LastOrDefault(); + ElderRayResult last = results[^1]; Assert.AreEqual(246.0129, last.Ema.Round(4)); Assert.AreEqual(-0.4729, last.BullPower.Round(4)); Assert.AreEqual(-3.1429, last.BearPower.Round(4)); @@ -107,5 +101,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetElderRay(0)); + => Quotes.ToElderRay(0)); } diff --git a/tests/indicators/e-k/Ema/Ema.Increments.Tests.cs b/tests/indicators/e-k/Ema/Ema.Increments.Tests.cs new file mode 100644 index 000000000..b19ce4c69 --- /dev/null +++ b/tests/indicators/e-k/Ema/Ema.Increments.Tests.cs @@ -0,0 +1,72 @@ +namespace Increments; + +[TestClass] +public class Ema : IncrementsTestBase +{ + private const int lookbackPeriods = 14; + + private static readonly IReadOnlyList reusables + = Quotes + .Cast() + .ToList(); + + private static readonly IReadOnlyList series + = Quotes.ToEma(lookbackPeriods); + + [TestMethod] + public void FromReusableSplit() + { + EmaList sut = new(lookbackPeriods); + + foreach (IReusable item in reusables) + { + sut.Add(item.Timestamp, item.Value); + } + + sut.Should().HaveCount(Quotes.Count); + sut.Should().BeEquivalentTo(series); + } + + [TestMethod] + public void FromReusableItem() + { + EmaList sut = new(lookbackPeriods); + + foreach (IReusable item in reusables) { sut.Add(item); } + + sut.Should().HaveCount(Quotes.Count); + sut.Should().BeEquivalentTo(series); + } + + [TestMethod] + public void FromReusableBatch() + { + EmaList sut = new(lookbackPeriods) { reusables }; + + sut.Should().HaveCount(Quotes.Count); + sut.Should().BeEquivalentTo(series); + } + + [TestMethod] + public override void FromQuote() + { + EmaList sut = new(lookbackPeriods); + + foreach (Quote q in Quotes) { sut.Add(q); } + + sut.Should().HaveCount(Quotes.Count); + sut.Should().BeEquivalentTo(series); + } + + [TestMethod] + public override void FromQuoteBatch() + { + EmaList sut = new(lookbackPeriods) { Quotes }; + + IReadOnlyList series + = Quotes.ToEma(lookbackPeriods); + + sut.Should().HaveCount(Quotes.Count); + sut.Should().BeEquivalentTo(series); + } +} diff --git a/tests/indicators/e-k/Ema/Ema.Obs.Tests.cs b/tests/indicators/e-k/Ema/Ema.Obs.Tests.cs deleted file mode 100644 index a62556b1e..000000000 --- a/tests/indicators/e-k/Ema/Ema.Obs.Tests.cs +++ /dev/null @@ -1,100 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - -namespace Tests.Indicators; - -[TestClass] -public class EmaStreamTests : TestBase -{ - [TestMethod] - public void Standard() - { - List quotesList = quotes - .ToSortedList(); - - int length = quotesList.Count; - - // time-series, for comparison - List seriesList = quotes - .GetEma(20) - .ToList(); - - // setup quote provider - QuoteProvider provider = new(); - - // initialize EMA observer - EmaObserver observer = provider - .GetEma(20); - - // fetch initial results - IEnumerable results - = observer.Results; - - // emulate adding quotes to provider - for (int i = 0; i < length; i++) - { - Quote q = quotesList[i]; - provider.Add(q); - } - - // final results - List resultsList - = results.ToList(); - - // assert, should equal series - for (int i = 0; i < seriesList.Count; i++) - { - EmaResult s = seriesList[i]; - EmaResult r = resultsList[i]; - - Assert.AreEqual(s.Date, r.Date); - Assert.AreEqual(s.Ema, r.Ema); - } - - observer.Unsubscribe(); - provider.EndTransmission(); - } - - [TestMethod] - public void Usee() - { - List quotesList = quotes - .ToSortedList(); - - int length = quotesList.Count; - - // time-series, for comparison - List staticEma = quotes - .Use(CandlePart.OC2) - .GetEma(11) - .ToList(); - - // setup quote provider - QuoteProvider provider = new(); - - // initialize EMA observer - List streamEma = provider - .Use(CandlePart.OC2) - .GetEma(11) - .ProtectedResults; - - // emulate adding quotes to provider - for (int i = 0; i < length; i++) - { - provider.Add(quotesList[i]); - } - - provider.EndTransmission(); - - // assert, should equal series - for (int i = 0; i < length; i++) - { - EmaResult t = staticEma[i]; - EmaResult s = streamEma[i]; - - Assert.AreEqual(t.Date, s.Date); - Assert.AreEqual(t.Ema, s.Ema); - } - } -} diff --git a/tests/indicators/e-k/Ema/Ema.Static.Tests.cs b/tests/indicators/e-k/Ema/Ema.StaticSeries.Tests.cs similarity index 55% rename from tests/indicators/e-k/Ema/Ema.Static.Tests.cs rename to tests/indicators/e-k/Ema/Ema.StaticSeries.Tests.cs index 7987bae6d..39a670a7f 100644 --- a/tests/indicators/e-k/Ema/Ema.Static.Tests.cs +++ b/tests/indicators/e-k/Ema/Ema.StaticSeries.Tests.cs @@ -1,14 +1,20 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class EmaStaticTests : TestBase +public class EmaTests : StaticSeriesTestBase { [TestMethod] - public void Standard() + public void Increment() { - List results = quotes - .GetEma(20) - .ToList(); + double ema = Ema.Increment(20, 217.5693, 222.10); + + Assert.AreEqual(218.0008, ema.Round(4)); + } + + [TestMethod] + public override void Standard() + { + IReadOnlyList results = Quotes.ToEma(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -28,10 +34,9 @@ public void Standard() [TestMethod] public void UsePart() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Open) - .GetEma(20) - .ToList(); + .ToEma(20); // assertions @@ -42,77 +47,62 @@ public void UsePart() // sample values EmaResult r29 = results[29]; - Assert.AreEqual(216.2643, NullMath.Round(r29.Ema, 4)); + Assert.AreEqual(216.2643, r29.Ema.Round(4)); EmaResult r249 = results[249]; - Assert.AreEqual(255.4875, NullMath.Round(r249.Ema, 4)); + Assert.AreEqual(255.4875, r249.Ema.Round(4)); EmaResult r501 = results[501]; - Assert.AreEqual(249.9157, NullMath.Round(r501.Ema, 4)); + Assert.AreEqual(249.9157, r501.Ema.Round(4)); } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetEma(20) - .ToList(); + .ToEma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Ema != null)); - Assert.AreEqual(0, results.Count(x => x.Ema is double and double.NaN)); - } - - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetEma(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Ema is double and double.NaN)); + Assert.AreEqual(0, results.Count(x => x.Ema is double.NaN)); } [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetEma(20) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToEma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Ema != null)); - Assert.AreEqual(0, results.Count(x => x.Ema is double and double.NaN)); + Assert.AreEqual(0, results.Count(x => x.Ema is double.NaN)); } [TestMethod] public void Chainor() { - List results = quotes - .GetEma(20) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToEma(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); - Assert.AreEqual(0, results.Count(x => x.Sma is double and double.NaN)); + Assert.AreEqual(0, results.Count(x => x.Sma is double.NaN)); } [TestMethod] public void ChaineeMore() { - List results = quotes - .GetRsi(14) - .GetEma(20) - .ToList(); + IReadOnlyList results = Quotes + .ToRsi() + .ToEma(20); // assertions Assert.AreEqual(502, results.Count); Assert.AreEqual(469, results.Count(x => x.Ema != null)); - Assert.AreEqual(0, results.Count(x => x.Ema is double and double.NaN)); + Assert.AreEqual(0, results.Count(x => x.Ema is double.NaN)); // sample values EmaResult r32 = results[32]; @@ -129,28 +119,22 @@ public void ChaineeMore() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetEma(15) - .ToList(); + IReadOnlyList r = BadQuotes.ToEma(15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Ema is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Ema is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetEma(10) - .ToList(); + IReadOnlyList r0 = Noquotes.ToEma(10); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetEma(10) - .ToList(); + IReadOnlyList r1 = Onequote.ToEma(10); Assert.AreEqual(1, r1.Count); } @@ -158,15 +142,13 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetEma(20) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes.ToEma(20) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (20 + 100), results.Count); - EmaResult last = results.LastOrDefault(); + EmaResult last = results[^1]; Assert.AreEqual(249.3519, last.Ema.Round(4)); } @@ -174,5 +156,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetEma(0)); + => Quotes.ToEma(0)); } diff --git a/tests/indicators/e-k/Ema/Ema.StreamHub.Tests.cs b/tests/indicators/e-k/Ema/Ema.StreamHub.Tests.cs new file mode 100644 index 000000000..60354ebfb --- /dev/null +++ b/tests/indicators/e-k/Ema/Ema.StreamHub.Tests.cs @@ -0,0 +1,176 @@ +namespace StreamHub; + +[TestClass] +public class EmaHub : StreamHubTestBase, ITestChainObserver, ITestChainProvider +{ + [TestMethod] + public override void QuoteObserver() + { + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // prefill quotes to provider + for (int i = 0; i < 20; i++) + { + provider.Add(quotesList[i]); + } + + // initialize observer + EmaHub observer = provider + .ToEma(5); + + // fetch initial results (early) + IReadOnlyList streamList + = observer.Results; + + // emulate adding quotes to provider + for (int i = 20; i < length; i++) + { + // skip one (add later) + if (i == 80) + { + continue; + } + + Quote q = quotesList[i]; + provider.Add(q); + + // resend duplicate quotes + if (i is > 100 and < 105) + { + provider.Add(q); + } + } + + // late arrival + provider.Insert(quotesList[80]); + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // time-series, for comparison + IReadOnlyList seriesList = quotesList.ToEma(5); + + // assert, should equal series + streamList.Should().HaveCount(length - 1); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public void ChainObserver() + { + int emaPeriods = 12; + int smaPeriods = 8; + + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // initialize observer + EmaHub observer = provider + .ToSma(smaPeriods) + .ToEma(emaPeriods); + + // emulate quote stream + for (int i = 0; i < length; i++) + { + provider.Add(quotesList[i]); + } + + // final results + IReadOnlyList streamList + = observer.Results; + + // time-series, for comparison + IReadOnlyList seriesList + = quotesList + .ToSma(smaPeriods) + .ToEma(emaPeriods); + + // assert, should equal series + streamList.Should().HaveCount(length); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public void ChainProvider() + { + int emaPeriods = 20; + int smaPeriods = 10; + + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // initialize observer + SmaHub observer = provider + .ToEma(emaPeriods) + .ToSma(smaPeriods); + + // emulate adding quotes to provider + for (int i = 0; i < length; i++) + { + // skip one (add later) + if (i == 80) + { + continue; + } + + Quote q = quotesList[i]; + provider.Add(q); + + // resend duplicate quotes + if (i is > 100 and < 105) + { + provider.Add(q); + } + } + + // late arrival + provider.Insert(quotesList[80]); + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // final results + IReadOnlyList streamList + = observer.Results; + + // time-series, for comparison + IReadOnlyList seriesList + = quotesList.ToEma(emaPeriods) + .ToSma(smaPeriods); + + // assert, should equal series + streamList.Should().HaveCount(length - 1); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public override void CustomToString() + { + EmaHub hub = new(new QuoteHub(), 14); + hub.ToString().Should().Be("EMA(14)"); + } +} diff --git a/tests/indicators/e-k/Epma/Epma.Tests.cs b/tests/indicators/e-k/Epma/Epma.StaticSeries.Tests.cs similarity index 54% rename from tests/indicators/e-k/Epma/Epma.Tests.cs rename to tests/indicators/e-k/Epma/Epma.StaticSeries.Tests.cs index 5db2e5478..6756a5159 100644 --- a/tests/indicators/e-k/Epma/Epma.Tests.cs +++ b/tests/indicators/e-k/Epma/Epma.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class EpmaTests : TestBase +public class Epma : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetEpma(20) - .ToList(); + IReadOnlyList results = Quotes + .ToEpma(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -32,35 +31,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetEpma(20) - .ToList(); + .ToEpma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Epma != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetEpma(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Epma is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetEpma(20) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToEpma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Epma != null)); @@ -69,38 +55,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetEpma(20) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToEpma(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetEpma(15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToEpma(15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Epma is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Epma is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetEpma(5) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToEpma(5); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetEpma(5) - .ToList(); + IReadOnlyList r1 = Onequote + .ToEpma(5); Assert.AreEqual(1, r1.Count); } @@ -108,15 +90,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetEpma(20) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToEpma(20) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 19, results.Count); - EpmaResult last = results.LastOrDefault(); + EpmaResult last = results[^1]; Assert.AreEqual(235.8131, last.Epma.Round(4)); } @@ -124,5 +105,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetEpma(0)); + => Quotes.ToEpma(0)); } diff --git a/tests/indicators/e-k/Fcb/Fcb.Tests.cs b/tests/indicators/e-k/Fcb/Fcb.StaticSeries.Tests.cs similarity index 68% rename from tests/indicators/e-k/Fcb/Fcb.Tests.cs rename to tests/indicators/e-k/Fcb/Fcb.StaticSeries.Tests.cs index ed51d3f43..e0742cacd 100644 --- a/tests/indicators/e-k/Fcb/Fcb.Tests.cs +++ b/tests/indicators/e-k/Fcb/Fcb.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class FcbTests : TestBase +public class Fcb : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetFcb(2) - .ToList(); + IReadOnlyList results = Quotes + .ToFcb(); // proper quantities Assert.AreEqual(502, results.Count); @@ -42,27 +41,24 @@ public void Standard() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetFcb() - .ToList(); + IReadOnlyList r = BadQuotes + .ToFcb(); Assert.AreEqual(502, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetFcb() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToFcb(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetFcb() - .ToList(); + IReadOnlyList r1 = Onequote + .ToFcb(); Assert.AreEqual(1, r1.Count); } @@ -70,15 +66,14 @@ public void NoQuotes() [TestMethod] public void Condense() { - List results = quotes - .GetFcb(2) - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToFcb() + .Condense(); // assertions Assert.AreEqual(502 - 5, results.Count); - FcbResult last = results.LastOrDefault(); + FcbResult last = results[^1]; Assert.AreEqual(262.47m, last.UpperBand); Assert.AreEqual(229.42m, last.LowerBand); } @@ -86,15 +81,14 @@ public void Condense() [TestMethod] public void Removed() { - List results = quotes - .GetFcb(2) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToFcb() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 5, results.Count); - FcbResult last = results.LastOrDefault(); + FcbResult last = results[^1]; Assert.AreEqual(262.47m, last.UpperBand); Assert.AreEqual(229.42m, last.LowerBand); } @@ -103,5 +97,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetFcb(1)); + => Quotes.ToFcb(1)); } diff --git a/tests/indicators/e-k/FisherTransform/FisherTransform.Tests.cs b/tests/indicators/e-k/FisherTransform/FisherTransform.StaticSeries.Tests.cs similarity index 63% rename from tests/indicators/e-k/FisherTransform/FisherTransform.Tests.cs rename to tests/indicators/e-k/FisherTransform/FisherTransform.StaticSeries.Tests.cs index 5ea407a37..e826b2b5d 100644 --- a/tests/indicators/e-k/FisherTransform/FisherTransform.Tests.cs +++ b/tests/indicators/e-k/FisherTransform/FisherTransform.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class FisherTransformTests : TestBase +public class FisherTransform : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetFisherTransform(10) - .ToList(); + IReadOnlyList results = Quotes + .ToFisherTransform(); // proper quantities Assert.AreEqual(502, results.Count); @@ -47,35 +46,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetFisherTransform(10) - .ToList(); + .ToFisherTransform(); Assert.AreEqual(502, results.Count); Assert.AreEqual(501, results.Count(x => x.Fisher != 0)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetFisherTransform(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Fisher is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetFisherTransform(10) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToFisherTransform(); Assert.AreEqual(502, results.Count); Assert.AreEqual(501, results.Count(x => x.Fisher != 0)); @@ -84,38 +70,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetFisherTransform(10) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToFisherTransform() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(493, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetFisherTransform(9) - .ToList(); + IReadOnlyList r = BadQuotes + .ToFisherTransform(9); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Fisher is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Fisher is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetFisherTransform() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToFisherTransform(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetFisherTransform() - .ToList(); + IReadOnlyList r1 = Onequote + .ToFisherTransform(); Assert.AreEqual(1, r1.Count); } @@ -124,5 +106,5 @@ public void NoQuotes() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetFisherTransform(0)); + => Quotes.ToFisherTransform(0)); } diff --git a/tests/indicators/e-k/ForceIndex/ForceIndex.Tests.cs b/tests/indicators/e-k/ForceIndex/ForceIndex.StaticSeries.Tests.cs similarity index 58% rename from tests/indicators/e-k/ForceIndex/ForceIndex.Tests.cs rename to tests/indicators/e-k/ForceIndex/ForceIndex.StaticSeries.Tests.cs index 0c6c37ea1..0e9d54eed 100644 --- a/tests/indicators/e-k/ForceIndex/ForceIndex.Tests.cs +++ b/tests/indicators/e-k/ForceIndex/ForceIndex.StaticSeries.Tests.cs @@ -1,12 +1,12 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class ForceIndexTests : TestBase +public class ForceIndex : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List r = quotes.GetForceIndex(13).ToList(); + IReadOnlyList r = Quotes.ToForceIndex(13).ToList(); // proper quantities Assert.AreEqual(502, r.Count); @@ -25,38 +25,34 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetForceIndex(13) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToForceIndex(13) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(480, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetForceIndex(2) - .ToList(); + IReadOnlyList r = BadQuotes + .ToForceIndex(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.ForceIndex is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.ForceIndex is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetForceIndex(5) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToForceIndex(5); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetForceIndex(5) - .ToList(); + IReadOnlyList r1 = Onequote + .ToForceIndex(5); Assert.AreEqual(1, r1.Count); } @@ -64,15 +60,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetForceIndex(13) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToForceIndex(13) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (13 + 100), results.Count); - ForceIndexResult last = results.LastOrDefault(); + ForceIndexResult last = results[^1]; Assert.AreEqual(-16824018.428, Math.Round(last.ForceIndex.Value, 3)); } @@ -80,5 +75,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetForceIndex(0)); + => Quotes.ToForceIndex(0)); } diff --git a/tests/indicators/e-k/Fractal/Fractal.Tests.cs b/tests/indicators/e-k/Fractal/Fractal.StaticSeries.Tests.cs similarity index 75% rename from tests/indicators/e-k/Fractal/Fractal.Tests.cs rename to tests/indicators/e-k/Fractal/Fractal.StaticSeries.Tests.cs index fcc7685f9..6085d67b7 100644 --- a/tests/indicators/e-k/Fractal/Fractal.Tests.cs +++ b/tests/indicators/e-k/Fractal/Fractal.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class FractalTests : TestBase +public class Fractal : StaticSeriesTestBase { [TestMethod] - public void StandardSpan2() + public override void Standard() // Span 2 { - List results = quotes - .GetFractal(2, EndType.HighLow) - .ToList(); + IReadOnlyList results = Quotes + .ToFractal(); // proper quantities Assert.AreEqual(502, results.Count); @@ -44,9 +43,8 @@ public void StandardSpan2() [TestMethod] public void StandardSpan4() { - List results = quotes - .GetFractal(4, 4, EndType.HighLow) - .ToList(); + IReadOnlyList results = Quotes + .ToFractal(4, 4); // proper quantities Assert.AreEqual(502, results.Count); @@ -80,27 +78,24 @@ public void StandardSpan4() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetFractal() - .ToList(); + IReadOnlyList r = BadQuotes + .ToFractal(); Assert.AreEqual(502, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetFractal() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToFractal(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetFractal() - .ToList(); + IReadOnlyList r1 = Onequote + .ToFractal(); Assert.AreEqual(1, r1.Count); } @@ -108,17 +103,16 @@ public void NoQuotes() [TestMethod] public void Condense() { - List r = quotes - .GetFractal() - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToFractal() + .Condense(); - Assert.AreEqual(129, r.Count); + Assert.AreEqual(129, results.Count); } // bad window span [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetFractal(1)); + => Quotes.ToFractal(1)); } diff --git a/tests/indicators/e-k/Gator/Gator.Tests.cs b/tests/indicators/e-k/Gator/Gator.StaticSeries.Tests.cs similarity index 81% rename from tests/indicators/e-k/Gator/Gator.Tests.cs rename to tests/indicators/e-k/Gator/Gator.StaticSeries.Tests.cs index bb7f44816..3c448b971 100644 --- a/tests/indicators/e-k/Gator/Gator.Tests.cs +++ b/tests/indicators/e-k/Gator/Gator.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class GatorTests : TestBase +public class Gator : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetGator() - .ToList(); + IReadOnlyList results = Quotes + .ToGator(); // proper quantities Assert.AreEqual(502, results.Count); @@ -76,10 +75,9 @@ public void Standard() [TestMethod] public void FromAlligator() { - List results = quotes - .GetAlligator() - .GetGator() - .ToList(); + IReadOnlyList results = Quotes + .ToAlligator() + .ToGator(); // proper quantities Assert.AreEqual(502, results.Count); @@ -145,63 +143,47 @@ public void FromAlligator() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetGator() - .ToList(); + .ToGator(); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Upper != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetGator() - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Upper is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetGator() - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToGator(); Assert.AreEqual(502, results.Count); Assert.AreEqual(481, results.Count(x => x.Upper != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetGator() - .ToList(); + IReadOnlyList r = BadQuotes + .ToGator(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Upper is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Upper is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetGator() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToGator(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetGator() - .ToList(); + IReadOnlyList r1 = Onequote + .ToGator(); Assert.AreEqual(1, r1.Count); } @@ -209,15 +191,14 @@ public void NoQuotes() [TestMethod] public void Condense() { - List results = quotes - .GetGator() - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToGator() + .Condense(); // assertions Assert.AreEqual(490, results.Count); - GatorResult last = results.LastOrDefault(); + GatorResult last = results[^1]; Assert.AreEqual(7.4538, Math.Round(last.Upper.Value, 4)); Assert.AreEqual(-9.2399, Math.Round(last.Lower.Value, 4)); Assert.IsTrue(last.UpperIsExpanding); @@ -227,15 +208,14 @@ public void Condense() [TestMethod] public void Removed() { - List results = quotes - .GetGator() - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToGator() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 150, results.Count); - GatorResult last = results.LastOrDefault(); + GatorResult last = results[^1]; Assert.AreEqual(7.4538, Math.Round(last.Upper.Value, 4)); Assert.AreEqual(-9.2399, Math.Round(last.Lower.Value, 4)); Assert.IsTrue(last.UpperIsExpanding); diff --git a/tests/indicators/e-k/HeikinAshi/HeikinAshi.StaticSeries.Tests.cs b/tests/indicators/e-k/HeikinAshi/HeikinAshi.StaticSeries.Tests.cs new file mode 100644 index 000000000..6d452026e --- /dev/null +++ b/tests/indicators/e-k/HeikinAshi/HeikinAshi.StaticSeries.Tests.cs @@ -0,0 +1,54 @@ +namespace StaticSeries; + +[TestClass] +public class HeikinAshi : StaticSeriesTestBase +{ + [TestMethod] + public override void Standard() + { + IReadOnlyList results = Quotes + .ToHeikinAshi(); + + // proper quantities + Assert.AreEqual(502, results.Count); + + // sample value + HeikinAshiResult r = results[501]; + Assert.AreEqual(241.3018m, r.Open.Round(4)); + Assert.AreEqual(245.54m, r.High.Round(4)); + Assert.AreEqual(241.3018m, r.Low.Round(4)); + Assert.AreEqual(244.6525m, r.Close.Round(4)); + Assert.AreEqual(147031456m, r.Volume); + } + + [TestMethod] + public void UseAsQuotes() + { + IReadOnlyList haQuotes = Quotes.ToHeikinAshi(); + IReadOnlyList haSma = haQuotes.ToSma(5); + Assert.AreEqual(498, haSma.Count(x => x.Sma != null)); + } + + [TestMethod] + public override void BadData() + { + IReadOnlyList r = BadQuotes + .ToHeikinAshi(); + + Assert.AreEqual(502, r.Count); + } + + [TestMethod] + public override void NoQuotes() + { + IReadOnlyList r0 = Noquotes + .ToHeikinAshi(); + + Assert.AreEqual(0, r0.Count); + + IReadOnlyList r1 = Onequote + .ToHeikinAshi(); + + Assert.AreEqual(1, r1.Count); + } +} diff --git a/tests/indicators/e-k/HeikinAshi/HeikinAshi.Tests.cs b/tests/indicators/e-k/HeikinAshi/HeikinAshi.Tests.cs deleted file mode 100644 index cfff18c7a..000000000 --- a/tests/indicators/e-k/HeikinAshi/HeikinAshi.Tests.cs +++ /dev/null @@ -1,83 +0,0 @@ -namespace Tests.Indicators; - -[TestClass] -public class HeikinAshiTests : TestBase -{ - [TestMethod] - public void Standard() - { - List results = quotes - .GetHeikinAshi() - .ToList(); - - // proper quantities - Assert.AreEqual(502, results.Count); - - // sample value - HeikinAshiResult r = results[501]; - Assert.AreEqual(241.3018m, r.Open.Round(4)); - Assert.AreEqual(245.54m, r.High.Round(4)); - Assert.AreEqual(241.3018m, r.Low.Round(4)); - Assert.AreEqual(244.6525m, r.Close.Round(4)); - Assert.AreEqual(147031456m, r.Volume); - } - - [TestMethod] - public void UseAsQuotes() - { - IEnumerable haQuotes = quotes.GetHeikinAshi(); - IEnumerable haSma = haQuotes.GetSma(5); - Assert.AreEqual(498, haSma.Count(x => x.Sma != null)); - } - - [TestMethod] - public void ToQuotes() - { - List results = quotes - .GetHeikinAshi() - .ToList(); - - List haQuotes = results - .ToQuotes() - .ToList(); - - for (int i = 0; i < results.Count; i++) - { - HeikinAshiResult r = results[i]; - Quote q = haQuotes[i]; - - Assert.AreEqual(r.Date, q.Date); - Assert.AreEqual(r.Open, q.Open); - Assert.AreEqual(r.High, q.High); - Assert.AreEqual(r.Low, q.Low); - Assert.AreEqual(r.Close, q.Close); - Assert.AreEqual(r.Volume, q.Volume); - } - } - - [TestMethod] - public void BadData() - { - List r = badQuotes - .GetHeikinAshi() - .ToList(); - - Assert.AreEqual(502, r.Count); - } - - [TestMethod] - public void NoQuotes() - { - List r0 = noquotes - .GetHeikinAshi() - .ToList(); - - Assert.AreEqual(0, r0.Count); - - List r1 = onequote - .GetHeikinAshi() - .ToList(); - - Assert.AreEqual(1, r1.Count); - } -} diff --git a/tests/indicators/e-k/Hma/Hma.Tests.cs b/tests/indicators/e-k/Hma/Hma.StaticSeries.Tests.cs similarity index 50% rename from tests/indicators/e-k/Hma/Hma.Tests.cs rename to tests/indicators/e-k/Hma/Hma.StaticSeries.Tests.cs index d773a8e69..a0bfafe45 100644 --- a/tests/indicators/e-k/Hma/Hma.Tests.cs +++ b/tests/indicators/e-k/Hma/Hma.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class HmaTests : TestBase +public class Hma : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetHma(20) - .ToList(); + IReadOnlyList results = Quotes + .ToHma(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -23,35 +22,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetHma(20) - .ToList(); + .ToHma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(480, results.Count(x => x.Hma != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetHma(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Hma is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetHma(19) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToHma(19); Assert.AreEqual(502, results.Count); Assert.AreEqual(480, results.Count(x => x.Hma != null)); @@ -60,38 +46,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetHma(20) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToHma(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(471, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetHma(15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToHma(15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Hma is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Hma is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetHma(5) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToHma(5); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetHma(5) - .ToList(); + IReadOnlyList r1 = Onequote + .ToHma(5); Assert.AreEqual(1, r1.Count); } @@ -99,15 +81,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetHma(20) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToHma(20) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(480, results.Count); - HmaResult last = results.LastOrDefault(); + HmaResult last = results[^1]; Assert.AreEqual(235.6972, last.Hma.Round(4)); } @@ -115,5 +96,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetHma(1)); + => Quotes.ToHma(1)); } diff --git a/tests/indicators/e-k/HtTrendline/HtTrendline.Tests.cs b/tests/indicators/e-k/HtTrendline/HtTrendline.StaticSeries.Tests.cs similarity index 65% rename from tests/indicators/e-k/HtTrendline/HtTrendline.Tests.cs rename to tests/indicators/e-k/HtTrendline/HtTrendline.StaticSeries.Tests.cs index c20807ea3..e15b2268c 100644 --- a/tests/indicators/e-k/HtTrendline/HtTrendline.Tests.cs +++ b/tests/indicators/e-k/HtTrendline/HtTrendline.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class HtTrendlineTests : TestBase +public class HtTrendline : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetHtTrendline() - .ToList(); + IReadOnlyList results = Quotes + .ToHtTrendline(); // proper quantities // should always be the same number of results as there is quotes @@ -58,35 +57,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetHtTrendline() - .ToList(); + .ToHtTrendline(); Assert.AreEqual(502, results.Count); Assert.AreEqual(502, results.Count(x => x.Trendline != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetHtTrendline() - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Trendline is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetHtTrendline() - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToHtTrendline(); Assert.AreEqual(502, results.Count); Assert.AreEqual(501, results.Count(x => x.Trendline != null)); @@ -95,38 +81,35 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetHtTrendline() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToHtTrendline() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(493, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetHtTrendline() - .ToList(); + IReadOnlyList r = BadQuotes + .ToHtTrendline(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Trendline is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Trendline is double.NaN)); } [TestMethod] public void Removed() { - List results = quotes - .GetHtTrendline() - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToHtTrendline() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 100, results.Count); - HtlResult last = results.LastOrDefault(); + HtlResult last = results[^1]; Assert.AreEqual(252.2172, last.Trendline.Round(4)); Assert.AreEqual(242.3435, last.SmoothPrice.Round(4)); } @@ -134,27 +117,24 @@ public void Removed() [TestMethod] public void PennyData() { - IEnumerable penny = TestData.GetPenny(); + IReadOnlyList penny = Data.GetPenny(); - List r = penny - .GetHtTrendline() - .ToList(); + IReadOnlyList r = penny + .ToHtTrendline(); Assert.AreEqual(533, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetHtTrendline() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToHtTrendline(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetHtTrendline() - .ToList(); + IReadOnlyList r1 = Onequote + .ToHtTrendline(); Assert.AreEqual(1, r1.Count); } diff --git a/tests/indicators/e-k/Hurst/Hurst.Tests.cs b/tests/indicators/e-k/Hurst/Hurst.StaticSeries.Tests.cs similarity index 50% rename from tests/indicators/e-k/Hurst/Hurst.Tests.cs rename to tests/indicators/e-k/Hurst/Hurst.StaticSeries.Tests.cs index 6cb517461..f596df5c5 100644 --- a/tests/indicators/e-k/Hurst/Hurst.Tests.cs +++ b/tests/indicators/e-k/Hurst/Hurst.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class HurstTests : TestBase +public class Hurst : StaticSeriesTestBase { [TestMethod] - public void StandardLong() + public override void Standard() { - List results = longestQuotes - .GetHurst(longestQuotes.Count() - 1) - .ToList(); + IReadOnlyList results = LongestQuotes + .ToHurst(LongestQuotes.Count - 1); // assertions @@ -22,35 +21,22 @@ public void StandardLong() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetHurst(100) - .ToList(); + .ToHurst(); Assert.AreEqual(502, results.Count); Assert.AreEqual(402, results.Count(x => x.HurstExponent != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetHurst(100) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.HurstExponent is double and double.NaN)); - } - [TestMethod] public void Chainor() { - List results = quotes - .GetHurst(100) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToHurst() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(393, results.Count(x => x.Sma != null)); @@ -59,38 +45,34 @@ public void Chainor() [TestMethod] public void Chainee() { - List results = quotes - .GetSma(10) - .GetHurst(100) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(10) + .ToHurst(); Assert.AreEqual(502, results.Count); Assert.AreEqual(393, results.Count(x => x.HurstExponent != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetHurst(150) - .ToList(); + IReadOnlyList r = BadQuotes + .ToHurst(150); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.HurstExponent is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.HurstExponent is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetHurst() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToHurst(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetHurst() - .ToList(); + IReadOnlyList r1 = Onequote + .ToHurst(); Assert.AreEqual(1, r1.Count); } @@ -98,14 +80,13 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = longestQuotes.GetHurst(longestQuotes.Count() - 1) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = LongestQuotes.ToHurst(LongestQuotes.Count - 1) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(1, results.Count); - HurstResult last = results.LastOrDefault(); + HurstResult last = results[^1]; Assert.AreEqual(0.483563, last.HurstExponent.Round(6)); } @@ -113,5 +94,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetHurst(19)); + => Quotes.ToHurst(19)); } diff --git a/tests/indicators/e-k/Ichimoku/Ichimoku.Tests.cs b/tests/indicators/e-k/Ichimoku/Ichimoku.StaticSeries.Tests.cs similarity index 69% rename from tests/indicators/e-k/Ichimoku/Ichimoku.Tests.cs rename to tests/indicators/e-k/Ichimoku/Ichimoku.StaticSeries.Tests.cs index 6974c481e..fd077faa0 100644 --- a/tests/indicators/e-k/Ichimoku/Ichimoku.Tests.cs +++ b/tests/indicators/e-k/Ichimoku/Ichimoku.StaticSeries.Tests.cs @@ -1,18 +1,17 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class IchimokuTests : TestBase +public class Ichimoku : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { int tenkanPeriods = 9; int kijunPeriods = 26; int senkouBPeriods = 52; - List results = quotes - .GetIchimoku(tenkanPeriods, kijunPeriods, senkouBPeriods) - .ToList(); + IReadOnlyList results = Quotes + .ToIchimoku(tenkanPeriods, kijunPeriods, senkouBPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -55,35 +54,31 @@ public void Standard() [TestMethod] public void Extended() { - List r = quotes - .GetIchimoku(3, 13, 40, 0, 0) - .ToList(); + IReadOnlyList results = Quotes + .GetIchimoku(3, 13, 40, 0, 0); - Assert.AreEqual(502, r.Count); + Assert.AreEqual(502, results.Count); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetIchimoku() - .ToList(); + IReadOnlyList r = BadQuotes + .ToIchimoku(); Assert.AreEqual(502, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetIchimoku() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToIchimoku(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetIchimoku() - .ToList(); + IReadOnlyList r1 = Onequote + .ToIchimoku(); Assert.AreEqual(1, r1.Count); } @@ -91,12 +86,11 @@ public void NoQuotes() [TestMethod] public void Condense() { - List r = quotes - .GetIchimoku() - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToIchimoku() + .Condense(); - Assert.AreEqual(502, r.Count); + Assert.AreEqual(502, results.Count); } [TestMethod] @@ -104,24 +98,24 @@ public void Exceptions() { // bad signal period Assert.ThrowsException(() => - quotes.GetIchimoku(0)); + Quotes.ToIchimoku(0)); // bad short span period Assert.ThrowsException(() => - quotes.GetIchimoku(9, 0, 52)); + Quotes.ToIchimoku(9, 0)); // bad long span period Assert.ThrowsException(() => - quotes.GetIchimoku(9, 26, 26)); + Quotes.ToIchimoku(9, 26, 26)); // invalid offsets Assert.ThrowsException(() => - quotes.GetIchimoku(9, 26, 52, -1)); + Quotes.GetIchimoku(9, 26, 52, -1)); Assert.ThrowsException(() => - quotes.GetIchimoku(9, 26, 52, -1, 12)); + Quotes.GetIchimoku(9, 26, 52, -1, 12)); Assert.ThrowsException(() => - quotes.GetIchimoku(9, 26, 52, 12, -1)); + Quotes.GetIchimoku(9, 26, 52, 12, -1)); } } diff --git a/tests/indicators/e-k/Kama/Kama.Tests.cs b/tests/indicators/e-k/Kama/Kama.StaticSeries.Tests.cs similarity index 53% rename from tests/indicators/e-k/Kama/Kama.Tests.cs rename to tests/indicators/e-k/Kama/Kama.StaticSeries.Tests.cs index 3375f0fb4..bbd19115c 100644 --- a/tests/indicators/e-k/Kama/Kama.Tests.cs +++ b/tests/indicators/e-k/Kama/Kama.StaticSeries.Tests.cs @@ -1,84 +1,70 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class KamaTests : TestBase +public class Kama : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { int erPeriods = 10; int fastPeriods = 2; int slowPeriods = 30; - List results = quotes - .GetKama(erPeriods, fastPeriods, slowPeriods) - .ToList(); + IReadOnlyList results = Quotes + .ToKama(erPeriods, fastPeriods, slowPeriods); // proper quantities Assert.AreEqual(502, results.Count); - Assert.AreEqual(492, results.Count(x => x.ER != null)); + Assert.AreEqual(492, results.Count(x => x.Er != null)); Assert.AreEqual(493, results.Count(x => x.Kama != null)); // sample values KamaResult r1 = results[8]; - Assert.AreEqual(null, r1.ER); + Assert.AreEqual(null, r1.Er); Assert.AreEqual(null, r1.Kama); KamaResult r2 = results[9]; - Assert.AreEqual(null, r2.ER); + Assert.AreEqual(null, r2.Er); Assert.AreEqual(213.7500, r2.Kama.Round(4)); KamaResult r3 = results[10]; - Assert.AreEqual(0.2465, r3.ER.Round(4)); + Assert.AreEqual(0.2465, r3.Er.Round(4)); Assert.AreEqual(213.7713, r3.Kama.Round(4)); KamaResult r4 = results[24]; - Assert.AreEqual(0.2136, r4.ER.Round(4)); + Assert.AreEqual(0.2136, r4.Er.Round(4)); Assert.AreEqual(214.7423, r4.Kama.Round(4)); KamaResult r5 = results[149]; - Assert.AreEqual(0.3165, r5.ER.Round(4)); + Assert.AreEqual(0.3165, r5.Er.Round(4)); Assert.AreEqual(235.5510, r5.Kama.Round(4)); KamaResult r6 = results[249]; - Assert.AreEqual(0.3182, r6.ER.Round(4)); + Assert.AreEqual(0.3182, r6.Er.Round(4)); Assert.AreEqual(256.0898, r6.Kama.Round(4)); KamaResult r7 = results[501]; - Assert.AreEqual(0.2214, r7.ER.Round(4)); + Assert.AreEqual(0.2214, r7.Er.Round(4)); Assert.AreEqual(240.1138, r7.Kama.Round(4)); } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetKama() - .ToList(); + .ToKama(); Assert.AreEqual(502, results.Count); Assert.AreEqual(493, results.Count(x => x.Kama != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetKama() - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Kama is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetKama() - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToKama(); Assert.AreEqual(502, results.Count); Assert.AreEqual(492, results.Count(x => x.Kama != null)); @@ -87,38 +73,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetKama() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToKama() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(484, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetKama() - .ToList(); + IReadOnlyList r = BadQuotes + .ToKama(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Kama is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Kama is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetKama() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToKama(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetKama() - .ToList(); + IReadOnlyList r1 = Onequote + .ToKama(); Assert.AreEqual(1, r1.Count); } @@ -130,16 +112,15 @@ public void Removed() int fastPeriods = 2; int slowPeriods = 30; - List results = quotes - .GetKama(erPeriods, fastPeriods, slowPeriods) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToKama(erPeriods, fastPeriods, slowPeriods) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - Math.Max(erPeriods + 100, erPeriods * 10), results.Count); - KamaResult last = results.LastOrDefault(); - Assert.AreEqual(0.2214, last.ER.Round(4)); + KamaResult last = results[^1]; + Assert.AreEqual(0.2214, last.Er.Round(4)); Assert.AreEqual(240.1138, last.Kama.Round(4)); } @@ -148,14 +129,14 @@ public void Exceptions() { // bad ER period Assert.ThrowsException(() => - quotes.GetKama(0, 2, 30)); + Quotes.ToKama(0)); // bad fast period Assert.ThrowsException(() => - quotes.GetKama(10, 0, 30)); + Quotes.ToKama(10, 0)); // bad slow period Assert.ThrowsException(() => - quotes.GetKama(10, 5, 5)); + Quotes.ToKama(10, 5, 5)); } } diff --git a/tests/indicators/e-k/Keltner/Keltner.Tests.cs b/tests/indicators/e-k/Keltner/Keltner.StaticSeries.Tests.cs similarity index 69% rename from tests/indicators/e-k/Keltner/Keltner.Tests.cs rename to tests/indicators/e-k/Keltner/Keltner.StaticSeries.Tests.cs index 05c6e88e4..ce3dc3c2f 100644 --- a/tests/indicators/e-k/Keltner/Keltner.Tests.cs +++ b/tests/indicators/e-k/Keltner/Keltner.StaticSeries.Tests.cs @@ -1,18 +1,17 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class KeltnerTests : TestBase +public class Keltner : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { int emaPeriods = 20; int multiplier = 2; int atrPeriods = 10; - List results = quotes - .GetKeltner(emaPeriods, multiplier, atrPeriods) - .ToList(); + IReadOnlyList results = Quotes + .ToKeltner(emaPeriods, multiplier, atrPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -38,28 +37,25 @@ public void Standard() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetKeltner(10, 3, 15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToKeltner(10, 3, 15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.UpperBand is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.UpperBand is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetKeltner() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToKeltner(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetKeltner() - .ToList(); + IReadOnlyList r1 = Onequote + .ToKeltner(); Assert.AreEqual(1, r1.Count); } @@ -71,15 +67,14 @@ public void Condense() int multiplier = 2; int atrPeriods = 10; - List results = quotes - .GetKeltner(emaPeriods, multiplier, atrPeriods) - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToKeltner(emaPeriods, multiplier, atrPeriods) + .Condense(); // assertions Assert.AreEqual(483, results.Count); - KeltnerResult last = results.LastOrDefault(); + KeltnerResult last = results[^1]; Assert.AreEqual(262.1873, last.UpperBand.Round(4)); Assert.AreEqual(249.3519, last.Centerline.Round(4)); Assert.AreEqual(236.5165, last.LowerBand.Round(4)); @@ -94,15 +89,14 @@ public void Removed() int atrPeriods = 10; int n = Math.Max(emaPeriods, atrPeriods); - List results = quotes - .GetKeltner(emaPeriods, multiplier, atrPeriods) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToKeltner(emaPeriods, multiplier, atrPeriods) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - Math.Max(2 * n, n + 100), results.Count); - KeltnerResult last = results.LastOrDefault(); + KeltnerResult last = results[^1]; Assert.AreEqual(262.1873, last.UpperBand.Round(4)); Assert.AreEqual(249.3519, last.Centerline.Round(4)); Assert.AreEqual(236.5165, last.LowerBand.Round(4)); @@ -114,14 +108,14 @@ public void Exceptions() { // bad EMA period Assert.ThrowsException(() => - quotes.GetKeltner(1, 2, 10)); + Quotes.ToKeltner(1)); // bad ATR period Assert.ThrowsException(() => - quotes.GetKeltner(20, 2, 1)); + Quotes.ToKeltner(20, 2, 1)); // bad multiplier Assert.ThrowsException(() => - quotes.GetKeltner(20, 0, 10)); + Quotes.ToKeltner(20, 0)); } } diff --git a/tests/indicators/e-k/Kvo/Kvo.Tests.cs b/tests/indicators/e-k/Kvo/Kvo.StaticSeries.Tests.cs similarity index 71% rename from tests/indicators/e-k/Kvo/Kvo.Tests.cs rename to tests/indicators/e-k/Kvo/Kvo.StaticSeries.Tests.cs index 80e1c7869..34dccb79f 100644 --- a/tests/indicators/e-k/Kvo/Kvo.Tests.cs +++ b/tests/indicators/e-k/Kvo/Kvo.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class KlingerTests : TestBase +public class Klinger : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = - quotes.GetKvo(34, 55, 13) - .ToList(); + IReadOnlyList results = + Quotes.ToKvo(); // proper quantities Assert.AreEqual(502, results.Count); @@ -48,38 +47,34 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetKvo() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToKvo() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(437, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetKvo() - .ToList(); + IReadOnlyList r = BadQuotes + .ToKvo(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Oscillator is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Oscillator is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetKvo() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToKvo(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetKvo() - .ToList(); + IReadOnlyList r1 = Onequote + .ToKvo(); Assert.AreEqual(1, r1.Count); } @@ -87,15 +82,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetKvo(34, 55, 13) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToKvo() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (55 + 150), results.Count); - KvoResult last = results.LastOrDefault(); + KvoResult last = results[^1]; Assert.AreEqual(-539224047, Math.Round(last.Oscillator.Value, 0)); Assert.AreEqual(-1548306127, Math.Round(last.Signal.Value, 0)); } @@ -105,14 +99,14 @@ public void Exceptions() { // bad fast period Assert.ThrowsException(() => - quotes.GetKvo(2)); + Quotes.ToKvo(2)); // bad slow period Assert.ThrowsException(() => - quotes.GetKvo(20, 20)); + Quotes.ToKvo(20, 20)); // bad signal period Assert.ThrowsException(() => - quotes.GetKvo(34, 55, 0)); + Quotes.ToKvo(34, 55, 0)); } } diff --git a/tests/indicators/m-r/MaEnvelopes/MaEnvelopes.Tests.cs b/tests/indicators/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.Tests.cs similarity index 74% rename from tests/indicators/m-r/MaEnvelopes/MaEnvelopes.Tests.cs rename to tests/indicators/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.Tests.cs index d1da84130..13aa3ddd7 100644 --- a/tests/indicators/m-r/MaEnvelopes/MaEnvelopes.Tests.cs +++ b/tests/indicators/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.Tests.cs @@ -1,14 +1,40 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class MaEnvelopesTests : TestBase +public class MaEnvelopes : StaticSeriesTestBase { + [TestMethod] + public override void Standard() // SMA + { + IReadOnlyList results = + Quotes.ToMaEnvelopes(20); + + // proper quantities + Assert.AreEqual(502, results.Count); + Assert.AreEqual(483, results.Count(x => x.Centerline != null)); + + // sample values + MaEnvelopeResult r1 = results[24]; + Assert.AreEqual(215.0310, r1.Centerline.Round(4)); + Assert.AreEqual(220.4068, r1.UpperEnvelope.Round(4)); + Assert.AreEqual(209.6552, r1.LowerEnvelope.Round(4)); + + MaEnvelopeResult r2 = results[249]; + Assert.AreEqual(255.5500, r2.Centerline.Round(4)); + Assert.AreEqual(261.9388, r2.UpperEnvelope.Round(4)); + Assert.AreEqual(249.16125, r2.LowerEnvelope.Round(5)); + + MaEnvelopeResult r3 = results[501]; + Assert.AreEqual(251.8600, r3.Centerline.Round(4)); + Assert.AreEqual(258.1565, r3.UpperEnvelope.Round(4)); + Assert.AreEqual(245.5635, r3.LowerEnvelope.Round(4)); + } + [TestMethod] public void Alma() { - List results = - quotes.GetMaEnvelopes(10, 2.5, MaType.ALMA) - .ToList(); + IReadOnlyList results = + Quotes.ToMaEnvelopes(10, 2.5, MaType.ALMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -34,9 +60,8 @@ public void Alma() [TestMethod] public void Dema() { - List results = - quotes.GetMaEnvelopes(20, 2.5, MaType.DEMA) - .ToList(); + IReadOnlyList results = + Quotes.ToMaEnvelopes(20, 2.5, MaType.DEMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -62,9 +87,8 @@ public void Dema() [TestMethod] public void Epma() { - List results = - quotes.GetMaEnvelopes(20, 2.5, MaType.EPMA) - .ToList(); + IReadOnlyList results = + Quotes.ToMaEnvelopes(20, 2.5, MaType.EPMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -90,9 +114,8 @@ public void Epma() [TestMethod] public void Ema() { - List results = - quotes.GetMaEnvelopes(20, 2.5, MaType.EMA) - .ToList(); + IReadOnlyList results = + Quotes.ToMaEnvelopes(20, 2.5, MaType.EMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -118,9 +141,8 @@ public void Ema() [TestMethod] public void Hma() { - List results = - quotes.GetMaEnvelopes(20, 2.5, MaType.HMA) - .ToList(); + IReadOnlyList results = + Quotes.ToMaEnvelopes(20, 2.5, MaType.HMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -138,40 +160,11 @@ public void Hma() Assert.AreEqual(229.8048, r3.LowerEnvelope.Round(4)); } - [TestMethod] - public void Sma() - { - List results = - quotes.GetMaEnvelopes(20, 2.5, MaType.SMA) - .ToList(); - - // proper quantities - Assert.AreEqual(502, results.Count); - Assert.AreEqual(483, results.Count(x => x.Centerline != null)); - - // sample values - MaEnvelopeResult r1 = results[24]; - Assert.AreEqual(215.0310, r1.Centerline.Round(4)); - Assert.AreEqual(220.4068, r1.UpperEnvelope.Round(4)); - Assert.AreEqual(209.6552, r1.LowerEnvelope.Round(4)); - - MaEnvelopeResult r2 = results[249]; - Assert.AreEqual(255.5500, r2.Centerline.Round(4)); - Assert.AreEqual(261.9388, r2.UpperEnvelope.Round(4)); - Assert.AreEqual(249.16125, r2.LowerEnvelope.Round(5)); - - MaEnvelopeResult r3 = results[501]; - Assert.AreEqual(251.8600, r3.Centerline.Round(4)); - Assert.AreEqual(258.1565, r3.UpperEnvelope.Round(4)); - Assert.AreEqual(245.5635, r3.LowerEnvelope.Round(4)); - } - [TestMethod] public void Smma() { - List results = - quotes.GetMaEnvelopes(20, 2.5, MaType.SMMA) - .ToList(); + IReadOnlyList results = + Quotes.ToMaEnvelopes(20, 2.5, MaType.SMMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -197,9 +190,8 @@ public void Smma() [TestMethod] public void Tema() { - List results = - quotes.GetMaEnvelopes(20, 2.5, MaType.TEMA) - .ToList(); + IReadOnlyList results = + Quotes.ToMaEnvelopes(20, 2.5, MaType.TEMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -225,9 +217,8 @@ public void Tema() [TestMethod] public void Wma() { - List results = - quotes.GetMaEnvelopes(20, 2.5, MaType.WMA) - .ToList(); + IReadOnlyList results = + Quotes.ToMaEnvelopes(20, 2.5, MaType.WMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -246,101 +237,93 @@ public void Wma() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetMaEnvelopes(10, 2.5, MaType.SMA) - .ToList(); + .ToMaEnvelopes(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(493, results.Count(x => x.Centerline != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetMaEnvelopes(8, 2.5, MaType.ALMA) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.UpperEnvelope is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetMaEnvelopes(10, 2.5, MaType.SMA) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToMaEnvelopes(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(492, results.Count(x => x.Centerline != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List a = badQuotes - .GetMaEnvelopes(5, 2.5, MaType.ALMA) - .ToList(); + IReadOnlyList a = BadQuotes + .ToMaEnvelopes(5, 2.5, MaType.ALMA); Assert.AreEqual(502, a.Count); - List d = badQuotes - .GetMaEnvelopes(5, 2.5, MaType.DEMA) - .ToList(); + IReadOnlyList d = BadQuotes + .ToMaEnvelopes(5, 2.5, MaType.DEMA); Assert.AreEqual(502, d.Count); - List p = badQuotes - .GetMaEnvelopes(5, 2.5, MaType.EPMA) - .ToList(); + IReadOnlyList p = BadQuotes + .ToMaEnvelopes(5, 2.5, MaType.EPMA); Assert.AreEqual(502, p.Count); - List e = badQuotes - .GetMaEnvelopes(5, 2.5, MaType.EMA) - .ToList(); + IReadOnlyList e = BadQuotes + .ToMaEnvelopes(5, 2.5, MaType.EMA); Assert.AreEqual(502, e.Count); - List h = badQuotes - .GetMaEnvelopes(5, 2.5, MaType.HMA) - .ToList(); + IReadOnlyList h = BadQuotes + .ToMaEnvelopes(5, 2.5, MaType.HMA); Assert.AreEqual(502, h.Count); - List s = badQuotes - .GetMaEnvelopes(5, 2.5, MaType.SMA) - .ToList(); + IReadOnlyList s = BadQuotes + .ToMaEnvelopes(5); Assert.AreEqual(502, s.Count); - List t = badQuotes - .GetMaEnvelopes(5, 2.5, MaType.TEMA) - .ToList(); + IReadOnlyList t = BadQuotes + .ToMaEnvelopes(5, 2.5, MaType.TEMA); Assert.AreEqual(502, t.Count); - List w = badQuotes - .GetMaEnvelopes(5, 2.5, MaType.WMA) - .ToList(); + IReadOnlyList w = BadQuotes + .ToMaEnvelopes(5, 2.5, MaType.WMA); Assert.AreEqual(502, w.Count); } + [TestMethod] + public override void NoQuotes() + { + IReadOnlyList r0 = Noquotes + .ToMaEnvelopes(10); + + Assert.AreEqual(0, r0.Count); + + IReadOnlyList r1 = Onequote + .ToMaEnvelopes(10); + + Assert.AreEqual(1, r1.Count); + } + [TestMethod] public void Condense() { - List r = quotes - .GetMaEnvelopes(20, 2.5, MaType.SMA) - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToMaEnvelopes(20) + .Condense(); - Assert.AreEqual(483, r.Count); + Assert.AreEqual(483, results.Count); } [TestMethod] @@ -348,11 +331,11 @@ public void Exceptions() { // bad offset period Assert.ThrowsException(() => - quotes.GetMaEnvelopes(14, 0)); + Quotes.ToMaEnvelopes(14, 0)); // bad MA period Assert.ThrowsException(() => - quotes.GetMaEnvelopes(14, 5, MaType.KAMA)); + Quotes.ToMaEnvelopes(14, 5, MaType.KAMA)); // note: insufficient quotes is tested elsewhere } diff --git a/tests/indicators/m-r/Macd/Macd.Tests.cs b/tests/indicators/m-r/Macd/Macd.StaticSeries.Tests.cs similarity index 65% rename from tests/indicators/m-r/Macd/Macd.Tests.cs rename to tests/indicators/m-r/Macd/Macd.StaticSeries.Tests.cs index c4b135464..63f78539e 100644 --- a/tests/indicators/m-r/Macd/Macd.Tests.cs +++ b/tests/indicators/m-r/Macd/Macd.StaticSeries.Tests.cs @@ -1,18 +1,17 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class MacdTests : TestBase +public class Macd : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { int fastPeriods = 12; int slowPeriods = 26; int signalPeriods = 9; - List results = - quotes.GetMacd(fastPeriods, slowPeriods, signalPeriods) - .ToList(); + IReadOnlyList results = + Quotes.ToMacd(fastPeriods, slowPeriods, signalPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -44,35 +43,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetMacd() - .ToList(); + .ToMacd(); Assert.AreEqual(502, results.Count); Assert.AreEqual(477, results.Count(x => x.Macd != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetMacd() - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Macd is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetMacd() - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToMacd(); Assert.AreEqual(502, results.Count); Assert.AreEqual(476, results.Count(x => x.Macd != null)); @@ -81,38 +67,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetMacd() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToMacd() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(468, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetMacd(10, 20, 5) - .ToList(); + IReadOnlyList r = BadQuotes + .ToMacd(10, 20, 5); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Macd is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Macd is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetMacd() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToMacd(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetMacd() - .ToList(); + IReadOnlyList r1 = Onequote + .ToMacd(); Assert.AreEqual(1, r1.Count); } @@ -124,15 +106,14 @@ public void Removed() int slowPeriods = 26; int signalPeriods = 9; - List results = quotes - .GetMacd(fastPeriods, slowPeriods, signalPeriods) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToMacd(fastPeriods, slowPeriods, signalPeriods) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (slowPeriods + signalPeriods + 250), results.Count); - MacdResult last = results.LastOrDefault(); + MacdResult last = results[^1]; Assert.AreEqual(-6.2198, last.Macd.Round(4)); Assert.AreEqual(-5.8569, last.Signal.Round(4)); Assert.AreEqual(-0.3629, last.Histogram.Round(4)); @@ -143,14 +124,14 @@ public void Exceptions() { // bad fast period Assert.ThrowsException(() => - quotes.GetMacd(0, 26, 9)); + Quotes.ToMacd(0)); // bad slow periods must be larger than faster period Assert.ThrowsException(() => - quotes.GetMacd(12, 12, 9)); + Quotes.ToMacd(12, 12)); // bad signal period Assert.ThrowsException(() => - quotes.GetMacd(12, 26, -1)); + Quotes.ToMacd(12, 26, -1)); } } diff --git a/tests/indicators/m-r/Mama/Mama.Tests.cs b/tests/indicators/m-r/Mama/Mama.StaticSeries.Tests.cs similarity index 63% rename from tests/indicators/m-r/Mama/Mama.Tests.cs rename to tests/indicators/m-r/Mama/Mama.StaticSeries.Tests.cs index 114426172..bdb748645 100644 --- a/tests/indicators/m-r/Mama/Mama.Tests.cs +++ b/tests/indicators/m-r/Mama/Mama.StaticSeries.Tests.cs @@ -1,17 +1,16 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class MamaTests : TestBase +public class Mama : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { double fastLimit = 0.5; double slowLimit = 0.05; - List results = quotes - .GetMama(fastLimit, slowLimit) - .ToList(); + IReadOnlyList results = Quotes + .ToMama(fastLimit, slowLimit); // proper quantities Assert.AreEqual(502, results.Count); @@ -48,35 +47,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetMama() - .ToList(); + .ToMama(); Assert.AreEqual(502, results.Count); Assert.AreEqual(497, results.Count(x => x.Mama != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetMama() - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Mama is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetMama() - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToMama(); Assert.AreEqual(502, results.Count); Assert.AreEqual(496, results.Count(x => x.Mama != null)); @@ -85,38 +71,32 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetMama() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToMama() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(488, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetMama() - .ToList(); + IReadOnlyList r = BadQuotes + .ToMama(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Mama is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Mama is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetMama() - .ToList(); + IReadOnlyList r0 = Noquotes.ToMama(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetMama() - .ToList(); + IReadOnlyList r1 = Onequote.ToMama(); Assert.AreEqual(1, r1.Count); } @@ -127,15 +107,14 @@ public void Removed() double fastLimit = 0.5; double slowLimit = 0.05; - List results = quotes - .GetMama(fastLimit, slowLimit) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToMama(fastLimit, slowLimit) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 50, results.Count); - MamaResult last = results.LastOrDefault(); + MamaResult last = results[^1]; Assert.AreEqual(244.1092, last.Mama.Round(4)); Assert.AreEqual(252.6139, last.Fama.Round(4)); } @@ -145,14 +124,14 @@ public void Exceptions() { // bad fast period (same as slow period) Assert.ThrowsException(() => - quotes.GetMama(0.5, 0.5)); + Quotes.ToMama(0.5, 0.5)); // bad fast period (cannot be 1 or more) Assert.ThrowsException(() => - quotes.GetMama(1, 0.5)); + Quotes.ToMama(1, 0.5)); // bad slow period Assert.ThrowsException(() => - quotes.GetMama(0.5, 0)); + Quotes.ToMama(0.5, 0)); } } diff --git a/tests/indicators/m-r/Marubozu/Marubozu.Tests.cs b/tests/indicators/m-r/Marubozu/Marubozu.StaticSeries.Tests.cs similarity index 66% rename from tests/indicators/m-r/Marubozu/Marubozu.Tests.cs rename to tests/indicators/m-r/Marubozu/Marubozu.StaticSeries.Tests.cs index bd3f17a0c..66ba37050 100644 --- a/tests/indicators/m-r/Marubozu/Marubozu.Tests.cs +++ b/tests/indicators/m-r/Marubozu/Marubozu.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class MarubozuTests : TestBase +public class Marubozu : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetMarubozu(95) - .ToList(); + IReadOnlyList results = Quotes + .ToMarubozu(); // proper quantities Assert.AreEqual(502, results.Count); @@ -41,27 +40,24 @@ public void Standard() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetMarubozu() - .ToList(); + IReadOnlyList r = BadQuotes + .ToMarubozu(); Assert.AreEqual(502, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetMarubozu() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToMarubozu(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetMarubozu() - .ToList(); + IReadOnlyList r1 = Onequote + .ToMarubozu(); Assert.AreEqual(1, r1.Count); } @@ -69,12 +65,11 @@ public void NoQuotes() [TestMethod] public void Condense() { - List r = quotes - .GetMarubozu(95) - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToMarubozu() + .Condense(); - Assert.AreEqual(6, r.Count); + Assert.AreEqual(6, results.Count); } [TestMethod] @@ -82,9 +77,9 @@ public void Exceptions() { // bad minimum body percent values Assert.ThrowsException(() => - quotes.GetMarubozu(79.9)); + Quotes.ToMarubozu(79.9)); Assert.ThrowsException(() => - quotes.GetMarubozu(100.1)); + Quotes.ToMarubozu(100.1)); } } diff --git a/tests/indicators/m-r/Mfi/Mfi.Tests.cs b/tests/indicators/m-r/Mfi/Mfi.StaticSeries.Tests.cs similarity index 60% rename from tests/indicators/m-r/Mfi/Mfi.Tests.cs rename to tests/indicators/m-r/Mfi/Mfi.StaticSeries.Tests.cs index 85ecf769a..bd5be8d19 100644 --- a/tests/indicators/m-r/Mfi/Mfi.Tests.cs +++ b/tests/indicators/m-r/Mfi/Mfi.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class MfiTests : TestBase +public class Mfi : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetMfi(14) - .ToList(); + IReadOnlyList results = Quotes + .ToMfi(); // proper quantities Assert.AreEqual(502, results.Count); @@ -25,10 +24,9 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetMfi() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToMfi() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(479, results.Count(x => x.Sma != null)); @@ -39,9 +37,8 @@ public void SmallLookback() { int lookbackPeriods = 4; - List results = quotes - .GetMfi(lookbackPeriods) - .ToList(); + IReadOnlyList results = Quotes + .ToMfi(lookbackPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -56,28 +53,25 @@ public void SmallLookback() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetMfi(15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToMfi(15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Mfi is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Mfi is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetMfi() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToMfi(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetMfi() - .ToList(); + IReadOnlyList r1 = Onequote + .ToMfi(); Assert.AreEqual(1, r1.Count); } @@ -87,15 +81,14 @@ public void Removed() { int lookbackPeriods = 14; - List results = quotes - .GetMfi(lookbackPeriods) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToMfi(lookbackPeriods) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 14, results.Count); - MfiResult last = results.LastOrDefault(); + MfiResult last = results[^1]; Assert.AreEqual(39.9494, last.Mfi.Round(4)); } @@ -103,5 +96,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetMfi(1)); + => Quotes.ToMfi(1)); } diff --git a/tests/indicators/m-r/Obv/Obv.Calc.xlsx b/tests/indicators/m-r/Obv/Obv.Calc.xlsx index bae142741fe9f12be2684b84f7d2eae9220c545d..f2df72246db431ff5a31b7da4af204224455d098 100644 GIT binary patch delta 61020 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 z

    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 delta 61098 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

    6ubtwp&*c?h?|wzP09v*++k)%ks;x?bKK zyWmc^Uaq+gEtGOvhqt_@+yPln_`5$z2Ezu?By3*BiiZxhhhpK5OICIqC%MR$w@{v< z#>2+OOB7H{WJFYDKA$iJ9;vJOMyndq{59lr?VmGz|W+@ZlW zDaK=H<*WBT>yQI`aEk97xYvEYKS*F~X6gjN&4D`>_`ogiaLx9xkcuNaKSE)BeP4%V zh0Y5EO_#XM`-lzgbhjf|LI-e{9@MR5b+R+|08a|h9)SpLyzHhyWfGVFvx3X8wy-%U zH}x$12}uiueDuz}q@s^iHHXmMKr9g51bX~6B-4CTp~ohJRK}1&w>W}Z@>y*NE*&n} z0_)hn*jC{hkhes%;znH~mE%b{y#Bdnp|;S8k7e`!_Y8Kb0D$bGI~-R#cK>Be5O@3I*#o(8ipJnHS3B~>|=V3 zYFhc`L+fD0jv-kF%Qy2FCc1M$o&jHMru^vJcW5q*i!bQ?|8xkxVl|3C3S3_FUW^bP z_`w^}Mk9zbVU&_5a_!rnks|{Wl3yKp9%T142+>jp1ISUtl-R=sj=tuVp9V9KlG=gM z59Z2-3(k$d6u*692UYSgCY%FdXAc#Exw#x;&K-YF6=@RLIGa)k2NlHB7Ag+p1!Zn! z_eW$b5mp1EcVl*-g&`F|SuW!~w*w-#Umiw?X+>!EGAoU?Q9rQL1j@xYO~M`(C1U=zefXCXd)~4e;s`-$elFR% zW~v7RJCME}Y-@DZNu=xFr+Km#QruQyj7UFs`f@=jgDzfPkOT*QoPI^*o@{sxFT2OC z4|%_l9|!69ZUJ9R0lZSiu{h%G#`akBDWN!ASNxzh`#`Pv27BPOuqFq9F!;QH)3{{z zXS|wz>m2&|FBeau%&npIb^@k|K4a>+A<6?;-&&E7SKc6GuR)HfMe*lVj&&bD_jgrM z9^GqzLmCcDRop_e+au1w-`Z?z`*?oZq#3s=O_S}AaJO@*%H`8npl&u`9%ydYNPOJZ zEx9%4zWc8&OU?#Rz8s^ZA=9>uCMGnr3lHVN&NNny7ui7hC`#rOE7OzkOJS6jm&=ag zfuzFGY3Pvz={}@(>l7i=)`oF>IyXDARTuB(oNEB$>W}wTRVCnioLqwhPp_C3z=L~7 zzW^Rw#k2byl*RA#6)pN5t1x?e=sfoL18Z-_>Q|@}fwG^SY$)q8ajOcoNN&(QOCGrh z-H+gH(BW$g(2wuXelUUSZd7h$21@7HPlAc6HC)Jdmi1f^PK)I;YJFd<>e5_MQLQ4gxq_=%s%go9NtsaQC8 zS7&=2!#$|;kPi(~@4con8!1B_{Ybu?o3_H8Pybxy6HI}ZdHWmlVI?x#2^JXJ-P%UK zZ)Onc3raKIT#7azARVnHovmif?0Sp9Z(`XM*+)wSKLVd+nCWdMK0EjNEzv^VGJQu9=$QVE06k7z=Wfnom~BG*#Re(b5l$Z zD7suQGnm+e2riu>J~p>uES%ym9HCPt$@S3EMwQ- zcQwkk;Ul=8FPN3=D@fpg8Nnv^qZ%O|$`pe0rd!~nY zM4=s8WDUb*-ih2h7%G8hjnJQsa4sT2gnYgoZX(R44^^I%~pw{VlgET9!AKq76} zr6RAvDK!cOm4|dZDvNwNChMV01%G=U3`Pf8Mrte`NmAyY0wY|j+jtp&Ak$K_9IJ-T z=t*4A?7}vP#^>zTq`gGR3jx6=nen+u=0)|L!M|MWiF-8E=ABy*_>;3*U7Xk1;^0(- z#DRO-O+P5JqF#(YT_=p4_?Q0o*(ibU_ydpGqw%Kgp?yhWy=Bj>;&YGgpoz1#ES)`s zR}ypWA5B3N3`J6d|9y8NoK%H`7{T0>hcQ8jKfzmT@m$me$xw}R$t5#vS!reLOJ-+P zCywT$7U4IaUq%B1Pw6Dj=4`|*G zvOn(y*SYMb`$3g^_DZ2&OS>l_)Glp^JkZuOA^tS4YotMd(82uh-|mgVa0i~^pB6e{ zUMy13RL_nNIGs-hAHt66O>api6hgc5fQ-;*Sc~Qr8E?x zWf>Z?CJCoqz#(?lz*eFuLc(0AdM*@bUVGaV(~KUgr6UH&{nh<>r{8NmkWmN!_Z88< zSisqM1mqVR%zwKmEO98Q9K6R!)11vj+s$M~sEPm#z3a~wT=J*Ll=j6P>81$xa)~C> zi*MOAAVx~tq1YObO-eB#%PNf92xnU5MXa#U7h|>N7tG4rf7QE?Ta9v#m8N;YO5u

    qK3VF#hILr1-sR#g-~ za-W6Tba({yd5}k15)?h{no>Kqa&9>&$_uV8dDqMw{#PVy1EJI`m%?go5EZIa4y-dG zBSm+_d73zdbLfprUrCuI9F4J1S4bjLTUa_O7Q;~p#KdkaOetY zA)Jk+*2NS(0E@C zrt{|*dY(0E;5SltGA{n0gXUC_6vWpdNte{+z)|nN8lM$HLR`RU9M>r;w(+L?v#itr z0xzl+w>-yA()$4mC@Zr?GK)86*X)r!GXF&O$Rj{#esqNvGcHq2hukob{VmT z6mLWQ0vjWV0psTl7kjv_@8!oV#74VojF#9L8HZWo5ECtxA5k%psH1_o{@=j?pp3*| z3~BPN((~&>G2n9_(+HUIK6S%q^h2d5`Z9F}$G5h5OJ{Tm$zY4yk(&NaM9I}b%(?Y? zv`6hH1-UoUaL}u1gUfU@?yrb{K4yAG)IAYHN!%oCuMgV+!sXDdBx?bZvnDuaUYEBN+U@Mk>kD?1w|em_wAsbOQ0`(;&YsKhAb@lUc<^)yjU0{~g-w z-$N7Fb?q_#lnHktK4pJ4{ayvCW;Y*=raI!f)byl&d*uEARN{FREPcTF)xc9wHg=mXF_^-Y4Q)vscf4~J_J(g#S$c)}YrrkM zTqV9C9!K`dK72#5RCU-VVpa0+-Uez2b%D18P9^9kwWNyc(T7NvlGadLR^m6VdFViN z^s_($GXZYlZh=`sdkXv-Ze7mi42+%L;<$M?+F=3jR(074Uh zR{Z+)9~I;W5&G5qmUzPN+e)aUm_ZVima>S?7jf7FaQ?nq(%UKwq)C5PJhdi|ntVD5 zF}MKN>+B5-kqO9*dHT68*lw5I_0rwh9JFvMU&pYqDd4|Ng_jzJ6rWqez$1{ zpaaLs*(mAbJlHq-h?dqTBf1;oo&GFVGqsD=~H{MGeFn!5#v+3>8m2d=xN_}q?mgODXA&WSs%EQjlBX< zcEX$YCnDUiW65!AV0JBX=rWQM_v~-W7UvAS@hf*iD5Er!>gzL}61uV{>jRc=E8YXu zrVLku>4(Q?yZLnr?`p$GOC@QT=1EU=n9q@K_WHmzilE?g-+c<|km~^y z2;4d`b~Xt-%Ke;&WDa6U8FpIWG(yjYiPA<9HW!4GOX)jC{`3M!(?3tU|#@DNGx2nC<>D%elS4{p^8qZJR7HeAVNV*@lLludf(B2G{!_%C;U zu728kKmog7R=OT2_;{DL_CJd99 zL~460d(HSMkc3z)Zw76-U#rNEPD9n2cGly66TDj^4vgCUeCs%(N-n@&Vt?EJd|8cD z6z8~`qk#e>AQxQ4vNFCe{uRkWFGa;6g1;I$S47y~$?|XVA9p!+z$nVzejfyIlU_OA zQ1rn59$q)N8w)(VvWVVGNu`0EdD$4ePjYw7QBeLKp%h5Sr_Tc;X!B}8pBcAu-|< zY6?A??2}|;A2u2unJ$k2dO6qU?w9sZP$ze{&L0bX&P-FWS?nhT7hX!M?=G$n-{&8# zrG1n`p!46iPv$CnPe80;K549Ibm;yBYA5ZYDeDq1+!RIGv}UwB{$W*5ZQfBLq3mrm zQ)I?Imcn;G2NA*_t)}206h{z@Tk%z=YWHWkH!f;PXN3!X^Wo7vKombmf*Jpnd~bTM z`|gY4)**N@X^A@1#OP{~LEK;VHXJVJqKO%qK@pkZYaZ@$q)iGc*nC=diqeKr_(nVh z4I<&-Krw}qFa?;Y;l?I<-12$K<>CyIkeq&wYog(ZXiJxu_!jpRdCyPNj>eSU%YSFl z(`u{aThdkt*9JhesR#YO5C31}8A0uSxoWPu($oHcE-rkTk& z=+?m~*Qi``b-mT%E!)3bZ09&h$oq9SXrIZ;9J7l}j%JUZN*$)%;5d=l#i^6yQ7>Q8 zRC;C2EU|?|^>F1cG-eH%8Q`(}yBkdVH&6ifGsx3tN@ZNeMrs~mpyphov87LyKv};P zbK~Oy9h+;|tz7ll-Z4FLt2$O>i8|8d$tI9~Zz8gq;r{)jm3tlpCS&O5$SM!=mf8u^H=r|3vDV*T^ylom1Zd zuK4b6Jo!wIHfD2^MV*A?awWM^Sr%v*3`RKmKi*9k0_kWJ>1>43e2bXR7NGq*9M!Y< z$9iwq=P^g@iyPhX2ntdw7(nGKzQ0ds{n%qHz$HQ*O7f^=gDD<2YW6YPAIUiD`N>7? zy5^mMlwbM%YbP3-<;9Z9?fe4qXQ4?R1ry;Lqh~MM@P~SLUbb;<(DM8g(-#^&ytQs$ zUR)sV>X|s?)U@sj{{B+CO$|I3-a9?S) z4;+1BCqu-zX=Qe}eE%Q;#M>uW!NPV@(?^Kb2@(y+%RlwRJbFWRLYTlz^1=v2(ceEi z)hPE`69m*-T2YLD8&O*QytX%^?0EzAW}%KIyot^wM|9zkSHDdxLSM^2P0U4ZA@#mE z%^4d|v@-hB(rzMCCjiJu@g8X4x@ApkZ0Y0!x(-u=p?=~l4 z;MUCOM@#z)nc4|S-{8cQ+;ktl3^F?D8BP4YnfASoL7b`*=>?ccR1@pW@|d(={d)7T zKp`qx=PTbUvsEXnIpU*oEL)$Td{Kq7yj6Qxn~(>`G7m+$5^JJ4@@QTUDVefZpip@| zQCs=3JVwOWqOs$n2nYTnG_k98rQ0u0ob7Kj=Gd&pv;<8;-;3(KB(}7ta}}brT9}3y zjw-AsGZ*stl5y&oR*+R=sOfNGN_AoAA&;qbO=INmwyJ! z;$|UuRFzG4TFP&jB;v=P|tC=(qQx6 zMl-=D$JTDgd5iJtdCPamp2&W8#axd%wXOU2r&dqd^pj>{D8Fs%!Y9?)4TtwO)YB75 z)|VX|m`RE~>&a4DvO$y+)|3q~kr-iPusm+tb9OB4s=c`E>x*)cY7i3YsO1f^fAI>3 zadPqtaLmz&3efi%u_FvE)jlQ~#YKQIJ%L{i#`FZ&0SS-BgUXojq|2F~52lRsn-hTTc&0{NfcR%ITZT430+UjA>e`k;F}n*qbQ6O(8GrUGf zk2(>x5Rg;o zw!V*wipr1Eq*zpw_T+iO%Oa+`TLi)ja)Dbc94;k8eTDcXQETc^B!E3*O(+Wteq zzUZj_RX#)uQA7g&=b3S9zz3=Zz;UtG)-&sSt+IVkuP=hufALGdK)lRR&r_Ji&7+@vxanIy$ z{Zx{dUuj;OlF@t7GO-sT-l@x|I$x%Tc=jN<8W1T2IYq%)NY`D`oLZ@5a8;juB=VjJk~0XDNgDi8`lef%g9Q z1zSCBwYvh8Ys~QI!{IlshM#>K17esh&+exfjtlt&S zU~R|8XT1dnK_!Y^iV0}^1dcC3kwcOqCX7~P@-@maG)Vu8FCzR2^{#}4?3S>Fmsk~r ztaj{AM2me?G}gu{;$tf6#~MApg7RUK0Il;(B-nJv{)*=NiZg0a!SmEHNq1>8JEN8F zAQXi<@`3-{Cr130w|qIvb~8ilRz5G(ae52;nv_Esa*>DkCurtSx|tS8!GJ{w^_ z1|7db4l*pP3oMwz0%EE$B@sjye`mCnFeK|4f58Ypj9Gn%3-j7d<@mCAg#D}e2V#PE z)Yb3@k*=%l67I$58Rz4?|4duJ9iXw_v7Q)M#_Ej}fWPnZJqOUHE)uq~`)Fv|dm3qJ z`6-*+=_MS!9~Uk&7b^Qbesg7= zoZi&b*A6)`Hb15Q)@+P<5Tm~D08CdrDnAt=Z+`uRV|mMTm=bmsz3rYv92>?qQ&~$+ zDv|P;@Z)zyqREKOMw=qmJM-4&^73#UcA0uP5mWo3AprW5W%D*Q)9Ib7q=hT%{lopQ z>ZE~7Hbt+^Uf_uob;1=%6(EF2Gm;-4cD`E|BxY(8mmUC-^KJ6B6W17Z&XSPuoOgn>@>$}{6+4A!|J@P?m0r{+npY#xy??#h(Z5kzXFe%P-$;Mrmr zY`#czi%nLzh&|w;-7dmgC#dErNP0Q6$32SB`}vdrJJMFpi)vp34;73 zqy(fJq*DYWN4gsvhP>zKecu0j-|zeGcVo`;ocZlFYpvgowbwp#81~utk(civP=TpBIzx{ChU$5M;Ec$1Ss1x2nop&M zm0<4up)2m)XH$hM!~1bk9Un_n8;Y_QuD3_?RY$1`X`~={!(vp0PIqZj?q0mljAtx< z)a$J3)g-+x3N8umsApbHFUQbPVq- zk?5*W2-=YKT@DU#M2I3>-wMOO;baKrQ8veOQQbIEMzWi*D+QXht0*+fSUNB}U5%o8(Vq#Ola` znc#_Dkl%=9^1-}bfVG9v>Y+*mHZMR^=2Dob!^qUeG1I$l*j-Kg!q0pgTLCDHV>2>b zqMv%n^l>^Ub?ILX>O%~>i2JFN_bZZEW`_5|anP3sVfwRhX4mj*jK z{_-80Y8{1rLPi`Ej_>mxh45kUY1lPR6*hD*)AYM4u0~42eP6z1qU!o2QEvxWfxnn< zCm!pp1J_VCa19N|i=;VtmBPJ1INn$AoDGQIWcx_zb+hbAPdzSszsxi@#iw6&zH+Y& zmS=Omni$?H|E2wFf{-%DP}f4>QF134al(9+j$rl(2OgQzQ>zDUA*e5<`NZ2|WTOx1 zjKMeq7r8?v>;YoBfoJU*yEDL(qd2)9-R1bbEz;J;n-at0(wc#w_)>M1oRoiI)cn8Ks&y%ajw3d3k~t z_&D{<6~x#2g{=Bx%GFN}mB>6UIrMSW3D2Ki8ab{IsMvs0rbMs!!y90tv_$VxyTSb4 zV>1Fjwl!VcyKOi`Ig8;7REg8;cdNw=ryi3l@YGGQm*QWv(I(khKXH*sCmypvfGmmi z%o)Vjo(ow$k1^rEebI2#8-fD!8XuyzRN(olR-N7^Wt72=Q>Qmc&;KXveyc9!h z;?&v`!G1xCasOQw8TIOjLuk2P_{fL@(c;haDRaFsmP~q^yx*_wtXmFc) z=1swK(H{-W3#=9V^1OJDt`|v$k5kfx{j^u7q|GQSh~j!&qeDEA4I?jUq!Fsb23gvI zp58Y3K?bt4P+tekKQKNsGS8PuTwI(ha)1*uPb#HLHI%cn)7DN+$`k5ES4V-Hka*3# ze_^Bgywtt&FWg1dFha07r)6cX>goAG#v>e^<$2giRE4q|1LuYx4XPNk!W~dxtR;7w zD+E*DJTdG>Pdq|lBHJ2Fda2lt@r9L}8-cUq#aRthF+a4m;p_8(qXqU2Uz;#WDT*ORo1 z);tr$Ftm(C3N5s;(b`V-IX=zVKfZO|;a zc}|{l;w}uK)li=bD~T%_CWsfT8|i-BbA$~(Brs*Wn3G@Yt%APf>&lw#x#pjleI>7Io*FxPy-du~5&#&Cto40F7#I;|7#PB ze4U-POq^X8YbY+w-d`yWSACt$i7^vEbJE)id*^IoO*bk_+&`Ot5iZd8O32@h({G|F z=c@5`uGs_Q&+GTZcS9+*rsBV*!VgK{OWI3gXS!5;unRHbc;DQcGA~q$y3h0S>KfiwXN9iU$P2zORrAgp9Em=%T-n6&<%15TlV8yLo|rAWWU(pB^#Qa zw%B=uU%ZM7DPlb|S?~$+owWfE(H)(M0HYjRS2E_bw!7K;y7N?qsHXzDJP&wPxVFAhx6o5I(cpAa&h?qA9Q6npK6 zUESh)oo#C_Z9j(3^(M7{=}Fyd8kx?wrLwO`tukvq%ch7OsmQ{-SNJ%a^>~i(KEFXt zQyRCMMg*qdD@o+CPDbDx#`9}N4BBC_rZDSfUf3-84sGO+dK-tXyWPXP6t$G~Z?&>g zYAi%FumhcTR1|AIR%<<#OBoTTOWdfE< z_VXi(vWNQe{l?~jG28jLz?_OXc8Gzhn0Jv?_*J*W1W#0zj`k@k zopn-_)ohX!SzhVlz2wmAA*lG8{4HUSFZAy9Pb64sMZb(=fS~mvQ34L&b1zF=2VYN2 zLPsg0;!Teg`0F;MVT6CS9jZFO*D< z;FC6x+^9MQlIN8g5bY{RCF#}K6RPl2grv+xZ`$SdOK8B?r0TEoBiwNF*(Pn!mUz2Z^&5gBP zKCL`Taz_lA%n@0)lCJ{HN~;$_HXR0w+}qrxOX}w9_=Cfb%=eSLZyaIOk(b2XPgb$# zU7URKy51nn(L{x#eEkMberac}$EQ-)wx6sqfmJO-_VH(Cn$%Zng^Bn|$=U$^UE14e z0VD_Zbb!XicUD(Mh5->0rlrDHdMq*8Y$2=Wmb4cS9w!8sjjA0j7W#qU&C?5WHj9f= z7akJkIBuS8OscYhK$=W(ik6J3dl($ucQFo2uVdEak>H5v^H9799VfsTBrWyTkO?bL z{_A?+dp*L_*uUb}86=Zbi79m3B`GQy$KgG;?I!uOn*%xkUzRZ3gj`M<@J|uT%!aNZ3Lz2_& z2OBR9fu*{ivx+$bFUg5tZZc~Ty!65=X^wvep5m|-g9F8^zp8f9nZJa2TZvfXKCp@4 zP+*czD_>yWBssv&<>kLCI>@5%4}q2bB}RhcDm`$S98|lO6X@})yg@x-aQVjVq>fpE zGO2Kz)TAzsYr&z0+&d1eefU=E7I%=3d~O@saJ?#D53RJcHmWcs_-ezdxF{BzPAUq= zH}uWg8JH}GTYdCa{1DKEGjK#y=xt%{Ufed<#yt<4?{vlluX{i9KM08SH79a*3^t#(De53n9UbSY|ynitNzG2&0aoRXq zcHw!{XPQXxIT5{uejSsf?8JOPe$i`HQ3D$*%wjks?BRJ9pzWStl%|@|CsO`mT-ka| z_Moa7*BR-!^cJNX#l==*V0b6Cq0}2UC@D$zu=}i#bL~?Rs4uVivuu4>dR+DZaBI6B zMJZ5{raJel>ixC^J0IzQT;{`q+QlG7<&?&_Y+i+ z0Z+L0LY{I)x{`eaH|gSdJ@XKcxg2m4s-3jeO13A;Llk`yN{)S@aeqhe&2aRrtlC6x zV{i|fa|D3pXGD=sPQap68O@=+A_*QW!~wiq-8q>4IUB<8;E zKYF9w@$_0u>~zHYSRj(7FO5~+?Aa3-PHz`W@PTcE&jD9wR9R%M)}^S{>{J&Zk(2Y@ zUVc8^A&J}!&yU@8v`3+pJ|GF<^5gL^o`RsqcwFFKdlKQ>4J5u)!#iWro3+U{1S(Bu zIPS{2rs8eM@1uZP`>5@3#|C<{(?`)8I1G<&*?c;c(`*lurJmu5wMqSktw+F!c_`O6 z`m;dNDcn7&#br$LnXBTX{Y5$*fX8sfg1jQuDt1Qz-6>n1i|lJ$d@ie_8SixT>-#`p z;*0sM6L{y=!`1oL$9_L8f@7P+i0hxeZ@R)T>eGLiM;>o`V5F`+^b?JVqc=Voi#S2*cMS4qm zG}tA6mCNsh&KNxJa7s`&4mK{0`QdmlwZ^qylZfSzCA&S?9@P$~yY^0?e{xb;1Klp4 z-dJSNCkMB!o%eh5sJtZ>jyHN}4;(#X9u*Kl%N+V@Qe{x$3QM99ULn4fj|belNR*K;Dd zh>64=2_TaJ>L#5Zq=jQDfBh1K8FnugM>~;)C?W_&O5aYxpZR(K<8VN6fHk&ouD?5K zSOVOl4JO}+9*S)S(CCRFqX2SN) zLsn^In}4ElhlG8DTMysBY@yPPp;F)AuZR4Zu>JGbtNOCd3ur($cShuXzU;#!)z1e! z`b@ZeAFTYS1Y^sWAJ@okT#~-3V+N1r)`e>{t}VXGo%CY8ovaLSPlg4z?k-jqco+kl zdv`Z02RxC%(!INfl?R^7kmKIn%PIh`WLR+T?qd~!cQCMdboaB0!M`(DdUOx4O2U^I zay+^RS!Llz0KgVawN zXCp3OIhQn5I&8ytl}4wCv{o8tGpu8S{a}*S z2A;tf;{9NX)uC&1z^r>9uIOF4ckdLhW8NPC{5z_<%J>g*UdPn|`6C;4|7uDve*X%Z z#>uF;Efv?Q4-#i1>DLPW#mhh3{kH+|AFK13`n8HDV;sxs9G!#_CK!#@V|AG{Ka_O#jxIb z`+o+2e=?g6`e!F+EB(hS!^bP%k5_&i@63#aPNXMHq<@%5ub4=0*Z-e0{u3csbKt-3 zNUQvp+PHkr6t_V@(8WTcj3#U-xOWJqr462jSi#dL^zv%yo&AGD#pf3nn}#kI7mle_ zmvbi5(gr1`D?|PmrpE(Wzuwnvq$}*c^d4b$pFpMq3cD63S;xDr7hc0=NnS(#j0^hK zxZk~~v2+c!$Vw~Z^LzaTeBN0r>y96mY!-19mNRjf+P;09bR4!K?HHS5mFoVEQ%!e# z8h^sD;HkBoL(L%8N4}o5BFDn`s6qEU2!=p#As>RN5PVVy!Lrx!Uw?Q3)MmPBDzdh@r7QC*3%uON);4~eTGxEu(`LHG>GH^IGp-34h1|4VeUqHu$*b^zv&e1Zo82qNjF3Ama$f%bW?mw zYQ4M&OKZA3>SrXY3ATf3`D9a!_22DP1)P-R=>{ruG8m2&GU0V1x98kN^RCWSY z{K@!-`*UkRap1?d`{GRRk7Xuxg}2NI$UqCV@Tvf04bAl_AqmpYO4G}o>w0$esOV>< z!``)k87Y zFgvhGgbn3hxhtI%7IwQ@WOLKNZ-yJn;OF+wpwA;noDuY^3EA83i$6s{XBl=8ptBEy zkTX1g>(NI8(LnnMSTg+8EuMaA5~BmPFrxy}!u|niiPD3VF;0+6Ll?;9(lyX!UbB7` z@jhzPr~Fgc4-cxL?Kuh3cu0rP1Jdy)1$At{L9}Jk5UnNDmy6qw!<``O58$^C?EPWy z-Sp+RPC#+t&@zEsCTKkA4;g!sK?M7daf&=-jD(CsFCgOu;44e~bSwL=GdHnwg0cM4 z)<{J`(NZ!Q8kpjekQr@LPDmGY%yxFKKnPZ9xQN$=SM?0+R1b^SPEyl=8fVKTPBDwS zh+Ql10o~%`c&9vNpiiFVY-_lDcMwmuatm30n@kXtPb&8{$poN>%Yi;Fh03k^iZe37 zv+Bt)btl#a8-3v(_>zq0L&mYtJBf=Kl*Cj1Ghc2`uBp>XQ9Dd{F>MdLv0!0_I1 zqwBc3O~8lH`doeSdk3h^v+*(Yt-`2P@y+Ygg38lD+{FOGYSjAJv5}+x*2rm-*YR~@ zY0%NTO0)RrcfNZ;n;V?o@XkBA5w2Cy5mk%nVBMICgKOjHhrs^%=wc@WQufF{g`!&c zbLt&g&swmYtoNrZEG`u8RqOUy66{8^tM01FjR}9XU!yr-Y=SN6z1BSI^9}GP8zxy}ZW8;37S!w~8EO63+c{$*zI?>g)Tia#NK#4rTc&lDl7bVQl@ zq4FdHQyo+WZiD4|cFP1$laej>3bXj)Z%`L)WvhTwtFc36Yj1klrjcX%^S(J$&fM7> zUIsvcOg$)=ZKRlPUo3P4`Bcl*8X&&e#Xo!@On>;;rT_2^F#X}vl7aZtEuRIN$nHRV z+xx%y(p7)+t(^VgGv52dCw~d@fpqog{ktxcE3wHzEgKsE=O9re|Kfl=3v>K!i~2K8 zO`1q>c+|Q`!@$__!^w8u;3VZEaIVPo3{kX8pJ8w&pZaLF&>vc3hb-b|j3A#{3G#TT z1V*miI*D@y+4B+zH^AaYZ}ci8Xj(?Dde{0jbFALlGuhyj%EuHudy(<;#WOUznP;;_5ELiNgkX~@V%t8Lu;;Z5LK>#fx}=+0ocx-*)&>t z$%&A*L`EbDRi}%mRFOr1JEenZ!%)~TNI0tY%dChe4jjG{bQs*@AY)mCD5~O)SXltu zzH$-Iu=@CFGMiCq3J#6f5=I_a_-#swTU-kU5B{exxQnS}BS+V#pRK!A`oeB1 z>b`5^oa5^5Pl%b8!0>dN=}W3G-S8+zEDh6CW-Km=(Spqaz;^Lc{vO{YbGqMg>n5$? zy9N#CZHK+zfubm(ulc25=!;qWqRViau}^bHs?>b8u@=}_+^auZz0^R-zRY~~G`?>$ z2Yl~m>#-FmQIpq?*`9||xBsgOV4-%_|C_&>CPpC>I5;0(4)<1TF93NC(fX}5|r=|l6QG!J&|f)uc;7{{qdgC z_GjWyrtY=)h^pSn=B&<`e_A;hQ0HDlY1EV;bxbH&yDZ65+29KPj#3cW!@v@rJmX$Y zWi$s=Gf0Ie?^+G!DE$uFmwNh|rG-Ps!D=yI{zxVb=CkM1l`E8{8(cLfaGjgDHF$vE)>C zIurvAsvWHnvU(@@RYF&rD8q6mp5N>*a`FE*XsS;d#8#`*@^Vv{_ z%?OUfZ%zOO6tDjX7;+==(clEk==5D)yPx?xi)SEdhqlKPK##AV9h-w;FY_m@W>C-t z*YDkBuJfbYoIgB@?N z@Mrd9+W4+DLGBB=m`*fTYBVaBl078fjHkZSCtbVO=OfDPlOso8uW+#$iM+BuU!6X5 zRlVFvAm5_Kb<1g4P^=cl!Mu$LRHoO3S79f~@e=4!A(h;#anr>(MqIAuDrX(=+q(jW zdVGc&Ibhqmj2ADw%13Y?QiiY%H&8rrB|g$CUc`N!VQW-YH=_L>Te6a`wA9^&$5o)= zN9T*<>n2nWrXITxRUXc1M=>H_S_%=8GInS@yK$IZY3xLMWog_($69Uh60i;Ni;yvQ zQel&3!)1QEGCeML_c_TOp5eK9F}3}yiBR30g;29p$)qEEN^j}TcjdUu9V3lC?qXHW z>6I1oD&1KNv8#C#!D9v+J9-S-sl=MKxGVI=E51fy?PLAu)#r=RIBo#JX?W((pczao zHX_V5a#U`^5EIj results = Quotes + .ToObv(); + + // proper quantities + Assert.AreEqual(502, results.Count); + + // sample values + ObvResult r1 = results[249]; + Assert.AreEqual(1780918888, r1.Obv); + + ObvResult r2 = results[501]; + Assert.AreEqual(539843504, r2.Obv); + } + + [TestMethod] + public void Chainor() + { + IReadOnlyList results = Quotes + .ToObv() + .ToSma(10); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(493, results.Count(x => x.Sma != null)); + } + + [TestMethod] + public override void BadData() + { + IReadOnlyList r = BadQuotes + .ToObv(); + + Assert.AreEqual(502, r.Count); + Assert.AreEqual(0, r.Count(x => double.IsNaN(x.Obv))); + } + + [TestMethod] + public void BigData() + { + IReadOnlyList r = BigQuotes + .ToObv(); + + Assert.AreEqual(1246, r.Count); + } + + [TestMethod] + public override void NoQuotes() + { + IReadOnlyList r0 = Noquotes + .ToObv(); + + Assert.AreEqual(0, r0.Count); + + IReadOnlyList r1 = Onequote + .ToObv(); + + Assert.AreEqual(1, r1.Count); + } +} diff --git a/tests/indicators/m-r/Obv/Obv.Tests.cs b/tests/indicators/m-r/Obv/Obv.Tests.cs deleted file mode 100644 index fe332d810..000000000 --- a/tests/indicators/m-r/Obv/Obv.Tests.cs +++ /dev/null @@ -1,98 +0,0 @@ -namespace Tests.Indicators; - -[TestClass] -public class ObvTests : TestBase -{ - [TestMethod] - public void Standard() - { - List results = quotes - .GetObv() - .ToList(); - - // proper quantities - Assert.AreEqual(502, results.Count); - Assert.AreEqual(502, results.Count(x => x.ObvSma == null)); - - // sample values - ObvResult r1 = results[249]; - Assert.AreEqual(1780918888, r1.Obv); - Assert.AreEqual(null, r1.ObvSma); - - ObvResult r2 = results[501]; - Assert.AreEqual(539843504, r2.Obv); - Assert.AreEqual(null, r2.ObvSma); - } - - [TestMethod] - public void WithSma() - { - List results = quotes - .GetObv(20) - .ToList(); - - // proper quantities - Assert.AreEqual(502, results.Count); - Assert.AreEqual(482, results.Count(x => x.ObvSma != null)); - - // sample values - ObvResult r1 = results[501]; - Assert.AreEqual(539843504, r1.Obv); - Assert.AreEqual(1016208844.40, r1.ObvSma); - } - - [TestMethod] - public void Chainor() - { - List results = quotes - .GetObv() - .GetSma(10) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(493, results.Count(x => x.Sma != null)); - } - - [TestMethod] - public void BadData() - { - List r = badQuotes - .GetObv() - .ToList(); - - Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => double.IsNaN(x.Obv))); - } - - [TestMethod] - public void BigData() - { - List r = bigQuotes - .GetObv() - .ToList(); - - Assert.AreEqual(1246, r.Count); - } - - [TestMethod] - public void NoQuotes() - { - List r0 = noquotes - .GetObv() - .ToList(); - - Assert.AreEqual(0, r0.Count); - - List r1 = onequote - .GetObv() - .ToList(); - - Assert.AreEqual(1, r1.Count); - } - - // bad SMA period - [TestMethod] - public void Exceptions() - => Assert.ThrowsException(() - => quotes.GetObv(0)); -} diff --git a/tests/indicators/m-r/ParabolicSar/ParabolicSar.Tests.cs b/tests/indicators/m-r/ParabolicSar/ParabolicSar.StaticSeries.Tests.cs similarity index 71% rename from tests/indicators/m-r/ParabolicSar/ParabolicSar.Tests.cs rename to tests/indicators/m-r/ParabolicSar/ParabolicSar.StaticSeries.Tests.cs index 36494f1d5..e8eea6afa 100644 --- a/tests/indicators/m-r/ParabolicSar/ParabolicSar.Tests.cs +++ b/tests/indicators/m-r/ParabolicSar/ParabolicSar.StaticSeries.Tests.cs @@ -1,16 +1,16 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class ParabolicSarTests : TestBase +public class ParabolicSar : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { double acclerationStep = 0.02; double maxAccelerationFactor = 0.2; List results = - quotes.GetParabolicSar(acclerationStep, maxAccelerationFactor) + Quotes.ToParabolicSar(acclerationStep, maxAccelerationFactor) .ToList(); // proper quantities @@ -43,7 +43,7 @@ public void Extended() double initialStep = 0.01; List results = - quotes.GetParabolicSar( + Quotes.GetParabolicSar( acclerationStep, maxAccelerationFactor, initialStep) .ToList(); @@ -76,10 +76,9 @@ public void Extended() [TestMethod] public void Chainor() { - List results = quotes - .GetParabolicSar() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToParabolicSar() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(479, results.Count(x => x.Sma != null)); @@ -91,13 +90,14 @@ public void InsufficientQuotes() double acclerationStep = 0.02; double maxAccelerationFactor = 0.2; - IEnumerable insufficientQuotes = TestData.GetDefault() - .OrderBy(x => x.Date) - .Take(10); + List insufficientQuotes = Data.GetDefault() + .OrderBy(x => x.Timestamp) + .Take(10) + .ToList(); - List results = - insufficientQuotes.GetParabolicSar(acclerationStep, maxAccelerationFactor) - .ToList(); + IReadOnlyList results = + insufficientQuotes + .ToParabolicSar(acclerationStep, maxAccelerationFactor); // assertions @@ -107,28 +107,25 @@ public void InsufficientQuotes() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetParabolicSar(0.2, 0.2, 0.2) - .ToList(); + IReadOnlyList r = BadQuotes + .GetParabolicSar(0.2, 0.2, 0.2); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Sar is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Sar is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetParabolicSar() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToParabolicSar(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetParabolicSar() - .ToList(); + IReadOnlyList r1 = Onequote + .ToParabolicSar(); Assert.AreEqual(1, r1.Count); } @@ -139,15 +136,14 @@ public void Removed() double acclerationStep = 0.02; double maxAccelerationFactor = 0.2; - List results = quotes - .GetParabolicSar(acclerationStep, maxAccelerationFactor) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToParabolicSar(acclerationStep, maxAccelerationFactor) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(488, results.Count); - ParabolicSarResult last = results.LastOrDefault(); + ParabolicSarResult last = results[^1]; Assert.AreEqual(229.7662, last.Sar.Round(4)); Assert.AreEqual(false, last.IsReversal); } @@ -157,18 +153,18 @@ public void Exceptions() { // bad acceleration step Assert.ThrowsException(() => - quotes.GetParabolicSar(0, 1)); + Quotes.ToParabolicSar(0, 1)); // insufficient acceleration step Assert.ThrowsException(() => - quotes.GetParabolicSar(0.02, 0)); + Quotes.ToParabolicSar(0.02, 0)); // step larger than factor Assert.ThrowsException(() => - quotes.GetParabolicSar(6, 2)); + Quotes.ToParabolicSar(6, 2)); // insufficient initial factor Assert.ThrowsException(() => - quotes.GetParabolicSar(0.02, 0.5, 0)); + Quotes.GetParabolicSar(0.02, 0.5, 0)); } } diff --git a/tests/indicators/m-r/PivotPoints/PivotPoints.Tests.cs b/tests/indicators/m-r/PivotPoints/PivotPoints.StaticSeries.Tests.cs similarity index 89% rename from tests/indicators/m-r/PivotPoints/PivotPoints.Tests.cs rename to tests/indicators/m-r/PivotPoints/PivotPoints.StaticSeries.Tests.cs index aa8e93910..56ad3e87d 100644 --- a/tests/indicators/m-r/PivotPoints/PivotPoints.Tests.cs +++ b/tests/indicators/m-r/PivotPoints/PivotPoints.StaticSeries.Tests.cs @@ -1,17 +1,16 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class PivotPointsTests : TestBase +public class PivotPointz : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { PeriodSize periodSize = PeriodSize.Month; PivotPointType pointType = PivotPointType.Standard; - List results = quotes - .GetPivotPoints(periodSize, pointType) - .ToList(); + IReadOnlyList results = Quotes + .ToPivotPoints(periodSize, pointType); // proper quantities Assert.AreEqual(502, results.Count); @@ -91,9 +90,9 @@ public void Camarilla() PeriodSize periodSize = PeriodSize.Week; PivotPointType pointType = PivotPointType.Camarilla; - IEnumerable h = TestData.GetDefault(38); - List results = h.GetPivotPoints(periodSize, pointType) - .ToList(); + IReadOnlyList h = Data.GetDefault(38); + IReadOnlyList results + = h.ToPivotPoints(periodSize, pointType); // proper quantities Assert.AreEqual(38, results.Count); @@ -162,9 +161,8 @@ public void Demark() PeriodSize periodSize = PeriodSize.Month; PivotPointType pointType = PivotPointType.Demark; - List results = quotes - .GetPivotPoints(periodSize, pointType) - .ToList(); + IReadOnlyList results = Quotes + .ToPivotPoints(periodSize, pointType); // proper quantities Assert.AreEqual(502, results.Count); @@ -238,7 +236,7 @@ public void Demark() Assert.AreEqual(null, r6.S4); // special Demark case: test close = open - PivotPointsResult d1 = Indicator.GetPivotPointDemark(125, 200, 100, 125); + WindowPoint d1 = PivotPoints.GetPivotPointDemark(125, 200, 100, 125); Assert.AreEqual(550m / 4, d1.PP); } @@ -248,9 +246,9 @@ public void Fibonacci() PeriodSize periodSize = PeriodSize.OneHour; PivotPointType pointType = PivotPointType.Fibonacci; - IEnumerable h = TestData.GetIntraday(300); - List results = h.GetPivotPoints(periodSize, pointType) - .ToList(); + IReadOnlyList h = Data.GetIntraday(300); + IReadOnlyList results + = h.ToPivotPoints(periodSize, pointType); // proper quantities Assert.AreEqual(300, results.Count); @@ -320,9 +318,9 @@ public void Woodie() PeriodSize periodSize = PeriodSize.Day; PivotPointType pointType = PivotPointType.Woodie; - IEnumerable h = TestData.GetIntraday(); - List results = h.GetPivotPoints(periodSize, pointType) - .ToList(); + IReadOnlyList h = Data.GetIntraday(); + IReadOnlyList results + = h.ToPivotPoints(periodSize, pointType); // proper quantities Assert.AreEqual(1564, results.Count); @@ -378,27 +376,24 @@ public void Woodie() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetPivotPoints(PeriodSize.Week) - .ToList(); + IReadOnlyList r = BadQuotes + .ToPivotPoints(PeriodSize.Week); Assert.AreEqual(502, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetPivotPoints(PeriodSize.Week) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToPivotPoints(PeriodSize.Week); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetPivotPoints(PeriodSize.Week) - .ToList(); + IReadOnlyList r1 = Onequote + .ToPivotPoints(PeriodSize.Week); Assert.AreEqual(1, r1.Count); } @@ -409,15 +404,14 @@ public void Removed() PeriodSize periodSize = PeriodSize.Month; PivotPointType pointType = PivotPointType.Standard; - List results = quotes - .GetPivotPoints(periodSize, pointType) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToPivotPoints(periodSize, pointType) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(482, results.Count); - PivotPointsResult last = results.LastOrDefault(); + PivotPointsResult last = results[^1]; Assert.AreEqual(266.6767m, last.PP.Round(4)); Assert.AreEqual(258.9633m, last.S1.Round(4)); Assert.AreEqual(248.9667m, last.S2.Round(4)); @@ -434,12 +428,12 @@ public void Exceptions() { // bad pointtype size Assert.ThrowsException(() - => quotes - .GetPivotPoints(PeriodSize.Week, (PivotPointType)999)); + => Quotes + .ToPivotPoints(PeriodSize.Week, (PivotPointType)999)); // bad window size Assert.ThrowsException(() - => quotes - .GetPivotPoints(PeriodSize.ThreeMinutes)); + => Quotes + .ToPivotPoints(PeriodSize.ThreeMinutes)); } } diff --git a/tests/indicators/m-r/Pivots/Pivots.Tests.cs b/tests/indicators/m-r/Pivots/Pivots.StaticSeries.Tests.cs similarity index 74% rename from tests/indicators/m-r/Pivots/Pivots.Tests.cs rename to tests/indicators/m-r/Pivots/Pivots.StaticSeries.Tests.cs index cc53cb23b..ef3c9385f 100644 --- a/tests/indicators/m-r/Pivots/Pivots.Tests.cs +++ b/tests/indicators/m-r/Pivots/Pivots.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class PivotsTests : TestBase +public class Pivots : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetPivots(4, 4, 20, EndType.HighLow) - .ToList(); + IReadOnlyList results = Quotes + .ToPivots(4, 4); // proper quantities Assert.AreEqual(502, results.Count); @@ -38,18 +37,18 @@ public void Standard() PivotsResult r120 = results[120]; Assert.AreEqual(233.02m, r120.HighPoint); - Assert.AreEqual(PivotTrend.LH, r120.HighTrend); + Assert.AreEqual(PivotTrend.Lh, r120.HighTrend); Assert.AreEqual(233.02m, r120.HighLine); Assert.AreEqual(null, r120.LowPoint); - Assert.AreEqual(PivotTrend.LL, r120.LowTrend); + Assert.AreEqual(PivotTrend.Ll, r120.LowTrend); Assert.AreEqual(228.9671m, r120.LowLine.Round(4)); PivotsResult r180 = results[180]; Assert.AreEqual(239.74m, r180.HighPoint); - Assert.AreEqual(PivotTrend.HH, r180.HighTrend); + Assert.AreEqual(PivotTrend.Hh, r180.HighTrend); Assert.AreEqual(239.74m, r180.HighLine); Assert.AreEqual(null, r180.LowPoint); - Assert.AreEqual(PivotTrend.HL, r180.LowTrend); + Assert.AreEqual(PivotTrend.Hl, r180.LowTrend); Assert.AreEqual(236.7050m, r180.LowLine.Round(4)); PivotsResult r250 = results[250]; @@ -62,10 +61,10 @@ public void Standard() PivotsResult r472 = results[472]; Assert.AreEqual(null, r472.HighPoint); - Assert.AreEqual(PivotTrend.LH, r472.HighTrend); + Assert.AreEqual(PivotTrend.Lh, r472.HighTrend); Assert.AreEqual(274.14m, r472.HighLine); Assert.AreEqual(null, r472.LowPoint); - Assert.AreEqual(PivotTrend.HL, r472.LowTrend); + Assert.AreEqual(PivotTrend.Hl, r472.LowTrend); Assert.AreEqual(255.8078m, r472.LowLine.Round(4)); PivotsResult r497 = results[497]; @@ -86,27 +85,24 @@ public void Standard() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetPivots() - .ToList(); + IReadOnlyList r = BadQuotes + .ToPivots(); Assert.AreEqual(502, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetPivots() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToPivots(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetPivots() - .ToList(); + IReadOnlyList r1 = Onequote + .ToPivots(); Assert.AreEqual(1, r1.Count); } @@ -114,12 +110,11 @@ public void NoQuotes() [TestMethod] public void Condense() { - List r = quotes - .GetPivots(4, 4, 20, EndType.HighLow) - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToPivots(4, 4) + .Condense(); - Assert.AreEqual(67, r.Count); + Assert.AreEqual(67, results.Count); } [TestMethod] @@ -127,14 +122,14 @@ public void Exceptions() { // bad left span Assert.ThrowsException(() => - quotes.GetPivots(1)); + Quotes.ToPivots(1)); // bad right span Assert.ThrowsException(() => - quotes.GetPivots(2, 1)); + Quotes.ToPivots(2, 1)); // bad lookback window Assert.ThrowsException(() => - quotes.GetPivots(20, 10, 20, EndType.Close)); + Quotes.ToPivots(20, 10, 20, EndType.Close)); } } diff --git a/tests/indicators/m-r/Pmo/Pmo.Calc.xlsx b/tests/indicators/m-r/Pmo/Pmo.Calc.xlsx index 4c77c998bf89fe6b45f49686f48f05dd5ab501e8..ba84b07a7cd20e81cb16438b54b8960639b68e53 100644 GIT binary patch delta 115154 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

    dzJTRuG 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 delta 95331 zcmY(qRal%&&@GA%?i$?P-8Hyd2u^_D?gYZ%?(Pl)f#B}0!QI{69nO6JfA)T!eKVKc zHLJSTs#^VaODSA>3;ip zKA~nBqUc>2NBy|=`=yC>4Jj{Jjk4Z&j12E^X{%;}TuXlOGjrX&GdKN$w{w%Ha!E{r z(1%)jUln`HjctLI@mj>2U@Zbl&Z|)WN;RA~uQ^xWFv_A0{<~wtko?@&Yi0R4ZZ5dv|3Q#IFBf3YUj;jQvd{CI5RsUNJfgBs*-RX)3i%y5DS7 zxjO8lyX831kv(OF_BbNzG$p&e{lw%@gd?AsfP7egKwcTpFNZmZetKZ>#=F95E8n-C zAdZ`3`5h}9=ze9he^-CNxN?h*+6;Q)S2xrM8!k)U@`2pMnEr-F0WD5}6dK3(P)Cf< zfAevUzoN)(9OF0!leL|wlG+msiBq=*O$`nFx>G}H4-Ek!4^9>uIA5N&tR4=|HYN@Z zHY}cYwnbV74w-D&zjNz9yDK`>*$}wIi#0?daIMx0+)oWDv{?9h{?w$~WoXWRyjGGI z$#E(f;8D{&G{jDJhzw=@UP@Z?%9H1$#|%>O*;9=l$qLsB==`m;4(VJ^PC=`Wm;ua_ z`7<4Q+vG5@puWB-=XD9;H-*3uRc10dt0S&ZpkgKk`!z*9xnE5CuaE(~yt9F-wfqHa zC#e_p52yzCnqLSZ`YbxqAdYec21TR-I$FTdX@FL%Bx7A_ck0jjgF=x+m$YJ`o_7coSAoOe2F%Nl}V|3)GaM!V4Yn%p0FH$=F3d zFqQmXSc)`FxR00-rz1hDzAqxDyZUp}62EBPk~(Ck1Cx2p32EwiIl<}Ito$Om)_r=? zLlK`d-}C{6MaThyKyao0Y#^kT_HKq#7=RoVFl(h926}Vdes1|jWQH#hwj;66wp2r$#cwn&&OSwb9= zpe;pX;72q_P{Ql+)vtRCA5;OpI-TF@%ntZ22c6@1^&RPoh7Y-Cj{`mE8ue+12Oz2L zJ(mo4doG!HVJ%&c9)HLSKE&5*K}I?8RRhEMU^lYVvAJTq2haaCuNM`mvXDf zZ~-!meEk_WH)fQA;|opN{4@5WVcYFd@cj@n^cEp|4ILs~#=iwz>7jT12I9M-c3*Bx zZaz-5cZ>k_b>yuO9f3R7J6oi`0X^B+uX^y(3&r zMXY9pdbE&7g;C*5yLge;d?PfPVnaIxNo0QWW(6!MU%NVElzek|*vEHZ+PO*g*yy(T zreR|3*vjoS8*7(oFXIt7=5Fi_1NRVBRl<{ee$u6+=s-~Z;e6_8nJuH$>wd_6tClfV z@q2C@6aUC!4sjNwRX8cgj-!XLAdU7ssFG@s}rUky)(G#vtW0MAG{5wM%53`wv zFkxgoo`8&q-#MPGTnMpaEPee-8&#O-?*P^1M2l6qNl!l#-Pr1g5hs^<`}5n*Yr!9NBnPo$MR4gBPbxmoQDo|({fqf}uaS%8 z`VyhPwT8ne`f@E_grbMiDztmVMT_}XGWnftUnv-RvLSFH;(3q0opx+K!K9iPuT=MN ze(|``^1;pCt?^nezeO|$Y{`mB?7N>)*2)0LQ+RYS(x|^yzoV<*a zi{oQ<+oe!%Q}{uXjz-)#ci3~?#;*7GG#teQ6*DDjn|f7+qbO=%9VdR&7M2PO@O757 z>-FMrj8_zR+qU^}KlJ%BVASQi*#0`80(?FQ&3yuPS%r9XJzbW(9;|M@t$ylvz8?2SUq--!2v#Z;bBoNbm6+c&wW~1asFMK6-o; zZEE7nKG}UT@&;4jA_-zc@4^v_Ym6C^aEl6O_A+y;CKG1NaJsrgV&w&i3D}br=%(t}ou#x}a+|l~QDI+%f`;rZjXk|p~ zOC+!Nh%H9*=3C%<$# zJgCdFuIb-g>Su}S=J(=GGU9pK+ykHADRZ=wn?|{yb(e+pI_x9V)<@xEcotk|jnWtJ z+V?1?K%tmA{HxG%+29wz9B;)St(qH=-Fz|_4MX}_t&dCz?VwAX5?o+WVWkR!EsP$ zCa9(Fw5)jG|HXk<+lp0I<~%$<3w-h(_MTr)Z~x6ug*7KZ5klJ1Pm#FN{$9YCOYGE# zRRntXm1Pf!#}aGDBc3Z+I*js+HWJ33b0HId<_+k4{96R~fgIQ2O8d;vOTvoky}cUqJT?`Rj(K)0*!uGi*$AWk&Oo8xtCvH?#f50U>UYEAaKjf1r4_C>+C}q^a`j9%OJxWUCP$uUj4)8? zBnj<(14hIp$Px2z4*6G;UG(jtIJ*4-s>+kIAup0lL*&E2k-5@3gp+?UTXvi(v&T!s z%Z8!xbtM5IsPwwDN-KBY)qGy1dWv~YjdM!6y|9mJKysPC3fTP5WVfR<;xm**dJ?0i zN$x0xBVk@rwC+tX513=$ujd$PUV4CL*7aW?a?PcD0-R81`6f(7dC#XDn;i#_*>c?$ zij-qt`WH7jxxj#Re znJr?~w){s1%@)_&cZ~MgDUbIx;!Ir4CgOVsFEAF5x}mfE9^;QInX*b6K)89P9Y(}* z<7n{8uHtb4%!zbwCX!~A8iSsg;SX&5i)D3u{ey#HwDFaqsDEe8xy@%`rb0+Z+!}3b zg!zM7WK1v^E2b9%MpT{uLdo~U^^E5Je!sIs2Hkz9 zzvB{*hCZ*X2$545s^C4l zCO{l#%7JgS1{B5VV)$+0zrW>nX(r~v22$e!VerYQI9w``{c+(V!kKi!M4iG2-w+p> z^ceDFN098dfZ&Y9N>hx!L@rfMG|ukha=l%9)EEm$kD=3|BoDm%n*0m!#}uS-&~Bc`9BKb9&b687B9ao z@(*8cbs(6XxK`Eyzd8a!gGF1|xyZcTF=;&%M}Cm0=9wau{5`1{^v{5Rxu(>x5dC+~ zNBU|{MMQ8H*s+p%aN_zsZUp-V9r;9nKu=_*k|@Ak&Fx=0@iBF3cuL*h z0zMogX!+@3VjgM4$y*WqOW%;31zyF5rOTGBp^4@yJkf`M*a;cL;m7&ZjsSug)m^u2 zQ`mEoiM49KbZcsqqm)FUH=veocg^tVAvOvO$(L3!49ifBAtzd zWCdghC{hON7Hg)S1uAy(36pC6>&ZVg@_pUx)zjG`WnqsD zv4~(IhCM2pU|XqxzzELCgceRBz?a>DLAKj7Xg$^4Vl*0!!bu3khA(v3WS#kf#Dty( zO3*hzD!62~h$1C8!~yN`1}`k^0L{#N_{RpXV9Ltlq;KN+KdWu4h`DRI{fm@{cXp`e zAVtH!0#z})>v9JMB=}=3EXfqPW8{qs=PWt>BO1^K zPB&C6j&uSy!2e>+Ru9_dA_^ptbDOYjoDd>zv&eQIz}Oi(IPgl`nqA13hoj+l1}0TbKcIvi01@&O z4xlE(1#3>{nff+&%MVJtu&}I8*?{>{KJz6B0cTb-CC;siWXJLzr;&+QdUoLi`HVUkllI8m zmw1Wa8{`CLh8pJ6R0-M0eT(*%ka4+soJ8Zb$+4XqkC((BOj>w^MZp0#jcX87)rVEB zXvz-f(AZgP+CZsg-ipskrK%ScS4)tSi-yn)Wd%VMv(o$!Jf9<`1saQpKPi@&gHX~` zGo6BdAv>Qwy`X9=+tb~1-8g752+)_NJm?NELvQ9L7HWWpF>^xT+B|bB9KGr9BC-EM) z;W;-4-|+hZbEkDB@L#6mR2eF&ZvP?yba3FAm3qmyFKK@CSX%V_4-}Z^; zQhE87lInz%8OX@qS$_WO@Ea02L0?su>a|ON3Up3N%MzE435)JvkVrGx3AS z$MBz-b<&saJPi|grBGi!}sjZ=P_wdV^V%&i(~6YgJXbOtyObqAS^7FGOlu&Fu%x5D$^a+KGg_<0tWJJD1-ZU%?2={)ezeJh(jWj#25P z*l|~8EN@LDzeXqaxeD6x0|t*PG%VCiCL+g8*w4AJikcII6U)>r{Q{I3v0)m4rxN!g1G1OjmIEoH+0a09sR)fp; z_1rH0d*D-FmBin7b@fdmGu4{$rl;^5E!R5ub4@sL=ABapb@vr0B=fEos;1w&=(G<>#%6DDX2?^!Mkw(dWfI*T+ffxVGM;y3nTT z9`x&*?7pMIjM^-S)T6z30rBlD&*A#^fbojP*8NZ5IPSF@EQ77N$}FNQ5YiOVt0tEJ zA{qr_k#66W!1M^6h43td&zd?#4u1j)Ag?`N}1*b6_%)wL5dKsuJ~BuYVD?R5n*w z2}xChfhKOPXkkYH5_T#V_%(n42PJ9^zg4C)t=|n&LKWm73tGB>!}_xf1wG?2x@^SE zCjn=;s!9g+(T(|e>y~5Rb2x&nrG7Ca1CvtQ2nF@ zsp9T-wMWs(BF=sSZW+Pn?C@$W=WBUJJf40Z4?n!D2%`h zox!}G87d52 z-{z(#r`vlEEc?rotq1VFK*Xli+4>KzDl4dEe(f53+I(i3oUc$t1KD4(9Xyht3H!)& zTjz+aAPP^bT1lGpGD>mon_Z6D`MS@7Y8K;guz=m`8@n!W)06(}1|AOY{+O|1ANkc* zH0&}x(W{}N7oG@B@Z@Mm-K~hXMcp$uml|&;IZre5`v~9~!m-Zj&MDCCPgS}erl(l_?uEt0G-Ea=dkoAIj>j(`D+)Xiz zwDZk*&#ZKVBM>vh0HebK9bqtToE+e8u|5Xj_00TBa)>=XWTewQKSOwahsMkUkA#4C+wwnB$Yq!^=Vrx9KC!3Tm6t;X6=ih)VkFB1f!@J-GN z1!2xdOL=}$^SrU|Yd?37=J`Y#@x>xK9LTh`z711!UIb;3)_|-qS_7TZ*YX_Iwgw_m z;)NPb-vkjBnP=phZx|A*OWBehV=EQFG|JQ)M|QjbBWEd(NP)s2n^`vnXMSq-_LDy0 z!al-GWD5CKc6o!VUX|+yx-F2Y@0GQt0r{u#^u6BO;4?>iI`#2sEfu(!khv91RAxCe zU&0WIq)LvLBNH7_b*PTsDu3iBs*d)8+YAn>_WY4nk{IxD{u06@muex0pJosi6V=@( za^u5X;%Usws zU+yJq@Gs%=a!!d)Z0rUp(G<-j#NM-D>gM(cv^WOUwlvvi87e2 za309jdSkhnsHB95{SV;;TAeHP!` zSscgP8viP1luD-q?B8WsV!U6=sYFt(n)P&mLMZ-bbOu{b_m#>Lx*&^@H{^iUP>~`h zx-2e`f(3+`Sf=Akt}=7zh4~URb^Ej6nH5%#GOzM$&;SuyMR%K5!k5NJe9Q$diTtyS z6r&BCOV4m-ZGF@V)l0p%{%4BzbcW+ob!srW@Z4HpbSZ$ESyM1G6m#Dk@o4i0m?*j_ zG)ETQ#eFsdM_@4VjKl6aIHYo&PqRtE)jUD?=Y&3=g8^7HR>q z--%tHQN6j50ybD5S?{+z_uTY_zK`EsO4bIE>cCZml$L>hQ&Eqc1xIMDMv5R#-FdiB zPjO4sh!6E3#nJ3l^1AoFU9O=_K|EqZkI{UZ19dmn^PaT6pe_7Vd}-GSzEPWa;9{28 zA5RloK_GVMk!$|wrI;q-S_E%PBoDO2#6`=IO`jT^;@>+-k|R~8c?&DA=C7yHR$1$o zPNo^_*0Ngh7HUVlG*|4j=Bo=f!3)z%>M%wiwAdwf*nW_Z{gSU{$zE$8B4UBr)dq+7 z+L|>&(P%?&=gwkoru zTCUSDJEL~QRaX?4dpBIGv_QbNSX93Z5EzlMzEpUc!2dhv z-}LSUxb~7SAQZKW)hjC_qvJTZGyeR-3_sYSi&2(P{$!;wJ|&X96rPuVd)&Tgk^%YzgoqwLP0f+jHT8b7%ZL zzImp1yc?Z75o3i10!qvTEidFic_roPq;7#qSEG{+Q`t|d(z?!%GuUfEr}ElCw;c%m z!9I7{eNq8`yq9Bh<-^TInx(JokyF_@5TN0k(JhKzW9^UUskwg-<6l!=a-(<$#(ca< zDr78EPo4{tY|LEJM1@5l42H`s{S!z0enV(c2Zt%$V=&S!544Efd?JWh+nfXumS?Tc ze0vs?dd2YGcUQ2-nZxYv|%%JG^zU@p%QN3j=sn{b& z19H~!D{HdlmMC&dxy@0ADR|Q}-i7uGUn)e=vd}q@W$aVQt*vAykudcXue;5Cc}b;| zu!iblT$t^Yfz1n5!q42DyVN{l)s8qa?A2MXJ9ryy{fC4KMA}5Dd_&sSdD5+pifo-+ z)*ngct~Tu~>)d@TG7DC9k_}x3 zIcc#%d#lYfdV;|l+uwh1jt${|zH3UP5~k(nU^%ef0FR_a)|5Ph`;=x~_L;a3=7ycx zRt701KWEm-2r-=bt66Tidm0#Zx7+Ln_x&?*@%UwkE^LLwZ)kd5o7R0IbnmA}Ra4dV z`gf%(_nN_-W~9Eq8J6sirW7}DqX~%OcwfUum3235Rw!27X&nvMv*OJge-uem9FP_X z$|4X2erpK&3Lv|IKlLSRIGxBHz`iGZcH}LVt_*cy9pTFO)Yy>u+Ez1Yid42bKubvI z1%f^$FuJvPUGQ`ZICytrTtsbI)I= zfifkszfN{DUKj`q%`g}$_EF*$TgwVZ$KY$7N}H^Lx?Sm1MliGQlKog>23%kcrX}@dJGS_MM=4u*T*xcNkYK&ABWYN!RMrF>zcis$e+h=Y^~ew^|c{=>&oAUp5!A_U&(p+OUd zAhTJXU`Qx7isI@N<+FUYgjKP(ABdhGTjSII&Eod|3UJc)3k#}h_r(m4HZ)W2-QQNC zC^)5}UoJ>SF_GX-!NAOuL+Ez)dBhUFEQ{KM`U@d!?dQ^EcVzs1fFYv}xOWb$?>#GZv z7$3P9x$0?4?)zKC{n~gBrhwWu#>baF9IQU4!j1k;X<`;Z*07+R=X>&53fT&t}W6%jUv!Tj|D~66yVrp&cx0o^e0HY?I7c(z@tg z&Y@X#N{6sxDeX^>1fs+%)2xua$Wf66SpJM4l)UWrVMpL3Q3li#yLHk|x?eEh5KEY+ zl6tj9R!uGk#vr(=P(Nv(KG$+|Y}*?)A*eFH1^e!qYg=Mx(#D34-UXcRL604JN9KA~ z-LDNFhbaE6&)I^H-bif5pflFU=at;gftVc35Ub*1K^Zebn5RDqkk;SI{xJIf)H-hri~1ZV>$>u z&g4|;R+C5Is55@sza2uu^0C)xF=#3BL%>4BiX{R9qWfw&32oX*com3r$hlCQ zk9U2)S7>GFo$*g5J(ng9ink*W>^NZyM<9TW_SJG+(`NSi*Ud<|uayJvNIf{FX^P`L zv_<)$?Mxh8l|RS!Bv?3H{n|4^Sq?lQFq<<&ODvFC4z@kpg~=-jdmpu+^@P@60rDDmnR$+1 z1#0wyP(cT%L`gaep zbH|01G(2u*9YsquF;rkhyNt_mct;2M0n_HR;38`E&Oc||p2`1-IF}G1%z|z?(j50U zt+C?Je~#aeVi5V;9nmXjBu1gjR(Er zdR2{ld(6f187d~{-3bf2gN$?Dbb?ca~AJMYxK8hX>f=G?t3(p<+o)DPq*uhe`%@T{0$+xhsPV}qFln&bql_E=%}Pk z=%{2M_2rL73MZo$QZk><4r=SF5GNY{ONT-90MlSC3AJ#Bn7|XA%GM55{rgy=tSZsj zKhPcl*RRKwNED3UXfmq6O__c(>k3+Qgu(V%#-@%?y|DZr4co{kPsZShDsA$;b$_dU zgu5m&hbGK{tNbc&HKu8agEVi{#B91(?`_x{O@y};X4}%X84?cTW}?S$IL9e44u&|_ zeH=d<9Adgk#wm;jWP5OeRHfWjXCGnS{r4f>@BNe~C7~bSb_IA)nAdqS z2&S55|9}O1JS&cONsxt)f}*j5W{^p`n*p<(gM5;gbY)y z)qsflj7DCWOyg9T|AOsp3Jo|02>epcZ0kQg_@#h1**Lvsh)hhI3v-k(_BV-vwazFT zHn0`tm`XQ3E`#b$2FSYPnGi-x@@|3?trUEm)AGIrZZ}dk^+Ui{hPIZejwv9aTf^Y+ zlo}H<&GIiM9u&|qC2cth!Uk!oHYMTEQ#J`S_N3LJxN-`w&YFpiLUQ|C5_gnCcJYOO zOBbLJx7{%gOqlIvlAo_(|L50h?p{zJ3HDUhjsJr#4bsUH&TxOJuVmyw)JpEHykoGC z=@59$vP`D0fx}QKnL;&Zs6t4s1GWbO|MU@S4s^@UhZ}iT-L{DHz#r}$`7HScEc!d4 z!#j-ne%9Lz;y2mFqtv!sRpbFYwX@tUrJYZgG-P=2U26Svo+ck$xk=8eo;F|-u1h~# z!(bje8T?nG^KIL}I(&m*q}ZzpJ>zhN0<15XHnW}(_P1p-PZbVfq0whWAHAe&q%has zy#1*OPA~0loFOu`#ooANT@LfESC}>W%Jxra#>{7C-|db;mq2TD{7!Qre;XL7&?q#P zq`5kue=2R}QG<0-+{1?yLjGFds90n}wcLVL}b)Yi7x8lq&T!g&O#iNvD+2%E6gYDZ|(*5uZv0Bpg z8&>6Be$x1(_2d)qbr{N;;+C}t1i&nb*u;(l=YSKo5>ixg9m``BhJsq3o+bIm&>d0T zqDjK9)AWm46q+4fzCXW@Mma(bstFhQ&pgAGPU+#kt&#ceW-s)4tOA7|gIaqmg}Js% zq6v$l{RtEiJ@?k?Y^3XQehLUbnz_KPLGXA$JcoC3DjPVRrJpndB0XJ2bkV)Q)aS{y zZVENx={nZMhv1>oE<2@d0BJ+JeIrcsV?olNAX4vEm`20&zufMdLAT<#B|VDOmq-hc z@nN>&xCj{wd5i#n&_l00-3N{h%>yxXzJ2#Xnk8sJQPS^;D-PFKm)tpo`hkuvl3XNP ztTRPs3dalhzj*#!YdWG#CBUuMw&Yo$Q$nf4?dMpMdyd`DIf2o$$@5Up`j7iy*A>Zh zV%0Oj=oNcs1?3FnUQXPYV5VH=Zz5Kw<#;QW4E};z1R9Sv1MF0w{kk3cg!GnNbw7;2 z!f}0ev?LtLGLrPzE$GkC{zCu_=hH>anCDDwndJXx6`??}LAiP1tDnu72(6IIB%f{3@6yCWNh7 zdsKJow-&V97h4?R_>FE?UD%1Q#vI%wSYd!!c`o?-9b!G#kFQn6u;V+lWQ+ZDc9(V?`y$A8ET6 z8OqHHgUjNePKs;z0P%?q4_mcAqGks8{0P|Lna4oEz2>%0Yykd`pn(fx#OH3}J~D9% zv7I&^p1QXf8VH^tdAw+TuUl=Nt_p+JO)=}2mmB`i=SbJ@3TGH=x_ zxTIR4Us=acA~e&HOrov%Z-xaMY!0NTx-ZlTJ=_G1{x4~0u0^vM z!`*4KN!L`X?)=ca-z{jsj9Vx>SzcLtFzYcc7ebd`pN+hNI+uJJ?E1-mejblXurr1q z4p4SiFG#Au&KjprkOQn(GTwsqPL7~3hEdom?7!N(DF)J85R_i$f(IrcM3W9)e>0(s z%hs@dPsdc3X7;(pn$aEiL5*>)G^klW(q$ldss^_uUt|g*LX}xA4xe9qO_B z`9V5{zUW#*UI26p&g;;3RH0y>>elY#;f?(Ea}{5E2+KYgpzq|U$30?xZUjuDfP{aLKkbql~`T@5sJu~SiVSvadYf)!a zq5sjgDjxrT0$J}##0b(J&>M+S;O#IdsG5PhFwF7~Y9*R!g zSPqmkFuN3MXNuL5NS(1NOL*^q$Bzw}zlRt2;PZEY^@4+?nmYOfF5*0KV**7Thu<+o zuG3#e64l$~C6T5dZP*8(9q$b?df>;xOM&rRDs)B^2A$&78`qeGLnMxCCdSXd9Ilc6 zBR{Udsk`1NS}uztCumz(Ptq%A_8jm4w^%2|Iec&uhWaBcAUUCEH)e%!D5oXHygICE zkkS`ybS7R~Rt~{%?5btNBum~NOp_lmc+%JmrIyEMQA4p<<DLzJK!Kun2fwiT;77uH*Voo#;rM|DLo_@dcE z7cJNBc8$|Q>D9kHmEzHn|H}xzg)j~?O3E?ze+*M)jR-L7N_05C8lv zx?|0Rw{filiuaxG>0PBX!Lx4(;5OzIqL&55QM>?@J8Wg=D&G`)&5yOwiYf+&JN$n+ zhK_>WF_WH^$_YW6b}Fa-_7pt+W?YIZh&YZNB1+wTUA+P}DckwteTUSZbmIz?5^5#c z@67tIidWe8z+ay&&RSP$Qt{m~SZ@tYnQPi38`QJXU>Km~!VQME<_JM{O?pC&8&6mQ z_=tw1RF6vKZTZ|wkA0S@p>K1%dr}`PG@88vY#>9TrR^W+8I<58?2gJ)0N-ZIPA$4+ zR^5fdEr950=Ic=UJCny-1!pIvGr~B_W3Cg|MJ>aOp^YQ{;iKZ$xW;UY)5y5j^R8y) zBL5>Gl|25I0#~#@`3>5Gz6yYAmxLqr<(&27<-W~=mi*hb2^0n!d|Nt+%zr*#xCJT` zw6B>TWN?<|Y!IWX3XDCWpE$e1d%nenQ$CQNoW7VXKT#iEw2`6qztL#v8?vHWo34t1 zD7460<2_XdBynjep5L;x$4JNw30~uQI=es)bB6d4D`qy47PXz<9}9vqnErF=rEfBv zX}7+BmGM3B34Eq&T7gmj5sT`C5x$*QJqjK^YMC7y@X(RK{sv>j?#gV=3hkj6@aF7Q zWD?5~;NpuW`1)Ihk|9i|o{UOed-3)sQ{c0&XrStpGA+I_gLV7XHxIqeL&yxszon&` z%+e{BkKhh#qxl>#NoGd5lTbn`JDe_F9b%+ZpO+>MI|;#U*KfD1ZI#mGA#JPK#F8sumk-qMb^-nNx?>fqvKi`Xzz<7bb_qV7=Cse=oLH&`^pXk zG19KrfHExE;0nNUG7)_dICsy%_mjeP4?VaGd^HmFWsRC(N*R=G32ZaFQ(@MoupU;l0x@%HbHJ1O#8L1k!i*qks zW9mK94tLjbwu4_5v@brdkb1JcI=B_%^sp)|3FtX3Ix9gymT3PuQ0a5D0OX@~pv&rsDmj zpn6$eD$c!VjH(aqTZD2ZTg3jzHQjG=TW| zCWx4|=7$g$h!OB@NMnd|tI$p`r=Qc$Vh&k#l`dhY))Cd2pjqn*IpvthI?jop^k+s){rQREhye0PRAS>YJE~(-pID6QL1k_sA;|+ zA>zcc9uy$dwDAN10R*PYk=d!lsxj}yM4w3noLFER6Eurs9u=xChZ{`2;^C+5(u5=z z^i-*dG4*jjYy0B$THcqS5dP_FvCQo?_YM^M5SOAs4RKB}IYLm5EelH<(vk?jeMWM? zon0UxD`fM12h`-zbt)`a_(Qt-y&M+r^!T=A1GxK z9@Qwe?K9XqJuy7EDA+pl!#QZ|I`$8^QLijikF_Hi$!kF zg%Y($qK)#CuoV&qK@+eiuo(EDYc`=kT$dpGJ*=({)k zSR+U&x(lZ3@n<^eP2rmc_=jmJhx!{({BIY!8^?P?DT1Jl{?-?@xA@yv9G*HFb?Gt7 zF*er}okENx=pSfGnM*C@ypdgyFo}j?q7?+eI_H0EdMt)Z;J617uoqo%T9F_X)Sy}b z(1}g>$8%EZ=JaL)fT%o1@3*xeRAFy8*2n z#YOFAv>J@&6=tQ57Iw=D7VmoCRdd_#0@Y+;SRSYs{aXnx=RLFfwK~CxyXGY_r_K~C zVH0%^dIYQ^}0LVh}F4HP=I(5?87olZB~d5SXg~&l{uUZD`84kr1+ z@eMtK_J3k}(K|Pyhgm7vRT;5~05XIk$44wbmw1n@HTAdv8!Ek;_-q}{8_>!DP%%i0btZ>}fAVo;5DZ9CVwRWZQv5;$12iM2)_krz7cn>qS(j_K?^WWGZ5N4G}B;RUQVjv6os4u zqdtg|rwJsC=Bf!%%m5=38>J?8F>dW4|=#-<+AlX{_WVuV$fCP4+@j`JP zsDQ8!uD^56bfIT~J&t=%#xLU;<&tCOjB7mPGHv{}V%Er*n13PM^ zqj^x!0b2D0*w`v2jLr}F3+ejQVT2!gmddVMzQbWM;&^Zg zp}@~C%=cesYT;5c-!0ji0ZA{<$r<*BK!j^y`y{es$1&)*d0AuWQ0( z!`FG*7s3vTd&r|33pcH*HI_=uk=v3)piyzy0GLQl0lMa?jwX8 zW3)559Pcw_vWZ+*MOBY0+^2O45~mTr*mtaF3wOJ;tVoXOW~6{>LSUBB{MvY=Kp%6I zDnW|g-~$sboZ?&@D46p@je0fUe6h!y1EV`Mg>gVFicJ{_>1NBiBTdsS72x_sF(S9b zck~z3GbAXhi=| zK@gE{kVXMf-+h1Y|GRdb>zpU<`+n|oJ{1f3P5B3r`Ayy5>LW#ge`2J96g#5FyUqQf ze>;A^AUqwXz58m@**RfyAMg!olhsA>(i~>sL1sCNFIyqj_9i3{#d+|p=JKZB!~HMI z$DWzC=&FZ2eIJ;@bZ?3q>Kt!+MtQ(nPw`XMJX_^nhqa`Fe&CQDh&fW8ty4kImFves zk*<1yizw^0ZwloeC>VJ^vxUESw~R;x}*JxeL%kFCC|5~?Whc2 zlW_GnIC5t@pQ_#MVQ_6^q75S+q3zUZ$mE^}>frzAO39+AiQ&IPK@=%&Yz7|#?CEW+ zz*@`0d?W5b^UvF%USz%FKR~JWP$o|j-3XhRs)in^@`+Kf;~#oFouhs{I6C6*amp!m zq*NG-9=#GYE zT5Ive$9~4jv#j%^hg_Q8c&)|QAI~uOgQTE1>T@reu@;xR>!LU-90OMu?}GiI5u^K? z2sz4I?Bvmoz*Vw&R!+(axKLxlR^=qZ2f!Mov@eVAm5tn3`_4`t+Yq`ml;uL9$I2d| zFO9A+fMv>D*6u`|^+8YXo=Q@Ls9Z&f)OOB5}F*n7c>D!(_&tFRrW+ zNIS|qaJY)Xlf}na^9&ZVW9_FN z%}f`9%$P@Pz6N&9Ju9ocNm4$o2|`-ttqLvj2>G#0F7Vn{P<#s)!zh(kenJa-gwK$b z)C_okDKd8T&(sgIqi+g-?iT}5u5i=Ad@3sAbpa)NGO-APfqg8REPbk^N997*w%s^{ z>#_KdP^uBIeEFF_yl+?Ry>x9hoBLiA*jN~JA0@%Z1Es>VgyLFb4hgJ~951tsgo^1P z9MvwW7W{8bSYxU6L)Hpa#(IydcnT;W8yHd5yE9ObVbnuOmH}-{4=CAf3VUOGN7>t; z8gDgNigd~X7YZb0$^n3mcm~vOCaeGr*HA(z(i}md`VOw*V(-gJg=NqlJmnr*FqQ_D zIWO6gHu+!`)AM7vdh12Z%}&$zuteIIwz%3$V$lQ7yl9L*<>38!(mf;J3t|y=d5uQo zHe2;3X>Csn3q#Exy8b?zZO@l>4m9*wB;Sz9HxGP??-;sOsnx0)J{o)r1tnF)b{mAl zLyfqVSAcM)yJN2NnU@NQnSYw=iunh@&^>%Bl?g5*M<}5ca^i!Ept!Mykz-qL#?Fd` zo#hghFQ`z?62Eb#im~{ZJ1jz&Me)a&oIs0`+~ZT-eXU%3EFa})V9$0LMQ~*KoyNS# zSFUSl`pmxGV6j^l#Z>Z!g5y#$nt80Ank%qwuD zj%fTd(UpR%#00GJm<4WG(;>uBiI$n+U=E%b+7^<3XX+SJhOIA_cYeu3lbqx?@Yy@Rp}Jr_EF(|S>zbn7sCK^i${UNWK2YxcWWcUU!5-+S zN&ozIX5QBEt7Ib%YOj%LJ%77Snik6ltJB>U71OdNd>!h`{LsE0vAGJ`NO&3P zxwn<=jwrrP%$>O*Nq$0;Go<^aktMV$uw<4~n=FU0q{r$u@iz9rI)u_Mqpyqo+4+J8 zrSf>}%r26%+^s3N5Bfv#3slPWG4` z^=43n3CJVGF66jwkzIuRi!)TpiM6j34C0kHkY?U|HLEjD!T)pS&adZ)LbV$A_;YoW zt0uuuCHHRoYMVtQ4YUM9cyRM>_t22@ATd71dt%>&;h*;%w+s+vYvAA)httII#1u_m zl&?Hh8+Xw$t-UR12n;Z?S$}1wFWdb-r_~x;Q=d?RyG=K+l&cE4xHu9-2;B62np{+L zc;2tPFW2_U<>wlHX&DCs(Hy><_1^w|AT4y1Sx(e0w$v>Xy=+8~9UvX^pxSGYMhojYRmPP6To%LR4m4c$!S3LbR^$q*yNT zzjfk{~S(IIb28u<{>zsuW|29aay>d(@Gg(Vjo(>C-9U$Uw!T`=l5=FQKVPT+7hJMavb} zc$m}5SK8$}VPQjHu#C$-qm8Q-(v?E=&hok)`s)B#6ws2HA#jfR);@Yi^?|L1!=PB9Q)Y0PLxY)*+qJMcS zjhtl04EXo-36ulBkg#R4%@HxW!ej&(xAn(VzcFn~2(iO+^%x)g{*$6s(}VTxY+)v6 zdn1o)-L8;#|yE-ye36-2DQ!fLh9; zh?8>N7#riYTXeSrqT;$m=b_5KTGXaov(NW$aMT`llBW|Z!x`cdC7JccNqAd<=Euu zGq@tKsBn)%mr|l)Q&APP9wTj#pcRZ$avxns54T`pA~(_M#s_1y7<^D=z%(+B0+{GK ziFB3Jl_u4wQVt*t=j~X5>d-#V8tI#YyV|ykG!K z?@ICuHe8E~v(HQHl7p3}Ts&DsvKqtTo&*Ba6vU4u1%>FGLx{ipIcd=i6>qSqS(Ynf z;9g1B%tD4)TO;)toQZ13=t+uD3|$v$67cdkrq~7lBCmE~V0{SHlYIQtB!4)5BF`XV z^bt>A<`8JiO6*X-VTi_gU$h&>9(bIiY%KO;zuwbd?F8(#BPwV*OkjfXJ{tZS>0rwn z(f3E3HYy6EG2kbM8%4?1#4D^E^SCJl+(=+|deat!=sLpQJ$&3wEvR!U!gmPCEYf7a z#o1VEA!n#+Q=C{ zy2O(55)z}xP&z!_&uxI(0o}{MKeFY9^*GJVOk%#k=#WltI*TAG1qDweAbA0f{2Tc~ ztdlEb3C*V+*pXDS6^~pq4`}nd*&1Xrpfh9H~EGWR8f`oE{y^9jVx|enx zUIySwe66_dD2rvmVOGtYUqG=5-QrU2%hQ|dinwQBIUAedFxo&xsMt@%S<3F7a@1nw zO!bXAu>P*Mg?S-r9d|R0sNrdmJX0dk%V6lufwP?+w` z@n2Y5;x2 zBbk@ILOOhz`*o{9(H_>h2&Dz#TNZ_rP_fi^L&KM<(I@D16Fnn}x|_N@stUYlY}ft8x-y`tQ;l^mM8Dc7Sj1Nv z*CVR`fi_nC0WYlJcqOnx{QE@xnRAPZ?jgh@Dn^>onYt0~*Krg)rD*)`%Qn40@E11+ zV(qFiSso5X%WtwJB4SDbq;0C}>p1*tSfa$*L=1(W4JGGeL5aKNX?#8Qj`!4`t`|(= zK{Hvas%HI#oHET`q9}lIFmcri84}qm5za6CPrKg*C{Vh zMN$geZ3kXV2btTBL_MlR<^#;{#*z3wz|j!>lgkZI6hYs@k!mSh9+=-B=t;>y@-fAv zPA@Q^uyBMUD1-sxn2PS_ZIxel$9bw{F(h#lb4sPPlugfe%D=be@99Eq+1RN?t zE;}{4M8M7!iFpe_smq~zowp_dV>7cwb6C64FmsPW5EuRONNr7dKu2LSntgX$k4iPJ zoJUp2c#PPrE+dnxi4G9^D*uHb&e%_4h7Yv$9Kuk>z*-cjXX<`g#IXE>>vic%j-ki5 zaIP_(k(D;&-X>zImMi*8f?s??UNPyK7Bz2m&(WL;u)61d<2g(dYi@dX_jOInO_$50 z7Pixlr6Axb-2mVn{R-WQnq^Lgo^!!4_zYiKevYUf2u@s$Gpp2QFZswf&$HOa^T z#J;*lsjmn@X$qOEwHY{9>e`6E283TExFR4r?`&DrDTVhuFH^1k!bg{&gUEgvv;OiKTnSY6PtsuL|_V*_Y zp3NFiH7kQ;yQ2cm29oWDD%kYHzWP&JNs(k3za zULAzOwEY5tfWw@EE_#npsTS2DDz(AQ7lwuc_5f7Y?&#*nOXi5}wTBl21KMT#tyZzp z8rQzs0gj=hbPbH+cCMq@%*x}yj`;|{2#xQT?L(FL#<&Iq1UU9p@p-{icz3SUuXN2%H?K1aCA|#*dv=?O9j;6&;{~c0R>wmZY~IM_ z{_2h8LnKEAe(8%bWy*0ZPooG9m)Fx|Ebe&+d`m=dVRGHuXAP36fM;6eh(*A8B|uSt zMhu}-%==QA+--?{JjR}&uKs`pEw8uGA0a(qhH4}Dwo6soOS}Whqsbu}>NM0d?BW+c zr`Z695jvL=lde*>1>>g*$;%b?@8;(zm3qf@iX{s00YHX=@rNu$i~Rd4b0Mq_%zVk6 z?tXYDFaIVx|6tZB9?uQoh-q_2Mdjp>cT8M#xBB!J#kQ(I9Y$VxTfUV%cT`P2W_&kW zog+mas-Oh>cBzKSZB|;!^E7G<-i#Wrgd?r=Vzn(V|B4B32S79bX**Df>A|uN#S+K- z5A3AU1o#WD?=hakX5Mj}$K!5076D(kqrv~Y%SmmR`RbQMn?J4O8(5J&Qj@;!Vz=bB zDSRo8qAc-eXR>{_b@6h2i~@FlG63C^@A9r1^FtR5OvjZwps3?zw8_fjO6{?%kLP?g zG)EVnL7lvmwZtyiVl<#S|4%CO@aoRkV6FO&&8+BXbhZbk{R}Y(|6-NsP?dg?`sSEw`^#93E!9b}T+& zChv!+T45;-e|v{lu}EHaw8@?_9otB(dq&l+yf^%rqlIjJh)dStNYHw-o|H=STPnbX z!dP7+C9QE<4i1I*0;WX%FqoU?e8%k)5{s>BTOc$3t{t^hNeJlz z6>=&Y-SJb&zIikykg3*Q3VSNdRrMLtk>;!4sN9h^L=tR`v_=8EaVO_J2UvE~T~~EJ zYV`SWHF&tS4C_2ES{m6^gvqNsdgUJU&Z#!%WYPxFJr@DVB)y14a@6B9!mZSy;9t0O zj*#@UBdlQ<&D{3qrSXb4$iiKzv(q9h9qT-4J})nQnQZ!l-IL-aVFV=R?#*Fsh!S+R z{k6E^4rX~>p;H%QJ2my}>~k1bCZ7_tF*51Jj`PL{XkO>4`~Kpw>G-=2H;*k~DiK1~ zSmj9C+uH@TYv9uJrEGVBpNx6|rUsHzCESlRp0Hi00_)LL!TyMAoI6N%b$LOtPRB|2lKuxYfSKuD6M-ooQT1dz8SZE+|bCeV*a%7 zQG1KyzDQPKHHe;#cbB8cJ6ilj+q>Q0-!C4BiH^%@cmonoOdCGZSZz+;qS|p>&6dV# z$*FTn4aYo+4yYRzI5JC(bi^%)$eI_GZf6>xf-|7utM^b}Nq2ATejAOnUGG}{@Y>B2 z$5%wPJ*N@LDmUDYb7^!?cX6SUHGbkJ+V~5W0fv_to z71jB4u>qY;7AG&*|{CxS%kS^B&$#OUW<_>&h=LU}xew1ns{V zkhZVk*hcIZr06qog<%V|~qR1j>o`urE9P=8E< z`%k~?Hf3d7u!5rcCv@l2#zUr{Q^U1Gkb{HY`a8dS@$~pG{iGAbvp%8kRs6h4!LfRW zH~RCN3j(ec@RK}IGYFM7Erz2@ro|cpxQgP?ZD@vy|R@No!lBsges<&}}=-PU6Ehhx0(0>RChxiY@ERC*{_G#IW;5(QzSJNNGoXH8|b^pUR^l^-)Ro}AjdYZ}<5*sSw-<-bE{H%vg1SdYK z-~w|dmX@=er4{ge#sA21&#v%}4OJ_jFl1WZo?)fdQ~F}InMGkHbT8xdN;{{WN(eS$ zdJKKQ72b&|FtK%e&f7r@6Se9OaV{$^#NrAHLSaK>0%at%Ft)X*!V0M2xFh$K@Q3{3L)uz})EO~*MP{b%;1{YOfx zD#VYYkcD#h5W{XEp8guonzu%ZT)D_?l#l%>JyWL!BZtX&(}%S$=MQ;5BqEBw+&J@v zF>W{^CHcF{kpcKOmyN2E5-TSj6#i1WLnHW|y<#aqb5Qh(MYfmR7y4_*`El!0V)cEq zMHEk3WtvjDLqyyU~mK=b^u@ z-LYk3pkH2-Yo6IRr82E^8vY%sUj@)55E}5wN+H}gGX_5YE+C~!fT+u&?j)T-cheD2 z+I)qhN_&+WL^q;EYrFL8R*~aW8t=?V=2VX7?{3^zd=xb~6iIVl1_)A!{>eE`v9)F_ zWRMp9UO9xAIn3Hk&hID#7j-T=aPnuJp;!qm*WJ-vRx@Dqb4eAa9p^0tWuS&ug;g}> z(@T8AOt>YJPNyn-fXqm^PomZa{Y$`X|Ia`TB1+$PVk0A8qY{w2ImUFF#n_=I+ml*KkiD(OnoN$bW^y%q zMN@x!MRHe!%49jpF&NMeKb19NroYp@jAH@;pAw;BU<*5$so-ScY*!K{u}J}2k`Tx1 zA*(#hnrw`BeeGOn>oO!X6 z%j2WmBX%y;YB|pKaV)53NA-Yi&KcI9xrauW!^(ZIrC4~7 z!I^%)-&(2;X;tDA2%DK~Mc=YvbvSZF*dORd)9~ZhwkTLchtaB|BlO&?K{FsKeudmP zjwUE~l}IfRz~Afz_SI7}VvBDqwaF@JM%L}QV6YFW@taH$Qs~=azG}Y^p3p@}$VNNa zcJ(nObCb$^#wnu3^cRVBC!oXV09PfW4@q%)eyur(DQ~leZuak=zjM zfE|O|k}aeMX2`pyQ>m?RUBi9u*P^JceDby55VH!)*ND1tHVYMzZ22Q^QP}#|arw)P zK4b{+UuT}20jz5xw9)u8iCix=j&B%l{uW-w#A0ODXqXvPf*&KdF)lg3`)sny!s@s1 zQ{>9)4^Q7zEVL?!8pB113IBwDkJW0y9Tl8Mv8lb38h@&N-Z>+DJ;C$BO=+8XIx2`O z&j29L-U#rkB?dW;FyqethKB-bIh1? z3FNiXt!B{9wT^l%b8Iv!B+ZRTz-hd#b)p|5>gZ)z4sC1bWw+Z52=urx;tF`S@tGY$ zn#3CoK@h<>HY*I-!A*MATD23MjLeRamj-32$-m*b8 ztZD)>0Qm`S4rMuo?3_Wq47@o|oCKeBK@LLwH6_6ze1X_fEcl^}#??5Oq z_6xkMQk}VQ_;7azaI-f8N(VeaOcGnnKLg6Iuvly?;b7YN#iaES$JZufvEyvmYT3;` z=q#0s;!v3Z{2_oiux5SjTOD~q#nxPK0E4DucBCNb=!j&?7kWfjQYTbo4@FF6!XG4^ zer(-*rRT+iNH5-bEWOGsMlqNgdMR=n_^h}V zeF`iT(j)svp6^ka_RFvatg`-%ks>F5_irpgiXY8ej&BL2mOxO6UTx#CGlZT;E1-Yi zDT}TR!LG{0EdY17s=?V%=anB!saact9l!N*jzkaZQ=b%TY zWB=(6A99Fm&_tc~e9+Jb9OdlqbxBIo3}YA4nkb#l+{m6me$J|*hO<2xAG-X5GZi<5 z8-A_b{ujr%JCdQ%dXja|Gq4g-wtFSa zoD|R@bYKn_CpUhIiC3l6_0L7I)guaeiUA?p?Gz6O0aaMZf1TRcC}6Aq-!W!=Ts+ zJ0FyRq6hXz8Ozwr7V^(lUL&O)(#ml-0(?^73mR1(10)vTEpax~*uGA!wuU&T+*gmu z^3^gb*=_E)RHl{(B!9%j>qfmuN41!+Iak3gIjFwO{FJ!+wgc@)C-+tr3vlpL=xP0p z!HQ-S3Yqy*{KBRK!LdfCp+_(KBxnhivV!2eTG zitRK5GLT)-p@ilcrKb`T`j&5g0%#jmOlDV1d8@BH-x{Tem(!4AwJv}j*NC;Dv|1FkDC1) zHGHD?5i}fpyEsa1!VK_60Pmd7F?NotT z)OQaeeqk&OG}aTL?!DVqi5pOA64)gqD7QJItfqi%4Ld&3e zn!wVvf6Qj~#dIxbkRyuy_Vo>=F~k5U+n3rv@x?YpUFmHX(^|T9QR0p-CiR;P2UDiz z3FvAQj3g=IA(rIjp2{PB!zsHuYGBDUCsu(Ps#-m4*`!DmcMtdQ=J1opf)9} z!Q;*g!A(}3+ZYf5oG!5n^A1@bmUzRcZ6^_RDMpIP!EH6hL}&=z_*gu@ zOavK>|MKC5V`0=P(*s#Pb-I|-*r9%AT}dcn?dE4%%FMjHyM00odw`ZV$EEIc*4m;h`33rE~@7M z>R2SaOcJ$R3_)*=d-u+e2?<((^CnLDZ!&Aj8P_6on9z!Axf>&Ew!Gt9`BJA<*?s%b zF?{B1LV16uEOS62!JNi%zu;pjiDfSgoQaF=Q5a1*;_Stbv&6cEXvSP4(%RykHII=0@A zNW2fp&6c$~%U+;}B_@Uivl|OkfZuf*COW!|V`?5NS%^nf@s#iU(1kZ7y4Ot^O=D87 zpc!3CL{HyyN3@Ifl!tK-s+s0fZ%&0nJSiysCs>T^r`s+q(N;D>KM<*OVAci6m<@V% zw@Dag$zrE9`qfWHVB&kMg6o6{ygfHspQu^uX-ZLeQiXC9W~8OET0H9We|l2%Tr*j!>%_k8Fd7j1ech*(Acf0KSN z^y17cvFlV&`+xFY8xSZwA3`KflknwjoaJ_SBh$Ir8oYDDV0A2e!Crj%WaNV7O6{}p zeCV5i-(FdKV6!speY3>-M;DwE3MqiZ#>(Bs9`#}YIg=p-6OLNQF6If zeiYJ0P?PC*Qj!zaD?&kRIN;_Nj+CAdPabz-sGYc0ElaBRo7Arrb>BF&VZwCSHmS_7@Ux>45Mn2#I%6(XfF{y)v)q zMK$|03Yu3%-3l%p=&zBGt*%ovSWlE<{Gu}J0zZ&(aH`jxTm9Ghm@OBz_L6%BH^P4o zQ;liPVx8cWma6_l{kE^0^H>c`ny8fwb%whooy+_S*d_Zx;dBa4WPFJ+8K25Em| zqpWJK)LjZ4hhW}Cj+6=dPHTz8Z|vHr8U&WgMVTf%vy&>vD7)uWqJt&F8q;E)j_fMo z?R2rVF5mf2tn4eba-&jZB5?XXN$?jM7};`r6;#_&h?lfdZ`nP8Dk9&MX+_s*xj3ZN ziMtZ$NPR@_b$a_Fu7lc*c#H-m>OoQ7-K!#YZWOr@$(hH&QGo8+9Xq*8=~WFC)YD|5 zVsY9C-OEgX;Jkc~rhWxC@uZ+$GZVgJk^y`q9?`+qtQf&_YQUi1 z#TE6VvL*au=sKv7TbQ2RlfE2C1wrtn13NRTnEi+HW+42ST+ zC}~2w<{S7GqJT$;fd1tZ%YHEINvqkGZ~HYNH8y;Aj8xe@9jCWdgJMVGXqil|R|C2( z&P|{^V+2OKROH;QCg*U6AG_kJUIlbb&G4`0mEJU$hhW7z-er|Ds*cmdYwX982mNIg zi`Kh@cH9~jt5L`B8v;+~?gtKD-0>~3%fnuCR65*SR$$-WhK4OK4zskwz!Xz|%JT@; z`wBz5m_(J5N>4zY(K8gxqT2y5G_WPb;>YIS?8zTv3vs!;(95^r7mvr^xW0tc^`%`US&<}9Y&I}=1P1W zjMAbgiMb*o8!hEhRB8z${C+t_Abd(qjpUPOr%yB>y72q=OGS5|7uf}2ZzAxCLmW!6 z^5+`lfyRDBTw1a=pR@SqEz~|6_F{n%HVC9Yl^)9|sO3GCKyogFv@tf^yEMNG+ditF z+g~?{^-aC4FH>bn-L2~g=H^0!>{ZK89`=>G$@k^&16T%hrv(A1Mp!Grup0iVm8_ZqTlzYs^D zOj=S)hv}%9xwv6tt+r)Gn7nypjoX*)@VQ0<7p*)lr_>sMMBGY?0Z8b|wpqxrX+vuo z6VQo<8qCz{zGVu=!Vbw()iz zwTCFv$7)?GTg)Fd7|ZA%wy|2Vnj=@7e3bISo0Hat1F|#2>jyvaKZB00UG(T-pokj$ zSvDpIibv{a=HF}R_fK9J3?95irGL&!*()kl&b7Zgq}S18yGk(HnePjY@?7LpkcTkz z{4UCYm=4YIg*ZHE-NgCmhC0;v^_d$WKS+>)G$76+8^rVc;>+bD3an2lAJ5Ppppsvb zXP%AY>>%&)S;p~}s{t37hV@FD3NFbc?LVz1hCu8{N(ZDzA+w(Ofsb>SupKY5Mo`KN z<$eNcvrV!w9d{F7A{kR(-TV7%hir~rdNNVlZZQRVqbdy<9R4+ z3p8tFQruy;6Yii_ev!cvA>liIu(Ed??HpDaffj;O-u^>K(LyBlGS zm8`W1bqFNN824KQy9D^ZjeHN~r=&D}Nw=RQwNCK^n1Y16+3#%Frms_Y`b8oe&H?(Z zPp0cjDCge))@L11CTUlWXYQAV^S(WtdFIFKjJz)IYYK_NjRlp=mkKlz>wO2R5BC}z zEbKk@K$$lc|61ga?KJO#@OvUTG`EinN|0uSCnEHEob@!;QF?F4^SM$hUyj5Lleczo z3xJO!;9PDLd0nPmG1kp3P)dF!<0YSjJ7vuwzKJ7gAFI zi}L1pHh|xm)&rmrsB1Gpn!F!y5t)ku>zC+8Ohi+*ZuKedee=})j!5ef(0Nf8)^HP( zs7|@JUp|WpX7U%X8x_CO+`EmhH1*{E6?k{>t4nDwn*&feyGz_mM;wLLTI%ELB_e)u zl3QyV`IMHUpYt?(9$_+%iG`@YqvWr!luB&y!3XXuhrz%m*&qD58k|`0tjGB^8myJe zWCiL&@KY=L)pNSA9G`vBTB;7*g&KsD}58YC-4!ZDhNCwzdH>ZVKKZtwL9_ z-m1SZC8Hen3u29-1S^S?K1?wqh4Cik*`!_EDFjLi+AX$El`sUAGTt@Mu0(V2W%O8A zCKNf)wNQWgWn5W{#o<(rFW;Cuo0sO0O(|3&(Fek!qTyJI<~YxqL*+Ldd%ql&;2+g! zu#MlKD=qU4@6L6p8EfZCq+`}{gIN6=EO#cuQgUKJu(c`9YK*XkIsHn0u)3t}Ioy;LBDW2SDg{{pO>bVL_Km(o1)Zo}NYzFQSS z0g(NW8aqjb`RQ*$MK7F{S3exZ@+icH|1;0TPL9ETfAoo#&McN-pgQYZM{*P(UXSW< z8KlNG(`Jb>%04qzZlQDpQKzAj^d<3{y-5R7tI{DqCX zQskn%aeLXv*qD)Z3>Abk(;p#sOD%dL%_t>niknHjtN62|A()$?nM@;~P*pdpedJF; zl&+n;x;ARe6n0%-S7;w3`3$41$s#IjVGGw3W$$5-M%_?A28v-B?@)wtiSp=^QkHo$ z=5^s(M)kuB(yPa_Q~PgcYgXRgdBdQ6R4~MEG=B6PoWf70=}-LLl{8ta@!I1v{Om%S z+LwFMQIq#k9kZ2I0@fey0&t=JKV9Q;QL(5*IhH}}tcWE~#4nOj!npV=zY67mKf9E& zVD0N8I5|Rw!ocnFb>MfioEFU4ilfRmLMzroKj1rBff5oAg);~Q7hRoFd`QamXjlz> zADZ?$p&5pl5)mu+sbEyo{;g3->mg1DzP4hqVVvUI$;K(-O);B=} z3SVkvL&HuoC=TpKP62U<2tG|K=?aJEOW*!U1su_)xnxp|!u+$Fhj^VB`JE6w_Jvp= zM)<4~(S)>=J_??X?xo*!u_BH&+~*!uz&gf;3Hz!BM&5c}R6oM6t6Q{t@$DNv`46}V zIZq@Vce|+Rqz!0ZN>E*(aYay=<-PL#@%R<4eetrh?bqrEyN-6?r0&B3IyxY8ZbrFCz49^`Ucfyg2qMEMCD}ot<2PbsOvsCwpp=HK?RlA&T!+5--oqlqg z_8mpy2P^8Ki=7h!l=tyLS1a-(y!_@`%gv52?>C+g%G2Dh85*rmR`1#Kxxq z*Sp3gZAH6{j3@0X7Chjys(Seaxz6PEYrG=b5yDrCx%(Q4pNK5OJCF=( zWIlPz%&MURzZrTVf>9DmOPLFwM7Fhy+@D{u?6pTH+7@oSF0wD;D8OTUF(&S)HRy!1 z?FYT_YD;mII$sjS1jhj2y|sC1(ToS`32=PFK`L5{K+i>X;8k6D0&>cxc<7nU?E4Au z?M~v$qM1f|vTsJ)L{3E?3r|2gig{(6C>KzBnPj6iK6sTLRCIWZBYcBlEGkZ#bgl8#w^4Zx zL_)JX9p}TjU2#mp+yY0kVXTO4@e)0AZODZ0WofCGZ@{SDU-~iC5m1X!^a(b>UwV5K zgS0Bf09ia;?!Wb6D zSt388!{Zd(=W;f)H4{SnFt9j;fVr}oHqH!ng_*uL1dbmqb>hvL>*@~RGt|jb$m$eT zD!-}g)B)89x)fj#U2(;G#q2SKlHXxbQLr_p3!>)8UK<=lk#_cbWfhpJLvw%%Zs&sc zh~(Vdjqj9GqMFK=>(UllF3K+G(YYG`E|buB_eVuBm18h_^M&!y6_wwi``wIhSiEnh zTPVU}fiqI#RfuB?;p0^}pbP0xR@mwmKV`FUShbFObJ;iKF>^eGBRRfS|2<)@PK+)m z7s52Q!nU&%b~A=IJ)-H(ZdaW7m<7R#KzDx#AG9H~Syw};}P_RxlSapJJRO%k0{x6j-~Gmq<6Z0<{hi1vrr=z=$lik-gt=#1=P-IX(?hjFGMF=XNoGm|D5&|=li0tf@vt&TB9I5XfESKInQIQ zD(l03XWesljHC#hf7g2OKb;DH&+kdqDKP&1>UvuA{&%VHce7UE-#2ggU(|K}dznOh z{w_t9u1lNJ(;H9C-t3BBHR1z%t0!lPT=%AP3pmlS@3LZ1KL$vetBbho$$0&-fv zkU>>~UFWm&G5%iG#ku=@KQv7#gc|(hx4nNS@0gY!cZut+=5ztsztyq_Cp`rJVRGxeLB6Y!%fj|RG6 zqdq1H{k{A(Bdr_K%CDuKaQ|HzJXjy{bE~ihK}8@o820s7QF#~c&8O64`;gMw8g8bz z{I@=|S_RZc*X07>c+d{NGx)9$O0Q3Lxf`br;p}g?S_UE*hFBad%oyG6D*8k_)6kU*M(mi zHn)uU@~uS@9tnd1pc8~7T?Nq*)dbhvs&agl^|SrmV_$s0jQ2X6K?=l!x^t0tEn*|6 z2FOBNnB)57y7$>w?>SCXXU6WOWsU2N5_b>y)5FS7%U&s*s1MTvLI0lNXrICt)@iBrzUk0#sxaRxX2GPP zy{-NIUDU#SUA zmW0Fb>L$)W-aVr>e5%=$)66$hA`g%acAVMHZu!F#j5D*^d~Q--483MxdETNZ=Ab&w zUb`@vce2FRGeUk{#o2JJjz}7+TFna{B^vL95E+2vRd%Q(`|Zw%k$+oReQ1o#CmZDtVZyy zbI}1c-Wr|Jdrf-2(B8w-(^1Ximm@}%_(YdWt3eJ3%l>8v%BUEhTghRCBoA0J5+)Np z=zQ_+*If+_p8QmKO=e_yGTsG2CD{RVqkS3MHzXx;>+=sdfAeQ{P7h6&;(MS>lN7`eezBn56Hasa`9>-XLzrei%F@8R({mFOXq@o3r*Mw_Ry6HI-_ zTf&vN*Qd&Qz=d~ipHrS4rP1W9gs>npA{Mj^Ehy*jW)xl7lYs7#%XRzhO(Sdb`QXy8 zNpZ#^!n@%;h6o&@jgsW3tsA~cuL**z&mYPr3Jl)YQx1$DixET(t9j{$-W;_Iy~f*Z z&WWN920S;t_-@sr>j#XH>qHgOd|QjwE%r|c4XhOQ-dBYK%%!*?&nas&_(qBsrg<1!Whnot%8ULTnl!B8=wCkKjxQJFNA1EOEg9X@W=U`3_w@A za$<7)o69Q4W7?9;%0KB_QFUe1!~RG0%q$v(SEDssU)0EX%!Za-qhf*>kM4|0)dWlb zx>CV$LY2QBlv%bw{Ft%)R(Za$+xXwIGspZDnKzu~+^Sh7E$b?6oGVDV4@uNA%bF2p zAL=yEtAnR_@<8!^BPyqwf8A-x+Rlm75DXrguX4!y-;QW#(1(Y_@ET`tOjF?Z=cO>efT>< ztX#1SKq2=MXfrFpNwt#1Yp{k0DiYE2 zLU6La#4j{D-4lmd*>Ha&su5oRhr-!a5!u0zq?%$m)Z*Np%`h@(@QWJA+PKy^Q@LQ@ zojCkdosdA_6&)jB!lyx%^MnfTiEn^R6Gm&?pKsp=NlttkVWa5^$7rI(@@i+5) zV*mGUG`;f1wP$2YFxwy;Bb-cph#Y-VoU3 zh(>O?YiL@D6xFCx*vo4}sJsv6P{Od3^-=vE>%Go>Q@_IKe{*@ukroePrB@p{FZny4 zefX1Q78nwektj(*G&7)J-^>TP^XFvYp8KF+{BQNjPPDb{OIs71G-OHf>S&FzOpMTi zax?*=Om4zIxQelI2tS)MhC+mqvZ>6OuUN~ard=y&J8=gYIVVtvmsczN2AZ8t9wG&X ziY@xcaj{1s{r``ntBi`PS%L(2m&J8)x1hn@-QAr0)O?sfFk%+5YZ7@=RE3o4Wx#M!!SIMYYa#A!z zsT1Ic#9l4G5N*)kTTFF`vRwTqjG^x!5`D6fgpNySS;DwWXce_MRBGifank_@xCFX8P_pz7FyZ7qWzfrH$VTJ8P?#s`bgPs_4bzr zMl8E8*RyYMpcPaU5444Lj{ggw91GuqN3re?CL*|*4}I*^1NcR(`BOpfqEoozd#PFV z0AiU&^5XrYV-|@P#J6h;tB;i%oM&G?sDdY{{Fw4fESWYA@&F#60V-nR2KZ@Bua3A? zq)A{12X~gfM+s8-puD+Uh>3|bj)ievF2=f!fN&B_r>FyL2>m8UHv|qkBCO+j;_)3y z1;O8wMrgkalt(E5_`A3uk)avQPsw7BDRH?YGB$uHYLVj8kz*^cq%}ktZi79FkamVYe3K0o%c!TWATp$wW3)!)Usg~xd_R&PILc}S{J*6I(Ob-IXDw4W( zUA*VuD{U1(Q-GNLq)f3+JBM2pW?+0QfNkumkckr~&^)5Ds=pqkFED-UlZd_lx?w%E zzO(8Fg*jeAq$6^OynR}=OD#Ux^*qNL6wz&HlW+Yu%C&e^14Pm#c-$y%zYs<=QKeH2 z%7djDdLfB(4TAW_U;VMaN`O@o3~qd@GhL_Me19q#-s=dD`-MZ&3iYXg3?1C+7(GkQ zPmNvb1Say^>1R<_f@v9wsTkQz3yTz+fO#@0Cn{_Tf6Ok-pWX#yvxXVk*wu$(5f;Zf z>+5)qr8dGOT-R#Y+Vq)MY7ggBjAEVf;$Hvc?u-t|cqHU*eA*l}LEufad*^-`?ULy# zVa0v!Xu1DS0Z4^3h3EX-9FUpPs?G+ihPGF}lqxtE z{>hURvX|EkPUWl5gKeqlELB5wc%aQL96Nn*Ih%!e%3VUR|H1&|Xh=86lVY6AnwCYp z3^kfta;iEnylsoY<8r$HSmmbfeBM-pIoVKUJyUa$vdsG4s=cP4!`QNCHP z?ivd-(^O8EN?|B)(xhxi}l1uxkMs{e4}5tEfZ??nt{EkArvh#r3h0-Hh%9Ug4{-> z5bN_TQ$maL2k$k{(z$-kow6ZhG36noSur=~pI`)HMOE#z3uj!NLwxLFdB-$PbYJ4v zS_S+-aKN|XbECp{$xZrd8enJrBFai;&$yIE407p+9T0e`fgT|*vA{(^{teNwG-3=` zZSl1;ph8JAg#Qk|dPtJcOf=|l&Gi)`bV;ZcVU}*LuUAfzVM8p?#hi>{xs7mdBLdzF zZcz>0#$`Xp1uY>SZDTdKY*48|B3hEr{ow#O(Ko!iLQWIn0g7>}ggli@Q=WV=Oq??( zdhU8|C*#ZO^koeGI7sV9#}N=u33hsC?}FRG@di_cltv12eL#krV$4yJDUu190cv#g(LEOI7V_r1f!-aDXsh^ z#?=B~I3j^KB)oi3{J(686Dv_vK;l!Yw0>Xab|oWFnq(-%u5e3%|7>&NY8m~W8~=gt z5Iy%~P7LX1W@?(}%fuF90y_cetZ|ME@aF}FMQxmx2-r$0nF>M2qCwqhQ8O@3BV9=uV`tDi}~zRqM1YfS$_a(gPkY9bAA zhKdgJLp0ofSpaIHkBZJ>LRylp?~V>!8MB~nKI8??>Q6!fd+y;)OX)Vh^b#Nc>W@n( zWFD{1*4rtT1k&0eId8=jGWLmr5}2fRXHmZEb=P}~Bict~$TCTbEg$6{hMS|_TF_%n zpZy|o4$LnF{``A>JBa}!g~U>1HLjL+LSkGrMHcguQlA4(ib1tV8&&EIb5=}3?V03b zOB;sC&BhN0oY&{0zn^D|^hKdC(S%ES@Fh+UsE-`@>T>4*A|eRgKJ%l;q3op+gN+`?(&)se_*om9u_hx~xAKe5O?r>K}5@%;TAJ99pc4)(|=QpfR*Fp_>hCjF;g z+PajQfTWireRUb_%?u*g^Q|v~3ZSW3O#34}r%;Idqkw$%Ka=>+n*O|oPI+IcXL!~U1ghukwsKx=doZl2?UTC0Qdk!=P(bX_h*nzZLE}J|F~&Y3 zXV>R3Bw*H!jq{4ZYc3)yN!aWakI6H;loqnjFL3r$U=t}|?N1#FbQvYpD?v-?XUdL6 zf;Q5f1LmB~KdGo@_$(1m?u2*cwBCL=6MN|ueI|W*RX37Dv_DXI&75MH$rFS$z%kJ{ z=jrZ|CevjkI!m(O(Rkee7RW+`BNO>QKFa#z4J8rMw(=jJj#x1eURR=!+@)d&q-FU2 zs3bL~Wen7zSExM~Iav_JF0KM;H;!CcS#V;*q-z`lO<-PGAqD~>lOx=y=(m7$jmqT| znZvc`c#Uz@8X)R|D7u@Q5Uf5DIKJ{AX+EkObuzvZ^jwQ4rK&N_8Mj`kAWr+2Sr9Ta zS)(%jkiK{Y|0>{NqVMf9srHgp=>hyFK;zHF)O;Q73(0L#`g2hb(p6u9Rz7{ql;c!o zai?cM!}U*WoV-hQV#2uml`qhThG$<*i~6LK_OX< zBuhzC$uhHmN(FP;zqKx!NqsmR95AnlZD$~E{PWahAS<=-=UP8(sbkq-^VHcZ0o}^? z*i+rg-+BQ#)2`Ecl7Wf)r`meuz>Bcn0xyE$f6&16fHD-&Ght+@!a|8l_Yqis(@QqR|7cZS>o_c>#B6d_KvzYl(!C=*tjhz|9(+$aJ_q zy|Fu@e55Wg7rc^VUZ8IZ{Wp0y9w0auT0WxzP0Px{KA5{?ilpCfPB@VgBUt%gj75FK z4+-qUBgR;+bok#rz!wyOVf`KakPw+!{~zKYu=bU%LW!J0R`Z;~k%+S-V#V-qsSJ3D z+By+El@peRn%!8fJStm@x3ksco8 zboV(`^`QwT%UgvXgNVOGVV zWG68w$2Dij)qg^6imhai9TU8H(B});)T;T3J?wk$IszQT@@KVgX5AVohvXU;)jMW+JIyK5hf#-l=))s()?KqI#9pvFsp2|AJ#CcG#5j$v(is zCs?DC)TtCC2UD0ict300p6MI{6S?fs_f;-i2(6ajc)&CtbB6FmaG`3Z--wxZ9b;9Vt?=%l@lkZi-x5C*xs{TGJ zdpX#f9e9FTh|xH|<4;|tNr7JY@phw0A{lgoY4y4IabK(J$9~P9(lWVAw9OxnU(7<< zi4tWGY8s)*llvQnQVe1nh1Q|?cgfE_lZp`sRRX_B!U);QMbbYcWNgG>lc3!?RDA0g zd%CM6_>E;R~2n)l>V2c`oirW^aZr#q-`^!4NFm zzrNK5C(?WQE~$eqUVNKd{y0|Erol$jYg+O~3h)My>$i+dFNTPz%-W~*bPUVw-)#!J zG3Y@1ugVEf#hW~o(hMe^yag<$Zt6cALOiRuXzAo&w~EmYvXd$1Rn-MTF$RkwK?2yL zQwU_i@{S)+BjW*9O3D}-Ng=_$mQs-y@O|L+{R|2-IF8DbzCj#zGc&J~|9n-DQTkH1 znao1Gj*|7N2kd)zbt{NGYK@Rti}p{(!SfXLdk#N{ZNs`e^ft|xPKuLKHWDb-i&w>q zRK5nM88u6++OA#DkR*@k@Q0gYKff~4MORYNVX$lp1bntcj7K@@h-@IRUPuayD{NGs zSf7yKBoV7;<<{MlI#ScHmv_H0;)|1PW! ztF*Ct)ER(y&nCT*$2`Z9y3>WJy=nm2`u74D^D-X~axIZBH504)c?g!af8XWW8q~ah zOdYPD8F!C$Hf>i_%>h;GnsCP1dqBE6)bxE+b9nJ7vM1WgADPa8d}oSiIsgNynGe>q z2*`?ESHRGW0aZIzA*j*ykg(?4iI@hmLgz@E-Dq$>od4J-PXE+T%#`OR+WhAA(zZz8 zpW_U6vE)CA^gtSqcfN*LK9cl+wqKlu{ z?RJb~$!NqjYnCRTCdDV}?HsW)It6sasZnheO_8?pOzVY)MNObMs88i1N5&jD!+yS* z6JVtlgbOrNhUG^d&clw4OyxyxEeU~HKuWE!1y-LQ2NRM47gpf6_oZ#vD|X)kQZhOd z=CRipzIu)XMGH4$B*zXI|2C0iJ3oQ=1B%P#^h1a{ip%8{f}lYpbH`)Io=KQxYW+n# z1U#OCAb`vF&YU-_QmVDoP!?m9A`{5`MeaTU@ExCO>Hv05U|5dOv8Y5)B`uh=n1tg; z0l9d3t!pcjgezsq(}@`pk=HF-WmPy}Y^}s01o*eMi&*lBZyD;f10DUXW6_hvtcfz(ER#8UuS@#zpTeSg$v~ zh{^~#@S38#bK0h1HmIIrz|}G=8q7_7a_ajpUqXIZ)*jcFR6IS!BK$>`QTLI&g<|l) zR7bIz;L6i4qMH*_jc3-UBOR)Gsji9swQ7!YxYb*0AU&@=jg4y0WQMZ}pBr>XyA`C= zHb_XKF~#Uu`z2b*P^J+n4maLEXuAP7vk=w~v6y^_w%uk_RmmJ@6WoDkrsW7-n)nT` zj7;{(w1%;Wb+eDqk%|QnuB9_vj#Ccxb!fHqg~GvkhlUpvP#{ZS2ImNY-r>MM0Eweu z_^GV%LK6V?lZJrgK}ctSK-lmKi&+~k96i&PB6t0*LbkiTNIHFtgVQW%>?6#WwkXF< zf$u0&cmI=90Cs67I3Wm^M8w@+PLezgL5AD83OyM2S4(l7TvCWZM{ympdLc{9pq{%k zNlUD+jI9HFDdaeIT3)6nv=@iptcWHZO^;h}hv&cY4d1{R%3k5B z+!{3-2~_bY*S9Y)q$8@6SLe;uh9g_E80s!GEa4wML!?qCts7Z>3H#Yu$}+uS*Ha2J zYHC2+f1l{pAX7%r133>rj>S)A^ckoAx$_yYH$M+8C0c;hEW6eIy@I zAC>(Nq`UGU{ckB{p9a}sZ5#fjo;+*hVFfx2XC1A(qhN3%F;B|&jzB<77I105yAWUJ z2VkP(cp%^Hqw;d8#me~`_1+-XT1X7C=1AdwVzLuv`#BxDq2x99^ej=$1s&cx)U-$!6z+*lMMTmYo=7I8OFC%C z^JpQwS2y2A+Nxv3^rovY1lGkVbmjxf7tUiXbYAs-%)%H>~ z7&#lv1IvF`z)Nzt+xMunUIz`x$Q4tD3P9v*37y2CB4&tDlgz+JEw43-Pst{CEShZC zSFHONT-+r0Z~xDiXNB*8MkTWG~bG;$2jF0QFw4nAS7BoRiPd{8Y)mMOJ^elDO zg;RX%Ghv|9lV3|?(Q54U7|{e6XDI^s^Fr>w^Sci=`8A1{OkW54;n1}?yfFNWHD=JM zL;U5Tiw+~vX{}A?8}m*I1KjGo3!5zTz4 zpI#w&NPE{P0B~-}dH&P$=gdRO`*Z=eSIRJzd+c|>52F!0+qky?tW`PiNYu1D?ph)k zEfLM)I8tM$jYB!-Ac$jeogtv5NiMc!nhC=qL*!$``@Af8RsD&Ah;)4$jUbq%W?ICd#<5^ozmIPnutv#r`4VF6$U^ z*(B)~BV=79{5Z0zVrAs5H#&;imi~(d?Xi3CMzEzeLYv2jVJK-6+AhrGn|lcU3<=TK z;Tdhh;~(Nh*bjG{xx>UJ9)e!aXS8WJ{RioLz!jlXab2lpfJ$_5?%`+Wgq`_K>WAa! z`1S%X-@3xjky@KoLfCmSBKwMDVu+!CDalQ^KNB;Y2^hTcdZZJEc92p(?hh@f%`Xd0 zffVQw3Z~2Byz21oDdh@!3);X4e`*SAR8mMfuu=~4w++H3P{F0$s*M(rvgp}7raecj ziPB#nA$Hr#qLTy|4{DB37KQaZH!=_x$>1A#%ApSz#MFE5<(wp**bdILQT%gCIfQ^Q zQWP4Qe|-W8MVC@!$<*XeTxF{rYZ8Qk&qFnVJV{Zc?qLYk%ec(2C*Vj`Xj87@f=HXM zG5eO@o_97M>c@GUmeGGY+l(Zc-Se*~!-zy=XW{}6=q}7RrDYQdfL<(9wpy=~Qb=xs zu)Y(eYR;)V9GwnKZZ)UjZ7GKsMP$L0)4uG%gt?!De}-_t|1Avu|+a6PE?H7-imuvF3-7TZe-d<;O3pS8ku{HWMd3 zaI=?1^frg(euVyNFRqa{)>X1Wm*O)(NQXy2v388Ohh%c2Hn1krU+VOMXfy-8WFCrJ z-btU*+vPa8AJkxz*{sU~5C&-=r~;FYc&W`*@E3IE1%(hlo0yyhtK9nMpCYXr}uR$5I(9`0ejDl(A z>)s4Xu#)zrTqv(n)#>U%PFMS;$y3sPSFN_1zcLGtX_cTSD2% z)3#_r9zg zLY8u+#tl0R&GuayD%j9b85{buN&auluUYtU`KCs*KNwGOoPI0ch~|Qkjj8G&e|c4j zItoh8P$kswL$dSH4;kCKo08Udo;WC343V@BmfoC4qjhkRcGQlw!n|*1z34{!R~DD! zhKiF3a_K@9RXqTNDxO~Xuhw7)~k*36h;yr%u5 zOC2Yf`ZO9~pr#E3@_{cRM4(M^dMZA8>Oju7!?h>0H#I*T)?r)B(=iDP#Z9O|^GedAnbtM|HOxnOFhnO%UWjB4NNDQ(HgQ1nYn$d|VrnC9M_u z-`6_@i6n?e&HBQ|RF|NW`PreDD^(&J-;$;?>t^I6k0W7(oOU&%6xmD_ju7k1xYsPp z)UWounZSuT>V`wySM_9(YC|-6_ZLuHiU0Or)e~d{;px%Z^Br?!+YuHi@Dx)8F*pB7 zc~C`iI>>(dFFDe+N7!vL@qJ>!a5SH7DCFahz|eMrU)<$?gw@e0kwtl~(w-xtez8xZ z49OkgzPc{?ZN3a$4ng4BN|$6I?v^8=8V3M=@Ph;e|68xPMIfw_uobq)QRIA;0xKL# z8!UW2{OkMoKQRMRj$L7cjnd-mS8cH4;W}^eHp;ZrqY~G786M}-UE$~?u3HH7sOVKb zTN-R>RMxFt%MV-+9zjDC&3o3%BtocbG~t7F1{|j0u@vS$Rs_=*7&q}FWV5P3Y!L}F zFl56&)!;__{!@tPlIJc4UR%&n4#X8!QU`d+nAv*6!_eYJICCwehSJWbzL)^mNuhW9 ziPA(31Nq<;mA5f7$)T&7NRMrkQBkA@m&P;>eP|BylTg37r-dG8w9C-KTG7!y#KdIB z!=8yuT5cj_p-xv-xIhbjJtb8(8y%nkx4o39;m9);# zl5-bTCaw%=__ms~j+!=Da-Llm6Vzj%qgsu~deF9gaL9pV1%Zol&5VaKLu&s96gVy# ztRxeDQgb;O-YZ2u$d8|Ro-=QFomTu{-e-6-j5w?;dbXKhD0Mq(EB7CewAv&9GluTZ z(5D~k@C9pnz*Vd$Wf~kt%@PH3R`MZCNB=JM4{J|6(fYc^^d;`isCRFFXrO(sdiug}8u9I5aI1b}*czE69y8Ne}dOsRO-$ zJk+_ZC4y9|YqrFGf9z3sK*o$?;;KcwLAyQ}9%%^yNs1?8h1p&n1s((Js)xoF#mUb$ z1+XY?RCL&UwZI;gJs{v%ck8Q^{uhX~2sG{Vk;H;{l3{nP5Q(dUP^K{D`5obRt^S_H z(o&n3kz@x;b%e&c+UCh9F)$?)CdymuMyd+P!CVF;T_;o_fBLWZ;(7#}xrkfSB7AaG zsCB_iSI3(HbVex;HGyY{pl=`(+)*6RW*Skm%**+HQZ2{zjUIm7IjpLV&gaB7cAJhh z6Af|ZjZPKcXhYZOt?LQX4#si!Av57FLD+AzNkqdh<5Ie|^E(J}X$foCvtFdic3-ox zuIJ`ty{=B*`2nHLS0ou89y}NpLfCPGHvdSIuq6`bH5)FFtHAdHp62+^zKI64$y0=| zn=In*(1#Tro|gcASFB#ef3teGAGAz1t_Rx8+C>~|ii0ODY?fB~xqIK>%x32Sc7wJ} zjIV>VS(&Wkzcugn(_3k4<%kaUY*CfAE!cqE5ws>0%;RdVWIbBJMCs3aB$;8}rddrT z%pwYvWI=;dN#KE3=GZ)B!-rF(A1#2y5%Zh%M!*t9C~F6qU4~d`#kqG#fh7kSH6ABD znMybUwdY#w00(Sn8yKsX=q^xOgrPY{k^@p7G*`RA+Be6%C0%8KLlDHYAw=1qY6FVl z7-^eQWIer%69k*F8*Yg5N1jSAO=Nv5w3bS=*L<10S%W2v zJ+iT!kaopN=4TFl>bBB{-DgP5xkJXuxUKxA;u?1LM{*Ds6~WU;&~D5*o!WShH3L*J zO!0*h=otL17Rbb_NyfYv8z4Y4QTrLh$IJTUyT)T^!F73BdK31xuki_+2_42AyOej> z`leL?yfzFd@&BrT&5s|d<`g%@0MGRNvlM<4G_lZ7@(qlYz z76ZK~_elD3dq;WcNG7a=9d;QTa2VNFYh|}--DWtnM!7;w3Gl~n@wBp$^Z#~GqFLIm zl+AP-t3m}xG=e>d#8CJCyRL?ohL6U_Ql7%l7;MhE!-59U&JvJfVsw0#ojT_lq85HkV4>~0?kMN=nDpu zLwtK9xs*0OI`63H9)jJ1X?RL#v$ZWszUG{ATPvJVcgl_g57m#T)ypLsr(4(7@PCKN zE3dEi6^Ffvtt4=ul7|cFbd*hr&w1)#)ovdN>pOvoPZ#d zSz*g>yLDg4`LWrZUZ=B1g@|K7XXudZ>@*=R*DW%(V#u7u0-z>JAEHPSQBuD7B*LLW#;+1`b z>;-tBX>ko=t)H5+%6PKl7!M|?UmO>IOry);)JE#{=01fJPGV@DWB{XXjv`cRX&mA6 zWB0B}vHi4;)%=ps#s@z@7e@+!XQdipb!<4Y)~Y>xf@Dd*Fo(gK#;F8H3*`ceK@1TBo!LvAD!A)^wUqtD5Uudky(%{c=8 z=P%Tk=kIJ6xe0{_=V0qH`|KnQbRXe=rCi&6mDZ}-S3$|e*J(n9_g>@bM`~PCzOx-PeCBZhlQ@N!snfzSv0s zN;Xf_>_7)E8B?-nG%Pi%_TvDWhA(`a5&Xyq;b7)OFM49mIxwN*a`P?%10Eg9>5v1l z@no~Zc}^)uem#3c^KPXAvW4v-k>|5BHCCIbaUdeYb1q*!>NPCVaF}m?ShqeP^f&tl z6-O3=QZ!Q&U@m-=LTDT%5t%Kc$t-}A+XBY3m+h#t`>P(R1_7T4YN!>l8kQurSd_yD zmG6FvUPJzZ;e327r!Yprb0}}pMLxh)c z-k&ruqYIGzaPLH@X+qY}HK!Zv@)SwuE8J=#m)oUnbvb@^Y%nAGwqyhfS=P` zN2-STf?IHV$d!{MpLFQC_o9)L2l3GR?2@p(-Q`}2%%O&EEHB9CUcE6Rx!gDqW*ul6|Se|_=}B`=;~R>6P_ zlgU_y=LrrV*-LasHePZnf>PW+&$Y@%97JA=OoDsu= z&-tluki7qhZ|`t;O`xJqsSlw8(boMf*3+(|oTjZ1BNcQbAOF;Il3Xk8CnDBL@$;2* z*jF#O=h5-!M<4FC`0_VKI!;nX%E9o0uc=`PMC9CryyVbDIbwJ}U1(_WSyi<(A=zs7 zQEOxy^+rqd^lhG-t%A^w8@JRFLUh$JZtp9W*NerVyDHR-xr7ifYCF`JQ1&Snl|x>R zp)4;?qdDuR98ylb`{u@`rCBgZA(A3nSMS7hF#g|!oZj%EH_Sh7#9PToJ51eRtke`M z>GnWkuHyELD^cW(=Avxs{xmg>K8s?(e3Y$0?Fi&sNy?dvM*G_aW!1w}|8e(%Yl?Ga zc37@Ijeq3%#CzKtp34=HSV?g@t4cPdZDVOUvU5Ake;~UD^IMEZOW5>!P*zkX|8gg{ zp-*hJ^&p>)32XOua4U`a9JEajE;D?B`uF#lKl(r7EpHNpeN|#gwjqSk>*E}{7^yTP z5z*p;Vp~k^2Ge;*8&a2JvY1CC0vpZW8oKXU8f2&Ng=Xa^dMKHNxM03gB94B|PallD zEK?Mja((W&_jS6-gp)7pVuaY7^Fax++7YU|o<3B0(t;M2K=o1v4iriausT(f+|a=pB1W6E ztRf=D#jjObb1IH;3)7$GpP&fMX<72bFPQb`VT_(BLHHia>ZDNpc{cWB=*>Bi0{KYi zyYBpqp$8NxiD#ULnIS#kL6tX}#oJ9Q0jL@JDqB*yGc@sxYbIy-^>ETc0Ys1K8IA!y zIZxLUZEh|0Sf-<#b%kb@q1Q6b>t3cR!g}gD!aD6yS#RQ7L=awGTIj+rCakelnl*@) zT!S|NVYwc$M??I9Y9l-XU2v68x#)Z2n4LT9v0pWx8GbaA4$XnNj^R_+aJ|DYPbNak zNrp$fg?`-;rEv=#^wPwz!ryEW$w2>wS?1yUx&I9XSBx9oM*h3rySDRonu6{3Uv+w5 zwifg!>)rY~*&3jbtZth)P`4UENlYTT?ap)@z36*bBqci>J*8i?dBcs!$ZYNAMouFw zR$``vP+He>G8sQOKTDk{w9)@p>e?c#*)wI(;GOeFy~Ms6@6cTfCI`ydl#eU}XtpMY zx14g1DKSTFkqJcVpHT_V;R~&lksMhPT~Kv`*Pk&B@tXVp4k3L|^)-&FP1=s_g$yB0 zbWqd$wPw{g1iuH&<~MyfQcfJ>hM3djdXd)$Ga_$>NQb}#bTC80xKJ-8Z!8w`n))1T z)z*BA}vb=DyThXJsOpXSIQS(F{lQ9eTTLoR}X zK?J3L4ebHO=gbxFY|cuS-?`7?SWs`;rC%2w=? z{JH6A@cn$TRb;w$9JWKo0h(Q8WV=e)nf#)S5)H4MR}aZnQEzjTzPJ}XPVk6Ox7n8w zZE`n4WxefL`1-)VzkdX*Mn#|wIz~iElcgMuWy6CjB#^T|)c>V0bOE~$+U7}1-REiY zE%eB6djluFF4gJur5%hp7F>C646+l8Oi&LwwqkdEg(iYo#afo$k5VK}7?`Rf=TCj+MK^yJ4FH z(Q?2&m8!YBq%Y0VP6@41j**CQ(*3X?H5(C_|7IQ@3Ml(PF z6hI&L+~CQ6uX~{aZYH|O3Q6RX+3v-C8_DV_>%1A_$eDNnH4%3+PerI?;whjrA-}J3 z{j@wohfuIlt~3;qnx>IW#gxMzsZPd!Ab}AOPupr52#f!Flk|WBh_Ty9Y72@Hn|jLF ztNY;%a>Wov`z0kevM=m(o)qoHO>(^MJA%fk#BVh8k{`vR$gbF)_`A<9Y^4UrdsZUW zN;+pqhw&)*j`{oEz?jfEptr4!(pPiwTa+m@d&}V8YhMJLvK^l{r1+S_BfE*uz-8db z4S}bdk>F`W=oSy`Y`x?vx01-;t3*R)kE=ble3ha~X?giYz0?gwRs=3h3a-p3 z`qhlRcitjcUx9id?hS2#ahkzfU-GaMYU?`o$iD3zHeybv-$8w8^NlO&(;`^Rx9Ftx z$;hq8kt0G}t+ZX|2Tw|+5SlD@?`o`gNcW4R9=c`1wcGg zqdZvPzarB2OxDTVZAA1(qI8>=B5MKuz_zsG#cn3Zh2=9w-<_9FtGsvz2WaV3x}?ga zm8X$hO})#gk?op(;0==oMgb$kgfq#-b0g3QOX~4AyaRNra+PiS!WrP-hesTH7+b`U z8LBX7PBoH3f1+!uUa?0#+b5tnW^NQVf3f+zR170^0n)%n457%wz%eyUpn8}!4if8P5lYIE{R+&?oZUgZ%jM}&w>70TstUr`YD zx_7TiZ|pnrg!I2YMrN`=AOHQ3A>I{fmc{-Y7_0`M_}t$Q8ShnP$X_! z^T(iYo(!)s()zBu&-1Nj?Kl&F7nNcuVpH;MScQtX!N~I9Pk0;{WqW`m_KpZX{PHCd zAqtI&hl&f~!xeiCVw^s$*~c09ZFw+XheSI?BsHeHk6H&QOU~^xN|eYmIPsJ!bj9Mu zqn>lZc#P3kyeAs~_rS&o@l(FY{2~hd{=sAOskfUG^jLuPbr`mdwWJ5j{NOY2C`m#x zL}^`U;Z|M}p#Tg~Bi{kv)La!9T84`fF5fV>=%N!=Mg4F|$ZV$*iSbww3sXM!+JfZ- z2i#q`cWvUa66Ny#Q`9=;byRoZ2}lS&^<%jR@#?ZBem$MxuLIz%4v@fxvaKK|XVUpt z#1DKsTx$bKzqx!obbvqZ3zR=x_W@@g`Sl+Ub00T5;E$b-{)4qI10 z>if7OGdUndc2Bz|@UuSpPD-vNjk_rBwyOPL9Ep>&H3atbMxFh@WfNVfE|^a8x;z+c zn2SM-abgNB4|zhm^ky-Q1XA21x0CVGQ8@hwuEQ|&>koXKCgEo$HW!Q%c1Kj=`3&$w z?ui2bkY+8`*;}2f7BFzWcRjR-|II%3y)kyA9I==|k)Ol^#RT{Ik4MW!v4qxxL z$a~}>#>XT0{i6ThJNVzD%f~_g`@zR^X8*_I&cCAu@bk_Qqw%5e4#S!^)lv4t+ppeX zpTGTIv+f7@AiV(~!>fnFp37~|S%8VA$s6=%C=Z1k{C+Pt%0o}Int$UIBhh#vzp*r> zw0rLcK$~`E%LDs#Un?4e92iZ7p7ns_Myzc-jIvLH!Y~4CVLAo-cRrP=^*9*g?@qjW z@#znY{uVEj#J}Lxy=0}s%qyFou@Dz4S?l1lMJ1}ti2Lb6hAP7A!Bpo`X)6V=aV)OE zcRf;=z^lR9->VC2ybj+-#_>?o$mG8^Lfr-X{2q~bGZA!L;oJJdh0wwaZM@{&xAh&b z3U7P3c;zc6v<71Q3`cnR6J;CH#zC)nn!Zs23C;Zr{{z3?*;t!7*$rK)_3r>{&f+Yded9Z7Y2&^J@R-pr(jt11A!I&BOQG>SLG9Tla&lbU5^Bu{ii&lN zd{B!1aG)-l-bQ$%)roFuZOW&GRlDbdx2C(Ad&Ar?X)i4?*XArKGiS6eiyhM!zMM0BM9;@e zP#I5AawmVwN$Igaz++Mzr8fHM3f(R?Q`|h}(KN5F2oK+<4Ze+d!75gYRcT5O_h9b4SJl#UV++SJRpFjjn_CCjZS20BxLzrNxf+{d({lqC(cG%GjW z%`_%Dp74Blki^0k@pMHyQN{Qk&IDh>k}vib@q2DJM@tWFEX$l1jq8Uo2T`qc7Yf35 zY1!>L`}Fc5LF{0Tgs?Xje`r~oz|>Rh;Ln`$(@7k_1J$}f)XJV^_+-xCXn_MgjN${# zY*?SRQUti~-kSO_;MYt}Tl)j6QUr{SE$=45#(#pEQu~JCd3BAq8+6R=TZ75xBun4B zQ_RCPCKgCscJf8MgB}{sNd!FSS4qyzyj-MAoSIN!gk+ye+Bx2==%zS6`9gO1rFt4P zBVNJI^aTYqam=M{%XNf$K3ePEgSyUV79V`ZvDz z%YB$XF(6kU7>_-mF}+0}?s(RnUezVv);{4HsvVT0-dEyp!h?q~NMa14&MWdZ&n|5Z z>(o$N`1rAqmK&a6)~zK0vHZouePS`6 zEK*1%1^O*PYZ0&~=3uQd1QYqM_>#Qk{3eO!Q71TTY;gOpjHyVLW;(<%tjmG*^DwpE z6#eKZu7^3|t9>gh&^9LRXZ!4{X|&nZ*DHXhIE*a!#k4H?`d-@70?#c_RVT1VLJb_- zYT2mc*-O{YX3vay*(8A+3u;@&mo!eTj1Y^SkA7C1UU>)}cnjdslb^Sc`E{rExu?Xn z94N876^cVIQEMj2t`|sI{WZ*1Dj%53T!?fIE!7GMSY1n;Q<)_t7-OQIg)-nwBXGVy zgV=q|_M5aot#0%e-Y3n`^3&5ld8;9UAPXpNLw1g7Vm zro@VZ7+)=6U)8sCCJ`U7&G9(HR^T~dCDId*ciNz2g+~=IMkS^z8RUQu``{&R^(JZc zo|8)CLE0d0%J`2X1%B4bcXeWE)L&p`-C!;Wv0qRZ=!Q}awX=U(*Q(_H$I)4bMftp4 zobK-K?v_TRLAtveX^`BNTDnURknUI-0qO2$326}Nkopq7&+q-8i))zSIWu$abARRn z$Q|2MV4#KNBS3ybOhOgjzNQpM7dUjpx7Y~jPJ1(Tf3+^HWVn_Qz&IfU3c2B=RH^NX z9A8L*OpdZM7q--s0A(pL!1umnwCMKjplfto6Z_OYuWsX&RSrKf{Uk-f02UaB zr9l1BME+GM)(G%`t}$a2-g;a(_&R~68$)CI7ihzcpWuw85a>(!CLRUXY0tNxK%-~k z!Cb~5$5pzAqX}Nr`I4l8m#Pu+ceNuD@~*5;L+O0=48C+6+1{I~=eop3Rg^nY@PZN| z%-y^)AG&*9b7_99yP7uJlqiB>5CbjO_fenl;}(_TP{;duC)VcAan#2RC>Vw1^sY*S z!d+IH6~FtV0aozzfGK-Kt8M{M#5k?~LqwY0F&8-dsz4gW!l zS7@df0JcobE+D$!rs&M26q3ms3+ddN4YPX@hwy|Kqn{x*q4^7^tt_8LWMOYnViM2m29g(x81EV?!2(#&7RY_ol$3;+8 z)QJGR9Z*M^Xg%&|2>5B2No7c@(BjL=&(RREiEvvJjL(3n3hURB!lr3bSbs9ZA)NNc zx2-s(Xd66{L23;pXc9e_BUxgD|8G}xvH0z!%6yDrJI%NipW#XuwN?EC$!0fk1yl?7 zh=|4Ddj>Z~k&&=!;8RH(`2+(udH#Td)C5M~{a$#}a!_t^FZd??k&y?)w$SxIOjcGU z8Vqo0a69zFvsnA94w8|lM+=5rHXz7lMpQN!v^_IFmuUW7YAM#u=@CuBJvyWp{PvvKN%Uk zCFKX1F~B!uX2gTMd=S`Rn!|2mY;uuk^elY}jYTO?1jk@vmW9Dt9uv#LeaQ|@S;gNA zf0zATWMrK+74u>L!@P1?i*_dVF0&Yc4;{}^gW-2dn%&@H1s80i}{&Xl?DH) zJ@2P$g|p!g=4NSn@-HBlJM`P_cX_eHKTYwC$x4z#EBfU8W3g_Z+6hyv@EU}PU`%^z{#wiy8K+(wO;pr z`v{*H3CnRvU{9sh?jNPRK23EW+xP~p{u4c}zX@e#-Q<MO*dC<4o{lb~dhR_@o`Sv;C*G=AtPqROY%kAKwRO;d=0lQT-H}Ex9 z5W+l@fJ54WSjCxfvEa-nYb-jUO|tmnlrgX9F3kJ0^eFzB=MWTj+NH&hy@aPSf4%4sFK<4M*u{ zjn5cYrhLekh)JJgE-RalQ$=!J=CNnW*_1aLt!@uS#8QDQChD{vT1$!m8<9s3oZKO& z7*CbCHzHL+t*phvk(qHU__+Q_ywBYe>Cn+209!?UfO7K(u}Ui+G8M+P(M+LRvi$WQ zx+?MjMnF1;9i&cGX4FVOU*nJ|7&Zw1@24G6J&vEK({8R5W61FiuKYMW_4fRyaN(^s z+AKh#tPGUI@F}?#eFn_IcG4YU~qNzi|75OjeNMu}B25_86V^#CIMP$a*+jRo>c zohUoLK3yp4+#)&y{r6JwnJzulU}MDJmu^R55iHa{IH9kT$C;>^IVJ6)$HM@=q~B1gvQN$-p1|+q^HLR`0l+@VBj~sg;K2y)FMrz?Z9Ibt>D$ z^aNDk@&>lW_6eg^0MYQvU}#Zl(?&cACMcN*xYQaacdK`>U*TXBK}p^Gmw}FxvHdeT z++X{J{J4Qvau^i1-BGPC*o}%Zkr*PB%eDMA6OnGM{_j0NqUx{%;rK0+9Yo9T5^JfZ zlkc&BS7B*>n^;E-#2#^j=9ZE)PDl(cpAx>yOM_>g$55@Xgum%ntEmx}7Q1PBjTR=0 z`eXo)jPz5RrJ4;|?T$mDLo-g-?xxDet*yktqM@Yes#^q~5Sk zdj=$Zo2(=G<&^+QTk?t%RfNZD&7`dIz;2AQ?Rmji*$bU3bBbUt<@+R4&Rt)#qb=EF zE%-37JY;w%D9=@m2(&O3%Z=ygz?6AQs3mt~Am+{S6F2zEy9fjB>7(Wr9TFMs&+0D3 z)p?1`a{uUW;&Xa|Th_4u*}2syJv-F)GnAFDh}o&@2`5}Qb)AtUUC`)!V(u=Pq~0nW zjm2`JAZHkY#~4pK6sSDt6I&$%Jx2QAzVq7{g5e6jqVCn6-97PLFda-x#l*qq?J|F<`ZIxV(@@Nje^10|AwcZ*3>wx8I-P(_8KwDa!q}R>6PZg&9A?+%3_Dhhj^WFCM|Ofg@0a1Bvh87fw<< zvZfR?Pdi3~@PmuyI64330*0>ft%k!?0FtsL_3E3dUsJv0_qO3(qZCTD+0|CfLwstT zgtFeW*Iw}pY1ux!(xKX|lsKZQIy)lIW>VHkQ21sCeE)Ho>BP%RSj0`i(v}h28^;kj zauY**r?mpF9jF?CT^gBy2lT%p9J67*R>~cB5pb*?7z=F6l70QHE}@$hST2<{TtpoGebjWrN0LId0vsYfpM93f5}QwE^1&FJz%UU{Jmf%zr)kx6+Sik^>$%;yT>Rc;R;xql`%J@W`j}>ab+M(!|Z?)1x<1@5GYr_?r3(L|P1R`I= zy%HMeYg7^i&5&Hn?3>$oa1>u9GiQAHBFEq|;^Y_ZGGZ>4FBTiTdQ>%F$u8y{i- zetdtA4=40D182V|k(sL zE?+y@kF6F(OceaZ3~gz2a7A*=ON9>TO|r(s740#-=gYts`cDMIjkX9z0s~c`=PD&|v>%W{OUWI|ff0j&>~C3OyqG#gy4R?2ilV zXC|5Vhd+@!>yr-Ho+%s zgv2dC47}t*z=`4pfdQ%(wR|d@^5LsjkbU!c^<>-tOKjq##6>>lFQ*ee3_=?@Ze)zx z5ai%YIzAbOd*eQ*C;IQIr%jwo_SMx)RfmWoH4Z|@jd8v78RK$#!fkvV+#gd$${<3b zo`lkEObo6lNxy;joTpZ*o) zb3;ir#*W#`bbuzb*e&V(q}jC=$v3>sb1UPqWNAzg0!g`!ixI1%XBF+B;$_J~HYdrr^`#pQ7g5N~ql^QEM>xikcq^Z&z>z(Xe(*n5KW9l+>-Lk+(77otVpXZ)FR3I5uGG#2CdQ;8t+8SSvvvF!%c zG<>%42qL_Xl<3Uclb&?uQn*E&`TX|@f91YLUA_Fr8ySClRm({rE1Hhdpg<#3Qser8 z@6oI_YuS;`_j`3L0sl&faK^m?(tc;W)q8ebZ^E~jpIFh6lRuF~u+6}Vh~e!6iOW;k zE{t6AhfO;(t%+IZi`zQERuLYoR1xxjJhM~w3HUw^i40nmDnzWA7`A9wWvMuzBMB9D zXz^9EwyB`(pO9fm#fM-v9}W(yI@5_LX>Mvhf}f>jfQ*@?NR1S|WWJPq>R_blt%O^;K=Du+xPZ%dbPc-hPw2VVq8gA-kYru zCC5cG!Hj-jd|CjY3xyOZW1tnbdKImR=M{WIc-BWIHZiY zq>V!Zk%p*(3A~b^OAGcm{CjsXaLQDXI>I~3hYcy=-W3wUgS1fzdPS>}>RS!I;KjvW zXBoZ45;a5oRlG|HHT`}tG|dAcz(c=Kk%`@7Pt^X8AF1q^8Fmf3i; z@`DNQ1Iyn-$0AR+7TeA_|GGBG{or&ozsKo_-Y?rvnvr86&-!iA0+i#O(QMcsjvovwH7}XyO%H z5jVo`Nsc4d@FNkHy>um0L-JA6bd)b9*h*tfRNT$An(@pe-HTo3)TsC{re-g{DF}@& z-d8?K3TinkobT_gh`YastdI%H91u(((Lwptz~>t=tOEJ`BBfJ;J;;hb*)&vA*MKAx zmXVD}J!Z3PtlJD$hGNk!W!-q#J0ZLy5;2>f{;}A#q!*-Ws9$^vEm+1E-WZ@3r~%kF za4)s}mX_`}(6*o!NSU_%CodX-o9}J*L|3iNpP&|kP!t(GHPcgXH?hhzo6N>1$5)jg zuo6_oVE@Djn_D)%DROy^nsUM$4|!s}`a{ITi|0bG^z-ls?YxuF&k^OyX%zGj4T{f_+3z8w26SA|VHty3^5>D=HEUIyw_^U{bdGSQ*N3exH^ zO665wF=# zzJ%-9sQd`i&qm~TP8S_r-ldvYWRUC^Yx#*V;FA$`PX2D_7u&VBLspLZr$!8RZN z6BkD0-dtK2q$!kO(`6Ej{&)zi^|?v%c?Zb~f-;Wp8x|~#b-;ygtEAgu4hK}1Wp0|i z|I2BLvW{S_)C3+{VE&CsqW%&uR4fg-2{`Ev&HU{M>!wOC5~Da)>G4>S$)I2INk=8x zFKc$o43RSx-m?zYLC4U1&8mIAgTFV*$$Lk=zt z{l?)b{KTXq)Qe;fe-05Y9Y*i(le&g?`;OE>Z;}(q4=KPLast<~H`3Ei;rNr2Gs@># zF}Tv%qv10IL`{thYwddNvKCmq8ERr~L+)Ke(PC1frFf4ljqBhSnv>seeg_N`JkRIG zVAG46%Bc>I^XApME1DEZrZ^LI-UY5pB!}=>#(z$>9BP;-lm>N*Bp*1)v5sN~<7|x_ zJp#VTO9A{i`?f;f_S9ZWeHMSXO!ltp&Eusg;1dd=>4&iYYBYc5tHqu85oUC_OMXWO zlCQhp83t%)NZ@R(eUb81c7n)B?LzrBa=E3TY^NJ_}V0g2yXQRFOD;N4MjpRAKf_ETfH` zk3+(KhSMwqcbj=;q=O@h252Hv7jl9Ga-EPcsbG7urCYhgwm*5w^XT~vGz&fDgOM3c z{tDy<4td}f7~Z_P4c%r}(y;;70geu0?1V*@_Y0E7fnV&2A_@0sE1>AVI;j*QpU^Fn z(_6uwcI|V`zUg>l=hU*Z7I^BQ4}J(?PNO<{zf-ZlQU(4i6q2zEo{U}CyrQJW9inK8 z{wvrNMSz*m>2ftV!}nsaV+k{aYen=9VbE26pH2rcs{@QsKeoAMWoq#Gv`RV%Ws zA_HimxKh-t2*LZ3aNc{)DmYtzMW|C0LFB!I|di zf7SL&9C|5MPgaH?>L8-6yKKTO@6~%1ymm0(16e!;!kW8-UrPTWp(HqLSoL|KPBL3#x;3HCiMYw6#PH3_FLtbk3E*>Qx^1k9KfPwW#P3z{e;Af$ZSmq8Ycg zXp9iIC)JefnGRyS!K6QDE|O~;D}AW?gx6h4ilVr=>+CE$`)zQvrIXsf$ZhgIvgP9K zJH$#1^DfY|o3BTpl;oMA@fu0#Cu#0R9<2^VCP6%EEF}=c6Hy+S)t9`10P2G?o<-HL zuVUpJ0w)xPrapI^d($4Rf{m$?dC&tS>sIo>p?j+`??d4P-Dlz^6d%FyL0zy2=`(fX z0mpa8t{jS5L0{fWu;;&N_HXbbhF+<)>KX8}5d8?hr^^{|`Rk`E>XAMLq=EmtL%apV z=H)*QIr$^!gpE;ydvu1|!Ash>H%6{}ZHX?c0bi2PJZ*!-&`nR@_XVw|$|-+3UbjalnCN6{bhtBCFDvbs3Iq6uOpK8zMJ>-VIUNyuHA;ed8? z)Wd-Q?e>a&>4`}9-x+)+=C+E^J^wW&BzVn8JF@ z>-%jaw}pG*%=YW^qtz*?#Z@!d?GveAU_58mZ@MnRUL#1!eQFIfQ+58vhlVfltB-9aTCL~7o};$_C`wmgY4$@B9pl*hNS-4 zqVLzv$0?r~w2OkTxYF^xgK1l}H#C`;^kGx&znrj-NW=?&Jfq#q-U^@e8!1PxW60z9 z28(*)bI*U8`ESF!L`h+^&XsmxiaJxB>>+*XLNFh^Qu~Uo!SsvfNX9`Di@!3OZJn~;JwYjSDMc-#YuE1~kuXdLh;-+!7O#tbRC z1oeTPya%UxoreqlMunSi@NzAr!H#N`-3V@WdiFAjQ$f^$Rur}r`t#zJ#dJg^WB)=6XBm5fAVY3kBIB?sZZ$gSN(-Dt zX){5946qW1Wk{7(4A+`c$EuG+o#?(}6h4cAfiK%GlP~Ex+{*}_&CIU@y2whNY7?Kj zOv<11to($tS?pF?sW!GzpsS$#g{C7WYxt%af^_Vi9qqTg|L&|xwX^BS)otdgFKyha z+Yqi400`JE553D;c50ZONpFSTf~)jG-+e!nj`b+LuIa}plel@9RQY(@my*fK?gZ-j z^Yvjc^{Yb-9ly-}tz2uugzp&1ub2s!jD2VJI@n8LQ((q^ffkPRs*~U|pMVXo;*jZk&Q}_q%8`S4yUHLLzr`bqMx0kSMs5bv39_dj*Ig` zv5vw-=e=Pi>C*$iMtWYB7d>O=;URKW9noQ@h40nkoqdvy^#o=RiZB4E;(4}PFkTib z8&qUI{W!W?T1YZ+`bLh17DJ|xD_=n^u&|tBxe&_rNh@Xqj4G$Uk7;WJfJ-Ps>LGEj zbOmA)D08}Sk6Ln{&ro~OTsJ>kCw>kWR2pBZ_~s%&A1UdHop?^iXDsFHJEa*;vDu!K zhanc0=gBZgHluSJ79A01{m2sBy&FofsJw3|9BaU* z8}59J3(1uPv&g>p8~l?reKS}>No)Tn;d`-WMp^SQ1mAwy4MmhrMrY{owy?^_z((+* zo(;8|ow#TXY%IZuK@t_SO1hiy%#+to8ApAQpGX)?cEfH$eEy48LV40u!S`JwWPwjV zJpcvprs@XNzE*|TlntO&C`x|7@MNeb7iZ)qd-%c$&QJY1k}>pYi=aB}FT+@Rz{o50 z@0oEn7kj)_pkY%_>R>pzBO_z38xQF)TFWD}WKU{OM^cVBvBP)d-1|Q8QDQF2tJoLC zTs0j1o1^eW?D9^txGTO{`4bI(Icg)h_fEt@PmIgvXKn0tV?qeCfGV=4M8UiMrx4BZ zfyotH@Z2nwDod8B)YWq1d?KzU8vqFwAh8&5TkcM{bRYk9L>AV5 zXYc7`yQsZxo3;$hq#%h=;>&Bb&>DTn=(4E6plZ%tGiz`1V4&frCHSm0XGS{8@3i{~ z6cW!>SnOqlDpHZmx#oCqK z7h}v1r#NfMTI3xG2H|m2R874%ErY^mKm4fE+q|jzx*reeie zcxgIzB;pL?!SD))2GkyoC~#3?QxU}{EbO)w4NBockF{w!uvwS|q#el2^ zCwa2Dd_*ZxXLOj5k-eGclWC3jMxVV9lY)etO!L1}km<$W%0GKst<3h{N(qjOg)xSm zYKO~SRsKVwtIE;favr9u3KFYv$TX4zhm5~K%=d+3oh7%zF1pp1)OXr94U?TEKVauc zGCbgRML3ViC6s1#%?;wE-N^M3BNEa=B<<;$^a;uJ;0sSaadBp%PLowBF$g@Gfk3y` zMXR$FPx04%s0@R3-hIka{;^n`M;M_|t4IC2RLOpv)2=%L6mbYQ4}z!)>L19#fTde( zpo(A%|1bIn-DfgQ?b^?}pkMTiK!Z_SH5uMZHLno`L#o3G=eJ|I(PR}iV9!p-FaRlP zBiL7(@#z#KcYJF5`@vk?Y*!w*7N#hWOI`~^cDfg(>CvR&nwDDOH3ulf>z80ekZ@{~ zC%2BFkjDHYF*&+@+HoAHz{3KsNaHoe4$JsgC3vtkCZU~;!!fb&pWt7=SWf2Wi@Rxe zL{EsgpWw;;4JO(<*SZ({lJ-x7U&szSVG0c{O)y`NaZTjoDT z^_+L;Z%=orM1JZ76v^PvU%Jjv5sLuTOg0M3oMoHciAZ#3 zfain$Tpg#+#*-M*CsU1U&<6mtjd4zE@??rh=zm&7A1ge?yK+$fy3egj<2Qcl@o!A< zxXAdgKiRhgsXrpFCy&MiLE^mw9bb3==%f&;A_A1Z)@TjvdBL>;cwm$@wty=-%a>v{ zH2~q)#@}RLX5Sr~`7i?FiK9C^4Q1 z?7xT!6&ixXTSNw8xK?V%UG*mWO|LgnBjePf+Vu~ONL)q6T_v}?u56|e8h|NnxyO=) zO4_tKoFt9p1}-3YShYBSlxv8;PkBG3#4G6Ftb*xQ?sg2(IuvPy?fVGouPaqujkYP} zFg?R{AHUph^@BoFR|3u!D7LlME@pyhaK90kqW<%6arq~nODUf(4*UOpqSZ!APQ|bi z_;)Xps__IajAK-1t5~gok9O~Bo}}CCb-l8M{eY76&t7bPiK+EEAi6ic9HA8$pD;04 z3PMvTiobL=+Ax<*o3z1M(Ce#Mmme{`U`#<@pS#|1`{slGi+%s@x4>*h4?*j7C5H={ z@q0kEj#g;#<)w~K z+8ausnPqT8Hilr9ybvYFSs1gM(>!?CMHtf+*6+?9;W1sVMRNF~j=}T+OC;7^lB%c+}7^lkQ+Dvqt(pqdzm$UBk+R`K&vTL^-uPMs9vzCYMKpfGK482rkcB#6zGA zW95j;?pno%#c8Fe$z9<@d}JaQH+evI!*q-RJZK^7Q||zrs=fE!X#-3`6`;Q#VLoXv z<^5GgBRUa4+0ik{JZQ8(Vam;^0T8#MBw9~D2^6F|Bvr;Kw3&rNX%L5?gH41GKAOgp z?*Gn|Q^pJ;G=9s8gkfO5PTBp%k!uAm)oJ6Nn|&pyxlP1{v#5COU|gnH69-))Aq-IV zaBJ2aF}R+vP=?xk# zS9=rF`M|jRvi}Lxs&iKA`56D$y4h59o0U0*stdMs#O^-;&g+LuCBjq9nTJF0Nu_uN z-kdDS`#b@Hz{DQdaVm<{O%~CexNbkG)nV|JH?mf!nCw5{1Uh;^f-X|h=O{W4U+WXb zyczIye7>cm^A=gJZN5b-X%tOBuw*sF?P^+hN2+;>;5|?SNt;uEN{KVG(Y|r!+!szJ z@VWvH#uQE_x3J{f|L#2-y}y;wv8{Mxc&%u{i%J(JPN|)#Ig03;Aruq<%3ZBy)Y&L7 zripw|eBC+KK{aL04Jo25cUS1K%w(ZDOHirzP7l?&#KYuW#!DWO@WSJ=&bVb*3)0fu zUxU)a9Q5kNM~SeeO|+qkKPGIGn&Cy@^)Y-odvP;fOnWhnh&1lUae%9)NIJq->9{BK zX!euLZ5*G7y64JieoL)ujlPaIoMP5oE8<+pf}*W(D7=q)gL)d3r(}5GImb^Eh=^1~ zFE1ksW6e&V>?w?`oQ=HN$-Gw&6hnKFZacQ=$SqWCr2^@@qN&Bw&y1{iR62q?SY#6` z5QT|k6wNO$v(uM3J9cklmOaPOmJTc9Q1KvWcz%IbdkTvm8nlVZnuokK1C@F3Ce)Gz zE4iX-iXDhih%@IBeuq1xXg@v`{u^g4M{rdr8-MQroaU%%As7yGW%-@BeThOnhmoXh zVW?27oBv*G>6=S853Pmo3bDXGqNKw3UAt!X=fQu%7T63#TaJraO!?grR}X7d>A$I0 zSL?V&-s(4`t@{v607p(!;z9sJSIt5mR(Es92g~qilSUtGe-;|^R9nRqSj3F^Nq)oS zkz+jWJeCFbzx~{F0LfmGlr4e^uHci68r%wNh${bE#O`_peOt1r9QaN!CZ$A3T>Jw; zx-N;Ye=TU_sMe@%{6F%|j-&@0WC72ImeTsCG`L^0feGN8fRt_E-zUO=mE;NUrUc@iw=Hdh-H>bI?|aK_o`F zDr_bzd<057hJmUJhtahxQasL7fN zm-drP{5LvOXOpOj*fnqkbjrB2^{Zy9F)0Hq` zlJ6_@HQs5dHr2Rf)H7`ca4sTS_XxTxJ7V*G#(X2Qpiqa66;B#8syG_$xUA)vV1pE3y*#^Y?n->%)SsBzBLZ} zk@Zx${RVG@9x1d#_--`Q0W3lhv%+|Ccqo>HZ1AR-L{w7OmP~>LjNF={Nafy9S=Ne$ z-0s-8R^)A@p3NhSFs6+D(9>g=GA7u1F()0V_cOaIn7T_d>%0GymGGn!ELpFTwOGhk zQv?I)fFe6&wCFlFQefdx-w=LcY@0OQZ(h}YyHU1&zwcOe5v4a-u7zX-SA0l7^KT_H zW~}ywLE`E=3)(W_M*CT`jX9_>&EjHi7F|;JJPT~)Sps^>p z(-a^upb#whN*2BOE`;s4dW0XX${Q;V^eE{BRy1sgw7vTA-|n^Wt~B(`$DfO(O#dMq zsDMP*uJRrW0uItQ%5`R)I7yCMV1u~&nX3EHz<{>`Q&w${l5lVIx7dg}>Q5SfOr)W(>Sr~9ghTG9R+y5ny^)17+8#-k`mV{q z!pwOw-Uo-u7`IK6{#1ca^rbU#38}z#`0NU-z1t7AR73NR`xE@7!?;JXx;LtBBa_67 zJ9r<7a{9RWWJ@Fy0Vl1~e+>(PlndJ%nP#BzyQiU=`E!i2nz?v0_;X?XxD5qx$LTb% z2-o12JZ}*T#6J+=v9KKjUivw4*#nc0e8^ZpSPCqb=EsA(u<(8hZdr6Cm<14c`(;!17jk8C?v(WN3PS^$cG7^z$p46D-?cF0xw!{jDx$xIO8bc z*<6BGN<=;y4j1(Qho56}I=O|7#as%!0nrIW2|npfdhIdx?_!KPO`}*-1mC-*tq1%J z3&)cG6O1?<=(1nzfHEA&60r&E8<|Xbc6gA`BXQU~MuOzu4yuf0jACpUFjAGK6CTB+ zLV=Ci9(^}Jzf7buNIhm%2h1{4$rbQZG&MAJCm>Pi!;ccdby3~+3^V8ZAX$fJ()pAe zfEsFHmvwn_T$S*=!tch}Bilu5)chM$I?kN0;6E#vB8j=(%Fw%nOg0F14P-x!v5Qzp z-cu?^eUFki+Jh}GY)Z06mN?c52K4BZ?3U6Fv}s4X8N#RRuBx}AfCU!U+JkkDfT|ob z+Xx~#RRWN>I3U=+uAcPRL1#oAO3R3emkp9%Y@0Q7L-}sWn1ZT-juD-98{8%u?3N8s zY-Rmva7(@mM-EMVqFFJ+y@4u8#lo2_`_n^B+{mt8|1^xKd@8>}>D3#=Z>m+ir6==L zepkIdk9NL1Z3GOIUhBEqQPwM8wzA6kj#L43@pE9o^96eXEg zr6EyzQ8N!B0q?_)A!>beNRm!fnE*UVD2+8|=ti&|K`?ksXxfDb%igV`U#n z!qD+<2P#WVw&O2X0X#!N%butTI-=VnJ%_+bcY_Hwud8=)+U_7V(#jm2S9~Mfd!)Nb zt2~?6ExNp@0j{E``?6V%YUZd{I2#|91qmr|PI3<iVJ%?Hlo-tHfHmFu#&| z@*c)Ivc%O+svnu95%8{3F@glwX9ZI%Pf4p1@|IL|VVA&bI?wWT-eHLHyNNY0FAWFK z82D~~@xO-*a9C6J?r+@VgbPosoML*_uO%%M4}A)<>`6vy9JskUCIeCye#|&z(zmj} z+2p^q{jCRFM#x!w_o!>32L?5XagOdwSE0P1zyFs$$ z_MOaIK6+>wNgW=FJkwtehciW&cq9JaiS7=gv;QFy-T{b&Hvo}v4t_ao`1krhM8aEk z@9QCeNZbmB9+_R3JWYBld>(YgQLK)o_NGsn}!PMt!1ns zJd;_EoFP4U+O|Oc_9F11Ls$TNWNg_wD|cH6C6^c-%epQ+TNCr)@Qm2Uw$GEMvBF~-l5I(O}hlf5x7x%LfzSMzf z9ABT9P&aQ@q22riVQApCtistaoEE^QV_kDh6)%W6g>T_agEqSnuka5B$ z=MB0=?xiU+j)1Sxj+RAq&_kU)BY5f5$;|Iu5*jJ5WN-nEWR1e04Pn%^?`#G=UA4l| zWmwiUx)Wb5EFYtzFD%88 zyvEmZ95-eMXqt6u0>fS862vCq|LKipdmmAm%6+O5aGJ;?pX^`MPk;D(L;pMf0>g6y zq7~({x(YVJF`yLxY)o$HCZdFPW~{aLCM+~-eplL=v%4c4u3wu`{Hr}ltVRo`Ab7&p zco&~7;Mdka>9(ULZMPySb~=+y)`OXyzpW_ldG}7azkHmg8mXvg@+UG5*|vFq^KchM zO-M$`8f!mkYiHp!=8YYGR~+;ewLdv*9gnCJbPoi-YUenL@nl%6#QdEFR(&Gja3|ZJ z3w1`^JHIwI_FVGdJf8oVVzS>Qn4`opwe_LD0p4DIFx)VrkM>6b3RHq?=jZ8W&Q!>e`-pTIc=$;>jPPY=7^BE88gPfkpE z%oD7d-p+gfQ46Q8D|$gvKWw4xJcoAc7`t51!bZ-A`a_8Nh0MuB6r ztnP_GMg(iM4k@o`uCg&g&ZpXwvzWcF|0pJaL7oUCbFV;xP>2`@6IwgNo_hT|a(R!| zX3xI+E(u5fZ;X{SAH*uJl##hHf2a1VgS~Z9R*@#IE%+yDOfF>U6%lgI{$3KP)=qfE zcOPlt3>;`!*zl)h7gD+Cres%eRJu`9$AHk%;&9~inT2AOqpTthTvGKCJ+vrR#vlQ2 z)v=KN+w5IRNyVD%i117|jy|fGV-v?)r-mMg2NYIN>`8*ZSk8rtOV6G_v>5jYR{X(e z`e&uCGe)3Ua+A#kXB!ImOTh1YKVWnXS}?>yO)-=*9YhZS*dls*tyF*f4ZOFE@NM9i z@70Keqdn&SO|z?BC*Hhv3hP085{zukO+S&qSU4u|b-w$dJwjfJB{wG$3? z-VSkVX_g9Wg{sN=p`NiJt_XTWgd}P8sTLW5x|f zg0*3hsVQ|NTT38&*W*_^%=Y_5?c>&G`yq;J41IAx{L!=raTAThOTjl~wV0psw*iOl zXh_X9WoqU_esthW)oYGzUufJI0C~D&k=Y<;% zd-o+Tx7UwezkqjRh!-pdpALp;4WOo-#9Ujc@qUNY= z_D3my_$BIuacVWKk(CD1UH`q{O(D5dK0$8(Z-Q5$Z93z2|Jon;&)rC*MW%=E$4-TG z@&?fm_k!{mG(8!xZG}CFr`M@KdxaH07O1*S#e>B4%fv*B!8)-hkFf;f85_&Z`cKYE z7^*)>vC6AWu%{{hsJBd}_3g@Der2e3qnG-{!f6Wa)ZiBV zXS<=JtxKVBN3!Bc(`+r0H;|<1+w!C94_aM!Ovx?K3f%D#txdq`Xy~bO4FU_SH|@t* zNs=bvwgTOEz%VRbuI}uAuHOq2OEn3_9bn2ZtEF;oy8Ct@*umObhY=XZ@{*7oVS?FHH}u&f5X(f<8h{6j$#K>pphkjyFCb& zaQA;aT~lS zD*yH1IsAzl?G(9x+RTDu&d)Zfcd(}(+7NVlOlT34w`h^$h0~&f#p%^_hKZ)6qr|0< z;7!C4FkhZO@c)Fe_U}tSlG~Q#$WN6tDadTo&E*)3(dQonPlf7C<#Xo=0{-QJDnWoQ zeP(!TRZ#+O5@dss0|#ih3k1HdN0MI3azpfnd^UUl`WqHpa3eJ-dTYCR?lFf_{Bot7 zOGO7Y|Jdtm&C2nu#rSy=techiA<*o0lu6PRtBxy*_7PrHm&|(~Xnrsl&J=(A5BgCv z1T7?4PGHRTfK`T$;`eSBI;tqUf+9dZ@}FA$OGmL(SH*q<7rNqsg0Gn?1w!}8PpVQ$ zL7;3$|9yGvN0cDek;Wpbb$m((f0eCM)S6!P@oS?bHt%NiJ%|=#gn7op=`j~hpx$d< zlHnmmtg9PK_}9jadJ== z!P`M4yiXDew38#iuQ1|O3ks{@5~?YM3D zQEC!64p7{U&3=bL3G6>Q1jw4mD2@1tb3*R)?CQV85^n5)K(J7*UT?0tC6C7dy`^!% zaz0S;0hfB*x)OIR5VoLb5)eT%mxQToH!3~)PHIip`AjcX#FOXRj3Yu&uZ-y@Iqs-4 znWeK@j*$JpNab|Y;)1WGI_GPaQWE+IzQ+Ied0`WK61XpI%;fovHQg-M;HK-?5Zmqk z$nZIfwZsCoo5#DN+6c#um$DTra{RQoiIh}S`WPByY_^;tuD2>ZKzd!c#L_0RNj_6{ zuH$-LpH7>s-O+I+;_tko=GWJ0DOKb7^BGW#bBCH3>@#-Z+$K9#gmQK}alzWGx#BXf z1+6$ooUOl;-GVv|KqMly5>Y5aQGkiIxRaw^Oz$)=SsK*xC+ZeY+>_=YrZrA=tG@L| ziISN?40Ix5cF(&mpak;2W5NjGwNT)8vQp=$#E(a?au>`hN83J|%vBq-;t)r4%Usm* zA%vyHBxK|+c>m0&3`pVi`|^V|0O77Q%GVcuIp1|h&3tPm>YwcguY3Y74{U^#{K4Zw z2vN8+J~9_o53+8erJO)PZAzL=oceF|FdN~H#8up`;3_~;_VMb6{Ci9S@tBlvb>hsJ zXJCqc0lXN$UE|IkpONCqJf4RpaPG|LE$*2d`cy7ht+h2c<&#Ynh$^~1t)*7F`kXy8j9!0TR8^!WR=S;V2*>H zhY+KIlW(>D_UCX~D4lk5UjN>wYDG-5JjW6}$Zu>Rgxk~=vx5mjtkcCv!$P30v*WC( z{3P+0(UggO=*rv!$)`32wg{r8mJq$bpmK`zm{yyVp2%%vu{f z2^t#%dBNh4@!-kl_bT9DR}+#g0+9h)@4o|p)_RqThhv@`TA6Tsi&PT`#tcGGH6R$Z zT7WV4aIO&B!OBKY&b3B-uii!s{fk}ND*@R5$cZM76GSY)T+WB1R^1pNs}~W4(&Zv( zpn@AxuPQ$|Y1slZKN;T6M(zsB4SYx?Q#!Q+FHyIE-5V>Zg>jEyj)hcQMj+bOcH_y; z636U!cR)}x^uQ-$%RAui^*%ddyri4~5P8sydVtpv3uX3}niaBFymMhH`aFO*JN6ixZEVi+NQ~VMAXPS99=gc&az3V+}^G`7WUMj zq>|IPOxXXj0?Ks?J8h~`uOm;`KN%9%mBIl;M(2j&IC%Q~J{m{4Du_bx)j=Hqqn3XM zCg2M&pi?ho*HEvhQHm6oJ@9rrvZRoNRMQX%kTj|#34CLhC{?cOSzpExty3y94IRFv z;gV7@>MV6~0K>H%hLf z>kWjZ5t&C4BA@1`5*^!FM)wi7KTWvp3FrZ~HP>PooB4_5CwwDT`^O&a($k!iRzT!P zf3P|fYW^M3(N*|@z!T9%s;@-c(Nq;MedM&*L}m#-ahJM47zcC*;Q0m!$XWppxS zNRZOipAWbUgwk4|6 zGvQ878)R_JMU67Gnc`F%owWV&9jVm|o+udwkR+-`={l^Eyk@{dP8P&T7l@P=@P;_^ ziFH3@eKR;DJzi7SOXh*e@oMNx3{C!Z+!5n~J?^c-2EGFbMQimI;*B!S@wx&@G>y;| z23i2ozWR8pZ;-Rqnv5j3$vAu4OL&oQN1qu40G>F+E$;L=#YU&yw0M%%u-d*x=A*|O zu6zn8*yiFV7JPBeWg`)0jl`An3-;F&Bj5#T&5HDeo{qx{rx>3I9WRRU1_yOkF`!O& zrzXN>T#11Gi?wKBu0}KnX3#B9ps%F{pIO|$8Jn6qp!;P^bD=M-b8FeDc*$+;#ZBr? z7I+3s??V1&aZ*OIuft%dYHA&wdd_HuN$EJl>MaPX6+keN5_DO~!~!XMZOqp}QQX(b zuvcjIUQ3+mO4vfA94=jqZ=1Q3) z4MbxdX!=?|0h`%j>@x`CyUrnOj^f5keGZ1wF*MGt5cH#%yHOvAzBdYn5cfm~_IOE; zM0nCV@w>1YIw$iL;M4<`JwCf?7Z?%_64qip=4k!{Is~9D(8JY~;{x6fBigaWodQ*f zjtY=dNk^TVv22;#2b2?-YZWp-yk4==hqgXMl2tr<{d2qS8o<+Iy@Mt+bBXKk6du!U zV;c>i8-A?6Q>x4h{?SE0qs?X6i5BZ>WD<+cSy3q~?gak{@~=5*G5oDw*$$h$vtucdU-Oo0bQf)vOaf zj&KLX`tp+a7vFIF_<{hfIw~k`ktI8Nh+jK%05F0+Fy5jNi>&Ek?4_aBEOC53|0F>3 z%RTm-VqkR%>%#cjU;+ou9dIrR+q*A;^jvKi!T|@!Vn7wt(0@yv_G@C1Tp@8)I05Ji z;4x3_dHpZDu7hzkDuyFOQL$_ONPqvW@w^0vMJ-&FEM9NcsXQVL7LZhv`OnR1jfLsfFq#MysdCvU~-SyO99t? zrL1SvOOq2;5;7-<{J$BEbE8HtY6A&ZQ|+hKpCP@*tw!?00steh$jjx z=D{|VF$6_EZo$Kv6Pc-`1Ghm-h|QI(Be54^dS!^BtC3JSlr^oyJ7GZ7x)80{wd&Gt zz*0paaM$*FqQQ(6db7?=LWMjOO1A}t%sbe75&W=~EBxa`FgrB7fY+t#L<*U{TG|FD zwwGqg8#abgr@-YC&ldt8py$DAs3=il6t8_4#sC<)U0ZLhjwOrEw%}ljUsTy3kc2HM zauZc08Ypb?gCUt0<^@zHfJnZRLMYj<7;!{*xnT%{syMK*ysxal@DKaQ z93_(gQ!(p7fqf7yEggZhC}a=~U`2pGoMc74z;AK6v~fNZAFT(#xZDL!VuDiY5WI4p z1~MpGKoMK}^cw>qsFoJ(|5Fw#yVMaYD|R+%q4zf zK}i66?wsJoH?swb2{N24U|wE*2nGb|Me{P^3@|v(B8-J6&@S3sL;!sL-SBcm-$I!| z4X#F@$1s0$egPU?WeI@kj)l}rLesbMl>NTbzRAjd$_CpFIYv(Aie)7#+$jh&ZXwo+ z+!sk1I2o{lA%K?1Dd2R%p=L#LGCVWUTJ0&^hV+Blpj6Sn0>zoxR)_*hh6J)Srw4sg zRID)5)J+44cmP=tyg({981xueS)s)eNhw>I?1~8h7?jmCKk# zvB>F$Q{+%m3hbnn2X@ld#$wPOw22NEfH2?VzLUqU2r)N0mU=bDok7d0e8v?TJ$?X~pVP1tOM+uXF3Eq`gNfSN` ze4d%%J$*5`?xVzLe+?pzlRFuNaZ6Jk2I{c*QLU}S-#A{^M9azlgl(<6=_liI4gh|e zKtGiG{AI&}nAV_~3PJYwG;QZ|p{NiXR0Lc2#l|Wpq_r@C5kV!7GNE&Z=a@ z$#z-vL<#Dl;$Bx8V8>Mm%qD2x@@n0oE(`E#v)4bL^I)LY>&aRX4p5kv2#vB2?LW8O zMBh;SP~sbkGxUrUz{wdk@gsRtE&*vp7*ck52;+vV1Z@qFYU~K(*8}f7WPe^aW^mQx zdpLfI?veIWu87}Aco`IKA~o(}Uu(~~1lpjj-zkP27Ba~~h9Bj#5=c-C9Hu8FBX1x* z;6AmFdJ9gv>?q?RZCx&*M{q&8NO<$gywTBKf{lt2EX`bJ4to^kd=YTQiUX=krG=rl z*@8inRmEWuK^yY!F9&%#bSZ@+ils`wg{M(-3fOKtu~rsjT|@6baHmxX?jQfea^_H< z5e^c?Sau*5;SRB0H<&jS+!yknNPj?{XZ<+Wi)|jyWIv(m$ewFPh4zeMLLq#OJttvT z#=~B|#xqG30)Z?SuX7(w%mc6yE}4d}3xZ&i5+?IGQI?k+*rx{1Sy@Rdm&yNT=*C+n zwKbCU>JjM3eB$krZPw1=|KJova^$7ksZYLU3zevrNcNW>_Yuo_wbQ&VO1XeJ`tds} zV_Y9=Ss~&p?_8+_tHl22>n`$r_{@ykna9s#DURU?XP74~zXE`qF$o~8DNE$dKWClC zI$&tu4khePl6xxCNlj1qMGAxiXfRvTg#D&5sGM)Toy8pz3+ znHLu#i3U7f{0IL#-e6txtA@);g2 z>0ypQTeuN^s8zbrB}o9xtj1nLCpnDb5uvw0Y?_3P@5DJC1>mlEqm&q7b5jh4Zk)$v zi9(aqaD^A?5=~=(C%kt?isxU0W{#0*Uz9+9`Xz34SiQ|<>TzcOK@tju>p>EX?lOBkn6pdMq-!5I3D4w8cxIqdFqIGbrbs!&^mG9mtmCEWMRF z%s;cl2e&uvzP@^3cG6g}_nr}291>7Na5sdT1#yv$m)b|iCWprMb6DtiOWlxDb zxeHZ9@hDIb%_Rf+n1TqKvo>#$j>??Yv=OfpBNf^7Bo?CfWD%Sxel538%fGfO8cQ~i z8^;ImseJYnf{Cg)O7eOH6PkY3Ge4$_7(-n*+z>oj4k8)Pjv>2xip`=_xAW)kB9MY# znSe+2VQhdR%F9*XDiTS1!ZHLBiW~h5Av5yEjg&oP$EE>n=gKdx`)tODCw#fcc?DJ` zan8Oc?1PS=3S}nu&GkzS1|%7!&pX^Qztn^^PYApibVq+}*}`97DtY9Bb4}zt{rvG# z1APNS@B7ip95Ei!CE-v#nO1&h+{*pF0Q)2KzPpcL2UU2lm+16IG4rgtO965&k^{=~ z9s8*^GBr40d91F{vaKs(zAILoD>4KIKDYrznqal86~q<6hZRY-C+(33RsJwt!?GDk zs&yu<_mFnz-iqp!p>8>EdnpqCWNH;B(EnqcpwCOA;DI zqX{jTK2OMVAc3hUbNf%uIiFOpkijIgObn8$q<(P#^#tnJt(!ZE^rwGHjv}RqQ`<;* zU3J*VQ{p;$tr2p;1&&oMFxgjhm~(4$P}5^7h~t-P=X;iA_Ps<{=jndmo(~@%qTxq!&W;p9aiElpIt3+9(kR zxMmby9;ijc#u4!1rDmB`EqoU*@p$%}qr34N6}5$lvuuyartQ5*dD*E$64X%r8<&d@HImYBs7%mZ8U?53{~u_-FpqbF4JE z!&rS%hEQh>xLTF+s#xq{waAmS?4PFqi_oQ&SagPT^lTzgsaT&9vtGBRkXYJIO(MnE zlCBZVg?LkO{@opz^1Xc&7W~j&m<{i%XNoP>++xB=`Di9rCDi$xQa;@bmcuR)ub!Y9 z)#WX!=g1n!rinZP&L(lC+5=kvy9%=+-eBKhQ3cZBHU70(kM9Q3jn<_19|vea1!GnH zFqCy!oNw#!`o^Re@R4bpZX~>a{MxW2u()+GIB|f!Y!YgD4TwCc63Qf9mZ^fLr|n$` z9Uk`2P_|7!pDipSq}C`%Mx(w=)&SuSN7za1J80-TzC!c}Xhj_)UefNo$q+8%f(T9p z%e|In)j>f{RR5d()9D>+*Wey7#-IB#?Uk3q9l~iNZigj^F`9sDB5Csg7EQ)csB9Yl zS{JvqZ}s$BW}C4hK&MW>3T5R&Y-XpsRN?~JQPLh)FjHITvwaRHDuJeqe0S8Gkt z^3#{4ckUAtD8C~tV)&G@SAhF(JduQlyJdGR;6S4L3ovHeiBhuBkCxFpgO;IqEMf2s zX-lgZ2pp*!g|kr9{&dvm`~y0iJeSj0M~xN^Ry2WAC7`if#ScBMBtjm$$6-lt*gqOl zEK9RJ13_Qb0UI0fdkbJ*wt@n$Zd!befyD@HK92q{ebkxJRI-EF7Ry{sh4zP__lJrJXnaIAYOG_~o`~5Lb~ohJT?DC+`>>aHv0UlrHDQ;g9UxF!9=? zuK$yvVx7Ej(IT<qdSC0?k|`o6P-q*R85WUBW6v_PdcT=O>(Mx+zD~9K)cHc{@f4D?EMSFDNg0 zWKt)f%cx)|Atf*O>JF<$xz&iy-;s&G`ct&5=nh(@+m=l))r=rpmX5-{mDCJPfYk%j&UuC2u%z&xqJeN)5}_KbpWD%)>3vu4f? zRxSFQ0Iy}=uqr%hT*(S}v+@W|^BZ(IX2KQ0$8u>c%noBuh9<0F_(gwS@rGTvy$JKw2h5kZYX6)X3Gu#Jz?I*!5(D! zn%tx_>Y4^3c+}BlB)!2LC%4PZyGYdtbOnc7B?J*8Y_BSk z?1TM{1ON4CL24H`#4<$;$AU^Cf9Oy9rNWS;FzPs1=G_p zcZ|OK=l+n&LC&WQTL15$nusMB`oFUWZ3LE5m8wyT#c~X*O?OM{E1aHgc&49D4Fnay zqz>6YitqMK&8GpIk$}5)Wq4EmZ+3*0)KXB~?7Iy%WvXM}dIbjsTX+wYHrarZp=(|J zvbG7=Sls5mrhGnjHtKc<%I5Gs}Qk==J?ya^n~A>zmZzYNHNA~tY*R?GKB({nQUW1-a)vc z*K|@~Zp&LSZ$S#De|lOJ|NfDJnFaGpJSoG9R^(=9a~grv+j8w4U}A;4nZ#owKF_vP zzG$lAD&sMytmd0$y^B}NQ!TBg9?12U<48?{Fp*Li$*xHX%dCzIxV8OKDD?Xl@r%*=T^}BhoWQ$)ron`Fe~HY^!D4>WWG! zoyDwLe_k>A{k07MLkj|+SvW;XEpKM5G}=xFu(eGZ&?i6D(~y{2OzXV_pS+|1b$~H0 znx#9T(}vQr*BOlBNsmUzdxf}D3d(6yOA8CiwCn2AwrO6)dLii)UWYF&WAI~Xu$ zEj=d7mN91P>BDZIH1Dh025?f99vmm7O=S2{t-{U`f7zr7x}M2q5P~5IbmORx5IMWE zCki88YVQSHcV*6*BEniHRZxX7W9O#uQ0DQge66fOFmajr>YX7etas|_bE=7hgevOy zkn-HB^dTYV{jM&o!{W18)@n?Y>cg{)ZHPdd2kW3F({VLW$V&vC_@6HZJ2R)+kv1!2+CA)2_j)bmH%PlP`Mf8 z%sdf7S!5OaEUu)>L3@CeN>jjm)JBx}_c;nV?!cH+VH>q+Y!&QSAkHCNG6>o1%tZde zFJiCobrS^kIv-=wRxAdx3_Zj#6eAA74>3TkY{5Basm42>3h)-G(Kq3|yAAL)wVOIC zV=%f)WapVuN3qhP4Iehp{JDRx=eWgL2^=|Z(67xPV(Sjxpix9FT2I_Vyd=#X={X~# zW(CRHl>gA0a;;(#g+w#ovbXdXKa!q|I_Z_}cY!_OH{Rz3bIo)X$P2;Qr}N14lLTyx zTvf{H)z+2=IQoeRd)_~<+%&9Cnz`K5t=XHCZ-mL^Vlktj%CvR7;LOH1K#u|o&2`DP z+=V-gj?xZ1eLEvG&Z|{fGlc{auhJX_!jN)~C{as1Fm4 zh#C6Aco!uYUyy42eY&UbPE=-!f*(!3YffEf4*m7lCx-S8>+|=p_WrL823viB1AL0a zleQpbG;DSGWpulxlpSelM>+ICx^#b>Gvi@h23y}^YpxH+InBHYNB|Hftcnd9@SRZA zQ9HD7zS?HysOz$=*1;8jQI}O$Bhz&vb*7{X)b!cuMDGNGTm6ONOowONlT$;9w=`Z= za^@)LT-^J;#BLIn{w%|jGl&0p&(H1pK$!STfZ6(3>OON8Un<@r-J4L{Ws?zTf2P-G zFoR(Ol>m;zy6vzC;33-Q=VxI?CmOxb33KEh*e|U5&5~eCQHT2`Pf#51mp?OW0i(7# zpa}Jbk`B?4FoMvNm*s#mrA)*&j%{k?ua44~sF!hCr!$zvMEa4LL zXD|Uik81dIg>RYgVWX;o<&H=*P=Lpu6t0s=FqHWMv_?lek9Jw`<&I8+T;95Yqe;zC zA-JCdR$V`pwCj)=Ew&3QJOdIu%Jf5~4Z5P&nlACw047(OTVanOBaId>O2*3xwl&b0 z=}&soBB*L??|DT=5cz*Uz60^VCx%I@8S72qLa}Fd?JifDyXPxeQDo2aj z&7BT80V7v!OTjujrf)_wAKr2fdybv}Y;FOrhYfSx1y|*sBU; zU63|lLAQdk82j>QDs0d?{OhP?V)u9ZkX5n*L?3skmj!VN?D{{a5V(`@zIBsE^t{m6 ziozy@NfV40Z_^9Oy3L$XKo255bR5f&Qa-qjrpd&6%l@6gc1B}wbq^_(|LxFkM-j1a zpkIGXZwVQ-jt+f*ye&oDh7~}OH`8GV`Q&1ZOfHzX&XV{UfgF|M^=z>g6C9kO?Xf`lsgA7p~(ID(G$ihA%t`#Xqj-Q)h?84lIuA{`tkv{;|#Ogc& zg-}QFsn!~tUKs4^w{G^yw61J&HUeepJ@iwgeu+LW@%lJM!*sBo1)l?gMcog54b~AD z&Z!t`Y)CgK1Sd9Zn-|Mw3VleM=pA$cp6Z%OTPO*Z4?hx@#n3=TNKf&`YO#=V_#5L` z0)&G)!SL=MXZK9e*HzNWXdQyFs@(>{ggq}vhm!1AU6z)_D}9d&hj}RST6pf28|;-d zc~uuHs|Ors{2&oQc-<~;JEk^MqDnSDIP;IWPRqgY5)<4;4*iw>(97Lk?nRda=K3XR ztM(M^Zy>velx#xiwUaco7QozN_!CTs7%#E1m3%9tkXupHpSp+q1<~}+;l~t@wal9A zHpX_M9D(R&d2HIa8m`0eP@0Y0B1Y-ab_06$(m>etV=lG$JlNSWXITUY7@-IMxZMd1 zbXq<%S$f}z1;MK4H5|ftkM=A8CQhG4M*}j2dpG9&l%OkLg6A|n8#lRF_APRsz=epQ zb{GM_Pm&c>Qu&Hy*ds6JHHb4n{N~L{l%fEh3Ft_$kMUv=KX`e--Jx0LXwsMa*PqPm%QA2<}tGi}8@D=DR9LbhD}!dK^%k2&~e$;6F}%lTR}B;cQjnvD0x# zf@0`vo7>IL_KGPu*z4NO^Jt)fbku$vPdRTzp@rIJ+;%?*IC9+J> zcMm;epaeBq4i^@DLvKH0&NJiR-o387_?x)nBZHV5&BpX!@`+%p>%b<0HlqVr#5Zh+ zc(H}MaGyv2r3wLZ`+3e%E- Mt`lg3L+qk=e7!hW)#5s%Ly7qd1dw9B46YfU~9=k zA_i%APr}QKK{^4HLbP-)E85Ofh?GApyJu#%hw%nTWnb`BKucpOQID~fW{1`8+c~5< zqd%qk2ppkEN~%g2;tJi~M7obNlmdXSOxVd=9PpK4aCZVot9z*zjJ*yli%%@K?-GYv zTJzGOgf(!i9au}~Cy~Qg#ni$nP2?v;hxoR%LRf_K;{$}|(-Y&x5+RSFlQJyCk0Y-L z(s&TO85zqsHWrpD^PkH!Az+uSZeKc2^ivJ+$Ct_Km5hM5q)ks=OD-e>g7Bhpo*6jp zlr-d*V+>Udrk02PkUwu%=u*E_^l1c*K4XsC#R%uno1#I}{y+cMGvV*2-v#9S#KL={ zC+E97WPk&_V^Ls77A_lrJP$YyC^kvlrtKkoUh5_swWtaWAEGo86fG zbUsRc!f%5pdYApLVV6Jj#Ud{51Y?b)gXuTk#3@FXDBlL=LM4#-2@IeWD6(ElfR~HG z?plp?J)x7EK8OAE@gBdQaQ_AyY26+!=j1NJ1WY?jfTzb&`S_*I2m4dN2N4En+T_)a z73&~=Nc5j5*)mPtd^MP*k2DZ*v_<$vI^TwMJfRdMN1Zgmzc;P9%IshsGC`NQ`p~+n zWSqd&jwLtajVz4lPWato3dFBhKU-B=GVLo*)7FS06wqM*ehaRgNV+E+ZZyO;K_v6> z0$5d4VmXk>E1zy@Vx$e@@!U3yuuI@FF4pqHZ?6v9g<|&pnbL{VZt9}7mq-`^SKY%@o6Pz}qyfZT?iO#a zg6tZ`E8@WpxcH2QY_u z=E>zDg#g-OUkfgN%gkq{LK!)IOn={XI2!EQKi0EBBXCP>fsNnXD)tq@D3pj7p zxE;muf^;cDR5;*VHU;Wh@YE6g6lTg}ovk}CNfqz1@ck2L> zB|feswF~0T>(CeNSZ7W&KB{_*StHV_Sq4=>%!Kk>06faGiNK&I zdW5ab3^>MZ>L4(;r;XskwU7h4k*8uW9hqojghEp97U& z(12zOOumeey%`aWQsDe|6{Ot+YSe@ig8M>6ghAey#%oep=fiWIEzx{Hf?8Vqq=KXW zBFrzyjtOOZ&MEQ3=*hLj2k}t^4&?^BGNs7Zz$X);o%$s#HhpJO2yse@U0pCo*Sq@7 zHl;ap)!c|L+zqZ_AB5ecUo0^fT7i{^6qAIo(^p?Hcx#ykKsR{61t|dnVJ1#QsoaJ^ zv+FbB<6$L}A68(z?ZzS#iS;S6yc=R6p`^IFpt1_b>H-0wUrF&(Ge@} z2E!gOk3`1O3QrN~u7%n0CcFhw)q+x5G18@c1b(?38aNIfmPXrE$9^4#xujp?MK58g z2A@$2f0rl<%d3smi5pniKw8I`czqr?C*a+KeV7}*@)3} za;M$;LOf{oW-z+4##IgV-~-2-K#7*r;ZMWaASp&7L2*9^uXAg8gfd(22yJ2)mc&l< z(S!!y2n5Ok?EP@I6s~ASfue&}ts{O4|E^9Emv7l^8dyny$+UwP$;}wiKu8V{xP15B z5hj78J(pj}`Mir>TT>bW9`=v;_`Ar0TYl;0cK--<|~Tkomi(9uM}z};&=ft05$BMR}flz*gdNYh!X3=}cap;+!EbnhJI zSpfPI>J;EUIwU?5InVPc;cMrCQqJ^ZShwE+yGz%e^H@AO&mjPob-^R)kdW$tz5o}i zpo;4hxX&fMwtNiUQBIPoE*bXnuA4ayIQi!j@ya=}5 zrV`N7da67?)!BkTjQ38vbBSwd*W>>@S%@eN1H@oY`g1<58dsVlu0R90FTpbqHRlJi zT=HfJwW@|23YMf%RDDsC@}WZ<^tX~(SJj>P z9`I;rYy}<6xSTpBAN1iO?qH0>QD#+yI<49U*QS8{iY8&qdnFnz?Dv6F`hrtRd3!`_aI8&P(0B5hunKv_(Y-C!E1tY7RT+D^8mnOx;8RiK_ zTmvRlOByhpjQ7?MccGGb$g;7$DI$oafP6r3qBb^@sJ6$tTHk%VR;UQNN8(Sqw{W`DD>1J zKC1ZlhQ{yAv=pScU^t!eE>prMK)J;-7(57{bZJ&z%;VJfFF!+-Gftifx#2%y$oM#U z)u8mX{pu@_)klc!D#}*|c=zL++T90Jni#FBgsB@zTwM@`=dBd8I_^aTa!1uDa z>BK0o!dna^Fp5RxcN0~mQlWFTm3IkEr5Tj~$cCj4|D1JgYTx5b( zKo(VP8^gsH=A2f!e8Gz-^)hbQEL^GwGb31SeT2Uo%WhXFDrJ&Pcd?hP$JK{+F3BI^ zUQ17>{Xr&ns95fP(Xrzut$kfy8{klrk@&MNf9>{z4;G2ZP*P2o6XC^k%?~W%jb$% zZy>3RFDzQ4?b_-h9O;%+rRSpYw2uc&-hm%g<`wH)#fY$URLiHbHDwKm#R&51kq7kU z(74Ypqy$q!-GuveETj;Dh1l($!N?(YF4qjW_F7@l)pljN5}?T<9H@4h8L56zjN!>3 zeBUth@N^_;1883*hQ=Tz1*6Pr8CSUmj0S&DfsL(rz(yvZLeYME+!UYhs2}K=uhz`GL7+@{0v^nUk=E z#$PyQ{bzea`ZTVqM2azmdWjJT7{@>r#R?QaXlKLvu!`a_g$)h^q1}m=K?B(u_(ieo z^y3MsXu;Jg;&<7&=?t{R`a>yPJpO%+8v{OGkbePuM}7YF{~AsZ{<_%vcl$Np|Mm3v zs~_+IO=~krFF>I8@0~ROJ9U&{>1;cF9xEy!LuzEhCNTkbmqH@MhAHaY=t z*6c4QeweP@^i{f9%_~u+YG@P{s9uY&6qJ_ch;@=d`KrB7YF_k2rd{02$N_Y5_=I+pVx#ZTtRk6d zW{f1vI0!Xor=oV8pf1;aeM(NYL`wr0RolS1-X8bg`!>Z+J#ku&q@dxW(nq%pcbRGW zK_p5W0LFjz;vbMc$Tzo~m?|xX3|}p$KkkFKN=oB&D*UZV(ff$b?nHxzT4g|pgPkK) zjD3NPVgI!(+|&2I{_k^pFW}!abN}~v|JO9&-}9^S_xHMS!1u2|1miWRFsOk5@s7Qd zv?aRJiU6P>qm0kaN-w9p)$mM8bTW)yk49($LeiDENxrPC>xr^C;IU1zq>zYS556ot zg&T#GxhN%4dUYRm;g6E&$(@9b${>2eyPIg1UwD`r!7vNS&*rmc@yb>!Q7`^bGw?s9a zv+hj8D2s?a?!J_t%U2#m6A4?G=PC>;?lOfXDlDu-Oi!FEZ;*cAN&~~~@ck;k%~j_a zTGeeDlp4fDF&&E{8DyD?*J}^`uKUx`m8E5D{o?<#A=IERI%?}25=j+ga>-Z?E!qb+ z8~$&rFP z$*5jVED)LGFN9LYeo#I>pAgr=8bt8s6yPCVjqJ7AaEdS7P(t+T3>)GW3Q}`nKJsfXO zz1r5$dm?Wa$>iQML^41z+t(660nl*HmW_fyx(dX~BEuNC3UG8j>OlQAD*Pw9&^G$J zINpfiof6-g*{ir4vdu5TDf(p?6n--u-SFu!<=F5e`r|f_^jVBAT9LA;(=l$;kU=-s zhiBeJ28s6X;U7MP)On-izpfl=aqNbgJvLbLU*}Gx-$!c~2pW{LJ2p2^uE%jJaT(Rl z%s@Al7>;d-UBI~C{4@p%Y8m;n&~*A)cKX~+?}Fm|VHx%ycMK2gZv>_kenvy8e17Fd z6&#!L`q!3)XYw>T9%O4WF)?l*9Y+3U9)qqd;b(B+Z4}*Vw7I#x-;%C}O5lNATns=?9LAkYhH{MzAns%X&QXE4LO*NlAla~_e&CO#pRP}(H?7N| zze_pSh{i0hnGc%T}2CEao zAphd+JaO@x{PF&eh1x;nyT<95C+?GG`~gDnIF^_Y9#uA*U;EI*4j|`HR){e8>tn<>afbg~BWzz3FXOzge`oa?cBIZKkfj7wy(O zyOjD@vt9cp-lU!6G&Vf9I8KPd<~0BPwK;V#-c)a$2ET9A1L5lMsylB6ZmQsk@bd?P zFo4%B!wY3hCgJ7bJJVyD*5fb$B^7+w30oS%0w5jZ@-=|!8PkPITFCM^Z_LDK60l=PjbRn$PvRqi}(mo;x+a=pI{u{7XIj&H1r7PqDO znix`TUu}Qtv&;bq@-^DZdY;dIX})!MmEGul55(GRe_>iXL{4+Aguc(TPsQ6z7%LT+ zz>0C2>NSA8NgSXBQ*Kq7r_o~6k*kZ<#w&mh^086sJrw)&1Y=G(1`}ErURijpY#3ic z-n5s_~VzrHcy z_*6#Zo1a1sO2?l3(@Xsg%>ZX?REs_Hm%#yIpiIDkbu1x~XhwLcKG;YEdkC{>qL_)% zQb1SEHDlTCrWs}~G%bv4t&Y4urie^0V#9P5oXirqOq$q}C7-xN4$OusQG%2@0v zQHZ}zm^w4Sbs_*rM}-fD7_2VBjiAa&+;LEaFD78DUO@8`N($G%u3tcR%d8?(p{@Np z3?I4+%E55+1A%m4-wUfygkY&)L;-1LNdKn zsfHk~)Cm3-ud?8SRyqN-f7f_}{F1i4fkJm4T%-rE504hjd}>6dVVdD=6_sgH1;!W> zxR8focm7ppHO>~zEQH6QixI)$iPvUh#rBwr^o~_Gp-Dyr-Jb|2srul=F&?m?+(tWb*!zXyZ;YBDVi`oLtN#FX-ymac0mJ^RY-GF|nfVeUdAb&VB|Vu7WOTau(@i zLP9y9_HBYMNN@-Q zr*U@+PSSX=#zW&FKmrLCAh^2(cejSd9YP2MUgyrNx%bYzA8)NU=f^&^s&?(FUGkl( zRlD}tk=43ix15Wu8cyCptGPltFequs;t@(yT>J?#sb;EJGm9|;Ex8nkW6Zf0=&&zoN!ai2>lp6ElI)F&| za7~O~fnP=r*90sthu1ea2lh_#+dKpEhR46xKaWY%d_kty{-r!sSSU9OV=ivb{;H8a{wJ*Q6{!sToGQ>0DIAy;Yh3iCFe-YoRaKSe5KN^cK{UMdW=}08bX1qW;8k-|k60eQ6I zSID8mH;HBnVQ=112rwa}L7*5h-0T{~3!;2{orblHJ0H-{dKF8hf2*BoJu*KlYJ%1< z*$v%l%;S_1emAix^kJIkoc;psHHV=tJLA$yAYYk3J1FJ|1ejuA8N53nUoO?KQbBK2uI1U{8XD7SQ>uc$V* z4u{cFjyd$n_JW^X@KfXLrx7Pvc*&m5j6O8@*4Cgg*efuC@;Ao?lF`_QHN}Cbh_!CB zCyQ>kJ1P~jk5HN(v>&*)ujFn&z!PF9zRWs!r%OS~T|nERBqw(yQ*LL5#;gx2e{Mz_ zk=rD%k+J9W1d;~xwNeVfA*LJ3r*_~`cN3H3zW&R9{UL{V?LKA0wF!MlFF8(yj{8hnozxw@L zF}-akTQbvwij2+sWO*hcCd*^>qqJy_X*tbsGN2xXsbH&&6F+yx0>i zrhPzwb-aL%gt)BA-=~_SB)T`^>%krvYYRQfS_kOF%H0MYVo-xn+H1mh1|8CJ;B}Y2 zt?HXtpvfQXa)OhKZozm6m+`g%W{Q^wow7atm`6!?iVm=i8cLC1b3gNZUUv?4s3 zHzZqVKs(B@bD~v7317w=u-M>HcIdk{@1*jY3h$EU zm6lCwXQ{|@D$9(iZe=oQ)Rm-rw%lQwyX9OSCPu1{)_C^<-sl2%Z67iYCo;dhI48)w zki2;U1Hk|!IG~PSmW#;N7zV(&ivgUF@{}&E9)7&%rz-af+fu46X!4Hxd{|C~6!aUP z+^HuebzFgW!Qp-%r&;)Nd6q9O)7n1(2=NNai)K}$mW}hBT{5w?04< z%1a6vq3Ywk&reBKUh5Qdd+p(+eOcAdD)v*GiewRL^b&ZjiHhU<-xrYWnZl~ z&^|+_a(`a2uaGb05Y4oLRk+Z8Lv|OqVEYZjRED^Wi{6#{dKq`5>NbrRcG(wVG&!^u zk7+}DAt!urnyR|lgS+FNqQ|_8;)UF2jdUBNQCoyu{I`|%f!99aAyU7Jzs3JbRG2w` zD*2cyNd-pQ497M&))*8~8o|p6r)^hl^&BdNJSG~lL?*dvJZ!Ht*1z$*hQK-Sg-DzJ z=w$w$1Y!NaL!FLDdtsG(bIB3ADQUjp_8K+7Dg)0yo=U-?xDaD$h+p;C7Wk0((7x(S zy0;%K({#7r?Rd+|*x^WZ+`O%K6)$tQeXuz2sdnz1V6Oa}j>{j6Q+Dyb+VEDgqU1v9 z>;e03gwJmQP0wM!X1&yXL$+|7qlfP16&}_D`M&3~e8Oe60ofPZH@A_J+P*cpB9w)p z91J)ysQ;JONMR>0tSbpXO2Bg`PDPH6g0hVLk2wt+p8}A=9zOwyA%4}a7QFPPN^!p@ zinVp<8Yu=!J554~P4ypxF^??MeyRD=@nk(gjO0rAL@zd&8Ecr=G!MCn_z&Xyu^GQ# zvNM+%&+DMW(;X{zN^{O4sUJp+$-3lv#D9Qf*A3k;<#aCV3;EBvwPZEBr&ZPpI=LUQ zm2Hd-&)H3N+x>#N5ZVC_@|xK!R`bahX8MXON7kqM0PLpH)I%QihUs6`@gTnRSEV zNY5#Sv=loeMH=g@G~6Z5dYaBNB>vglR2L(w?Vme-lk7EQOKYOz($|a5c;Z7DkA5E^1Mbs@FWgGFahm+Y3K@(md`9uq5eq7o?8$aHpsNY z8CQ2@RQ|5HnUD^EqGYJoKOV+PP+2?rR3umH_1Y(8E-;g z0rfPdRWgR3J60z6FXKjCEU$Pp{w z`+l2AcZt|R((PiGi}OXNviGy=bFk*9j@BPUYlXqj#uobK)3k@Pry-S3Ub$Iv=d`*n zMReZ%S;<>vrB7_8U7NdQi+IL2VSh8T`DydJ1v=_GX|jKO_i+SF=)aKBprG7$z&a`c z6p$Z@bQ0cCHyHO%<&R~!%n$6p2a%ysJP7$r;@$z!>!gVa)D@g5elFiFcnS!@<7+t! zGHuWJ1tZv1`sx4r!@a)#k@I-}3%2dq0(TjdFZ7tJaSdsMoeoF>f={5Ke1;b{F@%CrB=i~y>}w~TM?T0 zs>pwKFT8Gu;WU2?99(chUwVgWa*S5-^P{JlV;#ZwB}@}u8TilVWiZ`p0JjYLLH_rp z!7^y?(qK7sW@)ejy1g`53B6hxtUAtGm!Ygrv8I-&PqC#ou1~S24y;dcq|T~Oab8y6 zOqphdG1dSmA-i0qX)aPt_5VtOtEsp$6lQ~Ba0{)K^mt<9M0Bb-xD1r~YGT04Lv?xo zALLJ1nUl90`@G+O5*V}wzz$=m0kFU+03O(Q4FF4UzAgeRHTW;2 zXd!}S{;BtIX+2{Tfw8M1nN&)j0ZWq~Ny}zmMqqW777SDi@P<9F1u#JJwwF{Z|98od z?=j1~miNDrv8{p@ar|eABk?}h2WNTgSA7DqO@3qm2;hIe{Qq}--Y`JsWs_a#2kQtn zmew!KcRg;=_v`KZT~KDoc5rIrI!l+cMScFvkN$DF#rHX=SJTzhs9WY561n847h&Jc zHU=zbfAtw{ty!MK%XdNae0S44YO-IuS)L2|;DS(vn5tKTg+Ac8#nXC^hOE@^P7R7& zO7zz^vdp4?bS-0<@0Eg>jHJ12(elaiSF4S*OHxH$yS85A*{rkgMlo449MbQO&K5hnsaH#_2OS1a z{=EL7I%v3@K(ju6K~fVrpKXlVUbbUoi(MsB8+qS${DWX<>4yl!!&AFnKeZ0#5KHSu z@Jmivr6ojY`6BFsV$=`^bDy6`bY{g+bx-mNh>pPSASre)z7yvPIneT!0e;2 z{O0|3^{&KyO!hvcZ8!VQyQ)@$|Kb99u@l`|tb;e-hMb%i;Hy8JcGE<1}p zKD_9;`w`aPBCle7`|H(+#yC2b2s2nQQ>e43mrlx9;E}Wyb=v(-M_9N;Xl?%xafHQP zhQ?jfGq^J0XTchHbQU#t!3z(m$YQlElR#E$4gxX$T+Ph$lz@lpKPzUteO54sBMcYz zi`?1oS~JWm8$l~u#h-FFI&?2%VNAFvdt&7jQUG(C9TJ2YX4u*XOW@@G(B-Q=Z=c)Q zH?#D5OET#49~HU@7wKLC4B9}D<%Rm=Y?mFskNsBnttxc|H`DPLWSiR`{tW{1D!BP8 z3!m8P9lUdFu>AmR=k6HnvOp_=5|pGi6cn?gcYSbG!+Z@@aw9Pd)?IBaC;eI&*cPdC z>B{Sz&q|kGYI40>*=*!cOMD$qszqWIWAx)&)9L=B0p zeN@o_I$LCQ{6AZX=AT<_LrM^^FdR;ex{9K5Tf-8}dF;B)Vs(lGH*tLQ!x9d)X_X$Y zMLMOS0uFzhOVb{w9OpJFg*l$Y&6!})+=#qVM^z1r+0;xCYJyaIuM_tDx=+pw-)HpE zN9_+&!3x^=n9!*RuisQ!3{T2LS+NCZWA4cs}jpcS8fNt7^;_-LDV4m zk?gn*ePVO(?v{RU7N))JhgQVQ4yWWEkeZ!_EbL6Vkr{HVzcP!!g z1t%K>he4>bgZ8jrM)b6i@1FFFnfG9a5a|}Eg$3dnkCyNB>zGceupV>u`$%H%yibgh zyBpdH`g*2k{q!*>I_Vw!ihoKgFHc|I^jl=%kdeP(6u50d(3nutDjl(jXNeq^$FTN2*= zv1L1TF<}KN$Ff$$0qKZ&`pRd(^_+o|_}ms#pmcDNIvcmjhnuj3t;szvx;fQF6hR%n zT8#OZ=P-=kC2D?=y6`Y^o_7`K$KHA^sVSe^24R#24f8~A%t0J0D|3BfQJ(wmkOh}= zH;thaDseO=uD>+&&ckL(1$mEx3~fRbN$i4cG!*VNWXID8IaAt&BGqVmzx8<>GtN_z zcXP19s09dJQ^@um+Xs^FK4MD3*n}0(HuDwHt!!s4cV%@kpA(EVQb>aE!gr2F74=s# zA^FdL^PV-oH|^Ei2Bpz_WfVuse_^ZH84S-?K=<4}4U`sXeZG6Y;Ex3Jy*b>OB z0sUrObId|I_IWv4m))w8*_RrLs1~7(1J2?J%0uM~K z5ciuUSps#fCC!nUl6w-m4sogqdvY~|R0sETn2-XEUhQnGe*(Oo0T)*E-h1D;ni7y~ zj)%rj*fXyfX=?j)D9E##N`t3xUV%Cuv7e(| z+NB71WA&S=I zCrmoa4qcv(NPw9J#{6Dj_8lj!lH6vKLu1dx+Y?u9MM+ikX9%VD#WE0e7O865?Prm9 z)>^LUoOds2{z|LntHcaSj~0f!e0Iq)FOh!E(4i=q{vxK>2PTC2q6SR$0h=!c9F#;T z?ESusrGrzU{JWKbOL>d3PBxDuT&$48cxfMm@eW_cdVxuSWSh#~2K7r(uS+Ixw#|g4 z=;tb3Lwi`7YI(6mE%nfm!IXlV2a&ab%g8%c&pu!R!4TqL*fo=XJmU;P1(%c=Tt`bN zqX|D}$y!hkfU*xISaiEc4^2lzl6Nsqc?h68cqH(~mfPG;+svpGGym|j3YT*qG4SIb z&59dwXURF$L2ngzi0^gmbg8Gy64Trew`(jV6eTxD=A()war-+qbeMa;Ddmd|`u6uN z5F(P&oRJx82UQ6m_5uS}-rX*Vb6Ny7W}zc7pny?#mdi!#szvCL3f4Czow=%bhW!MF zFZ|^uhE2+`jCY?K#u}j<&1!mPM;3o^9xWs_;QCsgDSaz}z>4-@LV)_0_q`cJWJ-irGp8T~B7CZuM^FR#-)xiBG0fj40mZ1$P(I|HiV!u`Sg&q~^99h@BB+W2WPDxnXl zZrMdc^bG5;)l}TTy7r=cR)dJICUsv{GTHSg7FO=hAW4YDatbN=rhzYk+0>I@v#bgt zHi6Yb4hVLN8$%#2)&sC{#5MJhXEZWYr2~Ax5U%JwD!aW8>Q_hY0kX^+XCtdXX1M>6 zBHXlsQ6O(EM@Y8cTCv-xRcx0_@GM**nGTYBvuCZGnQjo&DQo+;`ZmbwtATxYGVw=m z2H)psCL>i>6odOBRj&@_m#B(cafVhiGZF?gnF#H(2s`fbGrd&YXfzS>7GuLTFibq6 zi1+%MgAnRZR3x(A1Kd5V_9f=h72k#-1qAWTHqKHZ|CdKAcsAAMqsq?|1`&*$=(F`p zRH;umMZzPRQf9Alkmt|-g_OaQ+;^P|DJpll=gkX*_EEB#T0!w9$%oRa{fYeSb=#6} z<1zQ86wcnAO|5~Cu%=Cs!|;#&FtPc7$2kN1laK*^*E#i;Xyp7kwcvYIk+C%-lG5%7 z81{d0&-SN~A~Np$Xz}|EgzeM2PKjWfaJ=K4;pMjhtxY!eg_~jJ@;FSb^}vAIa)2_}G2kej`>b|1+A`R>tj9 zIQj!^Cbu5QDVxgMxI*6=_B;T$sg4&bf|4+N(d_`DpXy8O)yDzK z)7MaiFP|%~MC-vJj){w6TPM-dV#r1|KW6yjjm368*pCP%v72ScRUFkk8r%n6f#qz>tC?Cwe6g`Z3`OUG z7xP@P_N;x)&QDvc*~4|!XCjs=MThkbH`wNM6^Nz1gOYu5UirwgwEe0%G<&-_x?V2q z5+XU*b`Ei;c8YJSdYOF4Ufg`9$ElOoDVI=quhYB-T&wcY+c0~Lg~Ye?&q`~{Ma>A; zEMF0Dd9T`;0h56>BcVzcs#hLR*d_6E;BvCCDPGqiceh$GuYzrBpNj7&CDox?tuPZt zT`tS#tcj?&NbnvkVPi`^;3l;nv4`iysM}O)0-0HbYn3FxvLv@^N?cnKG&lQRikaip zDd{|PGcBrQ-)B}un9*ugN-DOhl7P^R)_W`TJ6-M-%$-?1pI|r;#xSN(4Ou2w#=fmJ z3`P9L$b|}^!1ZpL4|{@}SmuxTF1q<+oKegOK02qy%I;EZTw?(ic%KHmOny;WGAa{Z78$=?>fIr-E^#y3(qJLy2XY` zjQChdqr|8albre%y5d%VTtnXG*h%+6_$6dndeMW=$l4+(^lZn97Lh2&EVMROvYFim zosPH~6$~is-Iy~=*x$ep6I%mv6x$oZuNas=`RZ@bQVU)%yL0x!LRI1#+xeF>)d{_;p6_d!!UHnPYShLG)u^2%bS$dAmV#t{LRxjsn9S>-?k zO!4dBB882tJky!?bk7d%=9{!&-=&ru^>D8QjTyoX2iHqfzOy%$Y^Z!|&)wZPaB>$E-mfZujgH z*ylIRJVePsL~s=29Yd>ols%JpG3MLPv%nQTtZa3WvEXeem)DT*gSXjt%+pR;ada!s z76#JjR@YSBMoDj2hoV`lfIG|C1MJo1Hv8GrjK)Q3v&nvqMBp2yrplAvV(9rvwqwAt z8M$M-==ihAsJCaI&wH>_UQ>N-VT+!BU)EInJpht?Pt_lo9-x^(cB;Hy#L*$Pc1z^{ zW%rNH%xo}o4xly2KbXvsFLvdLRbp~lZ~Va(QC=-((?OiWMC%Hx^UiGxRzj#^+n=SJ zG~Ha7QKT|If8UV)yYGtRCtd1CHyq7R4YT{6%I2)I356@_=x%IZ^RhDJVk`F8o|Ir3 z#%Dtkjv1zUhGy$p#NWpi+Ule=iD>y&3Gp1NJw~^%*+>;~v~ds50&6}Um{~RO#W4^D z-?;L{T2^p$w+QBM+Fm3OeOYXICSYmdC0NK57dpdJ!zKg)wz^x3ZodvnZZ}vvk#BvM zR`#^Z5V4zG%!?=BZ*4X_EIJ;l-v|r((IJ63FV!(d&tE41H`DM?`m!s;x;TSo2D-NqWvLe zaY;@_UL27OS9&X4P5ag5zGujm;S<>Hzx{CMvy)O|RK%S5jK$xYXK8H~o(z}o}1+sc+$ zm9JCiUE7s)=FphsKF8mS%culx^l{Guk?D#)o4#y=rSbbra#{p}=1Zm`5cFLcrl0Zw zdDvTI8E(zLlKyTeMlkSF506Ga-Db9mu<`f$Bix}@KKos~tI2qZ0izEC`=B|r;|i+q zOEp3PrdD^ryTL@a%EA%ybB0Z&!2v?r=(kX?@8E1UE?!p5(Mz8O6CcA~4f7^>yuTx% ziF_+HOhZxsNGJWxs~Jd}CWdzUhD#ZVu1}0#P_hN9<^)#C>y!M(cw06hCq=?`AqQ|P zQ#8fZ+DPo;c7=dW`Ymc0BUL3_jQm1?@&bg{ZTe&NFQ6pA8onVR4npM1I#2US^M#+H z&Kjc)3l4JVPlY-bxLfq@+-GWJ>M&Kvv={XzH|eVElV`g^=&4Y}$SE&%x+`TgRm+{U zzU@8eo6S1R2hG+6s&LjVVym9Kq}VgaF2JAd^OMfO&8S1oeXnW%t3VMtplTbPl$?2t zV=PtgBCG{ zf}BPyAHG6}8>Wp!#3t#|Ch2t2o4BXEw>?nUCuzDjw+~pox%*b13I$4OkN~QD@F*{l z${xBVP>OtR;kRePvyCrInC}bGz|c^9cOI2xMC3bYHl&}viyj@OczHDa@kG{)V$ilf zfg_x5MLuC)ps>eK21~(>TsbyuCYl0A_4OUJG#HYj5la}Vb^5hlxvea_E3!)>F`?K~ zo+U4XEbw`%&Sn1l{gWsO85MboTM%B5z}VD8$Ok@zjbI{k+yyKiIs2%s3d;gK*aU6k z5rHMS0AeJ>(k3;P)1_Gv?*LaGFqswPt@DX9S#6rj=}Itf)i<`vsKVjG_A{A?e)2Xo z*OQ)>aylLI2(}V|fSnoX3Iz&TK{J&>9_wdd_oULCGubnu%stOz7v&1!-F!Om`ZAhO z2=H`s+Z{Q?38OG5F= z^6>NU@gGO#*h{NlA~lg_5KawE!vF>VBqUWwJAxnSV@&g&p!oD9$EyYmqrxBgU~9pI z{)W$O%Y%-LZnVoC4V}?@2}#EiH-{2pnh0g%Ak2x+rFwD5Q&>6<^H_PeS6A83{%~F- z*r|Q1(M>KSrVt)?_aHKVW$qL(sgJ)zW+|<#8+{?EB<11~SG_5fJc2&@z6(*A;^Aq~y>k>Yx&;BoWn4*#)sJ6nv1#b;I$)%C8oseugwJGqALpFzmVWcozVz z*L3M9c^fGceATx7l`HLHPhtk9pQ)VIoiv(3>7I7`bU9kU3tRiN<3nNviCzfFF})QY zRn>q~Lg{6fb|E7TpgY{ar{~X7fO5INY%_@EPd^K1C&Z9H&)6s%PkHkXD}C(q4-U+= z$kv@|;2Z@o0w7&l)B?aZ!vneU5cV|`dHrn>`LH;JSA}d7%-KN>wPfbOcG?E29qm6W zi88{~mX`w%`?sT)wT&wxY=;w=$F5zU({k+`-*eZkk{rGLbGuFa12v4dOFaI`vW+F| z0*+aIRjH&+r9j5%%3-8AGbT>(wI8FKCDAQcZ%pj@`5r`UMgP4)!0}8yNc~=T{F(R( zmRszOYA}l}8oa#dk(D=3?RR%i(E5umU2t8+Bo2o=C%H)=Iaq0NRO=&Qh%|}LD$eYj z%hHcmT?5M1x|W`Fv35_`3O2^^z2cKKOv8VI;u7s?xx8g@e>;3X&b5C0l++%C) zOina@Q2&nCRuw~*jb#}d`!cVBoFZ5whIh8yTOTK5->(_*C;2$M;@fxAfYZ#LOFWR( zFHlVhFzs0cuF2Vej~ndT9q&j^mg`zDx)tcmMf<*LVIHt(;q=^^?3KlYk5# results = quotes - .GetPmo(35, 20, 10) - .ToList(); + IReadOnlyList results = Quotes + .ToPmo(); // proper quantities Assert.AreEqual(502, results.Count); @@ -26,35 +25,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetPmo() - .ToList(); + .ToPmo(); Assert.AreEqual(502, results.Count); Assert.AreEqual(448, results.Count(x => x.Pmo != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetPmo() - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Pmo is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetPmo() - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToPmo(); Assert.AreEqual(502, results.Count); Assert.AreEqual(447, results.Count(x => x.Pmo != null)); @@ -63,38 +49,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetPmo() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToPmo() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(439, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetPmo(25, 15, 5) - .ToList(); + IReadOnlyList r = BadQuotes + .ToPmo(25, 15, 5); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Pmo is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Pmo is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetPmo() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToPmo(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetPmo() - .ToList(); + IReadOnlyList r1 = Onequote + .ToPmo(); Assert.AreEqual(1, r1.Count); } @@ -102,15 +84,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetPmo(35, 20, 10) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToPmo() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (35 + 20 + 250), results.Count); - PmoResult last = results.LastOrDefault(); + PmoResult last = results[^1]; Assert.AreEqual(-2.7016, last.Pmo.Round(4)); Assert.AreEqual(-2.3117, last.Signal.Round(4)); } @@ -120,14 +101,14 @@ public void Exceptions() { // bad time period Assert.ThrowsException(() => - quotes.GetPmo(1)); + Quotes.ToPmo(1)); // bad smoothing period Assert.ThrowsException(() => - quotes.GetPmo(5, 0)); + Quotes.ToPmo(5, 0)); // bad signal period Assert.ThrowsException(() => - quotes.GetPmo(5, 5, 0)); + Quotes.ToPmo(5, 5, 0)); } } diff --git a/tests/indicators/m-r/Prs/Prs.StaticSeries.Tests.cs b/tests/indicators/m-r/Prs/Prs.StaticSeries.Tests.cs new file mode 100644 index 000000000..debcc99da --- /dev/null +++ b/tests/indicators/m-r/Prs/Prs.StaticSeries.Tests.cs @@ -0,0 +1,109 @@ +namespace StaticSeries; + +[TestClass] +public class Prs : StaticSeriesTestBase +{ + [TestMethod] + public override void Standard() + { + int lookbackPeriods = 30; + + IReadOnlyList results = OtherQuotes + .ToPrs(Quotes, lookbackPeriods); + + // proper quantities + Assert.AreEqual(502, results.Count); + Assert.AreEqual(502, results.Count(x => x.Prs != null)); + + // sample values + PrsResult r1 = results[8]; + Assert.AreEqual(1.108340, r1.Prs.Round(6)); + Assert.AreEqual(null, r1.PrsPercent); + + PrsResult r2 = results[249]; + Assert.AreEqual(1.222373, r2.Prs.Round(6)); + Assert.AreEqual(-0.023089, r2.PrsPercent.Round(6)); + + PrsResult r3 = results[501]; + Assert.AreEqual(1.356817, r3.Prs.Round(6)); + Assert.AreEqual(0.037082, r3.PrsPercent.Round(6)); + } + + [TestMethod] + public void UseReusable() + { + IReadOnlyList results = OtherQuotes + .Use(CandlePart.Close) + .ToPrs(Quotes.Use(CandlePart.Close), 20); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(502, results.Count(x => x.Prs != null)); + } + + [TestMethod] + public void Chainor() + { + IReadOnlyList results = OtherQuotes + .ToPrs(Quotes, 20) + .ToSma(10); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(493, results.Count(x => x.Sma != null)); + } + + [TestMethod] + public void Chainee() + { + IReadOnlyList results = Quotes + .ToSma(2) + .ToPrs(OtherQuotes.ToSma(2), 20); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(501, results.Count(x => x.Prs != null)); + Assert.AreEqual(0, results.Count(x => x.Prs is double.NaN)); + } + + [TestMethod] + public override void BadData() + { + IReadOnlyList r = BadQuotes + .ToPrs(BadQuotes, 15); + + Assert.AreEqual(502, r.Count); + Assert.AreEqual(0, r.Count(x => x.Prs is double.NaN)); + } + + [TestMethod] + public override void NoQuotes() + { + IReadOnlyList r0 = Noquotes + .ToPrs(Noquotes); + + Assert.AreEqual(0, r0.Count); + + IReadOnlyList r1 = Onequote + .ToPrs(Onequote); + + Assert.AreEqual(1, r1.Count); + } + + [TestMethod] + public void Exceptions() + { + // bad lookback period + Assert.ThrowsException(() => + OtherQuotes.ToPrs(Quotes, 0)); + + // insufficient quotes + Assert.ThrowsException(() => + Data.GetCompare(13).ToPrs(Quotes, 14)); + + // insufficient eval quotes + Assert.ThrowsException(() => + Data.GetCompare(300).ToPrs(Quotes, 14)); + + // mismatch quotes + Assert.ThrowsException(() => + OtherQuotes.ToPrs(MismatchQuotes, 14)); + } +} diff --git a/tests/indicators/m-r/Prs/Prs.Tests.cs b/tests/indicators/m-r/Prs/Prs.Tests.cs deleted file mode 100644 index e3bf0536c..000000000 --- a/tests/indicators/m-r/Prs/Prs.Tests.cs +++ /dev/null @@ -1,136 +0,0 @@ -namespace Tests.Indicators; - -[TestClass] -public class PrsTests : TestBase -{ - [TestMethod] - public void Standard() - { - int lookbackPeriods = 30; - int smaPeriods = 10; - - List results = - otherQuotes.GetPrs(quotes, lookbackPeriods, smaPeriods) - .ToList(); - - // proper quantities - Assert.AreEqual(502, results.Count); - Assert.AreEqual(502, results.Count(x => x.Prs != null)); - Assert.AreEqual(493, results.Count(x => x.PrsSma != null)); - - // sample values - PrsResult r1 = results[8]; - Assert.AreEqual(1.108340, r1.Prs.Round(6)); - Assert.AreEqual(null, r1.PrsSma); - Assert.AreEqual(null, r1.PrsPercent); - - PrsResult r2 = results[249]; - Assert.AreEqual(1.222373, r2.Prs.Round(6)); - Assert.AreEqual(1.275808, r2.PrsSma.Round(6)); - Assert.AreEqual(-0.023089, r2.PrsPercent.Round(6)); - - PrsResult r3 = results[501]; - Assert.AreEqual(1.356817, r3.Prs.Round(6)); - Assert.AreEqual(1.343445, r3.PrsSma.Round(6)); - Assert.AreEqual(0.037082, r3.PrsPercent.Round(6)); - } - - [TestMethod] - public void UseTuple() - { - List results = otherQuotes - .Use(CandlePart.Close) - .GetPrs(quotes.Use(CandlePart.Close), 20) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(502, results.Count(x => x.Prs != null)); - } - - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetPrs(tupleNanny, 6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Prs is double and double.NaN)); - } - - [TestMethod] - public void Chainor() - { - List results = otherQuotes - .GetPrs(quotes, 20) - .GetSma(10) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(493, results.Count(x => x.Sma != null)); - } - - [TestMethod] - public void Chainee() - { - List results = quotes - .GetSma(2) - .GetPrs(otherQuotes.GetSma(2), 20) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(501, results.Count(x => x.Prs != null)); - Assert.AreEqual(0, results.Count(x => x.Prs is double and double.NaN)); - } - - [TestMethod] - public void BadData() - { - List r = badQuotes - .GetPrs(badQuotes, 15, 4) - .ToList(); - - Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Prs is double and double.NaN)); - } - - [TestMethod] - public void NoQuotes() - { - List r0 = noquotes - .GetPrs(noquotes) - .ToList(); - - Assert.AreEqual(0, r0.Count); - - List r1 = onequote - .GetPrs(onequote) - .ToList(); - - Assert.AreEqual(1, r1.Count); - } - - [TestMethod] - public void Exceptions() - { - // bad lookback period - Assert.ThrowsException(() => - otherQuotes.GetPrs(quotes, 0)); - - // bad SMA period - Assert.ThrowsException(() => - otherQuotes.GetPrs(quotes, 14, 0)); - - // insufficient quotes - Assert.ThrowsException(() => - TestData.GetCompare(13).GetPrs(quotes, 14)); - - // insufficient eval quotes - Assert.ThrowsException(() => - TestData.GetCompare(300).GetPrs(quotes, 14)); - - // mismatch quotes - Assert.ThrowsException(() => - otherQuotes.GetPrs(mismatchQuotes, 14)); - } -} diff --git a/tests/indicators/m-r/Pvo/Pvo.Tests.cs b/tests/indicators/m-r/Pvo/Pvo.StaticSeries.Tests.cs similarity index 71% rename from tests/indicators/m-r/Pvo/Pvo.Tests.cs rename to tests/indicators/m-r/Pvo/Pvo.StaticSeries.Tests.cs index 0c82c340b..6b5e8123d 100644 --- a/tests/indicators/m-r/Pvo/Pvo.Tests.cs +++ b/tests/indicators/m-r/Pvo/Pvo.StaticSeries.Tests.cs @@ -1,18 +1,17 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class PvoTests : TestBase +public class Pvo : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { int fastPeriods = 12; int slowPeriods = 26; int signalPeriods = 9; - List results = - quotes.GetPvo(fastPeriods, slowPeriods, signalPeriods) - .ToList(); + IReadOnlyList results = + Quotes.ToPvo(fastPeriods, slowPeriods, signalPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -50,38 +49,34 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetPvo() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToPvo() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(468, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetPvo(10, 20, 5) - .ToList(); + IReadOnlyList r = BadQuotes + .ToPvo(10, 20, 5); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Pvo is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Pvo is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetPvo() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToPvo(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetPvo() - .ToList(); + IReadOnlyList r1 = Onequote + .ToPvo(); Assert.AreEqual(1, r1.Count); } @@ -93,15 +88,14 @@ public void Removed() int slowPeriods = 26; int signalPeriods = 9; - List results = quotes - .GetPvo(fastPeriods, slowPeriods, signalPeriods) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToPvo(fastPeriods, slowPeriods, signalPeriods) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (slowPeriods + signalPeriods + 250), results.Count); - PvoResult last = results.LastOrDefault(); + PvoResult last = results[^1]; Assert.AreEqual(10.4395, last.Pvo.Round(4)); Assert.AreEqual(12.2681, last.Signal.Round(4)); Assert.AreEqual(-1.8286, last.Histogram.Round(4)); @@ -112,14 +106,14 @@ public void Exceptions() { // bad fast period Assert.ThrowsException(() => - quotes.GetPvo(0, 26, 9)); + Quotes.ToPvo(0)); // bad slow periods must be larger than faster period Assert.ThrowsException(() => - quotes.GetPvo(12, 12, 9)); + Quotes.ToPvo(12, 12)); // bad signal period Assert.ThrowsException(() => - quotes.GetPvo(12, 26, -1)); + Quotes.ToPvo(12, 26, -1)); } } diff --git a/tests/indicators/m-r/Renko/Renko.Calc.xlsx b/tests/indicators/m-r/Renko/Renko.Calc.xlsx index 97f03fddbd0c91a35b22f4f3ff1f9b5886857482..b32259adbab440af1d729dd01ea9fa13b2b44fe2 100644 GIT binary patch delta 101526 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 z%0D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 diff --git a/tests/indicators/m-r/Renko/Renko.Tests.cs b/tests/indicators/m-r/Renko/Renko.StaticSeries.Tests.cs similarity index 76% rename from tests/indicators/m-r/Renko/Renko.Tests.cs rename to tests/indicators/m-r/Renko/Renko.StaticSeries.Tests.cs index 9e94bfaa5..1a2ed70ac 100644 --- a/tests/indicators/m-r/Renko/Renko.Tests.cs +++ b/tests/indicators/m-r/Renko/Renko.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class RenkoTests : TestBase +public class Renko : StaticSeriesTestBase { [TestMethod] - public void StandardClose() + public override void Standard() // close { - List results = quotes - .GetRenko(2.5m, EndType.Close) - .ToList(); + IReadOnlyList results = Quotes + .ToRenko(2.5m); // assertions @@ -33,7 +32,7 @@ public void StandardClose() Assert.AreEqual(4192959240m, r5.Volume); Assert.IsTrue(r5.IsUp); - RenkoResult last = results.LastOrDefault(); + RenkoResult last = results[^1]; Assert.AreEqual(240.5m, last.Open); Assert.AreEqual(243.68m, last.High); Assert.AreEqual(234.52m, last.Low); @@ -45,9 +44,8 @@ public void StandardClose() [TestMethod] public void StandardHighLow() { - List results = quotes - .GetRenko(2.5m, EndType.HighLow) - .ToList(); + IReadOnlyList results = Quotes + .ToRenko(2.5m, EndType.HighLow); // assertions @@ -70,7 +68,7 @@ public void StandardHighLow() Assert.AreEqual(100801672m, r25.Volume.Round(0)); Assert.IsTrue(r25.IsUp); - RenkoResult last = results.LastOrDefault(); + RenkoResult last = results[^1]; Assert.AreEqual(243m, last.Open); Assert.AreEqual(246.73m, last.High); Assert.AreEqual(241.87m, last.Low); @@ -82,9 +80,8 @@ public void StandardHighLow() [TestMethod] public void Atr() { - List results = quotes - .GetRenkoAtr(14, EndType.Close) - .ToList(); + IReadOnlyList results = Quotes + .GetRenkoAtr(14); // proper quantities Assert.AreEqual(29, results.Count); @@ -98,7 +95,7 @@ public void Atr() Assert.AreEqual(2090292272m, r0.Volume.Round(0)); Assert.IsTrue(r0.IsUp); - RenkoResult last = results.LastOrDefault(); + RenkoResult last = results[^1]; Assert.AreEqual(237.3990m, last.Open.Round(4)); Assert.AreEqual(246.73m, last.High.Round(4)); Assert.AreEqual(229.42m, last.Low.Round(4)); @@ -110,27 +107,25 @@ public void Atr() [TestMethod] public void UseAsQuotes() { - IEnumerable renkoQuotes = quotes.GetRenko(2.5m); - IEnumerable renkoSma = renkoQuotes.GetSma(5); + IReadOnlyList renkoQuotes = Quotes.ToRenko(2.5m); + IReadOnlyList renkoSma = renkoQuotes.ToSma(5); Assert.AreEqual(108, renkoSma.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetRenko(100m) - .ToList(); + IReadOnlyList r = BadQuotes + .ToRenko(100m); Assert.AreNotEqual(0, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetRenko(0.01m) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToRenko(0.01m); Assert.AreEqual(0, r0.Count); } @@ -140,10 +135,10 @@ public void Exceptions() { // bad arguments Assert.ThrowsException(() - => quotes.GetRenko(0)); + => Quotes.ToRenko(0)); // bad end type Assert.ThrowsException(() - => quotes.GetRenko(2, (EndType)int.MaxValue)); + => Quotes.ToRenko(2, (EndType)int.MaxValue)); } } diff --git a/tests/indicators/m-r/Renko/Renko.StreamHub.Tests.cs b/tests/indicators/m-r/Renko/Renko.StreamHub.Tests.cs new file mode 100644 index 000000000..9e859a39d --- /dev/null +++ b/tests/indicators/m-r/Renko/Renko.StreamHub.Tests.cs @@ -0,0 +1,147 @@ +namespace StreamHub; + +[TestClass] +public class RenkoHub : StreamHubTestBase, ITestChainProvider +{ + [TestMethod] + public override void QuoteObserver() + { + decimal brickSize = 2.5m; + EndType endType = EndType.HighLow; + + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // prefill quotes to provider + for (int i = 0; i < 50; i++) + { + provider.Add(quotesList[i]); + } + + // initialize observer + RenkoHub observer = provider + .ToRenko(brickSize, endType); + + // fetch initial results (early) + IReadOnlyList streamList + = observer.Results; + + // emulate adding quotes to provider + for (int i = 50; i < length; i++) + { + // skip one (add later) + if (i == 80) + { + continue; + } + + Quote q = quotesList[i]; + provider.Add(q); + + // resend duplicate quotes + if (i is > 100 and < 105) + { + provider.Add(q); + } + } + + // late arrival + provider.Insert(quotesList[80]); + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // time-series, for comparison + IReadOnlyList seriesList = quotesList + .ToRenko(brickSize, endType); + + // assert, should equal series + streamList.Should().BeEquivalentTo(seriesList); + streamList.Should().HaveCount(159); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public void ChainProvider() + { + decimal brickSize = 2.5m; + EndType endType = EndType.Close; + int smaPeriods = 50; + + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // initialize observer + SmaHub observer = provider + .ToRenko(brickSize, endType) + .ToSma(smaPeriods); + + // emulate quote stream + for (int i = 0; i < length; i++) + { + provider.Add(quotesList[i]); + } + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // final results + IReadOnlyList streamList + = observer.Results; + + // time-series, for comparison + IReadOnlyList seriesList = quotesList + .ToRenko(brickSize, endType) + .ToSma(smaPeriods); + + // assert, should equal series + streamList.Should().BeEquivalentTo(seriesList); + streamList.Should().HaveCount(112); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public override void CustomToString() + { + RenkoHub hub = new(new QuoteHub(), 2.5m, EndType.Close); + hub.ToString().Should().Be("RENKO(2.5,CLOSE)"); + } + + [TestMethod] + public void SettingsInheritance() + { + // setup quote hub (1st level) + QuoteHub quoteHub = new(); + + // setup renko hub (2nd level) + RenkoHub renkoHub = quoteHub + .ToRenko(brickSize: 2.5m, endType: EndType.Close); + + // setup child hub (3rd level) + SmaHub childHub = renkoHub + .ToSma(lookbackPeriods: 5); + + // note: dispite `quoteHub` being parentless, + // it has default properties; it should not + // inherit its own empty provider settings + + // assert + quoteHub.Properties.Settings.Should().Be(0b00000000, "is has default settings, not inherited"); + renkoHub.Properties.Settings.Should().Be(0b00000010, "it has custom Renko properties"); + childHub.Properties.Settings.Should().Be(0b00000010, "it inherits Renko properties"); + } +} diff --git a/tests/indicators/m-r/Roc/Roc.StaticSeries.Tests.cs b/tests/indicators/m-r/Roc/Roc.StaticSeries.Tests.cs new file mode 100644 index 000000000..1042bca94 --- /dev/null +++ b/tests/indicators/m-r/Roc/Roc.StaticSeries.Tests.cs @@ -0,0 +1,107 @@ +namespace StaticSeries; + +[TestClass] +public class Roc : StaticSeriesTestBase +{ + [TestMethod] + public override void Standard() + { + IReadOnlyList results = Quotes + .ToRoc(20); + + // proper quantities + Assert.AreEqual(502, results.Count); + Assert.AreEqual(482, results.Count(x => x.Momentum != null)); + Assert.AreEqual(482, results.Count(x => x.Roc != null)); + + // sample values + RocResult r49 = results[49]; + Assert.AreEqual(4.96, r49.Momentum.Round(4)); + Assert.AreEqual(2.2465, r49.Roc.Round(4)); + + RocResult r249 = results[249]; + Assert.AreEqual(6.25, r249.Momentum.Round(4)); + Assert.AreEqual(2.4827, r249.Roc.Round(4)); + + RocResult r501 = results[501]; + Assert.AreEqual(-22.05, r501.Momentum.Round(4)); + Assert.AreEqual(-8.2482, r501.Roc.Round(4)); + } + + [TestMethod] + public void UseReusable() + { + IReadOnlyList results = Quotes + .Use(CandlePart.Close) + .ToRoc(20); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(482, results.Count(x => x.Roc != null)); + } + + [TestMethod] + public void Chainee() + { + IReadOnlyList results = Quotes + .ToSma(2) + .ToRoc(20); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(481, results.Count(x => x.Roc != null)); + } + + [TestMethod] + public void Chainor() + { + IReadOnlyList results = Quotes + .ToRoc(20) + .ToSma(10); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(473, results.Count(x => x.Sma != null)); + } + + [TestMethod] + public override void BadData() + { + IReadOnlyList r = BadQuotes + .ToRoc(35); + + Assert.AreEqual(502, r.Count); + Assert.AreEqual(0, r.Count(x => x.Roc is double.NaN)); + } + + [TestMethod] + public override void NoQuotes() + { + IReadOnlyList r0 = Noquotes + .ToRoc(5); + + Assert.AreEqual(0, r0.Count); + + IReadOnlyList r1 = Onequote + .ToRoc(5); + + Assert.AreEqual(1, r1.Count); + } + + [TestMethod] + public void Removed() + { + IReadOnlyList results = Quotes + .ToRoc(20) + .RemoveWarmupPeriods(); + + // assertions + Assert.AreEqual(502 - 20, results.Count); + + RocResult last = results[^1]; + Assert.AreEqual(-8.2482, last.Roc.Round(4)); + } + + [TestMethod] + public void Exceptions() => + // bad lookback period + Assert.ThrowsException(() => + Quotes.ToRoc(0)); +} diff --git a/tests/indicators/m-r/Roc/Roc.Tests.cs b/tests/indicators/m-r/Roc/Roc.Tests.cs deleted file mode 100644 index 9933afca6..000000000 --- a/tests/indicators/m-r/Roc/Roc.Tests.cs +++ /dev/null @@ -1,162 +0,0 @@ -namespace Tests.Indicators; - -[TestClass] -public class RocTests : TestBase -{ - [TestMethod] - public void Standard() - { - List results = quotes - .GetRoc(20) - .ToList(); - - // proper quantities - Assert.AreEqual(502, results.Count); - Assert.AreEqual(482, results.Count(x => x.Momentum != null)); - Assert.AreEqual(482, results.Count(x => x.Roc != null)); - Assert.AreEqual(false, results.Any(x => x.RocSma != null)); - - // sample values - RocResult r49 = results[49]; - Assert.AreEqual(4.96, r49.Momentum.Round(4)); - Assert.AreEqual(2.2465, r49.Roc.Round(4)); - Assert.AreEqual(null, r49.RocSma); - - RocResult r249 = results[249]; - Assert.AreEqual(6.25, r249.Momentum.Round(4)); - Assert.AreEqual(2.4827, r249.Roc.Round(4)); - Assert.AreEqual(null, r249.RocSma); - - RocResult r501 = results[501]; - Assert.AreEqual(-22.05, r501.Momentum.Round(4)); - Assert.AreEqual(-8.2482, r501.Roc.Round(4)); - Assert.AreEqual(null, r501.RocSma); - } - - [TestMethod] - public void WithSma() - { - int lookbackPeriods = 20; - int smaPeriods = 5; - - List results = quotes - .GetRoc(lookbackPeriods, smaPeriods) - .ToList(); - - // proper quantities - Assert.AreEqual(502, results.Count); - Assert.AreEqual(482, results.Count(x => x.Roc != null)); - Assert.AreEqual(478, results.Count(x => x.RocSma != null)); - - // sample values - RocResult r1 = results[29]; - Assert.AreEqual(3.2936, r1.Roc.Round(4)); - Assert.AreEqual(2.1558, r1.RocSma.Round(4)); - - RocResult r2 = results[501]; - Assert.AreEqual(-8.2482, r2.Roc.Round(4)); - Assert.AreEqual(-8.4828, r2.RocSma.Round(4)); - } - - [TestMethod] - public void UseTuple() - { - List results = quotes - .Use(CandlePart.Close) - .GetRoc(20) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(482, results.Count(x => x.Roc != null)); - } - - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetRoc(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Roc is double and double.NaN)); - } - - [TestMethod] - public void Chainee() - { - List results = quotes - .GetSma(2) - .GetRoc(20) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(481, results.Count(x => x.Roc != null)); - } - - [TestMethod] - public void Chainor() - { - List results = quotes - .GetRoc(20) - .GetSma(10) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(473, results.Count(x => x.Sma != null)); - } - - [TestMethod] - public void BadData() - { - List r = badQuotes - .GetRoc(35, 2) - .ToList(); - - Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Roc is double and double.NaN)); - } - - [TestMethod] - public void NoQuotes() - { - List r0 = noquotes - .GetRoc(5) - .ToList(); - - Assert.AreEqual(0, r0.Count); - - List r1 = onequote - .GetRoc(5) - .ToList(); - - Assert.AreEqual(1, r1.Count); - } - - [TestMethod] - public void Removed() - { - List results = quotes - .GetRoc(20) - .RemoveWarmupPeriods() - .ToList(); - - // assertions - Assert.AreEqual(502 - 20, results.Count); - - RocResult last = results.LastOrDefault(); - Assert.AreEqual(-8.2482, last.Roc.Round(4)); - Assert.AreEqual(null, last.RocSma); - } - - [TestMethod] - public void Exceptions() - { - // bad lookback period - Assert.ThrowsException(() => - quotes.GetRoc(0)); - - // bad SMA period - Assert.ThrowsException(() => - quotes.GetRoc(14, 0)); - } -} diff --git a/tests/indicators/m-r/RocWb/RocWb.Tests.cs b/tests/indicators/m-r/RocWb/RocWb.StaticSeries.Tests.cs similarity index 70% rename from tests/indicators/m-r/RocWb/RocWb.Tests.cs rename to tests/indicators/m-r/RocWb/RocWb.StaticSeries.Tests.cs index 2e5b95412..65bd3dd2e 100644 --- a/tests/indicators/m-r/RocWb/RocWb.Tests.cs +++ b/tests/indicators/m-r/RocWb/RocWb.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class RocWbTests : TestBase +public class RocWb : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetRocWb(20, 3, 20) - .ToList(); + IReadOnlyList results = Quotes + .ToRocWb(20, 3, 20); // proper quantities Assert.AreEqual(502, results.Count); @@ -68,35 +67,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetRocWb(20, 3, 20) - .ToList(); + .ToRocWb(20, 3, 20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Roc != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetRocWb(6, 7, 5) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.UpperBand is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetRocWb(20, 3, 20) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToRocWb(20, 3, 20); Assert.AreEqual(502, results.Count); Assert.AreEqual(481, results.Count(x => x.Roc != null)); @@ -105,38 +91,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetRocWb(20, 3, 20) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToRocWb(20, 3, 20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(473, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetRocWb(35, 3, 35) - .ToList(); + IReadOnlyList r = BadQuotes + .ToRocWb(35, 3, 35); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Roc is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Roc is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetRocWb(5, 3, 2) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToRocWb(5, 3, 2); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetRocWb(5, 3, 2) - .ToList(); + IReadOnlyList r1 = Onequote + .ToRocWb(5, 3, 2); Assert.AreEqual(1, r1.Count); } @@ -144,15 +126,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetRocWb(20, 3, 20) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToRocWb(20, 3, 20) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (20 + 3 + 100), results.Count); - RocWbResult last = results.LastOrDefault(); + RocWbResult last = results[^1]; Assert.AreEqual(-8.2482, Math.Round(last.Roc.Value, 4)); Assert.AreEqual(-8.3390, Math.Round(last.RocEma.Value, 4)); Assert.AreEqual(6.1294, Math.Round(last.UpperBand.Value, 4)); @@ -164,14 +145,14 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - quotes.GetRocWb(0, 3, 12)); + Quotes.ToRocWb(0, 3, 12)); // bad EMA period Assert.ThrowsException(() => - quotes.GetRocWb(14, 0, 14)); + Quotes.ToRocWb(14, 0, 14)); // bad STDDEV period Assert.ThrowsException(() => - quotes.GetRocWb(15, 3, 16)); + Quotes.ToRocWb(15, 3, 16)); } } diff --git a/tests/indicators/m-r/RollingPivots/RollingPivots.Tests.cs b/tests/indicators/m-r/RollingPivots/RollingPivots.StaticSeries.Tests.cs similarity index 89% rename from tests/indicators/m-r/RollingPivots/RollingPivots.Tests.cs rename to tests/indicators/m-r/RollingPivots/RollingPivots.StaticSeries.Tests.cs index 2e422d274..7874751ca 100644 --- a/tests/indicators/m-r/RollingPivots/RollingPivots.Tests.cs +++ b/tests/indicators/m-r/RollingPivots/RollingPivots.StaticSeries.Tests.cs @@ -1,18 +1,17 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class RollingPivotsTests : TestBase +public class RollingPivots : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { int windowPeriods = 11; int offsetPeriods = 9; PivotPointType pointType = PivotPointType.Standard; - List results = - quotes.GetRollingPivots(windowPeriods, offsetPeriods, pointType) - .ToList(); + IReadOnlyList results = + Quotes.ToRollingPivots(windowPeriods, offsetPeriods, pointType); // proper quantities Assert.AreEqual(502, results.Count); @@ -82,11 +81,10 @@ public void Camarilla() int offsetPeriods = 0; PivotPointType pointType = PivotPointType.Camarilla; - IEnumerable h = TestData.GetDefault(38); + IReadOnlyList h = Data.GetDefault(38); - List results = h - .GetRollingPivots(windowPeriods, offsetPeriods, pointType) - .ToList(); + IReadOnlyList results = h + .ToRollingPivots(windowPeriods, offsetPeriods, pointType); // proper quantities Assert.AreEqual(38, results.Count); @@ -156,9 +154,8 @@ public void Demark() int offsetPeriods = 10; PivotPointType pointType = PivotPointType.Demark; - List results = quotes - .GetRollingPivots(windowPeriods, offsetPeriods, pointType) - .ToList(); + IReadOnlyList results = Quotes + .ToRollingPivots(windowPeriods, offsetPeriods, pointType); // proper quantities Assert.AreEqual(502, results.Count); @@ -239,11 +236,10 @@ public void Fibonacci() int offsetPeriods = 15; PivotPointType pointType = PivotPointType.Fibonacci; - IEnumerable h = TestData.GetIntraday(300); + IReadOnlyList h = Data.GetIntraday(300); - List results = - h.GetRollingPivots(windowPeriods, offsetPeriods, pointType) - .ToList(); + IReadOnlyList results = + h.ToRollingPivots(windowPeriods, offsetPeriods, pointType); // proper quantities Assert.AreEqual(300, results.Count); @@ -314,11 +310,10 @@ public void Woodie() int offsetPeriods = 16; PivotPointType pointType = PivotPointType.Woodie; - IEnumerable h = TestData.GetIntraday(1564); + IReadOnlyList h = Data.GetIntraday(); - List results = h - .GetRollingPivots(windowPeriods, offsetPeriods, pointType) - .ToList(); + IReadOnlyList results = h + .ToRollingPivots(windowPeriods, offsetPeriods, pointType); // proper quantities Assert.AreEqual(1564, results.Count); @@ -374,27 +369,24 @@ public void Woodie() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetRollingPivots(5, 5) - .ToList(); + IReadOnlyList r = BadQuotes + .ToRollingPivots(5, 5); Assert.AreEqual(502, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetRollingPivots(5, 2) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToRollingPivots(5, 2); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetRollingPivots(5, 2) - .ToList(); + IReadOnlyList r1 = Onequote + .ToRollingPivots(5, 2); Assert.AreEqual(1, r1.Count); } @@ -406,15 +398,14 @@ public void Removed() int offsetPeriods = 9; PivotPointType pointType = PivotPointType.Standard; - List results = quotes - .GetRollingPivots(windowPeriods, offsetPeriods, pointType) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToRollingPivots(windowPeriods, offsetPeriods, pointType) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (windowPeriods + offsetPeriods), results.Count); - RollingPivotsResult last = results.LastOrDefault(); + RollingPivotsResult last = results[^1]; Assert.AreEqual(260.0267m, last.PP.Round(4)); Assert.AreEqual(246.4633m, last.S1.Round(4)); Assert.AreEqual(238.7767m, last.S2.Round(4)); @@ -431,10 +422,10 @@ public void Exceptions() { // bad window period Assert.ThrowsException(() => - quotes.GetRollingPivots(0, 10)); + Quotes.ToRollingPivots(0, 10)); // bad offset period Assert.ThrowsException(() => - quotes.GetRollingPivots(10, -1)); + Quotes.ToRollingPivots(10, -1)); } } diff --git a/tests/indicators/m-r/Rsi/Rsi.Tests.cs b/tests/indicators/m-r/Rsi/Rsi.StaticSeries.Tests.cs similarity index 54% rename from tests/indicators/m-r/Rsi/Rsi.Tests.cs rename to tests/indicators/m-r/Rsi/Rsi.StaticSeries.Tests.cs index 2ef69eefe..f70a73693 100644 --- a/tests/indicators/m-r/Rsi/Rsi.Tests.cs +++ b/tests/indicators/m-r/Rsi/Rsi.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class RsiTests : TestBase +public class Rsi : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetRsi(14) - .ToList(); + IReadOnlyList results = Quotes + .ToRsi(); // proper quantities Assert.AreEqual(502, results.Count); @@ -32,9 +31,8 @@ public void Standard() public void SmallLookback() { int lookbackPeriods = 1; - List results = quotes - .GetRsi(lookbackPeriods) - .ToList(); + IReadOnlyList results = Quotes + .ToRsi(lookbackPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -51,45 +49,31 @@ public void SmallLookback() [TestMethod] public void CryptoData() { - IEnumerable btc = TestData.GetBitcoin(); + IReadOnlyList btc = Data.GetBitcoin(); - List r = btc - .GetRsi(1) - .ToList(); + IReadOnlyList r = btc + .ToRsi(1); Assert.AreEqual(1246, r.Count); } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetRsi(14) - .ToList(); + .ToRsi(); Assert.AreEqual(502, results.Count); Assert.AreEqual(488, results.Count(x => x.Rsi != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetRsi(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Rsi is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetRsi(14) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToRsi(); Assert.AreEqual(502, results.Count); Assert.AreEqual(487, results.Count(x => x.Rsi != null)); @@ -98,10 +82,9 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetRsi(14) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToRsi() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(479, results.Count(x => x.Sma != null)); @@ -110,35 +93,32 @@ public void Chainor() [TestMethod] public void NaN() { - IEnumerable r = TestData.GetBtcUsdNan() - .GetRsi(14); + IReadOnlyList r = Data.GetBtcUsdNan() + .ToRsi(); - Assert.AreEqual(0, r.Count(x => x.Rsi is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Rsi is double.NaN)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetRsi(20) - .ToList(); + IReadOnlyList r = BadQuotes + .ToRsi(20); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Rsi is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Rsi is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetRsi() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToRsi(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetRsi() - .ToList(); + IReadOnlyList r1 = Onequote + .ToRsi(); Assert.AreEqual(1, r1.Count); } @@ -146,15 +126,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetRsi(14) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToRsi() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (10 * 14), results.Count); - RsiResult last = results.LastOrDefault(); + RsiResult last = results[^1]; Assert.AreEqual(42.0773, last.Rsi.Round(4)); } @@ -162,5 +141,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetRsi(0)); + => Quotes.ToRsi(0)); } diff --git a/tests/indicators/s-z/Slope/Slope.Tests.cs b/tests/indicators/s-z/Slope/Slope.StaticSeries.Tests.cs similarity index 63% rename from tests/indicators/s-z/Slope/Slope.Tests.cs rename to tests/indicators/s-z/Slope/Slope.StaticSeries.Tests.cs index d6a51eab2..9fc6af0f4 100644 --- a/tests/indicators/s-z/Slope/Slope.Tests.cs +++ b/tests/indicators/s-z/Slope/Slope.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class SlopeTests : TestBase +public class Slope : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetSlope(20) - .ToList(); + IReadOnlyList results = Quotes + .ToSlope(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -40,35 +39,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetSlope(20) - .ToList(); + .ToSlope(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Slope != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetSlope(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Slope is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetSlope(20) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToSlope(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Slope != null)); @@ -77,48 +63,43 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetSlope(20) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToSlope(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetSlope(15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToSlope(15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Slope is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Slope is double.NaN)); } [TestMethod] public void BigData() { - List r = bigQuotes - .GetSlope(250) - .ToList(); + IReadOnlyList r = BigQuotes + .ToSlope(250); Assert.AreEqual(1246, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetSlope(5) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToSlope(5); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetSlope(5) - .ToList(); + IReadOnlyList r1 = Onequote + .ToSlope(5); Assert.AreEqual(1, r1.Count); } @@ -126,15 +107,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetSlope(20) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToSlope(20) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 19, results.Count); - SlopeResult last = results.LastOrDefault(); + SlopeResult last = results[^1]; Assert.AreEqual(-1.689143, last.Slope.Round(6)); Assert.AreEqual(1083.7629, last.Intercept.Round(4)); Assert.AreEqual(0.7955, last.RSquared.Round(4)); @@ -146,5 +126,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetSlope(1)); + => Quotes.ToSlope(1)); } diff --git a/tests/indicators/s-z/Sma/Sma.Analysis.Tests.cs b/tests/indicators/s-z/Sma/Sma.Analysis.Tests.cs deleted file mode 100644 index 790bbc920..000000000 --- a/tests/indicators/s-z/Sma/Sma.Analysis.Tests.cs +++ /dev/null @@ -1,117 +0,0 @@ -namespace Tests.Indicators; - -[TestClass] -public class SmaExtendedTests : TestBase -{ - [TestMethod] - public void Analysis() - { - List results = quotes - .GetSmaAnalysis(20) - .ToList(); - - // proper quantities - Assert.AreEqual(502, results.Count); - Assert.AreEqual(483, results.Count(x => x.Sma != null)); - - // sample value - SmaAnalysis r = results[501]; - Assert.AreEqual(251.86, r.Sma.Round(6)); - Assert.AreEqual(9.450000, r.Mad.Round(6)); - Assert.AreEqual(119.25102, r.Mse.Round(6)); - Assert.AreEqual(0.037637, r.Mape.Round(6)); - } - - [TestMethod] - public void UseTuple() - { - List results = quotes - .Use(CandlePart.Close) - .GetSmaAnalysis(20) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(483, results.Count(x => x.Sma != null)); - } - - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetSmaAnalysis(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Mse is double and double.NaN)); - } - - [TestMethod] - public void Chainee() - { - List results = quotes - .GetSma(2) - .GetSmaAnalysis(20) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(482, results.Count(x => x.Sma != null)); - } - - [TestMethod] - public void Chainor() - { - List results = quotes - .GetSmaAnalysis(10) - .GetEma(10) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(484, results.Count(x => x.Ema != null)); - } - - [TestMethod] - public void BadData() - { - List r = badQuotes - .GetSmaAnalysis(15) - .ToList(); - - Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Mape is double and double.NaN)); - } - - [TestMethod] - public void NoQuotes() - { - List r0 = noquotes - .GetSmaAnalysis(6) - .ToList(); - - Assert.AreEqual(0, r0.Count); - - List r1 = onequote - .GetSmaAnalysis(6) - .ToList(); - - Assert.AreEqual(1, r1.Count); - } - - [TestMethod] - public void Removed() - { - List results = quotes - .GetSmaAnalysis(20) - .RemoveWarmupPeriods() - .ToList(); - - // assertions - Assert.AreEqual(502 - 19, results.Count); - Assert.AreEqual(251.8600, Math.Round(results.LastOrDefault().Sma.Value, 4)); - } - - // bad lookback period - [TestMethod] - public void Exceptions() - => Assert.ThrowsException(() - => quotes.GetSmaAnalysis(0)); -} diff --git a/tests/indicators/s-z/Sma/Sma.Obs.Tests.cs b/tests/indicators/s-z/Sma/Sma.Obs.Tests.cs deleted file mode 100644 index bca4caab6..000000000 --- a/tests/indicators/s-z/Sma/Sma.Obs.Tests.cs +++ /dev/null @@ -1,126 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Skender.Stock.Indicators; -using Tests.Common; - -namespace Tests.Indicators; - -[TestClass] -public class SmaStreamTests : TestBase -{ - [TestMethod] - public void Standard() - { - List quotesList = quotes - .ToSortedList(); - - int length = quotesList.Count; - - // time-series, for comparison - List seriesList = quotes - .GetSma(20) - .ToList(); - - // setup quote provider - QuoteProvider provider = new(); - - // initialize EMA observer - SmaObserver observer = provider - .GetSma(20); - - // fetch initial results - IEnumerable results - = observer.Results; - - // emulate adding quotes to provider - for (int i = 0; i < length; i++) - { - Quote q = quotesList[i]; - provider.Add(q); - } - - // final results - List resultsList - = results.ToList(); - - // assert, should equal series - for (int i = 0; i < seriesList.Count; i++) - { - SmaResult s = seriesList[i]; - SmaResult r = resultsList[i]; - - Assert.AreEqual(s.Date, r.Date); - Assert.AreEqual(s.Sma, r.Sma); - } - - observer.Unsubscribe(); - provider.EndTransmission(); - } - - [TestMethod] - public void Increment() - { - // baseline for comparison - List<(DateTime Date, double Value)> tpList = new() - { - new (DateTime.Parse("1/1/2000", EnglishCulture), 1d), - new (DateTime.Parse("1/2/2000", EnglishCulture), 2d), - new (DateTime.Parse("1/3/2000", EnglishCulture), 3d), - new (DateTime.Parse("1/4/2000", EnglishCulture), 4d), - new (DateTime.Parse("1/5/2000", EnglishCulture), 5d), - new (DateTime.Parse("1/6/2000", EnglishCulture), 6d), - new (DateTime.Parse("1/7/2000", EnglishCulture), 7d), - new (DateTime.Parse("1/8/2000", EnglishCulture), 8d), - new (DateTime.Parse("1/9/2000", EnglishCulture), 9d), - }; - - double sma; - - sma = SmaObserver.Increment(tpList, tpList.Count - 1, 9); - Assert.AreEqual(5d, sma); - - sma = SmaObserver.Increment(tpList, tpList.Count - 1, 10); - Assert.AreEqual(double.NaN, sma); - } - - [TestMethod] - public void Usee() - { - List quotesList = quotes - .ToSortedList(); - - int length = quotesList.Count; - - // time-series, for comparison - List staticSma = quotes - .Use(CandlePart.OC2) - .GetSma(11) - .ToList(); - - // setup quote provider - QuoteProvider provider = new(); - - // initialize EMA observer - List streamSma = provider - .Use(CandlePart.OC2) - .GetSma(11) - .ProtectedResults; - - // emulate adding quotes to provider - for (int i = 0; i < length; i++) - { - provider.Add(quotesList[i]); - } - - provider.EndTransmission(); - - // assert, should equal series - for (int i = 0; i < length; i++) - { - SmaResult t = staticSma[i]; - SmaResult s = streamSma[i]; - - Assert.AreEqual(t.Date, s.Date); - Assert.AreEqual(t.Sma, s.Sma); - } - } -} diff --git a/tests/indicators/s-z/Sma/Sma.Static.Tests.cs b/tests/indicators/s-z/Sma/Sma.StaticSeries.Tests.cs similarity index 59% rename from tests/indicators/s-z/Sma/Sma.Static.Tests.cs rename to tests/indicators/s-z/Sma/Sma.StaticSeries.Tests.cs index e6defb30a..d66fe61ff 100644 --- a/tests/indicators/s-z/Sma/Sma.Static.Tests.cs +++ b/tests/indicators/s-z/Sma/Sma.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class SmaTests : TestBase +public class Sma : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetSma(20) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -26,10 +25,9 @@ public void Standard() [TestMethod] public void CandlePartOpen() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Open) - .GetSma(20) - .ToList(); + .ToSma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Sma != null)); @@ -46,10 +44,9 @@ public void CandlePartOpen() [TestMethod] public void CandlePartVolume() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Volume) - .GetSma(20) - .ToList(); + .ToSma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Sma != null)); @@ -62,66 +59,50 @@ public void CandlePartVolume() Assert.AreEqual(157958070.8, r290.Sma); SmaResult r501 = results[501]; - Assert.AreEqual(DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", EnglishCulture), r501.Date); + Assert.AreEqual(DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", invariantCulture), r501.Timestamp); Assert.AreEqual(163695200, r501.Sma); } [TestMethod] public void Chainor() { - List results = quotes - .GetSma(10) - .GetEma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(10) + .ToEma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(484, results.Count(x => x.Ema != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetSma(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Sma is double and double.NaN)); - } - [TestMethod] public void NaN() { - List r = TestData.GetBtcUsdNan() - .GetSma(50) - .ToList(); + IReadOnlyList r = Data.GetBtcUsdNan() + .ToSma(50); - Assert.AreEqual(0, r.Count(x => x.Sma is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Sma is double.NaN)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetSma(15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToSma(15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Sma is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Sma is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetSma(5) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToSma(5); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetSma(5) - .ToList(); + IReadOnlyList r1 = Onequote + .ToSma(5); Assert.AreEqual(1, r1.Count); } @@ -129,19 +110,40 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetSma(20) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToSma(20) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 19, results.Count); - Assert.AreEqual(251.8600, results.LastOrDefault().Sma.Round(4)); + Assert.AreEqual(251.8600, results[^1].Sma.Round(4)); + } + + [TestMethod] + public void Equality() + { + SmaResult r1 = new(Timestamp: EvalDate, Sma: 1d); + + SmaResult r2 = new(Timestamp: EvalDate, Sma: 1d); + + SmaResult r3 = new(Timestamp: EvalDate, Sma: 2d); + + Assert.IsTrue(Equals(r1, r2)); + Assert.IsFalse(Equals(r1, r3)); + + Assert.IsTrue(r1.Equals(r2)); + Assert.IsFalse(r1.Equals(r3)); + + Assert.IsTrue(r1 == r2); + Assert.IsFalse(r1 == r3); + + Assert.IsFalse(r1 != r2); + Assert.IsTrue(r1 != r3); } // bad lookback period [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetSma(0)); + => Quotes.ToSma(0)); } diff --git a/tests/indicators/s-z/Sma/Sma.StreamHub.Tests.cs b/tests/indicators/s-z/Sma/Sma.StreamHub.Tests.cs new file mode 100644 index 000000000..c09661948 --- /dev/null +++ b/tests/indicators/s-z/Sma/Sma.StreamHub.Tests.cs @@ -0,0 +1,166 @@ +namespace StreamHub; + +[TestClass] +public class SmaHub : StreamHubTestBase, ITestChainObserver, ITestChainProvider +{ + [TestMethod] + public override void QuoteObserver() + { + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // prefill quotes to provider + for (int i = 0; i < 20; i++) + { + provider.Add(quotesList[i]); + } + + // initialize observer + SmaHub observer = provider + .ToSma(5); + + // fetch initial results (early) + IReadOnlyList streamList + = observer.Results; + + // emulate adding quotes to provider + for (int i = 20; i < length; i++) + { + // skip one (add later) + if (i == 80) + { + continue; + } + + Quote q = quotesList[i]; + provider.Add(q); + + // resend duplicate quotes + if (i is > 100 and < 105) + { + provider.Add(q); + } + } + + // late arrival + provider.Insert(quotesList[80]); + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // time-series, for comparison + IReadOnlyList seriesList + = quotesList + .ToSma(5); + + // assert, should equal series + streamList.Should().HaveCount(length - 1); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public void ChainObserver() + { + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // prefill quotes to provider + for (int i = 0; i < 50; i++) + { + provider.Add(quotesList[i]); + } + + // initialize observer + SmaHub observer = provider + .ToQuotePart(CandlePart.OC2) + .ToSma(11); + + // emulate quote stream + for (int i = 50; i < length; i++) + { + provider.Add(quotesList[i]); + } + + IReadOnlyList streamList = + observer.Results; + + // time-series, for comparison + IReadOnlyList seriesList + = quotesList + .Use(CandlePart.OC2) + .ToSma(11); + + // assert, should equal series + streamList.Should().HaveCount(length); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public void ChainProvider() + { + int emaPeriods = 12; + int smaPeriods = 8; + + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // initialize observer + EmaHub observer + = provider + .ToSma(smaPeriods) + .ToEma(emaPeriods); + + // emulate quote stream + for (int i = 0; i < length; i++) + { + provider.Add(quotesList[i]); + } + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // final results + IReadOnlyList streamList + = observer.Results; + + // time-series, for comparison + IReadOnlyList seriesList + = quotesList + .ToSma(smaPeriods) + .ToEma(emaPeriods); + + // assert, should equal series + streamList.Should().HaveCount(length - 1); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public override void CustomToString() + { + SmaHub hub = new(new QuoteHub(), 5); + hub.ToString().Should().Be("SMA(5)"); + } +} diff --git a/tests/indicators/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.Tests.cs b/tests/indicators/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.Tests.cs new file mode 100644 index 000000000..1b41ece3d --- /dev/null +++ b/tests/indicators/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.Tests.cs @@ -0,0 +1,98 @@ +namespace StaticSeries; + +[TestClass] +public class SmaAnalyses : StaticSeriesTestBase +{ + [TestMethod] + public override void Standard() + { + IReadOnlyList results = Quotes + .ToSmaAnalysis(20); + + // proper quantities + Assert.AreEqual(502, results.Count); + Assert.AreEqual(483, results.Count(x => x.Sma != null)); + + // sample value + SmaAnalysis r = results[501]; + Assert.AreEqual(251.86, r.Sma.Round(6)); + Assert.AreEqual(9.450000, r.Mad.Round(6)); + Assert.AreEqual(119.25102, r.Mse.Round(6)); + Assert.AreEqual(0.037637, r.Mape.Round(6)); + } + + [TestMethod] + public void UseReusable() + { + IReadOnlyList results = Quotes + .Use(CandlePart.Close) + .ToSmaAnalysis(20); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(483, results.Count(x => x.Sma != null)); + } + + [TestMethod] + public void Chainee() + { + IReadOnlyList results = Quotes + .ToSma(2) + .ToSmaAnalysis(20); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(482, results.Count(x => x.Sma != null)); + } + + [TestMethod] + public void Chainor() + { + IReadOnlyList results = Quotes + .ToSmaAnalysis(10) + .ToEma(10); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(484, results.Count(x => x.Ema != null)); + } + + [TestMethod] + public override void BadData() + { + IReadOnlyList r = BadQuotes + .ToSmaAnalysis(15); + + Assert.AreEqual(502, r.Count); + Assert.AreEqual(0, r.Count(x => x.Mape is double.NaN)); + } + + [TestMethod] + public override void NoQuotes() + { + IReadOnlyList r0 = Noquotes + .ToSmaAnalysis(6); + + Assert.AreEqual(0, r0.Count); + + IReadOnlyList r1 = Onequote + .ToSmaAnalysis(6); + + Assert.AreEqual(1, r1.Count); + } + + [TestMethod] + public void Removed() + { + IReadOnlyList results = Quotes + .ToSmaAnalysis(20) + .RemoveWarmupPeriods(); + + // assertions + Assert.AreEqual(502 - 19, results.Count); + Assert.AreEqual(251.8600, Math.Round(results[^1].Sma.Value, 4)); + } + + // bad lookback period + [TestMethod] + public void Exceptions() + => Assert.ThrowsException(() + => Quotes.ToSmaAnalysis(0)); +} diff --git a/tests/indicators/s-z/Smi/Smi.Tests.cs b/tests/indicators/s-z/Smi/Smi.StaticSeries.Tests.cs similarity index 71% rename from tests/indicators/s-z/Smi/Smi.Tests.cs rename to tests/indicators/s-z/Smi/Smi.StaticSeries.Tests.cs index 1d0ecba39..5dc184c73 100644 --- a/tests/indicators/s-z/Smi/Smi.Tests.cs +++ b/tests/indicators/s-z/Smi/Smi.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class SmiTests : TestBase +public class Smi : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetSmi(14, 20, 5, 3) - .ToList(); + IReadOnlyList results = Quotes + .ToSmi(14, 20, 5); // proper quantities Assert.AreEqual(502, results.Count); @@ -48,10 +47,9 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetSmi(14, 20, 5, 3) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToSmi(14, 20, 5) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(480, results.Count(x => x.Sma != null)); @@ -60,9 +58,8 @@ public void Chainor() [TestMethod] public void NoSignal() { - List results = quotes - .GetSmi(5, 20, 20, 1) - .ToList(); + IReadOnlyList results = Quotes + .ToSmi(5, 20, 20, 1); // signal equals oscillator SmiResult r1 = results[487]; @@ -75,9 +72,8 @@ public void NoSignal() [TestMethod] public void SmallPeriods() { - List results = quotes - .GetSmi(1, 1, 1, 5) - .ToList(); + IReadOnlyList results = Quotes + .ToSmi(1, 1, 1, 5); // sample values SmiResult r51 = results[51]; @@ -94,28 +90,25 @@ public void SmallPeriods() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetSmi(5, 5, 1, 5) - .ToList(); + IReadOnlyList r = BadQuotes + .ToSmi(5, 5, 1, 5); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Smi is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Smi is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetSmi(5, 5, 2) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToSmi(5, 5); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetSmi(5, 3, 3) - .ToList(); + IReadOnlyList r1 = Onequote + .ToSmi(5, 3, 3); Assert.AreEqual(1, r1.Count); } @@ -123,15 +116,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetSmi(14, 20, 5, 3) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToSmi(14, 20, 5) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(501 - (14 + 100), results.Count); - SmiResult last = results.LastOrDefault(); + SmiResult last = results[^1]; Assert.AreEqual(-52.6560, last.Smi.Round(4)); Assert.AreEqual(-54.1903, last.Signal.Round(4)); } @@ -141,18 +133,18 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - quotes.GetSmi(0, 5, 5, 5)); + Quotes.ToSmi(0, 5, 5, 5)); // bad first smooth period Assert.ThrowsException(() => - quotes.GetSmi(14, 0, 5, 5)); + Quotes.ToSmi(14, 0, 5, 5)); // bad second smooth period Assert.ThrowsException(() => - quotes.GetSmi(14, 3, 0, 5)); + Quotes.ToSmi(14, 3, 0, 5)); // bad signal Assert.ThrowsException(() => - quotes.GetSmi(9, 3, 1, 0)); + Quotes.ToSmi(9, 3, 1, 0)); } } diff --git a/tests/indicators/s-z/Smma/Smma.Tests.cs b/tests/indicators/s-z/Smma/Smma.StaticSeries.Tests.cs similarity index 54% rename from tests/indicators/s-z/Smma/Smma.Tests.cs rename to tests/indicators/s-z/Smma/Smma.StaticSeries.Tests.cs index 27750da1a..b2370be85 100644 --- a/tests/indicators/s-z/Smma/Smma.Tests.cs +++ b/tests/indicators/s-z/Smma/Smma.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class SmmaTests : TestBase +public class Smma : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetSmma(20) - .ToList(); + IReadOnlyList results = Quotes + .ToSmma(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -27,35 +26,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetSmma(20) - .ToList(); + .ToSmma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Smma != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetSmma(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Smma is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetSmma(20) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToSmma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Smma != null)); @@ -64,38 +50,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetSmma(20) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToSmma(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetSmma(15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToSmma(15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Smma is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Smma is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetSmma(5) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToSmma(5); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetSmma(5) - .ToList(); + IReadOnlyList r1 = Onequote + .ToSmma(5); Assert.AreEqual(1, r1.Count); } @@ -103,19 +85,18 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetSmma(20) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToSmma(20) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (20 + 100), results.Count); - Assert.AreEqual(255.67462, Math.Round(results.LastOrDefault().Smma.Value, 5)); + Assert.AreEqual(255.67462, Math.Round(results[^1].Smma.Value, 5)); } // bad lookback period [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetSmma(0)); + => Quotes.ToSmma(0)); } diff --git a/tests/indicators/s-z/StarcBands/StarcBands.Tests.cs b/tests/indicators/s-z/StarcBands/StarcBands.StaticSeries.Tests.cs similarity index 70% rename from tests/indicators/s-z/StarcBands/StarcBands.Tests.cs rename to tests/indicators/s-z/StarcBands/StarcBands.StaticSeries.Tests.cs index 08a7a9ce4..86030fed3 100644 --- a/tests/indicators/s-z/StarcBands/StarcBands.Tests.cs +++ b/tests/indicators/s-z/StarcBands/StarcBands.StaticSeries.Tests.cs @@ -1,18 +1,17 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class StarcBandsTests : TestBase +public class StarcBands : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { int smaPeriods = 20; int multiplier = 2; int atrPeriods = 14; - List results = quotes - .GetStarcBands(smaPeriods, multiplier, atrPeriods) - .ToList(); + IReadOnlyList results = Quotes + .ToStarcBands(smaPeriods, multiplier, atrPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -48,28 +47,25 @@ public void Standard() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetStarcBands(10, 3, 15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToStarcBands(10, 3, 15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.UpperBand is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.UpperBand is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetStarcBands(10) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToStarcBands(10); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetStarcBands(10) - .ToList(); + IReadOnlyList r1 = Onequote + .ToStarcBands(10); Assert.AreEqual(1, r1.Count); } @@ -82,15 +78,14 @@ public void Condense() int atrPeriods = 14; int lookbackPeriods = Math.Max(smaPeriods, atrPeriods); - List results = quotes - .GetStarcBands(smaPeriods, multiplier, atrPeriods) - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToStarcBands(smaPeriods, multiplier, atrPeriods) + .Condense(); // assertions Assert.AreEqual(502 - lookbackPeriods + 1, results.Count); - StarcBandsResult last = results.LastOrDefault(); + StarcBandsResult last = results[^1]; Assert.AreEqual(251.8600, last.Centerline.Round(4)); Assert.AreEqual(264.1595, last.UpperBand.Round(4)); Assert.AreEqual(239.5605, last.LowerBand.Round(4)); @@ -104,15 +99,14 @@ public void Removed() int atrPeriods = 14; int lookbackPeriods = Math.Max(smaPeriods, atrPeriods); - List results = quotes - .GetStarcBands(smaPeriods, multiplier, atrPeriods) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToStarcBands(smaPeriods, multiplier, atrPeriods) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (lookbackPeriods + 150), results.Count); - StarcBandsResult last = results.LastOrDefault(); + StarcBandsResult last = results[^1]; Assert.AreEqual(251.8600, last.Centerline.Round(4)); Assert.AreEqual(264.1595, last.UpperBand.Round(4)); Assert.AreEqual(239.5605, last.LowerBand.Round(4)); @@ -123,14 +117,14 @@ public void Exceptions() { // bad EMA period Assert.ThrowsException(() => - quotes.GetStarcBands(1, 2, 10)); + Quotes.ToStarcBands(1)); // bad ATR period Assert.ThrowsException(() => - quotes.GetStarcBands(20, 2, 1)); + Quotes.ToStarcBands(20, 2, 1)); // bad multiplier Assert.ThrowsException(() => - quotes.GetStarcBands(20, 0, 10)); + Quotes.ToStarcBands(20, 0)); } } diff --git a/tests/indicators/s-z/Stc/Stc.Tests.cs b/tests/indicators/s-z/Stc/Stc.StaticSeries.Tests.cs similarity index 55% rename from tests/indicators/s-z/Stc/Stc.Tests.cs rename to tests/indicators/s-z/Stc/Stc.StaticSeries.Tests.cs index fda82df9b..9cf0fbb6b 100644 --- a/tests/indicators/s-z/Stc/Stc.Tests.cs +++ b/tests/indicators/s-z/Stc/Stc.StaticSeries.Tests.cs @@ -1,23 +1,17 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class StcTests : TestBase +public class Stc : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { int cyclePeriods = 9; int fastPeriods = 12; int slowPeriods = 26; - List results = - quotes.GetStc(cyclePeriods, fastPeriods, slowPeriods) - .ToList(); - - foreach (StcResult r in results) - { - Console.WriteLine($"{r.Date:d},{r.Stc:N4}"); - } + IReadOnlyList results = Quotes + .ToStc(cyclePeriods, fastPeriods, slowPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -36,40 +30,27 @@ public void Standard() StcResult r249 = results[249]; Assert.AreEqual(27.7340, r249.Stc.Round(4)); - StcResult last = results.LastOrDefault(); + StcResult last = results[^1]; Assert.AreEqual(19.2544, last.Stc.Round(4)); } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetStc(9, 12, 26) - .ToList(); + .ToStc(9, 12, 26); Assert.AreEqual(502, results.Count); Assert.AreEqual(467, results.Count(x => x.Stc != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetStc() - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Stc is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetStc(9, 12, 26) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToStc(9, 12, 26); Assert.AreEqual(502, results.Count); Assert.AreEqual(466, results.Count(x => x.Stc != null)); @@ -78,38 +59,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetStc(9, 12, 26) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToStc(9, 12, 26) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(458, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetStc(10, 23, 50) - .ToList(); + IReadOnlyList r = BadQuotes + .ToStc(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Stc is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Stc is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetStc() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToStc(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetStc() - .ToList(); + IReadOnlyList r1 = Onequote + .ToStc(); Assert.AreEqual(1, r1.Count); } @@ -121,9 +98,8 @@ public void Issue1107() RandomGbm quotes = new(58); - List results = quotes - .GetStc(10, 23, 50) - .ToList(); + IReadOnlyList results = quotes + .ToStc(); Assert.AreEqual(58, results.Count); } @@ -135,15 +111,14 @@ public void Removed() int fastPeriods = 12; int slowPeriods = 26; - List results = quotes - .GetStc(cyclePeriods, fastPeriods, slowPeriods) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToStc(cyclePeriods, fastPeriods, slowPeriods) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (slowPeriods + cyclePeriods + 250), results.Count); - StcResult last = results.LastOrDefault(); + StcResult last = results[^1]; Assert.AreEqual(19.2544, last.Stc.Round(4)); } @@ -152,14 +127,14 @@ public void Exceptions() { // bad fast period Assert.ThrowsException(() => - quotes.GetStc(9, 0, 26)); + Quotes.ToStc(9, 0, 26)); // bad slow periods must be larger than faster period Assert.ThrowsException(() => - quotes.GetStc(9, 12, 12)); + Quotes.ToStc(9, 12, 12)); // bad signal period Assert.ThrowsException(() => - quotes.GetStc(-1, 12, 26)); + Quotes.ToStc(-1, 12, 26)); } } diff --git a/tests/indicators/s-z/StdDev/StdDev.StaticSeries.Tests.cs b/tests/indicators/s-z/StdDev/StdDev.StaticSeries.Tests.cs new file mode 100644 index 000000000..c4fbc30b0 --- /dev/null +++ b/tests/indicators/s-z/StdDev/StdDev.StaticSeries.Tests.cs @@ -0,0 +1,127 @@ +namespace StaticSeries; + +[TestClass] +public class StdDev : StaticSeriesTestBase +{ + [TestMethod] + public override void Standard() + { + IReadOnlyList results = Quotes + .ToStdDev(10); + + // proper quantities + Assert.AreEqual(502, results.Count); + Assert.AreEqual(493, results.Count(x => x.StdDev != null)); + Assert.AreEqual(493, results.Count(x => x.ZScore != null)); + + // sample values + StdDevResult r1 = results[8]; + Assert.AreEqual(null, r1.StdDev); + Assert.AreEqual(null, r1.Mean); + Assert.AreEqual(null, r1.ZScore); + + StdDevResult r2 = results[9]; + Assert.AreEqual(0.5020, r2.StdDev.Round(4)); + Assert.AreEqual(214.0140, r2.Mean.Round(4)); + Assert.AreEqual(-0.525917, r2.ZScore.Round(6)); + + StdDevResult r3 = results[249]; + Assert.AreEqual(0.9827, r3.StdDev.Round(4)); + Assert.AreEqual(257.2200, r3.Mean.Round(4)); + Assert.AreEqual(0.783563, r3.ZScore.Round(6)); + + StdDevResult r4 = results[501]; + Assert.AreEqual(5.4738, r4.StdDev.Round(4)); + Assert.AreEqual(242.4100, r4.Mean.Round(4)); + Assert.AreEqual(0.524312, r4.ZScore.Round(6)); + } + + [TestMethod] + public void UseReusable() + { + IReadOnlyList results = Quotes + .Use(CandlePart.Close) + .ToStdDev(10); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(493, results.Count(x => x.StdDev != null)); + } + + [TestMethod] + public void Chainee() + { + IReadOnlyList results = Quotes + .ToSma(2) + .ToStdDev(10); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(492, results.Count(x => x.StdDev != null)); + } + + [TestMethod] + public void Chainor() + { + IReadOnlyList results = Quotes + .ToStdDev(10) + .ToSma(10); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(484, results.Count(x => x.Sma != null)); + } + + [TestMethod] + public override void BadData() + { + IReadOnlyList r = BadQuotes + .ToStdDev(15); + + Assert.AreEqual(502, r.Count); + Assert.AreEqual(0, r.Count(x => x.StdDev is double.NaN)); + } + + [TestMethod] + public void BigData() + { + IReadOnlyList r = BigQuotes + .ToStdDev(200); + + Assert.AreEqual(1246, r.Count); + } + + [TestMethod] + public override void NoQuotes() + { + IReadOnlyList r0 = Noquotes + .ToStdDev(10); + + Assert.AreEqual(0, r0.Count); + + IReadOnlyList r1 = Onequote + .ToStdDev(10); + + Assert.AreEqual(1, r1.Count); + } + + [TestMethod] + public void Removed() + { + IReadOnlyList results = Quotes + .ToStdDev(10) + .RemoveWarmupPeriods(); + + // assertions + Assert.AreEqual(502 - 9, results.Count); + + StdDevResult last = results[^1]; + Assert.AreEqual(5.4738, last.StdDev.Round(4)); + Assert.AreEqual(242.4100, last.Mean.Round(4)); + Assert.AreEqual(0.524312, last.ZScore.Round(6)); + } + + [TestMethod] + public void Exceptions() => + + // bad lookback period + Assert.ThrowsException(() => + Quotes.ToStdDev(1)); +} diff --git a/tests/indicators/s-z/StdDev/StdDev.Tests.cs b/tests/indicators/s-z/StdDev/StdDev.Tests.cs deleted file mode 100644 index 54a51fc51..000000000 --- a/tests/indicators/s-z/StdDev/StdDev.Tests.cs +++ /dev/null @@ -1,185 +0,0 @@ -namespace Tests.Indicators; - -[TestClass] -public class StdDevTests : TestBase -{ - [TestMethod] - public void Standard() - { - List results = quotes - .GetStdDev(10) - .ToList(); - - // proper quantities - Assert.AreEqual(502, results.Count); - Assert.AreEqual(493, results.Count(x => x.StdDev != null)); - Assert.AreEqual(493, results.Count(x => x.ZScore != null)); - Assert.AreEqual(false, results.Any(x => x.StdDevSma != null)); - - // sample values - StdDevResult r1 = results[8]; - Assert.AreEqual(null, r1.StdDev); - Assert.AreEqual(null, r1.Mean); - Assert.AreEqual(null, r1.ZScore); - Assert.AreEqual(null, r1.StdDevSma); - - StdDevResult r2 = results[9]; - Assert.AreEqual(0.5020, r2.StdDev.Round(4)); - Assert.AreEqual(214.0140, r2.Mean.Round(4)); - Assert.AreEqual(-0.525917, r2.ZScore.Round(6)); - Assert.AreEqual(null, r2.StdDevSma); - - StdDevResult r3 = results[249]; - Assert.AreEqual(0.9827, r3.StdDev.Round(4)); - Assert.AreEqual(257.2200, r3.Mean.Round(4)); - Assert.AreEqual(0.783563, r3.ZScore.Round(6)); - Assert.AreEqual(null, r3.StdDevSma); - - StdDevResult r4 = results[501]; - Assert.AreEqual(5.4738, r4.StdDev.Round(4)); - Assert.AreEqual(242.4100, r4.Mean.Round(4)); - Assert.AreEqual(0.524312, r4.ZScore.Round(6)); - Assert.AreEqual(null, r4.StdDevSma); - } - - [TestMethod] - public void UseTuple() - { - List results = quotes - .Use(CandlePart.Close) - .GetStdDev(10) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(493, results.Count(x => x.StdDev != null)); - } - - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetStdDev(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.StdDev is double and double.NaN)); - } - - [TestMethod] - public void Chainee() - { - List results = quotes - .GetSma(2) - .GetStdDev(10) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(492, results.Count(x => x.StdDev != null)); - } - - [TestMethod] - public void Chainor() - { - List results = quotes - .GetStdDev(10) - .GetSma(10) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(484, results.Count(x => x.Sma != null)); - } - - [TestMethod] - public void WithSma() - { - int lookbackPeriods = 10; - int smaPeriods = 5; - List results = quotes - .GetStdDev(lookbackPeriods, smaPeriods) - .ToList(); - - // proper quantities - Assert.AreEqual(502, results.Count); - Assert.AreEqual(493, results.Count(x => x.StdDev != null)); - Assert.AreEqual(493, results.Count(x => x.ZScore != null)); - Assert.AreEqual(489, results.Count(x => x.StdDevSma != null)); - - // sample values - StdDevResult r1 = results[19]; - Assert.AreEqual(1.1642, r1.StdDev.Round(4)); - Assert.AreEqual(-0.065282, r1.ZScore.Round(6)); - Assert.AreEqual(1.1422, r1.StdDevSma.Round(4)); - - StdDevResult r2 = results[501]; - Assert.AreEqual(5.4738, r2.StdDev.Round(4)); - Assert.AreEqual(0.524312, r2.ZScore.Round(6)); - Assert.AreEqual(7.6886, r2.StdDevSma.Round(4)); - } - - [TestMethod] - public void BadData() - { - List r = badQuotes - .GetStdDev(15, 3) - .ToList(); - - Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.StdDev is double and double.NaN)); - } - - [TestMethod] - public void BigData() - { - List r = bigQuotes - .GetStdDev(200, 3) - .ToList(); - - Assert.AreEqual(1246, r.Count); - } - - [TestMethod] - public void NoQuotes() - { - List r0 = noquotes - .GetStdDev(10) - .ToList(); - - Assert.AreEqual(0, r0.Count); - - List r1 = onequote - .GetStdDev(10) - .ToList(); - - Assert.AreEqual(1, r1.Count); - } - - [TestMethod] - public void Removed() - { - List results = quotes - .GetStdDev(10) - .RemoveWarmupPeriods() - .ToList(); - - // assertions - Assert.AreEqual(502 - 9, results.Count); - - StdDevResult last = results.LastOrDefault(); - Assert.AreEqual(5.4738, last.StdDev.Round(4)); - Assert.AreEqual(242.4100, last.Mean.Round(4)); - Assert.AreEqual(0.524312, last.ZScore.Round(6)); - Assert.AreEqual(null, last.StdDevSma); - } - - [TestMethod] - public void Exceptions() - { - // bad lookback period - Assert.ThrowsException(() => - quotes.GetStdDev(1)); - - // bad SMA period - Assert.ThrowsException(() => - quotes.GetStdDev(14, 0)); - } -} diff --git a/tests/indicators/s-z/StdDevChannels/StdDevChannels.Tests.cs b/tests/indicators/s-z/StdDevChannels/StdDevChannels.StaticSeries.Tests.cs similarity index 73% rename from tests/indicators/s-z/StdDevChannels/StdDevChannels.Tests.cs rename to tests/indicators/s-z/StdDevChannels/StdDevChannels.StaticSeries.Tests.cs index ad6abad22..dc145f82b 100644 --- a/tests/indicators/s-z/StdDevChannels/StdDevChannels.Tests.cs +++ b/tests/indicators/s-z/StdDevChannels/StdDevChannels.StaticSeries.Tests.cs @@ -1,17 +1,16 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class StdDevChannelsTests : TestBase +public class StdDevChannels : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { int lookbackPeriods = 20; double standardDeviations = 2; - List results = - quotes.GetStdDevChannels(lookbackPeriods, standardDeviations) - .ToList(); + IReadOnlyList results = + Quotes.ToStdDevChannels(lookbackPeriods, standardDeviations); // proper quantities Assert.AreEqual(502, results.Count); @@ -68,9 +67,8 @@ public void FullHistory() { // null provided for lookback period - List results = - quotes.GetStdDevChannels(null, 2) - .ToList(); + IReadOnlyList results = + Quotes.ToStdDevChannels(null); // proper quantities Assert.AreEqual(502, results.Count); @@ -98,63 +96,47 @@ public void FullHistory() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetStdDevChannels(20, 2) - .ToList(); + .ToStdDevChannels(); Assert.AreEqual(502, results.Count); Assert.AreEqual(500, results.Count(x => x.Centerline != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetStdDevChannels(6, 1.1) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.UpperChannel is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetStdDevChannels(20, 2) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToStdDevChannels(); Assert.AreEqual(502, results.Count); Assert.AreEqual(500, results.Count(x => x.Centerline != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetStdDevChannels() - .ToList(); + IReadOnlyList r = BadQuotes + .ToStdDevChannels(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.UpperChannel is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.UpperChannel is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetStdDevChannels() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToStdDevChannels(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetStdDevChannels() - .ToList(); + IReadOnlyList r1 = Onequote + .ToStdDevChannels(); Assert.AreEqual(1, r1.Count); } @@ -165,14 +147,13 @@ public void Condense() int lookbackPeriods = 20; double standardDeviations = 2; - List results = quotes - .GetStdDevChannels(lookbackPeriods, standardDeviations) - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToStdDevChannels(lookbackPeriods, standardDeviations) + .Condense(); // assertions Assert.AreEqual(500, results.Count); - StdDevChannelsResult last = results.LastOrDefault(); + StdDevChannelsResult last = results[^1]; Assert.AreEqual(235.8131, last.Centerline.Round(4)); Assert.AreEqual(257.6536, last.UpperChannel.Round(4)); Assert.AreEqual(213.9727, last.LowerChannel.Round(4)); @@ -185,14 +166,13 @@ public void Removed() int lookbackPeriods = 20; double standardDeviations = 2; - List results = quotes - .GetStdDevChannels(lookbackPeriods, standardDeviations) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToStdDevChannels(lookbackPeriods, standardDeviations) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(500, results.Count); - StdDevChannelsResult last = results.LastOrDefault(); + StdDevChannelsResult last = results[^1]; Assert.AreEqual(235.8131, last.Centerline.Round(4)); Assert.AreEqual(257.6536, last.UpperChannel.Round(4)); Assert.AreEqual(213.9727, last.LowerChannel.Round(4)); @@ -204,10 +184,10 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - quotes.GetStdDevChannels(0)); + Quotes.ToStdDevChannels(0)); // bad standard deviations Assert.ThrowsException(() => - quotes.GetStdDevChannels(20, 0)); + Quotes.ToStdDevChannels(20, 0)); } } diff --git a/tests/indicators/s-z/Stoch/Stoch.Tests.cs b/tests/indicators/s-z/Stoch/Stoch.StaticSeries.Tests.cs similarity index 75% rename from tests/indicators/s-z/Stoch/Stoch.Tests.cs rename to tests/indicators/s-z/Stoch/Stoch.StaticSeries.Tests.cs index 9abee8370..6e8fe7a1b 100644 --- a/tests/indicators/s-z/Stoch/Stoch.Tests.cs +++ b/tests/indicators/s-z/Stoch/Stoch.StaticSeries.Tests.cs @@ -1,18 +1,17 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class StochTests : TestBase +public class Stoch : StaticSeriesTestBase { [TestMethod] - public void Standard() // Slow + public override void Standard() // Slow { int lookbackPeriods = 14; int signalPeriods = 3; int smoothPeriods = 3; - List results = quotes - .GetStoch(lookbackPeriods, signalPeriods, smoothPeriods) - .ToList(); + IReadOnlyList results = Quotes + .ToStoch(lookbackPeriods, signalPeriods, smoothPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -47,10 +46,8 @@ public void Standard() // Slow // test boundary condition - for (int i = 0; i < results.Count; i++) + foreach (StochResult r in results) { - StochResult r = results[i]; - if (r.Oscillator is not null) { Assert.IsTrue(r.Oscillator >= 0); @@ -72,11 +69,10 @@ public void Standard() // Slow } [TestMethod] - public void Extended() // with extra parameteres + public void Extended() // with extra parameters { - List results = - quotes.GetStoch(9, 3, 3, 5, 4, MaType.SMMA) - .ToList(); + IReadOnlyList results = + Quotes.ToStoch(9, 3, 3, 5, 4, MaType.SMMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -118,10 +114,9 @@ public void Extended() // with extra parameteres [TestMethod] public void Chainor() { - List results = quotes - .GetStoch() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToStoch() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(478, results.Count(x => x.Sma != null)); @@ -130,13 +125,12 @@ public void Chainor() [TestMethod] public void NoSignal() { - int lookbackPeriods = 5; - int signalPeriods = 1; - int smoothPeriods = 3; + const int lookbackPeriods = 5; + const int signalPeriods = 1; + const int smoothPeriods = 3; - List results = quotes - .GetStoch(lookbackPeriods, signalPeriods, smoothPeriods) - .ToList(); + IReadOnlyList results = Quotes + .ToStoch(lookbackPeriods, signalPeriods, smoothPeriods); // signal equals oscillator StochResult r1 = results[487]; @@ -153,9 +147,8 @@ public void Fast() int signalPeriods = 10; int smoothPeriods = 1; - List results = quotes - .GetStoch(lookbackPeriods, signalPeriods, smoothPeriods) - .ToList(); + IReadOnlyList results = Quotes + .ToStoch(lookbackPeriods, signalPeriods, smoothPeriods); // sample values StochResult r1 = results[487]; @@ -174,9 +167,8 @@ public void FastSmall() int signalPeriods = 10; int smoothPeriods = 1; - List results = quotes - .GetStoch(lookbackPeriods, signalPeriods, smoothPeriods) - .ToList(); + IReadOnlyList results = Quotes + .ToStoch(lookbackPeriods, signalPeriods, smoothPeriods); // sample values StochResult r1 = results[70]; @@ -187,28 +179,25 @@ public void FastSmall() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetStoch(15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToStoch(15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Oscillator is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Oscillator is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetStoch() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToStoch(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetStoch() - .ToList(); + IReadOnlyList r1 = Onequote + .ToStoch(); Assert.AreEqual(1, r1.Count); } @@ -220,15 +209,14 @@ public void Removed() int signalPeriods = 3; int smoothPeriods = 3; - List results = quotes - .GetStoch(lookbackPeriods, signalPeriods, smoothPeriods) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToStoch(lookbackPeriods, signalPeriods, smoothPeriods) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (lookbackPeriods + smoothPeriods - 2), results.Count); - StochResult last = results.LastOrDefault(); + StochResult last = results[^1]; Assert.AreEqual(43.1353, last.Oscillator.Round(4)); Assert.AreEqual(35.5674, last.Signal.Round(4)); Assert.AreEqual(58.2712, last.PercentJ.Round(4)); @@ -241,10 +229,9 @@ public void Boundary() int signalPeriods = 3; int smoothPeriods = 3; - List results = TestData + IReadOnlyList results = Data .GetRandom(2500) - .GetStoch(lookbackPeriods, signalPeriods, smoothPeriods) - .ToList(); + .ToStoch(lookbackPeriods, signalPeriods, smoothPeriods); // test boundary condition @@ -277,26 +264,26 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - quotes.GetStoch(0)); + Quotes.ToStoch(0)); // bad signal period Assert.ThrowsException(() => - quotes.GetStoch(14, 0)); + Quotes.ToStoch(14, 0)); // bad smoothing period Assert.ThrowsException(() => - quotes.GetStoch(14, 3, 0)); + Quotes.ToStoch(14, 3, 0)); // bad kFactor Assert.ThrowsException(() => - quotes.GetStoch(9, 3, 1, 0, 2, MaType.SMA)); + Quotes.ToStoch(9, 3, 1, 0, 2, MaType.SMA)); // bad dFactor Assert.ThrowsException(() => - quotes.GetStoch(9, 3, 1, 3, 0, MaType.SMA)); + Quotes.ToStoch(9, 3, 1, 3, 0, MaType.SMA)); // bad MA type Assert.ThrowsException(() => - quotes.GetStoch(9, 3, 3, 3, 2, MaType.ALMA)); + Quotes.ToStoch(9, 3, 3, 3, 2, MaType.ALMA)); } } diff --git a/tests/indicators/s-z/StochRsi/StochRsi.Tests.cs b/tests/indicators/s-z/StochRsi/StochRsi.StaticSeries.Tests.cs similarity index 65% rename from tests/indicators/s-z/StochRsi/StochRsi.Tests.cs rename to tests/indicators/s-z/StochRsi/StochRsi.StaticSeries.Tests.cs index 966e9b40f..2efbd0983 100644 --- a/tests/indicators/s-z/StochRsi/StochRsi.Tests.cs +++ b/tests/indicators/s-z/StochRsi/StochRsi.StaticSeries.Tests.cs @@ -1,19 +1,18 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class StochRsiTests : TestBase +public class StochRsi : StaticSeriesTestBase { [TestMethod] - public void FastRsi() + public override void Standard() // Fast RSI { int rsiPeriods = 14; int stochPeriods = 14; int signalPeriods = 3; int smoothPeriods = 1; - List results = - quotes.GetStochRsi(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods) - .ToList(); + IReadOnlyList results = + Quotes.ToStochRsi(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods); // assertions @@ -48,9 +47,8 @@ public void SlowRsi() int signalPeriods = 3; int smoothPeriods = 3; - List results = - quotes.GetStochRsi(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods) - .ToList(); + IReadOnlyList results = + Quotes.ToStochRsi(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods); // assertions @@ -78,36 +76,23 @@ public void SlowRsi() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetStochRsi(14, 14, 3, 1) - .ToList(); + .ToStochRsi(14, 14, 3); Assert.AreEqual(502, results.Count); Assert.AreEqual(475, results.Count(x => x.StochRsi != null)); - Assert.AreEqual(0, results.Count(x => x.StochRsi is double and double.NaN)); - } - - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetStochRsi(14, 14, 3, 1) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.StochRsi is double and double.NaN)); + Assert.AreEqual(0, results.Count(x => x.StochRsi is double.NaN)); } [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetStochRsi(14, 14, 3, 1) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToStochRsi(14, 14, 3); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.StochRsi != null)); @@ -116,38 +101,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetStochRsi(14, 14, 3, 3) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToStochRsi(14, 14, 3, 3) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(464, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetStochRsi(15, 20, 3, 2) - .ToList(); + IReadOnlyList r = BadQuotes + .ToStochRsi(15, 20, 3, 2); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.StochRsi is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.StochRsi is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetStochRsi(10, 20, 3) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToStochRsi(10, 20, 3); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetStochRsi(8, 13, 2) - .ToList(); + IReadOnlyList r1 = Onequote + .ToStochRsi(8, 13, 2); Assert.AreEqual(1, r1.Count); } @@ -160,16 +141,15 @@ public void Removed() int signalPeriods = 3; int smoothPeriods = 3; - List results = quotes - .GetStochRsi(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToStochRsi(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods) + .RemoveWarmupPeriods(); // assertions int removeQty = rsiPeriods + stochPeriods + smoothPeriods + 100; Assert.AreEqual(502 - removeQty, results.Count); - StochRsiResult last = results.LastOrDefault(); + StochRsiResult last = results[^1]; Assert.AreEqual(89.8385, last.StochRsi.Round(4)); Assert.AreEqual(73.4176, last.Signal.Round(4)); } @@ -179,18 +159,18 @@ public void Exceptions() { // bad RSI period Assert.ThrowsException(() => - quotes.GetStochRsi(0, 14, 3, 1)); + Quotes.ToStochRsi(0, 14, 3)); // bad STO period Assert.ThrowsException(() => - quotes.GetStochRsi(14, 0, 3, 3)); + Quotes.ToStochRsi(14, 0, 3, 3)); // bad STO signal period Assert.ThrowsException(() => - quotes.GetStochRsi(14, 14, 0)); + Quotes.ToStochRsi(14, 14, 0)); // bad STO smoothing period Assert.ThrowsException(() => - quotes.GetStochRsi(14, 14, 3, 0)); + Quotes.ToStochRsi(14, 14, 3, 0)); } } diff --git a/tests/indicators/s-z/SuperTrend/SuperTrend.Tests.cs b/tests/indicators/s-z/SuperTrend/SuperTrend.StaticSeries.Tests.cs similarity index 68% rename from tests/indicators/s-z/SuperTrend/SuperTrend.Tests.cs rename to tests/indicators/s-z/SuperTrend/SuperTrend.StaticSeries.Tests.cs index 47232f3fd..3b652b756 100644 --- a/tests/indicators/s-z/SuperTrend/SuperTrend.Tests.cs +++ b/tests/indicators/s-z/SuperTrend/SuperTrend.StaticSeries.Tests.cs @@ -1,17 +1,16 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class SuperTrendTests : TestBase +public class SuperTrend : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - int lookbackPeriods = 14; - double multiplier = 3; + const int lookbackPeriods = 14; + const double multiplier = 3; - List results = quotes - .GetSuperTrend(lookbackPeriods, multiplier) - .ToList(); + IReadOnlyList results = Quotes + .ToSuperTrend(lookbackPeriods, multiplier); // proper quantities Assert.AreEqual(502, results.Count); @@ -52,11 +51,10 @@ public void Standard() [TestMethod] public void Bitcoin() { - IEnumerable h = TestData.GetBitcoin(); + IReadOnlyList h = Data.GetBitcoin(); - List results = h - .GetSuperTrend(10, 3) - .ToList(); + IReadOnlyList results = h + .ToSuperTrend(); Assert.AreEqual(1246, results.Count); @@ -65,27 +63,24 @@ public void Bitcoin() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetSuperTrend(7) - .ToList(); + IReadOnlyList r = BadQuotes + .ToSuperTrend(7); Assert.AreEqual(502, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetSuperTrend() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToSuperTrend(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetSuperTrend() - .ToList(); + IReadOnlyList r1 = Onequote + .ToSuperTrend(); Assert.AreEqual(1, r1.Count); } @@ -96,15 +91,14 @@ public void Condense() int lookbackPeriods = 14; double multiplier = 3; - List results = quotes - .GetSuperTrend(lookbackPeriods, multiplier) - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToSuperTrend(lookbackPeriods, multiplier) + .Condense(); // assertions Assert.AreEqual(488, results.Count); - SuperTrendResult last = results.LastOrDefault(); + SuperTrendResult last = results[^1]; Assert.AreEqual(250.7954m, last.SuperTrend.Round(4)); Assert.AreEqual(last.SuperTrend, last.UpperBand); Assert.AreEqual(null, last.LowerBand); @@ -116,15 +110,14 @@ public void Removed() int lookbackPeriods = 14; double multiplier = 3; - List results = quotes - .GetSuperTrend(lookbackPeriods, multiplier) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToSuperTrend(lookbackPeriods, multiplier) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(488, results.Count); - SuperTrendResult last = results.LastOrDefault(); + SuperTrendResult last = results[^1]; Assert.AreEqual(250.7954m, last.SuperTrend.Round(4)); Assert.AreEqual(last.SuperTrend, last.UpperBand); Assert.AreEqual(null, last.LowerBand); @@ -135,10 +128,10 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - quotes.GetSuperTrend(1)); + Quotes.ToSuperTrend(1)); // bad multiplier Assert.ThrowsException(() => - quotes.GetSuperTrend(7, 0)); + Quotes.ToSuperTrend(7, 0)); } } diff --git a/tests/indicators/s-z/T3/T3.Tests.cs b/tests/indicators/s-z/T3/T3.StaticSeries.Tests.cs similarity index 57% rename from tests/indicators/s-z/T3/T3.Tests.cs rename to tests/indicators/s-z/T3/T3.StaticSeries.Tests.cs index 145a773e2..5cb40775b 100644 --- a/tests/indicators/s-z/T3/T3.Tests.cs +++ b/tests/indicators/s-z/T3/T3.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class T3Tests : TestBase +public class T3 : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetT3(5, 0.7) - .ToList(); + IReadOnlyList results = Quotes + .ToT3(); // proper quantities Assert.AreEqual(502, results.Count); @@ -35,35 +34,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetT3() - .ToList(); + .ToT3(); Assert.AreEqual(502, results.Count); Assert.AreEqual(502, results.Count(x => x.T3 != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetT3() - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.T3 is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetT3() - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToT3(); Assert.AreEqual(502, results.Count); Assert.AreEqual(501, results.Count(x => x.T3 != null)); @@ -72,37 +58,33 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetT3() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToT3() + .ToSma(10); Assert.AreEqual(502, results.Count); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetT3() - .ToList(); + IReadOnlyList r = BadQuotes + .ToT3(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.T3 is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.T3 is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetT3() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToT3(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetT3() - .ToList(); + IReadOnlyList r1 = Onequote + .ToT3(); Assert.AreEqual(1, r1.Count); } @@ -112,10 +94,10 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - quotes.GetT3(0)); + Quotes.ToT3(0)); // bad volume factor Assert.ThrowsException(() => - quotes.GetT3(25, 0)); + Quotes.ToT3(25, 0)); } } diff --git a/tests/indicators/s-z/Tema/Tema.Tests.cs b/tests/indicators/s-z/Tema/Tema.StaticSeries.Tests.cs similarity index 51% rename from tests/indicators/s-z/Tema/Tema.Tests.cs rename to tests/indicators/s-z/Tema/Tema.StaticSeries.Tests.cs index 7970b480e..23e39b123 100644 --- a/tests/indicators/s-z/Tema/Tema.Tests.cs +++ b/tests/indicators/s-z/Tema/Tema.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class TemaTests : TestBase +public class Tema : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetTema(20) - .ToList(); + IReadOnlyList results = Quotes + .ToTema(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -29,35 +28,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetTema(20) - .ToList(); + .ToTema(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Tema != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetTema(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Tema is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetTema(20) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToTema(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Tema != null)); @@ -66,38 +52,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetTema(20) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToTema(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetTema(15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToTema(15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Tema is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Tema is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetTema(5) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToTema(5); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetTema(5) - .ToList(); + IReadOnlyList r1 = Onequote + .ToTema(5); Assert.AreEqual(1, r1.Count); } @@ -105,15 +87,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetTema(20) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToTema(20) + .RemoveWarmupPeriods(); // assertions - Assert.AreEqual(502 - ((3 * 20) + 100), results.Count); + Assert.AreEqual(502 - (3 * 20 + 100), results.Count); - TemaResult last = results.LastOrDefault(); + TemaResult last = results[^1]; Assert.AreEqual(238.7690, last.Tema.Round(4)); } @@ -121,5 +102,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetTema(0)); + => Quotes.ToTema(0)); } diff --git a/tests/indicators/s-z/Tr/Tr.Tests.cs b/tests/indicators/s-z/Tr/Tr.StaticSeries.Tests.cs similarity index 61% rename from tests/indicators/s-z/Tr/Tr.Tests.cs rename to tests/indicators/s-z/Tr/Tr.StaticSeries.Tests.cs index c7fbebe54..e44f90c03 100644 --- a/tests/indicators/s-z/Tr/Tr.Tests.cs +++ b/tests/indicators/s-z/Tr/Tr.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class TrTests : TestBase +public class Tr : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetTr() - .ToList(); + IReadOnlyList results = Quotes + .ToTr(); // proper quantities Assert.AreEqual(502, results.Count); @@ -41,48 +40,43 @@ public void Standard() public void Chainor() { // same as ATR - List results = quotes - .GetTr() - .GetSmma(14) - .ToList(); + IReadOnlyList results = Quotes + .ToTr() + .ToSmma(14); - List atrResults = quotes - .GetAtr(14) - .ToList(); + IReadOnlyList atrResults = Quotes + .ToAtr(); for (int i = 0; i < results.Count; i++) { SmmaResult r = results[i]; AtrResult a = atrResults[i]; - Assert.AreEqual(a.Date, r.Date); + Assert.AreEqual(a.Timestamp, r.Timestamp); Assert.AreEqual(a.Atr, r.Smma); } } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetTr() - .ToList(); + IReadOnlyList r = BadQuotes + .ToTr(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Tr is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Tr is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetTr() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToTr(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetTr() - .ToList(); + IReadOnlyList r1 = Onequote + .ToTr(); Assert.AreEqual(1, r1.Count); } diff --git a/tests/indicators/s-z/Tr/Tr.StreamHub.Tests.cs b/tests/indicators/s-z/Tr/Tr.StreamHub.Tests.cs new file mode 100644 index 000000000..b67bcc866 --- /dev/null +++ b/tests/indicators/s-z/Tr/Tr.StreamHub.Tests.cs @@ -0,0 +1,120 @@ +namespace StreamHub; + +[TestClass] +public class TrHub : StreamHubTestBase, ITestChainProvider +{ + [TestMethod] + public override void QuoteObserver() + { + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // prefill quotes to provider + for (int i = 0; i < 20; i++) + { + provider.Add(quotesList[i]); + } + + // initialize observer + StreamHub observer = provider + .ToTr(); + + // fetch initial results (early) + IReadOnlyList streamList + = observer.Results; + + // emulate adding quotes to provider + for (int i = 20; i < length; i++) + { + // skip one (add later) + if (i == 80) + { + continue; + } + + Quote q = quotesList[i]; + provider.Add(q); + + // resend duplicate quotes + if (i is > 100 and < 105) + { + provider.Add(q); + } + } + + // late arrival + provider.Insert(quotesList[80]); + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // time-series, for comparison + IReadOnlyList seriesList = quotesList + .ToTr(); + + // assert, should equal series + streamList.Should().HaveCount(length - 1); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public void ChainProvider() + { + int smaPeriods = 8; + + List quotesList = Quotes.ToList(); + + int length = quotesList.Count; + + // setup quote provider + QuoteHub provider = new(); + + // initialize observer + IChainProvider adlHub = provider + .ToTr(); + + SmaHub observer = adlHub + .ToSma(smaPeriods); + + // emulate quote stream + for (int i = 0; i < length; i++) + { + provider.Add(quotesList[i]); + } + + // delete + provider.Remove(quotesList[400]); + quotesList.RemoveAt(400); + + // final results + IReadOnlyList streamList + = observer.Results; + + // time-series, for comparison + IReadOnlyList seriesList = quotesList + .ToTr() + .ToSma(smaPeriods); + + // assert, should equal series + streamList.Should().HaveCount(length - 1); + streamList.Should().BeEquivalentTo(seriesList); + + observer.Unsubscribe(); + provider.EndTransmission(); + } + + [TestMethod] + public override void CustomToString() + { + TrHub hub = new(new QuoteHub()); + hub.ToString().Should().Be("TRUE RANGE"); + } +} diff --git a/tests/indicators/s-z/Trix/Trix.Tests.cs b/tests/indicators/s-z/Trix/Trix.StaticSeries.Tests.cs similarity index 51% rename from tests/indicators/s-z/Trix/Trix.Tests.cs rename to tests/indicators/s-z/Trix/Trix.StaticSeries.Tests.cs index cd3d52d92..626d544b5 100644 --- a/tests/indicators/s-z/Trix/Trix.Tests.cs +++ b/tests/indicators/s-z/Trix/Trix.StaticSeries.Tests.cs @@ -1,73 +1,54 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class TrixTests : TestBase +public class Trix : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetTrix(20, 5) - .ToList(); + IReadOnlyList results = Quotes + .ToTrix(20); // proper quantities Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Ema3 != null)); Assert.AreEqual(482, results.Count(x => x.Trix != null)); - Assert.AreEqual(478, results.Count(x => x.Signal != null)); // sample values TrixResult r24 = results[24]; Assert.AreEqual(214.5486, r24.Ema3.Round(4)); Assert.AreEqual(0.005047, r24.Trix.Round(6)); - Assert.AreEqual(0.002196, r24.Signal.Round(6)); TrixResult r67 = results[67]; Assert.AreEqual(221.7837, r67.Ema3.Round(4)); Assert.AreEqual(0.050030, r67.Trix.Round(6)); - Assert.AreEqual(0.057064, r67.Signal.Round(6)); TrixResult r249 = results[249]; Assert.AreEqual(249.4469, r249.Ema3.Round(4)); Assert.AreEqual(0.121781, r249.Trix.Round(6)); - Assert.AreEqual(0.119769, r249.Signal.Round(6)); TrixResult r501 = results[501]; Assert.AreEqual(263.3216, r501.Ema3.Round(4)); Assert.AreEqual(-0.230742, r501.Trix.Round(6)); - Assert.AreEqual(-0.204536, r501.Signal.Round(6)); } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetTrix(20, 5) - .ToList(); + .ToTrix(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Trix != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetTrix(6, 2) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Trix is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetTrix(20, 5) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToTrix(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(481, results.Count(x => x.Trix != null)); @@ -76,38 +57,34 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetTrix(20, 5) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToTrix(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(473, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetTrix(15, 2) - .ToList(); + IReadOnlyList r = BadQuotes + .ToTrix(15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Trix is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Trix is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetTrix(5) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToTrix(5); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetTrix(5) - .ToList(); + IReadOnlyList r1 = Onequote + .ToTrix(5); Assert.AreEqual(1, r1.Count); } @@ -115,23 +92,21 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetTrix(20, 5) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToTrix(20) + .RemoveWarmupPeriods(); // assertions - Assert.AreEqual(502 - ((3 * 20) + 100), results.Count); + Assert.AreEqual(502 - (3 * 20 + 100), results.Count); - TrixResult last = results.LastOrDefault(); + TrixResult last = results[^1]; Assert.AreEqual(263.3216, last.Ema3.Round(4)); Assert.AreEqual(-0.230742, last.Trix.Round(6)); - Assert.AreEqual(-0.204536, last.Signal.Round(6)); } // bad lookback period [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetTrix(0)); + => Quotes.ToTrix(0)); } diff --git a/tests/indicators/s-z/Tsi/Tsi.Tests.cs b/tests/indicators/s-z/Tsi/Tsi.StaticSeries.Tests.cs similarity index 55% rename from tests/indicators/s-z/Tsi/Tsi.Tests.cs rename to tests/indicators/s-z/Tsi/Tsi.StaticSeries.Tests.cs index 84b8d7776..ec96ba655 100644 --- a/tests/indicators/s-z/Tsi/Tsi.Tests.cs +++ b/tests/indicators/s-z/Tsi/Tsi.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class TsiTests : TestBase +public class Tsi : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetTsi(25, 13, 7) - .ToList(); + IReadOnlyList results = Quotes + .ToTsi(); // proper quantities Assert.AreEqual(502, results.Count); @@ -20,13 +19,13 @@ public void Standard() Assert.AreEqual(53.1204, r2.Tsi.Round(4)); Assert.AreEqual(null, r2.Signal); - TsiResult r3a = results[43]; - Assert.AreEqual(46.0960, r3a.Tsi.Round(4)); - Assert.AreEqual(51.6916, r3a.Signal.Round(4)); + TsiResult r3A = results[43]; + Assert.AreEqual(46.0960, r3A.Tsi.Round(4)); + Assert.AreEqual(51.6916, r3A.Signal.Round(4)); - TsiResult r3b = results[44]; - Assert.AreEqual(42.5121, r3b.Tsi.Round(4)); - Assert.AreEqual(49.3967, r3b.Signal.Round(4)); + TsiResult r3B = results[44]; + Assert.AreEqual(42.5121, r3B.Tsi.Round(4)); + Assert.AreEqual(49.3967, r3B.Signal.Round(4)); TsiResult r4 = results[149]; Assert.AreEqual(29.0936, r4.Tsi.Round(4)); @@ -42,35 +41,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetTsi() - .ToList(); + .ToTsi(); Assert.AreEqual(502, results.Count); Assert.AreEqual(465, results.Count(x => x.Tsi != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetTsi() - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Tsi is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetTsi() - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToTsi(); Assert.AreEqual(502, results.Count); Assert.AreEqual(464, results.Count(x => x.Tsi != null)); @@ -79,48 +65,43 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetTsi() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToTsi() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(456, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetTsi() - .ToList(); + IReadOnlyList r = BadQuotes + .ToTsi(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Tsi is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Tsi is double.NaN)); } [TestMethod] public void BigData() { - List r = bigQuotes - .GetTsi() - .ToList(); + IReadOnlyList r = BigQuotes + .ToTsi(); Assert.AreEqual(1246, r.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetTsi() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToTsi(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetTsi() - .ToList(); + IReadOnlyList r1 = Onequote + .ToTsi(); Assert.AreEqual(1, r1.Count); } @@ -128,15 +109,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetTsi(25, 13, 7) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToTsi() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - (25 + 13 + 250), results.Count); - TsiResult last = results.LastOrDefault(); + TsiResult last = results[^1]; Assert.AreEqual(-28.3513, last.Tsi.Round(4)); Assert.AreEqual(-29.3597, last.Signal.Round(4)); } @@ -146,14 +126,14 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - quotes.GetTsi(0)); + Quotes.ToTsi(0)); // bad smoothing period Assert.ThrowsException(() => - quotes.GetTsi(25, 0)); + Quotes.ToTsi(25, 0)); // bad signal period Assert.ThrowsException(() => - quotes.GetTsi(25, 13, -1)); + Quotes.ToTsi(25, 13, -1)); } } diff --git a/tests/indicators/s-z/UlcerIndex/UlcerIndex.StaticSeries.Tests.cs b/tests/indicators/s-z/UlcerIndex/UlcerIndex.StaticSeries.Tests.cs new file mode 100644 index 000000000..8e08fa5f7 --- /dev/null +++ b/tests/indicators/s-z/UlcerIndex/UlcerIndex.StaticSeries.Tests.cs @@ -0,0 +1,97 @@ +namespace StaticSeries; + +[TestClass] +public class UlcerIndex : StaticSeriesTestBase +{ + [TestMethod] + public override void Standard() + { + IReadOnlyList results = Quotes + .ToUlcerIndex(); + + // proper quantities + Assert.AreEqual(502, results.Count); + Assert.AreEqual(489, results.Count(x => x.UlcerIndex != null)); + + // sample value + UlcerIndexResult r = results[501]; + Assert.AreEqual(5.7255, r.UlcerIndex.Round(4)); + } + + [TestMethod] + public void UseReusable() + { + IReadOnlyList results = Quotes + .Use(CandlePart.Close) + .ToUlcerIndex(); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(489, results.Count(x => x.UlcerIndex != null)); + } + + [TestMethod] + public void Chainee() + { + IReadOnlyList results = Quotes + .ToSma(2) + .ToUlcerIndex(); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(488, results.Count(x => x.UlcerIndex != null)); + } + + [TestMethod] + public void Chainor() + { + IReadOnlyList results = Quotes + .ToUlcerIndex() + .ToSma(10); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(480, results.Count(x => x.Sma != null)); + } + + [TestMethod] + public override void BadData() + { + IReadOnlyList r = BadQuotes + .ToUlcerIndex(15); + + Assert.AreEqual(502, r.Count); + Assert.AreEqual(0, r.Count(x => x.UlcerIndex is double.NaN)); + } + + [TestMethod] + public override void NoQuotes() + { + IReadOnlyList r0 = Noquotes + .ToUlcerIndex(); + + Assert.AreEqual(0, r0.Count); + + IReadOnlyList r1 = Onequote + .ToUlcerIndex(); + + Assert.AreEqual(1, r1.Count); + } + + [TestMethod] + public void Removed() + { + IReadOnlyList results = Quotes + .ToUlcerIndex() + .RemoveWarmupPeriods(); + + // assertions + Assert.AreEqual(502 - 13, results.Count); + + UlcerIndexResult last = results[^1]; + Assert.AreEqual(5.7255, last.UlcerIndex.Round(4)); + } + + // bad lookback period + [TestMethod] + public void Exceptions() + => Assert.ThrowsException(() + => Quotes.ToUlcerIndex(0)); +} diff --git a/tests/indicators/s-z/UlcerIndex/UlcerIndex.Tests.cs b/tests/indicators/s-z/UlcerIndex/UlcerIndex.Tests.cs deleted file mode 100644 index f94d4e3f3..000000000 --- a/tests/indicators/s-z/UlcerIndex/UlcerIndex.Tests.cs +++ /dev/null @@ -1,116 +0,0 @@ -namespace Tests.Indicators; - -[TestClass] -public class UlcerIndexTests : TestBase -{ - [TestMethod] - public void Standard() - { - List results = quotes - .GetUlcerIndex(14) - .ToList(); - - // proper quantities - Assert.AreEqual(502, results.Count); - Assert.AreEqual(489, results.Count(x => x.UI != null)); - - // sample value - UlcerIndexResult r = results[501]; - Assert.AreEqual(5.7255, r.UI.Round(4)); - } - - [TestMethod] - public void UseTuple() - { - List results = quotes - .Use(CandlePart.Close) - .GetUlcerIndex(14) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(489, results.Count(x => x.UI != null)); - } - - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetUlcerIndex(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.UI is double and double.NaN)); - } - - [TestMethod] - public void Chainee() - { - List results = quotes - .GetSma(2) - .GetUlcerIndex(14) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(488, results.Count(x => x.UI != null)); - } - - [TestMethod] - public void Chainor() - { - List results = quotes - .GetUlcerIndex(14) - .GetSma(10) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(480, results.Count(x => x.Sma != null)); - } - - [TestMethod] - public void BadData() - { - List r = badQuotes - .GetUlcerIndex(15) - .ToList(); - - Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.UI is double and double.NaN)); - } - - [TestMethod] - public void NoQuotes() - { - List r0 = noquotes - .GetUlcerIndex() - .ToList(); - - Assert.AreEqual(0, r0.Count); - - List r1 = onequote - .GetUlcerIndex() - .ToList(); - - Assert.AreEqual(1, r1.Count); - } - - [TestMethod] - public void Removed() - { - List results = quotes - .GetUlcerIndex(14) - .RemoveWarmupPeriods() - .ToList(); - - // assertions - Assert.AreEqual(502 - 13, results.Count); - - UlcerIndexResult last = results.LastOrDefault(); - Assert.AreEqual(5.7255, last.UI.Round(4)); - } - - // bad lookback period - [TestMethod] - public void Exceptions() - => Assert.ThrowsException(() - => quotes.GetUlcerIndex(0)); -} diff --git a/tests/indicators/s-z/Ultimate/Ultimate.Tests.cs b/tests/indicators/s-z/Ultimate/Ultimate.StaticSeries.Tests.cs similarity index 57% rename from tests/indicators/s-z/Ultimate/Ultimate.Tests.cs rename to tests/indicators/s-z/Ultimate/Ultimate.StaticSeries.Tests.cs index 0ee1e7361..8571195da 100644 --- a/tests/indicators/s-z/Ultimate/Ultimate.Tests.cs +++ b/tests/indicators/s-z/Ultimate/Ultimate.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class UltimateTests : TestBase +public class Ultimate : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetUltimate(7, 14, 28) - .ToList(); + IReadOnlyList results = Quotes + .ToUltimate(); // proper quantities Assert.AreEqual(502, results.Count); @@ -28,38 +27,34 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetUltimate() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToUltimate() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(465, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetUltimate(1, 2, 3) - .ToList(); + IReadOnlyList r = BadQuotes + .ToUltimate(1, 2, 3); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Ultimate is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Ultimate is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetUltimate() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToUltimate(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetUltimate() - .ToList(); + IReadOnlyList r1 = Onequote + .ToUltimate(); Assert.AreEqual(1, r1.Count); } @@ -67,15 +62,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetUltimate(7, 14, 28) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToUltimate() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 28, results.Count); - UltimateResult last = results.LastOrDefault(); + UltimateResult last = results[^1]; Assert.AreEqual(49.5257, last.Ultimate.Round(4)); } @@ -84,14 +78,14 @@ public void Exceptions() { // bad short period Assert.ThrowsException(() => - quotes.GetUltimate(0)); + Quotes.ToUltimate(0)); // bad middle period Assert.ThrowsException(() => - quotes.GetUltimate(7, 6)); + Quotes.ToUltimate(7, 6)); // bad long period Assert.ThrowsException(() => - quotes.GetUltimate(7, 14, 11)); + Quotes.ToUltimate(7, 14, 11)); } } diff --git a/tests/indicators/s-z/VolatilityStop/VolatilityStop.Tests.cs b/tests/indicators/s-z/VolatilityStop/VolatilityStop.StaticSeries.Tests.cs similarity index 70% rename from tests/indicators/s-z/VolatilityStop/VolatilityStop.Tests.cs rename to tests/indicators/s-z/VolatilityStop/VolatilityStop.StaticSeries.Tests.cs index 6dbb3e82c..66687b868 100644 --- a/tests/indicators/s-z/VolatilityStop/VolatilityStop.Tests.cs +++ b/tests/indicators/s-z/VolatilityStop/VolatilityStop.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class VolatilityStopTests : TestBase +public class VolatilityStop : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = - quotes.GetVolatilityStop(14, 3) - .ToList(); + IReadOnlyList results = + Quotes.ToVolatilityStop(14); // proper quantities Assert.AreEqual(502, results.Count); @@ -54,7 +53,7 @@ public void Standard() Assert.AreEqual(249.7460, r284.LowerBand.Round(4)); Assert.IsNull(r284.UpperBand); - VolatilityStopResult last = results.LastOrDefault(); + VolatilityStopResult last = results[^1]; Assert.AreEqual(249.2423, last.Sar.Round(4)); Assert.AreEqual(false, last.IsStop); Assert.AreEqual(249.2423, last.UpperBand.Round(4)); @@ -64,38 +63,34 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetVolatilityStop() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToVolatilityStop() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(439, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetVolatilityStop() - .ToList(); + IReadOnlyList r = BadQuotes + .ToVolatilityStop(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Sar is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Sar is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetVolatilityStop() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToVolatilityStop(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetVolatilityStop() - .ToList(); + IReadOnlyList r1 = Onequote + .ToVolatilityStop(); Assert.AreEqual(1, r1.Count); } @@ -103,15 +98,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetVolatilityStop(14, 3) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToVolatilityStop(14) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(402, results.Count); - VolatilityStopResult last = results.LastOrDefault(); + VolatilityStopResult last = results[^1]; Assert.AreEqual(249.2423, last.Sar.Round(4)); Assert.AreEqual(false, last.IsStop); } @@ -121,10 +115,10 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - quotes.GetVolatilityStop(1)); + Quotes.ToVolatilityStop(1)); // bad multiplier Assert.ThrowsException(() => - quotes.GetVolatilityStop(20, 0)); + Quotes.ToVolatilityStop(20, 0)); } } diff --git a/tests/indicators/s-z/Vortex/Vortex.Tests.cs b/tests/indicators/s-z/Vortex/Vortex.StaticSeries.Tests.cs similarity index 63% rename from tests/indicators/s-z/Vortex/Vortex.Tests.cs rename to tests/indicators/s-z/Vortex/Vortex.StaticSeries.Tests.cs index 7d183471b..389863567 100644 --- a/tests/indicators/s-z/Vortex/Vortex.Tests.cs +++ b/tests/indicators/s-z/Vortex/Vortex.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class VortexTests : TestBase +public class Vortex : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetVortex(14) - .ToList(); + IReadOnlyList results = Quotes + .ToVortex(14); // proper quantities Assert.AreEqual(502, results.Count); @@ -37,28 +36,25 @@ public void Standard() } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetVortex(20) - .ToList(); + IReadOnlyList r = BadQuotes + .ToVortex(20); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Pvi is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Pvi is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetVortex(5) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToVortex(5); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetVortex(5) - .ToList(); + IReadOnlyList r1 = Onequote + .ToVortex(5); Assert.AreEqual(1, r1.Count); } @@ -66,15 +62,14 @@ public void NoQuotes() [TestMethod] public void Condense() { - List results = quotes - .GetVortex(14) - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToVortex(14) + .Condense(); // assertions Assert.AreEqual(502 - 14, results.Count); - VortexResult last = results.LastOrDefault(); + VortexResult last = results[^1]; Assert.AreEqual(0.8712, last.Pvi.Round(4)); Assert.AreEqual(1.1163, last.Nvi.Round(4)); } @@ -82,15 +77,14 @@ public void Condense() [TestMethod] public void Removed() { - List results = quotes - .GetVortex(14) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToVortex(14) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 14, results.Count); - VortexResult last = results.LastOrDefault(); + VortexResult last = results[^1]; Assert.AreEqual(0.8712, last.Pvi.Round(4)); Assert.AreEqual(1.1163, last.Nvi.Round(4)); } @@ -99,5 +93,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetVortex(1)); + => Quotes.ToVortex(1)); } diff --git a/tests/indicators/s-z/Vwap/Vwap.Tests.cs b/tests/indicators/s-z/Vwap/Vwap.StaticSeries.Tests.cs similarity index 61% rename from tests/indicators/s-z/Vwap/Vwap.Tests.cs rename to tests/indicators/s-z/Vwap/Vwap.StaticSeries.Tests.cs index 11143e3a7..cb07abc66 100644 --- a/tests/indicators/s-z/Vwap/Vwap.Tests.cs +++ b/tests/indicators/s-z/Vwap/Vwap.StaticSeries.Tests.cs @@ -1,17 +1,17 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class VwapTests : TestBase +public class Vwap : StaticSeriesTestBase { - private readonly IEnumerable intraday = TestData.GetIntraday() - .OrderBy(x => x.Date) - .Take(391); + private static readonly IReadOnlyList intraday = Data.GetIntraday() + .OrderBy(x => x.Timestamp) + .Take(391) + .ToList(); [TestMethod] - public void Standard() + public override void Standard() { - List results = intraday.GetVwap() - .ToList(); + IReadOnlyList results = intraday.ToVwap(); // proper quantities Assert.AreEqual(391, results.Count); @@ -35,11 +35,10 @@ public void Standard() public void WithStartDate() { DateTime startDate = - DateTime.ParseExact("2020-12-15 10:00", "yyyy-MM-dd h:mm", EnglishCulture); + DateTime.ParseExact("2020-12-15 10:00", "yyyy-MM-dd h:mm", invariantCulture); - List results = intraday - .GetVwap(startDate) - .ToList(); + IReadOnlyList results = intraday + .ToVwap(startDate); // proper quantities Assert.AreEqual(391, results.Count); @@ -62,38 +61,34 @@ public void WithStartDate() [TestMethod] public void Chainor() { - List results = quotes - .GetVwap() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToVwap() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(493, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetVwap() - .ToList(); + IReadOnlyList r = BadQuotes + .ToVwap(); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Vwap is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Vwap is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetVwap() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToVwap(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetVwap() - .ToList(); + IReadOnlyList r1 = Onequote + .ToVwap(); Assert.AreEqual(1, r1.Count); } @@ -102,30 +97,28 @@ public void NoQuotes() public void Removed() { // no start date - List results = intraday - .GetVwap() - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = intraday + .ToVwap() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(391, results.Count); - VwapResult last = results.LastOrDefault(); + VwapResult last = results[^1]; Assert.AreEqual(368.1804, last.Vwap.Round(4)); // with start date DateTime startDate = - DateTime.ParseExact("2020-12-15 10:00", "yyyy-MM-dd h:mm", EnglishCulture); + DateTime.ParseExact("2020-12-15 10:00", "yyyy-MM-dd h:mm", invariantCulture); - List sdResults = intraday - .GetVwap(startDate) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList sdResults = intraday + .ToVwap(startDate) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(361, sdResults.Count); - VwapResult sdLast = sdResults.LastOrDefault(); + VwapResult sdLast = sdResults[^1]; Assert.AreEqual(368.2908, sdLast.Vwap.Round(4)); } @@ -134,9 +127,9 @@ public void Exceptions() { // bad SMA period DateTime startDate = - DateTime.ParseExact("2000-12-15", "yyyy-MM-dd", EnglishCulture); + DateTime.ParseExact("2000-12-15", "yyyy-MM-dd", invariantCulture); Assert.ThrowsException(() => - quotes.GetVwap(startDate)); + Quotes.ToVwap(startDate)); } } diff --git a/tests/indicators/s-z/Vwma/Vwma.Tests.cs b/tests/indicators/s-z/Vwma/Vwma.StaticSeries.Tests.cs similarity index 58% rename from tests/indicators/s-z/Vwma/Vwma.Tests.cs rename to tests/indicators/s-z/Vwma/Vwma.StaticSeries.Tests.cs index f722d9d29..8cfc11cc0 100644 --- a/tests/indicators/s-z/Vwma/Vwma.Tests.cs +++ b/tests/indicators/s-z/Vwma/Vwma.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class VwmaTests : TestBase +public class Vwma : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetVwma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToVwma(10); // proper quantities Assert.AreEqual(502, results.Count); @@ -28,38 +27,34 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetVwma(10) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToVwma(10) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(484, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetVwma(15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToVwma(15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Vwma is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Vwma is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetVwma(4) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToVwma(4); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetVwma(4) - .ToList(); + IReadOnlyList r1 = Onequote + .ToVwma(4); Assert.AreEqual(1, r1.Count); } @@ -67,15 +62,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetVwma(10) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToVwma(10) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 9, results.Count); - VwmaResult last = results.LastOrDefault(); + VwmaResult last = results[^1]; Assert.AreEqual(242.101548, last.Vwma.Round(6)); } @@ -83,5 +77,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetVwma(0)); + => Quotes.ToVwma(0)); } diff --git a/tests/indicators/s-z/WilliamsR/WilliamsR.Tests.cs b/tests/indicators/s-z/WilliamsR/WilliamsR.StaticSeries.Tests.cs similarity index 64% rename from tests/indicators/s-z/WilliamsR/WilliamsR.Tests.cs rename to tests/indicators/s-z/WilliamsR/WilliamsR.StaticSeries.Tests.cs index 99f9455a1..c0af8b3e2 100644 --- a/tests/indicators/s-z/WilliamsR/WilliamsR.Tests.cs +++ b/tests/indicators/s-z/WilliamsR/WilliamsR.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class WilliamsRTests : TestBase +public class WilliamsR : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetWilliamsR(14) - .ToList(); + IReadOnlyList results = Quotes + .ToWilliamsR(); // proper quantities Assert.AreEqual(502, results.Count); @@ -37,41 +36,34 @@ public void Standard() [TestMethod] public void Chainor() { - List results = quotes - .GetWilliamsR() - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToWilliamsR() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(480, results.Count(x => x.Sma != null)); } [TestMethod] - public void BadData() + public override void BadData() { - List quotes = badQuotes - .ToSortedList(); - - List results = badQuotes - .GetWilliamsR(20) - .ToList(); + IReadOnlyList results = BadQuotes + .ToWilliamsR(20); Assert.AreEqual(502, results.Count); - Assert.AreEqual(0, results.Count(x => x.WilliamsR is double and double.NaN)); + Assert.AreEqual(0, results.Count(x => x.WilliamsR is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetWilliamsR() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToWilliamsR(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetWilliamsR() - .ToList(); + IReadOnlyList r1 = Onequote + .ToWilliamsR(); Assert.AreEqual(1, r1.Count); } @@ -79,25 +71,23 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetWilliamsR(14) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToWilliamsR() + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 13, results.Count); - WilliamsResult last = results.LastOrDefault(); + WilliamsResult last = results[^1]; Assert.AreEqual(-52.0121, last.WilliamsR.Round(4)); } [TestMethod] public void Boundary() { - List results = TestData + IReadOnlyList results = Data .GetRandom(2500) - .GetWilliamsR(14) - .ToList(); + .ToWilliamsR(); // analyze boundary for (int i = 0; i < results.Count; i++) @@ -118,16 +108,15 @@ public void Issue1127() // initialize IOrderedEnumerable test1127 = File.ReadAllLines("s-z/WilliamsR/issue1127quotes.csv") .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date); + .Select(Imports.QuoteFromCsv) + .OrderByDescending(x => x.Timestamp); - List quotesList = test1127.ToList(); + IReadOnlyList quotesList = test1127.ToList(); int length = quotesList.Count; // get indicators - List resultsList = test1127 - .GetWilliamsR(14) - .ToList(); + IReadOnlyList resultsList = quotesList + .ToWilliamsR(); Console.WriteLine($"%R from {length} quotes."); @@ -137,7 +126,7 @@ public void Issue1127() Quote q = quotesList[i]; WilliamsResult r = resultsList[i]; - Console.WriteLine($"{q.Date:s} {r.WilliamsR}"); + Console.WriteLine($"{q.Timestamp:s} {r.WilliamsR}"); if (r.WilliamsR is not null) { @@ -151,5 +140,5 @@ public void Issue1127() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetWilliamsR(0)); + => Quotes.ToWilliamsR(0)); } diff --git a/tests/indicators/s-z/WilliamsR/issue1127quotes.csv b/tests/indicators/s-z/WilliamsR/issue1127quotes.csv index c3c727ad9..a091ab242 100644 --- a/tests/indicators/s-z/WilliamsR/issue1127quotes.csv +++ b/tests/indicators/s-z/WilliamsR/issue1127quotes.csv @@ -1,4 +1,4 @@ -Date,Open,High,Low,Close,Volume +Timestamp,Open,High,Low,Close,Volume 2021-01-29,121.21,121.685,119.28,120.17,2103083 2021-02-01,120.69,122.09,120.18,120.83,1451881 2021-02-02,122.5,125.85,122.415,123.19,1675502 diff --git a/tests/indicators/s-z/Wma/Wma.Tests.cs b/tests/indicators/s-z/Wma/Wma.StaticSeries.Tests.cs similarity index 51% rename from tests/indicators/s-z/Wma/Wma.Tests.cs rename to tests/indicators/s-z/Wma/Wma.StaticSeries.Tests.cs index e114970de..842b28399 100644 --- a/tests/indicators/s-z/Wma/Wma.Tests.cs +++ b/tests/indicators/s-z/Wma/Wma.StaticSeries.Tests.cs @@ -1,14 +1,13 @@ -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class WmaTests : TestBase +public class Wma : StaticSeriesTestBase { [TestMethod] - public void Standard() + public override void Standard() { - List results = quotes - .GetWma(20) - .ToList(); + IReadOnlyList results = Quotes + .ToWma(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -23,35 +22,22 @@ public void Standard() } [TestMethod] - public void UseTuple() + public void UseReusable() { - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetWma(20) - .ToList(); + .ToWma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Wma != null)); } - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetWma(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Wma is double and double.NaN)); - } - [TestMethod] public void Chainee() { - List results = quotes - .GetSma(2) - .GetWma(20) - .ToList(); + IReadOnlyList results = Quotes + .ToSma(2) + .ToWma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Wma != null)); @@ -60,10 +46,9 @@ public void Chainee() [TestMethod] public void Chainor() { - List results = quotes - .GetWma(20) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToWma(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); @@ -72,14 +57,12 @@ public void Chainor() [TestMethod] public void Chaining() { - List standard = quotes - .GetWma(17) - .ToList(); + IReadOnlyList standard = Quotes + .ToWma(17); - List results = quotes + IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetWma(17) - .ToList(); + .ToWma(17); // assertions for (int i = 0; i < results.Count; i++) @@ -87,34 +70,31 @@ public void Chaining() WmaResult s = standard[i]; WmaResult c = results[i]; - Assert.AreEqual(s.Date, c.Date); + Assert.AreEqual(s.Timestamp, c.Timestamp); Assert.AreEqual(s.Wma, c.Wma); } } [TestMethod] - public void BadData() + public override void BadData() { - List r = badQuotes - .GetWma(15) - .ToList(); + IReadOnlyList r = BadQuotes + .ToWma(15); Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Wma is double and double.NaN)); + Assert.AreEqual(0, r.Count(x => x.Wma is double.NaN)); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetWma(5) - .ToList(); + IReadOnlyList r0 = Noquotes + .ToWma(5); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetWma(5) - .ToList(); + IReadOnlyList r1 = Onequote + .ToWma(5); Assert.AreEqual(1, r1.Count); } @@ -122,15 +102,14 @@ public void NoQuotes() [TestMethod] public void Removed() { - List results = quotes - .GetWma(20) - .RemoveWarmupPeriods() - .ToList(); + IReadOnlyList results = Quotes + .ToWma(20) + .RemoveWarmupPeriods(); // assertions Assert.AreEqual(502 - 19, results.Count); - WmaResult last = results.LastOrDefault(); + WmaResult last = results[^1]; Assert.AreEqual(246.5110, last.Wma.Round(4)); } @@ -138,5 +117,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => quotes.GetWma(0)); + => Quotes.ToWma(0)); } diff --git a/tests/indicators/s-z/ZigZag/ZigZag.Tests.cs b/tests/indicators/s-z/ZigZag/ZigZag.StaticSeries.Tests.cs similarity index 77% rename from tests/indicators/s-z/ZigZag/ZigZag.Tests.cs rename to tests/indicators/s-z/ZigZag/ZigZag.StaticSeries.Tests.cs index 869bae9bb..30c5e0300 100644 --- a/tests/indicators/s-z/ZigZag/ZigZag.Tests.cs +++ b/tests/indicators/s-z/ZigZag/ZigZag.StaticSeries.Tests.cs @@ -1,16 +1,15 @@ using Newtonsoft.Json; -namespace Tests.Indicators; +namespace StaticSeries; [TestClass] -public class ZigZagTests : TestBase +public class ZigZag : StaticSeriesTestBase { [TestMethod] - public void StandardClose() + public override void Standard() // on Close { - List results = - quotes.GetZigZag(EndType.Close, 3) - .ToList(); + IReadOnlyList results = + Quotes.ToZigZag(EndType.Close, 3); // proper quantities Assert.AreEqual(502, results.Count); @@ -60,9 +59,8 @@ public void StandardClose() [TestMethod] public void StandardHighLow() { - List results = - quotes.GetZigZag(EndType.HighLow, 3) - .ToList(); + IReadOnlyList results = + Quotes.ToZigZag(EndType.HighLow, 3); // proper quantities Assert.AreEqual(502, results.Count); @@ -112,10 +110,9 @@ public void StandardHighLow() [TestMethod] public void Chainor() { - List results = quotes - .GetZigZag(EndType.Close, 3) - .GetSma(10) - .ToList(); + IReadOnlyList results = Quotes + .ToZigZag(EndType.Close, 3) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(225, results.Count(x => x.Sma != null)); @@ -127,13 +124,13 @@ public void NoEntry() // thresholds are never met string json = File.ReadAllText("./s-z/ZigZag/data.ethusdt.json"); - IReadOnlyCollection quotes = JsonConvert - .DeserializeObject>(json); - - List results = quotes - .GetZigZag(EndType.Close, 5m) + IReadOnlyList quotes = JsonConvert + .DeserializeObject>(json) .ToList(); + IReadOnlyList results = quotes + .ToZigZag(); + Assert.AreEqual(0, results.Count(x => x.PointType != null)); } @@ -143,45 +140,40 @@ public void Issue632() // thresholds are never met string json = File.ReadAllText("./s-z/ZigZag/data.issue632.json"); - List quotesList = JsonConvert + IReadOnlyList quotesList = JsonConvert .DeserializeObject>(json) .ToList(); - List resultsList = quotesList - .GetZigZag(EndType.Close, 5m) - .ToList(); + IReadOnlyList resultsList = quotesList + .ToZigZag(); Assert.AreEqual(17, resultsList.Count); } [TestMethod] - public void BadData() + public override void BadData() { - List r1 = badQuotes - .GetZigZag(EndType.Close) - .ToList(); + IReadOnlyList r1 = BadQuotes + .ToZigZag(); Assert.AreEqual(502, r1.Count); - List r2 = badQuotes - .GetZigZag(EndType.HighLow) - .ToList(); + IReadOnlyList r2 = BadQuotes + .ToZigZag(EndType.HighLow); Assert.AreEqual(502, r2.Count); } [TestMethod] - public void NoQuotes() + public override void NoQuotes() { - List r0 = noquotes - .GetZigZag() - .ToList(); + IReadOnlyList r0 = Noquotes + .ToZigZag(); Assert.AreEqual(0, r0.Count); - List r1 = onequote - .GetZigZag() - .ToList(); + IReadOnlyList r1 = Onequote + .ToZigZag(); Assert.AreEqual(1, r1.Count); } @@ -189,10 +181,9 @@ public void NoQuotes() [TestMethod] public void Condense() { - List results = quotes - .GetZigZag(EndType.Close, 3) - .Condense() - .ToList(); + IReadOnlyList results = Quotes + .ToZigZag(EndType.Close, 3) + .Condense(); // assertions Assert.AreEqual(14, results.Count); @@ -203,17 +194,17 @@ public void SchrodingerScenario() { string json = File.ReadAllText("./s-z/ZigZag/data.schrodinger.json"); - List h = JsonConvert + IReadOnlyList h = JsonConvert .DeserializeObject>(json) - .OrderBy(x => x.Date) + .OrderBy(x => x.Timestamp) .ToList(); - List r1 = h.GetZigZag(EndType.Close, 0.25m).ToList(); + IReadOnlyList r1 = h.ToZigZag(EndType.Close, 0.25m).ToList(); Assert.AreEqual(342, r1.Count); // first period has High/Low that exceeds threhold // where it is both a H and L pivot simultaenously - List r2 = h.GetZigZag(EndType.HighLow, 3).ToList(); + IReadOnlyList r2 = h.ToZigZag(EndType.HighLow, 3).ToList(); Assert.AreEqual(342, r2.Count); } @@ -222,10 +213,10 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() - => quotes.GetZigZag(EndType.Close, 0)); + => Quotes.ToZigZag(EndType.Close, 0)); // bad end type Assert.ThrowsException(() - => quotes.GetZigZag((EndType)int.MaxValue, 2)); + => Quotes.ToZigZag((EndType)int.MaxValue, 2)); } } diff --git a/tests/indicators/s-z/ZigZag/data.ethusdt.json b/tests/indicators/s-z/ZigZag/data.ethusdt.json index 5a628f1dc..8ea90a20e 100644 --- a/tests/indicators/s-z/ZigZag/data.ethusdt.json +++ b/tests/indicators/s-z/ZigZag/data.ethusdt.json @@ -2,7 +2,7 @@ { "TimeOfCandle": "2021-10-27T09:55:00+03:30", "Amount": 163057.4955050465, - "Date": "2021-10-27T09:55:00+03:30", + "Timestamp": "2021-10-27T09:55:00+03:30", "Open": 4247.84, "High": 4256.64, "Low": 4246.00, @@ -12,7 +12,7 @@ { "TimeOfCandle": "2021-10-27T10:00:00+03:30", "Amount": 34654.0711221263, - "Date": "2021-10-27T10:00:00+03:30", + "Timestamp": "2021-10-27T10:00:00+03:30", "Open": 4256.49, "High": 4263.43, "Low": 4256.41, @@ -22,7 +22,7 @@ { "TimeOfCandle": "2021-10-27T10:05:00+03:30", "Amount": 119438.8924996978, - "Date": "2021-10-27T10:05:00+03:30", + "Timestamp": "2021-10-27T10:05:00+03:30", "Open": 4257.78, "High": 4257.78, "Low": 4250.64, @@ -32,7 +32,7 @@ { "TimeOfCandle": "2021-10-27T10:10:00+03:30", "Amount": 79710.1383965050, - "Date": "2021-10-27T10:10:00+03:30", + "Timestamp": "2021-10-27T10:10:00+03:30", "Open": 4251.66, "High": 4255.66, "Low": 4238.02, @@ -42,7 +42,7 @@ { "TimeOfCandle": "2021-10-27T10:15:00+03:30", "Amount": 35916.8475715381, - "Date": "2021-10-27T10:15:00+03:30", + "Timestamp": "2021-10-27T10:15:00+03:30", "Open": 4238.03, "High": 4244.00, "Low": 4236.20, @@ -52,7 +52,7 @@ { "TimeOfCandle": "2021-10-27T10:20:00+03:30", "Amount": 31735.4679234844, - "Date": "2021-10-27T10:20:00+03:30", + "Timestamp": "2021-10-27T10:20:00+03:30", "Open": 4238.01, "High": 4247.43, "Low": 4236.70, @@ -62,7 +62,7 @@ { "TimeOfCandle": "2021-10-27T10:25:00+03:30", "Amount": 34824.0746251221, - "Date": "2021-10-27T10:25:00+03:30", + "Timestamp": "2021-10-27T10:25:00+03:30", "Open": 4241.03, "High": 4244.64, "Low": 4239.55, @@ -72,7 +72,7 @@ { "TimeOfCandle": "2021-10-27T10:30:00+03:30", "Amount": 43817.2577893878, - "Date": "2021-10-27T10:30:00+03:30", + "Timestamp": "2021-10-27T10:30:00+03:30", "Open": 4239.91, "High": 4239.91, "Low": 4226.97, @@ -82,7 +82,7 @@ { "TimeOfCandle": "2021-10-27T10:35:00+03:30", "Amount": 37631.6618704417, - "Date": "2021-10-27T10:35:00+03:30", + "Timestamp": "2021-10-27T10:35:00+03:30", "Open": 4225.00, "High": 4246.19, "Low": 4224.88, @@ -92,7 +92,7 @@ { "TimeOfCandle": "2021-10-27T10:40:00+03:30", "Amount": 29871.7942738019, - "Date": "2021-10-27T10:40:00+03:30", + "Timestamp": "2021-10-27T10:40:00+03:30", "Open": 4233.36, "High": 4240.73, "Low": 4228.21, @@ -102,7 +102,7 @@ { "TimeOfCandle": "2021-10-27T10:45:00+03:30", "Amount": 13541.9867665602, - "Date": "2021-10-27T10:45:00+03:30", + "Timestamp": "2021-10-27T10:45:00+03:30", "Open": 4227.89, "High": 4229.75, "Low": 4221.03, @@ -112,7 +112,7 @@ { "TimeOfCandle": "2021-10-27T10:50:00+03:30", "Amount": 68016.9641253717, - "Date": "2021-10-27T10:50:00+03:30", + "Timestamp": "2021-10-27T10:50:00+03:30", "Open": 4224.68, "High": 4234.19, "Low": 4215.01, @@ -122,7 +122,7 @@ { "TimeOfCandle": "2021-10-27T10:55:00+03:30", "Amount": 104286.9385587473, - "Date": "2021-10-27T10:55:00+03:30", + "Timestamp": "2021-10-27T10:55:00+03:30", "Open": 4221.97, "High": 4221.97, "Low": 4200.71, @@ -132,7 +132,7 @@ { "TimeOfCandle": "2021-10-27T11:00:00+03:30", "Amount": 123321.2347351709, - "Date": "2021-10-27T11:00:00+03:30", + "Timestamp": "2021-10-27T11:00:00+03:30", "Open": 4210.00, "High": 4212.00, "Low": 4193.45, @@ -142,7 +142,7 @@ { "TimeOfCandle": "2021-10-27T11:05:00+03:30", "Amount": 71808.7749004083, - "Date": "2021-10-27T11:05:00+03:30", + "Timestamp": "2021-10-27T11:05:00+03:30", "Open": 4196.06, "High": 4196.06, "Low": 4166.13, @@ -152,7 +152,7 @@ { "TimeOfCandle": "2021-10-27T11:10:00+03:30", "Amount": 316937.3768832286, - "Date": "2021-10-27T11:10:00+03:30", + "Timestamp": "2021-10-27T11:10:00+03:30", "Open": 4171.97, "High": 4179.03, "Low": 4160.00, @@ -162,7 +162,7 @@ { "TimeOfCandle": "2021-10-27T11:15:00+03:30", "Amount": 150355.5828374267, - "Date": "2021-10-27T11:15:00+03:30", + "Timestamp": "2021-10-27T11:15:00+03:30", "Open": 4162.32, "High": 4173.10, "Low": 4146.00, @@ -172,7 +172,7 @@ { "TimeOfCandle": "2021-10-27T11:20:00+03:30", "Amount": 120265.5829372087, - "Date": "2021-10-27T11:20:00+03:30", + "Timestamp": "2021-10-27T11:20:00+03:30", "Open": 4150.00, "High": 4161.61, "Low": 4140.00, @@ -182,7 +182,7 @@ { "TimeOfCandle": "2021-10-27T11:25:00+03:30", "Amount": 69818.4970617914, - "Date": "2021-10-27T11:25:00+03:30", + "Timestamp": "2021-10-27T11:25:00+03:30", "Open": 4143.30, "High": 4152.12, "Low": 4135.00, @@ -192,7 +192,7 @@ { "TimeOfCandle": "2021-10-27T11:30:00+03:30", "Amount": 535494.0913993873, - "Date": "2021-10-27T11:30:00+03:30", + "Timestamp": "2021-10-27T11:30:00+03:30", "Open": 4135.61, "High": 4136.85, "Low": 4034.81, @@ -202,7 +202,7 @@ { "TimeOfCandle": "2021-10-27T11:35:00+03:30", "Amount": 584292.7598844911, - "Date": "2021-10-27T11:35:00+03:30", + "Timestamp": "2021-10-27T11:35:00+03:30", "Open": 4036.21, "High": 4059.58, "Low": 3943.00, @@ -212,7 +212,7 @@ { "TimeOfCandle": "2021-10-27T11:40:00+03:30", "Amount": 1104359.0885250876, - "Date": "2021-10-27T11:40:00+03:30", + "Timestamp": "2021-10-27T11:40:00+03:30", "Open": 3942.00, "High": 4040.00, "Low": 3930.00, @@ -222,7 +222,7 @@ { "TimeOfCandle": "2021-10-27T11:45:00+03:30", "Amount": 498093.6682223480, - "Date": "2021-10-27T11:45:00+03:30", + "Timestamp": "2021-10-27T11:45:00+03:30", "Open": 4035.44, "High": 4047.02, "Low": 4013.36, @@ -232,7 +232,7 @@ { "TimeOfCandle": "2021-10-27T11:50:00+03:30", "Amount": 393538.5112353333, - "Date": "2021-10-27T11:50:00+03:30", + "Timestamp": "2021-10-27T11:50:00+03:30", "Open": 4009.20, "High": 4028.57, "Low": 3985.01, @@ -242,7 +242,7 @@ { "TimeOfCandle": "2021-10-27T11:55:00+03:30", "Amount": 272563.4694278689, - "Date": "2021-10-27T11:55:00+03:30", + "Timestamp": "2021-10-27T11:55:00+03:30", "Open": 4029.00, "High": 4047.96, "Low": 4017.84, @@ -252,7 +252,7 @@ { "TimeOfCandle": "2021-10-27T12:00:00+03:30", "Amount": 189129.5282852959, - "Date": "2021-10-27T12:00:00+03:30", + "Timestamp": "2021-10-27T12:00:00+03:30", "Open": 4041.01, "High": 4051.49, "Low": 4025.01, @@ -262,7 +262,7 @@ { "TimeOfCandle": "2021-10-27T12:05:00+03:30", "Amount": 205552.1358770712, - "Date": "2021-10-27T12:05:00+03:30", + "Timestamp": "2021-10-27T12:05:00+03:30", "Open": 4054.51, "High": 4066.64, "Low": 4047.52, @@ -272,7 +272,7 @@ { "TimeOfCandle": "2021-10-27T12:10:00+03:30", "Amount": 152693.0842788783, - "Date": "2021-10-27T12:10:00+03:30", + "Timestamp": "2021-10-27T12:10:00+03:30", "Open": 4057.14, "High": 4057.14, "Low": 4019.91, @@ -282,7 +282,7 @@ { "TimeOfCandle": "2021-10-27T12:15:00+03:30", "Amount": 98541.8880402825, - "Date": "2021-10-27T12:15:00+03:30", + "Timestamp": "2021-10-27T12:15:00+03:30", "Open": 4030.05, "High": 4037.21, "Low": 4015.80, @@ -292,7 +292,7 @@ { "TimeOfCandle": "2021-10-27T12:20:00+03:30", "Amount": 53318.3827931539, - "Date": "2021-10-27T12:20:00+03:30", + "Timestamp": "2021-10-27T12:20:00+03:30", "Open": 4023.80, "High": 4041.68, "Low": 4020.00, @@ -302,7 +302,7 @@ { "TimeOfCandle": "2021-10-27T12:25:00+03:30", "Amount": 41183.5512783411, - "Date": "2021-10-27T12:25:00+03:30", + "Timestamp": "2021-10-27T12:25:00+03:30", "Open": 4037.21, "High": 4051.47, "Low": 4036.55, @@ -312,7 +312,7 @@ { "TimeOfCandle": "2021-10-27T12:30:00+03:30", "Amount": 92519.0718745724, - "Date": "2021-10-27T12:30:00+03:30", + "Timestamp": "2021-10-27T12:30:00+03:30", "Open": 4045.45, "High": 4045.89, "Low": 4027.83, @@ -322,7 +322,7 @@ { "TimeOfCandle": "2021-10-27T12:35:00+03:30", "Amount": 109053.6992621962, - "Date": "2021-10-27T12:35:00+03:30", + "Timestamp": "2021-10-27T12:35:00+03:30", "Open": 4041.27, "High": 4041.71, "Low": 4017.46, @@ -332,7 +332,7 @@ { "TimeOfCandle": "2021-10-27T12:40:00+03:30", "Amount": 125007.8261205876, - "Date": "2021-10-27T12:40:00+03:30", + "Timestamp": "2021-10-27T12:40:00+03:30", "Open": 4026.12, "High": 4031.00, "Low": 4004.06, @@ -342,7 +342,7 @@ { "TimeOfCandle": "2021-10-27T12:45:00+03:30", "Amount": 147232.5785344835, - "Date": "2021-10-27T12:45:00+03:30", + "Timestamp": "2021-10-27T12:45:00+03:30", "Open": 4014.00, "High": 4016.01, "Low": 3998.23, @@ -352,7 +352,7 @@ { "TimeOfCandle": "2021-10-27T12:50:00+03:30", "Amount": 102310.8955090087, - "Date": "2021-10-27T12:50:00+03:30", + "Timestamp": "2021-10-27T12:50:00+03:30", "Open": 4013.20, "High": 4017.07, "Low": 3988.11, @@ -362,7 +362,7 @@ { "TimeOfCandle": "2021-10-27T12:55:00+03:30", "Amount": 132902.3966462262, - "Date": "2021-10-27T12:55:00+03:30", + "Timestamp": "2021-10-27T12:55:00+03:30", "Open": 3991.04, "High": 4009.77, "Low": 3987.20, @@ -372,7 +372,7 @@ { "TimeOfCandle": "2021-10-27T13:00:00+03:30", "Amount": 73266.9402888383, - "Date": "2021-10-27T13:00:00+03:30", + "Timestamp": "2021-10-27T13:00:00+03:30", "Open": 3991.21, "High": 4009.00, "Low": 3991.21, @@ -382,7 +382,7 @@ { "TimeOfCandle": "2021-10-27T13:05:00+03:30", "Amount": 158835.2367543159, - "Date": "2021-10-27T13:05:00+03:30", + "Timestamp": "2021-10-27T13:05:00+03:30", "Open": 3996.90, "High": 4019.99, "Low": 3995.75, @@ -392,7 +392,7 @@ { "TimeOfCandle": "2021-10-27T13:10:00+03:30", "Amount": 44859.2898833674, - "Date": "2021-10-27T13:10:00+03:30", + "Timestamp": "2021-10-27T13:10:00+03:30", "Open": 4019.00, "High": 4027.00, "Low": 4017.12, @@ -402,7 +402,7 @@ { "TimeOfCandle": "2021-10-27T13:15:00+03:30", "Amount": 88053.4843285457, - "Date": "2021-10-27T13:15:00+03:30", + "Timestamp": "2021-10-27T13:15:00+03:30", "Open": 4017.03, "High": 4032.05, "Low": 4015.43, @@ -412,7 +412,7 @@ { "TimeOfCandle": "2021-10-27T13:20:00+03:30", "Amount": 52333.4460034385, - "Date": "2021-10-27T13:20:00+03:30", + "Timestamp": "2021-10-27T13:20:00+03:30", "Open": 4019.25, "High": 4020.96, "Low": 4009.15, @@ -422,7 +422,7 @@ { "TimeOfCandle": "2021-10-27T13:25:00+03:30", "Amount": 98046.1700274930, - "Date": "2021-10-27T13:25:00+03:30", + "Timestamp": "2021-10-27T13:25:00+03:30", "Open": 4017.50, "High": 4018.43, "Low": 4005.09, @@ -432,7 +432,7 @@ { "TimeOfCandle": "2021-10-27T13:30:00+03:30", "Amount": 46053.3690618458, - "Date": "2021-10-27T13:30:00+03:30", + "Timestamp": "2021-10-27T13:30:00+03:30", "Open": 4016.07, "High": 4016.07, "Low": 4000.01, @@ -442,7 +442,7 @@ { "TimeOfCandle": "2021-10-27T13:35:00+03:30", "Amount": 90834.5443393879, - "Date": "2021-10-27T13:35:00+03:30", + "Timestamp": "2021-10-27T13:35:00+03:30", "Open": 4000.80, "High": 4013.30, "Low": 3997.00, @@ -452,7 +452,7 @@ { "TimeOfCandle": "2021-10-27T13:40:00+03:30", "Amount": 82155.6342514178, - "Date": "2021-10-27T13:40:00+03:30", + "Timestamp": "2021-10-27T13:40:00+03:30", "Open": 3999.43, "High": 4010.57, "Low": 3996.37, @@ -462,7 +462,7 @@ { "TimeOfCandle": "2021-10-27T13:45:00+03:30", "Amount": 98227.7457658266, - "Date": "2021-10-27T13:45:00+03:30", + "Timestamp": "2021-10-27T13:45:00+03:30", "Open": 4005.90, "High": 4009.39, "Low": 3988.54, @@ -472,7 +472,7 @@ { "TimeOfCandle": "2021-10-27T13:50:00+03:30", "Amount": 2124956.7030016861, - "Date": "2021-10-27T13:50:00+03:30", + "Timestamp": "2021-10-27T13:50:00+03:30", "Open": 4002.54, "High": 4013.05, "Low": 3990.38, @@ -482,7 +482,7 @@ { "TimeOfCandle": "2021-10-27T13:55:00+03:30", "Amount": 1970623.1467266278, - "Date": "2021-10-27T13:55:00+03:30", + "Timestamp": "2021-10-27T13:55:00+03:30", "Open": 3994.88, "High": 4001.29, "Low": 3986.07, @@ -492,7 +492,7 @@ { "TimeOfCandle": "2021-10-27T14:00:00+03:30", "Amount": 290220.8725276126, - "Date": "2021-10-27T14:00:00+03:30", + "Timestamp": "2021-10-27T14:00:00+03:30", "Open": 3988.00, "High": 4008.37, "Low": 3972.00, @@ -502,7 +502,7 @@ { "TimeOfCandle": "2021-10-27T14:05:00+03:30", "Amount": 101606.6291843867, - "Date": "2021-10-27T14:05:00+03:30", + "Timestamp": "2021-10-27T14:05:00+03:30", "Open": 4000.23, "High": 4004.55, "Low": 3986.84, @@ -512,7 +512,7 @@ { "TimeOfCandle": "2021-10-27T14:10:00+03:30", "Amount": 83358.9949078253, - "Date": "2021-10-27T14:10:00+03:30", + "Timestamp": "2021-10-27T14:10:00+03:30", "Open": 4003.73, "High": 4014.77, "Low": 4000.04, @@ -522,7 +522,7 @@ { "TimeOfCandle": "2021-10-27T14:15:00+03:30", "Amount": 52820.2686269136, - "Date": "2021-10-27T14:15:00+03:30", + "Timestamp": "2021-10-27T14:15:00+03:30", "Open": 4014.51, "High": 4014.51, "Low": 4004.31, @@ -532,7 +532,7 @@ { "TimeOfCandle": "2021-10-27T14:20:00+03:30", "Amount": 62143.4357792429, - "Date": "2021-10-27T14:20:00+03:30", + "Timestamp": "2021-10-27T14:20:00+03:30", "Open": 4014.36, "High": 4031.17, "Low": 4014.36, @@ -542,7 +542,7 @@ { "TimeOfCandle": "2021-10-27T14:25:00+03:30", "Amount": 116474.0283805076, - "Date": "2021-10-27T14:25:00+03:30", + "Timestamp": "2021-10-27T14:25:00+03:30", "Open": 4020.97, "High": 4026.27, "Low": 4014.46, @@ -552,7 +552,7 @@ { "TimeOfCandle": "2021-10-27T14:30:00+03:30", "Amount": 73118.9082466528, - "Date": "2021-10-27T14:30:00+03:30", + "Timestamp": "2021-10-27T14:30:00+03:30", "Open": 4021.46, "High": 4030.01, "Low": 4017.30, @@ -562,7 +562,7 @@ { "TimeOfCandle": "2021-10-27T14:35:00+03:30", "Amount": 88706.0117936053, - "Date": "2021-10-27T14:35:00+03:30", + "Timestamp": "2021-10-27T14:35:00+03:30", "Open": 4028.61, "High": 4035.75, "Low": 4028.61, @@ -572,7 +572,7 @@ { "TimeOfCandle": "2021-10-27T14:40:00+03:30", "Amount": 107147.4920464002, - "Date": "2021-10-27T14:40:00+03:30", + "Timestamp": "2021-10-27T14:40:00+03:30", "Open": 4034.00, "High": 4046.41, "Low": 4031.23, @@ -582,7 +582,7 @@ { "TimeOfCandle": "2021-10-27T14:45:00+03:30", "Amount": 45140.2381872377, - "Date": "2021-10-27T14:45:00+03:30", + "Timestamp": "2021-10-27T14:45:00+03:30", "Open": 4045.31, "High": 4045.31, "Low": 4030.24, @@ -592,7 +592,7 @@ { "TimeOfCandle": "2021-10-27T14:50:00+03:30", "Amount": 148529.2821350580, - "Date": "2021-10-27T14:50:00+03:30", + "Timestamp": "2021-10-27T14:50:00+03:30", "Open": 4033.96, "High": 4037.22, "Low": 4022.02, @@ -602,7 +602,7 @@ { "TimeOfCandle": "2021-10-27T14:55:00+03:30", "Amount": 66960.0661849583, - "Date": "2021-10-27T14:55:00+03:30", + "Timestamp": "2021-10-27T14:55:00+03:30", "Open": 4024.03, "High": 4025.00, "Low": 4014.61, @@ -612,7 +612,7 @@ { "TimeOfCandle": "2021-10-27T15:00:00+03:30", "Amount": 116472.1985497611, - "Date": "2021-10-27T15:00:00+03:30", + "Timestamp": "2021-10-27T15:00:00+03:30", "Open": 4017.49, "High": 4017.49, "Low": 4004.12, @@ -622,7 +622,7 @@ { "TimeOfCandle": "2021-10-27T15:05:00+03:30", "Amount": 62059.9082265501, - "Date": "2021-10-27T15:05:00+03:30", + "Timestamp": "2021-10-27T15:05:00+03:30", "Open": 4008.99, "High": 4017.03, "Low": 4000.00, @@ -632,7 +632,7 @@ { "TimeOfCandle": "2021-10-27T15:10:00+03:30", "Amount": 78430.2838209792, - "Date": "2021-10-27T15:10:00+03:30", + "Timestamp": "2021-10-27T15:10:00+03:30", "Open": 4003.97, "High": 4008.36, "Low": 3997.28, @@ -642,7 +642,7 @@ { "TimeOfCandle": "2021-10-27T15:15:00+03:30", "Amount": 74775.0361081990, - "Date": "2021-10-27T15:15:00+03:30", + "Timestamp": "2021-10-27T15:15:00+03:30", "Open": 4005.15, "High": 4010.99, "Low": 3996.08, @@ -652,7 +652,7 @@ { "TimeOfCandle": "2021-10-27T15:20:00+03:30", "Amount": 69304.8337077704, - "Date": "2021-10-27T15:20:00+03:30", + "Timestamp": "2021-10-27T15:20:00+03:30", "Open": 3997.64, "High": 4005.67, "Low": 3988.17, @@ -662,7 +662,7 @@ { "TimeOfCandle": "2021-10-27T15:25:00+03:30", "Amount": 63705.7338575308, - "Date": "2021-10-27T15:25:00+03:30", + "Timestamp": "2021-10-27T15:25:00+03:30", "Open": 3988.11, "High": 3998.40, "Low": 3987.00, @@ -672,7 +672,7 @@ { "TimeOfCandle": "2021-10-27T15:30:00+03:30", "Amount": 221828.1794979553, - "Date": "2021-10-27T15:30:00+03:30", + "Timestamp": "2021-10-27T15:30:00+03:30", "Open": 3990.29, "High": 3997.33, "Low": 3963.00, @@ -682,7 +682,7 @@ { "TimeOfCandle": "2021-10-27T15:35:00+03:30", "Amount": 116058.4370126909, - "Date": "2021-10-27T15:35:00+03:30", + "Timestamp": "2021-10-27T15:35:00+03:30", "Open": 3995.28, "High": 4002.90, "Low": 3985.12, @@ -692,7 +692,7 @@ { "TimeOfCandle": "2021-10-27T15:40:00+03:30", "Amount": 53330.7365431283, - "Date": "2021-10-27T15:40:00+03:30", + "Timestamp": "2021-10-27T15:40:00+03:30", "Open": 3996.00, "High": 4001.89, "Low": 3985.20, @@ -702,7 +702,7 @@ { "TimeOfCandle": "2021-10-27T15:45:00+03:30", "Amount": 21997.5887486905, - "Date": "2021-10-27T15:45:00+03:30", + "Timestamp": "2021-10-27T15:45:00+03:30", "Open": 3993.86, "High": 4002.79, "Low": 3992.04, @@ -712,7 +712,7 @@ { "TimeOfCandle": "2021-10-27T15:50:00+03:30", "Amount": 48520.8751434566, - "Date": "2021-10-27T15:50:00+03:30", + "Timestamp": "2021-10-27T15:50:00+03:30", "Open": 3997.60, "High": 4013.52, "Low": 3997.14, @@ -722,7 +722,7 @@ { "TimeOfCandle": "2021-10-27T15:55:00+03:30", "Amount": 910637.5813854319, - "Date": "2021-10-27T15:55:00+03:30", + "Timestamp": "2021-10-27T15:55:00+03:30", "Open": 4010.15, "High": 4015.01, "Low": 4008.82, @@ -732,7 +732,7 @@ { "TimeOfCandle": "2021-10-27T16:00:00+03:30", "Amount": 35055.9461030449, - "Date": "2021-10-27T16:00:00+03:30", + "Timestamp": "2021-10-27T16:00:00+03:30", "Open": 4015.40, "High": 4020.00, "Low": 4010.94, @@ -742,7 +742,7 @@ { "TimeOfCandle": "2021-10-27T16:05:00+03:30", "Amount": 103567.0506405367, - "Date": "2021-10-27T16:05:00+03:30", + "Timestamp": "2021-10-27T16:05:00+03:30", "Open": 4018.00, "High": 4023.46, "Low": 4018.00, @@ -752,7 +752,7 @@ { "TimeOfCandle": "2021-10-27T16:10:00+03:30", "Amount": 1490851.0331273030, - "Date": "2021-10-27T16:10:00+03:30", + "Timestamp": "2021-10-27T16:10:00+03:30", "Open": 4022.39, "High": 4022.39, "Low": 4010.00, @@ -762,7 +762,7 @@ { "TimeOfCandle": "2021-10-27T16:15:00+03:30", "Amount": 2236162.5600715379, - "Date": "2021-10-27T16:15:00+03:30", + "Timestamp": "2021-10-27T16:15:00+03:30", "Open": 4010.00, "High": 4010.01, "Low": 3992.76, @@ -772,7 +772,7 @@ { "TimeOfCandle": "2021-10-27T16:20:00+03:30", "Amount": 64652.6701311666, - "Date": "2021-10-27T16:20:00+03:30", + "Timestamp": "2021-10-27T16:20:00+03:30", "Open": 3998.41, "High": 4016.44, "Low": 3998.01, @@ -782,7 +782,7 @@ { "TimeOfCandle": "2021-10-27T16:25:00+03:30", "Amount": 20619.8462768749, - "Date": "2021-10-27T16:25:00+03:30", + "Timestamp": "2021-10-27T16:25:00+03:30", "Open": 4008.38, "High": 4012.00, "Low": 4001.20, @@ -792,7 +792,7 @@ { "TimeOfCandle": "2021-10-27T16:30:00+03:30", "Amount": 319402.1248540172, - "Date": "2021-10-27T16:30:00+03:30", + "Timestamp": "2021-10-27T16:30:00+03:30", "Open": 4012.00, "High": 4014.78, "Low": 4000.00, @@ -802,7 +802,7 @@ { "TimeOfCandle": "2021-10-27T16:35:00+03:30", "Amount": 20982.3739803013, - "Date": "2021-10-27T16:35:00+03:30", + "Timestamp": "2021-10-27T16:35:00+03:30", "Open": 4014.77, "High": 4025.00, "Low": 4014.77, @@ -812,7 +812,7 @@ { "TimeOfCandle": "2021-10-27T16:40:00+03:30", "Amount": 48746.0780131629, - "Date": "2021-10-27T16:40:00+03:30", + "Timestamp": "2021-10-27T16:40:00+03:30", "Open": 4018.87, "High": 4026.00, "Low": 4016.89, @@ -822,7 +822,7 @@ { "TimeOfCandle": "2021-10-27T16:45:00+03:30", "Amount": 85098.1610378864, - "Date": "2021-10-27T16:45:00+03:30", + "Timestamp": "2021-10-27T16:45:00+03:30", "Open": 4026.00, "High": 4026.00, "Low": 4008.56, @@ -832,7 +832,7 @@ { "TimeOfCandle": "2021-10-27T16:50:00+03:30", "Amount": 41730.0008734617, - "Date": "2021-10-27T16:50:00+03:30", + "Timestamp": "2021-10-27T16:50:00+03:30", "Open": 4016.99, "High": 4019.37, "Low": 4007.00, @@ -842,7 +842,7 @@ { "TimeOfCandle": "2021-10-27T16:55:00+03:30", "Amount": 430881.4056792271, - "Date": "2021-10-27T16:55:00+03:30", + "Timestamp": "2021-10-27T16:55:00+03:30", "Open": 4007.14, "High": 4008.00, "Low": 3990.00, @@ -852,7 +852,7 @@ { "TimeOfCandle": "2021-10-27T17:00:00+03:30", "Amount": 35444.2488422846, - "Date": "2021-10-27T17:00:00+03:30", + "Timestamp": "2021-10-27T17:00:00+03:30", "Open": 3997.04, "High": 4002.32, "Low": 3990.00, @@ -862,7 +862,7 @@ { "TimeOfCandle": "2021-10-27T17:05:00+03:30", "Amount": 46657.3033344308, - "Date": "2021-10-27T17:05:00+03:30", + "Timestamp": "2021-10-27T17:05:00+03:30", "Open": 3997.00, "High": 4014.91, "Low": 3995.34, @@ -872,7 +872,7 @@ { "TimeOfCandle": "2021-10-27T17:10:00+03:30", "Amount": 37359.4277336326, - "Date": "2021-10-27T17:10:00+03:30", + "Timestamp": "2021-10-27T17:10:00+03:30", "Open": 4014.00, "High": 4021.00, "Low": 4009.81, @@ -882,7 +882,7 @@ { "TimeOfCandle": "2021-10-27T17:15:00+03:30", "Amount": 78110.4230670868, - "Date": "2021-10-27T17:15:00+03:30", + "Timestamp": "2021-10-27T17:15:00+03:30", "Open": 4022.94, "High": 4027.84, "Low": 4001.91, @@ -892,7 +892,7 @@ { "TimeOfCandle": "2021-10-27T17:20:00+03:30", "Amount": 26460.0944223798, - "Date": "2021-10-27T17:20:00+03:30", + "Timestamp": "2021-10-27T17:20:00+03:30", "Open": 4002.17, "High": 4006.14, "Low": 3996.16, @@ -902,7 +902,7 @@ { "TimeOfCandle": "2021-10-27T17:25:00+03:30", "Amount": 56156.7217013364, - "Date": "2021-10-27T17:25:00+03:30", + "Timestamp": "2021-10-27T17:25:00+03:30", "Open": 3998.24, "High": 4004.64, "Low": 3990.74, @@ -912,7 +912,7 @@ { "TimeOfCandle": "2021-10-27T17:30:00+03:30", "Amount": 14243.4526834259, - "Date": "2021-10-27T17:30:00+03:30", + "Timestamp": "2021-10-27T17:30:00+03:30", "Open": 3994.64, "High": 4004.64, "Low": 3991.04, @@ -922,7 +922,7 @@ { "TimeOfCandle": "2021-10-27T17:35:00+03:30", "Amount": 23717.8848672741, - "Date": "2021-10-27T17:35:00+03:30", + "Timestamp": "2021-10-27T17:35:00+03:30", "Open": 3990.24, "High": 3996.01, "Low": 3990.24, @@ -932,7 +932,7 @@ { "TimeOfCandle": "2021-10-27T17:40:00+03:30", "Amount": 110546.9260679716, - "Date": "2021-10-27T17:40:00+03:30", + "Timestamp": "2021-10-27T17:40:00+03:30", "Open": 3993.03, "High": 3996.31, "Low": 3977.90, @@ -942,7 +942,7 @@ { "TimeOfCandle": "2021-10-27T17:45:00+03:30", "Amount": 59327.4535829998, - "Date": "2021-10-27T17:45:00+03:30", + "Timestamp": "2021-10-27T17:45:00+03:30", "Open": 3982.02, "High": 3982.10, "Low": 3976.13, @@ -952,7 +952,7 @@ { "TimeOfCandle": "2021-10-27T17:50:00+03:30", "Amount": 115547.2986699034, - "Date": "2021-10-27T17:50:00+03:30", + "Timestamp": "2021-10-27T17:50:00+03:30", "Open": 3976.19, "High": 4007.17, "Low": 3966.68, @@ -962,7 +962,7 @@ { "TimeOfCandle": "2021-10-27T17:55:00+03:30", "Amount": 37807.3212559758, - "Date": "2021-10-27T17:55:00+03:30", + "Timestamp": "2021-10-27T17:55:00+03:30", "Open": 4003.03, "High": 4006.99, "Low": 3986.77, @@ -972,7 +972,7 @@ { "TimeOfCandle": "2021-10-27T18:00:00+03:30", "Amount": 40589.7326571969, - "Date": "2021-10-27T18:00:00+03:30", + "Timestamp": "2021-10-27T18:00:00+03:30", "Open": 3988.00, "High": 4005.00, "Low": 3988.00, @@ -982,7 +982,7 @@ { "TimeOfCandle": "2021-10-27T18:05:00+03:30", "Amount": 70403.5437790836, - "Date": "2021-10-27T18:05:00+03:30", + "Timestamp": "2021-10-27T18:05:00+03:30", "Open": 4003.00, "High": 4022.40, "Low": 3996.00, @@ -992,7 +992,7 @@ { "TimeOfCandle": "2021-10-27T18:10:00+03:30", "Amount": 94298.9659173314, - "Date": "2021-10-27T18:10:00+03:30", + "Timestamp": "2021-10-27T18:10:00+03:30", "Open": 4020.28, "High": 4026.00, "Low": 4014.18, @@ -1002,7 +1002,7 @@ { "TimeOfCandle": "2021-10-27T18:15:00+03:30", "Amount": 30485.1910075324, - "Date": "2021-10-27T18:15:00+03:30", + "Timestamp": "2021-10-27T18:15:00+03:30", "Open": 4016.03, "High": 4039.52, "Low": 4016.01, @@ -1012,7 +1012,7 @@ { "TimeOfCandle": "2021-10-27T18:20:00+03:30", "Amount": 76416.9726550779, - "Date": "2021-10-27T18:20:00+03:30", + "Timestamp": "2021-10-27T18:20:00+03:30", "Open": 4036.41, "High": 4038.70, "Low": 4018.00, @@ -1022,7 +1022,7 @@ { "TimeOfCandle": "2021-10-27T18:25:00+03:30", "Amount": 47503.5825096199, - "Date": "2021-10-27T18:25:00+03:30", + "Timestamp": "2021-10-27T18:25:00+03:30", "Open": 4020.00, "High": 4025.93, "Low": 4011.28, @@ -1032,7 +1032,7 @@ { "TimeOfCandle": "2021-10-27T18:30:00+03:30", "Amount": 105518.3959404762, - "Date": "2021-10-27T18:30:00+03:30", + "Timestamp": "2021-10-27T18:30:00+03:30", "Open": 4013.09, "High": 4020.00, "Low": 4008.08, @@ -1042,7 +1042,7 @@ { "TimeOfCandle": "2021-10-27T18:35:00+03:30", "Amount": 65027.5467606292, - "Date": "2021-10-27T18:35:00+03:30", + "Timestamp": "2021-10-27T18:35:00+03:30", "Open": 4009.30, "High": 4025.78, "Low": 4007.42, @@ -1052,7 +1052,7 @@ { "TimeOfCandle": "2021-10-27T18:40:00+03:30", "Amount": 43492.3142140926, - "Date": "2021-10-27T18:40:00+03:30", + "Timestamp": "2021-10-27T18:40:00+03:30", "Open": 4025.58, "High": 4034.82, "Low": 4019.18, @@ -1062,7 +1062,7 @@ { "TimeOfCandle": "2021-10-27T18:45:00+03:30", "Amount": 41467.5231430141, - "Date": "2021-10-27T18:45:00+03:30", + "Timestamp": "2021-10-27T18:45:00+03:30", "Open": 4034.35, "High": 4035.00, "Low": 4022.31, @@ -1072,7 +1072,7 @@ { "TimeOfCandle": "2021-10-27T18:50:00+03:30", "Amount": 43326.9804764149, - "Date": "2021-10-27T18:50:00+03:30", + "Timestamp": "2021-10-27T18:50:00+03:30", "Open": 4021.15, "High": 4021.15, "Low": 4005.03, @@ -1082,7 +1082,7 @@ { "TimeOfCandle": "2021-10-27T18:55:00+03:30", "Amount": 29033.0895006258, - "Date": "2021-10-27T18:55:00+03:30", + "Timestamp": "2021-10-27T18:55:00+03:30", "Open": 4008.01, "High": 4008.01, "Low": 3995.37, @@ -1092,7 +1092,7 @@ { "TimeOfCandle": "2021-10-27T19:00:00+03:30", "Amount": 27447.6735284852, - "Date": "2021-10-27T19:00:00+03:30", + "Timestamp": "2021-10-27T19:00:00+03:30", "Open": 3997.32, "High": 4007.42, "Low": 3994.40, @@ -1102,7 +1102,7 @@ { "TimeOfCandle": "2021-10-27T19:05:00+03:30", "Amount": 60780.3299001833, - "Date": "2021-10-27T19:05:00+03:30", + "Timestamp": "2021-10-27T19:05:00+03:30", "Open": 4009.91, "High": 4011.74, "Low": 3995.18, @@ -1112,7 +1112,7 @@ { "TimeOfCandle": "2021-10-27T19:10:00+03:30", "Amount": 102196.4306436680, - "Date": "2021-10-27T19:10:00+03:30", + "Timestamp": "2021-10-27T19:10:00+03:30", "Open": 3996.14, "High": 3999.10, "Low": 3984.01, @@ -1122,7 +1122,7 @@ { "TimeOfCandle": "2021-10-27T19:15:00+03:30", "Amount": 14416.7893995620, - "Date": "2021-10-27T19:15:00+03:30", + "Timestamp": "2021-10-27T19:15:00+03:30", "Open": 3987.01, "High": 3992.70, "Low": 3985.00, @@ -1132,7 +1132,7 @@ { "TimeOfCandle": "2021-10-27T19:20:00+03:30", "Amount": 39581.0880037031, - "Date": "2021-10-27T19:20:00+03:30", + "Timestamp": "2021-10-27T19:20:00+03:30", "Open": 3988.03, "High": 3999.05, "Low": 3985.03, @@ -1142,7 +1142,7 @@ { "TimeOfCandle": "2021-10-27T19:25:00+03:30", "Amount": 66857.2459465237, - "Date": "2021-10-27T19:25:00+03:30", + "Timestamp": "2021-10-27T19:25:00+03:30", "Open": 3988.05, "High": 3996.82, "Low": 3985.08, @@ -1152,7 +1152,7 @@ { "TimeOfCandle": "2021-10-27T19:30:00+03:30", "Amount": 33569.1745906142, - "Date": "2021-10-27T19:30:00+03:30", + "Timestamp": "2021-10-27T19:30:00+03:30", "Open": 3993.89, "High": 4004.94, "Low": 3990.75, @@ -1162,7 +1162,7 @@ { "TimeOfCandle": "2021-10-27T19:35:00+03:30", "Amount": 81290.5461734230, - "Date": "2021-10-27T19:35:00+03:30", + "Timestamp": "2021-10-27T19:35:00+03:30", "Open": 3996.64, "High": 4009.50, "Low": 3981.01, @@ -1172,7 +1172,7 @@ { "TimeOfCandle": "2021-10-27T19:40:00+03:30", "Amount": 57540.2307408276, - "Date": "2021-10-27T19:40:00+03:30", + "Timestamp": "2021-10-27T19:40:00+03:30", "Open": 3985.76, "High": 3991.79, "Low": 3974.00, @@ -1182,7 +1182,7 @@ { "TimeOfCandle": "2021-10-27T19:45:00+03:30", "Amount": 22353.9525734641, - "Date": "2021-10-27T19:45:00+03:30", + "Timestamp": "2021-10-27T19:45:00+03:30", "Open": 3977.45, "High": 3986.84, "Low": 3975.92, @@ -1192,7 +1192,7 @@ { "TimeOfCandle": "2021-10-27T19:50:00+03:30", "Amount": 166332.5234942742, - "Date": "2021-10-27T19:50:00+03:30", + "Timestamp": "2021-10-27T19:50:00+03:30", "Open": 3982.72, "High": 3988.68, "Low": 3966.01, @@ -1202,7 +1202,7 @@ { "TimeOfCandle": "2021-10-27T19:55:00+03:30", "Amount": 77740.6864855915, - "Date": "2021-10-27T19:55:00+03:30", + "Timestamp": "2021-10-27T19:55:00+03:30", "Open": 3970.50, "High": 3976.00, "Low": 3951.58, @@ -1212,7 +1212,7 @@ { "TimeOfCandle": "2021-10-27T20:00:00+03:30", "Amount": 150124.1088885074, - "Date": "2021-10-27T20:00:00+03:30", + "Timestamp": "2021-10-27T20:00:00+03:30", "Open": 3953.39, "High": 3971.17, "Low": 3950.70, @@ -1222,7 +1222,7 @@ { "TimeOfCandle": "2021-10-27T20:05:00+03:30", "Amount": 49443.4854979036, - "Date": "2021-10-27T20:05:00+03:30", + "Timestamp": "2021-10-27T20:05:00+03:30", "Open": 3969.27, "High": 3991.00, "Low": 3969.27, @@ -1232,7 +1232,7 @@ { "TimeOfCandle": "2021-10-27T20:10:00+03:30", "Amount": 46514.2790878015, - "Date": "2021-10-27T20:10:00+03:30", + "Timestamp": "2021-10-27T20:10:00+03:30", "Open": 3974.99, "High": 3989.00, "Low": 3969.57, @@ -1242,7 +1242,7 @@ { "TimeOfCandle": "2021-10-27T20:15:00+03:30", "Amount": 46794.7610396896, - "Date": "2021-10-27T20:15:00+03:30", + "Timestamp": "2021-10-27T20:15:00+03:30", "Open": 3991.20, "High": 3997.83, "Low": 3979.55, @@ -1252,7 +1252,7 @@ { "TimeOfCandle": "2021-10-27T20:20:00+03:30", "Amount": 31727.9092260733, - "Date": "2021-10-27T20:20:00+03:30", + "Timestamp": "2021-10-27T20:20:00+03:30", "Open": 3982.50, "High": 3992.88, "Low": 3974.13, @@ -1262,7 +1262,7 @@ { "TimeOfCandle": "2021-10-27T20:25:00+03:30", "Amount": 78922.4312739531, - "Date": "2021-10-27T20:25:00+03:30", + "Timestamp": "2021-10-27T20:25:00+03:30", "Open": 3991.22, "High": 3995.17, "Low": 3977.15, @@ -1272,7 +1272,7 @@ { "TimeOfCandle": "2021-10-27T20:30:00+03:30", "Amount": 39017.5323945848, - "Date": "2021-10-27T20:30:00+03:30", + "Timestamp": "2021-10-27T20:30:00+03:30", "Open": 3977.01, "High": 3987.04, "Low": 3977.01, @@ -1282,7 +1282,7 @@ { "TimeOfCandle": "2021-10-27T20:35:00+03:30", "Amount": 133448.2078679945, - "Date": "2021-10-27T20:35:00+03:30", + "Timestamp": "2021-10-27T20:35:00+03:30", "Open": 3986.20, "High": 3996.77, "Low": 3986.20, @@ -1292,7 +1292,7 @@ { "TimeOfCandle": "2021-10-27T20:40:00+03:30", "Amount": 21394.6901842039, - "Date": "2021-10-27T20:40:00+03:30", + "Timestamp": "2021-10-27T20:40:00+03:30", "Open": 3996.72, "High": 4000.06, "Low": 3994.08, @@ -1302,7 +1302,7 @@ { "TimeOfCandle": "2021-10-27T20:45:00+03:30", "Amount": 17995.6504669334, - "Date": "2021-10-27T20:45:00+03:30", + "Timestamp": "2021-10-27T20:45:00+03:30", "Open": 3995.47, "High": 3999.54, "Low": 3993.69, @@ -1312,7 +1312,7 @@ { "TimeOfCandle": "2021-10-27T20:50:00+03:30", "Amount": 51214.2288879049, - "Date": "2021-10-27T20:50:00+03:30", + "Timestamp": "2021-10-27T20:50:00+03:30", "Open": 3995.49, "High": 4004.66, "Low": 3991.93, @@ -1322,7 +1322,7 @@ { "TimeOfCandle": "2021-10-27T20:55:00+03:30", "Amount": 28253.4401483474, - "Date": "2021-10-27T20:55:00+03:30", + "Timestamp": "2021-10-27T20:55:00+03:30", "Open": 3998.10, "High": 4001.99, "Low": 3995.11, @@ -1332,7 +1332,7 @@ { "TimeOfCandle": "2021-10-27T21:00:00+03:30", "Amount": 58895.6822952612, - "Date": "2021-10-27T21:00:00+03:30", + "Timestamp": "2021-10-27T21:00:00+03:30", "Open": 3995.12, "High": 4004.65, "Low": 3994.48, @@ -1342,7 +1342,7 @@ { "TimeOfCandle": "2021-10-27T21:05:00+03:30", "Amount": 51273.3983814235, - "Date": "2021-10-27T21:05:00+03:30", + "Timestamp": "2021-10-27T21:05:00+03:30", "Open": 4000.39, "High": 4000.39, "Low": 3989.20, @@ -1352,7 +1352,7 @@ { "TimeOfCandle": "2021-10-27T21:10:00+03:30", "Amount": 30710.2070649218, - "Date": "2021-10-27T21:10:00+03:30", + "Timestamp": "2021-10-27T21:10:00+03:30", "Open": 3996.41, "High": 3997.52, "Low": 3983.00, @@ -1362,7 +1362,7 @@ { "TimeOfCandle": "2021-10-27T21:15:00+03:30", "Amount": 88986.9621437308, - "Date": "2021-10-27T21:15:00+03:30", + "Timestamp": "2021-10-27T21:15:00+03:30", "Open": 3985.65, "High": 3989.99, "Low": 3980.00, @@ -1372,7 +1372,7 @@ { "TimeOfCandle": "2021-10-27T21:20:00+03:30", "Amount": 137937.2976391142, - "Date": "2021-10-27T21:20:00+03:30", + "Timestamp": "2021-10-27T21:20:00+03:30", "Open": 3982.88, "High": 3988.88, "Low": 3980.00, @@ -1382,7 +1382,7 @@ { "TimeOfCandle": "2021-10-27T21:25:00+03:30", "Amount": 75699.0285909942, - "Date": "2021-10-27T21:25:00+03:30", + "Timestamp": "2021-10-27T21:25:00+03:30", "Open": 3984.61, "High": 3990.00, "Low": 3983.99, @@ -1392,7 +1392,7 @@ { "TimeOfCandle": "2021-10-27T21:30:00+03:30", "Amount": 30744.6813743739, - "Date": "2021-10-27T21:30:00+03:30", + "Timestamp": "2021-10-27T21:30:00+03:30", "Open": 3987.23, "High": 4000.00, "Low": 3987.23, @@ -1402,7 +1402,7 @@ { "TimeOfCandle": "2021-10-27T21:35:00+03:30", "Amount": 159389.8402236414, - "Date": "2021-10-27T21:35:00+03:30", + "Timestamp": "2021-10-27T21:35:00+03:30", "Open": 4000.32, "High": 4001.00, "Low": 3991.44, @@ -1412,7 +1412,7 @@ { "TimeOfCandle": "2021-10-27T21:40:00+03:30", "Amount": 70684.9116768428, - "Date": "2021-10-27T21:40:00+03:30", + "Timestamp": "2021-10-27T21:40:00+03:30", "Open": 3995.55, "High": 3996.92, "Low": 3986.26, @@ -1422,7 +1422,7 @@ { "TimeOfCandle": "2021-10-27T21:45:00+03:30", "Amount": 114116.0910697675, - "Date": "2021-10-27T21:45:00+03:30", + "Timestamp": "2021-10-27T21:45:00+03:30", "Open": 3986.26, "High": 3995.05, "Low": 3983.02, @@ -1432,7 +1432,7 @@ { "TimeOfCandle": "2021-10-27T21:50:00+03:30", "Amount": 112671.5869541956, - "Date": "2021-10-27T21:50:00+03:30", + "Timestamp": "2021-10-27T21:50:00+03:30", "Open": 3996.80, "High": 4000.00, "Low": 3994.33, @@ -1442,7 +1442,7 @@ { "TimeOfCandle": "2021-10-27T21:55:00+03:30", "Amount": 39700.5545955455, - "Date": "2021-10-27T21:55:00+03:30", + "Timestamp": "2021-10-27T21:55:00+03:30", "Open": 4000.00, "High": 4002.01, "Low": 3995.82, @@ -1452,7 +1452,7 @@ { "TimeOfCandle": "2021-10-27T22:00:00+03:30", "Amount": 64357.7482437342, - "Date": "2021-10-27T22:00:00+03:30", + "Timestamp": "2021-10-27T22:00:00+03:30", "Open": 3997.12, "High": 4001.00, "Low": 3996.56, @@ -1462,7 +1462,7 @@ { "TimeOfCandle": "2021-10-27T22:05:00+03:30", "Amount": 149177.2301050255, - "Date": "2021-10-27T22:05:00+03:30", + "Timestamp": "2021-10-27T22:05:00+03:30", "Open": 4002.70, "High": 4006.05, "Low": 3986.75, @@ -1472,7 +1472,7 @@ { "TimeOfCandle": "2021-10-27T22:10:00+03:30", "Amount": 14613.8462000497, - "Date": "2021-10-27T22:10:00+03:30", + "Timestamp": "2021-10-27T22:10:00+03:30", "Open": 3993.10, "High": 3993.10, "Low": 3983.26, @@ -1482,7 +1482,7 @@ { "TimeOfCandle": "2021-10-27T22:15:00+03:30", "Amount": 37524.6073212686, - "Date": "2021-10-27T22:15:00+03:30", + "Timestamp": "2021-10-27T22:15:00+03:30", "Open": 3985.18, "High": 3985.19, "Low": 3975.61, @@ -1492,7 +1492,7 @@ { "TimeOfCandle": "2021-10-27T22:20:00+03:30", "Amount": 39998.1036182549, - "Date": "2021-10-27T22:20:00+03:30", + "Timestamp": "2021-10-27T22:20:00+03:30", "Open": 3976.86, "High": 3985.21, "Low": 3976.86, @@ -1502,7 +1502,7 @@ { "TimeOfCandle": "2021-10-27T22:25:00+03:30", "Amount": 303615.7030023482, - "Date": "2021-10-27T22:25:00+03:30", + "Timestamp": "2021-10-27T22:25:00+03:30", "Open": 3981.71, "High": 3982.99, "Low": 3966.00, @@ -1512,7 +1512,7 @@ { "TimeOfCandle": "2021-10-27T22:30:00+03:30", "Amount": 94867.3177641075, - "Date": "2021-10-27T22:30:00+03:30", + "Timestamp": "2021-10-27T22:30:00+03:30", "Open": 3968.16, "High": 3975.43, "Low": 3955.00, @@ -1522,7 +1522,7 @@ { "TimeOfCandle": "2021-10-27T22:35:00+03:30", "Amount": 69408.1348228205, - "Date": "2021-10-27T22:35:00+03:30", + "Timestamp": "2021-10-27T22:35:00+03:30", "Open": 3958.32, "High": 3964.46, "Low": 3951.65, @@ -1532,7 +1532,7 @@ { "TimeOfCandle": "2021-10-27T22:40:00+03:30", "Amount": 80717.8313752461, - "Date": "2021-10-27T22:40:00+03:30", + "Timestamp": "2021-10-27T22:40:00+03:30", "Open": 3957.04, "High": 3957.04, "Low": 3949.00, @@ -1542,7 +1542,7 @@ { "TimeOfCandle": "2021-10-27T22:45:00+03:30", "Amount": 242708.8666580094, - "Date": "2021-10-27T22:45:00+03:30", + "Timestamp": "2021-10-27T22:45:00+03:30", "Open": 3949.70, "High": 3974.40, "Low": 3949.70, @@ -1552,7 +1552,7 @@ { "TimeOfCandle": "2021-10-27T22:50:00+03:30", "Amount": 227192.0887373993, - "Date": "2021-10-27T22:50:00+03:30", + "Timestamp": "2021-10-27T22:50:00+03:30", "Open": 3951.01, "High": 3971.00, "Low": 3945.00, @@ -1562,7 +1562,7 @@ { "TimeOfCandle": "2021-10-27T22:55:00+03:30", "Amount": 144539.4606930547, - "Date": "2021-10-27T22:55:00+03:30", + "Timestamp": "2021-10-27T22:55:00+03:30", "Open": 3970.63, "High": 3977.00, "Low": 3957.08, @@ -1572,7 +1572,7 @@ { "TimeOfCandle": "2021-10-27T23:00:00+03:30", "Amount": 186346.2273317000, - "Date": "2021-10-27T23:00:00+03:30", + "Timestamp": "2021-10-27T23:00:00+03:30", "Open": 3960.01, "High": 3960.01, "Low": 3939.99, @@ -1582,7 +1582,7 @@ { "TimeOfCandle": "2021-10-27T23:05:00+03:30", "Amount": 74035.7146876510, - "Date": "2021-10-27T23:05:00+03:30", + "Timestamp": "2021-10-27T23:05:00+03:30", "Open": 3960.00, "High": 3978.00, "Low": 3955.68, @@ -1592,7 +1592,7 @@ { "TimeOfCandle": "2021-10-27T23:10:00+03:30", "Amount": 327917.1808982129, - "Date": "2021-10-27T23:10:00+03:30", + "Timestamp": "2021-10-27T23:10:00+03:30", "Open": 3966.79, "High": 3968.00, "Low": 3954.62, @@ -1602,7 +1602,7 @@ { "TimeOfCandle": "2021-10-27T23:15:00+03:30", "Amount": 100023.7304644088, - "Date": "2021-10-27T23:15:00+03:30", + "Timestamp": "2021-10-27T23:15:00+03:30", "Open": 3956.51, "High": 3971.78, "Low": 3956.00, @@ -1612,7 +1612,7 @@ { "TimeOfCandle": "2021-10-27T23:20:00+03:30", "Amount": 81712.5185117450, - "Date": "2021-10-27T23:20:00+03:30", + "Timestamp": "2021-10-27T23:20:00+03:30", "Open": 3971.78, "High": 3989.99, "Low": 3970.50, @@ -1622,7 +1622,7 @@ { "TimeOfCandle": "2021-10-27T23:25:00+03:30", "Amount": 82774.0681344909, - "Date": "2021-10-27T23:25:00+03:30", + "Timestamp": "2021-10-27T23:25:00+03:30", "Open": 3989.98, "High": 4004.00, "Low": 3989.13, @@ -1632,7 +1632,7 @@ { "TimeOfCandle": "2021-10-27T23:30:00+03:30", "Amount": 106105.0303093647, - "Date": "2021-10-27T23:30:00+03:30", + "Timestamp": "2021-10-27T23:30:00+03:30", "Open": 3997.69, "High": 4022.73, "Low": 3994.01, @@ -1642,7 +1642,7 @@ { "TimeOfCandle": "2021-10-27T23:35:00+03:30", "Amount": 89768.7369124281, - "Date": "2021-10-27T23:35:00+03:30", + "Timestamp": "2021-10-27T23:35:00+03:30", "Open": 4022.52, "High": 4022.52, "Low": 4006.82, @@ -1652,7 +1652,7 @@ { "TimeOfCandle": "2021-10-27T23:40:00+03:30", "Amount": 232493.5492491686, - "Date": "2021-10-27T23:40:00+03:30", + "Timestamp": "2021-10-27T23:40:00+03:30", "Open": 4007.01, "High": 4009.00, "Low": 3977.64, @@ -1662,7 +1662,7 @@ { "TimeOfCandle": "2021-10-27T23:45:00+03:30", "Amount": 129847.5874973421, - "Date": "2021-10-27T23:45:00+03:30", + "Timestamp": "2021-10-27T23:45:00+03:30", "Open": 3978.77, "High": 3991.34, "Low": 3977.63, @@ -1672,7 +1672,7 @@ { "TimeOfCandle": "2021-10-27T23:50:00+03:30", "Amount": 67983.2700277651, - "Date": "2021-10-27T23:50:00+03:30", + "Timestamp": "2021-10-27T23:50:00+03:30", "Open": 3981.60, "High": 3986.64, "Low": 3977.81, @@ -1682,7 +1682,7 @@ { "TimeOfCandle": "2021-10-27T23:55:00+03:30", "Amount": 16043.0685293261, - "Date": "2021-10-27T23:55:00+03:30", + "Timestamp": "2021-10-27T23:55:00+03:30", "Open": 3979.39, "High": 3979.39, "Low": 3974.31, @@ -1692,7 +1692,7 @@ { "TimeOfCandle": "2021-10-28T00:00:00+03:30", "Amount": 34836.9960663127, - "Date": "2021-10-28T00:00:00+03:30", + "Timestamp": "2021-10-28T00:00:00+03:30", "Open": 3975.01, "High": 3985.00, "Low": 3968.49, @@ -1702,7 +1702,7 @@ { "TimeOfCandle": "2021-10-28T00:05:00+03:30", "Amount": 49114.9030808019, - "Date": "2021-10-28T00:05:00+03:30", + "Timestamp": "2021-10-28T00:05:00+03:30", "Open": 3968.48, "High": 3976.00, "Low": 3967.00, @@ -1712,7 +1712,7 @@ { "TimeOfCandle": "2021-10-28T00:10:00+03:30", "Amount": 37188.1367738455, - "Date": "2021-10-28T00:10:00+03:30", + "Timestamp": "2021-10-28T00:10:00+03:30", "Open": 3968.19, "High": 3968.19, "Low": 3958.77, @@ -1722,7 +1722,7 @@ { "TimeOfCandle": "2021-10-28T00:15:00+03:30", "Amount": 46604.1712072566, - "Date": "2021-10-28T00:15:00+03:30", + "Timestamp": "2021-10-28T00:15:00+03:30", "Open": 3961.03, "High": 3977.00, "Low": 3960.66, @@ -1732,7 +1732,7 @@ { "TimeOfCandle": "2021-10-28T00:20:00+03:30", "Amount": 48573.0487806496, - "Date": "2021-10-28T00:20:00+03:30", + "Timestamp": "2021-10-28T00:20:00+03:30", "Open": 3974.92, "High": 3980.97, "Low": 3966.22, @@ -1742,7 +1742,7 @@ { "TimeOfCandle": "2021-10-28T00:25:00+03:30", "Amount": 65288.0958001717, - "Date": "2021-10-28T00:25:00+03:30", + "Timestamp": "2021-10-28T00:25:00+03:30", "Open": 3978.44, "High": 3983.99, "Low": 3975.41, @@ -1752,7 +1752,7 @@ { "TimeOfCandle": "2021-10-28T00:30:00+03:30", "Amount": 106878.1389167607, - "Date": "2021-10-28T00:30:00+03:30", + "Timestamp": "2021-10-28T00:30:00+03:30", "Open": 3983.00, "High": 3990.00, "Low": 3972.55, @@ -1762,7 +1762,7 @@ { "TimeOfCandle": "2021-10-28T00:35:00+03:30", "Amount": 15221.3173230813, - "Date": "2021-10-28T00:35:00+03:30", + "Timestamp": "2021-10-28T00:35:00+03:30", "Open": 3987.37, "High": 3987.37, "Low": 3981.16, @@ -1772,7 +1772,7 @@ { "TimeOfCandle": "2021-10-28T00:40:00+03:30", "Amount": 19901.4094844077, - "Date": "2021-10-28T00:40:00+03:30", + "Timestamp": "2021-10-28T00:40:00+03:30", "Open": 3985.13, "High": 3991.00, "Low": 3985.13, @@ -1782,7 +1782,7 @@ { "TimeOfCandle": "2021-10-28T00:45:00+03:30", "Amount": 8190.7067113876, - "Date": "2021-10-28T00:45:00+03:30", + "Timestamp": "2021-10-28T00:45:00+03:30", "Open": 3991.00, "High": 3994.58, "Low": 3989.23, @@ -1792,7 +1792,7 @@ { "TimeOfCandle": "2021-10-28T00:50:00+03:30", "Amount": 487558.2178707675, - "Date": "2021-10-28T00:50:00+03:30", + "Timestamp": "2021-10-28T00:50:00+03:30", "Open": 3996.75, "High": 3996.75, "Low": 3987.70, @@ -1802,7 +1802,7 @@ { "TimeOfCandle": "2021-10-28T00:55:00+03:30", "Amount": 14335.0927518033, - "Date": "2021-10-28T00:55:00+03:30", + "Timestamp": "2021-10-28T00:55:00+03:30", "Open": 3989.73, "High": 3989.73, "Low": 3985.78, @@ -1812,7 +1812,7 @@ { "TimeOfCandle": "2021-10-28T01:00:00+03:30", "Amount": 12354.9197489652, - "Date": "2021-10-28T01:00:00+03:30", + "Timestamp": "2021-10-28T01:00:00+03:30", "Open": 3986.90, "High": 3997.15, "Low": 3985.78, @@ -1822,7 +1822,7 @@ { "TimeOfCandle": "2021-10-28T01:05:00+03:30", "Amount": 40364.0646764932, - "Date": "2021-10-28T01:05:00+03:30", + "Timestamp": "2021-10-28T01:05:00+03:30", "Open": 3997.08, "High": 3998.20, "Low": 3993.02, @@ -1832,7 +1832,7 @@ { "TimeOfCandle": "2021-10-28T01:10:00+03:30", "Amount": 229209.3429570671, - "Date": "2021-10-28T01:10:00+03:30", + "Timestamp": "2021-10-28T01:10:00+03:30", "Open": 3996.99, "High": 4004.37, "Low": 3996.99, @@ -1842,7 +1842,7 @@ { "TimeOfCandle": "2021-10-28T01:15:00+03:30", "Amount": 31346.6619300750, - "Date": "2021-10-28T01:15:00+03:30", + "Timestamp": "2021-10-28T01:15:00+03:30", "Open": 4002.59, "High": 4010.00, "Low": 4001.58, @@ -1852,7 +1852,7 @@ { "TimeOfCandle": "2021-10-28T01:20:00+03:30", "Amount": 57976.4738653399, - "Date": "2021-10-28T01:20:00+03:30", + "Timestamp": "2021-10-28T01:20:00+03:30", "Open": 4001.30, "High": 4004.76, "Low": 3994.84, @@ -1862,7 +1862,7 @@ { "TimeOfCandle": "2021-10-28T01:25:00+03:30", "Amount": 65667.2820190157, - "Date": "2021-10-28T01:25:00+03:30", + "Timestamp": "2021-10-28T01:25:00+03:30", "Open": 4002.62, "High": 4002.62, "Low": 3993.57, @@ -1872,7 +1872,7 @@ { "TimeOfCandle": "2021-10-28T01:30:00+03:30", "Amount": 41969.6892109929, - "Date": "2021-10-28T01:30:00+03:30", + "Timestamp": "2021-10-28T01:30:00+03:30", "Open": 3993.57, "High": 3998.71, "Low": 3985.90, @@ -1882,7 +1882,7 @@ { "TimeOfCandle": "2021-10-28T01:35:00+03:30", "Amount": 22620.1554278169, - "Date": "2021-10-28T01:35:00+03:30", + "Timestamp": "2021-10-28T01:35:00+03:30", "Open": 3988.08, "High": 3990.90, "Low": 3985.67, @@ -1892,7 +1892,7 @@ { "TimeOfCandle": "2021-10-28T01:40:00+03:30", "Amount": 127048.3484521555, - "Date": "2021-10-28T01:40:00+03:30", + "Timestamp": "2021-10-28T01:40:00+03:30", "Open": 3988.00, "High": 3988.00, "Low": 3974.74, @@ -1902,7 +1902,7 @@ { "TimeOfCandle": "2021-10-28T01:45:00+03:30", "Amount": 83169.8303165688, - "Date": "2021-10-28T01:45:00+03:30", + "Timestamp": "2021-10-28T01:45:00+03:30", "Open": 3975.80, "High": 3982.45, "Low": 3975.80, @@ -1912,7 +1912,7 @@ { "TimeOfCandle": "2021-10-28T01:50:00+03:30", "Amount": 39434.4127253306, - "Date": "2021-10-28T01:50:00+03:30", + "Timestamp": "2021-10-28T01:50:00+03:30", "Open": 3975.79, "High": 3975.79, "Low": 3970.48, @@ -1922,7 +1922,7 @@ { "TimeOfCandle": "2021-10-28T01:55:00+03:30", "Amount": 52445.5821880096, - "Date": "2021-10-28T01:55:00+03:30", + "Timestamp": "2021-10-28T01:55:00+03:30", "Open": 3970.49, "High": 3985.61, "Low": 3970.01, @@ -1932,7 +1932,7 @@ { "TimeOfCandle": "2021-10-28T02:00:00+03:30", "Amount": 21106.8787935945, - "Date": "2021-10-28T02:00:00+03:30", + "Timestamp": "2021-10-28T02:00:00+03:30", "Open": 3983.00, "High": 3985.31, "Low": 3977.38, @@ -1942,7 +1942,7 @@ { "TimeOfCandle": "2021-10-28T02:05:00+03:30", "Amount": 40052.2333173531, - "Date": "2021-10-28T02:05:00+03:30", + "Timestamp": "2021-10-28T02:05:00+03:30", "Open": 3987.99, "High": 3990.15, "Low": 3975.92, @@ -1952,7 +1952,7 @@ { "TimeOfCandle": "2021-10-28T02:10:00+03:30", "Amount": 214487.7497857716, - "Date": "2021-10-28T02:10:00+03:30", + "Timestamp": "2021-10-28T02:10:00+03:30", "Open": 3975.94, "High": 3975.94, "Low": 3970.00, @@ -1962,7 +1962,7 @@ { "TimeOfCandle": "2021-10-28T02:15:00+03:30", "Amount": 32175.6777478184, - "Date": "2021-10-28T02:15:00+03:30", + "Timestamp": "2021-10-28T02:15:00+03:30", "Open": 3968.14, "High": 3971.38, "Low": 3960.00, @@ -1972,7 +1972,7 @@ { "TimeOfCandle": "2021-10-28T02:20:00+03:30", "Amount": 19627.3625307061, - "Date": "2021-10-28T02:20:00+03:30", + "Timestamp": "2021-10-28T02:20:00+03:30", "Open": 3960.21, "High": 3965.78, "Low": 3958.01, @@ -1982,7 +1982,7 @@ { "TimeOfCandle": "2021-10-28T02:25:00+03:30", "Amount": 6893.4923853207, - "Date": "2021-10-28T02:25:00+03:30", + "Timestamp": "2021-10-28T02:25:00+03:30", "Open": 3965.39, "High": 3975.14, "Low": 3965.39, @@ -1992,7 +1992,7 @@ { "TimeOfCandle": "2021-10-28T02:30:00+03:30", "Amount": 28637.4926543907, - "Date": "2021-10-28T02:30:00+03:30", + "Timestamp": "2021-10-28T02:30:00+03:30", "Open": 3974.51, "High": 3982.64, "Low": 3974.01, @@ -2002,7 +2002,7 @@ { "TimeOfCandle": "2021-10-28T02:35:00+03:30", "Amount": 39542.6510080857, - "Date": "2021-10-28T02:35:00+03:30", + "Timestamp": "2021-10-28T02:35:00+03:30", "Open": 3976.78, "High": 3981.33, "Low": 3970.41, @@ -2012,7 +2012,7 @@ { "TimeOfCandle": "2021-10-28T02:40:00+03:30", "Amount": 42491.3188601221, - "Date": "2021-10-28T02:40:00+03:30", + "Timestamp": "2021-10-28T02:40:00+03:30", "Open": 3979.77, "High": 3979.77, "Low": 3969.01, @@ -2022,7 +2022,7 @@ { "TimeOfCandle": "2021-10-28T02:45:00+03:30", "Amount": 4649.3478574620, - "Date": "2021-10-28T02:45:00+03:30", + "Timestamp": "2021-10-28T02:45:00+03:30", "Open": 3971.79, "High": 3971.79, "Low": 3961.79, @@ -2032,7 +2032,7 @@ { "TimeOfCandle": "2021-10-28T02:50:00+03:30", "Amount": 23515.6423111153, - "Date": "2021-10-28T02:50:00+03:30", + "Timestamp": "2021-10-28T02:50:00+03:30", "Open": 3961.81, "High": 3961.81, "Low": 3952.42, @@ -2042,7 +2042,7 @@ { "TimeOfCandle": "2021-10-28T02:55:00+03:30", "Amount": 29357.1486591352, - "Date": "2021-10-28T02:55:00+03:30", + "Timestamp": "2021-10-28T02:55:00+03:30", "Open": 3951.54, "High": 3960.62, "Low": 3950.00, @@ -2052,7 +2052,7 @@ { "TimeOfCandle": "2021-10-28T03:00:00+03:30", "Amount": 23042.5607715281, - "Date": "2021-10-28T03:00:00+03:30", + "Timestamp": "2021-10-28T03:00:00+03:30", "Open": 3952.51, "High": 3963.99, "Low": 3952.51, @@ -2062,7 +2062,7 @@ { "TimeOfCandle": "2021-10-28T03:05:00+03:30", "Amount": 42530.4085163106, - "Date": "2021-10-28T03:05:00+03:30", + "Timestamp": "2021-10-28T03:05:00+03:30", "Open": 3956.10, "High": 3956.10, "Low": 3943.26, @@ -2072,7 +2072,7 @@ { "TimeOfCandle": "2021-10-28T03:10:00+03:30", "Amount": 39556.1025329786, - "Date": "2021-10-28T03:10:00+03:30", + "Timestamp": "2021-10-28T03:10:00+03:30", "Open": 3943.20, "High": 3948.69, "Low": 3942.01, @@ -2082,7 +2082,7 @@ { "TimeOfCandle": "2021-10-28T03:15:00+03:30", "Amount": 178762.7658904020, - "Date": "2021-10-28T03:15:00+03:30", + "Timestamp": "2021-10-28T03:15:00+03:30", "Open": 3945.38, "High": 3952.45, "Low": 3912.18, @@ -2092,7 +2092,7 @@ { "TimeOfCandle": "2021-10-28T03:20:00+03:30", "Amount": 72632.1365462373, - "Date": "2021-10-28T03:20:00+03:30", + "Timestamp": "2021-10-28T03:20:00+03:30", "Open": 3930.03, "High": 3948.13, "Low": 3923.49, @@ -2102,7 +2102,7 @@ { "TimeOfCandle": "2021-10-28T03:25:00+03:30", "Amount": 62098.1811618796, - "Date": "2021-10-28T03:25:00+03:30", + "Timestamp": "2021-10-28T03:25:00+03:30", "Open": 3948.84, "High": 3949.70, "Low": 3920.88, @@ -2112,7 +2112,7 @@ { "TimeOfCandle": "2021-10-28T03:30:00+03:30", "Amount": 31756.3007380927, - "Date": "2021-10-28T03:30:00+03:30", + "Timestamp": "2021-10-28T03:30:00+03:30", "Open": 3922.00, "High": 3939.59, "Low": 3922.00, @@ -2122,7 +2122,7 @@ { "TimeOfCandle": "2021-10-28T03:35:00+03:30", "Amount": 48851.7663252967, - "Date": "2021-10-28T03:35:00+03:30", + "Timestamp": "2021-10-28T03:35:00+03:30", "Open": 3936.99, "High": 3949.83, "Low": 3926.83, @@ -2132,7 +2132,7 @@ { "TimeOfCandle": "2021-10-28T03:40:00+03:30", "Amount": 45433.3652010404, - "Date": "2021-10-28T03:40:00+03:30", + "Timestamp": "2021-10-28T03:40:00+03:30", "Open": 3926.78, "High": 3956.03, "Low": 3926.78, @@ -2142,7 +2142,7 @@ { "TimeOfCandle": "2021-10-28T03:45:00+03:30", "Amount": 60588.5254922610, - "Date": "2021-10-28T03:45:00+03:30", + "Timestamp": "2021-10-28T03:45:00+03:30", "Open": 3958.00, "High": 3967.24, "Low": 3955.51, @@ -2152,7 +2152,7 @@ { "TimeOfCandle": "2021-10-28T03:50:00+03:30", "Amount": 55195.7613779615, - "Date": "2021-10-28T03:50:00+03:30", + "Timestamp": "2021-10-28T03:50:00+03:30", "Open": 3963.88, "High": 3965.91, "Low": 3955.14, @@ -2162,7 +2162,7 @@ { "TimeOfCandle": "2021-10-28T03:55:00+03:30", "Amount": 2856.5374718826, - "Date": "2021-10-28T03:55:00+03:30", + "Timestamp": "2021-10-28T03:55:00+03:30", "Open": 3956.99, "High": 3956.99, "Low": 3945.96, @@ -2172,7 +2172,7 @@ { "TimeOfCandle": "2021-10-28T04:00:00+03:30", "Amount": 35308.7610833563, - "Date": "2021-10-28T04:00:00+03:30", + "Timestamp": "2021-10-28T04:00:00+03:30", "Open": 3947.13, "High": 3948.44, "Low": 3929.76, @@ -2182,7 +2182,7 @@ { "TimeOfCandle": "2021-10-28T04:05:00+03:30", "Amount": 146879.0690564311, - "Date": "2021-10-28T04:05:00+03:30", + "Timestamp": "2021-10-28T04:05:00+03:30", "Open": 3931.00, "High": 3950.08, "Low": 3930.68, @@ -2192,7 +2192,7 @@ { "TimeOfCandle": "2021-10-28T04:10:00+03:30", "Amount": 111037.5786578993, - "Date": "2021-10-28T04:10:00+03:30", + "Timestamp": "2021-10-28T04:10:00+03:30", "Open": 3943.76, "High": 3943.76, "Low": 3933.44, @@ -2202,7 +2202,7 @@ { "TimeOfCandle": "2021-10-28T04:15:00+03:30", "Amount": 59594.7710248393, - "Date": "2021-10-28T04:15:00+03:30", + "Timestamp": "2021-10-28T04:15:00+03:30", "Open": 3941.01, "High": 3948.92, "Low": 3930.04, @@ -2212,7 +2212,7 @@ { "TimeOfCandle": "2021-10-28T04:20:00+03:30", "Amount": 61073.9412918996, - "Date": "2021-10-28T04:20:00+03:30", + "Timestamp": "2021-10-28T04:20:00+03:30", "Open": 3930.86, "High": 3932.91, "Low": 3920.23, @@ -2222,7 +2222,7 @@ { "TimeOfCandle": "2021-10-28T04:25:00+03:30", "Amount": 16457.5220338643, - "Date": "2021-10-28T04:25:00+03:30", + "Timestamp": "2021-10-28T04:25:00+03:30", "Open": 3929.00, "High": 3929.00, "Low": 3915.00, @@ -2232,7 +2232,7 @@ { "TimeOfCandle": "2021-10-28T04:30:00+03:30", "Amount": 261686.4374838773, - "Date": "2021-10-28T04:30:00+03:30", + "Timestamp": "2021-10-28T04:30:00+03:30", "Open": 3912.25, "High": 3930.02, "Low": 3890.00, @@ -2242,7 +2242,7 @@ { "TimeOfCandle": "2021-10-28T04:35:00+03:30", "Amount": 35550.2363406301, - "Date": "2021-10-28T04:35:00+03:30", + "Timestamp": "2021-10-28T04:35:00+03:30", "Open": 3932.03, "High": 3942.50, "Low": 3929.63, @@ -2252,7 +2252,7 @@ { "TimeOfCandle": "2021-10-28T04:40:00+03:30", "Amount": 61593.6344219828, - "Date": "2021-10-28T04:40:00+03:30", + "Timestamp": "2021-10-28T04:40:00+03:30", "Open": 3941.34, "High": 3952.40, "Low": 3940.15, @@ -2262,7 +2262,7 @@ { "TimeOfCandle": "2021-10-28T04:45:00+03:30", "Amount": 28698.6822369537, - "Date": "2021-10-28T04:45:00+03:30", + "Timestamp": "2021-10-28T04:45:00+03:30", "Open": 3948.04, "High": 3964.28, "Low": 3943.70, @@ -2272,7 +2272,7 @@ { "TimeOfCandle": "2021-10-28T04:50:00+03:30", "Amount": 35214.5840803390, - "Date": "2021-10-28T04:50:00+03:30", + "Timestamp": "2021-10-28T04:50:00+03:30", "Open": 3958.39, "High": 3971.33, "Low": 3947.02, @@ -2282,7 +2282,7 @@ { "TimeOfCandle": "2021-10-28T04:55:00+03:30", "Amount": 22680.0582273844, - "Date": "2021-10-28T04:55:00+03:30", + "Timestamp": "2021-10-28T04:55:00+03:30", "Open": 3949.04, "High": 3966.13, "Low": 3949.04, @@ -2292,7 +2292,7 @@ { "TimeOfCandle": "2021-10-28T05:00:00+03:30", "Amount": 12510.2603304619, - "Date": "2021-10-28T05:00:00+03:30", + "Timestamp": "2021-10-28T05:00:00+03:30", "Open": 3966.48, "High": 3968.58, "Low": 3956.14, @@ -2302,7 +2302,7 @@ { "TimeOfCandle": "2021-10-28T05:05:00+03:30", "Amount": 4157.8630732236, - "Date": "2021-10-28T05:05:00+03:30", + "Timestamp": "2021-10-28T05:05:00+03:30", "Open": 3953.75, "High": 3961.31, "Low": 3953.75, @@ -2312,7 +2312,7 @@ { "TimeOfCandle": "2021-10-28T05:10:00+03:30", "Amount": 55071.1071877642, - "Date": "2021-10-28T05:10:00+03:30", + "Timestamp": "2021-10-28T05:10:00+03:30", "Open": 3963.98, "High": 3989.79, "Low": 3963.98, @@ -2322,7 +2322,7 @@ { "TimeOfCandle": "2021-10-28T05:15:00+03:30", "Amount": 27931.7230543210, - "Date": "2021-10-28T05:15:00+03:30", + "Timestamp": "2021-10-28T05:15:00+03:30", "Open": 3984.00, "High": 3986.71, "Low": 3973.01, @@ -2332,7 +2332,7 @@ { "TimeOfCandle": "2021-10-28T05:20:00+03:30", "Amount": 20140.0700601502, - "Date": "2021-10-28T05:20:00+03:30", + "Timestamp": "2021-10-28T05:20:00+03:30", "Open": 3972.43, "High": 3988.00, "Low": 3971.40, @@ -2342,7 +2342,7 @@ { "TimeOfCandle": "2021-10-28T05:25:00+03:30", "Amount": 27940.2730614710, - "Date": "2021-10-28T05:25:00+03:30", + "Timestamp": "2021-10-28T05:25:00+03:30", "Open": 3984.68, "High": 3994.15, "Low": 3982.10, @@ -2352,7 +2352,7 @@ { "TimeOfCandle": "2021-10-28T05:30:00+03:30", "Amount": 377296.6298401995, - "Date": "2021-10-28T05:30:00+03:30", + "Timestamp": "2021-10-28T05:30:00+03:30", "Open": 3994.48, "High": 3994.48, "Low": 3978.80, @@ -2362,7 +2362,7 @@ { "TimeOfCandle": "2021-10-28T05:35:00+03:30", "Amount": 61752.0229436478, - "Date": "2021-10-28T05:35:00+03:30", + "Timestamp": "2021-10-28T05:35:00+03:30", "Open": 3983.46, "High": 3992.54, "Low": 3980.07, @@ -2372,7 +2372,7 @@ { "TimeOfCandle": "2021-10-28T05:40:00+03:30", "Amount": 28619.8133087463, - "Date": "2021-10-28T05:40:00+03:30", + "Timestamp": "2021-10-28T05:40:00+03:30", "Open": 3994.46, "High": 3994.46, "Low": 3979.46, @@ -2382,7 +2382,7 @@ { "TimeOfCandle": "2021-10-28T05:45:00+03:30", "Amount": 3253.3199269062, - "Date": "2021-10-28T05:45:00+03:30", + "Timestamp": "2021-10-28T05:45:00+03:30", "Open": 3979.64, "High": 3982.15, "Low": 3977.41, @@ -2392,7 +2392,7 @@ { "TimeOfCandle": "2021-10-28T05:50:00+03:30", "Amount": 14832.3673962559, - "Date": "2021-10-28T05:50:00+03:30", + "Timestamp": "2021-10-28T05:50:00+03:30", "Open": 3975.96, "High": 3976.64, "Low": 3970.00, @@ -2402,7 +2402,7 @@ { "TimeOfCandle": "2021-10-28T05:55:00+03:30", "Amount": 107206.4616606490, - "Date": "2021-10-28T05:55:00+03:30", + "Timestamp": "2021-10-28T05:55:00+03:30", "Open": 3970.04, "High": 3979.03, "Low": 3970.04, @@ -2412,7 +2412,7 @@ { "TimeOfCandle": "2021-10-28T06:00:00+03:30", "Amount": 33323.9111571420, - "Date": "2021-10-28T06:00:00+03:30", + "Timestamp": "2021-10-28T06:00:00+03:30", "Open": 3974.06, "High": 3984.99, "Low": 3974.00, @@ -2422,7 +2422,7 @@ { "TimeOfCandle": "2021-10-28T06:05:00+03:30", "Amount": 86242.5545243392, - "Date": "2021-10-28T06:05:00+03:30", + "Timestamp": "2021-10-28T06:05:00+03:30", "Open": 3980.46, "High": 3986.89, "Low": 3980.46, @@ -2432,7 +2432,7 @@ { "TimeOfCandle": "2021-10-28T06:10:00+03:30", "Amount": 29914.3522593907, - "Date": "2021-10-28T06:10:00+03:30", + "Timestamp": "2021-10-28T06:10:00+03:30", "Open": 3982.66, "High": 3986.47, "Low": 3980.74, @@ -2442,7 +2442,7 @@ { "TimeOfCandle": "2021-10-28T06:15:00+03:30", "Amount": 23767.7803766722, - "Date": "2021-10-28T06:15:00+03:30", + "Timestamp": "2021-10-28T06:15:00+03:30", "Open": 3984.14, "High": 3992.99, "Low": 3983.50, @@ -2452,7 +2452,7 @@ { "TimeOfCandle": "2021-10-28T06:20:00+03:30", "Amount": 33204.2712460854, - "Date": "2021-10-28T06:20:00+03:30", + "Timestamp": "2021-10-28T06:20:00+03:30", "Open": 3989.02, "High": 3989.02, "Low": 3978.49, @@ -2462,7 +2462,7 @@ { "TimeOfCandle": "2021-10-28T06:25:00+03:30", "Amount": 67822.6708667253, - "Date": "2021-10-28T06:25:00+03:30", + "Timestamp": "2021-10-28T06:25:00+03:30", "Open": 3986.00, "High": 3997.00, "Low": 3984.73, @@ -2472,7 +2472,7 @@ { "TimeOfCandle": "2021-10-28T06:30:00+03:30", "Amount": 66611.3175330311, - "Date": "2021-10-28T06:30:00+03:30", + "Timestamp": "2021-10-28T06:30:00+03:30", "Open": 3997.99, "High": 4019.16, "Low": 3997.45, @@ -2482,7 +2482,7 @@ { "TimeOfCandle": "2021-10-28T06:35:00+03:30", "Amount": 176207.8822032963, - "Date": "2021-10-28T06:35:00+03:30", + "Timestamp": "2021-10-28T06:35:00+03:30", "Open": 4018.85, "High": 4020.00, "Low": 4003.28, @@ -2492,7 +2492,7 @@ { "TimeOfCandle": "2021-10-28T06:40:00+03:30", "Amount": 24921.7508786178, - "Date": "2021-10-28T06:40:00+03:30", + "Timestamp": "2021-10-28T06:40:00+03:30", "Open": 4003.26, "High": 4011.01, "Low": 4001.57, @@ -2502,7 +2502,7 @@ { "TimeOfCandle": "2021-10-28T06:45:00+03:30", "Amount": 115028.2245376136, - "Date": "2021-10-28T06:45:00+03:30", + "Timestamp": "2021-10-28T06:45:00+03:30", "Open": 4004.66, "High": 4004.66, "Low": 3987.98, @@ -2512,7 +2512,7 @@ { "TimeOfCandle": "2021-10-28T06:50:00+03:30", "Amount": 28514.5788980357, - "Date": "2021-10-28T06:50:00+03:30", + "Timestamp": "2021-10-28T06:50:00+03:30", "Open": 3987.44, "High": 3990.61, "Low": 3979.01, @@ -2522,7 +2522,7 @@ { "TimeOfCandle": "2021-10-28T06:55:00+03:30", "Amount": 11977.9046242508, - "Date": "2021-10-28T06:55:00+03:30", + "Timestamp": "2021-10-28T06:55:00+03:30", "Open": 3983.30, "High": 3984.21, "Low": 3976.00, @@ -2532,7 +2532,7 @@ { "TimeOfCandle": "2021-10-28T07:00:00+03:30", "Amount": 15257.1653014095, - "Date": "2021-10-28T07:00:00+03:30", + "Timestamp": "2021-10-28T07:00:00+03:30", "Open": 3983.00, "High": 3993.76, "Low": 3983.00, @@ -2542,7 +2542,7 @@ { "TimeOfCandle": "2021-10-28T07:05:00+03:30", "Amount": 8790.8925250374, - "Date": "2021-10-28T07:05:00+03:30", + "Timestamp": "2021-10-28T07:05:00+03:30", "Open": 3993.23, "High": 3996.32, "Low": 3987.25, @@ -2552,7 +2552,7 @@ { "TimeOfCandle": "2021-10-28T07:10:00+03:30", "Amount": 23316.0996030819, - "Date": "2021-10-28T07:10:00+03:30", + "Timestamp": "2021-10-28T07:10:00+03:30", "Open": 3988.60, "High": 3992.75, "Low": 3986.00, @@ -2562,7 +2562,7 @@ { "TimeOfCandle": "2021-10-28T07:15:00+03:30", "Amount": 178519.9664682986, - "Date": "2021-10-28T07:15:00+03:30", + "Timestamp": "2021-10-28T07:15:00+03:30", "Open": 3986.01, "High": 3997.99, "Low": 3985.19, @@ -2572,7 +2572,7 @@ { "TimeOfCandle": "2021-10-28T07:20:00+03:30", "Amount": 4270.3175749069, - "Date": "2021-10-28T07:20:00+03:30", + "Timestamp": "2021-10-28T07:20:00+03:30", "Open": 3996.34, "High": 3996.93, "Low": 3990.00, @@ -2582,7 +2582,7 @@ { "TimeOfCandle": "2021-10-28T07:25:00+03:30", "Amount": 29999.1433082533, - "Date": "2021-10-28T07:25:00+03:30", + "Timestamp": "2021-10-28T07:25:00+03:30", "Open": 3997.99, "High": 4001.73, "Low": 3993.00, @@ -2592,7 +2592,7 @@ { "TimeOfCandle": "2021-10-28T07:30:00+03:30", "Amount": 71830.2989172508, - "Date": "2021-10-28T07:30:00+03:30", + "Timestamp": "2021-10-28T07:30:00+03:30", "Open": 3995.20, "High": 4011.00, "Low": 3995.20, @@ -2602,7 +2602,7 @@ { "TimeOfCandle": "2021-10-28T07:35:00+03:30", "Amount": 44696.4380463335, - "Date": "2021-10-28T07:35:00+03:30", + "Timestamp": "2021-10-28T07:35:00+03:30", "Open": 4002.64, "High": 4002.71, "Low": 3998.57, @@ -2612,7 +2612,7 @@ { "TimeOfCandle": "2021-10-28T07:40:00+03:30", "Amount": 90436.5260374202, - "Date": "2021-10-28T07:40:00+03:30", + "Timestamp": "2021-10-28T07:40:00+03:30", "Open": 3999.99, "High": 4019.00, "Low": 3999.99, @@ -2622,7 +2622,7 @@ { "TimeOfCandle": "2021-10-28T07:45:00+03:30", "Amount": 40713.0396065300, - "Date": "2021-10-28T07:45:00+03:30", + "Timestamp": "2021-10-28T07:45:00+03:30", "Open": 4014.97, "High": 4016.44, "Low": 4001.64, @@ -2632,7 +2632,7 @@ { "TimeOfCandle": "2021-10-28T07:50:00+03:30", "Amount": 36715.0971062257, - "Date": "2021-10-28T07:50:00+03:30", + "Timestamp": "2021-10-28T07:50:00+03:30", "Open": 4005.00, "High": 4005.00, "Low": 3996.76, @@ -2642,7 +2642,7 @@ { "TimeOfCandle": "2021-10-28T07:55:00+03:30", "Amount": 23058.1884856409, - "Date": "2021-10-28T07:55:00+03:30", + "Timestamp": "2021-10-28T07:55:00+03:30", "Open": 4000.10, "High": 4003.99, "Low": 3997.01, @@ -2652,7 +2652,7 @@ { "TimeOfCandle": "2021-10-28T08:00:00+03:30", "Amount": 22167.5891585791, - "Date": "2021-10-28T08:00:00+03:30", + "Timestamp": "2021-10-28T08:00:00+03:30", "Open": 3997.28, "High": 3999.95, "Low": 3991.41, @@ -2662,7 +2662,7 @@ { "TimeOfCandle": "2021-10-28T08:05:00+03:30", "Amount": 40659.2361277657, - "Date": "2021-10-28T08:05:00+03:30", + "Timestamp": "2021-10-28T08:05:00+03:30", "Open": 3991.94, "High": 3996.32, "Low": 3988.69, @@ -2672,7 +2672,7 @@ { "TimeOfCandle": "2021-10-28T08:10:00+03:30", "Amount": 62058.8328728053, - "Date": "2021-10-28T08:10:00+03:30", + "Timestamp": "2021-10-28T08:10:00+03:30", "Open": 3993.77, "High": 4008.00, "Low": 3993.36, @@ -2682,7 +2682,7 @@ { "TimeOfCandle": "2021-10-28T08:15:00+03:30", "Amount": 11128.7966533162, - "Date": "2021-10-28T08:15:00+03:30", + "Timestamp": "2021-10-28T08:15:00+03:30", "Open": 4006.00, "High": 4007.00, "Low": 3991.01, @@ -2692,7 +2692,7 @@ { "TimeOfCandle": "2021-10-28T08:20:00+03:30", "Amount": 5858.2039898135, - "Date": "2021-10-28T08:20:00+03:30", + "Timestamp": "2021-10-28T08:20:00+03:30", "Open": 3994.68, "High": 3997.00, "Low": 3986.52, @@ -2702,7 +2702,7 @@ { "TimeOfCandle": "2021-10-28T08:25:00+03:30", "Amount": 77289.1383465509, - "Date": "2021-10-28T08:25:00+03:30", + "Timestamp": "2021-10-28T08:25:00+03:30", "Open": 3986.47, "High": 4000.00, "Low": 3986.06, @@ -2712,7 +2712,7 @@ { "TimeOfCandle": "2021-10-28T08:30:00+03:30", "Amount": 42635.1897146719, - "Date": "2021-10-28T08:30:00+03:30", + "Timestamp": "2021-10-28T08:30:00+03:30", "Open": 3994.61, "High": 4006.00, "Low": 3994.61, @@ -2722,7 +2722,7 @@ { "TimeOfCandle": "2021-10-28T08:35:00+03:30", "Amount": 14503.8375733987, - "Date": "2021-10-28T08:35:00+03:30", + "Timestamp": "2021-10-28T08:35:00+03:30", "Open": 4005.99, "High": 4014.36, "Low": 4005.03, @@ -2732,7 +2732,7 @@ { "TimeOfCandle": "2021-10-28T08:40:00+03:30", "Amount": 33348.6125536275, - "Date": "2021-10-28T08:40:00+03:30", + "Timestamp": "2021-10-28T08:40:00+03:30", "Open": 4010.44, "High": 4013.00, "Low": 4006.97, @@ -2742,7 +2742,7 @@ { "TimeOfCandle": "2021-10-28T08:45:00+03:30", "Amount": 33285.4127162129, - "Date": "2021-10-28T08:45:00+03:30", + "Timestamp": "2021-10-28T08:45:00+03:30", "Open": 4010.00, "High": 4017.50, "Low": 4003.36, @@ -2752,7 +2752,7 @@ { "TimeOfCandle": "2021-10-28T08:50:00+03:30", "Amount": 29632.1558832761, - "Date": "2021-10-28T08:50:00+03:30", + "Timestamp": "2021-10-28T08:50:00+03:30", "Open": 4005.03, "High": 4006.95, "Low": 3997.51, @@ -2762,7 +2762,7 @@ { "TimeOfCandle": "2021-10-28T08:55:00+03:30", "Amount": 20273.8551947189, - "Date": "2021-10-28T08:55:00+03:30", + "Timestamp": "2021-10-28T08:55:00+03:30", "Open": 4000.28, "High": 4007.00, "Low": 3998.00, @@ -2772,7 +2772,7 @@ { "TimeOfCandle": "2021-10-28T09:00:00+03:30", "Amount": 50376.0214964975, - "Date": "2021-10-28T09:00:00+03:30", + "Timestamp": "2021-10-28T09:00:00+03:30", "Open": 4009.10, "High": 4009.10, "Low": 3991.00, @@ -2782,7 +2782,7 @@ { "TimeOfCandle": "2021-10-28T09:05:00+03:30", "Amount": 30198.8728909118, - "Date": "2021-10-28T09:05:00+03:30", + "Timestamp": "2021-10-28T09:05:00+03:30", "Open": 4003.01, "High": 4013.90, "Low": 3998.00, @@ -2792,7 +2792,7 @@ { "TimeOfCandle": "2021-10-28T09:10:00+03:30", "Amount": 43756.7826465745, - "Date": "2021-10-28T09:10:00+03:30", + "Timestamp": "2021-10-28T09:10:00+03:30", "Open": 4014.01, "High": 4017.00, "Low": 4011.22, @@ -2802,7 +2802,7 @@ { "TimeOfCandle": "2021-10-28T09:15:00+03:30", "Amount": 23511.2437216130, - "Date": "2021-10-28T09:15:00+03:30", + "Timestamp": "2021-10-28T09:15:00+03:30", "Open": 4014.23, "High": 4017.39, "Low": 4007.53, @@ -2812,7 +2812,7 @@ { "TimeOfCandle": "2021-10-28T09:20:00+03:30", "Amount": 14040.4748413780, - "Date": "2021-10-28T09:20:00+03:30", + "Timestamp": "2021-10-28T09:20:00+03:30", "Open": 4007.29, "High": 4013.00, "Low": 4007.29, @@ -2822,7 +2822,7 @@ { "TimeOfCandle": "2021-10-28T09:25:00+03:30", "Amount": 32784.2049970370, - "Date": "2021-10-28T09:25:00+03:30", + "Timestamp": "2021-10-28T09:25:00+03:30", "Open": 4010.10, "High": 4021.00, "Low": 4007.78, @@ -2832,7 +2832,7 @@ { "TimeOfCandle": "2021-10-28T09:30:00+03:30", "Amount": 135151.0443850006, - "Date": "2021-10-28T09:30:00+03:30", + "Timestamp": "2021-10-28T09:30:00+03:30", "Open": 4021.25, "High": 4030.00, "Low": 3998.68, @@ -2842,7 +2842,7 @@ { "TimeOfCandle": "2021-10-28T09:35:00+03:30", "Amount": 353945.0049915913, - "Date": "2021-10-28T09:35:00+03:30", + "Timestamp": "2021-10-28T09:35:00+03:30", "Open": 4002.00, "High": 4007.05, "Low": 3993.00, @@ -2852,7 +2852,7 @@ { "TimeOfCandle": "2021-10-28T09:40:00+03:30", "Amount": 143843.8618766398, - "Date": "2021-10-28T09:40:00+03:30", + "Timestamp": "2021-10-28T09:40:00+03:30", "Open": 3995.00, "High": 4004.00, "Low": 3982.80, @@ -2862,7 +2862,7 @@ { "TimeOfCandle": "2021-10-28T09:45:00+03:30", "Amount": 178253.2209346618, - "Date": "2021-10-28T09:45:00+03:30", + "Timestamp": "2021-10-28T09:45:00+03:30", "Open": 4004.55, "High": 4007.00, "Low": 3987.46, @@ -2872,7 +2872,7 @@ { "TimeOfCandle": "2021-10-28T09:50:00+03:30", "Amount": 242622.9380993191, - "Date": "2021-10-28T09:50:00+03:30", + "Timestamp": "2021-10-28T09:50:00+03:30", "Open": 4003.04, "High": 4017.53, "Low": 3996.69, @@ -2882,7 +2882,7 @@ { "TimeOfCandle": "2021-10-28T09:55:00+03:30", "Amount": 78878.8866204291, - "Date": "2021-10-28T09:55:00+03:30", + "Timestamp": "2021-10-28T09:55:00+03:30", "Open": 4016.42, "High": 4027.00, "Low": 4011.62, @@ -2892,7 +2892,7 @@ { "TimeOfCandle": "2021-10-28T10:00:00+03:30", "Amount": 371175.5092334881, - "Date": "2021-10-28T10:00:00+03:30", + "Timestamp": "2021-10-28T10:00:00+03:30", "Open": 4020.76, "High": 4021.41, "Low": 4011.14, @@ -2902,7 +2902,7 @@ { "TimeOfCandle": "2021-10-28T10:05:00+03:30", "Amount": 88168.4615095025, - "Date": "2021-10-28T10:05:00+03:30", + "Timestamp": "2021-10-28T10:05:00+03:30", "Open": 4022.99, "High": 4024.02, "Low": 4013.36, @@ -2912,7 +2912,7 @@ { "TimeOfCandle": "2021-10-28T10:10:00+03:30", "Amount": 94346.7704570256, - "Date": "2021-10-28T10:10:00+03:30", + "Timestamp": "2021-10-28T10:10:00+03:30", "Open": 4025.00, "High": 4028.70, "Low": 4015.01, @@ -2922,7 +2922,7 @@ { "TimeOfCandle": "2021-10-28T10:15:00+03:30", "Amount": 42233.7585981885, - "Date": "2021-10-28T10:15:00+03:30", + "Timestamp": "2021-10-28T10:15:00+03:30", "Open": 4018.07, "High": 4028.98, "Low": 4016.96, @@ -2932,7 +2932,7 @@ { "TimeOfCandle": "2021-10-28T10:20:00+03:30", "Amount": 74681.1523884324, - "Date": "2021-10-28T10:20:00+03:30", + "Timestamp": "2021-10-28T10:20:00+03:30", "Open": 4028.43, "High": 4036.30, "Low": 4023.40, @@ -2942,7 +2942,7 @@ { "TimeOfCandle": "2021-10-28T10:25:00+03:30", "Amount": 110575.7778364137, - "Date": "2021-10-28T10:25:00+03:30", + "Timestamp": "2021-10-28T10:25:00+03:30", "Open": 4029.00, "High": 4029.31, "Low": 4018.78, @@ -2952,7 +2952,7 @@ { "TimeOfCandle": "2021-10-28T10:30:00+03:30", "Amount": 193815.0065813983, - "Date": "2021-10-28T10:30:00+03:30", + "Timestamp": "2021-10-28T10:30:00+03:30", "Open": 4023.52, "High": 4041.00, "Low": 4023.52, @@ -2962,7 +2962,7 @@ { "TimeOfCandle": "2021-10-28T10:35:00+03:30", "Amount": 35844.0412768128, - "Date": "2021-10-28T10:35:00+03:30", + "Timestamp": "2021-10-28T10:35:00+03:30", "Open": 4024.98, "High": 4029.11, "Low": 4022.38, @@ -2972,7 +2972,7 @@ { "TimeOfCandle": "2021-10-28T10:40:00+03:30", "Amount": 165107.3654087503, - "Date": "2021-10-28T10:40:00+03:30", + "Timestamp": "2021-10-28T10:40:00+03:30", "Open": 4025.26, "High": 4025.26, "Low": 4015.00, @@ -2982,7 +2982,7 @@ { "TimeOfCandle": "2021-10-28T10:45:00+03:30", "Amount": 37197.2751947274, - "Date": "2021-10-28T10:45:00+03:30", + "Timestamp": "2021-10-28T10:45:00+03:30", "Open": 4016.73, "High": 4019.68, "Low": 4010.00, @@ -2992,7 +2992,7 @@ { "TimeOfCandle": "2021-10-28T10:50:00+03:30", "Amount": 11167.1724041085, - "Date": "2021-10-28T10:50:00+03:30", + "Timestamp": "2021-10-28T10:50:00+03:30", "Open": 4013.09, "High": 4016.05, "Low": 4010.00, diff --git a/tests/indicators/s-z/ZigZag/data.issue632.json b/tests/indicators/s-z/ZigZag/data.issue632.json index 39f8994d3..18eba9c48 100644 --- a/tests/indicators/s-z/ZigZag/data.issue632.json +++ b/tests/indicators/s-z/ZigZag/data.issue632.json @@ -1,6 +1,6 @@ [ { - "Date": "2021-10-20T00:00:00", + "Timestamp": "2021-10-20T00:00:00", "Open": 23.59800, "High": 24.42100, "Low": 23.57500, @@ -8,7 +8,7 @@ "Volume": 91.0 }, { - "Date": "2021-10-21T00:00:00", + "Timestamp": "2021-10-21T00:00:00", "Open": 24.29400, "High": 24.48700, "Low": 24.00800, @@ -16,7 +16,7 @@ "Volume": 89.0 }, { - "Date": "2021-10-22T00:00:00", + "Timestamp": "2021-10-22T00:00:00", "Open": 24.15200, "High": 24.82600, "Low": 24.12300, @@ -24,7 +24,7 @@ "Volume": 84.0 }, { - "Date": "2021-10-25T00:00:00", + "Timestamp": "2021-10-25T00:00:00", "Open": 24.39800, "High": 24.61800, "Low": 24.30600, @@ -32,7 +32,7 @@ "Volume": 92.0 }, { - "Date": "2021-10-26T00:00:00", + "Timestamp": "2021-10-26T00:00:00", "Open": 24.55600, "High": 24.55800, "Low": 23.87900, @@ -40,7 +40,7 @@ "Volume": 92.0 }, { - "Date": "2021-10-27T00:00:00", + "Timestamp": "2021-10-27T00:00:00", "Open": 24.14500, "High": 24.25400, "Low": 23.83700, @@ -48,7 +48,7 @@ "Volume": 92.0 }, { - "Date": "2021-10-28T00:00:00", + "Timestamp": "2021-10-28T00:00:00", "Open": 24.03800, "High": 24.24600, "Low": 23.96300, @@ -56,7 +56,7 @@ "Volume": 92.0 }, { - "Date": "2021-10-29T00:00:00", + "Timestamp": "2021-10-29T00:00:00", "Open": 24.06600, "High": 24.08100, "Low": 23.66100, @@ -64,7 +64,7 @@ "Volume": 84.0 }, { - "Date": "2021-11-01T00:00:00", + "Timestamp": "2021-11-01T00:00:00", "Open": 23.77900, "High": 24.10200, "Low": 23.74900, @@ -72,7 +72,7 @@ "Volume": 92.0 }, { - "Date": "2021-11-02T00:00:00", + "Timestamp": "2021-11-02T00:00:00", "Open": 24.02300, "High": 24.06700, "Low": 23.39200, @@ -80,7 +80,7 @@ "Volume": 92.0 }, { - "Date": "2021-11-03T00:00:00", + "Timestamp": "2021-11-03T00:00:00", "Open": 23.52700, "High": 23.72400, "Low": 23.02000, @@ -88,7 +88,7 @@ "Volume": 92.0 }, { - "Date": "2021-11-04T00:00:00", + "Timestamp": "2021-11-04T00:00:00", "Open": 23.72100, "High": 24.04100, "Low": 23.44300, @@ -96,7 +96,7 @@ "Volume": 92.0 }, { - "Date": "2021-11-05T00:00:00", + "Timestamp": "2021-11-05T00:00:00", "Open": 23.77300, "High": 24.17800, "Low": 23.62700, @@ -104,7 +104,7 @@ "Volume": 84.0 }, { - "Date": "2021-11-08T00:00:00", + "Timestamp": "2021-11-08T00:00:00", "Open": 24.13100, "High": 24.51300, "Low": 24.05800, @@ -112,7 +112,7 @@ "Volume": 92.0 }, { - "Date": "2021-11-09T00:00:00", + "Timestamp": "2021-11-09T00:00:00", "Open": 24.44700, "High": 24.47700, "Low": 24.02900, @@ -120,7 +120,7 @@ "Volume": 92.0 }, { - "Date": "2021-11-10T00:00:00", + "Timestamp": "2021-11-10T00:00:00", "Open": 24.30800, "High": 25.13000, "Low": 24.06500, @@ -128,7 +128,7 @@ "Volume": 92.0 }, { - "Date": "2021-11-11T00:00:00", + "Timestamp": "2021-11-11T00:00:00", "Open": 24.64500, "High": 25.01200, "Low": 24.59000, diff --git a/tests/indicators/s-z/ZigZag/data.schrodinger.json b/tests/indicators/s-z/ZigZag/data.schrodinger.json index 3bbad1348..655aee761 100644 --- a/tests/indicators/s-z/ZigZag/data.schrodinger.json +++ b/tests/indicators/s-z/ZigZag/data.schrodinger.json @@ -1,6 +1,6 @@ [ { - "Date": "2019-10-29 15:00", + "Timestamp": "2019-10-29 15:00", "Open": 8.424630165, "High": 10.11337376, "Low": 8.424630165, @@ -8,7 +8,7 @@ "Volume": 4059400321 }, { - "Date": "2019-10-30 15:00", + "Timestamp": "2019-10-30 15:00", "Open": 8.396008492, "High": 8.396008492, "Low": 8.033452988, @@ -16,7 +16,7 @@ "Volume": 2320616294 }, { - "Date": "2019-10-31 15:00", + "Timestamp": "2019-10-31 15:00", "Open": 7.489620686, "High": 8.081157684, "Low": 7.308342934, @@ -24,7 +24,7 @@ "Volume": 2286077392 }, { - "Date": "2019-11-01 15:00", + "Timestamp": "2019-11-01 15:00", "Open": 7.508702278, "High": 7.699520588, "Low": 7.441916466, @@ -32,7 +32,7 @@ "Volume": 1336825882 }, { - "Date": "2019-11-04 15:00", + "Timestamp": "2019-11-04 15:00", "Open": 7.451457024, "High": 7.556407452, "Low": 7.308342934, @@ -40,7 +40,7 @@ "Volume": 1266693767 }, { - "Date": "2019-11-05 15:00", + "Timestamp": "2019-11-05 15:00", "Open": 7.336966038, "High": 7.422834396, "Low": 7.165229797, @@ -48,7 +48,7 @@ "Volume": 1184345773 }, { - "Date": "2019-11-06 15:00", + "Timestamp": "2019-11-06 15:00", "Open": 7.279720783, "High": 7.279720783, "Low": 7.107983589, @@ -56,7 +56,7 @@ "Volume": 934589619 }, { - "Date": "2019-11-07 15:00", + "Timestamp": "2019-11-07 15:00", "Open": 7.117525101, "High": 7.251097679, "Low": 7.117525101, @@ -64,7 +64,7 @@ "Volume": 634838541 }, { - "Date": "2019-11-08 15:00", + "Timestamp": "2019-11-08 15:00", "Open": 7.212934017, "High": 7.212934017, "Low": 7.088901997, @@ -72,7 +72,7 @@ "Volume": 573972846 }, { - "Date": "2019-11-11 15:00", + "Timestamp": "2019-11-11 15:00", "Open": 7.060279369, "High": 7.060279369, "Low": 6.85037899, @@ -80,7 +80,7 @@ "Volume": 700986893 }, { - "Date": "2019-11-12 15:00", + "Timestamp": "2019-11-12 15:00", "Open": 6.85037899, "High": 6.888542652, "Low": 6.697724342, @@ -88,7 +88,7 @@ "Volume": 557485894 }, { - "Date": "2019-11-13 15:00", + "Timestamp": "2019-11-13 15:00", "Open": 6.745429039, "High": 6.754970074, "Low": 6.611855984, @@ -96,7 +96,7 @@ "Volume": 556249904 }, { - "Date": "2019-11-14 15:00", + "Timestamp": "2019-11-14 15:00", "Open": 6.583233356, "High": 6.936247349, "Low": 6.516447067, @@ -104,7 +104,7 @@ "Volume": 933603542 }, { - "Date": "2019-11-15 15:00", + "Timestamp": "2019-11-15 15:00", "Open": 6.726347446, "High": 6.812215328, "Low": 6.611855984, @@ -112,7 +112,7 @@ "Volume": 529075401 }, { - "Date": "2019-11-18 15:00", + "Timestamp": "2019-11-18 15:00", "Open": 6.602315426, "High": 6.697724342, "Low": 6.564151764, @@ -120,7 +120,7 @@ "Volume": 363056995 }, { - "Date": "2019-11-19 15:00", + "Timestamp": "2019-11-19 15:00", "Open": 6.621397018, "High": 6.735888004, "Low": 6.602315426, @@ -128,7 +128,7 @@ "Volume": 425813619 }, { - "Date": "2019-11-20 15:00", + "Timestamp": "2019-11-20 15:00", "Open": 6.697724342, "High": 6.716806412, "Low": 6.611855984, @@ -136,7 +136,7 @@ "Volume": 371981297 }, { - "Date": "2019-11-21 15:00", + "Timestamp": "2019-11-21 15:00", "Open": 6.602315426, "High": 6.640479088, "Low": 6.583233356, @@ -144,7 +144,7 @@ "Volume": 265244733 }, { - "Date": "2019-11-22 15:00", + "Timestamp": "2019-11-22 15:00", "Open": 6.611855984, "High": 6.650019646, "Low": 6.554610729, @@ -152,7 +152,7 @@ "Volume": 305350578 }, { - "Date": "2019-11-25 15:00", + "Timestamp": "2019-11-25 15:00", "Open": 6.583233356, "High": 6.697724342, "Low": 6.564151764, @@ -160,7 +160,7 @@ "Volume": 406964233 }, { - "Date": "2019-11-26 15:00", + "Timestamp": "2019-11-26 15:00", "Open": 6.621397018, "High": 6.697724342, "Low": 6.611855984, @@ -168,7 +168,7 @@ "Volume": 407453677 }, { - "Date": "2019-11-27 15:00", + "Timestamp": "2019-11-27 15:00", "Open": 6.592774391, "High": 6.592774391, "Low": 6.449660778, @@ -176,7 +176,7 @@ "Volume": 535419253 }, { - "Date": "2019-11-28 15:00", + "Timestamp": "2019-11-28 15:00", "Open": 6.449660778, "High": 6.478283405, "Low": 6.363792419, @@ -184,7 +184,7 @@ "Volume": 364736312 }, { - "Date": "2019-11-29 15:00", + "Timestamp": "2019-11-29 15:00", "Open": 6.363792419, "High": 6.411496639, "Low": 6.325628757, @@ -192,7 +192,7 @@ "Volume": 255866984 }, { - "Date": "2019-12-02 15:00", + "Timestamp": "2019-12-02 15:00", "Open": 6.344710827, "High": 6.363792419, "Low": 6.297005653, @@ -200,7 +200,7 @@ "Volume": 258548232 }, { - "Date": "2019-12-03 15:00", + "Timestamp": "2019-12-03 15:00", "Open": 6.287465096, "High": 6.287465096, "Low": 6.230219841, @@ -208,7 +208,7 @@ "Volume": 230697680 }, { - "Date": "2019-12-04 15:00", + "Timestamp": "2019-12-04 15:00", "Open": 6.249301434, "High": 6.316087723, "Low": 6.230219841, @@ -216,7 +216,7 @@ "Volume": 181464531 }, { - "Date": "2019-12-05 15:00", + "Timestamp": "2019-12-05 15:00", "Open": 6.277924061, "High": 6.363792419, "Low": 6.258841991, @@ -224,7 +224,7 @@ "Volume": 296670173 }, { - "Date": "2019-12-06 15:00", + "Timestamp": "2019-12-06 15:00", "Open": 6.316087723, "High": 6.325628757, "Low": 6.277924061, @@ -232,7 +232,7 @@ "Volume": 217770930 }, { - "Date": "2019-12-09 15:00", + "Timestamp": "2019-12-09 15:00", "Open": 6.316087723, "High": 6.344710827, "Low": 6.297005653, @@ -240,7 +240,7 @@ "Volume": 241221190 }, { - "Date": "2019-12-10 15:00", + "Timestamp": "2019-12-10 15:00", "Open": 6.306547165, "High": 6.316087723, "Low": 6.258841991, @@ -248,7 +248,7 @@ "Volume": 305170005 }, { - "Date": "2019-12-11 15:00", + "Timestamp": "2019-12-11 15:00", "Open": 6.258841991, "High": 6.306547165, "Low": 6.249301434, @@ -256,7 +256,7 @@ "Volume": 237129090 }, { - "Date": "2019-12-12 15:00", + "Timestamp": "2019-12-12 15:00", "Open": 6.268383503, "High": 6.268383503, "Low": 6.182515144, @@ -264,7 +264,7 @@ "Volume": 328011417 }, { - "Date": "2019-12-13 15:00", + "Timestamp": "2019-12-13 15:00", "Open": 6.211137772, "High": 6.277924061, "Low": 6.192055702, @@ -272,7 +272,7 @@ "Volume": 414155025 }, { - "Date": "2019-12-16 15:00", + "Timestamp": "2019-12-16 15:00", "Open": 6.258841991, "High": 6.268383503, "Low": 6.220678806, @@ -280,7 +280,7 @@ "Volume": 319083760 }, { - "Date": "2019-12-17 15:00", + "Timestamp": "2019-12-17 15:00", "Open": 6.277924061, "High": 6.745429039, "Low": 6.258841991, @@ -288,7 +288,7 @@ "Volume": 1437600223 }, { - "Date": "2019-12-18 15:00", + "Timestamp": "2019-12-18 15:00", "Open": 6.53552866, "High": 6.611855984, "Low": 6.468742847, @@ -296,7 +296,7 @@ "Volume": 791161760 }, { - "Date": "2019-12-19 15:00", + "Timestamp": "2019-12-19 15:00", "Open": 6.506906509, "High": 6.65956068, "Low": 6.497364998, @@ -304,7 +304,7 @@ "Volume": 579540342 }, { - "Date": "2019-12-20 15:00", + "Timestamp": "2019-12-20 15:00", "Open": 6.53552866, "High": 6.707265854, "Low": 6.516447067, @@ -312,7 +312,7 @@ "Volume": 743212796 }, { - "Date": "2019-12-23 15:00", + "Timestamp": "2019-12-23 15:00", "Open": 6.573692322, "High": 6.602315426, "Low": 6.401956081, @@ -320,7 +320,7 @@ "Volume": 495364395 }, { - "Date": "2019-12-24 15:00", + "Timestamp": "2019-12-24 15:00", "Open": 6.449660778, "High": 6.468742847, "Low": 6.373332977, @@ -328,7 +328,7 @@ "Volume": 270327101 }, { - "Date": "2019-12-25 15:00", + "Timestamp": "2019-12-25 15:00", "Open": 6.421037674, "High": 6.430578709, "Low": 6.373332977, @@ -336,7 +336,7 @@ "Volume": 257601111 }, { - "Date": "2019-12-26 15:00", + "Timestamp": "2019-12-26 15:00", "Open": 6.392415047, "High": 6.421037674, "Low": 6.363792419, @@ -344,7 +344,7 @@ "Volume": 255462864 }, { - "Date": "2019-12-27 15:00", + "Timestamp": "2019-12-27 15:00", "Open": 6.411496639, "High": 6.525988102, "Low": 6.392415047, @@ -352,7 +352,7 @@ "Volume": 415148112 }, { - "Date": "2019-12-30 15:00", + "Timestamp": "2019-12-30 15:00", "Open": 6.354251385, "High": 6.401956081, "Low": 6.258841991, @@ -360,7 +360,7 @@ "Volume": 389765020 }, { - "Date": "2019-12-31 15:00", + "Timestamp": "2019-12-31 15:00", "Open": 6.382874489, "High": 6.440119743, "Low": 6.354251385, @@ -368,7 +368,7 @@ "Volume": 228308972 }, { - "Date": "2020-01-02 15:00", + "Timestamp": "2020-01-02 15:00", "Open": 6.497364998, "High": 6.545070171, "Low": 6.449660778, @@ -376,7 +376,7 @@ "Volume": 519608791 }, { - "Date": "2020-01-03 15:00", + "Timestamp": "2020-01-03 15:00", "Open": 6.478283405, "High": 6.478283405, "Low": 6.430578709, @@ -384,7 +384,7 @@ "Volume": 297443419 }, { - "Date": "2020-01-06 15:00", + "Timestamp": "2020-01-06 15:00", "Open": 6.440119743, "High": 6.48782444, "Low": 6.401956081, @@ -392,7 +392,7 @@ "Volume": 326749047 }, { - "Date": "2020-01-07 15:00", + "Timestamp": "2020-01-07 15:00", "Open": 6.449660778, "High": 6.48782444, "Low": 6.430578709, @@ -400,7 +400,7 @@ "Volume": 341134545 }, { - "Date": "2020-01-08 15:00", + "Timestamp": "2020-01-08 15:00", "Open": 6.468742847, "High": 6.468742847, "Low": 6.373332977, @@ -408,7 +408,7 @@ "Volume": 364122847 }, { - "Date": "2020-01-09 15:00", + "Timestamp": "2020-01-09 15:00", "Open": 6.411496639, "High": 6.440119743, "Low": 6.401956081, @@ -416,7 +416,7 @@ "Volume": 215216477 }, { - "Date": "2020-01-10 15:00", + "Timestamp": "2020-01-10 15:00", "Open": 6.430578709, "High": 6.440119743, "Low": 6.382874489, @@ -424,7 +424,7 @@ "Volume": 168831418 }, { - "Date": "2020-01-13 15:00", + "Timestamp": "2020-01-13 15:00", "Open": 6.392415047, "High": 6.401956081, "Low": 6.354251385, @@ -432,7 +432,7 @@ "Volume": 179832623 }, { - "Date": "2020-01-14 15:00", + "Timestamp": "2020-01-14 15:00", "Open": 6.401956081, "High": 6.411496639, "Low": 6.382874489, @@ -440,7 +440,7 @@ "Volume": 160677776 }, { - "Date": "2020-01-15 15:00", + "Timestamp": "2020-01-15 15:00", "Open": 6.382874489, "High": 6.382874489, "Low": 6.335169315, @@ -448,7 +448,7 @@ "Volume": 172642834 }, { - "Date": "2020-01-16 15:00", + "Timestamp": "2020-01-16 15:00", "Open": 6.354251385, "High": 6.354251385, "Low": 6.297005653, @@ -456,7 +456,7 @@ "Volume": 160226870 }, { - "Date": "2020-01-17 15:00", + "Timestamp": "2020-01-17 15:00", "Open": 6.297005653, "High": 6.325628757, "Low": 6.297005653, @@ -464,7 +464,7 @@ "Volume": 118202341 }, { - "Date": "2020-01-20 15:00", + "Timestamp": "2020-01-20 15:00", "Open": 6.297005653, "High": 6.316087723, "Low": 6.277924061, @@ -472,7 +472,7 @@ "Volume": 137543797 }, { - "Date": "2020-01-21 15:00", + "Timestamp": "2020-01-21 15:00", "Open": 6.325628757, "High": 6.325628757, "Low": 6.249301434, @@ -480,7 +480,7 @@ "Volume": 176235026 }, { - "Date": "2020-01-22 15:00", + "Timestamp": "2020-01-22 15:00", "Open": 6.249301434, "High": 6.258841991, "Low": 6.211137772, @@ -488,7 +488,7 @@ "Volume": 168290099 }, { - "Date": "2020-01-23 15:00", + "Timestamp": "2020-01-23 15:00", "Open": 6.220678806, "High": 6.230219841, "Low": 6.039401054, @@ -496,7 +496,7 @@ "Volume": 285122377 }, { - "Date": "2020-02-03 15:00", + "Timestamp": "2020-02-03 15:00", "Open": 5.486027718, "High": 5.52419138, "Low": 5.486027718, @@ -504,7 +504,7 @@ "Volume": 268612320 }, { - "Date": "2020-02-04 15:00", + "Timestamp": "2020-02-04 15:00", "Open": 5.333373547, "High": 5.543273449, "Low": 5.333373547, @@ -512,7 +512,7 @@ "Volume": 274029620 }, { - "Date": "2020-02-05 15:00", + "Timestamp": "2020-02-05 15:00", "Open": 5.438323021, "High": 5.552814484, "Low": 5.409700871, @@ -520,7 +520,7 @@ "Volume": 287029249 }, { - "Date": "2020-02-06 15:00", + "Timestamp": "2020-02-06 15:00", "Open": 5.486027718, "High": 5.571896553, "Low": 5.447864056, @@ -528,7 +528,7 @@ "Volume": 292072223 }, { - "Date": "2020-02-07 15:00", + "Timestamp": "2020-02-07 15:00", "Open": 5.505109787, "High": 5.505109787, "Low": 5.447864056, @@ -536,7 +536,7 @@ "Volume": 218100591 }, { - "Date": "2020-02-10 15:00", + "Timestamp": "2020-02-10 15:00", "Open": 5.466946125, "High": 5.52419138, "Low": 5.438323021, @@ -544,7 +544,7 @@ "Volume": 196809450 }, { - "Date": "2020-02-11 15:00", + "Timestamp": "2020-02-11 15:00", "Open": 5.52419138, "High": 5.6482234, "Low": 5.505109787, @@ -552,7 +552,7 @@ "Volume": 304820437 }, { - "Date": "2020-02-12 15:00", + "Timestamp": "2020-02-12 15:00", "Open": 5.552814484, "High": 5.600518703, "Low": 5.533732891, @@ -560,7 +560,7 @@ "Volume": 196059746 }, { - "Date": "2020-02-13 15:00", + "Timestamp": "2020-02-13 15:00", "Open": 5.581437111, "High": 5.600518703, "Low": 5.505109787, @@ -568,7 +568,7 @@ "Volume": 208902228 }, { - "Date": "2020-02-14 15:00", + "Timestamp": "2020-02-14 15:00", "Open": 5.486027718, "High": 5.552814484, "Low": 5.486027718, @@ -576,7 +576,7 @@ "Volume": 153275933 }, { - "Date": "2020-02-17 15:00", + "Timestamp": "2020-02-17 15:00", "Open": 5.533732891, "High": 5.705469131, "Low": 5.52419138, @@ -584,7 +584,7 @@ "Volume": 401712899 }, { - "Date": "2020-02-18 15:00", + "Timestamp": "2020-02-18 15:00", "Open": 5.657764435, "High": 5.686387062, "Low": 5.629141808, @@ -592,7 +592,7 @@ "Volume": 262387253 }, { - "Date": "2020-02-19 15:00", + "Timestamp": "2020-02-19 15:00", "Open": 5.6482234, "High": 5.695928097, "Low": 5.629141808, @@ -600,7 +600,7 @@ "Volume": 222794958 }, { - "Date": "2020-02-20 15:00", + "Timestamp": "2020-02-20 15:00", "Open": 5.66730547, "High": 5.819960117, "Low": 5.638682365, @@ -608,7 +608,7 @@ "Volume": 459717561 }, { - "Date": "2020-02-21 15:00", + "Timestamp": "2020-02-21 15:00", "Open": 5.753173828, "High": 5.819960117, "Low": 5.743632793, @@ -616,7 +616,7 @@ "Volume": 317394574 }, { - "Date": "2020-02-24 15:00", + "Timestamp": "2020-02-24 15:00", "Open": 5.753173828, "High": 5.762714386, "Low": 5.695928097, @@ -624,7 +624,7 @@ "Volume": 250149645 }, { - "Date": "2020-02-25 15:00", + "Timestamp": "2020-02-25 15:00", "Open": 5.629141808, "High": 5.629141808, "Low": 5.505109787, @@ -632,7 +632,7 @@ "Volume": 332851339 }, { - "Date": "2020-02-26 15:00", + "Timestamp": "2020-02-26 15:00", "Open": 5.533732891, "High": 5.734092236, "Low": 5.495569229, @@ -640,7 +640,7 @@ "Volume": 354230119 }, { - "Date": "2020-02-27 15:00", + "Timestamp": "2020-02-27 15:00", "Open": 5.657764435, "High": 5.715009689, "Low": 5.619600773, @@ -648,7 +648,7 @@ "Volume": 260972510 }, { - "Date": "2020-02-28 15:00", + "Timestamp": "2020-02-28 15:00", "Open": 5.600518703, "High": 5.629141808, "Low": 5.476486683, @@ -656,7 +656,7 @@ "Volume": 327142262 }, { - "Date": "2020-03-02 15:00", + "Timestamp": "2020-03-02 15:00", "Open": 5.495569229, "High": 5.600518703, "Low": 5.486027718, @@ -664,7 +664,7 @@ "Volume": 242493530 }, { - "Date": "2020-03-03 15:00", + "Timestamp": "2020-03-03 15:00", "Open": 5.619600773, "High": 5.6482234, "Low": 5.581437111, @@ -672,7 +672,7 @@ "Volume": 237301914 }, { - "Date": "2020-03-04 15:00", + "Timestamp": "2020-03-04 15:00", "Open": 5.571896553, "High": 5.610059738, "Low": 5.552814484, @@ -680,7 +680,7 @@ "Volume": 170073889 }, { - "Date": "2020-03-05 15:00", + "Timestamp": "2020-03-05 15:00", "Open": 5.638682365, "High": 5.877205372, "Low": 5.619600773, @@ -688,7 +688,7 @@ "Volume": 799500914 }, { - "Date": "2020-03-06 15:00", + "Timestamp": "2020-03-06 15:00", "Open": 5.762714386, "High": 5.79133749, "Low": 5.724550724, @@ -696,7 +696,7 @@ "Volume": 392612912 }, { - "Date": "2020-03-09 15:00", + "Timestamp": "2020-03-09 15:00", "Open": 5.657764435, "High": 5.66730547, "Low": 5.562355042, @@ -704,7 +704,7 @@ "Volume": 294122353 }, { - "Date": "2020-03-10 15:00", + "Timestamp": "2020-03-10 15:00", "Open": 5.505109787, "High": 5.610059738, "Low": 5.495569229, @@ -712,7 +712,7 @@ "Volume": 270879498 }, { - "Date": "2020-03-11 15:00", + "Timestamp": "2020-03-11 15:00", "Open": 5.600518703, "High": 5.638682365, "Low": 5.562355042, @@ -720,7 +720,7 @@ "Volume": 181957781 }, { - "Date": "2020-03-12 15:00", + "Timestamp": "2020-03-12 15:00", "Open": 5.552814484, "High": 5.629141808, "Low": 5.514650822, @@ -728,7 +728,7 @@ "Volume": 207296655 }, { - "Date": "2020-03-13 15:00", + "Timestamp": "2020-03-13 15:00", "Open": 5.361995697, "High": 5.543273449, "Low": 5.304750443, @@ -736,7 +736,7 @@ "Volume": 263231569 }, { - "Date": "2020-03-16 15:00", + "Timestamp": "2020-03-16 15:00", "Open": 5.543273449, "High": 5.543273449, "Low": 5.390618801, @@ -744,7 +744,7 @@ "Volume": 218431971 }, { - "Date": "2020-03-17 15:00", + "Timestamp": "2020-03-17 15:00", "Open": 5.400159359, "High": 5.438323021, "Low": 5.285668373, @@ -752,7 +752,7 @@ "Volume": 169382232 }, { - "Date": "2020-03-18 15:00", + "Timestamp": "2020-03-18 15:00", "Open": 5.371537209, "High": 5.390618801, "Low": 5.247505188, @@ -760,7 +760,7 @@ "Volume": 152820214 }, { - "Date": "2020-03-19 15:00", + "Timestamp": "2020-03-19 15:00", "Open": 5.247505188, "High": 5.257046223, "Low": 5.104391098, @@ -768,7 +768,7 @@ "Volume": 181264768 }, { - "Date": "2020-03-20 15:00", + "Timestamp": "2020-03-20 15:00", "Open": 5.190259457, "High": 5.209341526, "Low": 5.152095795, @@ -776,7 +776,7 @@ "Volume": 114235724 }, { - "Date": "2020-03-23 15:00", + "Timestamp": "2020-03-23 15:00", "Open": 5.113932133, "High": 5.113932133, "Low": 5.008982182, @@ -784,7 +784,7 @@ "Volume": 127052162 }, { - "Date": "2020-03-24 15:00", + "Timestamp": "2020-03-24 15:00", "Open": 5.075768471, "High": 5.104391098, "Low": 5.028063774, @@ -792,7 +792,7 @@ "Volume": 88650349 }, { - "Date": "2020-03-25 15:00", + "Timestamp": "2020-03-25 15:00", "Open": 5.152095795, "High": 5.190259457, "Low": 5.123472691, @@ -800,7 +800,7 @@ "Volume": 147656317 }, { - "Date": "2020-03-26 15:00", + "Timestamp": "2020-03-26 15:00", "Open": 5.152095795, "High": 5.218882084, "Low": 5.133014202, @@ -808,7 +808,7 @@ "Volume": 128997735 }, { - "Date": "2020-03-27 15:00", + "Timestamp": "2020-03-27 15:00", "Open": 5.190259457, "High": 5.218882084, "Low": 5.14255476, @@ -816,7 +816,7 @@ "Volume": 121873505 }, { - "Date": "2020-03-30 15:00", + "Timestamp": "2020-03-30 15:00", "Open": 5.085309505, "High": 5.085309505, "Low": 5.028063774, @@ -824,7 +824,7 @@ "Volume": 106167125 }, { - "Date": "2020-03-31 15:00", + "Timestamp": "2020-03-31 15:00", "Open": 5.085309505, "High": 5.09485054, "Low": 5.037604809, @@ -832,7 +832,7 @@ "Volume": 66378042 }, { - "Date": "2020-04-01 15:00", + "Timestamp": "2020-04-01 15:00", "Open": 5.056686878, "High": 5.104391098, "Low": 5.037604809, @@ -840,7 +840,7 @@ "Volume": 79057850 }, { - "Date": "2020-04-02 15:00", + "Timestamp": "2020-04-02 15:00", "Open": 5.018523216, "High": 5.085309505, "Low": 5.008982182, @@ -848,7 +848,7 @@ "Volume": 71676279 }, { - "Date": "2020-04-03 15:00", + "Timestamp": "2020-04-03 15:00", "Open": 5.075768471, "High": 5.09485054, "Low": 5.047145844, @@ -856,7 +856,7 @@ "Volume": 65745365 }, { - "Date": "2020-04-07 15:00", + "Timestamp": "2020-04-07 15:00", "Open": 5.133014202, "High": 5.14255476, "Low": 5.09485054, @@ -864,7 +864,7 @@ "Volume": 98229462 }, { - "Date": "2020-04-08 15:00", + "Timestamp": "2020-04-08 15:00", "Open": 5.085309505, "High": 5.113932133, "Low": 5.085309505, @@ -872,7 +872,7 @@ "Volume": 53963630 }, { - "Date": "2020-04-09 15:00", + "Timestamp": "2020-04-09 15:00", "Open": 5.123472691, "High": 5.123472691, "Low": 5.104391098, @@ -880,7 +880,7 @@ "Volume": 57992533 }, { - "Date": "2020-04-10 15:00", + "Timestamp": "2020-04-10 15:00", "Open": 5.113932133, "High": 5.113932133, "Low": 5.047145844, @@ -888,7 +888,7 @@ "Volume": 83259057 }, { - "Date": "2020-04-13 15:00", + "Timestamp": "2020-04-13 15:00", "Open": 5.037604809, "High": 5.047145844, "Low": 5.018523216, @@ -896,7 +896,7 @@ "Volume": 54171844 }, { - "Date": "2020-04-14 15:00", + "Timestamp": "2020-04-14 15:00", "Open": 5.018523216, "High": 5.056686878, "Low": 5.018523216, @@ -904,7 +904,7 @@ "Volume": 48392879 }, { - "Date": "2020-04-15 15:00", + "Timestamp": "2020-04-15 15:00", "Open": 5.047145844, "High": 5.066227436, "Low": 5.028063774, @@ -912,7 +912,7 @@ "Volume": 53872919 }, { - "Date": "2020-04-16 15:00", + "Timestamp": "2020-04-16 15:00", "Open": 5.018523216, "High": 5.047145844, "Low": 5.008982182, @@ -920,7 +920,7 @@ "Volume": 56733163 }, { - "Date": "2020-04-17 15:00", + "Timestamp": "2020-04-17 15:00", "Open": 5.047145844, "High": 5.056686878, "Low": 5.028063774, @@ -928,7 +928,7 @@ "Volume": 59468552 }, { - "Date": "2020-04-20 15:00", + "Timestamp": "2020-04-20 15:00", "Open": 5.037604809, "High": 5.047145844, "Low": 5.018523216, @@ -936,7 +936,7 @@ "Volume": 41532302 }, { - "Date": "2020-04-21 15:00", + "Timestamp": "2020-04-21 15:00", "Open": 5.028063774, "High": 5.028063774, "Low": 4.980359077, @@ -944,7 +944,7 @@ "Volume": 82288866 }, { - "Date": "2020-04-22 15:00", + "Timestamp": "2020-04-22 15:00", "Open": 4.980359077, "High": 4.989900112, "Low": 4.961277008, @@ -952,7 +952,7 @@ "Volume": 54268019 }, { - "Date": "2020-04-23 15:00", + "Timestamp": "2020-04-23 15:00", "Open": 4.980359077, "High": 4.989900112, "Low": 4.97081852, @@ -960,7 +960,7 @@ "Volume": 47618500 }, { - "Date": "2020-04-24 15:00", + "Timestamp": "2020-04-24 15:00", "Open": 4.961277008, "High": 4.980359077, "Low": 4.904031754, @@ -968,7 +968,7 @@ "Volume": 80555276 }, { - "Date": "2020-04-27 15:00", + "Timestamp": "2020-04-27 15:00", "Open": 4.894491196, "High": 5.047145844, "Low": 4.894491196, @@ -976,7 +976,7 @@ "Volume": 150645009 }, { - "Date": "2020-04-28 15:00", + "Timestamp": "2020-04-28 15:00", "Open": 4.97081852, "High": 4.989900112, "Low": 4.837245464, @@ -984,7 +984,7 @@ "Volume": 101539030 }, { - "Date": "2020-04-29 15:00", + "Timestamp": "2020-04-29 15:00", "Open": 4.884950161, "High": 4.961277008, "Low": 4.875409126, @@ -992,7 +992,7 @@ "Volume": 77926459 }, { - "Date": "2020-04-30 15:00", + "Timestamp": "2020-04-30 15:00", "Open": 4.751377106, "High": 4.799081802, "Low": 4.675049782, @@ -1000,7 +1000,7 @@ "Volume": 682889816 }, { - "Date": "2020-05-06 15:00", + "Timestamp": "2020-05-06 15:00", "Open": 4.741836071, "High": 4.76091814, "Low": 4.713213444, @@ -1008,7 +1008,7 @@ "Volume": 238092495 }, { - "Date": "2020-05-07 15:00", + "Timestamp": "2020-05-07 15:00", "Open": 4.751377106, "High": 4.751377106, "Low": 4.722754478, @@ -1016,7 +1016,7 @@ "Volume": 131296840 }, { - "Date": "2020-05-08 15:00", + "Timestamp": "2020-05-08 15:00", "Open": 4.741836071, "High": 4.76091814, "Low": 4.722754478, @@ -1024,7 +1024,7 @@ "Volume": 162045680 }, { - "Date": "2020-05-11 15:00", + "Timestamp": "2020-05-11 15:00", "Open": 4.741836071, "High": 4.76091814, "Low": 4.732295513, @@ -1032,7 +1032,7 @@ "Volume": 120217831 }, { - "Date": "2020-05-12 15:00", + "Timestamp": "2020-05-12 15:00", "Open": 4.732295513, "High": 4.741836071, "Low": 4.703672409, @@ -1040,7 +1040,7 @@ "Volume": 125054914 }, { - "Date": "2020-05-13 15:00", + "Timestamp": "2020-05-13 15:00", "Open": 4.694131851, "High": 4.713213444, "Low": 4.684590816, @@ -1048,7 +1048,7 @@ "Volume": 99711909 }, { - "Date": "2020-05-14 15:00", + "Timestamp": "2020-05-14 15:00", "Open": 4.694131851, "High": 4.713213444, "Low": 4.675049782, @@ -1056,7 +1056,7 @@ "Volume": 109620513 }, { - "Date": "2020-05-15 15:00", + "Timestamp": "2020-05-15 15:00", "Open": 4.703672409, "High": 4.713213444, "Low": 4.675049782, @@ -1064,7 +1064,7 @@ "Volume": 90973657 }, { - "Date": "2020-05-18 15:00", + "Timestamp": "2020-05-18 15:00", "Open": 4.694131851, "High": 4.694131851, "Low": 4.655968189, @@ -1072,7 +1072,7 @@ "Volume": 93449775 }, { - "Date": "2020-05-19 15:00", + "Timestamp": "2020-05-19 15:00", "Open": 4.694131851, "High": 4.703672409, "Low": 4.675049782, @@ -1080,7 +1080,7 @@ "Volume": 83965436 }, { - "Date": "2020-05-20 15:00", + "Timestamp": "2020-05-20 15:00", "Open": 4.675049782, "High": 4.751377106, "Low": 4.665508747, @@ -1088,7 +1088,7 @@ "Volume": 216752922 }, { - "Date": "2020-05-21 15:00", + "Timestamp": "2020-05-21 15:00", "Open": 4.694131851, "High": 4.713213444, "Low": 4.665508747, @@ -1096,7 +1096,7 @@ "Volume": 102522007 }, { - "Date": "2020-05-22 15:00", + "Timestamp": "2020-05-22 15:00", "Open": 4.665508747, "High": 4.665508747, "Low": 4.589181423, @@ -1104,7 +1104,7 @@ "Volume": 139951436 }, { - "Date": "2020-05-25 15:00", + "Timestamp": "2020-05-25 15:00", "Open": 4.608263493, "High": 4.655968189, "Low": 4.598722458, @@ -1112,7 +1112,7 @@ "Volume": 108551422 }, { - "Date": "2020-05-26 15:00", + "Timestamp": "2020-05-26 15:00", "Open": 4.598722458, "High": 4.63688612, "Low": 4.598722458, @@ -1120,7 +1120,7 @@ "Volume": 81845831 }, { - "Date": "2020-05-27 15:00", + "Timestamp": "2020-05-27 15:00", "Open": 4.627345085, "High": 4.655968189, "Low": 4.608263493, @@ -1128,7 +1128,7 @@ "Volume": 92758595 }, { - "Date": "2020-05-28 15:00", + "Timestamp": "2020-05-28 15:00", "Open": 4.63688612, "High": 4.694131851, "Low": 4.627345085, @@ -1136,7 +1136,7 @@ "Volume": 119546499 }, { - "Date": "2020-05-29 15:00", + "Timestamp": "2020-05-29 15:00", "Open": 4.627345085, "High": 4.63688612, "Low": 4.608263493, @@ -1144,7 +1144,7 @@ "Volume": 96768908 }, { - "Date": "2020-06-01 15:00", + "Timestamp": "2020-06-01 15:00", "Open": 4.63688612, "High": 4.684590816, "Low": 4.617804527, @@ -1152,7 +1152,7 @@ "Volume": 135039814 }, { - "Date": "2020-06-02 15:00", + "Timestamp": "2020-06-02 15:00", "Open": 4.675049782, "High": 4.732295513, "Low": 4.665508747, @@ -1160,7 +1160,7 @@ "Volume": 202329736 }, { - "Date": "2020-06-03 15:00", + "Timestamp": "2020-06-03 15:00", "Open": 4.722754478, "High": 4.78000021, "Low": 4.703672409, @@ -1168,7 +1168,7 @@ "Volume": 164585588 }, { - "Date": "2020-06-04 15:00", + "Timestamp": "2020-06-04 15:00", "Open": 4.722754478, "High": 4.732295513, "Low": 4.684590816, @@ -1176,7 +1176,7 @@ "Volume": 102598710 }, { - "Date": "2020-06-05 15:00", + "Timestamp": "2020-06-05 15:00", "Open": 4.694131851, "High": 4.713213444, "Low": 4.675049782, @@ -1184,7 +1184,7 @@ "Volume": 77107447 }, { - "Date": "2020-06-08 15:00", + "Timestamp": "2020-06-08 15:00", "Open": 4.703672409, "High": 4.732295513, "Low": 4.703672409, @@ -1192,7 +1192,7 @@ "Volume": 94208437 }, { - "Date": "2020-06-09 15:00", + "Timestamp": "2020-06-09 15:00", "Open": 4.722754478, "High": 4.741836071, "Low": 4.713213444, @@ -1200,7 +1200,7 @@ "Volume": 77383403 }, { - "Date": "2020-06-10 15:00", + "Timestamp": "2020-06-10 15:00", "Open": 4.722754478, "High": 4.722754478, "Low": 4.694131851, @@ -1208,7 +1208,7 @@ "Volume": 80669428 }, { - "Date": "2020-06-11 15:00", + "Timestamp": "2020-06-11 15:00", "Open": 4.722754478, "High": 4.751377106, "Low": 4.703672409, @@ -1216,7 +1216,7 @@ "Volume": 179211762 }, { - "Date": "2020-06-12 15:00", + "Timestamp": "2020-06-12 15:00", "Open": 4.694131851, "High": 4.741836071, "Low": 4.675049782, @@ -1224,7 +1224,7 @@ "Volume": 178743165 }, { - "Date": "2020-06-15 15:00", + "Timestamp": "2020-06-15 15:00", "Open": 4.703672409, "High": 4.741836071, "Low": 4.684590816, @@ -1232,7 +1232,7 @@ "Volume": 114689957 }, { - "Date": "2020-06-16 15:00", + "Timestamp": "2020-06-16 15:00", "Open": 4.732295513, "High": 4.741836071, "Low": 4.703672409, @@ -1240,7 +1240,7 @@ "Volume": 91245129 }, { - "Date": "2020-06-17 15:00", + "Timestamp": "2020-06-17 15:00", "Open": 4.741836071, "High": 4.770459175, "Low": 4.732295513, @@ -1248,7 +1248,7 @@ "Volume": 121430348 }, { - "Date": "2020-06-18 15:00", + "Timestamp": "2020-06-18 15:00", "Open": 4.770459175, "High": 4.789540768, "Low": 4.741836071, @@ -1256,7 +1256,7 @@ "Volume": 161660437 }, { - "Date": "2020-06-19 15:00", + "Timestamp": "2020-06-19 15:00", "Open": 4.800000191, "High": 4.800000191, "Low": 4.739999771, @@ -1264,7 +1264,7 @@ "Volume": 220829811 }, { - "Date": "2020-06-22 15:00", + "Timestamp": "2020-06-22 15:00", "Open": 4.75, "High": 4.769999981, "Low": 4.71999979, @@ -1272,7 +1272,7 @@ "Volume": 146421030 }, { - "Date": "2020-06-23 15:00", + "Timestamp": "2020-06-23 15:00", "Open": 4.730000019, "High": 4.730000019, "Low": 4.679999828, @@ -1280,7 +1280,7 @@ "Volume": 99974108 }, { - "Date": "2020-06-24 15:00", + "Timestamp": "2020-06-24 15:00", "Open": 4.699999809, "High": 4.789999962, "Low": 4.679999828, @@ -1288,7 +1288,7 @@ "Volume": 136540853 }, { - "Date": "2020-06-29 15:00", + "Timestamp": "2020-06-29 15:00", "Open": 4.75, "High": 4.760000229, "Low": 4.690000057, @@ -1296,7 +1296,7 @@ "Volume": 95327177 }, { - "Date": "2020-06-30 15:00", + "Timestamp": "2020-06-30 15:00", "Open": 4.699999809, "High": 4.739999771, "Low": 4.690000057, @@ -1304,7 +1304,7 @@ "Volume": 88925761 }, { - "Date": "2020-07-01 15:00", + "Timestamp": "2020-07-01 15:00", "Open": 4.71999979, "High": 4.769999981, "Low": 4.690000057, @@ -1312,7 +1312,7 @@ "Volume": 163310116 }, { - "Date": "2020-07-02 15:00", + "Timestamp": "2020-07-02 15:00", "Open": 4.760000229, "High": 4.940000057, "Low": 4.75, @@ -1320,7 +1320,7 @@ "Volume": 445781574 }, { - "Date": "2020-07-03 15:00", + "Timestamp": "2020-07-03 15:00", "Open": 4.949999809, "High": 5.289999962, "Low": 4.929999828, @@ -1328,7 +1328,7 @@ "Volume": 817401435 }, { - "Date": "2020-07-06 15:00", + "Timestamp": "2020-07-06 15:00", "Open": 5.300000191, "High": 5.710000038, "Low": 5.300000191, @@ -1336,7 +1336,7 @@ "Volume": 1345110540 }, { - "Date": "2020-07-07 15:00", + "Timestamp": "2020-07-07 15:00", "Open": 6.28000021, "High": 6.28000021, "Low": 5.96999979, @@ -1344,7 +1344,7 @@ "Volume": 2589682228 }, { - "Date": "2020-07-08 15:00", + "Timestamp": "2020-07-08 15:00", "Open": 5.71999979, "High": 6.119999886, "Low": 5.610000134, @@ -1352,7 +1352,7 @@ "Volume": 1797593800 }, { - "Date": "2020-07-09 15:00", + "Timestamp": "2020-07-09 15:00", "Open": 5.829999924, "High": 5.96999979, "Low": 5.75, @@ -1360,7 +1360,7 @@ "Volume": 1246966813 }, { - "Date": "2020-07-10 15:00", + "Timestamp": "2020-07-10 15:00", "Open": 5.760000229, "High": 5.78000021, "Low": 5.53000021, @@ -1368,7 +1368,7 @@ "Volume": 880319408 }, { - "Date": "2020-07-13 15:00", + "Timestamp": "2020-07-13 15:00", "Open": 5.489999771, "High": 5.710000038, "Low": 5.460000038, @@ -1376,7 +1376,7 @@ "Volume": 589179313 }, { - "Date": "2020-07-14 15:00", + "Timestamp": "2020-07-14 15:00", "Open": 5.619999886, "High": 5.639999866, "Low": 5.5, @@ -1384,7 +1384,7 @@ "Volume": 430760990 }, { - "Date": "2020-07-15 15:00", + "Timestamp": "2020-07-15 15:00", "Open": 5.559999943, "High": 5.599999905, "Low": 5.400000095, @@ -1392,7 +1392,7 @@ "Volume": 390929289 }, { - "Date": "2020-07-16 15:00", + "Timestamp": "2020-07-16 15:00", "Open": 5.400000095, "High": 5.570000172, "Low": 5.369999886, @@ -1400,7 +1400,7 @@ "Volume": 473236597 }, { - "Date": "2020-07-17 15:00", + "Timestamp": "2020-07-17 15:00", "Open": 5.380000114, "High": 5.449999809, "Low": 5.25, @@ -1408,7 +1408,7 @@ "Volume": 306479042 }, { - "Date": "2020-07-20 15:00", + "Timestamp": "2020-07-20 15:00", "Open": 5.340000153, "High": 5.460000038, "Low": 5.289999962, @@ -1416,7 +1416,7 @@ "Volume": 321410629 }, { - "Date": "2020-07-21 15:00", + "Timestamp": "2020-07-21 15:00", "Open": 5.429999828, "High": 5.460000038, "Low": 5.360000134, @@ -1424,7 +1424,7 @@ "Volume": 208745107 }, { - "Date": "2020-07-22 15:00", + "Timestamp": "2020-07-22 15:00", "Open": 5.369999886, "High": 5.46999979, "Low": 5.340000153, @@ -1432,7 +1432,7 @@ "Volume": 270127340 }, { - "Date": "2020-07-23 15:00", + "Timestamp": "2020-07-23 15:00", "Open": 5.349999905, "High": 5.349999905, "Low": 5.210000038, @@ -1440,7 +1440,7 @@ "Volume": 263988476 }, { - "Date": "2020-07-24 15:00", + "Timestamp": "2020-07-24 15:00", "Open": 5.260000229, "High": 5.289999962, "Low": 5.110000134, @@ -1448,7 +1448,7 @@ "Volume": 281719996 }, { - "Date": "2020-07-27 15:00", + "Timestamp": "2020-07-27 15:00", "Open": 5.110000134, "High": 5.150000095, "Low": 5.059999943, @@ -1456,7 +1456,7 @@ "Volume": 113996849 }, { - "Date": "2020-07-28 15:00", + "Timestamp": "2020-07-28 15:00", "Open": 5.110000134, "High": 5.139999866, "Low": 5.090000153, @@ -1464,7 +1464,7 @@ "Volume": 93843641 }, { - "Date": "2020-07-29 15:00", + "Timestamp": "2020-07-29 15:00", "Open": 5.099999905, "High": 5.199999809, "Low": 5.090000153, @@ -1472,7 +1472,7 @@ "Volume": 170719515 }, { - "Date": "2020-07-30 15:00", + "Timestamp": "2020-07-30 15:00", "Open": 5.179999828, "High": 5.199999809, "Low": 5.130000114, @@ -1480,7 +1480,7 @@ "Volume": 157100671 }, { - "Date": "2020-07-31 15:00", + "Timestamp": "2020-07-31 15:00", "Open": 5.130000114, "High": 5.199999809, "Low": 5.119999886, @@ -1488,7 +1488,7 @@ "Volume": 142827024 }, { - "Date": "2020-08-03 15:00", + "Timestamp": "2020-08-03 15:00", "Open": 5.179999828, "High": 5.260000229, "Low": 5.150000095, @@ -1496,7 +1496,7 @@ "Volume": 277216353 }, { - "Date": "2020-08-04 15:00", + "Timestamp": "2020-08-04 15:00", "Open": 5.28000021, "High": 5.440000057, "Low": 5.21999979, @@ -1504,7 +1504,7 @@ "Volume": 437510900 }, { - "Date": "2020-08-05 15:00", + "Timestamp": "2020-08-05 15:00", "Open": 5.289999962, "High": 5.289999962, "Low": 5.210000038, @@ -1512,7 +1512,7 @@ "Volume": 220489374 }, { - "Date": "2020-08-06 15:00", + "Timestamp": "2020-08-06 15:00", "Open": 5.25, "High": 5.28000021, "Low": 5.199999809, @@ -1520,7 +1520,7 @@ "Volume": 146723495 }, { - "Date": "2020-08-07 15:00", + "Timestamp": "2020-08-07 15:00", "Open": 5.230000019, "High": 5.320000172, "Low": 5.210000038, @@ -1528,7 +1528,7 @@ "Volume": 205343220 }, { - "Date": "2020-08-10 15:00", + "Timestamp": "2020-08-10 15:00", "Open": 5.239999771, "High": 5.300000191, "Low": 5.199999809, @@ -1536,7 +1536,7 @@ "Volume": 161078305 }, { - "Date": "2020-08-11 15:00", + "Timestamp": "2020-08-11 15:00", "Open": 5.260000229, "High": 5.400000095, "Low": 5.25, @@ -1544,7 +1544,7 @@ "Volume": 317081873 }, { - "Date": "2020-08-12 15:00", + "Timestamp": "2020-08-12 15:00", "Open": 5.21999979, "High": 5.300000191, "Low": 5.199999809, @@ -1552,7 +1552,7 @@ "Volume": 188144748 }, { - "Date": "2020-08-13 15:00", + "Timestamp": "2020-08-13 15:00", "Open": 5.25, "High": 5.269999981, "Low": 5.230000019, @@ -1560,7 +1560,7 @@ "Volume": 106954175 }, { - "Date": "2020-08-14 15:00", + "Timestamp": "2020-08-14 15:00", "Open": 5.230000019, "High": 5.329999924, "Low": 5.210000038, @@ -1568,7 +1568,7 @@ "Volume": 209282026 }, { - "Date": "2020-08-17 15:00", + "Timestamp": "2020-08-17 15:00", "Open": 5.329999924, "High": 5.590000153, "Low": 5.329999924, @@ -1576,7 +1576,7 @@ "Volume": 627526355 }, { - "Date": "2020-08-18 15:00", + "Timestamp": "2020-08-18 15:00", "Open": 5.489999771, "High": 5.489999771, "Low": 5.389999866, @@ -1584,7 +1584,7 @@ "Volume": 337782466 }, { - "Date": "2020-08-19 15:00", + "Timestamp": "2020-08-19 15:00", "Open": 5.400000095, "High": 5.429999828, "Low": 5.360000134, @@ -1592,7 +1592,7 @@ "Volume": 178051523 }, { - "Date": "2020-08-20 15:00", + "Timestamp": "2020-08-20 15:00", "Open": 5.340000153, "High": 5.369999886, "Low": 5.300000191, @@ -1600,7 +1600,7 @@ "Volume": 133022101 }, { - "Date": "2020-08-21 15:00", + "Timestamp": "2020-08-21 15:00", "Open": 5.349999905, "High": 5.360000134, "Low": 5.28000021, @@ -1608,7 +1608,7 @@ "Volume": 117089980 }, { - "Date": "2020-08-24 15:00", + "Timestamp": "2020-08-24 15:00", "Open": 5.300000191, "High": 5.320000172, "Low": 5.269999981, @@ -1616,7 +1616,7 @@ "Volume": 87444583 }, { - "Date": "2020-08-25 15:00", + "Timestamp": "2020-08-25 15:00", "Open": 5.300000191, "High": 5.329999924, "Low": 5.260000229, @@ -1624,7 +1624,7 @@ "Volume": 89329710 }, { - "Date": "2020-08-26 15:00", + "Timestamp": "2020-08-26 15:00", "Open": 5.260000229, "High": 5.28000021, "Low": 5.179999828, @@ -1632,7 +1632,7 @@ "Volume": 118942032 }, { - "Date": "2020-08-27 15:00", + "Timestamp": "2020-08-27 15:00", "Open": 5.190000057, "High": 5.21999979, "Low": 5.170000076, @@ -1640,7 +1640,7 @@ "Volume": 83194697 }, { - "Date": "2020-08-28 15:00", + "Timestamp": "2020-08-28 15:00", "Open": 5.210000038, "High": 5.269999981, "Low": 5.190000057, @@ -1648,7 +1648,7 @@ "Volume": 121289550 }, { - "Date": "2020-08-31 15:00", + "Timestamp": "2020-08-31 15:00", "Open": 5.269999981, "High": 5.300000191, "Low": 5.210000038, @@ -1656,7 +1656,7 @@ "Volume": 131156840 }, { - "Date": "2020-09-01 15:00", + "Timestamp": "2020-09-01 15:00", "Open": 5.199999809, "High": 5.230000019, "Low": 5.179999828, @@ -1664,7 +1664,7 @@ "Volume": 94861950 }, { - "Date": "2020-09-02 15:00", + "Timestamp": "2020-09-02 15:00", "Open": 5.210000038, "High": 5.210000038, "Low": 5.170000076, @@ -1672,7 +1672,7 @@ "Volume": 95333658 }, { - "Date": "2020-09-03 15:00", + "Timestamp": "2020-09-03 15:00", "Open": 5.170000076, "High": 5.199999809, "Low": 5.150000095, @@ -1680,7 +1680,7 @@ "Volume": 83132609 }, { - "Date": "2020-09-04 15:00", + "Timestamp": "2020-09-04 15:00", "Open": 5.119999886, "High": 5.159999847, "Low": 5.110000134, @@ -1688,7 +1688,7 @@ "Volume": 78193398 }, { - "Date": "2020-09-07 15:00", + "Timestamp": "2020-09-07 15:00", "Open": 5.139999866, "High": 5.170000076, "Low": 5.099999905, @@ -1696,7 +1696,7 @@ "Volume": 87042221 }, { - "Date": "2020-09-08 15:00", + "Timestamp": "2020-09-08 15:00", "Open": 5.110000134, "High": 5.179999828, "Low": 5.110000134, @@ -1704,7 +1704,7 @@ "Volume": 90201450 }, { - "Date": "2020-09-09 15:00", + "Timestamp": "2020-09-09 15:00", "Open": 5.139999866, "High": 5.159999847, "Low": 5.119999886, @@ -1712,7 +1712,7 @@ "Volume": 87860782 }, { - "Date": "2020-09-10 15:00", + "Timestamp": "2020-09-10 15:00", "Open": 5.159999847, "High": 5.159999847, "Low": 5.059999943, @@ -1720,7 +1720,7 @@ "Volume": 96375360 }, { - "Date": "2020-09-11 15:00", + "Timestamp": "2020-09-11 15:00", "Open": 5.059999943, "High": 5.070000172, "Low": 5.03000021, @@ -1728,7 +1728,7 @@ "Volume": 68229573 }, { - "Date": "2020-09-14 15:00", + "Timestamp": "2020-09-14 15:00", "Open": 5.059999943, "High": 5.099999905, "Low": 5.039999962, @@ -1736,7 +1736,7 @@ "Volume": 67006885 }, { - "Date": "2020-09-15 15:00", + "Timestamp": "2020-09-15 15:00", "Open": 5.070000172, "High": 5.079999924, "Low": 5.050000191, @@ -1744,7 +1744,7 @@ "Volume": 45045921 }, { - "Date": "2020-09-16 15:00", + "Timestamp": "2020-09-16 15:00", "Open": 5.059999943, "High": 5.090000153, "Low": 5.050000191, @@ -1752,7 +1752,7 @@ "Volume": 44274686 }, { - "Date": "2020-09-17 15:00", + "Timestamp": "2020-09-17 15:00", "Open": 5.050000191, "High": 5.059999943, "Low": 5.03000021, @@ -1760,7 +1760,7 @@ "Volume": 50017793 }, { - "Date": "2020-09-18 15:00", + "Timestamp": "2020-09-18 15:00", "Open": 5.039999962, "High": 5.150000095, "Low": 5.03000021, @@ -1768,7 +1768,7 @@ "Volume": 135338733 }, { - "Date": "2020-09-21 15:00", + "Timestamp": "2020-09-21 15:00", "Open": 5.159999847, "High": 5.170000076, "Low": 5.099999905, @@ -1776,7 +1776,7 @@ "Volume": 71215691 }, { - "Date": "2020-09-22 15:00", + "Timestamp": "2020-09-22 15:00", "Open": 5.079999924, "High": 5.230000019, "Low": 5.059999943, @@ -1784,7 +1784,7 @@ "Volume": 98780698 }, { - "Date": "2020-09-23 15:00", + "Timestamp": "2020-09-23 15:00", "Open": 5.110000134, "High": 5.110000134, "Low": 5.079999924, @@ -1792,7 +1792,7 @@ "Volume": 38418433 }, { - "Date": "2020-09-24 15:00", + "Timestamp": "2020-09-24 15:00", "Open": 5.079999924, "High": 5.090000153, "Low": 5.019999981, @@ -1800,7 +1800,7 @@ "Volume": 63226540 }, { - "Date": "2020-09-25 15:00", + "Timestamp": "2020-09-25 15:00", "Open": 5.03000021, "High": 5.050000191, "Low": 5.010000229, @@ -1808,7 +1808,7 @@ "Volume": 40838063 }, { - "Date": "2020-09-28 15:00", + "Timestamp": "2020-09-28 15:00", "Open": 5.019999981, "High": 5.039999962, "Low": 5, @@ -1816,7 +1816,7 @@ "Volume": 37235104 }, { - "Date": "2020-09-29 15:00", + "Timestamp": "2020-09-29 15:00", "Open": 5.019999981, "High": 5.03000021, "Low": 5, @@ -1824,7 +1824,7 @@ "Volume": 36330271 }, { - "Date": "2020-09-30 15:00", + "Timestamp": "2020-09-30 15:00", "Open": 5, "High": 5.03000021, "Low": 4.980000019, @@ -1832,7 +1832,7 @@ "Volume": 44230595 }, { - "Date": "2020-10-09 15:00", + "Timestamp": "2020-10-09 15:00", "Open": 5, "High": 5.039999962, "Low": 5, @@ -1840,7 +1840,7 @@ "Volume": 69641535 }, { - "Date": "2020-10-12 15:00", + "Timestamp": "2020-10-12 15:00", "Open": 5.03000021, "High": 5.150000095, "Low": 5.010000229, @@ -1848,7 +1848,7 @@ "Volume": 130031246 }, { - "Date": "2020-10-13 15:00", + "Timestamp": "2020-10-13 15:00", "Open": 5.099999905, "High": 5.110000134, "Low": 5.070000172, @@ -1856,7 +1856,7 @@ "Volume": 49437559 }, { - "Date": "2020-10-14 15:00", + "Timestamp": "2020-10-14 15:00", "Open": 5.090000153, "High": 5.099999905, "Low": 5.019999981, @@ -1864,7 +1864,7 @@ "Volume": 58099320 }, { - "Date": "2020-10-15 15:00", + "Timestamp": "2020-10-15 15:00", "Open": 5.050000191, "High": 5.119999886, "Low": 5.03000021, @@ -1872,7 +1872,7 @@ "Volume": 79762659 }, { - "Date": "2020-10-16 15:00", + "Timestamp": "2020-10-16 15:00", "Open": 5.059999943, "High": 5.130000114, "Low": 5.03000021, @@ -1880,7 +1880,7 @@ "Volume": 153036072 }, { - "Date": "2020-10-19 15:00", + "Timestamp": "2020-10-19 15:00", "Open": 5.130000114, "High": 5.190000057, "Low": 5.070000172, @@ -1888,7 +1888,7 @@ "Volume": 136985180 }, { - "Date": "2020-10-20 15:00", + "Timestamp": "2020-10-20 15:00", "Open": 5.059999943, "High": 5.070000172, "Low": 5.019999981, @@ -1896,7 +1896,7 @@ "Volume": 85103625 }, { - "Date": "2020-10-21 15:00", + "Timestamp": "2020-10-21 15:00", "Open": 5.039999962, "High": 5.090000153, "Low": 5.019999981, @@ -1904,7 +1904,7 @@ "Volume": 88404624 }, { - "Date": "2020-10-22 15:00", + "Timestamp": "2020-10-22 15:00", "Open": 5.070000172, "High": 5.079999924, "Low": 5.03000021, @@ -1912,7 +1912,7 @@ "Volume": 75363592 }, { - "Date": "2020-10-23 15:00", + "Timestamp": "2020-10-23 15:00", "Open": 5.039999962, "High": 5.099999905, "Low": 5.03000021, @@ -1920,7 +1920,7 @@ "Volume": 108124617 }, { - "Date": "2020-10-26 15:00", + "Timestamp": "2020-10-26 15:00", "Open": 5.050000191, "High": 5.050000191, "Low": 4.980000019, @@ -1928,7 +1928,7 @@ "Volume": 104402119 }, { - "Date": "2020-10-27 15:00", + "Timestamp": "2020-10-27 15:00", "Open": 4.980000019, "High": 4.980000019, "Low": 4.909999847, @@ -1936,7 +1936,7 @@ "Volume": 79701713 }, { - "Date": "2020-10-28 15:00", + "Timestamp": "2020-10-28 15:00", "Open": 4.940000057, "High": 4.940000057, "Low": 4.78000021, @@ -1944,7 +1944,7 @@ "Volume": 146061526 }, { - "Date": "2020-10-29 15:00", + "Timestamp": "2020-10-29 15:00", "Open": 4.760000229, "High": 4.78000021, "Low": 4.71999979, @@ -1952,7 +1952,7 @@ "Volume": 88982782 }, { - "Date": "2020-10-30 15:00", + "Timestamp": "2020-10-30 15:00", "Open": 4.510000229, "High": 4.559999943, "Low": 4.429999828, @@ -1960,7 +1960,7 @@ "Volume": 739564040 }, { - "Date": "2020-11-02 15:00", + "Timestamp": "2020-11-02 15:00", "Open": 4.440000057, "High": 4.460000038, "Low": 4.380000114, @@ -1968,7 +1968,7 @@ "Volume": 291674752 }, { - "Date": "2020-11-03 15:00", + "Timestamp": "2020-11-03 15:00", "Open": 4.400000095, "High": 4.460000038, "Low": 4.389999866, @@ -1976,7 +1976,7 @@ "Volume": 177273320 }, { - "Date": "2020-11-04 15:00", + "Timestamp": "2020-11-04 15:00", "Open": 4.460000038, "High": 4.480000019, "Low": 4.420000076, @@ -1984,7 +1984,7 @@ "Volume": 162728036 }, { - "Date": "2020-11-05 15:00", + "Timestamp": "2020-11-05 15:00", "Open": 4.449999809, "High": 4.46999979, "Low": 4.429999828, @@ -1992,7 +1992,7 @@ "Volume": 150917855 }, { - "Date": "2020-11-06 15:00", + "Timestamp": "2020-11-06 15:00", "Open": 4.460000038, "High": 4.460000038, "Low": 4.429999828, @@ -2000,7 +2000,7 @@ "Volume": 130816487 }, { - "Date": "2020-11-09 15:00", + "Timestamp": "2020-11-09 15:00", "Open": 4.449999809, "High": 4.5, "Low": 4.449999809, @@ -2008,7 +2008,7 @@ "Volume": 291472862 }, { - "Date": "2020-11-10 15:00", + "Timestamp": "2020-11-10 15:00", "Open": 4.489999771, "High": 4.53000021, "Low": 4.449999809, @@ -2016,7 +2016,7 @@ "Volume": 255342765 }, { - "Date": "2020-11-11 15:00", + "Timestamp": "2020-11-11 15:00", "Open": 4.449999809, "High": 4.480000019, "Low": 4.429999828, @@ -2024,7 +2024,7 @@ "Volume": 237066062 }, { - "Date": "2020-11-12 15:00", + "Timestamp": "2020-11-12 15:00", "Open": 4.449999809, "High": 4.460000038, "Low": 4.429999828, @@ -2032,7 +2032,7 @@ "Volume": 143700968 }, { - "Date": "2020-11-13 15:00", + "Timestamp": "2020-11-13 15:00", "Open": 4.429999828, "High": 4.440000057, "Low": 4.400000095, @@ -2040,7 +2040,7 @@ "Volume": 138486840 }, { - "Date": "2020-11-16 15:00", + "Timestamp": "2020-11-16 15:00", "Open": 4.429999828, "High": 4.460000038, "Low": 4.420000076, @@ -2048,7 +2048,7 @@ "Volume": 130619948 }, { - "Date": "2020-11-17 15:00", + "Timestamp": "2020-11-17 15:00", "Open": 4.449999809, "High": 4.480000019, "Low": 4.440000057, @@ -2056,7 +2056,7 @@ "Volume": 171720546 }, { - "Date": "2020-11-18 15:00", + "Timestamp": "2020-11-18 15:00", "Open": 4.46999979, "High": 4.53000021, "Low": 4.460000038, @@ -2064,7 +2064,7 @@ "Volume": 245552039 }, { - "Date": "2020-11-19 15:00", + "Timestamp": "2020-11-19 15:00", "Open": 4.5, "High": 4.519999981, "Low": 4.489999771, @@ -2072,7 +2072,7 @@ "Volume": 178069173 }, { - "Date": "2020-11-20 15:00", + "Timestamp": "2020-11-20 15:00", "Open": 4.489999771, "High": 4.510000229, "Low": 4.480000019, @@ -2080,7 +2080,7 @@ "Volume": 133448004 }, { - "Date": "2020-11-23 15:00", + "Timestamp": "2020-11-23 15:00", "Open": 4.489999771, "High": 4.570000172, "Low": 4.489999771, @@ -2088,7 +2088,7 @@ "Volume": 252056078 }, { - "Date": "2020-11-24 15:00", + "Timestamp": "2020-11-24 15:00", "Open": 4.539999962, "High": 4.570000172, "Low": 4.519999981, @@ -2096,7 +2096,7 @@ "Volume": 163739479 }, { - "Date": "2020-11-25 15:00", + "Timestamp": "2020-11-25 15:00", "Open": 4.550000191, "High": 4.579999924, "Low": 4.5, @@ -2104,7 +2104,7 @@ "Volume": 214558614 }, { - "Date": "2020-11-26 15:00", + "Timestamp": "2020-11-26 15:00", "Open": 4.5, "High": 4.53000021, "Low": 4.489999771, @@ -2112,7 +2112,7 @@ "Volume": 139644749 }, { - "Date": "2020-11-27 15:00", + "Timestamp": "2020-11-27 15:00", "Open": 4.519999981, "High": 4.610000134, "Low": 4.510000229, @@ -2120,7 +2120,7 @@ "Volume": 346593716 }, { - "Date": "2020-11-30 15:00", + "Timestamp": "2020-11-30 15:00", "Open": 4.639999866, "High": 4.809999943, "Low": 4.610000134, @@ -2128,7 +2128,7 @@ "Volume": 623885277 }, { - "Date": "2020-12-01 15:00", + "Timestamp": "2020-12-01 15:00", "Open": 4.610000134, "High": 4.820000172, "Low": 4.590000153, @@ -2136,7 +2136,7 @@ "Volume": 628148306 }, { - "Date": "2020-12-02 15:00", + "Timestamp": "2020-12-02 15:00", "Open": 4.769999981, "High": 4.78000021, "Low": 4.710000038, @@ -2144,7 +2144,7 @@ "Volume": 399448879 }, { - "Date": "2020-12-03 15:00", + "Timestamp": "2020-12-03 15:00", "Open": 4.730000019, "High": 4.869999886, "Low": 4.699999809, @@ -2152,7 +2152,7 @@ "Volume": 568819755 }, { - "Date": "2020-12-04 15:00", + "Timestamp": "2020-12-04 15:00", "Open": 4.769999981, "High": 4.769999981, "Low": 4.690000057, @@ -2160,7 +2160,7 @@ "Volume": 370847881 }, { - "Date": "2020-12-07 15:00", + "Timestamp": "2020-12-07 15:00", "Open": 4.699999809, "High": 4.71999979, "Low": 4.630000114, @@ -2168,7 +2168,7 @@ "Volume": 212967349 }, { - "Date": "2020-12-08 15:00", + "Timestamp": "2020-12-08 15:00", "Open": 4.639999866, "High": 4.670000076, "Low": 4.590000153, @@ -2176,7 +2176,7 @@ "Volume": 141026711 }, { - "Date": "2020-12-09 15:00", + "Timestamp": "2020-12-09 15:00", "Open": 4.610000134, "High": 4.699999809, "Low": 4.610000134, @@ -2184,7 +2184,7 @@ "Volume": 230571559 }, { - "Date": "2020-12-10 15:00", + "Timestamp": "2020-12-10 15:00", "Open": 4.590000153, "High": 4.619999886, "Low": 4.559999943, @@ -2192,7 +2192,7 @@ "Volume": 164670529 }, { - "Date": "2020-12-11 15:00", + "Timestamp": "2020-12-11 15:00", "Open": 4.599999905, "High": 4.630000114, "Low": 4.559999943, @@ -2200,7 +2200,7 @@ "Volume": 155700296 }, { - "Date": "2020-12-14 15:00", + "Timestamp": "2020-12-14 15:00", "Open": 4.579999924, "High": 4.590000153, "Low": 4.539999962, @@ -2208,7 +2208,7 @@ "Volume": 119271066 }, { - "Date": "2020-12-15 15:00", + "Timestamp": "2020-12-15 15:00", "Open": 4.539999962, "High": 4.539999962, "Low": 4.480000019, @@ -2216,7 +2216,7 @@ "Volume": 115664623 }, { - "Date": "2020-12-16 15:00", + "Timestamp": "2020-12-16 15:00", "Open": 4.510000229, "High": 4.539999962, "Low": 4.46999979, @@ -2224,7 +2224,7 @@ "Volume": 105325134 }, { - "Date": "2020-12-17 15:00", + "Timestamp": "2020-12-17 15:00", "Open": 4.480000019, "High": 4.53000021, "Low": 4.440000057, @@ -2232,7 +2232,7 @@ "Volume": 162318881 }, { - "Date": "2020-12-18 15:00", + "Timestamp": "2020-12-18 15:00", "Open": 4.510000229, "High": 4.53000021, "Low": 4.480000019, @@ -2240,7 +2240,7 @@ "Volume": 102932684 }, { - "Date": "2020-12-21 15:00", + "Timestamp": "2020-12-21 15:00", "Open": 4.489999771, "High": 4.519999981, "Low": 4.46999979, @@ -2248,7 +2248,7 @@ "Volume": 92663190 }, { - "Date": "2020-12-22 15:00", + "Timestamp": "2020-12-22 15:00", "Open": 4.489999771, "High": 4.5, "Low": 4.409999847, @@ -2256,7 +2256,7 @@ "Volume": 148952288 }, { - "Date": "2020-12-23 15:00", + "Timestamp": "2020-12-23 15:00", "Open": 4.420000076, "High": 4.449999809, "Low": 4.409999847, @@ -2264,7 +2264,7 @@ "Volume": 88930202 }, { - "Date": "2020-12-24 15:00", + "Timestamp": "2020-12-24 15:00", "Open": 4.429999828, "High": 4.46999979, "Low": 4.409999847, @@ -2272,7 +2272,7 @@ "Volume": 95734205 }, { - "Date": "2020-12-25 15:00", + "Timestamp": "2020-12-25 15:00", "Open": 4.409999847, "High": 4.440000057, "Low": 4.400000095, @@ -2280,7 +2280,7 @@ "Volume": 101063413 }, { - "Date": "2020-12-28 15:00", + "Timestamp": "2020-12-28 15:00", "Open": 4.420000076, "High": 4.449999809, "Low": 4.389999866, @@ -2288,7 +2288,7 @@ "Volume": 106086864 }, { - "Date": "2020-12-29 15:00", + "Timestamp": "2020-12-29 15:00", "Open": 4.420000076, "High": 4.460000038, "Low": 4.409999847, @@ -2296,7 +2296,7 @@ "Volume": 134490694 }, { - "Date": "2020-12-30 15:00", + "Timestamp": "2020-12-30 15:00", "Open": 4.420000076, "High": 4.449999809, "Low": 4.409999847, @@ -2304,7 +2304,7 @@ "Volume": 112040625 }, { - "Date": "2020-12-31 15:00", + "Timestamp": "2020-12-31 15:00", "Open": 4.440000057, "High": 4.550000191, "Low": 4.429999828, @@ -2312,7 +2312,7 @@ "Volume": 216125896 }, { - "Date": "2021-01-04 15:00", + "Timestamp": "2021-01-04 15:00", "Open": 4.489999771, "High": 4.489999771, "Low": 4.420000076, @@ -2320,7 +2320,7 @@ "Volume": 251881884 }, { - "Date": "2021-01-05 15:00", + "Timestamp": "2021-01-05 15:00", "Open": 4.429999828, "High": 4.440000057, "Low": 4.369999886, @@ -2328,7 +2328,7 @@ "Volume": 265717296 }, { - "Date": "2021-01-06 15:00", + "Timestamp": "2021-01-06 15:00", "Open": 4.400000095, "High": 4.409999847, "Low": 4.380000114, @@ -2336,7 +2336,7 @@ "Volume": 247874438 }, { - "Date": "2021-01-07 15:00", + "Timestamp": "2021-01-07 15:00", "Open": 4.400000095, "High": 4.400000095, "Low": 4.289999962, @@ -2344,7 +2344,7 @@ "Volume": 322600805 }, { - "Date": "2021-01-08 15:00", + "Timestamp": "2021-01-08 15:00", "Open": 4.300000191, "High": 4.349999905, "Low": 4.25, @@ -2352,7 +2352,7 @@ "Volume": 155169899 }, { - "Date": "2021-01-11 15:00", + "Timestamp": "2021-01-11 15:00", "Open": 4.329999924, "High": 4.349999905, "Low": 4.260000229, @@ -2360,7 +2360,7 @@ "Volume": 153473953 }, { - "Date": "2021-01-12 15:00", + "Timestamp": "2021-01-12 15:00", "Open": 4.260000229, "High": 4.329999924, "Low": 4.260000229, @@ -2368,7 +2368,7 @@ "Volume": 138909043 }, { - "Date": "2021-01-13 15:00", + "Timestamp": "2021-01-13 15:00", "Open": 4.320000172, "High": 4.329999924, "Low": 4.260000229, @@ -2376,7 +2376,7 @@ "Volume": 126967274 }, { - "Date": "2021-01-14 15:00", + "Timestamp": "2021-01-14 15:00", "Open": 4.289999962, "High": 4.380000114, "Low": 4.289999962, @@ -2384,7 +2384,7 @@ "Volume": 135689641 }, { - "Date": "2021-01-15 15:00", + "Timestamp": "2021-01-15 15:00", "Open": 4.360000134, "High": 4.460000038, "Low": 4.349999905, @@ -2392,7 +2392,7 @@ "Volume": 351355466 }, { - "Date": "2021-01-18 15:00", + "Timestamp": "2021-01-18 15:00", "Open": 4.389999866, "High": 4.420000076, "Low": 4.369999886, @@ -2400,7 +2400,7 @@ "Volume": 180705429 }, { - "Date": "2021-01-19 15:00", + "Timestamp": "2021-01-19 15:00", "Open": 4.389999866, "High": 4.460000038, "Low": 4.369999886, @@ -2408,7 +2408,7 @@ "Volume": 211825785 }, { - "Date": "2021-01-20 15:00", + "Timestamp": "2021-01-20 15:00", "Open": 4.429999828, "High": 4.449999809, "Low": 4.380000114, @@ -2416,7 +2416,7 @@ "Volume": 138801836 }, { - "Date": "2021-01-21 15:00", + "Timestamp": "2021-01-21 15:00", "Open": 4.389999866, "High": 4.429999828, "Low": 4.389999866, @@ -2424,7 +2424,7 @@ "Volume": 139520057 }, { - "Date": "2021-01-22 15:00", + "Timestamp": "2021-01-22 15:00", "Open": 4.400000095, "High": 4.400000095, "Low": 4.320000172, @@ -2432,7 +2432,7 @@ "Volume": 134402252 }, { - "Date": "2021-01-25 15:00", + "Timestamp": "2021-01-25 15:00", "Open": 4.320000172, "High": 4.329999924, "Low": 4.28000021, @@ -2440,7 +2440,7 @@ "Volume": 105412237 }, { - "Date": "2021-01-26 15:00", + "Timestamp": "2021-01-26 15:00", "Open": 4.289999962, "High": 4.329999924, "Low": 4.28000021, @@ -2448,7 +2448,7 @@ "Volume": 108292551 }, { - "Date": "2021-01-27 15:00", + "Timestamp": "2021-01-27 15:00", "Open": 4.289999962, "High": 4.369999886, "Low": 4.289999962, @@ -2456,7 +2456,7 @@ "Volume": 121845089 }, { - "Date": "2021-01-28 15:00", + "Timestamp": "2021-01-28 15:00", "Open": 4.289999962, "High": 4.300000191, "Low": 4.190000057, @@ -2464,7 +2464,7 @@ "Volume": 337418602 }, { - "Date": "2021-01-29 15:00", + "Timestamp": "2021-01-29 15:00", "Open": 4.21999979, "High": 4.21999979, "Low": 4.130000114, @@ -2472,7 +2472,7 @@ "Volume": 258409212 }, { - "Date": "2021-02-01 15:00", + "Timestamp": "2021-02-01 15:00", "Open": 4.150000095, "High": 4.170000076, "Low": 4.110000134, @@ -2480,7 +2480,7 @@ "Volume": 238873654 }, { - "Date": "2021-02-02 15:00", + "Timestamp": "2021-02-02 15:00", "Open": 4.119999886, "High": 4.150000095, "Low": 4.099999905, @@ -2488,7 +2488,7 @@ "Volume": 209954937 }, { - "Date": "2021-02-03 15:00", + "Timestamp": "2021-02-03 15:00", "Open": 4.099999905, "High": 4.110000134, "Low": 4.039999962, @@ -2496,7 +2496,7 @@ "Volume": 192374742 }, { - "Date": "2021-02-04 15:00", + "Timestamp": "2021-02-04 15:00", "Open": 4.070000172, "High": 4.150000095, "Low": 4.039999962, @@ -2504,7 +2504,7 @@ "Volume": 255367422 }, { - "Date": "2021-02-05 15:00", + "Timestamp": "2021-02-05 15:00", "Open": 4.070000172, "High": 4.190000057, "Low": 4.050000191, @@ -2512,7 +2512,7 @@ "Volume": 636100362 }, { - "Date": "2021-02-08 15:00", + "Timestamp": "2021-02-08 15:00", "Open": 4.150000095, "High": 4.150000095, "Low": 4.059999943, @@ -2520,7 +2520,7 @@ "Volume": 364780069 }, { - "Date": "2021-02-09 15:00", + "Timestamp": "2021-02-09 15:00", "Open": 4.079999924, "High": 4.119999886, "Low": 4.059999943, @@ -2528,7 +2528,7 @@ "Volume": 252588058 }, { - "Date": "2021-02-10 15:00", + "Timestamp": "2021-02-10 15:00", "Open": 4.099999905, "High": 4.159999847, "Low": 4.079999924, @@ -2536,7 +2536,7 @@ "Volume": 248939365 }, { - "Date": "2021-02-18 15:00", + "Timestamp": "2021-02-18 15:00", "Open": 4.170000076, "High": 4.21999979, "Low": 4.150000095, @@ -2544,7 +2544,7 @@ "Volume": 314925622 }, { - "Date": "2021-02-19 15:00", + "Timestamp": "2021-02-19 15:00", "Open": 4.179999828, "High": 4.260000229, "Low": 4.170000076, @@ -2552,7 +2552,7 @@ "Volume": 302803021 }, { - "Date": "2021-02-22 15:00", + "Timestamp": "2021-02-22 15:00", "Open": 4.260000229, "High": 4.329999924, "Low": 4.239999771, @@ -2560,7 +2560,7 @@ "Volume": 354856473 }, { - "Date": "2021-02-23 15:00", + "Timestamp": "2021-02-23 15:00", "Open": 4.269999981, "High": 4.320000172, "Low": 4.25, @@ -2568,7 +2568,7 @@ "Volume": 291327120 }, { - "Date": "2021-02-24 15:00", + "Timestamp": "2021-02-24 15:00", "Open": 4.260000229, "High": 4.300000191, "Low": 4.210000038, @@ -2576,7 +2576,7 @@ "Volume": 272271388 }, { - "Date": "2021-02-25 15:00", + "Timestamp": "2021-02-25 15:00", "Open": 4.260000229, "High": 4.329999924, "Low": 4.230000019, @@ -2584,7 +2584,7 @@ "Volume": 353270608 }, { - "Date": "2021-02-26 15:00", + "Timestamp": "2021-02-26 15:00", "Open": 4.260000229, "High": 4.289999962, "Low": 4.21999979, @@ -2592,7 +2592,7 @@ "Volume": 303420663 }, { - "Date": "2021-03-01 15:00", + "Timestamp": "2021-03-01 15:00", "Open": 4.25, "High": 4.260000229, "Low": 4.21999979, @@ -2600,7 +2600,7 @@ "Volume": 171659937 }, { - "Date": "2021-03-02 15:00", + "Timestamp": "2021-03-02 15:00", "Open": 4.25, "High": 4.269999981, "Low": 4.199999809, @@ -2608,7 +2608,7 @@ "Volume": 204390011 }, { - "Date": "2021-03-03 15:00", + "Timestamp": "2021-03-03 15:00", "Open": 4.21999979, "High": 4.309999943, "Low": 4.210000038, @@ -2616,7 +2616,7 @@ "Volume": 324998052 }, { - "Date": "2021-03-04 15:00", + "Timestamp": "2021-03-04 15:00", "Open": 4.28000021, "High": 4.320000172, "Low": 4.260000229, @@ -2624,7 +2624,7 @@ "Volume": 302154537 }, { - "Date": "2021-03-05 15:00", + "Timestamp": "2021-03-05 15:00", "Open": 4.289999962, "High": 4.329999924, "Low": 4.260000229, @@ -2632,7 +2632,7 @@ "Volume": 280188294 }, { - "Date": "2021-03-08 15:00", + "Timestamp": "2021-03-08 15:00", "Open": 4.329999924, "High": 4.369999886, "Low": 4.309999943, @@ -2640,7 +2640,7 @@ "Volume": 279702805 }, { - "Date": "2021-03-09 15:00", + "Timestamp": "2021-03-09 15:00", "Open": 4.329999924, "High": 4.329999924, "Low": 4.239999771, @@ -2648,7 +2648,7 @@ "Volume": 235903429 }, { - "Date": "2021-03-10 15:00", + "Timestamp": "2021-03-10 15:00", "Open": 4.269999981, "High": 4.289999962, "Low": 4.239999771, @@ -2656,7 +2656,7 @@ "Volume": 169045868 }, { - "Date": "2021-03-11 15:00", + "Timestamp": "2021-03-11 15:00", "Open": 4.260000229, "High": 4.309999943, "Low": 4.21999979, @@ -2664,7 +2664,7 @@ "Volume": 243095346 }, { - "Date": "2021-03-12 15:00", + "Timestamp": "2021-03-12 15:00", "Open": 4.289999962, "High": 4.349999905, "Low": 4.269999981, @@ -2672,7 +2672,7 @@ "Volume": 300471167 }, { - "Date": "2021-03-15 15:00", + "Timestamp": "2021-03-15 15:00", "Open": 4.340000153, "High": 4.409999847, "Low": 4.329999924, @@ -2680,7 +2680,7 @@ "Volume": 314736665 }, { - "Date": "2021-03-16 15:00", + "Timestamp": "2021-03-16 15:00", "Open": 4.380000114, "High": 4.409999847, "Low": 4.369999886, @@ -2688,7 +2688,7 @@ "Volume": 212190818 }, { - "Date": "2021-03-17 15:00", + "Timestamp": "2021-03-17 15:00", "Open": 4.380000114, "High": 4.400000095, "Low": 4.340000153, @@ -2696,7 +2696,7 @@ "Volume": 180738804 }, { - "Date": "2021-03-18 15:00", + "Timestamp": "2021-03-18 15:00", "Open": 4.360000134, "High": 4.389999866, "Low": 4.349999905, @@ -2704,7 +2704,7 @@ "Volume": 122488225 }, { - "Date": "2021-03-19 15:00", + "Timestamp": "2021-03-19 15:00", "Open": 4.349999905, "High": 4.360000134, "Low": 4.300000191, @@ -2712,7 +2712,7 @@ "Volume": 173591254 }, { - "Date": "2021-03-22 15:00", + "Timestamp": "2021-03-22 15:00", "Open": 4.320000172, "High": 4.369999886, "Low": 4.309999943, @@ -2720,7 +2720,7 @@ "Volume": 156250662 }, { - "Date": "2021-03-23 15:00", + "Timestamp": "2021-03-23 15:00", "Open": 4.369999886, "High": 4.369999886, "Low": 4.329999924, @@ -2728,7 +2728,7 @@ "Volume": 119699501 }, { - "Date": "2021-03-24 15:00", + "Timestamp": "2021-03-24 15:00", "Open": 4.340000153, "High": 4.349999905, "Low": 4.320000172, diff --git a/tests/observe/Program.cs b/tests/observe/Program.cs deleted file mode 100644 index f7c7d0511..000000000 --- a/tests/observe/Program.cs +++ /dev/null @@ -1,122 +0,0 @@ -using Alpaca.Markets; -using Skender.Stock.Indicators; - -namespace ObserveStreaming; - -internal class Program -{ - private static async Task Main(string[] args) - { - if (args.Length != 0) - { - Console.WriteLine(args); - } - - QuoteStream quoteStream = new(); - await quoteStream.SubscribeToQuotes("BTC/USD"); - } -} - -public class QuoteStream -{ - private readonly string alpacaApiKey = Environment.GetEnvironmentVariable("ALPACA_KEY"); - private readonly string alpacaSecret = Environment.GetEnvironmentVariable("ALPACA_SECRET"); - - internal QuoteStream() - { - if (string.IsNullOrEmpty(alpacaApiKey)) - { - throw new ArgumentNullException( - alpacaApiKey, - $"API KEY missing, use `setx ALPACA_KEY \"MY_ALPACA_KEY\"` to set."); - } - - if (string.IsNullOrEmpty(alpacaSecret)) - { - throw new ArgumentNullException( - alpacaSecret, - $"API SECRET missing, use `setx ALPACA_SECRET \"MY_ALPACA_SECRET\"` to set."); - } - } - - public async Task SubscribeToQuotes(string symbol) - { - Console.WriteLine("Press any key to exit the process..."); - Console.WriteLine("PLEASE WAIT. QUOTES ARRIVE EVERY MINUTE."); - - if (string.IsNullOrEmpty(alpacaApiKey)) - { - throw new ArgumentNullException(alpacaApiKey); - } - - if (string.IsNullOrEmpty(alpacaSecret)) - { - throw new ArgumentNullException(alpacaSecret); - } - - // initialize our quote provider and a few subscribers - QuoteProvider provider = new(); - - EmaObserver ema = provider.GetEma(14); - SmaObserver sma = provider.GetSma(5); - EmaObserver emaChain = provider - .Use(CandlePart.HL2) - .GetEma(10); - - // connect to Alpaca websocket - SecretKey secretKey = new(alpacaApiKey, alpacaSecret); - - IAlpacaCryptoStreamingClient client - = Environments - .Paper - .GetAlpacaCryptoStreamingClient(secretKey); - - await client.ConnectAndAuthenticateAsync(); - - // TODO: is this needed? - AutoResetEvent[] waitObjects = [new AutoResetEvent(false)]; - - IAlpacaDataSubscription quoteSubscription - = client.GetMinuteBarSubscription(symbol); - - await client.SubscribeAsync(quoteSubscription); - - // console display header - Console.WriteLine("A new quote will be shown when they arrive every minute."); - Console.WriteLine("PLEASE WAIT > 8 MINUTES BEFORE EXITING TO SEE ALL 4 INDICATORS CALCULATED."); - Console.WriteLine("Press any key to EXIT the process and to see results."); - Console.WriteLine(); - - Console.WriteLine("Date Close price SMA(3) EMA(5) EMA(7,HL2) SMA/EMA(8)"); - Console.WriteLine("----------------------------------------------------------------------------------"); - - // handle new quotes - quoteSubscription.Received += (q) => - { - // add to our provider - provider.Add(new Quote - { - Date = q.TimeUtc, - Open = q.Open, - High = q.High, - Low = q.Low, - Close = q.Close, - Volume = q.Volume - }); - - Console.WriteLine($"{q.Symbol} {q.TimeUtc:s} ${q.Close:N2} | {q.TradeCount} trades"); - }; - - await client.SubscribeAsync(quoteSubscription); - - // to stop watching on key press - Console.ReadKey(); - - // end observation - provider.EndTransmission(); - - // close WebSocket - await client.UnsubscribeAsync(quoteSubscription); - await client.DisconnectAsync(); - } -} diff --git a/tests/observe/README.md b/tests/observe/README.md deleted file mode 100644 index f53a331ac..000000000 --- a/tests/observe/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Prerequisite steps - -To get connected to the WebSocket in this console test project, you have to add your own Alpaca key and secret information to your local environment variables. - -Run the following command line items to set, after replacing the secret and key values. - -```bash -setx AlpacaApiKey "ALPACA_API_KEY" -setx AlpacaSecret "ALPACA_SECRET" -``` diff --git a/tests/other/Convergence.Tests.cs b/tests/other/Convergence.Tests.cs index cace80bc0..541bb7cca 100644 --- a/tests/other/Convergence.Tests.cs +++ b/tests/other/Convergence.Tests.cs @@ -1,7 +1,7 @@ -namespace Tests.Convergence; +namespace Behavioral; [TestClass] -public class ConvergenceTests : TestBase +public class Convergence : TestBase { private static readonly int[] QuotesQuantities = [5, 14, 28, 40, 50, 75, 100, 110, 120, 130, 140, 150, 160, 175, 200, 250, 350, 500, 600, 700, 800, 900, 1000]; @@ -11,13 +11,11 @@ public void Adx() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetAdx(14); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToAdx(); - AdxResult l = r.LastOrDefault(); - Console.WriteLine( - "ADX(14) on {0:d} with {1,4} historical quotes: {2:N8}", - l.Date, quotes.Count(), l.Adx); + AdxResult l = r[^1]; + Console.WriteLine($"ADX(14) on {l.Timestamp:d} with {qts.Count,4} historical qts: {l.Adx:N8}"); } } @@ -26,13 +24,13 @@ public void Alligator() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetAlligator(); + IReadOnlyList quotes = Data.GetLongish(qty); + IReadOnlyList r = quotes.ToAlligator(); - AlligatorResult l = r.LastOrDefault(); + AlligatorResult l = r[^1]; Console.WriteLine( "ALLIGATOR(13,8,5) on {0:d} with {1,4} periods: Jaw {2:N8}", - l.Date, quotes.Count(), l.Jaw); + l.Timestamp, quotes.Count, l.Jaw); } } @@ -41,13 +39,11 @@ public void Atr() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetAtr(14); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToAtr(); - AtrResult l = r.LastOrDefault(); - Console.WriteLine( - "ATR(14) on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Atr); + AtrResult l = r[^1]; + Console.WriteLine($"ATR(14) on {l.Timestamp:d} with {qts.Count,4} periods: {l.Atr:N8}"); } } @@ -56,13 +52,11 @@ public void ChaikinOsc() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetChaikinOsc(); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToChaikinOsc(); - ChaikinOscResult l = r.LastOrDefault(); - Console.WriteLine( - "CHAIKIN OSC on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Oscillator); + ChaikinOscResult l = r[^1]; + Console.WriteLine($"CHAIKIN OSC on {l.Timestamp:d} with {qts.Count,4} periods: {l.Oscillator:N8}"); } } @@ -71,13 +65,11 @@ public void ConnorsRsi() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetConnorsRsi(3, 2, 10); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToConnorsRsi(3, 2, 10); - ConnorsRsiResult l = r.LastOrDefault(); - Console.WriteLine( - "CRSI on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.ConnorsRsi); + ConnorsRsiResult l = r[^1]; + Console.WriteLine($"CRSI on {l.Timestamp:d} with {qts.Count,4} periods: {l.ConnorsRsi:N8}"); } } @@ -86,13 +78,11 @@ public void Dema() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetDema(15); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToDema(15); - DemaResult l = r.LastOrDefault(); - Console.WriteLine( - "DEMA(15) on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Dema); + DemaResult l = r[^1]; + Console.WriteLine($"DEMA(15) on {l.Timestamp:d} with {qts.Count,4} periods: {l.Dema:N8}"); } } @@ -101,13 +91,11 @@ public void Dynamic() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetDynamic(100); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToDynamic(100); - DynamicResult l = r.LastOrDefault(); - Console.WriteLine( - "DYNAMIC(15) on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Dynamic); + DynamicResult l = r[^1]; + Console.WriteLine($"DYNAMIC(15) on {l.Timestamp:d} with {qts.Count,4} periods: {l.Dynamic:N8}"); } } @@ -116,13 +104,11 @@ public void Ema() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetEma(15); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToEma(15); - EmaResult l = r.LastOrDefault(); - Console.WriteLine( - "EMA(15) on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Ema); + EmaResult l = r[^1]; + Console.WriteLine($"EMA(15) on {l.Timestamp:d} with {qts.Count,4} periods: {l.Ema:N8}"); } } @@ -131,13 +117,11 @@ public void FisherTransform() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetFisherTransform(10); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToFisherTransform(); - FisherTransformResult l = r.LastOrDefault(); - Console.WriteLine( - "FT(10) on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Fisher); + FisherTransformResult l = r[^1]; + Console.WriteLine($"FT(10) on {l.Timestamp:d} with {qts.Count,4} periods: {l.Fisher:N8}"); } } @@ -146,13 +130,13 @@ public void Gator() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetGator(); + IReadOnlyList quotes = Data.GetLongish(qty); + IReadOnlyList r = quotes.ToGator(); - GatorResult l = r.LastOrDefault(); + GatorResult l = r[^1]; Console.WriteLine( "GATOR() on {0:d} with {1,4} periods: Upper {2:N8} Lower {3:N8}", - l.Date, quotes.Count(), l.Upper, l.Lower); + l.Timestamp, quotes.Count, l.Upper, l.Lower); } } @@ -161,13 +145,11 @@ public void HtTrendline() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetHtTrendline(); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToHtTrendline(); - HtlResult l = r.LastOrDefault(); - Console.WriteLine( - "HTL on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Trendline); + HtlResult l = r[^1]; + Console.WriteLine($"HTL on {l.Timestamp:d} with {qts.Count,4} periods: {l.Trendline:N8}"); } } @@ -176,13 +158,11 @@ public void Kama() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetKama(10); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToKama(); - KamaResult l = r.LastOrDefault(); - Console.WriteLine( - "KAMA(10) on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Kama); + KamaResult l = r[^1]; + Console.WriteLine($"KAMA(10) on {l.Timestamp:d} with {qts.Count,4} periods: {l.Kama:N8}"); } } @@ -191,13 +171,11 @@ public void Keltner() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetKeltner(100); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToKeltner(100); - KeltnerResult l = r.LastOrDefault(); - Console.WriteLine( - "KC-UP on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.UpperBand); + KeltnerResult l = r[^1]; + Console.WriteLine($"KC-UP on {l.Timestamp:d} with {qts.Count,4} periods: {l.UpperBand:N8}"); } } @@ -206,13 +184,11 @@ public void Macd() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(15 + qty); - IEnumerable r = quotes.GetMacd(); + IReadOnlyList qts = Data.GetLongish(15 + qty); + IReadOnlyList r = qts.ToMacd(); - MacdResult l = r.LastOrDefault(); - Console.WriteLine( - "MACD on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Macd); + MacdResult l = r[^1]; + Console.WriteLine($"MACD on {l.Timestamp:d} with {qts.Count,4} periods: {l.Macd:N8}"); } } @@ -221,13 +197,11 @@ public void Mama() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetMama(); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToMama(); - MamaResult l = r.LastOrDefault(); - Console.WriteLine( - "MAMA on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Mama); + MamaResult l = r[^1]; + Console.WriteLine($"MAMA on {l.Timestamp:d} with {qts.Count,4} periods: {l.Mama:N8}"); } } @@ -236,13 +210,11 @@ public void Pmo() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetPmo(); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToPmo(); - PmoResult l = r.LastOrDefault(); - Console.WriteLine( - "PMO on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Pmo); + PmoResult l = r[^1]; + Console.WriteLine($"PMO on {l.Timestamp:d} with {qts.Count,4} periods: {l.Pmo:N8}"); } } @@ -251,13 +223,11 @@ public void Pvo() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetPvo(); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToPvo(); - PvoResult l = r.LastOrDefault(); - Console.WriteLine( - "PVO on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Pvo); + PvoResult l = r[^1]; + Console.WriteLine($"PVO on {l.Timestamp:d} with {qts.Count,4} periods: {l.Pvo:N8}"); } } @@ -266,13 +236,11 @@ public void Rsi() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetRsi(14); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToRsi(); - RsiResult l = r.LastOrDefault(); - Console.WriteLine( - "RSI(14) on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Rsi); + RsiResult l = r[^1]; + Console.WriteLine($"RSI(14) on {l.Timestamp:d} with {qts.Count,4} periods: {l.Rsi:N8}"); } } @@ -281,13 +249,11 @@ public void Smi() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetDefault(qty); - IEnumerable r = quotes.GetSmi(14, 20, 5, 3); + IReadOnlyList qts = Data.GetDefault(qty); + IReadOnlyList r = qts.ToSmi(14, 20, 5); - SmiResult l = r.LastOrDefault(); - Console.WriteLine( - "SMI on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Smi); + SmiResult l = r[^1]; + Console.WriteLine($"SMI on {l.Timestamp:d} with {qts.Count,4} periods: {l.Smi:N8}"); } } @@ -296,13 +262,11 @@ public void Smma() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetSmma(15); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToSmma(15); - SmmaResult l = r.LastOrDefault(); - Console.WriteLine( - "SMMA(15) on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Smma); + SmmaResult l = r[^1]; + Console.WriteLine($"SMMA(15) on {l.Timestamp:d} with {qts.Count,4} periods: {l.Smma:N8}"); } } @@ -311,13 +275,11 @@ public void StarcBands() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetStarcBands(20); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToStarcBands(20); - StarcBandsResult l = r.LastOrDefault(); - Console.WriteLine( - "STARC UPPER on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.UpperBand); + StarcBandsResult l = r[^1]; + Console.WriteLine($"STARC UPPER on {l.Timestamp:d} with {qts.Count,4} periods: {l.UpperBand:N8}"); } } @@ -326,13 +288,11 @@ public void StochRsi() { foreach (int qty in QuotesQuantities.Where(x => x <= 502)) { - IEnumerable quotes = TestData.GetDefault(qty); - IEnumerable r = quotes.GetStochRsi(14, 14, 3, 1); + IReadOnlyList qts = Data.GetDefault(qty); + IReadOnlyList r = qts.ToStochRsi(14, 14, 3); - StochRsiResult l = r.LastOrDefault(); - Console.WriteLine( - "SRSI on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.StochRsi); + StochRsiResult l = r[^1]; + Console.WriteLine($"SRSI on {l.Timestamp:d} with {qts.Count,4} periods: {l.StochRsi:N8}"); } } @@ -341,13 +301,11 @@ public void T3() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetT3(20); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToT3(20); - T3Result l = r.LastOrDefault(); - Console.WriteLine( - "T3 on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.T3); + T3Result l = r[^1]; + Console.WriteLine($"T3 on {l.Timestamp:d} with {qts.Count,4} periods: {l.T3:N8}"); } } @@ -356,13 +314,11 @@ public void Tema() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetTema(15); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToTema(15); - TemaResult l = r.LastOrDefault(); - Console.WriteLine( - "TEMA on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Tema); + TemaResult l = r[^1]; + Console.WriteLine($"TEMA on {l.Timestamp:d} with {qts.Count,4} periods: {l.Tema:N8}"); } } @@ -371,13 +327,11 @@ public void Trix() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetTrix(15); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToTrix(15); - TrixResult l = r.LastOrDefault(); - Console.WriteLine( - "TRIX on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Trix); + TrixResult l = r[^1]; + Console.WriteLine($"TRIX on {l.Timestamp:d} with {qts.Count,4} periods: {l.Trix:N8}"); } } @@ -386,13 +340,11 @@ public void Tsi() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(20 + qty); - IEnumerable r = quotes.GetTsi(); + IReadOnlyList qts = Data.GetLongish(20 + qty); + IReadOnlyList r = qts.ToTsi(); - TsiResult l = r.LastOrDefault(); - Console.WriteLine( - "TSI on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Tsi); + TsiResult l = r[^1]; + Console.WriteLine($"TSI on {l.Timestamp:d} with {qts.Count,4} periods: {l.Tsi:N8}"); } } @@ -401,13 +353,11 @@ public void Vortex() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = TestData.GetLongish(qty); - IEnumerable r = quotes.GetVortex(14); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToVortex(14); - VortexResult l = r.LastOrDefault(); - Console.WriteLine( - "VI+ on {0:d} with {1,4} periods: {2:N8}", - l.Date, quotes.Count(), l.Pvi); + VortexResult l = r[^1]; + Console.WriteLine($"VI+ on {l.Timestamp:d} with {qts.Count,4} periods: {l.Pvi:N8}"); } } } diff --git a/tests/other/Custom.Indicator.Tests.cs b/tests/other/Custom.Indicator.Tests.cs new file mode 100644 index 000000000..133d4d488 --- /dev/null +++ b/tests/other/Custom.Indicator.Tests.cs @@ -0,0 +1,162 @@ +using System.Collections.ObjectModel; +using System.Globalization; +using Sut; + +namespace Customization; + +// CUSTOM INDICATORS + +[TestClass] +public class CustomIndicators +{ + private static readonly CultureInfo EnglishCulture = new("en-US", false); + + private static readonly IReadOnlyList quotes = Data.GetDefault(); + private static readonly IReadOnlyList badQuotes = Data.GetBad(); + private static readonly IReadOnlyList noquotes = []; + private static readonly IReadOnlyList onequote = Data.GetDefault(1); + + [TestMethod] + public void Standard() + { + IReadOnlyList results = quotes + .GetIndicator(20); + + // proper quantities + Assert.AreEqual(502, results.Count); + Assert.AreEqual(483, results.Count(x => x.Sma != null)); + + // sample values + Assert.IsNull(results[18].Sma); + Assert.AreEqual(214.5250, Math.Round(results[19].Sma.Value, 4)); + Assert.AreEqual(215.0310, Math.Round(results[24].Sma.Value, 4)); + Assert.AreEqual(234.9350, Math.Round(results[149].Sma.Value, 4)); + Assert.AreEqual(255.5500, Math.Round(results[249].Sma.Value, 4)); + Assert.AreEqual(251.8600, Math.Round(results[501].Sma.Value, 4)); + } + + [TestMethod] + public void CandlePartOpen() + { + IReadOnlyList results = quotes + .Use(CandlePart.Open) + .GetIndicator(20); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(483, results.Count(x => x.Sma != null)); + + // sample values + Assert.IsNull(results[18].Sma); + Assert.AreEqual(214.3795, Math.Round(results[19].Sma.Value, 4)); + Assert.AreEqual(214.9535, Math.Round(results[24].Sma.Value, 4)); + Assert.AreEqual(234.8280, Math.Round(results[149].Sma.Value, 4)); + Assert.AreEqual(255.6915, Math.Round(results[249].Sma.Value, 4)); + Assert.AreEqual(253.1725, Math.Round(results[501].Sma.Value, 4)); + } + + [TestMethod] + public void CandlePartVolume() + { + IReadOnlyList results = quotes + .Use(CandlePart.Volume) + .GetIndicator(20); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(483, results.Count(x => x.Sma != null)); + + // sample values + CustomReusable r24 = results[24]; + Assert.AreEqual(77293768.2, r24.Sma); + + CustomReusable r290 = results[290]; + Assert.AreEqual(157958070.8, r290.Sma); + + CustomReusable r501 = results[501]; + Assert.AreEqual(DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", EnglishCulture), r501.Timestamp); + Assert.AreEqual(163695200, r501.Sma); + } + + [TestMethod] + public void Chainor() + { + IReadOnlyList results = quotes + .GetIndicator(10) + .ToEma(10); + + Assert.AreEqual(502, results.Count); + Assert.AreEqual(484, results.Count(x => x.Ema != null)); + } + + [TestMethod] + public void QuoteToSortedList() + { + IReadOnlyList mismatch = Data.GetMismatch(); + + IReadOnlyList h = mismatch.ToSortedList(); + + // proper quantities + Assert.AreEqual(502, h.Count); + + // check first date + DateTime firstDate = DateTime.ParseExact("01/18/2016", "MM/dd/yyyy", EnglishCulture); + Assert.AreEqual(firstDate, h[0].Timestamp); + + // check last date + DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", EnglishCulture); + Assert.AreEqual(lastDate, h[^1].Timestamp); + + // spot check an out of sequence date + DateTime spotDate = DateTime.ParseExact("03/16/2017", "MM/dd/yyyy", EnglishCulture); + Assert.AreEqual(spotDate, h[50].Timestamp); + } + + [TestMethod] + public void NaN() + { + IReadOnlyList r = Data.GetBtcUsdNan() + .GetIndicator(50); + + Assert.AreEqual(0, r.Count(x => x.Sma is double.NaN)); + } + + [TestMethod] + public void BadData() + { + IReadOnlyList r = badQuotes + .GetIndicator(15); + + Assert.AreEqual(502, r.Count); + Assert.AreEqual(0, r.Count(x => x.Sma is double.NaN)); + } + + [TestMethod] + public void NoQuotesExist() + { + IReadOnlyList r0 = noquotes + .GetIndicator(5); + + Assert.AreEqual(0, r0.Count); + + IReadOnlyList r1 = onequote + .GetIndicator(5); + + Assert.AreEqual(1, r1.Count); + } + + [TestMethod] + public void Removed() + { + IReadOnlyList results = quotes + .GetIndicator(20) + .RemoveWarmupPeriods(19); + + Assert.AreEqual(502 - 19, results.Count); + Assert.AreEqual(251.8600, Math.Round(results[^1].Sma.Value, 4)); + } + + // bad lookback period + [TestMethod] + public void Exceptions() + => Assert.ThrowsException(() + => quotes.GetIndicator(0)); +} diff --git a/tests/other/Custom.Quotes.Tests.cs b/tests/other/Custom.Quotes.Tests.cs new file mode 100644 index 000000000..3b50b542d --- /dev/null +++ b/tests/other/Custom.Quotes.Tests.cs @@ -0,0 +1,178 @@ +using System.Globalization; +using Sut; + +namespace Customization; + +// CUSTOM QUOTES + +[TestClass] +public class CustomQuotes +{ + private static readonly CultureInfo EnglishCulture + = new("en-US", false); + + private static readonly DateTime EvalDate + = DateTime.ParseExact( + "12/31/2018", "MM/dd/yyyy", EnglishCulture); + + private static readonly IReadOnlyList quotes = Data.GetDefault(); + private static readonly IReadOnlyList intraday = Data.GetIntraday(); + + [TestMethod] + public void CustomQuoteSeries() + { + List myGenericHistory = quotes + .Select(x => new CustomQuote { + CloseDate = x.Timestamp, + Open = x.Open, + High = x.High, + Low = x.Low, + CloseValue = x.Close, + Volume = x.Volume, + MyOtherProperty = 123456 + }) + .ToList(); + + IReadOnlyList results = Ema.ToEma(myGenericHistory, 20); + + // proper quantities + Assert.AreEqual(502, results.Count); + Assert.AreEqual(483, results.Count(x => x.Ema != null)); + + // 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)); + } + + [TestMethod] + public void CustomQuoteEquality() + { + CustomQuote q1 = new() { + CloseDate = EvalDate, + Open = 1m, + High = 1m, + Low = 1m, + CloseValue = 1m, + Volume = 100 + }; + + CustomQuote q2 = new() { + CloseDate = EvalDate, + Open = 1m, + High = 1m, + Low = 1m, + CloseValue = 1m, + Volume = 100 + }; + + CustomQuote q3 = new() { + CloseDate = EvalDate, + Open = 1m, + High = 1m, + Low = 1m, + CloseValue = 2m, + Volume = 99 + }; + + Assert.IsTrue(Equals(q1, q2)); + Assert.IsFalse(Equals(q1, q3)); + + Assert.IsTrue(q1.Equals(q2)); + Assert.IsFalse(q1.Equals(q3)); + + Assert.IsTrue(q1 == q2, "== operator"); + Assert.IsFalse(q1 == q3, "== operator"); + + Assert.IsFalse(q1 != q2, "!= operator"); + Assert.IsTrue(q1 != q3, "!= operator"); + } + + [TestMethod] + public void CustomQuoteAggregate() + { + List myGenericHistory = intraday + .Select(x => new CustomQuote { + CloseDate = x.Timestamp, + Open = x.Open, + High = x.High, + Low = x.Low, + CloseValue = x.Close, + Volume = x.Volume, + MyOtherProperty = 123456 + }) + .ToList(); + + IReadOnlyList quotesList = myGenericHistory + .Aggregate(PeriodSize.TwoHours); + + // proper quantities + Assert.AreEqual(20, quotesList.Count); + + // sample values + Quote r19 = quotesList[19]; + Assert.AreEqual(369.04m, r19.Low); + } + + [TestMethod] + public void CustomQuoteAggregateTimeSpan() + { + List myGenericHistory = intraday + .Select(x => new CustomQuote { + CloseDate = x.Timestamp, + Open = x.Open, + High = x.High, + Low = x.Low, + CloseValue = x.Close, + Volume = x.Volume, + MyOtherProperty = 123456 + }) + .ToList(); + + IReadOnlyList quotesList = myGenericHistory + .Aggregate(TimeSpan.FromHours(2)); + + // proper quantities + Assert.AreEqual(20, quotesList.Count); + + // sample values + Quote r19 = quotesList[19]; + Assert.AreEqual(369.04m, r19.Low); + } + + [TestMethod] + public void CustomQuoteInheritedSeries() + { + List myGenericHistory = quotes + .Select(x => new CustomQuoteInherited( + CloseDate: x.Timestamp, + Open: x.Open, + High: x.High, + Low: x.Low, + Close: x.Close, + Volume: x.Volume, + MyOtherProperty: 123456)) + .ToList(); + + IReadOnlyList results = myGenericHistory.ToEma(20); + + // proper quantities + Assert.AreEqual(502, results.Count); + Assert.AreEqual(483, results.Count(x => x.Ema != null)); + + // 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)); + } +} diff --git a/tests/other/Custom.Results.Tests.cs b/tests/other/Custom.Results.Tests.cs new file mode 100644 index 000000000..d7b4c7f60 --- /dev/null +++ b/tests/other/Custom.Results.Tests.cs @@ -0,0 +1,86 @@ +using System.Globalization; +using Sut; + +namespace Customization; + +// CUSTOM RESULTS + +[TestClass] +public class CustomResults +{ + private static readonly CultureInfo EnglishCulture + = new("en-US", false); + + private static readonly IReadOnlyList quotes = Data.GetDefault(); + + [TestMethod] + public void CustomSeriesClass() + { + // can use a derive Indicator class + CustomSeries myIndicator = new() { + Timestamp = DateTime.Now, + Ema = 123.456, + MyProperty = false + }; + + Assert.AreEqual(false, myIndicator.MyProperty); + } + + [TestMethod] + public void CustomSeriesClassLinq() + { + IReadOnlyList emaResults = quotes.ToEma(14); + + // can use a derive Indicator class using Linq + + IEnumerable myIndicatorResults = emaResults + .Where(x => x.Ema != null) + .Select(x => new CustomSeries { + Timestamp = x.Timestamp, + Ema = x.Ema, + MyProperty = false + }); + + Assert.IsTrue(myIndicatorResults.Any()); + } + + [TestMethod] + public void CustomSeriesClassFind() + { + List emaResults + = quotes.ToEma(20).ToList(); + + // can use a derive Indicator class using Linq + + List myIndicatorResults = emaResults + .Where(x => x.Ema != null) + .Select(x => new CustomSeries { + Id = 12345, + Timestamp = x.Timestamp, + Ema = x.Ema, + MyProperty = false + }) + .ToList(); + + Assert.IsTrue(myIndicatorResults.Count > 0); + + // find specific date + DateTime findDate = DateTime.ParseExact( + "2018-12-31", "yyyy-MM-dd", EnglishCulture); + + CustomSeries i = myIndicatorResults.Find(x => x.Timestamp == findDate); + Assert.AreEqual(12345, i.Id); + + EmaResult r = emaResults.Find(x => x.Timestamp == findDate); + Assert.AreEqual(249.3519m, Math.Round((decimal)r.Ema, 4)); + } + + [TestMethod] + public void CustomReusable() => Assert.Inconclusive("Test not implemented"); + + [TestMethod] + public void CustomReusableInherited() => Assert.Inconclusive("Test not implemented"); + + [TestMethod] + public void CustomInheritedEma() => Assert.Inconclusive("Test not implemented"); +} diff --git a/tests/other/CustomIndicator.Tests.cs b/tests/other/CustomIndicator.Tests.cs deleted file mode 100644 index 328909e2f..000000000 --- a/tests/other/CustomIndicator.Tests.cs +++ /dev/null @@ -1,264 +0,0 @@ -using System.Collections.ObjectModel; -using System.Globalization; - -namespace Tests.CustomIndicators; - -public sealed class MyResult : ResultBase, IReusableResult -{ - public MyResult(DateTime date) - { - Date = date; - } - - public double? Sma { get; set; } - - double? IReusableResult.Value => Sma; -} - -public static class CustomIndicator -{ - // SERIES, from TQuote - public static IEnumerable GetIndicator( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToTupleCollection(CandlePart.Close) - .CalcIndicator(lookbackPeriods); - - // SERIES, from CHAIN - public static IEnumerable GetIndicator( - this IEnumerable results, - int lookbackPeriods) => results - .ToTupleChainable() - .CalcIndicator(lookbackPeriods) - .SyncIndex(results, SyncType.Prepend); - - // SERIES, from TUPLE - public static IEnumerable GetIndicator( - this IEnumerable<(DateTime, double)> priceTuples, - int lookbackPeriods) => priceTuples - .ToSortedCollection() - .CalcIndicator(lookbackPeriods); - - internal static List CalcIndicator( - this Collection<(DateTime, double)> tpList, - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for SMA."); - } - - // initialize - List results = new(tpList.Count); - - // roll through quotes - for (int i = 0; i < tpList.Count; i++) - { - (DateTime date, double _) = tpList[i]; - - MyResult result = new(date); - results.Add(result); - - if (i + 1 >= lookbackPeriods) - { - double sumSma = 0; - for (int p = i + 1 - lookbackPeriods; p <= i; p++) - { - (DateTime _, double pValue) = tpList[p]; - sumSma += pValue; - } - - result.Sma = (sumSma / lookbackPeriods).NaN2Null(); - } - } - - return results; - } -} - -[TestClass] -public class CustomIndicatorTests -{ - private static readonly CultureInfo EnglishCulture = new("en-US", false); - - internal static readonly IEnumerable quotes = TestData.GetDefault(); - internal static readonly IEnumerable otherQuotes = TestData.GetCompare(); - internal static readonly IEnumerable badQuotes = TestData.GetBad(); - internal static readonly IEnumerable bigQuotes = TestData.GetTooBig(); - internal static readonly IEnumerable maxQuotes = TestData.GetMax(); - internal static readonly IEnumerable longishQuotes = TestData.GetLongish(); - internal static readonly IEnumerable longestQuotes = TestData.GetLongest(); - internal static readonly IEnumerable mismatchQuotes = TestData.GetMismatch(); - internal static readonly IEnumerable noquotes = new List(); - internal static readonly IEnumerable onequote = TestData.GetDefault(1); - internal static readonly IEnumerable randomQuotes = TestData.GetRandom(1000); - internal static readonly IEnumerable zeroesQuotes = TestData.GetZeros(); - internal static readonly IEnumerable<(DateTime, double)> tupleNanny = TestData.GetTupleNaN(); - - [TestMethod] - public void Standard() - { - List results = quotes - .GetIndicator(20) - .ToList(); - - // proper quantities - Assert.AreEqual(502, results.Count); - Assert.AreEqual(483, results.Count(x => x.Sma != null)); - - // sample values - Assert.IsNull(results[18].Sma); - Assert.AreEqual(214.5250, Math.Round(results[19].Sma.Value, 4)); - Assert.AreEqual(215.0310, Math.Round(results[24].Sma.Value, 4)); - Assert.AreEqual(234.9350, Math.Round(results[149].Sma.Value, 4)); - Assert.AreEqual(255.5500, Math.Round(results[249].Sma.Value, 4)); - Assert.AreEqual(251.8600, Math.Round(results[501].Sma.Value, 4)); - } - - [TestMethod] - public void CandlePartOpen() - { - List results = quotes - .Use(CandlePart.Open) - .GetIndicator(20) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(483, results.Count(x => x.Sma != null)); - - // sample values - Assert.IsNull(results[18].Sma); - Assert.AreEqual(214.3795, Math.Round(results[19].Sma.Value, 4)); - Assert.AreEqual(214.9535, Math.Round(results[24].Sma.Value, 4)); - Assert.AreEqual(234.8280, Math.Round(results[149].Sma.Value, 4)); - Assert.AreEqual(255.6915, Math.Round(results[249].Sma.Value, 4)); - Assert.AreEqual(253.1725, Math.Round(results[501].Sma.Value, 4)); - } - - [TestMethod] - public void CandlePartVolume() - { - List results = quotes - .Use(CandlePart.Volume) - .GetIndicator(20) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(483, results.Count(x => x.Sma != null)); - - // sample values - MyResult r24 = results[24]; - Assert.AreEqual(77293768.2, r24.Sma); - - MyResult r290 = results[290]; - Assert.AreEqual(157958070.8, r290.Sma); - - MyResult r501 = results[501]; - Assert.AreEqual(DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", EnglishCulture), r501.Date); - Assert.AreEqual(163695200, r501.Sma); - } - - [TestMethod] - public void Chainor() - { - List results = quotes - .GetIndicator(10) - .GetEma(10) - .ToList(); - - Assert.AreEqual(502, results.Count); - Assert.AreEqual(484, results.Count(x => x.Ema != null)); - } - - [TestMethod] - public void QuoteToSortedList() - { - IEnumerable mismatch = TestData.GetMismatch(); - - Collection h = mismatch.ToSortedCollection(); - - // proper quantities - Assert.AreEqual(502, h.Count); - - // check first date - DateTime firstDate = DateTime.ParseExact("01/18/2016", "MM/dd/yyyy", EnglishCulture); - Assert.AreEqual(firstDate, h[0].Date); - - // check last date - DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", EnglishCulture); - Assert.AreEqual(lastDate, h.LastOrDefault().Date); - - // spot check an out of sequence date - DateTime spotDate = DateTime.ParseExact("03/16/2017", "MM/dd/yyyy", EnglishCulture); - Assert.AreEqual(spotDate, h[50].Date); - } - - [TestMethod] - public void TupleNaN() - { - List r = tupleNanny - .GetIndicator(6) - .ToList(); - - Assert.AreEqual(200, r.Count); - Assert.AreEqual(0, r.Count(x => x.Sma is not null and double.NaN)); - } - - [TestMethod] - public void NaN() - { - List r = TestData.GetBtcUsdNan() - .GetIndicator(50) - .ToList(); - - Assert.AreEqual(0, r.Count(x => x.Sma is not null and double.NaN)); - } - - [TestMethod] - public void BadData() - { - List r = badQuotes - .GetIndicator(15) - .ToList(); - - Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.Sma is not null and double.NaN)); - } - - [TestMethod] - public void NoQuotesExist() - { - List r0 = noquotes - .GetIndicator(5) - .ToList(); - - Assert.AreEqual(0, r0.Count); - - List r1 = onequote - .GetIndicator(5) - .ToList(); - - Assert.AreEqual(1, r1.Count); - } - - [TestMethod] - public void Removed() - { - List results = quotes - .GetIndicator(20) - .RemoveWarmupPeriods(19) - .ToList(); - - Assert.AreEqual(502 - 19, results.Count); - Assert.AreEqual(251.8600, Math.Round(results.LastOrDefault().Sma.Value, 4)); - } - - // bad lookback period - [TestMethod] - public void Exceptions() - => Assert.ThrowsException(() - => quotes.GetIndicator(0)); -} diff --git a/tests/other/GlobalUsings.cs b/tests/other/GlobalUsings.cs index 211ba4da6..fc2afe48a 100644 --- a/tests/other/GlobalUsings.cs +++ b/tests/other/GlobalUsings.cs @@ -1,3 +1,4 @@ +global using FluentAssertions; global using Microsoft.VisualStudio.TestTools.UnitTesting; global using Skender.Stock.Indicators; -global using Tests.Common; +global using Test.Data; diff --git a/tests/other/PublicApi.Interface.Tests.cs b/tests/other/PublicApi.Interface.Tests.cs new file mode 100644 index 000000000..9dad62942 --- /dev/null +++ b/tests/other/PublicApi.Interface.Tests.cs @@ -0,0 +1,147 @@ +[assembly: CLSCompliant(true)] +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] + +namespace PublicApi; + +// PUBLIC API (INTERFACES) + +[TestClass] +public class UserInterface +{ + private static readonly IReadOnlyList quotes = Data.GetDefault(); + private static readonly IReadOnlyList quotesBad = Data.GetBad(); + + [TestMethod] + public void QuoteValidation() + { + IReadOnlyList clean = quotes; + + clean.Validate(); + clean.ToSma(6); + clean.ToEma(5); + + IReadOnlyList reverse = quotes + .OrderByDescending(x => x.Timestamp) + .ToList(); + + // has duplicates + InvalidQuotesException dx + = Assert.ThrowsException( + () => quotesBad.Validate()); + + dx.Message.Should().Contain("Duplicate date found"); + + // out of order + InvalidQuotesException sx + = Assert.ThrowsException( + () => reverse.Validate()); + + sx.Message.Should().Contain("Quotes are out of sequence"); + } + + [TestMethod] + public void QuoteValidationReturn() + { + IReadOnlyList h = quotes.Validate(); + + Quote f = h[0]; + Console.WriteLine($"Quote:{f}"); + } + + [TestMethod] + public void StreamMany() // from quote provider + { + /****************************************************** + * Attaches many stream observers to one Quote provider + * for a full sprectrum stream collective. + * + * Currently, it does not include any [direct] chains. + * + * This test covers most of the unusual test cases, like: + * + * - out of order quotes (late arrivals) + * - duplicates, but not to an overflow situation + * + ******************************************************/ + + int length = quotes.Count; + + // setup quote provider + QuoteHub provider = new(); + + // initialize observers + AdlHub adlHub = provider.ToAdl(); + AlligatorHub alligatorHub = provider.ToAlligator(); + AtrHub atrHub = provider.ToAtr(); + AtrStopHub atrStopHub = provider.ToAtrStop(); + EmaHub emaHub = provider.ToEma(20); + QuotePartHub quotePartHub = provider.ToQuotePart(CandlePart.OHL3); + SmaHub smaHub = provider.ToSma(20); + TrHub trHub = provider.ToTr(); + + // emulate adding quotes to provider + for (int i = 0; i < length; i++) + { + // skip one (add later) + if (i == 80) + { + continue; + } + + Quote q = quotes[i]; + provider.Add(q); + + // resend duplicate quotes + if (i is > 100 and < 105) + { + provider.Add(q); + } + } + + // late arrival + provider.Insert(quotes[80]); + + // end all observations + provider.EndTransmission(); + + // get static equivalents for comparison + IReadOnlyList staticAdl = quotes.ToAdl(); + IReadOnlyList staticAtr = quotes.ToAtr(); + IReadOnlyList staticAtrStop = quotes.ToAtrStop(); + IReadOnlyList staticAlligator = quotes.ToAlligator(); + IReadOnlyList staticEma = quotes.ToEma(20); + IReadOnlyList staticQuotePart = quotes.Use(CandlePart.OHL3); + IReadOnlyList staticSma = quotes.ToSma(20); + IReadOnlyList staticTr = quotes.ToTr(); + + // final results should persist in scope + IReadOnlyList streamAdl = adlHub.Results; + IReadOnlyList streamAtr = atrHub.Results; + IReadOnlyList streamAtrStop = atrStopHub.Results; + IReadOnlyList streamAlligator = alligatorHub.Results; + IReadOnlyList streamEma = emaHub.Results; + IReadOnlyList streamQuotePart = quotePartHub.Results; + IReadOnlyList streamSma = smaHub.Results; + IReadOnlyList streamTr = trHub.Results; + + // assert, should be correct length + streamAdl.Should().HaveCount(length); + streamAlligator.Should().HaveCount(length); + streamAtr.Should().HaveCount(length); + streamAtrStop.Should().HaveCount(length); + streamEma.Should().HaveCount(length); + streamQuotePart.Should().HaveCount(length); + streamSma.Should().HaveCount(length); + streamTr.Should().HaveCount(length); + + // assert, should equal static series + streamAdl.Should().BeEquivalentTo(staticAdl); + streamAtr.Should().BeEquivalentTo(staticAtr); + streamAtrStop.Should().BeEquivalentTo(staticAtrStop); + streamAlligator.Should().BeEquivalentTo(staticAlligator); + streamEma.Should().BeEquivalentTo(staticEma); + streamQuotePart.Should().BeEquivalentTo(staticQuotePart); + streamSma.Should().BeEquivalentTo(staticSma); + streamTr.Should().BeEquivalentTo(staticTr); + } +} diff --git a/tests/other/PublicApi.Tests.cs b/tests/other/PublicApi.Tests.cs deleted file mode 100644 index 7ffe5f564..000000000 --- a/tests/other/PublicApi.Tests.cs +++ /dev/null @@ -1,237 +0,0 @@ -using System.Globalization; - -[assembly: CLSCompliant(true)] -namespace Tests.PublicApi; - -internal sealed class MyQuote : Quote -{ - public bool MyProperty { get; set; } - public decimal? MyClose { get; set; } -} - -internal sealed class MyEma : ResultBase -{ - public int Id { get; set; } - public bool MyProperty { get; set; } - public double? Ema { get; set; } -} - -internal sealed class MyGenericQuote : IQuote -{ - // required base properties - DateTime ISeries.Date => CloseDate; - public decimal Open { get; set; } - public decimal High { get; set; } - public decimal Low { get; set; } - decimal IQuote.Close => CloseValue; - public decimal Volume { get; set; } - - // custom properties - public int MyOtherProperty { get; set; } - public DateTime CloseDate { get; set; } - public decimal CloseValue { get; set; } -} - -[TestClass] -public class PublicClassTests -{ - internal static readonly CultureInfo EnglishCulture = new("en-US", false); - - [TestMethod] - public void ValidateHistory() - { - IEnumerable quotes = TestData.GetDefault(); - - quotes.Validate(); - quotes.GetSma(6); - quotes.GetSma(5); - } - - [TestMethod] - public void ReadQuoteClass() - { - IEnumerable quotes = TestData.GetDefault(); - IEnumerable h = quotes.Validate(); - - Quote f = h.FirstOrDefault(); - Console.WriteLine("Date:{0},Close:{1}", f.Date, f.Close); - } - - [TestMethod] - public void DerivedQuoteClass() - { - // can use a derive Quote class - MyQuote myQuote = new() { - Date = DateTime.Now, - MyProperty = true - }; - - Assert.AreEqual(true, myQuote.MyProperty); - } - - [TestMethod] - public void DerivedQuoteClassLinq() - { - IEnumerable quotes = TestData.GetDefault(); - quotes = quotes.Validate(); - - // can use a derive Quote class using Linq - - IEnumerable myHistory = quotes - .Select(x => new MyQuote { - Date = x.Date, - MyClose = x.Close, - MyProperty = false - }); - - Assert.IsTrue(myHistory.Any()); - } - - [TestMethod] - public void CustomQuoteClass() - { - List myGenericHistory = TestData.GetDefault() - .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 results = myGenericHistory.GetEma(20) - .ToList(); - - // proper quantities - Assert.AreEqual(502, results.Count); - Assert.AreEqual(483, results.Count(x => x.Ema != null)); - - // 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)); - } - - [TestMethod] - public void CustomQuoteAggregate() - { - List myGenericHistory = TestData.GetIntraday() - .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 quotesList = myGenericHistory - .Aggregate(PeriodSize.TwoHours) - .ToList(); - - // proper quantities - Assert.AreEqual(20, quotesList.Count); - - // sample values - Quote r19 = quotesList[19]; - Assert.AreEqual(369.04m, r19.Low); - } - - [TestMethod] - public void CustomQuoteAggregateTimeSpan() - { - List myGenericHistory = TestData.GetIntraday() - .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 quotesList = myGenericHistory - .Aggregate(TimeSpan.FromHours(2)) - .ToList(); - - // proper quantities - Assert.AreEqual(20, quotesList.Count); - - // sample values - Quote r19 = quotesList[19]; - Assert.AreEqual(369.04m, r19.Low); - } - - [TestMethod] - public void CustomIndicatorClass() - { - // can use a derive Indicator class - MyEma myIndicator = new() { - Date = DateTime.Now, - Ema = 123.456, - MyProperty = false - }; - - Assert.AreEqual(false, myIndicator.MyProperty); - } - - [TestMethod] - public void CustomIndicatorClassLinq() - { - IEnumerable quotes = TestData.GetDefault(); - IEnumerable emaResults = quotes.GetEma(14); - - // can use a derive Indicator class using Linq - - IEnumerable myIndicatorResults = emaResults - .Where(x => x.Ema != null) - .Select(x => new MyEma { - Date = x.Date, - Ema = x.Ema, - MyProperty = false - }); - - Assert.IsTrue(myIndicatorResults.Any()); - } - - [TestMethod] - public void CustomIndicatorClassFind() - { - IEnumerable quotes = TestData.GetDefault(); - IEnumerable emaResults = Indicator.GetEma(quotes, 20); - - // can use a derive Indicator class using Linq - - IEnumerable myIndicatorResults = emaResults - .Where(x => x.Ema != null) - .Select(x => new MyEma { - Id = 12345, - Date = x.Date, - Ema = x.Ema, - MyProperty = false - }); - - Assert.IsTrue(myIndicatorResults.Any()); - - // find specific date - DateTime findDate = DateTime.ParseExact("2018-12-31", "yyyy-MM-dd", EnglishCulture); - - MyEma i = myIndicatorResults.Find(findDate); - Assert.AreEqual(12345, i.Id); - - EmaResult r = emaResults.Find(findDate); - Assert.AreEqual(249.3519m, Math.Round((decimal)r.Ema, 4)); - } -} diff --git a/tests/other/Sut.CustomItems.cs b/tests/other/Sut.CustomItems.cs new file mode 100644 index 000000000..2715bb860 --- /dev/null +++ b/tests/other/Sut.CustomItems.cs @@ -0,0 +1,138 @@ +using System.Collections.ObjectModel; + +namespace Sut; + +// SUBJECT UNDER TEST (SUT) + +public sealed record CustomQuote : IQuote +{ + // override, redirect required properties + DateTime ISeries.Timestamp + => CloseDate; + + decimal IQuote.Close + => CloseValue; + + // custom properties + public int MyOtherProperty { get; set; } + public DateTime CloseDate { get; init; } + public decimal CloseValue { get; init; } + + // required base properties + public decimal Open { get; init; } + public decimal High { get; init; } + public decimal Low { get; init; } + public decimal Volume { get; init; } + + double IReusable.Value + => (double)CloseValue; +} + +[Serializable] +public record CustomQuoteInherited +( + DateTime CloseDate, + decimal Open, + decimal High, + decimal Low, + decimal Close, + decimal Volume, + int MyOtherProperty +) : Quote(CloseDate, Open, High, Low, Close, Volume); + +public sealed record CustomReusable : IReusable +{ + public DateTime Timestamp { get; init; } + public double? Sma { get; init; } + + double IReusable.Value + => Sma.Null2NaN(); +} + +public sealed record CustomReusableInherited( + DateTime Timestamp, + double? Sma + ) : IReusable +{ + public double Value + => Sma.Null2NaN(); +} + +public sealed class CustomSeries : ISeries +{ + public DateTime Timestamp { get; init; } + public int Id { get; init; } + public bool MyProperty { get; init; } + public double? Ema { get; set; } +} + +public record class CustomInheritEma : EmaResult +{ + // classic constructor + public CustomInheritEma(DateTime Timestamp) + : base(Timestamp) { } + + // custom properties (has defaults) + public int Id { get; set; } + public bool MyProperty { get; set; } +} + +public static class CustomIndicator +{ + // SERIES, from CHAIN + public static IReadOnlyList GetIndicator( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + => source + .ToSortedList() + .CalcIndicator(lookbackPeriods); + + private static List CalcIndicator( + this IReadOnlyList source, + int lookbackPeriods) + where T : IReusable + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for SMA."); + } + + // initialize + int length = source.Count; + List results = new(length); + + // roll through source values + for (int i = 0; i < length; i++) + { + T s = source[i]; + + double? sma; + + if (i >= lookbackPeriods - 1) + { + double sum = 0; + for (int p = i - lookbackPeriods + 1; p <= i; p++) + { + T ps = source[p]; + sum += ps.Value; + } + + sma = (sum / lookbackPeriods).NaN2Null(); + } + else + { + sma = null; + } + + results.Add(new() { + Timestamp = s.Timestamp, + Sma = sma + }); + } + + return results; + } +} diff --git a/tests/other/Tests.Other.csproj b/tests/other/Tests.Other.csproj index 4163be127..7d31c15e1 100644 --- a/tests/other/Tests.Other.csproj +++ b/tests/other/Tests.Other.csproj @@ -14,7 +14,12 @@ + + + all + runtime; build; native; contentfiles; analyzers + diff --git a/tests/performance/GlobalSuppressions.cs b/tests/performance/GlobalSuppressions.cs index 0d913b5dc..76695c9cc 100644 --- a/tests/performance/GlobalSuppressions.cs +++ b/tests/performance/GlobalSuppressions.cs @@ -1,24 +1,11 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage( - "Performance", - "CA1822:Mark members as static", + "Design", + "CA1051:Do not declare visible instance fields", Justification = "Required for BenchmarkDotNet")] [assembly: SuppressMessage( - "StyleCop.CSharp.MaintainabilityRules", - "SA1401:Fields should be private", - Justification = "Required for BenchmarkDotNet", - Scope = "member", - Target = "~F:Tests.Performance.InternalsPerformance.Periods")] - -[assembly: SuppressMessage("Design", - "CA1051:Do not declare visible instance fields", - Justification = "Required for BenchmarkDotNet", - Scope = "member", - Target = "~F:Tests.Performance.InternalsPerformance.Periods")] + "Performance", + "CA1822:Mark members as static", + Justification = "Required for BenchmarkDotNet")] diff --git a/tests/performance/GlobalUsings.cs b/tests/performance/GlobalUsings.cs index 373a066f6..9293b0a2d 100644 --- a/tests/performance/GlobalUsings.cs +++ b/tests/performance/GlobalUsings.cs @@ -1,3 +1,3 @@ global using BenchmarkDotNet.Attributes; global using Skender.Stock.Indicators; -global using Tests.Common; +global using Test.Data; diff --git a/tests/performance/Perf.Helpers.cs b/tests/performance/Perf.Helpers.cs deleted file mode 100644 index 8b5b4ed54..000000000 --- a/tests/performance/Perf.Helpers.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace Tests.Performance; - -// HELPERS, both public and private - -[ShortRunJob] -public class HelperPerformance -{ - private static IEnumerable h; - private static IEnumerable i; - - [GlobalSetup] - public static void Setup() => h = TestData.GetDefault(); - - [GlobalSetup(Targets = [nameof(Aggregate)])] - public static void SetupIntraday() => i = TestData.GetIntraday(); - - [Benchmark] - public object ToSortedList() => h.ToSortedList(); - - [Benchmark] - public object ToSortedCollection() => h.ToSortedCollection(); - - [Benchmark] - public object ToListQuoteD() => h.ToQuoteD(); - - [Benchmark] - public object ToTupleClose() => h.ToTuple(CandlePart.Close); - - [Benchmark] - public object ToTupleOHLC4() => h.ToTuple(CandlePart.OHLC4); - - [Benchmark] - public object ToCandleResults() => h.ToCandleResults(); - - [Benchmark] - public object Validate() => h.Validate(); - - [Benchmark] - public object Aggregate() => i.Aggregate(PeriodSize.FifteenMinutes); -} diff --git a/tests/performance/Perf.Increments.cs b/tests/performance/Perf.Increments.cs new file mode 100644 index 000000000..cb4ffdef1 --- /dev/null +++ b/tests/performance/Perf.Increments.cs @@ -0,0 +1,95 @@ +namespace Performance; + +// TIME-SERIES INDICATORS + +[ShortRunJob] +public class Incrementals +{ + private static readonly IReadOnlyList quotes + = Data.GetDefault(); + + private static readonly IReadOnlyList reusables + = quotes + .Cast() + .ToList(); + + private readonly QuoteHub provider = new(); + + [GlobalSetup] + public void Setup() => provider.Add(quotes); + + [GlobalCleanup] + public void Cleanup() + { + provider.EndTransmission(); + provider.Cache.Clear(); + } + + [Benchmark] + public object EmaIncRusBatch() + { + EmaList sut = new(14) { reusables }; + return sut; + } + + [Benchmark] + public object EmaIncRusItem() + { + EmaList sut = new(14); + + for (int i = 0; i < reusables.Count; i++) + { + sut.Add(reusables[i]); + } + + return sut; + } + + [Benchmark] + public object EmaIncRusSplit() + { + EmaList sut = new(14); + + for (int i = 0; i < reusables.Count; i++) + { + sut.Add(reusables[i].Timestamp, reusables[i].Value); + } + + return sut; + } + + [Benchmark] + public object EmaIncQotBatch() + { + EmaList sut = new(14) { quotes }; + return sut; + } + + [Benchmark] + public object EmaIncQot() + { + EmaList sut = new(14); + + for (int i = 0; i < quotes.Count; i++) + { + sut.Add(quotes[i]); + } + + return sut; + } + + // TIME-SERIES EQUIVALENTS + + [Benchmark] + public object EmaSeries() => quotes.ToEma(14); + + [Benchmark] + public object EmaIncrem() + { + EmaList ema = new(14) { quotes }; + return ema; + } + + [Benchmark] + public object EmaStream() => provider.ToEma(14).Results; +} diff --git a/tests/performance/Perf.Indicators.Static.cs b/tests/performance/Perf.Indicators.Static.cs deleted file mode 100644 index 36ebfd01a..000000000 --- a/tests/performance/Perf.Indicators.Static.cs +++ /dev/null @@ -1,324 +0,0 @@ -using BenchmarkDotNet.Attributes; -using Skender.Stock.Indicators; -using Tests.Common; - -namespace Tests.Performance; - -[ShortRunJob] -public class IndicatorsStatic -{ - private static IEnumerable q; - private static IEnumerable o; - private static List ql; - private static List ll; - - // SETUP - - [GlobalSetup] - public static void Setup() - { - q = TestData.GetDefault(); - ql = q.ToList(); - ll = TestData.GetLongest().ToList(); - } - - [GlobalSetup(Targets = new[] - { - nameof(GetBeta), - nameof(GetBetaUp), - nameof(GetBetaDown), - nameof(GetBetaAll), - nameof(GetCorrelation), - nameof(GetPrs), - nameof(GetPrsWithSma) - })] - public static void SetupCompare() - { - q = TestData.GetDefault(); - o = TestData.GetCompare(); - } - - // BENCHMARKS - - [Benchmark] - public object GetAdl() => q.GetAdl(); - - [Benchmark] - public object GetAdx() => q.GetAdx(); - - [Benchmark] - public object GetAlligator() => q.GetAlligator(); - - [Benchmark] - public object GetAlma() => q.GetAlma(); - - [Benchmark] - public object GetAroon() => q.GetAroon(); - - [Benchmark] - public object GetAtr() => q.GetAtr(); - - [Benchmark] - public object GetAtrStop() => q.GetAtrStop(); - - [Benchmark] - public object GetAwesome() => q.GetAwesome(); - - [Benchmark] - public object GetBeta() => Indicator.GetBeta(q, o, 20, BetaType.Standard); - - [Benchmark] - public object GetBetaUp() => Indicator.GetBeta(q, o, 20, BetaType.Up); - - [Benchmark] - public object GetBetaDown() => Indicator.GetBeta(q, o, 20, BetaType.Down); - - [Benchmark] - public object GetBetaAll() => Indicator.GetBeta(q, o, 20, BetaType.All); - - [Benchmark] - public object GetBollingerBands() => q.GetBollingerBands(); - - [Benchmark] - public object GetBop() => q.GetBop(); - - [Benchmark] - public object GetCci() => q.GetCci(); - - [Benchmark] - public object GetChaikinOsc() => q.GetChaikinOsc(); - - [Benchmark] - public object GetChandelier() => q.GetChandelier(); - - [Benchmark] - public object GetChop() => q.GetChop(); - - [Benchmark] - public object GetCmf() => q.GetCmf(); - - [Benchmark] - public object GetCmo() => q.GetCmo(14); - - [Benchmark] - public object GetConnorsRsi() => q.GetConnorsRsi(); - - [Benchmark] - public object GetCorrelation() => q.GetCorrelation(o, 20); - - [Benchmark] - public object GetDema() => q.GetDema(14); - - [Benchmark] - public object GetDoji() => q.GetDoji(); - - [Benchmark] - public object GetDonchian() => q.GetDonchian(); - - [Benchmark] - public object GetDpo() => q.GetDpo(14); - - [Benchmark] - public object GetDynamic() => q.GetDynamic(20); - - [Benchmark] - public object GetElderRay() => q.GetElderRay(); - - [Benchmark] - public object GetEma() => q.GetEma(14); - - [Benchmark] - public object GetEpma() => q.GetEpma(14); - - [Benchmark] - public object GetFcb() => q.GetFcb(14); - - [Benchmark] - public object GetFisherTransform() => q.GetFisherTransform(10); - - [Benchmark] - public object GetForceIndex() => q.GetForceIndex(13); - - [Benchmark] - public object GetFractal() => q.GetFractal(); - - [Benchmark] - public object GetGator() => q.GetGator(); - - [Benchmark] - public object GetHeikinAshi() => q.GetHeikinAshi(); - - [Benchmark] - public object GetHma() => q.GetHma(14); - - [Benchmark] - public object GetHtTrendline() => q.GetHtTrendline(); - - [Benchmark] - public object GetHurst() => q.GetHurst(); - - [Benchmark] - public object GetIchimoku() => q.GetIchimoku(); - - [Benchmark] - public object GetKama() => q.GetKama(); - - [Benchmark] - public object GetKlinger() => q.GetKvo(); - - [Benchmark] - public object GetKeltner() => q.GetKeltner(); - - [Benchmark] - public object GetKvo() => q.GetKvo(); - - [Benchmark] - public object GetMacd() => q.GetMacd(); - - [Benchmark] - public object GetMaEnvelopes() => q.GetMaEnvelopes(20, 2.5, MaType.SMA); - - [Benchmark] - public object GetMama() => q.GetMama(); - - [Benchmark] - public object GetMarubozu() => q.GetMarubozu(); - - [Benchmark] - public object GetMfi() => q.GetMfi(); - - [Benchmark] - public object GetObv() => q.GetObv(); - - [Benchmark] - public object GetObvWithSma() => q.GetObv(14); - - [Benchmark] - public object GetParabolicSar() => q.GetParabolicSar(); - - [Benchmark] - public object GetPivotPoints() => q.GetPivotPoints(PeriodSize.Month, PivotPointType.Standard); - - [Benchmark] - public object GetPivots() => q.GetPivots(); - - [Benchmark] - public object GetPmo() => q.GetPmo(); - - [Benchmark] - public object GetPrs() => q.GetPrs(o); - - [Benchmark] - public object GetPrsWithSma() => q.GetPrs(o, null, 5); - - [Benchmark] - public object GetPvo() => q.GetPvo(); - - [Benchmark] - public object GetRenko() => q.GetRenko(2.5m); - - [Benchmark] - public object GetRenkoAtr() => q.GetRenko(14); - - [Benchmark] - public object GetRoc() => q.GetRoc(20); - - [Benchmark] - public object GetRocWb() => q.GetRocWb(12, 3, 12); - - [Benchmark] - public object GetRocWithSma() => q.GetRoc(20, 14); - - [Benchmark] - public object GetRollingPivots() => q.GetRollingPivots(14, 1); - - [Benchmark] - public object GetRsi() => q.GetRsi(); - - [Benchmark] - public object GetSlope() => q.GetSlope(20); - - [Benchmark] - public object GetSma() => q.GetSma(10); - - [Benchmark] - public object GetSmaAnalysis() => q.GetSmaAnalysis(10); - - [Benchmark] - public object GetSmi() => q.GetSmi(5, 20, 5, 3); - - [Benchmark] - public object GetSmma() => q.GetSmma(10); - - [Benchmark] - public object GetStarcBands() => q.GetStarcBands(10); - - [Benchmark] - public object GetStc() => q.GetStc(); - - [Benchmark] - public object GetStdDev() => q.GetStdDev(20); - - [Benchmark] - public object GetStdDevWithSma() => q.GetStdDev(20, 14); - - [Benchmark] - public object GetStdDevChannels() => q.GetStdDevChannels(); - - [Benchmark] - public object GetStoch() => q.GetStoch(); - - [Benchmark] - public object GetStochSMMA() => q.GetStoch(9, 3, 3, 3, 2, MaType.SMMA); - - [Benchmark] - public object GetStochRsi() => q.GetStochRsi(14, 14, 3); - - [Benchmark] - public object GetSuperTrend() => q.GetSuperTrend(); - - [Benchmark] - public object GetT3() => q.GetT3(); - - [Benchmark] - public object GetTema() => q.GetTema(14); - - [Benchmark] - public object GetTr() => q.GetTr(); - - [Benchmark] - public object GetTrix() => q.GetTrix(14); - - [Benchmark] - public object GetTrixWithSma() => q.GetTrix(14, 5); - - [Benchmark] - public object GetTsi() => q.GetTsi(); - - [Benchmark] - public object GetUlcerIndex() => q.GetUlcerIndex(); - - [Benchmark] - public object GetUltimate() => q.GetUltimate(); - - [Benchmark] - public object GetVolatilityStop() => q.GetVolatilityStop(); - - [Benchmark] - public object GetVortex() => q.GetVortex(14); - - [Benchmark] - public object GetVwap() => q.GetVwap(); - - [Benchmark] - public object GetVwma() => q.GetVwma(14); - - [Benchmark] - public object GetWilliamsR() => q.GetWilliamsR(); - - [Benchmark] - public object GetWma() => q.GetWma(14); - - [Benchmark] - public object GetZigZag() => q.GetZigZag(); -} diff --git a/tests/performance/Perf.Indicators.Stream.cs b/tests/performance/Perf.Indicators.Stream.cs deleted file mode 100644 index 485ffc8dd..000000000 --- a/tests/performance/Perf.Indicators.Stream.cs +++ /dev/null @@ -1,62 +0,0 @@ -using BenchmarkDotNet.Attributes; -using Skender.Stock.Indicators; -using Tests.Common; - -namespace Tests.Performance; - -public class IndicatorsStreaming -{ - private static IEnumerable q; - private static List ql; - - // SETUP - - [GlobalSetup] - public void Setup() - { - q = TestData.GetDefault(); - ql = q.ToSortedList(); - } - - // BENCHMARKS - - [Benchmark] - public object GetEma() => q.GetEma(14); - - [Benchmark] - public object GetEmaStream() - { - // todo: refactor to exclude provider - QuoteProvider provider = new(); - EmaObserver observer = provider.GetEma(14); - - for (int i = 0; i < ql.Count; i++) - { - provider.Add(ql[i]); - } - - provider.EndTransmission(); - - return observer.Results; - } - - [Benchmark] - public object GetSma() => q.GetSma(10); - - [Benchmark] - public object GetSmaStream() - { - // todo: refactor to exclude provider - QuoteProvider provider = new(); - SmaObserver observer = provider.GetSma(10); - - for (int i = 0; i < ql.Count; i++) - { - provider.Add(ql[i]); - } - - provider.EndTransmission(); - - return observer.Results; - } -} diff --git a/tests/performance/Perf.StaticSeries.cs b/tests/performance/Perf.StaticSeries.cs new file mode 100644 index 000000000..6892ef6e4 --- /dev/null +++ b/tests/performance/Perf.StaticSeries.cs @@ -0,0 +1,277 @@ +namespace Performance; + +// TIME-SERIES INDICATORS + +[ShortRunJob] +public class SeriesIndicators +{ + private static readonly IReadOnlyList q = Data.GetDefault(); + private static readonly IReadOnlyList o = Data.GetCompare(); + + [Benchmark] + public object ToAdl() => q.ToAdl(); + + [Benchmark] + public object ToAdx() => q.ToAdx(); + + [Benchmark] + public object ToAlligator() => q.ToAlligator(); + + [Benchmark] + public object ToAlma() => q.ToAlma(); + + [Benchmark] + public object ToAroon() => q.ToAroon(); + + [Benchmark] + public object ToAtr() => q.ToAtr(); + + [Benchmark] + public object ToAtrStop() => q.ToAtrStop(); + + [Benchmark] + public object ToAwesome() => q.ToAwesome(); + + [Benchmark] + public object ToBeta() => Beta.ToBeta(q, o, 20, BetaType.Standard); + + [Benchmark] + public object ToBetaUp() => Beta.ToBeta(q, o, 20, BetaType.Up); + + [Benchmark] + public object ToBetaDown() => Beta.ToBeta(q, o, 20, BetaType.Down); + + [Benchmark] + public object ToBetaAll() => Beta.ToBeta(q, o, 20, BetaType.All); + + [Benchmark] + public object ToBollingerBands() => q.ToBollingerBands(); + + [Benchmark] + public object ToBop() => q.ToBop(); + + [Benchmark] + public object ToCci() => q.ToCci(); + + [Benchmark] + public object ToChaikinOsc() => q.ToChaikinOsc(); + + [Benchmark] + public object ToChandelier() => q.ToChandelier(); + + [Benchmark] + public object ToChop() => q.ToChop(); + + [Benchmark] + public object ToCmf() => q.ToCmf(); + + [Benchmark] + public object ToCmo() => q.ToCmo(14); + + [Benchmark] + public object ToConnorsRsi() => q.ToConnorsRsi(); + + [Benchmark] + public object ToCorrelation() => q.ToCorrelation(o, 20); + + [Benchmark] + public object ToDema() => q.ToDema(14); + + [Benchmark] + public object ToDoji() => q.ToDoji(); + + [Benchmark] + public object ToDonchian() => q.ToDonchian(); + + [Benchmark] + public object ToDpo() => q.ToDpo(14); + + [Benchmark] + public object ToDynamic() => q.ToDynamic(20); + + [Benchmark] + public object ToElderRay() => q.ToElderRay(); + + [Benchmark] + public object ToEma() => q.ToEma(14); + + [Benchmark] + public object ToEpma() => q.ToEpma(14); + + [Benchmark] + public object ToFcb() => q.ToFcb(14); + + [Benchmark] + public object ToFisherTransform() => q.ToFisherTransform(10); + + [Benchmark] + public object ToForceIndex() => q.ToForceIndex(13); + + [Benchmark] + public object ToFractal() => q.ToFractal(); + + [Benchmark] + public object ToGator() => q.ToGator(); + + [Benchmark] + public object ToHeikinAshi() => q.ToHeikinAshi(); + + [Benchmark] + public object ToHma() => q.ToHma(14); + + [Benchmark] + public object ToHtTrendline() => q.ToHtTrendline(); + + [Benchmark] + public object ToHurst() => q.ToHurst(); + + [Benchmark] + public object ToIchimoku() => q.ToIchimoku(); + + [Benchmark] + public object ToKama() => q.ToKama(); + + [Benchmark] + public object ToKlinger() => q.ToKvo(); + + [Benchmark] + public object ToKeltner() => q.ToKeltner(); + + [Benchmark] + public object ToKvo() => q.ToKvo(); + + [Benchmark] + public object ToMacd() => q.ToMacd(); + + [Benchmark] + public object ToMaEnvelopes() => q.ToMaEnvelopes(20, 2.5, MaType.SMA); + + [Benchmark] + public object ToMama() => q.ToMama(); + + [Benchmark] + public object ToMarubozu() => q.ToMarubozu(); + + [Benchmark] + public object ToMfi() => q.ToMfi(); + + [Benchmark] + public object ToObv() => q.ToObv(); + + [Benchmark] + public object ToParabolicSar() => q.ToParabolicSar(); + + [Benchmark] + public object ToPivotPoints() => q.ToPivotPoints(PeriodSize.Month, PivotPointType.Standard); + + [Benchmark] + public object ToPivots() => q.ToPivots(); + + [Benchmark] + public object ToPmo() => q.ToPmo(); + + [Benchmark] + public object ToPrs() => q.ToPrs(o); + + [Benchmark] + public object ToPvo() => q.ToPvo(); + + [Benchmark] + public object ToRenko() => q.ToRenko(2.5m); + + [Benchmark] + public object ToRenkoAtr() => q.ToRenko(14); + + [Benchmark] + public object ToRoc() => q.ToRoc(20); + + [Benchmark] + public object ToRocWb() => q.ToRocWb(12, 3, 12); + + [Benchmark] + public object ToRollingPivots() => q.ToRollingPivots(14, 1); + + [Benchmark] + public object ToRsi() => q.ToRsi(); + + [Benchmark] + public object ToSlope() => q.ToSlope(20); + + [Benchmark] + public object ToSma() => q.ToSma(10); + + [Benchmark] + public object ToSmaAnalysis() => q.ToSmaAnalysis(10); + + [Benchmark] + public object ToSmi() => q.ToSmi(5, 20, 5, 3); + + [Benchmark] + public object ToSmma() => q.ToSmma(10); + + [Benchmark] + public object ToStarcBands() => q.ToStarcBands(10); + + [Benchmark] + public object ToStc() => q.ToStc(); + + [Benchmark] + public object ToStdDev() => q.ToStdDev(20); + + [Benchmark] + public object ToStdDevChannels() => q.ToStdDevChannels(); + + [Benchmark] + public object ToStoch() => q.ToStoch(); + + [Benchmark] + public object ToStochSMMA() => q.ToStoch(9, 3, 3, 3, 2, MaType.SMMA); + + [Benchmark] + public object ToStochRsi() => q.ToStochRsi(14, 14, 3); + + [Benchmark] + public object ToSuperTrend() => q.ToSuperTrend(); + + [Benchmark] + public object ToT3() => q.ToT3(); + + [Benchmark] + public object ToTema() => q.ToTema(14); + + [Benchmark] + public object ToTr() => q.ToTr(); + + [Benchmark] + public object ToTrix() => q.ToTrix(14); + + [Benchmark] + public object ToTsi() => q.ToTsi(); + + [Benchmark] + public object ToUlcerIndex() => q.ToUlcerIndex(); + + [Benchmark] + public object ToUltimate() => q.ToUltimate(); + + [Benchmark] + public object ToVolatilityStop() => q.ToVolatilityStop(); + + [Benchmark] + public object ToVortex() => q.ToVortex(14); + + [Benchmark] + public object ToVwap() => q.ToVwap(); + + [Benchmark] + public object ToVwma() => q.ToVwma(14); + + [Benchmark] + public object ToWilliamsR() => q.ToWilliamsR(); + + [Benchmark] + public object ToWma() => q.ToWma(14); + + [Benchmark] + public object ToZigZag() => q.ToZigZag(); +} diff --git a/tests/performance/Perf.StreamHub.Externals.cs b/tests/performance/Perf.StreamHub.Externals.cs new file mode 100644 index 000000000..3a2dd4eb0 --- /dev/null +++ b/tests/performance/Perf.StreamHub.Externals.cs @@ -0,0 +1,42 @@ +namespace Performance; + +// STREAMING INDICATOR HUBS (EXTERNAL CACHE) + +[ShortRunJob] +public class StreamExternal +{ + private static readonly IReadOnlyList quotes + = Data.GetDefault(); + + private readonly QuoteHub provider = new(); + + /* SETUP/CLEANUP - runs before and after each. + * + * This Setup implies that each benchmark + * will start with a prepopulated observable + * QuoteHub provider. + * + * We do this because we want to measure + * the performance of observer methods + * without the overhead of the provider. */ + + [GlobalSetup] + public void Setup() => provider.Add(quotes); + + [GlobalCleanup] + public void Cleanup() + { + provider.EndTransmission(); + provider.Cache.Clear(); + } + + // BENCHMARKS + + // TODO: replace with external data cache model, when available + + [Benchmark(Baseline = true)] + public object EmaSeries() => quotes.ToEma(14); + + [Benchmark] + public object EmaStream() => provider.ToEma(14).Results; +} diff --git a/tests/performance/Perf.StreamHub.cs b/tests/performance/Perf.StreamHub.cs new file mode 100644 index 000000000..6ad618641 --- /dev/null +++ b/tests/performance/Perf.StreamHub.cs @@ -0,0 +1,78 @@ +namespace Performance; + +// STREAMING INDICATOR HUBS + +[ShortRunJob] +public class StreamIndicators +{ + private static readonly IReadOnlyList quotes + = Data.GetDefault(); + + private readonly QuoteHub provider = new(); // prepopulated + private readonly QuoteHub supplier = new(); // empty + + /* SETUP/CLEANUP - runs before and after each. + * + * This Setup implies that each benchmark + * will start with a prepopulated observable + * QuoteHub provider. + * + * We do this because we want to measure + * the performance of observer methods + * without the overhead of the provider. */ + + [GlobalSetup] + public void Setup() => provider.Add(quotes); + + [GlobalCleanup] + public void Cleanup() + { + provider.EndTransmission(); + provider.Cache.Clear(); + } + + // BENCHMARKS + + [Benchmark] + public object AdlHub() => provider.ToAdl().Results; + + [Benchmark] + public object AtrHub() => provider.ToAtr(14).Results; + + [Benchmark] + public object AtrStopHub() => provider.ToAtrStop().Results; + + [Benchmark] + public object AlligatorHub() => provider.ToAlligator().Results; + + [Benchmark] + public object EmaHub() => provider.ToEma(14).Results; + + [Benchmark] + public object EmaHub2() + { + EmaHub observer = supplier.ToEma(14); + + for (int i = 0; i < quotes.Count; i++) + { + observer.OnAdd(quotes[i], notify: false, i); + } + + return observer.Results; + } + + [Benchmark] + public object QuoteHub() => provider.ToQuote().Results; + + [Benchmark] + public object QuotePartHub() => provider.ToQuotePart(CandlePart.OHL3).Results; + + [Benchmark] + public object RenkoHub() => provider.ToRenko(2.5m).Results; + + [Benchmark] + public object SmaHub() => provider.ToSma(10).Results; + + [Benchmark] + public object TrHub() => provider.ToTr().Results; +} diff --git a/tests/performance/Perf.Internals.cs b/tests/performance/Perf.Utility.Maths.cs similarity index 55% rename from tests/performance/Perf.Internals.cs rename to tests/performance/Perf.Utility.Maths.cs index 461dbab29..a8f4acab7 100644 --- a/tests/performance/Perf.Internals.cs +++ b/tests/performance/Perf.Utility.Maths.cs @@ -1,22 +1,22 @@ -namespace Tests.Performance; +namespace Performance; -// INTERNAL FUNCTIONS +// INTERNAL UTILITIES [ShortRunJob] -public class InternalsPerformance +public class UtilityMaths { [Params(20, 50, 250, 1000)] public int Periods; - private double[] values; + private double[] _values; // standard deviation [GlobalSetup(Targets = [nameof(StdDev)])] public void Setup() - => values = TestData.GetLongish(Periods) + => _values = Data.GetLongish(Periods) .Select(x => (double)x.Close) .ToArray(); [Benchmark] - public object StdDev() => values.StdDev(); + public object StdDev() => _values.StdDev(); } diff --git a/tests/performance/Perf.Utility.cs b/tests/performance/Perf.Utility.cs new file mode 100644 index 000000000..eb1401be1 --- /dev/null +++ b/tests/performance/Perf.Utility.cs @@ -0,0 +1,31 @@ +namespace Performance; + +// INTERNAL UTILITIES + +[ShortRunJob] +public class Utility +{ + private static readonly IReadOnlyList q = Data.GetDefault(); + private static readonly IReadOnlyList i = Data.GetIntraday(); + + [Benchmark] + public object ToSortedList() => q.ToSortedList(); + + [Benchmark] + public object ToListQuoteD() => q.ToQuoteDList(); + + [Benchmark] + public object ToReusableClose() => q.ToReusableList(CandlePart.Close); + + [Benchmark] + public object ToReusableOhlc4() => q.ToReusableList(CandlePart.OHLC4); + + [Benchmark] + public object ToCandleResults() => q.ToCandles(); + + [Benchmark] + public object Validate() => q.Validate(); + + [Benchmark] + public object Aggregate() => i.Aggregate(PeriodSize.FifteenMinutes); +} diff --git a/tests/performance/Program.cs b/tests/performance/Program.cs index 0c6b90d6b..068dd6fe8 100644 --- a/tests/performance/Program.cs +++ b/tests/performance/Program.cs @@ -2,7 +2,7 @@ [assembly: CLSCompliant(false)] -namespace Tests.Performance; +namespace Performance; public static class Program { @@ -21,4 +21,13 @@ public static void Main(string[] args) BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); } } + + /* USAGE + * + * dotnet build -c Release + * + * Examples, to run cohorts: + * dotnet run -c Release -filter *Stream* + * dotnet run -c Release -filter *External.EmaHub* + */ } diff --git a/tests/performance/Tests.Performance.csproj b/tests/performance/Tests.Performance.csproj index e2f0d910d..43bc26589 100644 --- a/tests/performance/Tests.Performance.csproj +++ b/tests/performance/Tests.Performance.csproj @@ -2,8 +2,14 @@ net8.0 - enable Exe + Performance.Program + + false + None + true + + enable true latest @@ -19,24 +25,7 @@ - - - - - Always - - - Always - - - Always - - - Always - - - Always - + diff --git a/tests/performance/helpers/Helper.Getter.cs b/tests/performance/helpers/Helper.Getter.cs deleted file mode 100644 index c82c64dcd..000000000 --- a/tests/performance/helpers/Helper.Getter.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace Tests.Common; - -// IMPORT TEST DATA -internal static class TestData -{ - // DEFAULT: S&P 500 ~2 years of daily data - internal static IEnumerable GetDefault(int days = 502) - => File.ReadAllLines("helpers/data/default.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date) - .Take(days) - .ToList(); - - // COMPARE DATA ~2 years of TSLA data (matches default time) - internal static IEnumerable GetCompare(int days = 502) - => File.ReadAllLines("helpers/data/compare.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date) - .Take(days) - .ToList(); - - // INTRADAY DATA - internal static IEnumerable GetIntraday(int days = 1564) - => File.ReadAllLines("helpers/data/intraday.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date) - .Take(days) - .ToList(); - - // LONGEST DATA ~62 years of S&P 500 daily data - internal static IEnumerable GetLongest() - => File.ReadAllLines("helpers/data/longest.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .ToList(); - - // LONGISH DATA ~20 years of S&P 500 daily data - internal static IEnumerable GetLongish(int days = 5285) - => File.ReadAllLines("helpers/data/longish.csv") - .Skip(1) - .Select(Importer.QuoteFromCsv) - .OrderByDescending(x => x.Date) - .Take(days) - .ToList(); -} diff --git a/tests/performance/helpers/Helper.Importer.cs b/tests/performance/helpers/Helper.Importer.cs deleted file mode 100644 index b88006ca4..000000000 --- a/tests/performance/helpers/Helper.Importer.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Globalization; - -namespace Tests.Common; - -// TEST QUOTE IMPORTER -internal static class Importer -{ - private static readonly CultureInfo EnglishCulture = new("en-US", false); - - // importer / parser - internal static Quote QuoteFromCsv(string csvLine) - { - if (string.IsNullOrEmpty(csvLine)) - { - return new Quote(); - } - - string[] values = csvLine.Split(','); - Quote quote = new(); - - HandleOHLCV(quote, "D", values[0]); - HandleOHLCV(quote, "O", values[1]); - HandleOHLCV(quote, "H", values[2]); - HandleOHLCV(quote, "L", values[3]); - HandleOHLCV(quote, "C", values[4]); - HandleOHLCV(quote, "V", values[5]); - - return quote; - } - - internal static decimal ToDecimal(this string value) - => decimal.TryParse(value, out decimal d) ? d - : throw new NotFiniteNumberException( - $"Cannot convert `{value}`, it is not a number."); - - internal static decimal? ToDecimalNull(this string value) - => decimal.TryParse(value, out decimal d) ? d : null; - - internal static double? ToDoubleNull(this string value) - => double.TryParse(value, out double d) ? d : null; - - private static void HandleOHLCV(Quote quote, string position, string value) - { - if (string.IsNullOrEmpty(value)) - { - return; - } - - switch (position) - { - case "D": - quote.Date = Convert.ToDateTime(value, EnglishCulture); - break; - case "O": - quote.Open = Convert.ToDecimal(value, EnglishCulture); - break; - case "H": - quote.High = Convert.ToDecimal(value, EnglishCulture); - break; - case "L": - quote.Low = Convert.ToDecimal(value, EnglishCulture); - break; - case "C": - quote.Close = Convert.ToDecimal(value, EnglishCulture); - break; - case "V": - quote.Volume = Convert.ToDecimal(value, EnglishCulture); - break; - default: - throw new ArgumentOutOfRangeException(nameof(position)); - } - } -} diff --git a/tests/simulate/Program.cs b/tests/simulate/Program.cs new file mode 100644 index 000000000..38e5a2c32 --- /dev/null +++ b/tests/simulate/Program.cs @@ -0,0 +1,37 @@ +using Skender.Stock.Indicators; +using Utilities; + +// define simulated quotes, arrival rate +int quotesPerMinute = 600; +int quantityToStream = 75; + +List quotes + = Util.Setup(quantityToStream, quotesPerMinute); + +// initialize quote provider + +QuoteHub provider = new(); + +// subscribe indicator hubs (SMA, EMA, etc.) + +SmaHub smaHub = provider.ToSma(3); +EmaHub emaHub = provider.ToEma(5); +EmaHub useChain = provider.ToQuotePart(CandlePart.HL2).ToEma(7); +EmaHub emaChain = provider.ToSma(4).ToEma(4); // chainable + +/* normally, you'd plugin your WebSocket here + * and use `provider.Add(q);` to connect the streams */ + +// simulate streaming quotes + +for (int i = 0; i < quantityToStream; i++) +{ + Quote quote = quotes[i]; + provider.Add(quote); // on arrival from external WebSocket + + // govern simulation rate + Thread.Sleep(60000 / quotesPerMinute); + + // send output to console + Util.PrintData(quote, smaHub, emaHub, useChain, emaChain); +} diff --git a/tests/observe/Observe.Streaming.csproj b/tests/simulate/Test.Simulation.csproj similarity index 75% rename from tests/observe/Observe.Streaming.csproj rename to tests/simulate/Test.Simulation.csproj index a6edaccf3..914048054 100644 --- a/tests/observe/Observe.Streaming.csproj +++ b/tests/simulate/Test.Simulation.csproj @@ -4,12 +4,9 @@ Exe net8.0 enable + enable - - - - diff --git a/tests/simulate/Utilities.cs b/tests/simulate/Utilities.cs new file mode 100644 index 000000000..941f5a659 --- /dev/null +++ b/tests/simulate/Utilities.cs @@ -0,0 +1,140 @@ +using Skender.Stock.Indicators; + +namespace Utilities; + +internal static class Util +{ + internal static List Setup(int quantityToStream, int quotesPerMinute) + { + Console.WriteLine($"Simulating {quotesPerMinute:N0} quotes per minute"); + PrintHeader(); + + return new RandomGbm(bars: quantityToStream); + } + + internal static void PrintHeader() + { + // dislay header + Console.WriteLine(); + Console.WriteLine(""" + Date Close price SMA(3) EMA(5) EMA(7,HL2) SMA/EMA(8) + ------------------------------------------------------------------------------ + """); + } + + internal static void PrintData( + Quote q, + SmaHub smaHub, + EmaHub emaHub, + EmaHub useChain, + EmaHub emaChain) + { + // send output to console + string m = $"{q.Timestamp:yyyy-MM-dd HH:mm} {q.Close,11:N2}"; + + SmaResult s = smaHub.Results[^1]; + EmaResult e = emaHub.Results[^1]; + EmaResult u = useChain.Results[^1]; + EmaResult c = emaChain.Results[^1]; + + if (s.Sma is not null) + { + m += $"{s.Sma,12:N1}"; + } + + if (e.Ema is not null) + { + m += $"{e.Ema,12:N1}"; + } + + if (u.Ema is not null) + { + m += $"{u.Ema,12:N1}"; + } + + if (c.Ema is not null) + { + m += $"{c.Ema,12:N1}"; + } + + Console.WriteLine(m); + } +} + +///

    +/// Geometric Brownian Motion (GMB) is a random simulator of market movement. +/// GBM can be used for testing indicators, validation and Monte Carlo simulations of strategies. +/// +/// +/// Sample usage: +/// +/// RandomGbm data = new(); // generates 1 year (252) list of bars +/// RandomGbm data = new(Bars: 1000); // generates 1,000 bars +/// RandomGbm data = new(Bars: 252, Volatility: 0.05, Drift: 0.0005, Seed: 100.0) +/// +/// Parameters: +/// +/// Bars: number of bars (quotes) requested +/// Volatility: how dymamic/volatile the series should be; default is 1 +/// Drift: incremental drift due to annual interest rate; default is 5% +/// Seed: starting value of the random series; should not be 0. +/// + +internal class RandomGbm : List +{ + private readonly double _volatility; + private readonly double _drift; + private double _seed; + + internal RandomGbm( + int bars = 250, + double volatility = 1.0, + double drift = 0.01, + double seed = 1000.0) + { + _seed = seed; + _volatility = volatility * 0.01; + _drift = drift * 0.001; + for (int i = 0; i < bars; i++) + { + DateTime date = DateTime.Today.AddMinutes(i - bars); + Add(date); + } + } + + public void Add(DateTime timestamp) + { + double open = Price(_seed, _volatility * _volatility, _drift); + double close = Price(open, _volatility, _drift); + + double ocMax = Math.Max(open, close); + double high = Price(_seed, _volatility * 0.5, 0); + high = high < ocMax ? (2 * ocMax) - high : high; + + double ocMin = Math.Min(open, close); + double low = Price(_seed, _volatility * 0.5, 0); + low = low > ocMin ? (2 * ocMin) - low : low; + + double volume = Price(_seed * 10, _volatility * 2, drift: 0); + + Quote quote = new( + Timestamp: timestamp, + Open: (decimal)open, + High: (decimal)high, + Low: (decimal)low, + Close: (decimal)close, + Volume: (decimal)volume); + + Add(quote); + _seed = close; + } + + private static double Price(double seed, double volatility, double drift) + { + Random rnd = new((int)DateTime.UtcNow.Ticks); + double u1 = 1.0 - rnd.NextDouble(); + double u2 = 1.0 - rnd.NextDouble(); + double z = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); + return seed * Math.Exp(drift - (volatility * volatility * 0.5) + (volatility * z)); + } +} From 070a00ecbcc87b0596c92e52c905ff93c63a2801 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Sun, 3 Nov 2024 00:07:27 -0400 Subject: [PATCH 10/18] test: Isolate integration, external tests (#1156) Signed-off-by: Dave Skender <8432125+DaveSkender@users.noreply.github.com> --- .github/workflows/test-indicators.yml | 36 ++------ .github/workflows/test-integration.yml | 58 +++++++++++++ Stock.Indicators.sln | 36 ++++---- docs/contributing.md | 2 +- src/_common/Quotes/Quote.Converters.cs | 4 +- src/s-z/Sma/Sma.Utilities.cs | 13 +-- tests/README.md | 80 ++++++++++++++++++ tests/application/Test.Application.csproj | 19 ----- .../application/GlobalUsings.cs | 0 tests/{ => external}/application/Program.cs | 24 +++--- tests/{ => external}/application/README.md | 0 .../application/Test.Application.csproj | 28 +++++++ .../application/_testdata/TestData.Getter.cs | 0 .../application/_testdata/TestData.Imports.cs | 0 .../application/_testdata/data/compare.csv | 0 .../application/_testdata/data/default.csv | 0 .../application/_testdata/data/intraday.csv | 0 .../application/_testdata/data/longest.csv | 0 .../application/_testdata/data/longish.csv | 0 tests/external/integration/GlobalUsings.cs | 3 + .../integration/Tests.Integration.csproj} | 17 ++-- tests/external/integration/WilliamsR.Tests.cs | 40 +++++++++ .../integration/_common/Helper.LiveQuotes.cs | 75 +++++++++++++++++ .../public-api}/Convergence.Tests.cs | 2 +- .../public-api}/GlobalUsings.cs | 0 .../public-api/Tests.PublicApi.csproj | 36 ++++++++ .../customizable}/Custom.Indicator.Tests.cs | 2 +- .../customizable}/Custom.Quotes.Tests.cs | 2 +- .../customizable}/Custom.Results.Tests.cs | 11 +-- .../customizable}/Sut.CustomItems.cs | 0 .../indicators}/PublicApi.Interface.Tests.cs | 1 + tests/indicators/README.md | 82 +++++++++++++++++++ tests/indicators/TestBase.cs | 2 +- tests/indicators/Tests.Indicators.csproj | 10 ++- .../_common/Generics/BinarySettingsTests.cs | 21 +++++ .../_common/Generics/Transforms.Tests.cs | 2 +- .../Observables/StreamHub.CacheMgmt.Tests.cs | 9 -- .../_common/Quotes/Quote.Converters.Tests.cs | 62 +++++++++++++- .../QuotePart.Utilities.Tests.cs | 12 ++- .../s-z/Sma/Sma.StaticSeries.Tests.cs | 2 +- .../indicators/s-z/Sma/Sma.Utilities.Tests.cs | 23 ++++++ tests/performance/Tests.Performance.csproj | 9 +- tests/simulate/Test.Simulation.csproj | 8 ++ tests/simulate/Utilities.cs | 15 +++- tests/tests.integration.runsettings | 5 ++ tests/tests.unit.runsettings | 5 ++ 46 files changed, 625 insertions(+), 131 deletions(-) create mode 100644 .github/workflows/test-integration.yml create mode 100644 tests/README.md delete mode 100644 tests/application/Test.Application.csproj rename tests/{ => external}/application/GlobalUsings.cs (100%) rename tests/{ => external}/application/Program.cs (91%) rename tests/{ => external}/application/README.md (100%) create mode 100644 tests/external/application/Test.Application.csproj rename tests/{ => external}/application/_testdata/TestData.Getter.cs (100%) rename tests/{ => external}/application/_testdata/TestData.Imports.cs (100%) rename tests/{ => external}/application/_testdata/data/compare.csv (100%) rename tests/{ => external}/application/_testdata/data/default.csv (100%) rename tests/{ => external}/application/_testdata/data/intraday.csv (100%) rename tests/{ => external}/application/_testdata/data/longest.csv (100%) rename tests/{ => external}/application/_testdata/data/longish.csv (100%) create mode 100644 tests/external/integration/GlobalUsings.cs rename tests/{other/Tests.Other.csproj => external/integration/Tests.Integration.csproj} (77%) create mode 100644 tests/external/integration/WilliamsR.Tests.cs create mode 100644 tests/external/integration/_common/Helper.LiveQuotes.cs rename tests/{other => external/public-api}/Convergence.Tests.cs (99%) rename tests/{other => external/public-api}/GlobalUsings.cs (100%) create mode 100644 tests/external/public-api/Tests.PublicApi.csproj rename tests/{other => external/public-api/customizable}/Custom.Indicator.Tests.cs (99%) rename tests/{other => external/public-api/customizable}/Custom.Quotes.Tests.cs (99%) rename tests/{other => external/public-api/customizable}/Custom.Results.Tests.cs (86%) rename tests/{other => external/public-api/customizable}/Sut.CustomItems.cs (100%) rename tests/{other => external/public-api/indicators}/PublicApi.Interface.Tests.cs (99%) create mode 100644 tests/indicators/README.md create mode 100644 tests/indicators/s-z/Sma/Sma.Utilities.Tests.cs create mode 100644 tests/tests.integration.runsettings create mode 100644 tests/tests.unit.runsettings diff --git a/.github/workflows/test-indicators.yml b/.github/workflows/test-indicators.yml index a5a0932a2..ada36f373 100644 --- a/.github/workflows/test-indicators.yml +++ b/.github/workflows/test-indicators.yml @@ -2,7 +2,7 @@ name: Indicators on: push: - branches: ["main"] + branches: ["main","v3"] pull_request: types: [opened, synchronize, reopened] @@ -27,29 +27,17 @@ jobs: # identifying primary configuration so only one reports coverage IS_PRIMARY: ${{ matrix.os == 'ubuntu-latest' && matrix.dotnet-version == '8.x' }} - # .NET SDK versions in the matrix that support `ga` quality spec - # versions before 5.x do not support it - SUPPORT_GA: ${{ contains(fromJson('["6.x", "8.x"]'), matrix.dotnet-version) }} - steps: - name: Checkout source uses: actions/checkout@v4 - name: Setup .NET - id: dotnet-new uses: actions/setup-dotnet@v4 - if: env.SUPPORT_GA == 'true' with: dotnet-version: ${{ matrix.dotnet-version }} dotnet-quality: "ga" - - name: Setup .NET (older) - uses: actions/setup-dotnet@v4 - if: env.SUPPORT_GA == 'false' - with: - dotnet-version: ${{ matrix.dotnet-version }} - - name: Build library run: > dotnet build @@ -58,36 +46,24 @@ jobs: -warnAsError - name: Test indicators - env: - ALPACA_KEY: ${{ secrets.ALPACA_KEY }} - ALPACA_SECRET: ${{ secrets.ALPACA_SECRET }} run: > - dotnet test tests/indicators/Tests.Indicators.csproj + dotnet test --configuration Release + --settings tests/tests.unit.runsettings + --results-directory ./test-results --no-build --verbosity normal --logger trx --collect:"XPlat Code Coverage" - --results-directory ./test-indicators # the remaining steps are only needed from one primary instance - - name: Test other items - if: env.IS_PRIMARY == 'true' - run: > - dotnet test tests/other/Tests.Other.csproj - --configuration Release - --no-build - --verbosity normal - --logger trx - --results-directory ./test-other - - name: Post test summary uses: dorny/test-reporter@v1 if: env.IS_PRIMARY == 'true' && always() with: name: Test results - path: ./test-indicators/**/*.trx + path: ./test-results/**/*.trx reporter: dotnet-trx - name: Publish coverage to Codacy @@ -95,4 +71,4 @@ jobs: if: env.IS_PRIMARY == 'true' with: project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} - coverage-reports: ./test-indicators/**/coverage.cobertura.xml + coverage-reports: ./test-results/**/coverage.cobertura.xml diff --git a/.github/workflows/test-integration.yml b/.github/workflows/test-integration.yml new file mode 100644 index 000000000..6da929b33 --- /dev/null +++ b/.github/workflows/test-integration.yml @@ -0,0 +1,58 @@ +name: Indicators + +on: + push: + branches: ["main","v3"] + + pull_request: + types: [opened, synchronize, reopened] + +jobs: + test: + name: integration tests + runs-on: ubuntu-latest + + permissions: + contents: read + actions: read + checks: write + + steps: + + - name: Checkout source + uses: actions/checkout@v4 + + - name: Setup .NET + id: dotnet-new + uses: actions/setup-dotnet@v4 + with: + dotnet-version: "8.x" + dotnet-quality: "ga" + + - name: Build library + run: > + dotnet build + --configuration Release + --property:ContinuousIntegrationBuild=true + -warnAsError + + - name: Test integrations + env: + ALPACA_KEY: ${{ secrets.ALPACA_KEY }} + ALPACA_SECRET: ${{ secrets.ALPACA_SECRET }} + run: > + dotnet test + --configuration Release + --settings tests/tests.integration.runsettings + --results-directory ./test-results + --no-build + --verbosity normal + --logger trx + + - name: Post test summary + uses: dorny/test-reporter@v1 + if: always() + with: + name: Test results + path: ./test-results/**/*.trx + reporter: dotnet-trx diff --git a/Stock.Indicators.sln b/Stock.Indicators.sln index 8d241723d..2241c476b 100644 --- a/Stock.Indicators.sln +++ b/Stock.Indicators.sln @@ -9,24 +9,24 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Configuration", "Configurat .gitattributes = .gitattributes .gitignore = .gitignore src\gitversion.yml = src\gitversion.yml + tests\tests.integration.runsettings = tests\tests.integration.runsettings + tests\tests.unit.runsettings = tests\tests.unit.runsettings EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Indicators", "src\Indicators.csproj", "{8D0F1781-EDA3-4C51-B05D-D33FF1156E49}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Indicators", "tests\indicators\Tests.Indicators.csproj", "{11CD6C7E-871F-4903-AEAD-58E034C6521D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Other", "tests\other\Tests.Other.csproj", "{97905D26-4854-41FF-A4F7-CE042B2ACD02}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Performance", "tests\performance\Tests.Performance.csproj", "{3BD4837B-D197-41FD-A286-A3256D0770E1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.Application", "tests\application\Test.Application.csproj", "{14DEC3AF-9AF2-4A66-8BEE-C342C6CC4307}" - ProjectSection(ProjectDependencies) = postProject - {11CD6C7E-871F-4903-AEAD-58E034C6521D} = {11CD6C7E-871F-4903-AEAD-58E034C6521D} - {8D0F1781-EDA3-4C51-B05D-D33FF1156E49} = {8D0F1781-EDA3-4C51-B05D-D33FF1156E49} - EndProjectSection -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.Simulation", "tests\simulate\Test.Simulation.csproj", "{9C9045D2-9928-41F8-97FC-ECCBDD3B9868}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.Application", "tests\external\application\Test.Application.csproj", "{F98B09DA-0E0B-41D1-B7B6-3A40EE6C1DBE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Integration", "tests\external\integration\Tests.Integration.csproj", "{88C59340-F6C6-497B-A7F3-08ACCC8873EE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.PublicApi", "tests\external\public-api\Tests.PublicApi.csproj", "{D6CF664F-8232-4EE9-B044-CA34A0BA522E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -41,22 +41,26 @@ Global {11CD6C7E-871F-4903-AEAD-58E034C6521D}.Debug|Any CPU.Build.0 = Debug|Any CPU {11CD6C7E-871F-4903-AEAD-58E034C6521D}.Release|Any CPU.ActiveCfg = Release|Any CPU {11CD6C7E-871F-4903-AEAD-58E034C6521D}.Release|Any CPU.Build.0 = Release|Any CPU - {97905D26-4854-41FF-A4F7-CE042B2ACD02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {97905D26-4854-41FF-A4F7-CE042B2ACD02}.Debug|Any CPU.Build.0 = Debug|Any CPU - {97905D26-4854-41FF-A4F7-CE042B2ACD02}.Release|Any CPU.ActiveCfg = Release|Any CPU - {97905D26-4854-41FF-A4F7-CE042B2ACD02}.Release|Any CPU.Build.0 = Release|Any CPU {3BD4837B-D197-41FD-A286-A3256D0770E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3BD4837B-D197-41FD-A286-A3256D0770E1}.Debug|Any CPU.Build.0 = Debug|Any CPU {3BD4837B-D197-41FD-A286-A3256D0770E1}.Release|Any CPU.ActiveCfg = Release|Any CPU {3BD4837B-D197-41FD-A286-A3256D0770E1}.Release|Any CPU.Build.0 = Release|Any CPU - {14DEC3AF-9AF2-4A66-8BEE-C342C6CC4307}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {14DEC3AF-9AF2-4A66-8BEE-C342C6CC4307}.Debug|Any CPU.Build.0 = Debug|Any CPU - {14DEC3AF-9AF2-4A66-8BEE-C342C6CC4307}.Release|Any CPU.ActiveCfg = Release|Any CPU - {14DEC3AF-9AF2-4A66-8BEE-C342C6CC4307}.Release|Any CPU.Build.0 = Release|Any CPU {9C9045D2-9928-41F8-97FC-ECCBDD3B9868}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9C9045D2-9928-41F8-97FC-ECCBDD3B9868}.Debug|Any CPU.Build.0 = Debug|Any CPU {9C9045D2-9928-41F8-97FC-ECCBDD3B9868}.Release|Any CPU.ActiveCfg = Release|Any CPU {9C9045D2-9928-41F8-97FC-ECCBDD3B9868}.Release|Any CPU.Build.0 = Release|Any CPU + {F98B09DA-0E0B-41D1-B7B6-3A40EE6C1DBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F98B09DA-0E0B-41D1-B7B6-3A40EE6C1DBE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F98B09DA-0E0B-41D1-B7B6-3A40EE6C1DBE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F98B09DA-0E0B-41D1-B7B6-3A40EE6C1DBE}.Release|Any CPU.Build.0 = Release|Any CPU + {88C59340-F6C6-497B-A7F3-08ACCC8873EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {88C59340-F6C6-497B-A7F3-08ACCC8873EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {88C59340-F6C6-497B-A7F3-08ACCC8873EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {88C59340-F6C6-497B-A7F3-08ACCC8873EE}.Release|Any CPU.Build.0 = Release|Any CPU + {D6CF664F-8232-4EE9-B044-CA34A0BA522E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6CF664F-8232-4EE9-B044-CA34A0BA522E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6CF664F-8232-4EE9-B044-CA34A0BA522E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6CF664F-8232-4EE9-B044-CA34A0BA522E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/docs/contributing.md b/docs/contributing.md index 4524cb9fe..4b08b08f2 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -66,7 +66,7 @@ Running the `Tests.Performance` console application in `Release` mode will produ dotnet run -c Release # run individual performance benchmark -dotnet run -c Release --filter *.GetAdx +dotnet run -c Release --filter *.ToAdx ``` ## Documentation diff --git a/src/_common/Quotes/Quote.Converters.cs b/src/_common/Quotes/Quote.Converters.cs index 0a03e5e13..7db6c964c 100644 --- a/src/_common/Quotes/Quote.Converters.cs +++ b/src/_common/Quotes/Quote.Converters.cs @@ -6,7 +6,7 @@ public static partial class Quotes { /* LISTS */ - // convert TQuote type list to built-in Quote type list + // convert TQuote type list to built-in Quote type list (public API only) public static IReadOnlyList ToQuoteList( this IReadOnlyList quotes) where TQuote : IQuote @@ -27,7 +27,7 @@ internal static List ToQuoteDList( /* TYPES */ - // convert any IQuote type to native Quote type + // convert any IQuote type to native Quote type (public API only) public static Quote ToQuote(this TQuote quote) where TQuote : IQuote diff --git a/src/s-z/Sma/Sma.Utilities.cs b/src/s-z/Sma/Sma.Utilities.cs index f8700d185..fe202fc86 100644 --- a/src/s-z/Sma/Sma.Utilities.cs +++ b/src/s-z/Sma/Sma.Utilities.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Numerics; namespace Skender.Stock.Indicators; @@ -22,19 +23,20 @@ public static partial class Sma /// if incalculable /// values are in range. /// - internal static double? Average( + public static double? Average( // public API only this IReadOnlyList values, int lookbackPeriods, int? endIndex = null) where T : IReusable + { + ArgumentNullException.ThrowIfNull(values); - // TODO: unused SMA utility, either make public or remove - - => Increment( + return Increment( values, lookbackPeriods, endIndex ?? values.Count - 1) .NaN2Null(); + } /// /// Simple moving average calculation @@ -69,9 +71,10 @@ internal static double Increment( // TODO: apply this SMA increment method more widely in other indicators (see EMA example) } + [ExcludeFromCodeCoverage] // experimental SIMD code internal static double[] Increment(this double[] prices, int period) { - // TODO: is this used (probably just an experiment, has rounding errors) + // TODO: remove/consider experiment, has rounding errors int count = prices.Length - period + 1; double[] sma = new double[count]; diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 000000000..321dcb53d --- /dev/null +++ b/tests/README.md @@ -0,0 +1,80 @@ +# Testing + +Tests are split into different projects for isolation of purpose. + +```bash +# runs all unit +# and integration tests +dotnet test +``` + +> When developing locally, we recommend that you normally _unload_ the external test projects shown below, except when testing externalities. + +## Unit tests + +> `indicators/Tests.Indicators.csproj` unit tests library + +Our primary full unit test project covers the entire utility of the library. In most IDE, you can [manually select](https://learn.microsoft.com/en-us/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file?view=vs-2022#manually-select-the-run-settings-file) the `tests/tests.unit.runsettings` for isolation for local IDE dev/test efficiency, or use the _unload_ approach described above. + +```bash +# CLI equivalent +dotnet test --settings tests/tests.unit.runsettings +``` + +## Performance tests + +> `tests/performance/Tests.Performance.csproj` benchmark tests + +Running the `Tests.Performance` console application in `Release` mode will produce [benchmark performance data](https://dotnet.stockindicators.dev/performance/) that we include on our documentation site. + +```bash +# run all performance benchmarks (~15-20 minutes) +dotnet run -c Release + +# run individual performance benchmark +dotnet run -c Release --filter *.ToAdx + +# run cohorts of performance benchmarks +dotnet run -c Release --filter ** +``` + +```bash +# to see all cohorts +dotnet run --list +... +# Available Benchmarks: + #0 Incrementals + #1 SeriesIndicators + #2 StreamExternal + #3 StreamIndicators + #4 Utility + #5 UtilityMaths + +# to see all tests +dotnet run --list flat +``` + +## External tests + +All external integration and API tests can be run with one CLI + +```bash +# CLI equivalent +dotnet test --settings tests/tests.integration.runsettings +``` + +Since we assume tests are non-integration tests by default, set the category attribute on any new test classes that contain integration tests. This can be applied uniquely to `[TestMethod]` as well. + +```csharp +[TestClass, TestCategory("Integration")] +public class MyIntegrationTests : TestBase +... +``` + +### Public API tests + +> `external/public-api/Tests.PublicApi.csproj` E2E libary external tests + +### Integration tests + +> `external/integration/Tests.Integration.csproj` connected to Live 3rd-party API diff --git a/tests/application/Test.Application.csproj b/tests/application/Test.Application.csproj deleted file mode 100644 index a727a0290..000000000 --- a/tests/application/Test.Application.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - Exe - net8.0 - enable - - - - - - - - - Always - - - - diff --git a/tests/application/GlobalUsings.cs b/tests/external/application/GlobalUsings.cs similarity index 100% rename from tests/application/GlobalUsings.cs rename to tests/external/application/GlobalUsings.cs diff --git a/tests/application/Program.cs b/tests/external/application/Program.cs similarity index 91% rename from tests/application/Program.cs rename to tests/external/application/Program.cs index 620267b46..c5a305fee 100644 --- a/tests/application/Program.cs +++ b/tests/external/application/Program.cs @@ -1,6 +1,6 @@ namespace Test.Application; -internal class Program +public class Program { private static void Main(string[] args) { @@ -11,18 +11,20 @@ private static void Main(string[] args) string scenario = "C"; + Go go = new(); + switch (scenario) { - case "A": Do.QuoteHub(); break; - case "B": Do.EmaHub(); break; - case "C": Do.MultipleSubscribers(); break; + case "A": go.QuoteHub(); break; + case "B": go.EmaHub(); break; + case "C": go.MultipleSubscribers(); break; } } } -public class Do +public class Go { - private static readonly bool verbose = true; // turn this off when profiling + private readonly bool verbose = true; private static readonly QuoteHub provider = new(); @@ -30,9 +32,9 @@ public class Do private static readonly int quotesLength = quotesList.Count; - internal Do() + public Go() { - if (!verbose) + if (verbose) { Prefill(); } @@ -47,7 +49,7 @@ private static void Prefill() } } - internal static void QuoteHub() + internal void QuoteHub() { EmaHub emaHub = provider.ToEma(14); @@ -83,7 +85,7 @@ private static void SendToConsole(Quote q) } - internal static void EmaHub() + internal void EmaHub() { EmaHub emaHub = provider.ToEma(14); @@ -131,7 +133,7 @@ private static void SendToConsole(Quote q, EmaHub emaHub) Console.WriteLine(m); } - internal static void MultipleSubscribers() + internal void MultipleSubscribers() { SmaHub smaHub = provider.ToSma(3); EmaHub emaHub = provider.ToEma(5); diff --git a/tests/application/README.md b/tests/external/application/README.md similarity index 100% rename from tests/application/README.md rename to tests/external/application/README.md diff --git a/tests/external/application/Test.Application.csproj b/tests/external/application/Test.Application.csproj new file mode 100644 index 000000000..86cd6da93 --- /dev/null +++ b/tests/external/application/Test.Application.csproj @@ -0,0 +1,28 @@ + + + + Exe + net8.0 + + enable + enable + + true + latest + AllEnabledByDefault + true + true + true + + + + + + + + + Always + + + + diff --git a/tests/application/_testdata/TestData.Getter.cs b/tests/external/application/_testdata/TestData.Getter.cs similarity index 100% rename from tests/application/_testdata/TestData.Getter.cs rename to tests/external/application/_testdata/TestData.Getter.cs diff --git a/tests/application/_testdata/TestData.Imports.cs b/tests/external/application/_testdata/TestData.Imports.cs similarity index 100% rename from tests/application/_testdata/TestData.Imports.cs rename to tests/external/application/_testdata/TestData.Imports.cs diff --git a/tests/application/_testdata/data/compare.csv b/tests/external/application/_testdata/data/compare.csv similarity index 100% rename from tests/application/_testdata/data/compare.csv rename to tests/external/application/_testdata/data/compare.csv diff --git a/tests/application/_testdata/data/default.csv b/tests/external/application/_testdata/data/default.csv similarity index 100% rename from tests/application/_testdata/data/default.csv rename to tests/external/application/_testdata/data/default.csv diff --git a/tests/application/_testdata/data/intraday.csv b/tests/external/application/_testdata/data/intraday.csv similarity index 100% rename from tests/application/_testdata/data/intraday.csv rename to tests/external/application/_testdata/data/intraday.csv diff --git a/tests/application/_testdata/data/longest.csv b/tests/external/application/_testdata/data/longest.csv similarity index 100% rename from tests/application/_testdata/data/longest.csv rename to tests/external/application/_testdata/data/longest.csv diff --git a/tests/application/_testdata/data/longish.csv b/tests/external/application/_testdata/data/longish.csv similarity index 100% rename from tests/application/_testdata/data/longish.csv rename to tests/external/application/_testdata/data/longish.csv diff --git a/tests/external/integration/GlobalUsings.cs b/tests/external/integration/GlobalUsings.cs new file mode 100644 index 000000000..de3d38d3d --- /dev/null +++ b/tests/external/integration/GlobalUsings.cs @@ -0,0 +1,3 @@ +global using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: Parallelize] diff --git a/tests/other/Tests.Other.csproj b/tests/external/integration/Tests.Integration.csproj similarity index 77% rename from tests/other/Tests.Other.csproj rename to tests/external/integration/Tests.Integration.csproj index 7d31c15e1..de6f586ca 100644 --- a/tests/other/Tests.Other.csproj +++ b/tests/external/integration/Tests.Integration.csproj @@ -2,30 +2,35 @@ net8.0 - enable false + enable + enable + true latest AllEnabledByDefault true true true + + true + + - + + + all runtime; build; native; contentfiles; analyzers - - - + - diff --git a/tests/external/integration/WilliamsR.Tests.cs b/tests/external/integration/WilliamsR.Tests.cs new file mode 100644 index 000000000..149d5dedd --- /dev/null +++ b/tests/external/integration/WilliamsR.Tests.cs @@ -0,0 +1,40 @@ +using Skender.Stock.Indicators; + +namespace Tests.Indicators; + +[TestClass, TestCategory("Integration")] +public class WilliamsRTests +{ + [TestMethod] + public async Task Issue1127() + { + // initialize + IEnumerable feedQuotes = await FeedData // live quotes + .GetQuotes("A", 365 * 3) + .ConfigureAwait(false); + + IReadOnlyList quotes = feedQuotes.ToList(); + int length = quotes.Count; + + // get indicators + IReadOnlyList resultsList = quotes + .ToWilliamsR(14); + + Console.WriteLine($"%R from {length} quotes."); + + // analyze boundary + for (int i = 0; i < length; i++) + { + Quote q = quotes[i]; + WilliamsResult r = resultsList[i]; + + Console.WriteLine($"{q.Timestamp:s} {r.WilliamsR}"); + + if (r.WilliamsR is not null) + { + Assert.IsTrue(r.WilliamsR <= 0); + Assert.IsTrue(r.WilliamsR >= -100); + } + } + } +} diff --git a/tests/external/integration/_common/Helper.LiveQuotes.cs b/tests/external/integration/_common/Helper.LiveQuotes.cs new file mode 100644 index 000000000..690f8b5cd --- /dev/null +++ b/tests/external/integration/_common/Helper.LiveQuotes.cs @@ -0,0 +1,75 @@ +using Alpaca.Markets; + +using Skender.Stock.Indicators; + +namespace Tests.Indicators; + +internal static class FeedData +{ + internal static async Task> GetQuotes(string symbol) + => await GetQuotes(symbol, 365 * 2) + .ConfigureAwait(false); + + internal static async Task> GetQuotes(string symbol, int days) + { + /* This won't run if environment variables not set. + Use FeedData.InconclusiveIfNotSetup() in tests. + + (1) get your API keys + https://alpaca.markets/docs/market-data/getting-started/ + + (2) manually install in your environment (replace value) + + setx ALPACA_KEY "y0ur_Alp@ca_K3Y_v@lue" + setx ALPACA_SECRET "y0ur_Alp@ca_S3cret_v@lue" + + ****************************************************/ + + // get and validate keys + string? alpacaApiKey = Environment.GetEnvironmentVariable("ALPACA_KEY"); + string? alpacaSecret = Environment.GetEnvironmentVariable("ALPACA_SECRET"); + + if (string.IsNullOrEmpty(alpacaApiKey) || string.IsNullOrEmpty(alpacaSecret)) + { + Assert.Inconclusive("Data feed unusable. Environment variables missing."); + } + + ArgumentException.ThrowIfNullOrEmpty(nameof(alpacaApiKey)); + ArgumentException.ThrowIfNullOrEmpty(nameof(alpacaSecret)); + + // connect to Alpaca REST API + SecretKey secretKey = new(alpacaApiKey, alpacaSecret); + + IAlpacaDataClient client = Environments + .Paper + .GetAlpacaDataClient(secretKey); + + // compose request + // (excludes last 15 minutes for free delayed quotes) + DateTime into = DateTime.Now.Subtract(TimeSpan.FromMinutes(16)); + DateTime from = into.Subtract(TimeSpan.FromDays(days)); + + HistoricalBarsRequest request = new(symbol, from, into, BarTimeFrame.Day); + + // fetch minute-bar quotes in Alpaca's format + IPage barSet = await client + .ListHistoricalBarsAsync(request) + .ConfigureAwait(false); + + // convert library compatible quotes + IEnumerable quotes = barSet + .Items + .Select(bar => new Quote + ( + Timestamp: bar.TimeUtc, + Open: bar.Open, + High: bar.High, + Low: bar.Low, + Close: bar.Close, + Volume: bar.Volume + )) + .OrderBy(x => x.Timestamp); + + return quotes; + } +} diff --git a/tests/other/Convergence.Tests.cs b/tests/external/public-api/Convergence.Tests.cs similarity index 99% rename from tests/other/Convergence.Tests.cs rename to tests/external/public-api/Convergence.Tests.cs index 541bb7cca..ebe75e0a1 100644 --- a/tests/other/Convergence.Tests.cs +++ b/tests/external/public-api/Convergence.Tests.cs @@ -1,6 +1,6 @@ namespace Behavioral; -[TestClass] +[TestClass, TestCategory("Integration")] public class Convergence : TestBase { private static readonly int[] QuotesQuantities = diff --git a/tests/other/GlobalUsings.cs b/tests/external/public-api/GlobalUsings.cs similarity index 100% rename from tests/other/GlobalUsings.cs rename to tests/external/public-api/GlobalUsings.cs diff --git a/tests/external/public-api/Tests.PublicApi.csproj b/tests/external/public-api/Tests.PublicApi.csproj new file mode 100644 index 000000000..abd2b8ede --- /dev/null +++ b/tests/external/public-api/Tests.PublicApi.csproj @@ -0,0 +1,36 @@ + + + + net8.0 + false + + enable + disable + + true + latest + AllEnabledByDefault + true + true + true + + true + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + diff --git a/tests/other/Custom.Indicator.Tests.cs b/tests/external/public-api/customizable/Custom.Indicator.Tests.cs similarity index 99% rename from tests/other/Custom.Indicator.Tests.cs rename to tests/external/public-api/customizable/Custom.Indicator.Tests.cs index 133d4d488..abe0ae097 100644 --- a/tests/other/Custom.Indicator.Tests.cs +++ b/tests/external/public-api/customizable/Custom.Indicator.Tests.cs @@ -6,7 +6,7 @@ namespace Customization; // CUSTOM INDICATORS -[TestClass] +[TestClass, TestCategory("Integration")] public class CustomIndicators { private static readonly CultureInfo EnglishCulture = new("en-US", false); diff --git a/tests/other/Custom.Quotes.Tests.cs b/tests/external/public-api/customizable/Custom.Quotes.Tests.cs similarity index 99% rename from tests/other/Custom.Quotes.Tests.cs rename to tests/external/public-api/customizable/Custom.Quotes.Tests.cs index 3b50b542d..e1c29af25 100644 --- a/tests/other/Custom.Quotes.Tests.cs +++ b/tests/external/public-api/customizable/Custom.Quotes.Tests.cs @@ -5,7 +5,7 @@ namespace Customization; // CUSTOM QUOTES -[TestClass] +[TestClass, TestCategory("Integration")] public class CustomQuotes { private static readonly CultureInfo EnglishCulture diff --git a/tests/other/Custom.Results.Tests.cs b/tests/external/public-api/customizable/Custom.Results.Tests.cs similarity index 86% rename from tests/other/Custom.Results.Tests.cs rename to tests/external/public-api/customizable/Custom.Results.Tests.cs index d7b4c7f60..ea8f1ccc3 100644 --- a/tests/other/Custom.Results.Tests.cs +++ b/tests/external/public-api/customizable/Custom.Results.Tests.cs @@ -5,7 +5,7 @@ namespace Customization; // CUSTOM RESULTS -[TestClass] +[TestClass, TestCategory("Integration")] public class CustomResults { private static readonly CultureInfo EnglishCulture @@ -74,13 +74,4 @@ List emaResults EmaResult r = emaResults.Find(x => x.Timestamp == findDate); Assert.AreEqual(249.3519m, Math.Round((decimal)r.Ema, 4)); } - - [TestMethod] - public void CustomReusable() => Assert.Inconclusive("Test not implemented"); - - [TestMethod] - public void CustomReusableInherited() => Assert.Inconclusive("Test not implemented"); - - [TestMethod] - public void CustomInheritedEma() => Assert.Inconclusive("Test not implemented"); } diff --git a/tests/other/Sut.CustomItems.cs b/tests/external/public-api/customizable/Sut.CustomItems.cs similarity index 100% rename from tests/other/Sut.CustomItems.cs rename to tests/external/public-api/customizable/Sut.CustomItems.cs diff --git a/tests/other/PublicApi.Interface.Tests.cs b/tests/external/public-api/indicators/PublicApi.Interface.Tests.cs similarity index 99% rename from tests/other/PublicApi.Interface.Tests.cs rename to tests/external/public-api/indicators/PublicApi.Interface.Tests.cs index 9dad62942..fc80b25cc 100644 --- a/tests/other/PublicApi.Interface.Tests.cs +++ b/tests/external/public-api/indicators/PublicApi.Interface.Tests.cs @@ -6,6 +6,7 @@ namespace PublicApi; // PUBLIC API (INTERFACES) [TestClass] +[TestCategory("Integration")] public class UserInterface { private static readonly IReadOnlyList quotes = Data.GetDefault(); diff --git a/tests/indicators/README.md b/tests/indicators/README.md new file mode 100644 index 000000000..c44b96c5e --- /dev/null +++ b/tests/indicators/README.md @@ -0,0 +1,82 @@ +# Testing + +Tests are split into different projects for isolation of purpose. + +```bash +# runs all unit +# and integration tests +dotnet test +``` + +> When developing locally, we recommend that you normally _unload_ the external test projects shown below, except when testing externalities. + +## Unit tests + +> `indicators/Tests.Indicators.csproj` unit tests library + +Our primary full unit test project covers the entire utility of the library. In most IDE, you can [manually select](https://learn.microsoft.com/en-us/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file?view=vs-2022#manually-select-the-run-settings-file) the `tests/tests.unit.runsettings` for isolation for local IDE dev/test efficiency, or use the _unload_ approach described above. + +```bash +# CLI equivalent +dotnet test --settings tests/tests.unit.runsettings +``` + +## Performance tests + +> `tests/performance/Tests.Performance.csproj` benchmark tests + +Running the `Tests.Performance` console application in `Release` mode will produce [benchmark performance data](https://dotnet.stockindicators.dev/performance/) that we include on our documentation site. + +```bash +# run all performance benchmarks (~15-20 minutes) +dotnet run -c Release + +# run individual performance benchmark +dotnet run -c Release --filter *.ToAdx + +# run cohorts of performance benchmarks +dotnet run -c Release --filter ** +``` + +```bash +# to see all cohorts +dotnet run --list +... +# Available Benchmarks: + #0 Incrementals + #1 SeriesIndicators + #2 StreamExternal + #3 StreamIndicators + #4 Utility + #5 UtilityMaths + +# to see all tests +dotnet run --list flat +``` + +## External tests + +All external integration and API tests can be run with one CLI + +```bash +# CLI equivalent +dotnet test --settings tests/tests.integration.runsettings +``` +Since we assume tests are non-integration tests by default, set the category attribute on any new test classes that contain integration tests. This can be applied uniquely to `[TestMethod]` as well. + +```csharp +[TestClass, TestCategory("Integration")] +public class MyIntegrationTests : TestBase +... +``` + +### Public API tests + +> `external/tests.Indicators.csproj` exercises real-world scenarios against a directly loaded package. + + + +### Integration tests + +- `indicators/tests.Indicators.csproj` unit tests the main NuGet library + diff --git a/tests/indicators/TestBase.cs b/tests/indicators/TestBase.cs index 5486c5f0e..adf3a165f 100644 --- a/tests/indicators/TestBase.cs +++ b/tests/indicators/TestBase.cs @@ -4,7 +4,7 @@ // GLOBALS & INITIALIZATION OF TEST DATA [assembly: CLSCompliant(true)] -[assembly: InternalsVisibleTo("Tests.Other")] // these use test data +[assembly: InternalsVisibleTo("Tests.PublicApi")] // these use test data [assembly: InternalsVisibleTo("Tests.Performance")] [assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] diff --git a/tests/indicators/Tests.Indicators.csproj b/tests/indicators/Tests.Indicators.csproj index 7f6a08c48..ef89704b7 100644 --- a/tests/indicators/Tests.Indicators.csproj +++ b/tests/indicators/Tests.Indicators.csproj @@ -2,9 +2,11 @@ net8.0 - enable false + enable + disable + true latest AllEnabledByDefault @@ -16,9 +18,9 @@ - - - + + + all runtime; build; native; contentfiles; analyzers diff --git a/tests/indicators/_common/Generics/BinarySettingsTests.cs b/tests/indicators/_common/Generics/BinarySettingsTests.cs index aa2b01b78..8aee04b11 100644 --- a/tests/indicators/_common/Generics/BinarySettingsTests.cs +++ b/tests/indicators/_common/Generics/BinarySettingsTests.cs @@ -69,5 +69,26 @@ public void Equality() BinarySettings sut = new(); Assert.AreEqual(0b00000000, sut.Settings); Assert.AreEqual(0b11111111, sut.Mask); + + object obj = new BinarySettings(0b01100010); + BinarySettings sutA = new(0b01100010); + BinarySettings sutB = new(0b01100010); + BinarySettings sutC = new(0b01101010); // different + + Assert.AreEqual(sutA, sutB); + + // object equality + Assert.IsTrue(obj.Equals(sutA)); + Assert.IsTrue(sutA.Equals(obj)); + + // struct equality + Assert.IsTrue(sutA.Equals(sutB)); + Assert.IsTrue(sutB.Equals(sutA)); + Assert.IsFalse(sutB.Equals(sutC)); + + // operator equality + Assert.IsTrue(sutA == sutB); + Assert.IsFalse(sutA == sutC); + Assert.IsTrue(sutB != sutC); } } diff --git a/tests/indicators/_common/Generics/Transforms.Tests.cs b/tests/indicators/_common/Generics/Transforms.Tests.cs index 60a8da66b..719e810c1 100644 --- a/tests/indicators/_common/Generics/Transforms.Tests.cs +++ b/tests/indicators/_common/Generics/Transforms.Tests.cs @@ -14,7 +14,7 @@ public void ToCollection() Assert.IsNotNull(collection); Assert.AreEqual(502, collection.Count); - Assert.AreEqual(245.28m, collection.LastOrDefault().Close); + Assert.AreEqual(245.28m, collection[^1].Close); } // null ToCollection diff --git a/tests/indicators/_common/Observables/StreamHub.CacheMgmt.Tests.cs b/tests/indicators/_common/Observables/StreamHub.CacheMgmt.Tests.cs index 2ebd45acf..188ba1470 100644 --- a/tests/indicators/_common/Observables/StreamHub.CacheMgmt.Tests.cs +++ b/tests/indicators/_common/Observables/StreamHub.CacheMgmt.Tests.cs @@ -3,12 +3,6 @@ namespace Observables; [TestClass] public class CacheManagement : TestBase { - [TestMethod] - public void ModifyWithAnalysis() => Assert.Inconclusive("test not implemented"); - - [TestMethod] - public void ModifyWithAct() => Assert.Inconclusive("test not implemented"); - [TestMethod] public void Remove() { @@ -24,9 +18,6 @@ public void Remove() observer.Results[19].Sma.Should().BeApproximately(214.5260, precision: DoublePrecision); } - [TestMethod] // TODO: tests should include all Act enum methods - public void ActInstructions() => Assert.Inconclusive("test not implemented"); - [TestMethod] public void ActAddOld() // late arrival { diff --git a/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs b/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs index 8d837fe98..03ee23803 100644 --- a/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs @@ -1,5 +1,3 @@ -using System.Collections.ObjectModel; - // quote list converters namespace Utilities; @@ -29,4 +27,64 @@ public void ToSortedList() DateTime spotDate = DateTime.ParseExact("03/16/2017", "MM/dd/yyyy", TestBase.invariantCulture); Assert.AreEqual(spotDate, h[50].Timestamp); } + + [TestMethod] + public void ToQuoteList() + { + // setup + IReadOnlyList quotes + = Quotes.Take(5).ToList(); + + IReadOnlyList myQuotes = quotes + .Select(x => new MyQuote { + Timestamp = x.Timestamp, + Open = x.Open, + High = x.High, + Low = x.Low, + Close = x.Close, + Volume = x.Volume + }).ToList(); + + // sut + IReadOnlyList sut + = myQuotes.ToQuoteList(); + + // assert is same as original + sut.Should().BeEquivalentTo(quotes); + } + + [TestMethod] + public void ToQuote() + { + // setup + Quote q = Quotes[0]; + + MyQuote myQuote = new() { + Timestamp = q.Timestamp, + Open = q.Open, + High = q.High, + Low = q.Low, + Close = q.Close, + Volume = q.Volume + }; + + // sut + Quote sut = myQuote.ToQuote(); + + // assert value based equality + sut.Should().Be(q); + sut.Value.Should().Be(q.Value); + } + + private class MyQuote : IQuote + { + public DateTime Timestamp { get; set; } + public decimal Open { get; set; } + public decimal High { get; set; } + public decimal Low { get; set; } + public decimal Close { get; set; } + public decimal Volume { get; set; } + + public double Value => (double)Close; + } } diff --git a/tests/indicators/_common/Use (QuotePart)/QuotePart.Utilities.Tests.cs b/tests/indicators/_common/Use (QuotePart)/QuotePart.Utilities.Tests.cs index bbf643f4b..ed56cd917 100644 --- a/tests/indicators/_common/Use (QuotePart)/QuotePart.Utilities.Tests.cs +++ b/tests/indicators/_common/Use (QuotePart)/QuotePart.Utilities.Tests.cs @@ -3,8 +3,16 @@ namespace Utilities; [TestClass] public class QuoteParts : TestBase { - // this is an alias of Quotes.Use() - // so we're only testing the base utilities here + [TestMethod] + public void Instantiation() + { + Quote q = Quotes[1]; + + QuotePart sut0 = new(q.Timestamp, (double)q.Close); + QuotePart sut1 = new(q); + + sut1.Should().Be(sut0); + } [TestMethod] public void ConvertQuote() diff --git a/tests/indicators/s-z/Sma/Sma.StaticSeries.Tests.cs b/tests/indicators/s-z/Sma/Sma.StaticSeries.Tests.cs index d66fe61ff..2f2caaa58 100644 --- a/tests/indicators/s-z/Sma/Sma.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/Sma/Sma.StaticSeries.Tests.cs @@ -1,7 +1,7 @@ namespace StaticSeries; [TestClass] -public class Sma : StaticSeriesTestBase +public partial class Sma : StaticSeriesTestBase { [TestMethod] public override void Standard() diff --git a/tests/indicators/s-z/Sma/Sma.Utilities.Tests.cs b/tests/indicators/s-z/Sma/Sma.Utilities.Tests.cs new file mode 100644 index 000000000..dd8e344b6 --- /dev/null +++ b/tests/indicators/s-z/Sma/Sma.Utilities.Tests.cs @@ -0,0 +1,23 @@ +namespace StaticSeries; + +public partial class Sma : StaticSeriesTestBase +{ + [TestMethod] + public void Average() + { + QuotePart[] results = new[] + { + new QuotePart(Quotes[0].Timestamp, 0.0), + new QuotePart(Quotes[1].Timestamp, 4.0), + new QuotePart(Quotes[2].Timestamp, 8.0) + }; + + // sut + double? mid = results.Average(2, 1); + double? end = results.Average(2); + + // assert + mid.Should().Be(2); + end.Should().Be(6); + } +} diff --git a/tests/performance/Tests.Performance.csproj b/tests/performance/Tests.Performance.csproj index 43bc26589..18fb733e2 100644 --- a/tests/performance/Tests.Performance.csproj +++ b/tests/performance/Tests.Performance.csproj @@ -5,10 +5,6 @@ Exe Performance.Program - false - None - true - enable true @@ -17,6 +13,11 @@ true true true + + false + None + true + diff --git a/tests/simulate/Test.Simulation.csproj b/tests/simulate/Test.Simulation.csproj index 914048054..6c4315d35 100644 --- a/tests/simulate/Test.Simulation.csproj +++ b/tests/simulate/Test.Simulation.csproj @@ -3,8 +3,16 @@ Exe net8.0 + enable enable + + true + latest + AllEnabledByDefault + true + true + true diff --git a/tests/simulate/Utilities.cs b/tests/simulate/Utilities.cs index 941f5a659..4cf5a9ba2 100644 --- a/tests/simulate/Utilities.cs +++ b/tests/simulate/Utilities.cs @@ -1,20 +1,23 @@ using Skender.Stock.Indicators; namespace Utilities; +#pragma warning disable CA5394 // Do not use insecure randomness internal static class Util { internal static List Setup(int quantityToStream, int quotesPerMinute) { + // Log the simulation rate Console.WriteLine($"Simulating {quotesPerMinute:N0} quotes per minute"); PrintHeader(); + // Generate and return a list of quotes using Geometric Brownian Motion return new RandomGbm(bars: quantityToStream); } internal static void PrintHeader() { - // dislay header + // Display header for the output Console.WriteLine(); Console.WriteLine(""" Date Close price SMA(3) EMA(5) EMA(7,HL2) SMA/EMA(8) @@ -29,19 +32,22 @@ internal static void PrintData( EmaHub useChain, EmaHub emaChain) { - // send output to console + // Format the initial part of the output string with timestamp and close price string m = $"{q.Timestamp:yyyy-MM-dd HH:mm} {q.Close,11:N2}"; + // Get the latest results from the hubs SmaResult s = smaHub.Results[^1]; EmaResult e = emaHub.Results[^1]; EmaResult u = useChain.Results[^1]; EmaResult c = emaChain.Results[^1]; + // Append SMA result if available if (s.Sma is not null) { m += $"{s.Sma,12:N1}"; } + // Append EMA results if available if (e.Ema is not null) { m += $"{e.Ema,12:N1}"; @@ -57,6 +63,7 @@ internal static void PrintData( m += $"{c.Ema,12:N1}"; } + // Output the formatted string to the console Console.WriteLine(m); } } @@ -80,13 +87,13 @@ internal static void PrintData( /// Seed: starting value of the random series; should not be 0. /// -internal class RandomGbm : List +internal sealed class RandomGbm : List { private readonly double _volatility; private readonly double _drift; private double _seed; - internal RandomGbm( + public RandomGbm( int bars = 250, double volatility = 1.0, double drift = 0.01, diff --git a/tests/tests.integration.runsettings b/tests/tests.integration.runsettings new file mode 100644 index 000000000..30d2fbffd --- /dev/null +++ b/tests/tests.integration.runsettings @@ -0,0 +1,5 @@ + + + TestCategory=Integration + + diff --git a/tests/tests.unit.runsettings b/tests/tests.unit.runsettings new file mode 100644 index 000000000..c807c9384 --- /dev/null +++ b/tests/tests.unit.runsettings @@ -0,0 +1,5 @@ + + + TestCategory!=Integration + + \ No newline at end of file From c676ed97604824bee2a19ad17e562fb053d3fd64 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Sun, 10 Nov 2024 21:10:16 -0500 Subject: [PATCH 11/18] docs: Add inline comments, remove `info.xml` (#1155) Signed-off-by: Dave Skender <8432125+DaveSkender@users.noreply.github.com> --- .editorconfig | 152 ++++++++------- .github/workflows/deploy-package.yml | 25 +-- .vscode/extensions.json | 12 ++ docs/examples/.editorconfig | 39 +--- src/.editorconfig | 5 + src/Directory.Packages.props | 10 + src/Indicators.csproj | 30 +-- src/_common/BinarySettings.cs | 18 +- src/_common/Candles/Candles.Models.cs | 77 +++++++- src/_common/Candles/Candles.Utilities.cs | 23 ++- src/_common/Enums.cs | 184 +++++++++++++++++- src/_common/Generics/Pruning.cs | 24 ++- src/_common/Generics/Seek.cs | 14 +- src/_common/Generics/Sorting.cs | 13 +- src/_common/Generics/Transforms.cs | 15 +- src/_common/ISeries.cs | 12 +- src/_common/Incrementals/IIncremental.cs | 42 ++-- src/_common/Math/NullMath.cs | 49 +++++ src/_common/Math/Numerical.cs | 38 +++- src/_common/Observables/IStreamHub.cs | 2 - src/_common/Observables/IStreamObservable.cs | 25 +-- src/_common/Observables/IStreamObserver.cs | 2 - .../Observables/StreamHub.Observable.cs | 18 +- src/_common/Observables/StreamHub.Observer.cs | 12 +- .../Observables/StreamHub.Utilities.cs | 65 +++---- src/_common/Observables/StreamHub.cs | 150 ++++++++------ src/_common/ObsoleteV3.cs | 1 + src/_common/Quotes/Quote.Aggregates.cs | 21 +- src/_common/Quotes/Quote.Converters.cs | 32 ++- src/_common/Quotes/Quote.Exceptions.cs | 46 +++-- src/_common/Quotes/Quote.Models.cs | 2 + src/_common/Quotes/Quote.StreamHub.cs | 54 ++++- src/_common/Quotes/info.xml | 37 ---- src/_common/Reusable/Reusable.Utilities.cs | 20 +- .../Use (QuotePart)/QuotePart.Models.cs | 5 + .../Use (QuotePart)/QuotePart.StaticSeries.cs | 18 +- .../Use (QuotePart)/QuotePart.StreamHub.cs | 28 +++ src/a-d/Adl/Adl.Models.cs | 8 + src/a-d/Adl/Adl.StaticSeries.cs | 15 +- src/a-d/Adl/Adl.StreamHub.cs | 29 ++- src/a-d/Adl/Adl.Utilities.cs | 34 ++-- src/a-d/Adx/Adx.Models.cs | 9 + src/a-d/Adx/Adx.StaticSeries.cs | 42 ++-- src/a-d/Adx/Adx.Utilities.cs | 18 +- src/a-d/Adx/info.xml | 33 ---- src/a-d/Alligator/Alligator.Models.cs | 7 + src/a-d/Alligator/Alligator.StaticSeries.cs | 10 +- src/a-d/Alligator/Alligator.StreamHub.cs | 83 +++++++- src/a-d/Alligator/Alligator.Utilities.cs | 28 ++- src/a-d/Alma/Alma.Models.cs | 7 + src/a-d/Alma/Alma.StaticSeries.cs | 15 +- src/a-d/Alma/Alma.Utilities.cs | 16 +- src/a-d/Alma/info.xml | 20 -- src/a-d/Aroon/Aroon.Models.cs | 8 + src/a-d/Aroon/Aroon.StaticSeries.cs | 18 +- src/a-d/Aroon/info.xml | 17 -- src/a-d/Atr/Atr.Models.cs | 8 + src/a-d/Atr/Atr.StaticSeries.cs | 19 +- src/a-d/Atr/Atr.StreamHub.cs | 32 ++- src/a-d/Atr/Atr.Utilities.cs | 26 ++- src/a-d/Atr/info.xml | 17 -- src/a-d/AtrStop/AtrStop.StaticSeries.cs | 22 ++- src/a-d/AtrStop/AtrStop.StreamHub.cs | 53 ++++- src/a-d/AtrStop/AtrStop.Utilities.cs | 29 ++- src/a-d/AtrStop/info.xml | 21 -- src/a-d/Awesome/Awesome.Models.cs | 7 + src/a-d/Awesome/Awesome.StaticSeries.cs | 13 +- src/a-d/Awesome/info.xml | 18 -- src/a-d/Beta/Beta.Models.cs | 30 +++ src/a-d/Beta/Beta.StaticSeries.cs | 26 ++- src/a-d/Beta/info.xml | 20 -- .../BollingerBands/BollingerBands.Models.cs | 11 ++ .../BollingerBands.StaticSeries.cs | 14 +- src/a-d/BollingerBands/info.xml | 18 -- src/a-d/Bop/Bop.Models.cs | 6 + src/a-d/Bop/Bop.StaticSeries.cs | 18 +- src/a-d/Bop/Bop.Utilities.cs | 11 +- src/a-d/Bop/info.xml | 17 -- src/a-d/Cci/Cci.Models.cs | 6 + src/a-d/Cci/Cci.StaticSeries.cs | 18 +- src/a-d/Cci/Cci.Utilities.cs | 12 +- src/a-d/Cci/info.xml | 17 -- src/a-d/ChaikinOsc/ChaikinOsc.Models.cs | 9 + src/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.cs | 13 +- src/a-d/ChaikinOsc/ChaikinOsc.Utilities.cs | 22 ++- src/a-d/ChaikinOsc/info.xml | 18 -- src/a-d/Chandelier/Chandelier.Models.cs | 16 ++ src/a-d/Chandelier/Chandelier.StaticSeries.cs | 22 ++- src/a-d/Chandelier/Chandelier.Utilities.cs | 15 +- src/a-d/Chandelier/info.xml | 19 -- src/a-d/Chop/Chop.Models.cs | 6 + src/a-d/Chop/Chop.StaticSeries.cs | 18 +- src/a-d/Chop/Chop.Utilities.cs | 11 +- src/a-d/Chop/info.xml | 18 -- src/a-d/Cmf/Cmf.Models.cs | 8 + src/a-d/Cmf/Cmf.StaticSeries.cs | 19 +- src/a-d/Cmf/Cmf.Utilities.cs | 12 +- src/a-d/Cmf/info.xml | 17 -- src/a-d/Cmo/Cmo.Models.cs | 6 + src/a-d/Cmo/Cmo.StaticSeries.cs | 12 +- src/a-d/Cmo/Cmo.Utilities.cs | 11 +- src/a-d/Cmo/info.xml | 21 -- src/a-d/ConnorsRsi/ConnorsRsi.Models.cs | 10 + src/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.cs | 23 ++- src/a-d/ConnorsRsi/ConnorsRsi.Utilities.cs | 17 +- src/a-d/ConnorsRsi/info.xml | 19 -- src/a-d/Correlation/Correlation.Models.cs | 10 + .../Correlation/Correlation.StaticSeries.cs | 23 ++- src/a-d/Correlation/info.xml | 22 --- src/a-d/Dema/Dema.Models.cs | 6 + src/a-d/Dema/Dema.StaticSeries.cs | 14 +- src/a-d/Dema/Dema.Utilities.cs | 18 +- src/a-d/Dema/info.xml | 17 -- src/a-d/Doji/Doji.StaticSeries.cs | 7 +- src/a-d/Doji/Doji.Utilities.cs | 13 +- src/a-d/Donchian/Donchian.Models.cs | 8 + src/a-d/Donchian/Donchian.StaticSeries.cs | 15 +- src/a-d/Donchian/Donchian.Utilities.cs | 25 ++- src/a-d/Donchian/info.xml | 17 -- src/a-d/Dpo/Dpo.Models.cs | 7 + src/a-d/Dpo/Dpo.StaticSeries.cs | 14 +- src/a-d/Dpo/Dpo.Utilities.cs | 14 +- src/a-d/Dpo/info.xml | 17 -- src/a-d/Dynamic/Dynamic.Models.cs | 6 + src/a-d/Dynamic/Dynamic.StaticSeries.cs | 15 +- src/a-d/Dynamic/Dynamic.Utilities.cs | 24 ++- src/a-d/Dynamic/info.xml | 18 -- src/e-k/ElderRay/ElderRay.Models.cs | 8 + src/e-k/ElderRay/ElderRay.StaticSeries.cs | 20 +- src/e-k/ElderRay/ElderRay.Utilities.cs | 19 +- src/e-k/ElderRay/info.xml | 17 -- src/e-k/Ema/Ema.Increments.cs | 41 +++- src/e-k/Ema/Ema.Models.cs | 6 + src/e-k/Ema/Ema.StaticSeries.cs | 14 +- src/e-k/Ema/Ema.StreamHub.cs | 50 +++-- src/e-k/Ema/Ema.Utilities.cs | 40 +++- src/e-k/Ema/info.xml | 20 -- src/e-k/Epma/Epma.Models.cs | 6 + src/e-k/Epma/Epma.StaticSeries.cs | 14 +- src/e-k/Epma/Epma.Utilities.cs | 19 +- src/e-k/Epma/info.xml | 17 -- src/e-k/Fcb/Fcb.Models.cs | 6 + src/e-k/Fcb/Fcb.StaticSeries.cs | 14 +- src/e-k/Fcb/Fcb.Utilities.cs | 23 ++- src/e-k/Fcb/info.xml | 17 -- .../FisherTransform/FisherTransform.Models.cs | 7 + .../FisherTransform.StaticSeries.cs | 14 +- .../FisherTransform.Utilities.cs | 13 +- src/e-k/FisherTransform/info.xml | 17 -- src/e-k/ForceIndex/ForceIndex.Models.cs | 6 + src/e-k/ForceIndex/ForceIndex.StaticSeries.cs | 22 ++- src/e-k/ForceIndex/ForceIndex.Utilities.cs | 19 +- src/e-k/ForceIndex/info.xml | 17 -- src/e-k/Fractal/Fractal.Models.cs | 6 + src/e-k/Fractal/Fractal.StaticSeries.cs | 26 ++- src/e-k/Fractal/Fractal.Utilities.cs | 19 +- src/e-k/Fractal/info.xml | 40 ---- src/e-k/Gator/Gator.Models.cs | 8 + src/e-k/Gator/Gator.StaticSeries.cs | 24 +-- src/e-k/Gator/Gator.Utilities.cs | 17 +- src/e-k/HeikinAshi/HeikinAshi.Models.cs | 9 + src/e-k/HeikinAshi/HeikinAshi.StaticSeries.cs | 12 +- src/e-k/HeikinAshi/info.xml | 15 -- src/e-k/Hma/Hma.Models.cs | 6 + src/e-k/Hma/Hma.StaticSeries.cs | 14 +- src/e-k/Hma/Hma.Utilities.cs | 11 +- src/e-k/Hma/info.xml | 17 -- src/e-k/HtTrendline/HtTrendline.Models.cs | 8 + .../HtTrendline/HtTrendline.StaticSeries.cs | 18 +- src/e-k/HtTrendline/HtTrendline.Utilities.cs | 11 +- src/e-k/Hurst/Hurst.Models.cs | 6 + src/e-k/Hurst/Hurst.StaticSeries.cs | 19 +- src/e-k/Hurst/Hurst.Utilities.cs | 19 +- src/e-k/Hurst/info.xml | 17 -- src/e-k/Ichimoku/Ichimoku.Models.cs | 21 +- src/e-k/Ichimoku/Ichimoku.StaticSeries.cs | 72 ++++++- src/e-k/Ichimoku/Ichimoku.Utilities.cs | 24 ++- src/e-k/Ichimoku/info.xml | 62 ------ src/e-k/Kama/Kama.Models.cs | 7 + src/e-k/Kama/Kama.StaticSeries.cs | 16 +- src/e-k/Kama/Kama.Utilities.cs | 22 ++- src/e-k/Kama/info.xml | 19 -- src/e-k/Keltner/Keltner.Models.cs | 8 + src/e-k/Keltner/Keltner.StaticSeries.cs | 23 ++- src/e-k/Keltner/Keltner.Utilities.cs | 29 ++- src/e-k/Keltner/info.xml | 19 -- src/e-k/Kvo/Kvo.Models.cs | 7 + src/e-k/Kvo/Kvo.StaticSeries.cs | 23 ++- src/e-k/Kvo/Kvo.Utilities.cs | 17 +- src/e-k/Kvo/info.xml | 19 -- src/m-r/MaEnvelopes/MaEnvelopes.Models.cs | 7 + .../MaEnvelopes/MaEnvelopes.StaticSeries.cs | 16 +- src/m-r/MaEnvelopes/MaEnvelopes.Utilities.cs | 18 +- src/m-r/MaEnvelopes/info.xml | 19 -- src/m-r/Macd/Macd.Models.cs | 10 + src/m-r/Macd/Macd.StaticSeries.cs | 16 +- src/m-r/Macd/Macd.Utilities.cs | 22 ++- src/m-r/Macd/info.xml | 19 -- src/m-r/Mama/Mama.Models.cs | 7 + src/m-r/Mama/Mama.StaticSeries.cs | 14 +- src/m-r/Mama/Mama.Utilities.cs | 21 +- src/m-r/Mama/info.xml | 18 -- src/m-r/Marubozu/Marubozu.StaticSeries.cs | 14 +- src/m-r/Marubozu/Marubozu.Utilities.cs | 13 +- src/m-r/Marubozu/info.xml | 17 -- src/m-r/Mfi/Mfi.Models.cs | 10 +- src/m-r/Mfi/Mfi.StaticSeries.cs | 32 ++- src/m-r/Mfi/Mfi.Utilities.cs | 19 +- src/m-r/Mfi/info.xml | 17 -- src/m-r/Obv/Obv.Models.cs | 6 + src/m-r/Obv/Obv.StaticSeries.cs | 16 +- src/m-r/Obv/info.xml | 16 -- src/m-r/ParabolicSar/ParabolicSar.Models.cs | 7 + .../ParabolicSar/ParabolicSar.StaticSeries.cs | 54 +++-- .../ParabolicSar/ParabolicSar.Utilities.cs | 15 +- src/m-r/ParabolicSar/info.xml | 37 ---- src/m-r/PivotPoints/PivotPoints.Models.cs | 89 ++++++++- .../PivotPoints/PivotPoints.StaticSeries.cs | 67 ++++++- src/m-r/PivotPoints/PivotPoints.Utilities.cs | 19 +- src/m-r/PivotPoints/info.xml | 18 -- src/m-r/Pivots/Pivots.Models.cs | 52 +++-- src/m-r/Pivots/Pivots.StaticSeries.cs | 16 +- src/m-r/Pivots/Pivots.Utilities.cs | 19 +- src/m-r/Pivots/info.xml | 20 -- src/m-r/Pmo/Pmo.Models.cs | 7 + src/m-r/Pmo/Pmo.StaticSeries.cs | 15 +- src/m-r/Pmo/Pmo.Utilities.cs | 19 +- src/m-r/Pmo/info.xml | 19 -- src/m-r/Prs/Prs.Models.cs | 7 + src/m-r/Prs/Prs.StaticSeries.cs | 15 +- src/m-r/Prs/Prs.Utilities.cs | 15 +- src/m-r/Prs/info.xml | 22 --- src/m-r/Pvo/Pvo.Models.cs | 8 + src/m-r/Pvo/Pvo.StaticSeries.cs | 40 +++- src/m-r/Pvo/Pvo.Utilities.cs | 19 +- src/m-r/Pvo/info.xml | 19 -- src/m-r/Renko/Renko.Models.cs | 10 + src/m-r/Renko/Renko.StaticSeries.cs | 14 +- src/m-r/Renko/Renko.StreamHub.cs | 58 ++++-- src/m-r/Renko/Renko.Utilities.cs | 23 ++- src/m-r/Renko/info.xml | 38 ---- src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs | 13 +- src/m-r/Roc/Roc.Models.cs | 7 + src/m-r/Roc/Roc.StaticSeries.cs | 13 +- src/m-r/Roc/Roc.Utilities.cs | 11 +- src/m-r/Roc/info.xml | 21 -- src/m-r/RocWb/RocWb.Models.cs | 9 + src/m-r/RocWb/RocWb.StaticSeries.cs | 15 +- src/m-r/RocWb/RocWb.Utilities.cs | 19 +- src/m-r/RocWb/info.xml | 23 --- src/m-r/RollingPivots/RollingPivots.Models.cs | 39 ++++ .../RollingPivots.StaticSeries.cs | 15 +- .../RollingPivots/RollingPivots.Utilities.cs | 18 +- src/m-r/RollingPivots/info.xml | 20 -- src/m-r/Rsi/Rsi.Models.cs | 6 + src/m-r/Rsi/Rsi.StaticSeries.cs | 13 +- src/m-r/Rsi/Rsi.Utilities.cs | 17 +- src/m-r/Rsi/info.xml | 18 -- src/s-z/Slope/Slope.Models.cs | 10 + src/s-z/Slope/Slope.StaticSeries.cs | 14 +- src/s-z/Slope/Slope.Utilities.cs | 11 +- src/s-z/Slope/info.xml | 18 -- src/s-z/Sma/Sma.Models.cs | 6 + src/s-z/Sma/Sma.StaticSeries.cs | 14 +- src/s-z/Sma/Sma.StreamHub.cs | 30 +++ src/s-z/Sma/Sma.Utilities.cs | 51 ++--- src/s-z/Sma/info.xml | 37 ---- src/s-z/SmaAnalysis/SmaAnalysis.Models.cs | 13 +- .../SmaAnalysis/SmaAnalysis.StaticSeries.cs | 12 +- src/s-z/Smi/Smi.Models.cs | 7 + src/s-z/Smi/Smi.StaticSeries.cs | 24 ++- src/s-z/Smi/Smi.Utilities.cs | 23 ++- src/s-z/Smi/info.xml | 23 --- src/s-z/Smma/Smma.Models.cs | 6 + src/s-z/Smma/Smma.StaticSeries.cs | 19 +- src/s-z/Smma/Smma.Utilities.cs | 18 +- src/s-z/Smma/info.xml | 19 -- src/s-z/StarcBands/StarcBands.Models.cs | 7 + src/s-z/StarcBands/StarcBands.StaticSeries.cs | 22 ++- src/s-z/StarcBands/StarcBands.Utilities.cs | 27 ++- src/s-z/StarcBands/info.xml | 19 -- src/s-z/Stc/Stc.Models.cs | 6 + src/s-z/Stc/Stc.StaticSeries.cs | 16 +- src/s-z/Stc/Stc.Utilities.cs | 20 +- src/s-z/Stc/info.xml | 19 -- src/s-z/StdDev/StdDev.Models.cs | 8 + src/s-z/StdDev/StdDev.StaticSeries.cs | 18 +- src/s-z/StdDev/StdDev.Utilities.cs | 11 +- src/s-z/StdDev/info.xml | 17 -- .../StdDevChannels/StdDevChannels.Models.cs | 8 + .../StdDevChannels.StaticSeries.cs | 13 +- .../StdDevChannels.Utilities.cs | 26 ++- src/s-z/StdDevChannels/info.xml | 18 -- src/s-z/Stoch/Stoch.Models.cs | 23 +++ src/s-z/Stoch/Stoch.StaticSeries.cs | 59 ++++-- src/s-z/Stoch/Stoch.Utilities.cs | 23 ++- src/s-z/Stoch/info.xml | 91 --------- src/s-z/StochRsi/StochRsi.Models.cs | 7 + src/s-z/StochRsi/StochRsi.StaticSeries.cs | 16 +- src/s-z/StochRsi/StochRsi.Utilities.cs | 21 +- src/s-z/StochRsi/info.xml | 20 -- src/s-z/SuperTrend/SuperTrend.Models.cs | 7 + src/s-z/SuperTrend/SuperTrend.StaticSeries.cs | 21 +- src/s-z/SuperTrend/SuperTrend.Utilities.cs | 26 ++- src/s-z/SuperTrend/info.xml | 20 -- src/s-z/T3/T3.Models.cs | 6 + src/s-z/T3/T3.StaticSeries.cs | 14 +- src/s-z/T3/T3.Utilities.cs | 12 +- src/s-z/T3/info.xml | 18 -- src/s-z/Tema/Tema.Models.cs | 6 + src/s-z/Tema/Tema.StaticSeries.cs | 14 +- src/s-z/Tema/Tema.Utilities.cs | 18 +- src/s-z/Tema/info.xml | 17 -- src/s-z/Tr/Tr.Models.cs | 6 + src/s-z/Tr/Tr.StaticSeries.cs | 16 +- src/s-z/Tr/Tr.StreamHub.cs | 16 ++ src/s-z/Tr/Tr.Utilities.cs | 12 +- src/s-z/Tr/info.xml | 30 --- src/s-z/Trix/Trix.Models.cs | 7 + src/s-z/Trix/Trix.StaticSeries.cs | 17 +- src/s-z/Trix/Trix.Utilities.cs | 18 +- src/s-z/Trix/info.xml | 17 -- src/s-z/Tsi/Tsi.Models.cs | 7 + src/s-z/Tsi/Tsi.StaticSeries.cs | 15 +- src/s-z/Tsi/Tsi.Utilities.cs | 17 +- src/s-z/Tsi/info.xml | 19 -- src/s-z/UlcerIndex/UlcerIndex.Models.cs | 9 + src/s-z/UlcerIndex/UlcerIndex.StaticSeries.cs | 13 +- src/s-z/UlcerIndex/UlcerIndex.Utilities.cs | 11 +- src/s-z/UlcerIndex/info.xml | 17 -- src/s-z/Ultimate/Ultimate.Models.cs | 6 + src/s-z/Ultimate/Ultimate.StaticSeries.cs | 23 ++- src/s-z/Ultimate/Ultimate.Utilities.cs | 14 +- src/s-z/Ultimate/info.xml | 19 -- .../VolatilityStop/VolatilityStop.Models.cs | 9 + .../VolatilityStop.StaticSeries.cs | 21 +- .../VolatilityStop.Utilities.cs | 18 +- src/s-z/VolatilityStop/info.xml | 18 -- src/s-z/Vortex/Vortex.Models.cs | 6 + src/s-z/Vortex/Vortex.StaticSeries.cs | 19 +- src/s-z/Vortex/Vortex.Utilities.cs | 24 ++- src/s-z/Vortex/info.xml | 18 -- src/s-z/Vwap/Vwap.Models.cs | 10 +- src/s-z/Vwap/Vwap.StaticSeries.cs | 19 +- src/s-z/Vwap/Vwap.Utilities.cs | 18 +- src/s-z/Vwap/info.xml | 17 -- src/s-z/Vwma/Vwma.Models.cs | 6 + src/s-z/Vwma/Vwma.StaticSeries.cs | 19 +- src/s-z/Vwma/Vwma.Utilities.cs | 12 +- src/s-z/Vwma/info.xml | 17 -- src/s-z/WilliamsR/WilliamsR.Models.cs | 6 + src/s-z/WilliamsR/WilliamsR.StaticSeries.cs | 19 +- src/s-z/WilliamsR/WilliamsR.Utilities.cs | 12 +- src/s-z/WilliamsR/info.xml | 17 -- src/s-z/Wma/Wma.Models.cs | 6 + src/s-z/Wma/Wma.StaticSeries.cs | 14 +- src/s-z/Wma/Wma.Utilities.cs | 12 +- src/s-z/Wma/info.xml | 17 -- src/s-z/ZigZag/ZigZag.Models.cs | 47 ++++- src/s-z/ZigZag/ZigZag.StaticSeries.cs | 47 ++++- src/s-z/ZigZag/ZigZag.Utilities.cs | 17 +- src/s-z/ZigZag/info.xml | 18 -- tests/.editorconfig | 5 + tests/Directory.Packages.props | 17 ++ .../application/Test.Application.csproj | 2 +- .../integration/Tests.Integration.csproj | 15 +- .../public-api/Tests.PublicApi.csproj | 13 +- tests/indicators/Tests.Indicators.csproj | 18 +- .../_common/Math/Numerical.Tests.cs | 4 +- .../StreamHub.Stackoverflow.Tests.cs | 12 +- .../indicators/s-z/Sma/Sma.Utilities.Tests.cs | 6 +- tests/performance/Tests.Performance.csproj | 3 +- tests/simulate/Test.Simulation.csproj | 2 +- 373 files changed, 4637 insertions(+), 2760 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 src/.editorconfig create mode 100644 src/Directory.Packages.props delete mode 100644 src/_common/Quotes/info.xml delete mode 100644 src/a-d/Adx/info.xml delete mode 100644 src/a-d/Alma/info.xml delete mode 100644 src/a-d/Aroon/info.xml delete mode 100644 src/a-d/Atr/info.xml delete mode 100644 src/a-d/AtrStop/info.xml delete mode 100644 src/a-d/Awesome/info.xml delete mode 100644 src/a-d/Beta/info.xml delete mode 100644 src/a-d/BollingerBands/info.xml delete mode 100644 src/a-d/Bop/info.xml delete mode 100644 src/a-d/Cci/info.xml delete mode 100644 src/a-d/ChaikinOsc/info.xml delete mode 100644 src/a-d/Chandelier/info.xml delete mode 100644 src/a-d/Chop/info.xml delete mode 100644 src/a-d/Cmf/info.xml delete mode 100644 src/a-d/Cmo/info.xml delete mode 100644 src/a-d/ConnorsRsi/info.xml delete mode 100644 src/a-d/Correlation/info.xml delete mode 100644 src/a-d/Dema/info.xml delete mode 100644 src/a-d/Donchian/info.xml delete mode 100644 src/a-d/Dpo/info.xml delete mode 100644 src/a-d/Dynamic/info.xml delete mode 100644 src/e-k/ElderRay/info.xml delete mode 100644 src/e-k/Ema/info.xml delete mode 100644 src/e-k/Epma/info.xml delete mode 100644 src/e-k/Fcb/info.xml delete mode 100644 src/e-k/FisherTransform/info.xml delete mode 100644 src/e-k/ForceIndex/info.xml delete mode 100644 src/e-k/Fractal/info.xml delete mode 100644 src/e-k/HeikinAshi/info.xml delete mode 100644 src/e-k/Hma/info.xml delete mode 100644 src/e-k/Hurst/info.xml delete mode 100644 src/e-k/Ichimoku/info.xml delete mode 100644 src/e-k/Kama/info.xml delete mode 100644 src/e-k/Keltner/info.xml delete mode 100644 src/e-k/Kvo/info.xml delete mode 100644 src/m-r/MaEnvelopes/info.xml delete mode 100644 src/m-r/Macd/info.xml delete mode 100644 src/m-r/Mama/info.xml delete mode 100644 src/m-r/Marubozu/info.xml delete mode 100644 src/m-r/Mfi/info.xml delete mode 100644 src/m-r/Obv/info.xml delete mode 100644 src/m-r/ParabolicSar/info.xml delete mode 100644 src/m-r/PivotPoints/info.xml delete mode 100644 src/m-r/Pivots/info.xml delete mode 100644 src/m-r/Pmo/info.xml delete mode 100644 src/m-r/Prs/info.xml delete mode 100644 src/m-r/Pvo/info.xml delete mode 100644 src/m-r/Renko/info.xml delete mode 100644 src/m-r/Roc/info.xml delete mode 100644 src/m-r/RocWb/info.xml delete mode 100644 src/m-r/RollingPivots/info.xml delete mode 100644 src/m-r/Rsi/info.xml delete mode 100644 src/s-z/Slope/info.xml delete mode 100644 src/s-z/Sma/info.xml delete mode 100644 src/s-z/Smi/info.xml delete mode 100644 src/s-z/Smma/info.xml delete mode 100644 src/s-z/StarcBands/info.xml delete mode 100644 src/s-z/Stc/info.xml delete mode 100644 src/s-z/StdDev/info.xml delete mode 100644 src/s-z/StdDevChannels/info.xml delete mode 100644 src/s-z/Stoch/info.xml delete mode 100644 src/s-z/StochRsi/info.xml delete mode 100644 src/s-z/SuperTrend/info.xml delete mode 100644 src/s-z/T3/info.xml delete mode 100644 src/s-z/Tema/info.xml delete mode 100644 src/s-z/Tr/info.xml delete mode 100644 src/s-z/Trix/info.xml delete mode 100644 src/s-z/Tsi/info.xml delete mode 100644 src/s-z/UlcerIndex/info.xml delete mode 100644 src/s-z/Ultimate/info.xml delete mode 100644 src/s-z/VolatilityStop/info.xml delete mode 100644 src/s-z/Vortex/info.xml delete mode 100644 src/s-z/Vwap/info.xml delete mode 100644 src/s-z/Vwma/info.xml delete mode 100644 src/s-z/WilliamsR/info.xml delete mode 100644 src/s-z/Wma/info.xml delete mode 100644 src/s-z/ZigZag/info.xml create mode 100644 tests/.editorconfig create mode 100644 tests/Directory.Packages.props diff --git a/.editorconfig b/.editorconfig index 6eba79cb1..478516fbe 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,7 @@ # top-most EditorConfig file root = true -# global baselines +#### Core EditorConfig Options #### [*] charset = utf-8 end_of_line = lf @@ -11,103 +11,134 @@ indent_size = 2 tab_width = 2 line_length = 150 -max_line_length = 150 trim_trailing_whitespace = true insert_final_newline = true -[*.{cs,vb}] -tab_width = 4 +# .NET Globals +[*.cs] indent_size = 4 +tab_width = 4 -#### Naming styles #### - -# Naming rules - -dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion -dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface -dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i +#### .NET Coding Conventions #### +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/overview -dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.types_should_be_pascal_case.symbols = types -dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = true +file_header_template = unset -dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members -dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members -# Symbol specifications +# Expression-level preferences +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_namespace_match_folder = false:none +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Collection expression preferences +dotnet_style_prefer_collection_expression = true:suggestion +dotnet_diagnostic.IDE0305.severity = none # exclude collection expression for fluent +csharp_space_between_square_brackets = false +csharp_space_between_empty_square_brackets = false + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Code block and new line preferences +dotnet_style_prefer_braces = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = false -dotnet_naming_symbols.interface.applicable_kinds = interface -dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interface.required_modifiers = +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true -dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum -dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types.required_modifiers = +# Field preferences +dotnet_style_readonly_field = true -dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method -dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = +# Assignment preferences +dotnet_code_quality_unused_parameters = warning +dotnet_code_quality_unused_value = warning -# Naming styles +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = suggestion +# Naming conventions +dotnet_naming_style.pascal_case.capitalization = pascal_case dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = dotnet_naming_style.begins_with_i.capitalization = pascal_case +dotnet_code_quality_explicit_tuple_names = true:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case - -dotnet_code_quality_unused_parameters = all:suggestion +#### C# Coding Conventions #### +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options -dotnet_sort_system_directives_first = true - -dotnet_style_operator_placement_when_wrapping = beginning_of_line -dotnet_style_predefined_type_for_locals_parameters_members = true:silent -dotnet_style_namespace_match_folder = false:none -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion -dotnet_style_prefer_auto_properties = true:silent -dotnet_style_object_initializer = true:suggestion -dotnet_style_prefer_collection_expression = false:suggestion - -[*.cs] -# ref: https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options +# var preferences +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false +# Labeling and using directives csharp_indent_labels = one_less_than_current csharp_using_directive_placement = outside_namespace:warning -csharp_prefer_simple_using_statement = true:suggestion csharp_prefer_static_local_function = true:suggestion +# Namespace and method preferences csharp_style_namespace_declarations = file_scoped:suggestion csharp_style_prefer_method_group_conversion = true:silent csharp_style_prefer_top_level_statements = true:silent csharp_style_prefer_primary_constructors = true:suggestion + +# Expression-bodied Members csharp_style_expression_bodied_methods = true:silent -csharp_style_expression_bodied_constructors = false:silent -csharp_style_expression_bodied_operators = false:silent csharp_style_expression_bodied_properties = true:silent csharp_style_expression_bodied_indexers = true:silent csharp_style_expression_bodied_accessors = true:silent csharp_style_expression_bodied_lambdas = true:silent -csharp_style_expression_bodied_local_functions = false:silent + +# Function preferences csharp_style_prefer_local_over_anonymous_function = true:suggestion -csharp_style_unused_value_assignment_preference = unused_local_variable:suggestion csharp_style_prefer_switch_expression = true:suggestion csharp_style_prefer_pattern_matching = true:silent csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion csharp_style_pattern_matching_over_as_with_null_check = true:suggestion csharp_style_prefer_not_pattern = true:suggestion csharp_style_prefer_extended_property_pattern = true:suggestion + +# Variable declarations csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_unused_value_assignment_preference = unused_local_variable:suggestion + +# Modifier preferences +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true -# braces +# Code block preferences csharp_prefer_braces = true:suggestion +csharp_prefer_simple_using_statement = true:suggestion +csharp_type_declaration_braces = next_line +csharp_empty_block_style = together_same_line +# New line preferences csharp_new_line_before_open_brace = methods, properties, control_blocks, types csharp_new_line_before_else = true csharp_new_line_before_catch = true @@ -115,12 +146,3 @@ csharp_new_line_before_finally = true csharp_new_line_before_members_in_object_initializers = false csharp_new_line_before_members_in_anonymous_types = false csharp_new_line_between_query_expression_clauses = false - -# braces overrides -csharp_type_declaration_braces = next_line -csharp_empty_block_style = together_same_line - -# diagnostic analyzers -dotnet_diagnostic.CA1303.severity = none -dotnet_diagnostic.CS1591.severity = silent -dotnet_diagnostic.IDE0058.severity = none diff --git a/.github/workflows/deploy-package.yml b/.github/workflows/deploy-package.yml index c471762e6..e3d15e657 100644 --- a/.github/workflows/deploy-package.yml +++ b/.github/workflows/deploy-package.yml @@ -34,6 +34,12 @@ jobs: with: fetch-depth: 0 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: "8.x" + dotnet-quality: "ga" + - name: Setup GitVersion uses: gittools/actions/gitversion/setup@v1.2.0 with: @@ -51,21 +57,8 @@ jobs: - name: Compose version id: compose run: | - ver=${{ steps.gitversion.outputs.majorMinorPatch }}${{ inputs.preview && '-preview.' || '' }}${{ inputs.preview && steps.gitversion.outputs.preReleaseNumber || inputs.preview && github.run_number || '' }} - echo "version=$ver" >> "$GITHUB_OUTPUT" - - - name: Update release notes URL - uses: jacobtomlinson/gha-find-replace@v3 - with: - find: "https://github.com/DaveSkender/Stock.Indicators/releases" - replace: "https://github.com/DaveSkender/Stock.Indicators/releases/tag/${{ steps.compose.outputs.version }}" - regex: false - - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: "8.x" - dotnet-quality: "ga" + composed_version=${{ steps.gitversion.outputs.majorMinorPatch }}${{ inputs.preview && '-preview.' || '' }}${{ inputs.preview && steps.gitversion.outputs.preReleaseNumber || inputs.preview && github.run_number || '' }} + echo "version=$composed_version" >> "$GITHUB_OUTPUT" - name: Build library run: > @@ -100,7 +93,7 @@ jobs: echo "| Minor | ${{ steps.gitversion.outputs.minor }} |" echo "| Patch | ${{ steps.gitversion.outputs.patch }} |" echo "| Base | ${{ steps.gitversion.outputs.majorMinorPatch }} |" - echo "| Composed | ${{ steps.compose.outputs.version }} |" + echo "| Composed | ${{ steps.compose.outputs.version }} |" } >> $GITHUB_STEP_SUMMARY deploy: diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..939f58bb1 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,12 @@ +{ + "recommendations": [ + "codacy-app.codacy", + "adrianwilczynski.user-secrets", + "github.vscode-github-actions", + "github.vscode-pull-request-github", + "mechatroner.rainbow-csv", + "dotjoshjohnson.xml", + "redhat.vscode-yaml", + "vunguyentuan.vscode-css-variables" + ] +} diff --git a/docs/examples/.editorconfig b/docs/examples/.editorconfig index 1139ee700..12d648ba1 100644 --- a/docs/examples/.editorconfig +++ b/docs/examples/.editorconfig @@ -1,38 +1,5 @@ -# top-most EditorConfig file -root = true - -# global baselines -[*] -charset = utf-8 -end_of_line = lf - -indent_style = space -indent_size = 2 -tab_width = 2 - -line_length = 150 -max_line_length = 150 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.{csproj,props,targets}] -indent_style = space -indent_size = 2 - -[*.yml] -indent_style = space -indent_size = 2 - -[*.{cs,vb}] -tab_width = 4 -indent_size = 4 -end_of_line = lf - -#### Naming styles #### - -# Naming styles - -dotnet_style_namespace_match_folder = false:none +# code analysis for EXAMPLES only +root = false [*.cs] -dotnet_diagnostic.IDE0058.severity = none +dotnet_diagnostic.CS1591.severity = none # Missing XML comment for public members diff --git a/src/.editorconfig b/src/.editorconfig new file mode 100644 index 000000000..8dffa4aec --- /dev/null +++ b/src/.editorconfig @@ -0,0 +1,5 @@ +# code analysis for SRC only +root = false + +[*.cs] +dotnet_diagnostic.CS1591.severity = suggestion # Missing XML comment for public members diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props new file mode 100644 index 000000000..11f9313e2 --- /dev/null +++ b/src/Directory.Packages.props @@ -0,0 +1,10 @@ + + + true + true + $(NoWarn);NU1507 + + + + + diff --git a/src/Indicators.csproj b/src/Indicators.csproj index c54bdd1e7..77a2751c7 100644 --- a/src/Indicators.csproj +++ b/src/Indicators.csproj @@ -1,12 +1,13 @@ - net8.0 + net8.0;net6.0 + 12.0 Dave Skender Stock Indicators for .NET @2020 Dave Skender - Stock Indicators for .NET. Transform financial market price quotes into technical analysis indicators such as MACD, Stochastic RSI, Average True Range, Parabolic SAR, etc. Nothing more. + Transform financial market price data into technical analysis indicators such as MACD, Stochastic RSI, Average True Range, Parabolic SAR, and more. https://github.com/DaveSkender/Stock.Indicators git @@ -14,7 +15,6 @@ Skender.Stock.Indicators Skender.Stock.Indicators - 12.0 en-US false @@ -22,19 +22,9 @@ https://dotnet.stockindicators.dev Skender.Stock.Indicators - Indicators;Stock;Market;Technical;Analysis;Algorithmic;Trading;Trade;Trend;Momentum;Finance;Algorithm;Algo; - AlgoTrading;Financial;Strategy;Chart;Charting;Oscillator;Overlay;Equity;Bitcoin;Crypto;Cryptocurrency;Forex; - Quantitative;Historical;Quotes;Accumulation;Distribution;ADL;Aroon;True;Range;ATR;Bollinger;Bands;Commodity; - CCI;Chandelier;Exit;Chaikin;Money;Flow;CMF;MFI;Kaufman;Adaptive;Simple;Weighted;Exponential;Moving;Average; - Convergence;Divergence;MACD;SMA;EMA;DEMA;TEMA;TRIX;Smoothed;Elder-ray;Bull;Bear;Power;Force;Hurst;Exponent; - Relative;Strength;Index;RSI;ConnorsRSI;Stochastic;Directional;Movement;ADX;Donchian;Keltner;Channel; - Heikin-Ashi;Hilbert;Transform;Instantaneous;Trendline;Hull;HMA;Ichimoku;Cloud;On-balance;Volume;OBV; - Price;PMO;PRS;Parabolic;SAR;Stop;Reverse;PSAR;Pivot;Points;Rate;Change;ROC;Linear;Regression;R-Squared;Renko; - Schaff;Cycle;Slope;Standard;Deviation;Beta;Convexity;Correlation;SuperTrend;Ulcer;Ultimate;Vortex;Williams;%R; - Alligator;Gator;Fractal;Chaos;Choppiness;Endpoint;WMA;ZigZag;.NET;C#;Library;Package; + Stock;Market;Indicators;Technical;Analysis;Algorithmic;Trading;Trade;Finance; README.md - https://github.com/DaveSkender/Stock.Indicators/releases Apache-2.0 icon.png @@ -53,30 +43,30 @@ true true true - True + true - True + true - True + true - True + true - True + true - + diff --git a/src/_common/BinarySettings.cs b/src/_common/BinarySettings.cs index 4f8f9e63f..e62867410 100644 --- a/src/_common/BinarySettings.cs +++ b/src/_common/BinarySettings.cs @@ -36,11 +36,20 @@ public readonly struct BinarySettings( byte settings, byte mask = 0b11111111) : IEquatable { + /// + /// Gets the binary settings. + /// public byte Settings { get; } = settings; + + /// + /// Gets the mask for settings inheritance. + /// public byte Mask { get; } = mask; - // use default settings (none) and mask - // important: this explicit parameterless ctor required for struct + /// + /// Initializes a new instance of the struct + /// with default settings (0b00000000) and mask (0b11111111). + /// public BinarySettings() : this(settings: 0b00000000) { } /// @@ -93,18 +102,23 @@ public BinarySettings Combine(BinarySettings parentSettings) return new BinarySettings((byte)(Settings | maskedParentSettings), parentSettings.Mask); } + /// public override bool Equals(object? obj) => obj is BinarySettings other && Equals(other); + /// public bool Equals(BinarySettings other) => Settings == other.Settings && Mask == other.Mask; + /// public override int GetHashCode() => HashCode.Combine(Settings, Mask); + /// public static bool operator ==(BinarySettings left, BinarySettings right) => left.Equals(right); + /// public static bool operator !=(BinarySettings left, BinarySettings right) => !(left == right); } diff --git a/src/_common/Candles/Candles.Models.cs b/src/_common/Candles/Candles.Models.cs index 3ed861d29..36a5bd36f 100644 --- a/src/_common/Candles/Candles.Models.cs +++ b/src/_common/Candles/Candles.Models.cs @@ -2,6 +2,15 @@ namespace Skender.Stock.Indicators; // CANDLESTICK MODELS +/// +/// Represents the properties of a candlestick. +/// +/// The timestamp of the candlestick. +/// The opening price of the candlestick. +/// The highest price of the candlestick. +/// The lowest price of the candlestick. +/// The closing price of the candlestick. +/// The volume of the candlestick. [Serializable] public record CandleProperties ( @@ -13,25 +22,65 @@ public record CandleProperties decimal Volume ) : Quote(Timestamp, Open, High, Low, Close, Volume) { - // raw sizes + /// + /// Gets the size of the candlestick. + /// public decimal? Size => High - Low; + + /// + /// Gets the body size of the candlestick. + /// public decimal? Body => Open > Close ? Open - Close : Close - Open; + + /// + /// Gets the size of the upper wick of the candlestick. + /// public decimal? UpperWick => High - (Open > Close ? Open : Close); + + /// + /// Gets the size of the lower wick of the candlestick. + /// public decimal? LowerWick => (Open > Close ? Close : Open) - Low; - // percent sizes + /// + /// Gets the body size as a percentage of the total size. + /// public double? BodyPct => Size != 0 ? (double?)(Body / Size) : 1; + + /// + /// Gets the upper wick size as a percentage of the total size. + /// public double? UpperWickPct => Size != 0 ? (double?)(UpperWick / Size) : 1; + + /// + /// Gets the lower wick size as a percentage of the total size. + /// public double? LowerWickPct => Size != 0 ? (double?)(LowerWick / Size) : 1; - // directional info + /// + /// Gets a value indicating whether the candlestick is bullish. + /// public bool IsBullish => Close > Open; + + /// + /// Gets a value indicating whether the candlestick is bearish. + /// public bool IsBearish => Close < Open; } +/// +/// Represents the result of a candlestick analysis. +/// [Serializable] public record CandleResult : ISeries { + /// + /// Initializes a new instance of the record. + /// + /// The timestamp of the result. + /// The quote used for the analysis. + /// The match result of the analysis. + /// The price associated with the result. public CandleResult( DateTime timestamp, IQuote quote, @@ -44,6 +93,13 @@ public CandleResult( Candle = quote.ToCandle(); } + /// + /// Initializes a new instance of the record. + /// + /// The timestamp of the result. + /// The candlestick properties. + /// The match result of the analysis. + /// The price associated with the result. public CandleResult( DateTime timestamp, CandleProperties candle, @@ -56,8 +112,23 @@ public CandleResult( Candle = candle; } + /// + /// Gets the timestamp of the result. + /// public DateTime Timestamp { get; init; } + + /// + /// Gets the price associated with the result. + /// public decimal? Price { get; init; } + + /// + /// Gets the match result of the analysis. + /// public Match Match { get; init; } + + /// + /// Gets the candlestick properties. + /// public CandleProperties Candle { get; init; } } diff --git a/src/_common/Candles/Candles.Utilities.cs b/src/_common/Candles/Candles.Utilities.cs index 82d278637..0032d5665 100644 --- a/src/_common/Candles/Candles.Utilities.cs +++ b/src/_common/Candles/Candles.Utilities.cs @@ -1,12 +1,26 @@ namespace Skender.Stock.Indicators; -public static partial class Utility +/// +/// Utility for candlestick data. +/// +public static class Candlesticks { + /// + /// Condenses the list of candle results by filtering out those with no match. + /// + /// The list of candle results to condense. + /// A condensed list of candle results. public static IReadOnlyList Condense( this IReadOnlyList candleResults) => candleResults .Where(candle => candle.Match != Match.None) .ToList(); + /// + /// Converts a quote to candle properties. + /// + /// The type of the quote. + /// The quote to convert. + /// The candle properties. public static CandleProperties ToCandle( this TQuote quote) where TQuote : IQuote => new( @@ -17,7 +31,12 @@ public static CandleProperties ToCandle( Close: quote.Close, Volume: quote.Volume); - // convert/sort quotes into candles list + /// + /// Converts and sorts a list of quotes into a list of candle properties. + /// + /// The type of the quote. + /// The list of quotes to convert. + /// A sorted list of candle properties. public static IReadOnlyList ToCandles( this IReadOnlyList quotes) where TQuote : IQuote => quotes diff --git a/src/_common/Enums.cs b/src/_common/Enums.cs index f254baaea..16bc408ac 100644 --- a/src/_common/Enums.cs +++ b/src/_common/Enums.cs @@ -1,10 +1,7 @@ namespace Skender.Stock.Indicators; -// SHARED ENUMERATIONS -// note: indicator unique ENUMS filed with their models - /// -/// Cache action instruction or outcome +/// Cache action instruction or outcome. /// internal enum Act { @@ -30,80 +27,247 @@ internal enum Act } /// -/// Part or value of a quote candle +/// Part or value of a quote candle. /// public enum CandlePart { + /// + /// Opening price. + /// Open, + + /// + /// Highest price. + /// High, + + /// + /// Lowest price. + /// Low, + + /// + /// Closing price. + /// Close, + + /// + /// Volume of trades. + /// Volume, + + /// + /// Average of high and low prices. + /// HL2, + + /// + /// Average of high, low, and close prices. + /// HLC3, + + /// + /// Average of open and close prices. + /// OC2, + + /// + /// Average of open, high, and low prices. + /// OHL3, + + /// + /// Average of open, high, low, and close prices. + /// OHLC4 } /// -/// Candle close or high/low wick values +/// Price candle end types used to select which aspect of the candle +/// to use in indicator threshold calculations. /// public enum EndType { + /// + /// Closing price. + /// Close = 0, + + /// + /// High and low prices. + /// HighLow = 1 } /// -/// Candlestick pattern matching type +/// Candlestick pattern matching type. /// public enum Match { + /// + /// Strong bullish confirmation. + /// BullConfirmed = 200, + + /// + /// Bullish signal. + /// BullSignal = 100, + + /// + /// Bullish basis. + /// BullBasis = 10, + + /// + /// Neutral pattern. + /// Neutral = 1, + + /// + /// No pattern. + /// None = 0, + + /// + /// Bearish basis. + /// BearBasis = -10, + + /// + /// Bearish signal. + /// BearSignal = -100, + + /// + /// Strong bearish confirmation. + /// BearConfirmed = -200 } /// -/// Moving average type +/// Moving average type. /// public enum MaType { + /// + /// Arnaud Legoux Moving Average. + /// ALMA, + + /// + /// Double Exponential Moving Average. + /// DEMA, + + /// + /// Exponential Percentage Moving Average. + /// EPMA, + + /// + /// Exponential Moving Average. + /// EMA, + + /// + /// Hull Moving Average. + /// HMA, + + /// + /// Kaufman Adaptive Moving Average. + /// KAMA, + + /// + /// MESA Adaptive Moving Average. + /// MAMA, + + /// + /// Simple Moving Average. + /// SMA, + + /// + /// Smoothed Moving Average. + /// SMMA, + + /// + /// Triple Exponential Moving Average. + /// TEMA, + + /// + /// Weighted Moving Average. + /// WMA } /// -/// Period size. Usually referring to the -/// time period represented in a quote candle. +/// Period size, usually referring to the time period represented in a quote candle. /// public enum PeriodSize { + /// + /// Monthly period. + /// Month, + + /// + /// Weekly period. + /// Week, + + /// + /// Daily period. + /// Day, + + /// + /// Four-hour period. + /// FourHours, + + /// + /// Two-hour period. + /// TwoHours, + + /// + /// One-hour period. + /// OneHour, + + /// + /// Thirty-minute period. + /// ThirtyMinutes, + + /// + /// Fifteen-minute period. + /// FifteenMinutes, + + /// + /// Five-minute period. + /// FiveMinutes, + + /// + /// Three-minute period. + /// ThreeMinutes, + + /// + /// Two-minute period. + /// TwoMinutes, + + /// + /// One-minute period. + /// OneMinute } diff --git a/src/_common/Generics/Pruning.cs b/src/_common/Generics/Pruning.cs index 997957a91..ff49ad98d 100644 --- a/src/_common/Generics/Pruning.cs +++ b/src/_common/Generics/Pruning.cs @@ -1,10 +1,18 @@ namespace Skender.Stock.Indicators; -// REMOVE AND PRUNING of SERIES - -public static partial class Utility +/// +/// Utility for removing and pruning series data. +/// +public static class Pruning { - // REMOVE SPECIFIC PERIODS + /// + /// Removes a specified number of warmup periods from the beginning of the series. + /// + /// The type of elements in the series. + /// The series from which to remove warmup periods. + /// The number of periods to remove. + /// A new series with the specified number of warmup periods removed. + /// Thrown when removePeriods is less than 0. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList series, int removePeriods) @@ -13,7 +21,13 @@ public static IReadOnlyList RemoveWarmupPeriods( "If specified, the Remove Periods value must be greater than or equal to 0.") : series.Remove(removePeriods); - // REMOVE PERIODS + /// + /// Removes a specified number of periods from the beginning of the series. + /// + /// The type of elements in the series. + /// The series from which to remove periods. + /// The number of periods to remove. + /// A new list with the specified number of periods removed. internal static List Remove( this IReadOnlyList series, int removePeriods) diff --git a/src/_common/Generics/Seek.cs b/src/_common/Generics/Seek.cs index 8397739af..c94ea2d27 100644 --- a/src/_common/Generics/Seek.cs +++ b/src/_common/Generics/Seek.cs @@ -1,10 +1,18 @@ namespace Skender.Stock.Indicators; -// SEEK & FIND in SERIES - +/// +/// Provides extension methods for seeking and finding elements in a series. +/// public static class Seeking { - // FIND by DATE + /// + /// Finds an element in the series by its timestamp. + /// + /// The type of the elements in the series, which must implement . + /// The series of elements to search. + /// The timestamp to look for. + /// The element with the matching timestamp, or the default value if not found. + /// Thrown when the series is null. public static T? Find( this IReadOnlyList series, DateTime lookupDate) diff --git a/src/_common/Generics/Sorting.cs b/src/_common/Generics/Sorting.cs index 56ed632c6..143c0a724 100644 --- a/src/_common/Generics/Sorting.cs +++ b/src/_common/Generics/Sorting.cs @@ -1,9 +1,16 @@ namespace Skender.Stock.Indicators; -// SORTED of SERIES - -public static partial class Utility +/// +/// Provides extension methods for sorting a series of elements. +/// +public static class Sorting { + /// + /// Sorts the series by their timestamps in ascending order. + /// + /// The type of the elements in the series, which must implement . + /// The series of elements to sort. + /// A read-only list of the sorted elements. public static IReadOnlyList ToSortedList( this IEnumerable series) where TSeries : ISeries diff --git a/src/_common/Generics/Transforms.cs b/src/_common/Generics/Transforms.cs index 11e6b3ffd..cd3ec05a0 100644 --- a/src/_common/Generics/Transforms.cs +++ b/src/_common/Generics/Transforms.cs @@ -2,11 +2,18 @@ namespace Skender.Stock.Indicators; -// GENERIC TRANSFORMS - -public static partial class Utility +/// +/// Provides extension methods for transforming collections. +/// +public static class Transforming { - // TO COLLECTION + /// + /// Converts an to a . + /// + /// The type of the elements in the source collection. + /// The source collection to convert. + /// A containing the elements from the source collection. + /// Thrown when the source collection is null. internal static Collection ToCollection(this IEnumerable source) { ArgumentNullException.ThrowIfNull(source); diff --git a/src/_common/ISeries.cs b/src/_common/ISeries.cs index 3483eddde..7d3b0ecb1 100644 --- a/src/_common/ISeries.cs +++ b/src/_common/ISeries.cs @@ -5,10 +5,8 @@ namespace Skender.Stock.Indicators; /// public interface ISeries { - // TODO: consider adding (long) UnixDate (seconds) to ISeries - /// - /// Date/time of record. + /// Gets the date/time of the record. /// /// /// For types, this is the @@ -19,6 +17,14 @@ public interface ISeries /// DateTime Timestamp { get; } + // TODO: consider adding (long) UnixDate (seconds) to ISeries + + /// + /// Gets the date/time of the record. + /// + /// + /// Deprecated. Use 'Timestamp' instead. + /// [Obsolete("Deprecated. Use 'Timestamp' instead.")] DateTime Date => Timestamp; } diff --git a/src/_common/Incrementals/IIncremental.cs b/src/_common/Incrementals/IIncremental.cs index 90de40106..b9ad914a0 100644 --- a/src/_common/Incrementals/IIncremental.cs +++ b/src/_common/Incrementals/IIncremental.cs @@ -1,52 +1,44 @@ namespace Skender.Stock.Indicators; +/// +/// Interface for adding reusable incremental values to a list. +/// public interface IAddReusable { /// - /// Converts an incremental value into - /// the next incremental indicator value - /// and added it to the list. + /// Converts an incremental value into the next incremental indicator value and adds it to the list. /// - /// Date context - /// Next value + /// The date context. + /// The next value. void Add(DateTime timestamp, double value); /// - /// Converts an incremental reusable value into - /// the next incremental indicator value - /// and added it to the list. + /// Converts an incremental reusable value into the next incremental indicator value and adds it to the list. /// - /// Next value + /// The next reusable value. void Add(IReusable value); /// - /// Converts batch of reusable values into - /// the next incremental indicator values - /// and added them to the list. + /// Converts a batch of reusable values into the next incremental indicator values and adds them to the list. /// - /// - /// Chronologically ordered batch of IReusable info - /// + /// A chronologically ordered batch of values. void Add(IReadOnlyList values); } +/// +/// Interface for adding incremental quotes to a list. +/// public interface IAddQuote { /// - /// Converts an incremental quote into - /// the next incremental indicator value - /// and added it to the list. + /// Converts an incremental quote into the next incremental indicator value and adds it to the list. /// - /// Next quote value + /// The next quote value. void Add(IQuote quote); /// - /// Converts batch of quotes into - /// the next incremental indicator values - /// and added them to the list. + /// Converts a batch of quotes into the next incremental indicator values and adds them to the list. /// - /// - /// Chronologically ordered batch of quotes - /// + /// A chronologically ordered batch of quotes. void Add(IReadOnlyList quotes); } diff --git a/src/_common/Math/NullMath.cs b/src/_common/Math/NullMath.cs index a9a197922..f82656be5 100644 --- a/src/_common/Math/NullMath.cs +++ b/src/_common/Math/NullMath.cs @@ -11,40 +11,89 @@ namespace Skender.Stock.Indicators; /// public static class NullMath { + /// + /// Returns the absolute value of a nullable double. + /// + /// The nullable double value. + /// The absolute value, or null if the input is null. public static double? Abs(this double? value) => value is null ? null : value < 0 ? (double)-value : (double)value; + /// + /// Rounds a nullable decimal value to a specified number of fractional digits. + /// + /// The nullable decimal value. + /// The number of fractional digits. + /// The rounded value, or null if the input is null. public static decimal? Round(this decimal? value, int digits) => value is null ? null : Math.Round((decimal)value, digits); + /// + /// Rounds a nullable double value to a specified number of fractional digits. + /// + /// The nullable double value. + /// The number of fractional digits. + /// The rounded value, or null if the input is null. public static double? Round(this double? value, int digits) => value is null ? null : Math.Round((double)value, digits); + /// + /// Rounds a double value to a specified number of fractional digits. + /// + /// The double value. + /// The number of fractional digits. + /// The rounded value. public static double Round(this double value, int digits) => Math.Round(value, digits); + /// + /// Rounds a decimal value to a specified number of fractional digits. + /// + /// The decimal value. + /// The number of fractional digits. + /// The rounded value. public static decimal Round(this decimal value, int digits) => Math.Round(value, digits); + /// + /// Converts a nullable double value to NaN if it is null. + /// + /// The nullable double value. + /// The value, or NaN if the input is null. public static double Null2NaN(this double? value) => value ?? double.NaN; + /// + /// Converts a nullable decimal value to NaN if it is null. + /// + /// The nullable decimal value. + /// The value as a double, or NaN if the input is null. public static double Null2NaN(this decimal? value) => value is null ? double.NaN : (double)value; + /// + /// Converts a nullable double value to null if it is NaN. + /// + /// The nullable double value. + /// The value, or null if the input is NaN. public static double? NaN2Null(this double? value) => value is double.NaN ? null : value; + /// + /// Converts a double value to null if it is NaN. + /// + /// The double value. + /// The value, or null if the input is NaN. public static double? NaN2Null(this double value) => double.IsNaN(value) ? null diff --git a/src/_common/Math/Numerical.cs b/src/_common/Math/Numerical.cs index bba184344..30a7a1a6b 100644 --- a/src/_common/Math/Numerical.cs +++ b/src/_common/Math/Numerical.cs @@ -1,8 +1,16 @@ namespace Skender.Stock.Indicators; +/// +/// Provides numerical utility methods. +/// public static class Numerical { - // STANDARD DEVIATION + /// + /// Calculates the standard deviation of an array of double values. + /// + /// The array of double values. + /// The standard deviation of the values. + /// Thrown when the values array is null. public static double StdDev(this double[] values) { ArgumentNullException.ThrowIfNull( @@ -34,7 +42,14 @@ public static double StdDev(this double[] values) return Math.Sqrt(sumSq / n); } - // SLOPE of BEST FIT LINE + /// + /// Calculates the slope of the best fit line for the given x and y values. + /// + /// The array of x values. + /// The array of y values. + /// The slope of the best fit line. + /// Thrown when the x or y array is null. + /// Thrown when the x and y arrays are not the same size. public static double Slope(double[] x, double[] y) { // validate parameters @@ -80,7 +95,12 @@ public static double Slope(double[] x, double[] y) return slope; } - // DATE ROUNDING + /// + /// Rounds down a DateTime to the nearest interval. + /// + /// The DateTime value. + /// The interval to round down to. + /// The rounded down DateTime value. internal static DateTime RoundDown( this DateTime dateTime, TimeSpan interval) => interval == TimeSpan.Zero @@ -88,7 +108,11 @@ internal static DateTime RoundDown( : dateTime .AddTicks(-(dateTime.Ticks % interval.Ticks)); - // PERIOD-SIZE to TIMESPAN CONVERSION + /// + /// Converts a PeriodSize to a TimeSpan. + /// + /// The PeriodSize value. + /// The corresponding TimeSpan value. internal static TimeSpan ToTimeSpan(this PeriodSize periodSize) => periodSize switch { PeriodSize.OneMinute => TimeSpan.FromMinutes(1), @@ -105,7 +129,11 @@ internal static TimeSpan ToTimeSpan(this PeriodSize periodSize) _ => TimeSpan.Zero }; - // DETERMINE DECIMAL PLACES + /// + /// Determines the number of decimal places in a decimal value. + /// + /// The decimal value. + /// The number of decimal places. internal static int GetDecimalPlaces(this decimal n) { // source: https://stackoverflow.com/a/30205131/4496145 diff --git a/src/_common/Observables/IStreamHub.cs b/src/_common/Observables/IStreamHub.cs index f73b1250d..f4cb4b18d 100644 --- a/src/_common/Observables/IStreamHub.cs +++ b/src/_common/Observables/IStreamHub.cs @@ -1,7 +1,5 @@ namespace Skender.Stock.Indicators; -// STREAM HUB INTERFACE - /// /// Streaming hub: management of observer /// and observable indicator data diff --git a/src/_common/Observables/IStreamObservable.cs b/src/_common/Observables/IStreamObservable.cs index 394678c6f..1fe143665 100644 --- a/src/_common/Observables/IStreamObservable.cs +++ b/src/_common/Observables/IStreamObservable.cs @@ -4,16 +4,19 @@ namespace Skender.Stock.Indicators; #region chain and quote variants -/// +/// public interface IQuoteProvider : IChainProvider - where T : IQuote + where T : IQuote { + /// + /// Gets the read-only list of quotes. + /// IReadOnlyList Quotes { get; } } -/// +/// public interface IChainProvider : IStreamObservable - where T : IReusable; + where T : IReusable; #endregion /// @@ -25,7 +28,7 @@ public interface IChainProvider : IStreamObservable public interface IStreamObservable { /// - /// Hub observable properties and behaviors. + /// Gets the hub observable properties and behaviors. /// /// /// This struct holds cumulative overrides for @@ -58,22 +61,22 @@ public interface IStreamObservable BinarySettings Properties { get; } /// - /// Current number of subscribers + /// Gets the current number of subscribers. /// int ObserverCount { get; } /// - /// Provider currently has subscribers + /// Gets a value indicating whether the provider currently has subscribers. /// bool HasObservers { get; } /// - /// Checks if a specific observer is subscribed + /// Checks if a specific observer is subscribed. /// /// - /// Subscriber IStreamObserver reference + /// Subscriber IStreamObserver reference. /// - /// True if subscribed/registered + /// True if subscribed/registered. bool HasSubscriber(IStreamObserver observer); /// @@ -96,7 +99,7 @@ public interface IStreamObservable bool Unsubscribe(IStreamObserver observer); /// - /// Unsubscribe all observers (subscribers) + /// Unsubscribe all observers (subscribers). /// void EndTransmission(); diff --git a/src/_common/Observables/IStreamObserver.cs b/src/_common/Observables/IStreamObserver.cs index 32398e668..ed5c89a30 100644 --- a/src/_common/Observables/IStreamObserver.cs +++ b/src/_common/Observables/IStreamObserver.cs @@ -1,7 +1,5 @@ namespace Skender.Stock.Indicators; -// STREAM (OBSERVER) INTERFACE - /// /// Management of observing + processing of streamed inbound data. /// diff --git a/src/_common/Observables/StreamHub.Observable.cs b/src/_common/Observables/StreamHub.Observable.cs index 5f6ec2269..efc79e41b 100644 --- a/src/_common/Observables/StreamHub.Observable.cs +++ b/src/_common/Observables/StreamHub.Observable.cs @@ -4,34 +4,34 @@ namespace Skender.Stock.Indicators; public abstract partial class StreamHub : IStreamObservable { - private readonly HashSet> _observers = new(); + private readonly HashSet> _observers = []; - /// + /// public bool HasObservers => _observers.Count > 0; - /// + /// public int ObserverCount => _observers.Count; - /// + /// public IReadOnlyList ReadCache => Cache; - /// + /// public virtual BinarySettings Properties { get; init; } = new(0); // default 0b00000000 #region SUBSCRIPTION SERVICES - /// + /// public IDisposable Subscribe(IStreamObserver observer) { _observers.Add(observer); return new Unsubscriber(_observers, observer); } - /// + /// public bool Unsubscribe(IStreamObserver observer) => _observers.Remove(observer); - /// + /// public bool HasSubscriber(IStreamObserver observer) => _observers.Contains(observer); @@ -58,7 +58,7 @@ private class Unsubscriber( public void Dispose() => _observers.Remove(_observer); } - /// + /// public void EndTransmission() { foreach (IStreamObserver observer diff --git a/src/_common/Observables/StreamHub.Observer.cs b/src/_common/Observables/StreamHub.Observer.cs index c2b374336..522c3a3d8 100644 --- a/src/_common/Observables/StreamHub.Observer.cs +++ b/src/_common/Observables/StreamHub.Observer.cs @@ -4,7 +4,7 @@ namespace Skender.Stock.Indicators; public abstract partial class StreamHub : IStreamObserver { - /// + /// public bool IsSubscribed => Provider.HasSubscriber(this); /// @@ -24,7 +24,7 @@ public abstract partial class StreamHub : IStreamObserver // Observer methods - /// + /// public virtual void OnAdd(TIn item, bool notify, int? indexHint) { // Convert the input item to the output type and append it to the cache. @@ -34,19 +34,19 @@ public virtual void OnAdd(TIn item, bool notify, int? indexHint) AppendCache(result, notify); } - /// + /// public void OnChange(DateTime fromTimestamp) => Rebuild(fromTimestamp); - /// + /// public void OnError(Exception exception) => throw exception; - /// + /// public void OnCompleted() => Unsubscribe(); - /// + /// public void Unsubscribe() { // Ensure thread-safety for EndTransmission > OnCompleted-type race conditions diff --git a/src/_common/Observables/StreamHub.Utilities.cs b/src/_common/Observables/StreamHub.Utilities.cs index 49b1c528c..1721a775a 100644 --- a/src/_common/Observables/StreamHub.Utilities.cs +++ b/src/_common/Observables/StreamHub.Utilities.cs @@ -1,18 +1,18 @@ namespace Skender.Stock.Indicators; -// STREAM HUB (STATIC UTILITIES) - +/// +/// Provides static utility methods for stream hub operations. +/// public static class StreamHub { /// - /// Try to find index position of the provided timestamp + /// Try to find index position of the provided timestamp. /// - /// - /// Timestamp to seek - /// - /// Index of timestamp or -1 when not found - /// - /// True if found + /// Type of the items in the cache, must implement ISeries. + /// The cache to search. + /// Timestamp to seek. + /// Index of timestamp or -1 when not found. + /// True if found. internal static bool TryFindIndex( this IReadOnlyList cache, DateTime timestamp, @@ -26,17 +26,12 @@ internal static bool TryFindIndex( /// /// Get the cache index based on item equality. /// - /// - /// - /// Time-series object to find in cache - /// - /// - /// Throw exception when item is not found - /// - /// Index position - /// - /// When items is not found (should never happen). - /// + /// Type of the items in the cache, must implement ISeries. + /// The cache to search. + /// Time-series object to find in cache. + /// Throw exception when item is not found. + /// Index position. + /// When item is not found (should never happen). internal static int GetIndex( this IReadOnlyList cache, T cachedItem, @@ -109,20 +104,15 @@ internal static int GetIndex( /// /// /// Only use this when you are looking for a point in time - /// without a matching item for context. In most cases + /// without a matching item for context. In most cases /// is more appropriate. /// - /// - /// - /// Timestamp of cached item - /// - /// - /// Throw exception when timestamp is not found - /// - /// Index position - /// - /// When timestamp is not found (should never happen). - /// + /// Type of the items in the cache, must implement ISeries. + /// The cache to search. + /// Timestamp of cached item. + /// Throw exception when timestamp is not found. + /// Index position. + /// When timestamp is not found (should never happen). internal static int GetIndex( this IReadOnlyList cache, DateTime timestamp, @@ -163,14 +153,13 @@ internal static int GetIndex( /// /// /// Only use this when you are looking for a point in time - /// without a matching item for context. In most cases + /// without a matching item for context. In most cases /// is more appropriate. /// - /// - /// - /// Timestamp of cached item - /// - /// First index position or -1 if not found + /// Type of the items in the cache, must implement ISeries. + /// The cache to search. + /// Timestamp of cached item. + /// First index position or -1 if not found. internal static int GetIndexGte( this IReadOnlyList cache, DateTime timestamp) diff --git a/src/_common/Observables/StreamHub.cs b/src/_common/Observables/StreamHub.cs index 53c6302f4..62f419350 100644 --- a/src/_common/Observables/StreamHub.cs +++ b/src/_common/Observables/StreamHub.cs @@ -9,9 +9,10 @@ public abstract partial class StreamHub : IStreamHub { #region constructor - /// - /// Streaming data provider - /// + /// + /// Initializes a new instance of the class. + /// + /// Streaming data provider. private protected StreamHub(IStreamObservable provider) { // store provider reference @@ -35,29 +36,31 @@ private protected StreamHub(IStreamObservable provider) public bool IsFaulted { get; private set; } /// - /// Cache of stored values (base). + /// Gets the cache of stored values (base). /// - internal List Cache { get; } = new(); + internal List Cache { get; } = []; /// - /// Current count of repeated caching attempts. + /// Gets the current count of repeated caching attempts. /// An overflow condition is triggered after 100. /// internal byte OverflowCount { get; private set; } /// - /// Reference to this hub's provider's cache. + /// Gets the reference to this hub's provider's cache. /// protected IReadOnlyList ProviderCache { get; } /// - /// Most recent item saved to cache. + /// Gets or sets the most recent item saved to cache. /// private TOut? LastItem { get; set; } #endregion - // reset fault flag and condition + /// + /// Resets the fault flag and condition. + /// /// public void ResetFault() { @@ -65,27 +68,35 @@ public void ResetFault() IsFaulted = false; } - // fetch cache reference - /// + /// + /// Fetches the cache reference. + /// + /// The cache reference. public IReadOnlyList GetCacheRef() => Cache; + /// public abstract override string ToString(); /// - /// Converts incremental value into - /// an indicator candidate and cache position. + /// Converts incremental value into an indicator candidate and cache position. /// - /// New item from provider - /// Provider index hint - /// Cacheable item candidate and index hint - protected abstract (TOut result, int index) - ToIndicator(TIn item, int? indexHint); + /// New item from provider. + /// Provider index hint. + /// Cacheable item candidate and index hint. + protected abstract (TOut result, int index) ToIndicator(TIn item, int? indexHint); #region ADD & ANALYZE - public void Add(TIn newIn) - => OnAdd(newIn, notify: true, null); + /// + /// Adds a new item to the stream. + /// + /// The new item to add. + public void Add(TIn newIn) => OnAdd(newIn, notify: true, null); + /// + /// Adds a batch of new items to the stream. + /// + /// The batch of new items to add. public void Add(IEnumerable batchIn) { foreach (TIn newIn in batchIn.OrderBy(x => x.Timestamp)) @@ -94,6 +105,10 @@ public void Add(IEnumerable batchIn) } } + /// + /// Inserts a new item into the stream. + /// + /// The new item to insert. public void Insert(TIn newIn) { // note: should only be used when newer timestamps @@ -123,14 +138,11 @@ public void Insert(TIn newIn) } /// - /// Perform appropriate caching action after analysis. + /// Performs appropriate caching action after analysis. /// It will add if new, ignore if duplicate, or rebuild if late-arrival. /// - /// TSeries item to cache. - /// - /// Notify subscribers of change (send to observers). - /// This is disabled for bulk operations like rebuild. - /// + /// Item to cache. + /// Notify subscribers of change (send to observers). This is disabled for bulk operations like rebuild. protected void AppendCache(TOut result, bool notify) { // check overflow/duplicates @@ -166,9 +178,9 @@ protected void AppendCache(TOut result, bool notify) } /// - /// Add item to cache and notify observers. + /// Adds an item to cache and notifies observers. /// - /// Item to add to end of cache + /// Item to add to end of cache. /// Inherited notification instructions. private void Add(TOut item, bool notify) { @@ -189,16 +201,12 @@ private void Add(TOut item, bool notify) } /// - /// Validate outbound item and compare to prior cached item, + /// Validates outbound item and compares to prior cached item, /// to gracefully manage and prevent overflow conditions. /// - /// Cacheable time-series object - /// - /// True if item is repeating and duplicate was suppressed. - /// - /// - /// Too many sequential duplicates were detected. - /// + /// Cacheable time-series object. + /// True if item is repeating and duplicate was suppressed. + /// Too many sequential duplicates were detected. private bool IsOverflowing(TOut item) { // skip first arrival @@ -219,10 +227,10 @@ private bool IsOverflowing(TOut item) if (OverflowCount > 100) { const string msg = """ - A repeated stream update exceeded the 100 attempt threshold. - Check and remove circular chains or check your stream provider. - Provider terminated. - """; + A repeated stream update exceeded the 100 attempt threshold. + Check and remove circular chains or check your stream provider. + Provider terminated. + """; IsFaulted = true; @@ -234,15 +242,7 @@ Provider terminated. // bypass duplicate prevention // when forced caching is enabled - if (Properties[1]) - { - return false; - - // note: will still overflow - // when the 100 limit is reached - } - - return true; + return !Properties[1]; } // not repeating @@ -254,7 +254,10 @@ Provider terminated. #region REMOVE & REMOVE RANGE - /// remove cached item + /// + /// Removes a cached item. + /// + /// The cached item to remove. /// public void Remove(TOut cachedItem) { @@ -262,7 +265,10 @@ public void Remove(TOut cachedItem) NotifyObserversOnChange(cachedItem.Timestamp); } - /// remove cached item at index position + /// + /// Removes a cached item at a specific index position. + /// + /// The index position of the cached item to remove. /// public void RemoveAt(int cacheIndex) { @@ -271,7 +277,11 @@ public void RemoveAt(int cacheIndex) NotifyObserversOnChange(cachedItem.Timestamp); } - /// remove cache range from timestamp + /// + /// Removes a range of cached items from a specific timestamp. + /// + /// The timestamp from which to start removing cached items. + /// Whether to notify observers of the change. /// public void RemoveRange(DateTime fromTimestamp, bool notify) { @@ -288,7 +298,11 @@ public void RemoveRange(DateTime fromTimestamp, bool notify) } } - /// remove cache range from index + /// + /// Removes a range of cached items from a specific index. + /// + /// The index from which to start removing cached items. + /// Whether to notify observers of the change. /// public void RemoveRange(int fromIndex, bool notify) { @@ -309,7 +323,9 @@ public void RemoveRange(int fromIndex, bool notify) #region REBUILD & REINITIALIZE - // full reset + /// + /// Fully resets the stream hub. + /// /// public void Reinitialize() { @@ -325,12 +341,16 @@ public void Reinitialize() // and subscribe; will it miss any high frequency data? } - // rebuild cache + /// + /// Rebuilds the cache. + /// /// - public void Rebuild() - => Rebuild(DateTime.MinValue); + public void Rebuild() => Rebuild(DateTime.MinValue); - // rebuild cache from timestamp + /// + /// Rebuilds the cache from a specific timestamp. + /// + /// The timestamp from which to start rebuilding the cache. /// public void Rebuild(DateTime fromTimestamp) { @@ -353,7 +373,10 @@ public void Rebuild(DateTime fromTimestamp) NotifyObserversOnChange(fromTimestamp); } - // rebuild cache from index + /// + /// Rebuilds the cache from a specific index. + /// + /// The index from which to start rebuilding the cache. /// public void Rebuild(int fromIndex) { @@ -367,7 +390,7 @@ public void Rebuild(int fromIndex) } /// - /// Rollback internal state to a point in time. + /// Rollbacks internal state to a point in time. /// Behavior varies by indicator. /// /// @@ -375,9 +398,7 @@ public void Rebuild(int fromIndex) /// point in time (e.g. when rebuilding cache). Example: /// /// - /// - /// Point in time to restore. - /// + /// Point in time to restore. protected virtual void RollbackState(DateTime timestamp) { // note: override when rollback is needed @@ -396,6 +417,9 @@ IStreamObservable provider where TIn : IReusable where TOut : IQuote { + /// + /// Gets the quotes. + /// public IReadOnlyList Quotes => Cache; }; diff --git a/src/_common/ObsoleteV3.cs b/src/_common/ObsoleteV3.cs index c0a2a245e..828eec155 100644 --- a/src/_common/ObsoleteV3.cs +++ b/src/_common/ObsoleteV3.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; // ReSharper disable all +#pragma warning disable CS1591 // Missing XML comments namespace Skender.Stock.Indicators; diff --git a/src/_common/Quotes/Quote.Aggregates.cs b/src/_common/Quotes/Quote.Aggregates.cs index 8af1b3ccd..03e60d16c 100644 --- a/src/_common/Quotes/Quote.Aggregates.cs +++ b/src/_common/Quotes/Quote.Aggregates.cs @@ -4,9 +4,13 @@ namespace Skender.Stock.Indicators; public static partial class Quotes { - // aggregation (quantization) - /// - /// + /// + /// Aggregates the quotes to a specified period size. + /// + /// Type of the quotes, must implement IQuote. + /// The quotes to aggregate. + /// The new period size to aggregate to. + /// A list of aggregated quotes. public static IReadOnlyList Aggregate( this IReadOnlyList quotes, PeriodSize newSize) @@ -36,9 +40,14 @@ public static IReadOnlyList Aggregate( // month } - // aggregation (quantization) using TimeSpan - /// - /// + /// + /// Aggregates the quotes to a specified time span. + /// + /// Type of the quotes, must implement IQuote. + /// The quotes to aggregate. + /// The time span to aggregate to. + /// A list of aggregated quotes. + /// Thrown when the time span is less than or equal to zero. public static IReadOnlyList Aggregate( this IReadOnlyList quotes, TimeSpan timeSpan) diff --git a/src/_common/Quotes/Quote.Converters.cs b/src/_common/Quotes/Quote.Converters.cs index 7db6c964c..abca8da71 100644 --- a/src/_common/Quotes/Quote.Converters.cs +++ b/src/_common/Quotes/Quote.Converters.cs @@ -1,12 +1,18 @@ namespace Skender.Stock.Indicators; -// QUOTE UTILITIES (CONVERTERS) - +/// +/// Provides methods for manipulating and handling quote data. +/// public static partial class Quotes { /* LISTS */ - // convert TQuote type list to built-in Quote type list (public API only) + /// + /// Convert TQuote type list to built-in Quote type list (public API only). + /// + /// The type of the quote. + /// The list of quotes to convert. + /// A list of converted quotes. public static IReadOnlyList ToQuoteList( this IReadOnlyList quotes) where TQuote : IQuote @@ -16,7 +22,12 @@ public static IReadOnlyList ToQuoteList( .Select(x => x.ToQuote()) .ToList(); - // convert TQuote type list to QuoteD type list + /// + /// Convert TQuote type list to QuoteD type list. + /// + /// The type of the quote. + /// The list of quotes to convert. + /// A list of converted quotes in double precision. internal static List ToQuoteDList( this IReadOnlyList quotes) where TQuote : IQuote @@ -27,7 +38,12 @@ internal static List ToQuoteDList( /* TYPES */ - // convert any IQuote type to native Quote type (public API only) + /// + /// Convert any IQuote type to native Quote type (public API only). + /// + /// The type of the quote. + /// The quote to convert. + /// A converted quote. public static Quote ToQuote(this TQuote quote) where TQuote : IQuote @@ -39,7 +55,11 @@ public static Quote ToQuote(this TQuote quote) Close: quote.Close, Volume: quote.Volume); - // convert to quote in double precision + /// + /// Convert to quote in double precision. + /// + /// The quote to convert. + /// A converted quote in double precision. internal static QuoteD ToQuoteD(this IQuote quote) => new( diff --git a/src/_common/Quotes/Quote.Exceptions.cs b/src/_common/Quotes/Quote.Exceptions.cs index 2ba860334..1e8e7f86c 100644 --- a/src/_common/Quotes/Quote.Exceptions.cs +++ b/src/_common/Quotes/Quote.Exceptions.cs @@ -1,29 +1,45 @@ namespace Skender.Stock.Indicators; +/// +/// Exception thrown when invalid quotes are encountered. +/// [Serializable] public class InvalidQuotesException : ArgumentOutOfRangeException { - public InvalidQuotesException() - { - } + /// + /// Initializes a new instance of the class. + /// + public InvalidQuotesException() { } + /// + /// Initializes a new instance of the class with the name of the parameter that causes this exception. + /// + /// The name of the parameter that causes this exception. public InvalidQuotesException(string? paramName) - : base(paramName) - { - } + : base(paramName) { } + /// + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for this exception. + /// The exception that is the cause of the current exception. public InvalidQuotesException(string? message, Exception? innerException) - : base(message, innerException) - { - } + : base(message, innerException) { } + /// + /// Initializes a new instance of the class with the name of the parameter that causes this exception and a specified error message. + /// + /// The name of the parameter that causes this exception. + /// The error message that explains the reason for this exception. public InvalidQuotesException(string? paramName, string? message) - : base(paramName, message) - { - } + : base(paramName, message) { } + /// + /// Initializes a new instance of the class with the name of the parameter that causes this exception, the value of the argument, and a specified error message. + /// + /// The name of the parameter that causes this exception. + /// The value of the argument that causes this exception. + /// The error message that explains the reason for this exception. public InvalidQuotesException(string? paramName, object? actualValue, string? message) - : base(paramName, actualValue, message) - { - } + : base(paramName, actualValue, message) { } } diff --git a/src/_common/Quotes/Quote.Models.cs b/src/_common/Quotes/Quote.Models.cs index 46e65e5b6..887ebaa67 100644 --- a/src/_common/Quotes/Quote.Models.cs +++ b/src/_common/Quotes/Quote.Models.cs @@ -83,6 +83,7 @@ public record Quote decimal Volume ) : IQuote { + /// public double Value => (double)Close; // TODO: add [Obsolete] auto-getter/setter for 'Date' property @@ -106,5 +107,6 @@ internal record QuoteD double Volume ) : IReusable { + /// public double Value => Close; } diff --git a/src/_common/Quotes/Quote.StreamHub.cs b/src/_common/Quotes/Quote.StreamHub.cs index 6f43feb02..d729cbbd5 100644 --- a/src/_common/Quotes/Quote.StreamHub.cs +++ b/src/_common/Quotes/Quote.StreamHub.cs @@ -4,6 +4,12 @@ namespace Skender.Stock.Indicators; public static partial class Quotes { + /// + /// Converts an IQuoteProvider to a QuoteHub. + /// + /// The type of quote. + /// The quote provider to convert. + /// A new instance of QuoteHub. public static QuoteHub ToQuote( this IQuoteProvider quoteProvider) where TQuote : IQuote => new(quoteProvider); @@ -11,14 +17,22 @@ public static QuoteHub ToQuote( #endregion /// -/// Quote provider (abstract base) +/// Represents a hub for managing quotes. /// +/// The type of quote. public class QuoteHub : QuoteProvider where TQuote : IQuote { + /// + /// Initializes a new instance of the class. + /// public QuoteHub() : base(new EmptyQuoteProvider()) { } + /// + /// Initializes a new instance of the class with a specified provider. + /// + /// The quote provider. public QuoteHub( IQuoteProvider provider) : base(provider) @@ -28,6 +42,7 @@ public QuoteHub( // METHODS + /// protected override (TQuote result, int index) ToIndicator(TQuote item, int? indexHint) { @@ -37,6 +52,7 @@ protected override (TQuote result, int index) return (item, index == -1 ? Cache.Count : index); } + /// public override string ToString() => $"QUOTES<{typeof(TQuote).Name}>: {Quotes.Count} items"; } @@ -55,18 +71,54 @@ public class EmptyQuoteProvider /// It does not transfer its setting to its children. /// public BinarySettings Properties { get; } = new(0b00000001, 0b11111110); + + /// + /// Gets the number of observers. + /// public int ObserverCount => 0; + + /// + /// Gets a value indicating whether there are any observers. + /// public bool HasObservers => false; + + /// + /// Gets the list of quotes. + /// public IReadOnlyList Quotes { get; } = Array.Empty(); + + /// + /// Gets a reference to the cache. + /// + /// A read-only list of quotes. public IReadOnlyList GetCacheRef() => Array.Empty(); + + /// + /// Determines whether the specified observer is a subscriber. + /// + /// The observer to check. + /// true if the observer is a subscriber; otherwise, false. public bool HasSubscriber(IStreamObserver observer) => false; + /// + /// Subscribes the specified observer. + /// + /// The observer to subscribe. + /// A disposable object that can be used to unsubscribe. public IDisposable Subscribe(IStreamObserver observer) => throw new InvalidOperationException(); + /// + /// Unsubscribes the specified observer. + /// + /// The observer to unsubscribe. + /// true if the observer was unsubscribed; otherwise, false. public bool Unsubscribe(IStreamObserver observer) => throw new InvalidOperationException(); + /// + /// Ends the transmission. + /// public void EndTransmission() => throw new InvalidOperationException(); } diff --git a/src/_common/Quotes/info.xml b/src/_common/Quotes/info.xml deleted file mode 100644 index 721479e5b..000000000 --- a/src/_common/Quotes/info.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - Converts historical quotes into larger bar sizes. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - PeriodSize enum representing the new bar size. - Time series of historical quote values. - Invalid parameter value provided. - - - - - Converts historical quotes into larger bar sizes. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - TimeSpan representing the new bar size. - Time series of historical quote values. - Invalid parameter value provided. - - - diff --git a/src/_common/Reusable/Reusable.Utilities.cs b/src/_common/Reusable/Reusable.Utilities.cs index d0439f591..b28a791cf 100644 --- a/src/_common/Reusable/Reusable.Utilities.cs +++ b/src/_common/Reusable/Reusable.Utilities.cs @@ -1,10 +1,17 @@ namespace Skender.Stock.Indicators; -// REUSABLE TYPE UTILITIES - +/// +/// Provides utility methods for reusable types. +/// public static partial class Reusable { - // convert IQuote type list to IReusable list + /// + /// Converts a list of quotes to a list of reusable types. + /// + /// The type of the quote. + /// The list of quotes. + /// The part of the candle to use. + /// A list of reusable types. public static IReadOnlyList ToReusableList( this IReadOnlyList quotes, CandlePart candlePart) @@ -60,7 +67,12 @@ internal static IReadOnlyList RemoveWarmupPeriods( // Note: Some or all of these may already be removed. } - // convert TQuote element to a basic chainable class + /// + /// Converts a quote to a basic chainable class. + /// + /// The quote to convert. + /// The part of the candle to use. + /// A reusable type. internal static IReusable ToReusable(this IQuote q, CandlePart candlePart) => q.ToQuotePart(candlePart); } diff --git a/src/_common/Use (QuotePart)/QuotePart.Models.cs b/src/_common/Use (QuotePart)/QuotePart.Models.cs index 8bd3e4851..b97c54c97 100644 --- a/src/_common/Use (QuotePart)/QuotePart.Models.cs +++ b/src/_common/Use (QuotePart)/QuotePart.Models.cs @@ -10,10 +10,15 @@ public record QuotePart double Value ) : IReusable { + /// + /// Initializes a new instance of the class using an object. + /// + /// The reusable object to initialize from. public QuotePart(IReusable reusable) : this(reusable?.Timestamp ?? default, reusable?.Value ?? default) { } + /// public double Value { get; } = Value; } diff --git a/src/_common/Use (QuotePart)/QuotePart.StaticSeries.cs b/src/_common/Use (QuotePart)/QuotePart.StaticSeries.cs index 53821b9f0..be46657c7 100644 --- a/src/_common/Use (QuotePart)/QuotePart.StaticSeries.cs +++ b/src/_common/Use (QuotePart)/QuotePart.StaticSeries.cs @@ -1,7 +1,8 @@ namespace Skender.Stock.Indicators; -// USE / QUOTE CONVERTER (SERIES) - +/// +/// Provides extension methods for converting quotes to quote parts. +/// public static partial class QuoteParts { /// @@ -12,10 +13,10 @@ public static partial class QuoteParts /// Use this conversion if indicator needs to /// use something other than the default Close price. /// - /// - /// Sorted list of IQuote or IReusable items - /// - /// List of IReusable items + /// The type of the quote. + /// Sorted list of IQuote or IReusable items. + /// The candle part to convert to. + /// List of IReusable items. public static IReadOnlyList ToQuotePart( this IReadOnlyList quotes, CandlePart candlePart) @@ -32,7 +33,10 @@ public static IReadOnlyList ToQuotePart( return result; } - // QuotePart alias + /// + /// Converts to + /// an list. + /// /// public static IReadOnlyList Use( this IReadOnlyList quotes, diff --git a/src/_common/Use (QuotePart)/QuotePart.StreamHub.cs b/src/_common/Use (QuotePart)/QuotePart.StreamHub.cs index 4e7448fbe..e48d33ad1 100644 --- a/src/_common/Use (QuotePart)/QuotePart.StreamHub.cs +++ b/src/_common/Use (QuotePart)/QuotePart.StreamHub.cs @@ -3,8 +3,15 @@ namespace Skender.Stock.Indicators; // USE / QUOTE CONVERTER (STREAM HUB) #region hub interface and initializer + +/// +/// Interface for Quote Part Hub. +/// public interface IQuotePartHub { + /// + /// Gets the selected candle part. + /// CandlePart CandlePartSelection { get; } // TODO: consider renaming to IBarPartHub, with IQuote to IBar @@ -12,6 +19,13 @@ public interface IQuotePartHub public static partial class QuoteParts { + /// + /// Converts the quote provider to a QuotePartHub. + /// + /// The type of the quote. + /// The quote provider. + /// The candle part to select. + /// A QuotePartHub instance. public static QuotePartHub ToQuotePart( this IQuoteProvider quoteProvider, CandlePart candlePart) @@ -20,12 +34,21 @@ public static QuotePartHub ToQuotePart( } #endregion +/// +/// Represents a hub for managing quote parts. +/// +/// The type of the quote. public class QuotePartHub : ChainProvider, IQuotePartHub where TQuote : IQuote { #region constructors + /// + /// Initializes a new instance of the class. + /// + /// The quote provider. + /// The candle part to select. internal QuotePartHub( IQuoteProvider provider, CandlePart candlePart) @@ -37,10 +60,14 @@ internal QuotePartHub( } #endregion + /// + /// Gets the selected candle part. + /// public CandlePart CandlePartSelection { get; init; } // METHODS + /// protected override (QuotePart result, int index) ToIndicator(TQuote item, int? indexHint) { @@ -51,6 +78,7 @@ QuotePart r return (r, indexHint ?? Cache.Count); } + /// public override string ToString() => $"QUOTE-PART({CandlePartSelection.ToString().ToUpperInvariant()})"; } diff --git a/src/a-d/Adl/Adl.Models.cs b/src/a-d/Adl/Adl.Models.cs index 4e62a66ee..c10375293 100644 --- a/src/a-d/Adl/Adl.Models.cs +++ b/src/a-d/Adl/Adl.Models.cs @@ -1,5 +1,12 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the Accumulation/Distribution Line (ADL) indicator. +/// +/// Gets the timestamp of the result. +/// Gets the ADL value. +/// Gets the Money Flow Multiplier. +/// Gets the Money Flow Volume. [Serializable] public record AdlResult ( @@ -9,5 +16,6 @@ public record AdlResult double? MoneyFlowVolume = null ) : IReusable { + /// public double Value => Adl; } diff --git a/src/a-d/Adl/Adl.StaticSeries.cs b/src/a-d/Adl/Adl.StaticSeries.cs index 10f833ed4..4fb5f827a 100644 --- a/src/a-d/Adl/Adl.StaticSeries.cs +++ b/src/a-d/Adl/Adl.StaticSeries.cs @@ -1,11 +1,18 @@ namespace Skender.Stock.Indicators; -// ACCUMULATION/DISTRIBUTION LINE (SERIES) - +/// +/// Provides methods for calculating the Accumulation/Distribution Line (ADL). +/// public static partial class Adl { - public static IReadOnlyList ToAdl( - this IReadOnlyList source) + /// + /// Calculates the Accumulation/Distribution Line (ADL) from a series of quotes. + /// + /// The type of the quote. + /// The source list of quotes. + /// A read-only list of ADL results. + /// Thrown when the source is null. + public static IReadOnlyList ToAdl(this IReadOnlyList source) where TQuote : IQuote { ArgumentNullException.ThrowIfNull(source); diff --git a/src/a-d/Adl/Adl.StreamHub.cs b/src/a-d/Adl/Adl.StreamHub.cs index e336e51b3..e5b7fdaab 100644 --- a/src/a-d/Adl/Adl.StreamHub.cs +++ b/src/a-d/Adl/Adl.StreamHub.cs @@ -1,32 +1,40 @@ namespace Skender.Stock.Indicators; -// ACCUMULATION/DISTRIBUTION LINE (STREAM HUB) - -#region hub initializer - +/// +/// Provides methods for the Accumulation/Distribution Line (ADL) indicator. +/// public static partial class Adl { + /// + /// Converts the quote provider to an ADL hub. + /// + /// The type of the input quote. + /// The quote provider. + /// An instance of . public static AdlHub ToAdl( this IQuoteProvider quoteProvider) where TIn : IQuote => new(quoteProvider); } -#endregion +/// +/// Represents a hub for the Accumulation/Distribution Line (ADL) indicator. +/// +/// The type of the input quote. public class AdlHub : ChainProvider where TIn : IQuote { - #region constructors - + /// + /// Initializes a new instance of the class. + /// + /// The quote provider. internal AdlHub(IQuoteProvider provider) : base(provider) { Reinitialize(); } - #endregion - - // METHODS + /// protected override (AdlResult result, int index) ToIndicator(TIn item, int? indexHint) { @@ -44,5 +52,6 @@ protected override (AdlResult result, int index) return (r, i); } + /// public override string ToString() => Cache.Count == 0 ? "ADL" : $"ADL({Cache[0].Timestamp:d})"; } diff --git a/src/a-d/Adl/Adl.Utilities.cs b/src/a-d/Adl/Adl.Utilities.cs index d964a392a..3917e2231 100644 --- a/src/a-d/Adl/Adl.Utilities.cs +++ b/src/a-d/Adl/Adl.Utilities.cs @@ -1,25 +1,17 @@ namespace Skender.Stock.Indicators; -// ACCUMULATION/DISTRIBUTION LINE (UTILITIES) - -/// -/// See the -/// Stock Indicators for .NET online guide for more information. -/// public static partial class Adl { /// - /// Get the next incremental Accumulation/Distribution Line(ADL) value. + /// Get the next incremental Accumulation/Distribution Line (ADL) value. /// - /// timestamp - /// High price, current period - /// Low price, current period - /// Close price, current period - /// Volume, current period - /// New ADL result value - /// - /// Last ADL value, from prior period - /// + /// Timestamp of the current period. + /// High price of the current period. + /// Low price of the current period. + /// Close price of the current period. + /// Volume of the current period. + /// Last ADL value from the prior period. + /// New ADL result value. public static AdlResult Increment( DateTime timestamp, double high, @@ -43,6 +35,16 @@ public static AdlResult Increment( MoneyFlowVolume: mfv); } + /// + /// Get the next incremental Accumulation/Distribution Line (ADL) value. + /// + /// Timestamp of the current period. + /// High price of the current period. + /// Low price of the current period. + /// Close price of the current period. + /// Volume of the current period. + /// Last ADL value from the prior period. + /// New ADL result value. internal static AdlResult Increment( DateTime timestamp, decimal high, diff --git a/src/a-d/Adx/Adx.Models.cs b/src/a-d/Adx/Adx.Models.cs index 11a3bb56d..783a741fd 100644 --- a/src/a-d/Adx/Adx.Models.cs +++ b/src/a-d/Adx/Adx.Models.cs @@ -1,5 +1,13 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the ADX (Average Directional Index) calculation. +/// +/// Gets the timestamp of the result. +/// Gets the Positive Directional Indicator (PDI) value. +/// Gets the Negative Directional Indicator (MDI) value. +/// Gets the Average Directional Index (ADX) value. +/// Gets the Average Directional Movement Rating (ADXR) value. [Serializable] public record AdxResult ( @@ -10,5 +18,6 @@ public record AdxResult double? Adxr = null ) : IReusable { + /// public double Value => Adx.Null2NaN(); } diff --git a/src/a-d/Adx/Adx.StaticSeries.cs b/src/a-d/Adx/Adx.StaticSeries.cs index 45839098a..35d94dd7e 100644 --- a/src/a-d/Adx/Adx.StaticSeries.cs +++ b/src/a-d/Adx/Adx.StaticSeries.cs @@ -1,16 +1,30 @@ namespace Skender.Stock.Indicators; -// AVERAGE DIRECTIONAL INDEX (SERIES) - +/// +/// Provides methods for calculating the Average Directional Index (ADX). +/// public static partial class Adx { + /// + /// Calculates the Average Directional Index (ADX) from a series of quotes. + /// + /// The type of the quote. + /// The list of quotes. + /// The number of periods to look back for the ADX calculation. + /// A list of ADX results. public static IReadOnlyList ToAdx( - this IReadOnlyList quotes, - int lookbackPeriods = 14) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcAdx(lookbackPeriods); - + this IReadOnlyList quotes, + int lookbackPeriods = 14) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcAdx(lookbackPeriods); + + /// + /// Calculates the ADX from a list of quotes. + /// + /// The list of quotes. + /// The number of periods to look back for the ADX calculation. + /// A list of ADX results. private static List CalcAdx( this IReadOnlyList source, int lookbackPeriods = 14) @@ -94,9 +108,9 @@ private static List CalcAdx( } else { - trs = prevTrs - prevTrs / lookbackPeriods + tr; - pdm = prevPdm - prevPdm / lookbackPeriods + pdm1; - mdm = prevMdm - prevMdm / lookbackPeriods + mdm1; + trs = prevTrs - (prevTrs / lookbackPeriods) + tr; + pdm = prevPdm - (prevPdm / lookbackPeriods) + pdm1; + mdm = prevMdm - (prevMdm / lookbackPeriods) + mdm1; } prevTrs = trs; @@ -123,9 +137,9 @@ private static List CalcAdx( double adx = double.NaN; double adxr = double.NaN; - if (i > 2 * lookbackPeriods - 1) + if (i > (2 * lookbackPeriods) - 1) { - adx = (prevAdx * (lookbackPeriods - 1) + dx) / lookbackPeriods; + adx = ((prevAdx * (lookbackPeriods - 1)) + dx) / lookbackPeriods; double priorAdx = results[i - lookbackPeriods + 1].Adx.Null2NaN(); @@ -134,7 +148,7 @@ private static List CalcAdx( } // initial ADX - else if (i == 2 * lookbackPeriods - 1) + else if (i == (2 * lookbackPeriods) - 1) { sumDx += dx; adx = sumDx / lookbackPeriods; diff --git a/src/a-d/Adx/Adx.Utilities.cs b/src/a-d/Adx/Adx.Utilities.cs index 1611769cd..c73e8745b 100644 --- a/src/a-d/Adx/Adx.Utilities.cs +++ b/src/a-d/Adx/Adx.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// AVERAGE DIRECTIONAL INDEX (UTILITIES) - +/// +/// Provides utility methods for the ADX (Average Directional Index) indicator. +/// public static partial class Adx { - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the ADX results. + /// + /// The list of ADX results. + /// A list of ADX results with the warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -16,7 +20,11 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove((2 * n) + 100); } - // parameter validation + /// + /// Validates the parameters for the ADX calculation. + /// + /// The number of periods to look back for the calculation. + /// Thrown when the lookback periods are less than or equal to 1. internal static void Validate( int lookbackPeriods) { diff --git a/src/a-d/Adx/info.xml b/src/a-d/Adx/info.xml deleted file mode 100644 index 578fe7127..000000000 --- a/src/a-d/Adx/info.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - Directional Movement Index (DMI) and Average Directional Movement Index (ADX) is a measure of price directional movement. - It includes upward and downward indicators, and is often used to measure strength of trend. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of ADX and Plus/Minus Directional values. - Invalid parameter value provided. - - - - Get the next incremental Average Directional Movement Index (ADX) result. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quote. - New AdxResult value. - - diff --git a/src/a-d/Alligator/Alligator.Models.cs b/src/a-d/Alligator/Alligator.Models.cs index 3b689004b..9685d4474 100644 --- a/src/a-d/Alligator/Alligator.Models.cs +++ b/src/a-d/Alligator/Alligator.Models.cs @@ -1,5 +1,12 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the Alligator indicator calculation. +/// +/// The date and time of the record. +/// The value of the Jaw line of the Alligator indicator. +/// The value of the Teeth line of the Alligator indicator. +/// The value of the Lips line of the Alligator indicator. [Serializable] public record AlligatorResult ( diff --git a/src/a-d/Alligator/Alligator.StaticSeries.cs b/src/a-d/Alligator/Alligator.StaticSeries.cs index f35217b70..73f998f61 100644 --- a/src/a-d/Alligator/Alligator.StaticSeries.cs +++ b/src/a-d/Alligator/Alligator.StaticSeries.cs @@ -1,14 +1,12 @@ namespace Skender.Stock.Indicators; -// WILLIAMS ALLIGATOR (SERIES) - +/// +/// Provides methods for calculating the Williams Alligator indicator. +/// public static partial class Alligator { - // SERIES, from CHAIN /// - /// Williams Alligator is an indicator that transposes multiple moving averages, - /// showing chart patterns that creator Bill Williams compared to an alligator's - /// feeding habits when describing market movement. + /// Calculates the Williams Alligator indicator for a series of data. /// /// /// T must be or type diff --git a/src/a-d/Alligator/Alligator.StreamHub.cs b/src/a-d/Alligator/Alligator.StreamHub.cs index 8ade9b1ac..099c4f59c 100644 --- a/src/a-d/Alligator/Alligator.StreamHub.cs +++ b/src/a-d/Alligator/Alligator.StreamHub.cs @@ -4,19 +4,56 @@ namespace Skender.Stock.Indicators; #region hub interface and initializer +/// +/// Interface for Alligator Hub. +/// public interface IAlligatorHub { + /// + /// Gets the number of periods for the jaw. + /// int JawPeriods { get; } + + /// + /// Gets the offset for the jaw. + /// int JawOffset { get; } + + /// + /// Gets the number of periods for the teeth. + /// int TeethPeriods { get; } + + /// + /// Gets the offset for the teeth. + /// int TeethOffset { get; } + + /// + /// Gets the number of periods for the lips. + /// int LipsPeriods { get; } + + /// + /// Gets the offset for the lips. + /// int LipsOffset { get; } } public static partial class Alligator { - // HUB, from Chain Provider + /// + /// Converts a chain provider to an Alligator hub. + /// + /// The type of the input. + /// The chain provider. + /// The number of periods for the jaw. + /// The offset for the jaw. + /// The number of periods for the teeth. + /// The offset for the teeth. + /// The number of periods for the lips. + /// The offset for the lips. + /// An Alligator hub. public static AlligatorHub ToAlligator( this IChainProvider chainProvider, int jawPeriods = 13, @@ -37,14 +74,29 @@ public static AlligatorHub ToAlligator( } #endregion +/// +/// Represents a stream hub for calculating the Alligator indicator. +/// +/// The type of the input. +/// public class AlligatorHub - : StreamHub, IAlligatorHub - where TIn : IReusable + : StreamHub, IAlligatorHub + where TIn : IReusable { #region constructors private readonly string hubName; + /// + /// Initializes a new instance of the class. + /// + /// The chain provider. + /// The number of periods for the jaw. + /// The offset for the jaw. + /// The number of periods for the teeth. + /// The offset for the teeth. + /// The number of periods for the lips. + /// The offset for the lips. internal AlligatorHub( IChainProvider provider, int jawPeriods, int jawOffset, @@ -70,17 +122,42 @@ internal AlligatorHub( } #endregion + /// + /// Gets the number of periods for the jaw. + /// public int JawPeriods { get; init; } + + /// + /// Gets the offset for the jaw. + /// public int JawOffset { get; init; } + + /// + /// Gets the number of periods for the teeth. + /// public int TeethPeriods { get; init; } + + /// + /// Gets the offset for the teeth. + /// public int TeethOffset { get; init; } + + /// + /// Gets the number of periods for the lips. + /// public int LipsPeriods { get; init; } + + /// + /// Gets the offset for the lips. + /// public int LipsOffset { get; init; } // METHODS + /// public override string ToString() => hubName; + /// protected override (AlligatorResult result, int index) ToIndicator(TIn item, int? indexHint) { diff --git a/src/a-d/Alligator/Alligator.Utilities.cs b/src/a-d/Alligator/Alligator.Utilities.cs index 03050cb34..09f4dce61 100644 --- a/src/a-d/Alligator/Alligator.Utilities.cs +++ b/src/a-d/Alligator/Alligator.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// WILLIAMS ALLIGATOR (UTILITIES) - +/// +/// Provides utility methods for the Williams Alligator indicator. +/// public static partial class Alligator { - // CONDENSE (REMOVE null results) + /// + /// Removes non-essential records containing null values for Jaw, Teeth, and Lips. + /// + /// The Alligator results to evaluate. + /// A condensed list of Alligator results. public static IReadOnlyList Condense( this IEnumerable results) { @@ -18,7 +23,11 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } - // remove recommended periods + /// + /// Removes the recommended quantity of results from the beginning of the results list. + /// + /// The Alligator results to evaluate. + /// A pruned list of Alligator results. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -29,7 +38,16 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } - // parameter validation + /// + /// Validates the parameters for the Williams Alligator indicator. + /// + /// The lookback periods for the Jaw. + /// The offset periods for the Jaw. + /// The lookback periods for the Teeth. + /// The offset periods for the Teeth. + /// The lookback periods for the Lips. + /// The offset periods for the Lips. + /// Thrown when any parameter is out of range. internal static void Validate( int jawPeriods, int jawOffset, diff --git a/src/a-d/Alma/Alma.Models.cs b/src/a-d/Alma/Alma.Models.cs index 419a23d56..29db95aec 100644 --- a/src/a-d/Alma/Alma.Models.cs +++ b/src/a-d/Alma/Alma.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the +/// ALMA (Arnaud Legoux Moving Average) calculation. +/// +/// The timestamp of the result. +/// The ALMA value. [Serializable] public record AlmaResult ( @@ -7,5 +13,6 @@ public record AlmaResult double? Alma ) : IReusable { + /// public double Value => Alma.Null2NaN(); } diff --git a/src/a-d/Alma/Alma.StaticSeries.cs b/src/a-d/Alma/Alma.StaticSeries.cs index 372f68c9e..4d2577a45 100644 --- a/src/a-d/Alma/Alma.StaticSeries.cs +++ b/src/a-d/Alma/Alma.StaticSeries.cs @@ -1,9 +1,20 @@ namespace Skender.Stock.Indicators; -// ARNAUD LEGOUX MOVING AVERAGE (SERIES) - +/// +/// Provides methods for calculating the Arnaud Legoux Moving Average (ALMA). +/// public static partial class Alma { + /// + /// Calculates the Arnaud Legoux Moving Average (ALMA) for a series of data. + /// + /// The type of elements in the source series. + /// The source series. + /// The number of periods to look back. Default is 9. + /// The offset for the ALMA calculation. Default is 0.85. + /// The sigma for the ALMA calculation. Default is 6. + /// A list of ALMA results. + /// Thrown when the source series is null. public static IReadOnlyList ToAlma( this IReadOnlyList source, int lookbackPeriods = 9, diff --git a/src/a-d/Alma/Alma.Utilities.cs b/src/a-d/Alma/Alma.Utilities.cs index da7f11eb8..347d727b0 100644 --- a/src/a-d/Alma/Alma.Utilities.cs +++ b/src/a-d/Alma/Alma.Utilities.cs @@ -1,10 +1,20 @@ namespace Skender.Stock.Indicators; -// ARNAUD LEGOUX MOVING AVERAGE (UTILITIES) - +/// +/// Provides utility methods for the ALMA (Arnaud Legoux Moving Average) indicator. +/// public static partial class Alma { - // parameter validation + /// + /// Validates the parameters for the ALMA calculation. + /// + /// The number of periods to look back for the calculation. + /// The offset parameter for the ALMA calculation, must be between 0 and 1. + /// The sigma parameter for the ALMA calculation, must be greater than 0. + /// + /// Thrown when the lookback periods are less than or equal to 1, + /// the offset is not between 0 and 1, or the sigma is less than or equal to 0. + /// internal static void Validate( int lookbackPeriods, double offset, diff --git a/src/a-d/Alma/info.xml b/src/a-d/Alma/info.xml deleted file mode 100644 index b774ea05f..000000000 --- a/src/a-d/Alma/info.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - Arnaud Legoux Moving Average (ALMA) is a Gaussian distribution - weighted moving average of price over a lookback window. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Adjusts smoothness versus responsiveness. - Defines the width of the Gaussian normal distribution. - Time series of ALMA values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/Aroon/Aroon.Models.cs b/src/a-d/Aroon/Aroon.Models.cs index 5c4adcdda..fb80f80fb 100644 --- a/src/a-d/Aroon/Aroon.Models.cs +++ b/src/a-d/Aroon/Aroon.Models.cs @@ -1,5 +1,12 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the Aroon indicator calculation. +/// +/// The timestamp of the result. +/// The Aroon Up value. +/// The Aroon Down value. +/// The Aroon Oscillator value. [Serializable] public record AroonResult ( @@ -9,5 +16,6 @@ public record AroonResult double? Oscillator ) : IReusable { + /// public double Value => Oscillator.Null2NaN(); } diff --git a/src/a-d/Aroon/Aroon.StaticSeries.cs b/src/a-d/Aroon/Aroon.StaticSeries.cs index 4fcdbe3be..c06a150de 100644 --- a/src/a-d/Aroon/Aroon.StaticSeries.cs +++ b/src/a-d/Aroon/Aroon.StaticSeries.cs @@ -1,9 +1,17 @@ namespace Skender.Stock.Indicators; -// AROON OSCILLATOR (SERIES) - +/// +/// Provides methods for calculating the Aroon Oscillator. +/// public static partial class Aroon { + /// + /// Calculates the Aroon Oscillator from a series of quotes. + /// + /// The type of the quote. + /// The list of quotes. + /// The number of periods to look back. Default is 25. + /// A list of Aroon results. public static IReadOnlyList ToAroon( this IReadOnlyList quotes, int lookbackPeriods = 25) @@ -11,6 +19,12 @@ public static IReadOnlyList ToAroon( .ToQuoteDList() .CalcAroon(lookbackPeriods); + /// + /// Calculates the Aroon Oscillator for the given source data. + /// + /// The source data. + /// The number of periods to look back. + /// A list of Aroon results. private static List CalcAroon( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/a-d/Aroon/info.xml b/src/a-d/Aroon/info.xml deleted file mode 100644 index beda1958f..000000000 --- a/src/a-d/Aroon/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Aroon is a simple oscillator view of how long the new high or low price occured over a lookback window. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of Aroon Up/Down and Oscillator values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/Atr/Atr.Models.cs b/src/a-d/Atr/Atr.Models.cs index 67097be0f..7a0da44d0 100644 --- a/src/a-d/Atr/Atr.Models.cs +++ b/src/a-d/Atr/Atr.Models.cs @@ -1,5 +1,12 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the Average True Range (ATR) indicator calculation. +/// +/// The timestamp of the result. +/// The True Range (TR) value. +/// The Average True Range (ATR) value. +/// The ATR percentage value. [Serializable] public record AtrResult ( @@ -9,5 +16,6 @@ public record AtrResult double? Atrp = null ) : IReusable { + /// public double Value => Atrp.Null2NaN(); } diff --git a/src/a-d/Atr/Atr.StaticSeries.cs b/src/a-d/Atr/Atr.StaticSeries.cs index 10db80315..3223af8c6 100644 --- a/src/a-d/Atr/Atr.StaticSeries.cs +++ b/src/a-d/Atr/Atr.StaticSeries.cs @@ -1,9 +1,18 @@ namespace Skender.Stock.Indicators; -// AVERAGE TRUE RANGE (SERIES) - +/// +/// Provides methods for calculating the +/// Average True Range (ATR) from a series of quotes. +/// public static partial class Atr { + /// + /// Calculates Average True Range (ATR) for a series of quotes. + /// + /// The type of the quote. + /// The list of quotes. + /// The number of periods to look back for ATR calculation. Default is 14. + /// A read-only list of ATR results. public static IReadOnlyList ToAtr( this IReadOnlyList quotes, int lookbackPeriods = 14) @@ -11,6 +20,12 @@ public static IReadOnlyList ToAtr( .ToQuoteDList() .CalcAtr(lookbackPeriods); + /// + /// Calculates the Average True Range (ATR) for a list of quotes. + /// + /// The list of quotes. + /// The number of periods to look back for ATR calculation. + /// A list of ATR results. internal static List CalcAtr( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/a-d/Atr/Atr.StreamHub.cs b/src/a-d/Atr/Atr.StreamHub.cs index d37d45062..d4a39b9f0 100644 --- a/src/a-d/Atr/Atr.StreamHub.cs +++ b/src/a-d/Atr/Atr.StreamHub.cs @@ -1,24 +1,38 @@ namespace Skender.Stock.Indicators; -// AVERAGE TRUE RANGE (STREAM HUB) - #region hub interface and initializer +/// +/// Interface for Average True Range (ATR) Hub. +/// public interface IAtrHub { + /// + /// Gets the lookback periods for ATR calculation. + /// int LookbackPeriods { get; } } +#endregion public static partial class Atr { + /// + /// Converts the provided quote provider to an ATR hub with the specified lookback periods. + /// + /// The type of the input quote. + /// The quote provider to convert. + /// The number of lookback periods for ATR calculation. Default is 14. + /// An instance of . public static AtrHub ToAtr( this IQuoteProvider quoteProvider, int lookbackPeriods = 14) where TIn : IQuote => new(quoteProvider, lookbackPeriods); } -#endregion - +/// +/// Represents a hub for calculating the Average True Range (ATR) indicator. +/// +/// The type of the input quote. public class AtrHub : ChainProvider, IAtrHub where TIn : IQuote @@ -27,6 +41,11 @@ public class AtrHub private readonly string hubName; + /// + /// Initializes a new instance of the class. + /// + /// The quote provider. + /// The number of lookback periods for ATR calculation. internal AtrHub(IQuoteProvider provider, int lookbackPeriods) : base(provider) @@ -39,12 +58,17 @@ internal AtrHub(IQuoteProvider provider, } #endregion + /// + /// Gets the number of lookback periods for ATR calculation. + /// public int LookbackPeriods { get; init; } // METHODS + /// public override string ToString() => hubName; + /// protected override (AtrResult result, int index) ToIndicator(TIn item, int? indexHint) { diff --git a/src/a-d/Atr/Atr.Utilities.cs b/src/a-d/Atr/Atr.Utilities.cs index febeb0aa6..74c32d117 100644 --- a/src/a-d/Atr/Atr.Utilities.cs +++ b/src/a-d/Atr/Atr.Utilities.cs @@ -2,7 +2,15 @@ namespace Skender.Stock.Indicators; public static partial class Atr { - // increment + /// + /// Calculates the Average True Range (ATR) incrementally. + /// + /// The number of periods to look back. + /// The high price of the current period. + /// The low price of the current period. + /// The close price of the previous period. + /// The ATR value of the previous period. + /// The ATR value for the current period. internal static double Increment( int lookbackPeriods, double high, @@ -16,7 +24,15 @@ internal static double Increment( // TODO: this may be unused, verify before making public } - // increment + /// + /// Calculates the Average True Range (ATR) incrementally for a given quote. + /// + /// The type of the quote. + /// The number of periods to look back. + /// The current quote. + /// The close price of the previous period. + /// The ATR value of the previous period. + /// An containing the ATR values for the current period. public static AtrResult Increment( int lookbackPeriods, TQuote quote, @@ -39,7 +55,11 @@ public static AtrResult Increment( atrp.NaN2Null()); } - // parameter validation + /// + /// Validates the parameters for the Average True Range (ATR) calculation. + /// + /// The number of periods to look back. + /// Thrown when the lookback periods are less than or equal to 1. internal static void Validate( int lookbackPeriods) { diff --git a/src/a-d/Atr/info.xml b/src/a-d/Atr/info.xml deleted file mode 100644 index 571bf1416..000000000 --- a/src/a-d/Atr/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Average True Range (ATR) is a measure of volatility that captures gaps and limits between periods. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of ATR values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/AtrStop/AtrStop.StaticSeries.cs b/src/a-d/AtrStop/AtrStop.StaticSeries.cs index b4b840f48..abcb8e033 100644 --- a/src/a-d/AtrStop/AtrStop.StaticSeries.cs +++ b/src/a-d/AtrStop/AtrStop.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// ATR TRAILING STOP (SERIES) - +/// +/// Provides methods for calculating the ATR Trailing Stop. +/// public static partial class AtrStop { + /// + /// Calculates the ATR Trailing Stop from a series of quotes. + /// + /// The type of the quote. + /// The list of quotes. + /// The number of periods to look back. Default is 21. + /// The multiplier for the ATR. Default is 3. + /// The type of price to use for the calculation. Default is . + /// A list of ATR Trailing Stop results. public static IReadOnlyList ToAtrStop( this IReadOnlyList quotes, int lookbackPeriods = 21, @@ -13,6 +23,14 @@ public static IReadOnlyList ToAtrStop( .ToQuoteDList() .CalcAtrStop(lookbackPeriods, multiplier, endType); + /// + /// Calculates the ATR Trailing Stop for the given source data. + /// + /// The source data. + /// The number of periods to look back. + /// The multiplier for the ATR. + /// The type of price to use for the calculation. + /// A list of ATR Trailing Stop results. private static List CalcAtrStop( this IReadOnlyList source, int lookbackPeriods, diff --git a/src/a-d/AtrStop/AtrStop.StreamHub.cs b/src/a-d/AtrStop/AtrStop.StreamHub.cs index 20c0e7d0e..d74c521c6 100644 --- a/src/a-d/AtrStop/AtrStop.StreamHub.cs +++ b/src/a-d/AtrStop/AtrStop.StreamHub.cs @@ -4,15 +4,41 @@ namespace Skender.Stock.Indicators; #region hub interface and initializer +/// +/// Interface for ATR Stop Hub. +/// public interface IAtrStopHub { + /// + /// Gets the number of periods to look back. + /// int LookbackPeriods { get; } + + /// + /// Gets the multiplier for the ATR. + /// double Multiplier { get; } + + /// + /// Gets the type of price to use for the calculation. + /// EndType EndType { get; } } +/// +/// Provides methods for calculating the ATR Trailing Stop using a stream hub. +/// public static partial class AtrStop { + /// + /// Initializes a new instance of the class. + /// + /// The type of the input quote. + /// The quote provider. + /// The number of periods to look back. Default is 21. + /// The multiplier for the ATR. Default is 3. + /// The type of price to use for the calculation. Default is . + /// An instance of . public static AtrStopHub ToAtrStop( this IQuoteProvider quoteProvider, int lookbackPeriods = 21, @@ -23,6 +49,10 @@ public static AtrStopHub ToAtrStop( } #endregion +/// +/// Represents a stream hub for calculating the ATR Trailing Stop. +/// +/// The type of the input quote. public class AtrStopHub : StreamHub, IAtrStopHub where TIn : IQuote @@ -31,6 +61,13 @@ public class AtrStopHub private readonly string hubName; + /// + /// Initializes a new instance of the class. + /// + /// The quote provider. + /// The number of periods to look back. + /// The multiplier for the ATR. + /// The type of price to use for the calculation. internal AtrStopHub( IQuoteProvider provider, int lookbackPeriods, @@ -48,8 +85,19 @@ internal AtrStopHub( } #endregion + /// + /// Gets the number of periods to look back. + /// public int LookbackPeriods { get; init; } + + /// + /// Gets the multiplier for the ATR. + /// public double Multiplier { get; init; } + + /// + /// Gets the type of price to use for the calculation. + /// public EndType EndType { get; init; } // prevailing direction and band thresholds @@ -59,8 +107,10 @@ internal AtrStopHub( // METHODS + /// public override string ToString() => hubName; + /// protected override (AtrStopResult result, int index) ToIndicator(TIn item, int? indexHint) { @@ -176,8 +226,9 @@ protected override (AtrStopResult result, int index) } /// - /// Restore prior ATR Stop + /// Restores the prior ATR Stop state. /// + /// The timestamp to restore to. /// protected override void RollbackState(DateTime timestamp) { diff --git a/src/a-d/AtrStop/AtrStop.Utilities.cs b/src/a-d/AtrStop/AtrStop.Utilities.cs index be667403f..c41ad8a75 100644 --- a/src/a-d/AtrStop/AtrStop.Utilities.cs +++ b/src/a-d/AtrStop/AtrStop.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// ATR TRAILING STOP (UTILITIES) - +/// +/// Provides utility methods for the ATR Trailing Stop indicator. +/// public static partial class AtrStop { - // CONDENSE (REMOVE null results) - /// + /// + /// Removes empty (null) periods from the ATR Trailing Stop results. + /// + /// The list of ATR Trailing Stop results. + /// A list of ATR Trailing Stop results with empty periods removed. public static IReadOnlyList Condense( this IReadOnlyList results) { @@ -17,8 +21,11 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the ATR Trailing Stop results. + /// + /// The list of ATR Trailing Stop results. + /// A list of ATR Trailing Stop results with the warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -29,7 +36,15 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } - // parameter validation + /// + /// Validates the parameters for the ATR Trailing Stop calculation. + /// + /// The number of periods to look back for the calculation. + /// The multiplier for the ATR calculation, must be greater than 0. + /// + /// Thrown when the lookback periods are less than or equal to 1, + /// or the multiplier is less than or equal to 0. + /// internal static void Validate( int lookbackPeriods, double multiplier) diff --git a/src/a-d/AtrStop/info.xml b/src/a-d/AtrStop/info.xml deleted file mode 100644 index 2858bc633..000000000 --- a/src/a-d/AtrStop/info.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - ATR Trailing Stop attempts to determine the primary trend of prices by using - Average True Range (ATR) band thresholds. It can indicate a buy/sell signal or a - trailing stop when the trend changes. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for ATR. - Multiplier sets the ATR band width. - Sets basis for stop offsets (Close or High/Low). - Time series of ATR Trailing Stop values. - Invalid parameter value provided. - diff --git a/src/a-d/Awesome/Awesome.Models.cs b/src/a-d/Awesome/Awesome.Models.cs index ed1dd78de..2c9d37276 100644 --- a/src/a-d/Awesome/Awesome.Models.cs +++ b/src/a-d/Awesome/Awesome.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the Awesome Oscillator calculation. +/// +/// The timestamp of the result. +/// The value of the Awesome Oscillator. +/// The normalized value of the Awesome Oscillator. [Serializable] public record AwesomeResult ( @@ -8,5 +14,6 @@ public record AwesomeResult double? Normalized ) : IReusable { + /// public double Value => Oscillator.Null2NaN(); } diff --git a/src/a-d/Awesome/Awesome.StaticSeries.cs b/src/a-d/Awesome/Awesome.StaticSeries.cs index b8f38f542..0aef8557a 100644 --- a/src/a-d/Awesome/Awesome.StaticSeries.cs +++ b/src/a-d/Awesome/Awesome.StaticSeries.cs @@ -1,9 +1,18 @@ namespace Skender.Stock.Indicators; -// AWESOME OSCILLATOR (SERIES) - +/// +/// Provides methods for calculating the Awesome Oscillator. +/// public static partial class Awesome { + /// + /// Calculates the Awesome Oscillator for a series of data. + /// + /// The type of the source data. + /// The source data. + /// The number of periods for the fast moving average. Default is 5. + /// The number of periods for the slow moving average. Default is 34. + /// A list of Awesome Oscillator results. public static IReadOnlyList ToAwesome( this IReadOnlyList source, int fastPeriods = 5, diff --git a/src/a-d/Awesome/info.xml b/src/a-d/Awesome/info.xml deleted file mode 100644 index 68e2163e4..000000000 --- a/src/a-d/Awesome/info.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Awesome Oscillator (aka Super AO) is a measure of the gap between a fast and slow period modified moving average. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the Fast moving average. - Number of periods in the Slow moving average. - Time series of Awesome Oscillator values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/Beta/Beta.Models.cs b/src/a-d/Beta/Beta.Models.cs index 5d62fd0eb..907d60a52 100644 --- a/src/a-d/Beta/Beta.Models.cs +++ b/src/a-d/Beta/Beta.Models.cs @@ -1,5 +1,16 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the Beta calculation. +/// +/// The timestamp of the result. +/// The Beta value. +/// The Beta+ upside value. +/// The Beta- downside value. +/// The ratio of BetaUp to BetaDown. +/// The convexity of the Beta. +/// The returns of the evaluated asset. +/// The returns of the market. [Serializable] public record BetaResult( DateTime Timestamp, @@ -12,13 +23,32 @@ public record BetaResult( double? ReturnsMrkt = null ) : IReusable { + /// public double Value => Beta.Null2NaN(); } +/// +/// Specifies the type of Beta calculation. +/// public enum BetaType { + /// + /// Standard Beta only + /// Standard, + + /// + /// Beta+ updside only + /// Up, + + /// + /// Beta- downside only + /// Down, + + /// + /// Calculation all Beta types + /// All } diff --git a/src/a-d/Beta/Beta.StaticSeries.cs b/src/a-d/Beta/Beta.StaticSeries.cs index d8e3280a6..d8bbf33a0 100644 --- a/src/a-d/Beta/Beta.StaticSeries.cs +++ b/src/a-d/Beta/Beta.StaticSeries.cs @@ -1,9 +1,21 @@ namespace Skender.Stock.Indicators; -// BETA COEFFICIENT (SERIES) - +/// +/// Provides methods for calculating the Beta coefficient. +/// public static partial class Beta { + /// + /// Calculates the Beta coefficient for a series of data. + /// + /// The type of the source data. + /// The source data for the evaluated asset. + /// The source data for the market. + /// The number of periods to look back. + /// The type of Beta calculation. Default is . + /// A list of Beta results. + /// Thrown when sourceEval or sourceMrkt is null. + /// Thrown when the timestamps of sourceEval and sourceMrkt do not match. public static IReadOnlyList ToBeta( this IReadOnlyList sourceEval, IReadOnlyList sourceMrkt, @@ -112,7 +124,15 @@ public static IReadOnlyList ToBeta( return results; } - // calculate beta + /// + /// Calculates the Beta value for a specific window of data. + /// + /// The current index in the data. + /// The number of periods to look back. + /// The market returns data. + /// The evaluated asset returns data. + /// The type of Beta calculation. + /// The calculated Beta value. private static double? CalcBetaWindow( int i, int lookbackPeriods, diff --git a/src/a-d/Beta/info.xml b/src/a-d/Beta/info.xml deleted file mode 100644 index 81c65ac58..000000000 --- a/src/a-d/Beta/info.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - Beta shows how strongly one stock responds to systemic volatility of the entire market. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes for Evaluation. - Historical price quotes for Market. - Number of periods in the lookback window. - Type of Beta to calculate. - Time series of Beta values. - Invalid parameter value provided. - Invalid quotes provided. - diff --git a/src/a-d/BollingerBands/BollingerBands.Models.cs b/src/a-d/BollingerBands/BollingerBands.Models.cs index 7f577f160..63f93597a 100644 --- a/src/a-d/BollingerBands/BollingerBands.Models.cs +++ b/src/a-d/BollingerBands/BollingerBands.Models.cs @@ -1,5 +1,15 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of Bollinger Bands calculation. +/// +/// The timestamp of the result. +/// The simple moving average. +/// The upper Bollinger Band. +/// The lower Bollinger Band. +/// The %B value. +/// The Z-Score value. +/// The width of the Bollinger Bands. [Serializable] public record BollingerBandsResult ( @@ -12,5 +22,6 @@ public record BollingerBandsResult double? Width = null ) : IReusable { + /// public double Value => PercentB.Null2NaN(); } diff --git a/src/a-d/BollingerBands/BollingerBands.StaticSeries.cs b/src/a-d/BollingerBands/BollingerBands.StaticSeries.cs index 8945cb590..d2940e1f6 100644 --- a/src/a-d/BollingerBands/BollingerBands.StaticSeries.cs +++ b/src/a-d/BollingerBands/BollingerBands.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// BOLLINGER BANDS (SERIES) - +/// +/// Provides methods for calculating Bollinger Bands. +/// public static partial class BollingerBands { + /// + /// Calculates the Bollinger Bands for a series of data. + /// + /// The type of the elements in the source list, which must implement . + /// The source list of data. + /// The number of periods to use for the lookback window. Default is 20. + /// The number of standard deviations to use for the bands. Default is 2. + /// A read-only list of containing the Bollinger Bands calculation results. + /// Thrown when the source list is null. public static IReadOnlyList ToBollingerBands( this IReadOnlyList source, int lookbackPeriods = 20, diff --git a/src/a-d/BollingerBands/info.xml b/src/a-d/BollingerBands/info.xml deleted file mode 100644 index 094e502e5..000000000 --- a/src/a-d/BollingerBands/info.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Bollinger Bands® depict volatility as standard deviation boundary lines from a moving average of price. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Width of bands. Number of Standard Deviations from the moving average. - Time series of Bollinger Band and %B values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/Bop/Bop.Models.cs b/src/a-d/Bop/Bop.Models.cs index 2abc7e6b1..e6eada31b 100644 --- a/src/a-d/Bop/Bop.Models.cs +++ b/src/a-d/Bop/Bop.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the Balance of Power (BOP) calculation. +/// +/// The timestamp of the result. +/// The Balance of Power value. [Serializable] public record BopResult ( @@ -7,5 +12,6 @@ public record BopResult double? Bop ) : IReusable { + /// public double Value => Bop.Null2NaN(); } diff --git a/src/a-d/Bop/Bop.StaticSeries.cs b/src/a-d/Bop/Bop.StaticSeries.cs index 37a1c7b7c..447a538b0 100644 --- a/src/a-d/Bop/Bop.StaticSeries.cs +++ b/src/a-d/Bop/Bop.StaticSeries.cs @@ -1,9 +1,17 @@ namespace Skender.Stock.Indicators; -// BALANCE OF POWER (SERIES) - +/// +/// Provides methods for calculating the Balance of Power (BOP) on a series of quotes. +/// public static partial class Bop { + /// + /// Calculates the Balance of Power (BOP) for a series of quotes. + /// + /// The type of the elements in the quotes list, which must implement . + /// The source list of quotes. + /// The number of periods to use for smoothing. Default is 14. + /// A read-only list of containing the BOP calculation results. public static IReadOnlyList ToBop( this IReadOnlyList quotes, int smoothPeriods = 14) @@ -11,6 +19,12 @@ public static IReadOnlyList ToBop( .ToQuoteDList() .CalcBop(smoothPeriods); + /// + /// Calculates the Balance of Power (BOP) for a series of quotes. + /// + /// The source list of quotes. + /// The number of periods to use for smoothing. + /// A list of containing the BOP calculation results. private static List CalcBop( this IReadOnlyList source, int smoothPeriods) diff --git a/src/a-d/Bop/Bop.Utilities.cs b/src/a-d/Bop/Bop.Utilities.cs index 488440947..c94669316 100644 --- a/src/a-d/Bop/Bop.Utilities.cs +++ b/src/a-d/Bop/Bop.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// BALANCE OF POWER (UTILITIES) - +/// +/// Provides utility methods for the BOP (Balance of Power) indicator. +/// public static partial class Bop { - // parameter validation + /// + /// Validates the parameters for the BOP calculation. + /// + /// The number of smoothing periods for the BOP calculation. + /// Thrown when the smoothing periods are less than or equal to 0. internal static void Validate( int smoothPeriods) { diff --git a/src/a-d/Bop/info.xml b/src/a-d/Bop/info.xml deleted file mode 100644 index 1ecaf503b..000000000 --- a/src/a-d/Bop/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Balance of Power (aka Balance of Market Power) is a momentum oscillator that depicts the strength of buying and selling pressure. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for smoothing. - Time series of BOP values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/Cci/Cci.Models.cs b/src/a-d/Cci/Cci.Models.cs index f2a819eb3..a29070b1b 100644 --- a/src/a-d/Cci/Cci.Models.cs +++ b/src/a-d/Cci/Cci.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the Commodity Channel Index (CCI) calculation. +/// +/// The timestamp of the result. +/// The Commodity Channel Index value. [Serializable] public record CciResult ( @@ -7,5 +12,6 @@ public record CciResult double? Cci ) : IReusable { + /// public double Value => Cci.Null2NaN(); } diff --git a/src/a-d/Cci/Cci.StaticSeries.cs b/src/a-d/Cci/Cci.StaticSeries.cs index e854ce386..cea770a61 100644 --- a/src/a-d/Cci/Cci.StaticSeries.cs +++ b/src/a-d/Cci/Cci.StaticSeries.cs @@ -1,9 +1,17 @@ namespace Skender.Stock.Indicators; -// COMMODITY CHANNEL INDEX (SERIES) - +/// +/// Provides methods for calculating the Commodity Channel Index (CCI) on a series of quotes. +/// public static partial class Cci { + /// + /// Calculates the Commodity Channel Index (CCI) for a series of quotes. + /// + /// The type of the elements in the quotes list, which must implement . + /// The source list of quotes. + /// The number of periods to use for the lookback window. Default is 20. + /// A read-only list of containing the CCI calculation results. public static IReadOnlyList ToCci( this IReadOnlyList quotes, int lookbackPeriods = 20) @@ -11,6 +19,12 @@ public static IReadOnlyList ToCci( .ToQuoteDList() .CalcCci(lookbackPeriods); + /// + /// Calculates the Commodity Channel Index (CCI) for a series of quotes. + /// + /// The source list of quotes. + /// The number of periods to use for the lookback window. + /// A list of containing the CCI calculation results. private static List CalcCci( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/a-d/Cci/Cci.Utilities.cs b/src/a-d/Cci/Cci.Utilities.cs index 34bce451c..cd4e9f15b 100644 --- a/src/a-d/Cci/Cci.Utilities.cs +++ b/src/a-d/Cci/Cci.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// COMMODITY CHANNEL INDEX (UTILITIES) - +/// +/// Provides utility methods for the CCI (Commodity Channel Index) indicator. +/// public static partial class Cci { - // parameter validation + /// + /// Validates the parameters for the CCI calculation. + /// + /// The number of periods to look back for the calculation. + /// Thrown when the lookback periods are less than or equal to 0. internal static void Validate( int lookbackPeriods) { @@ -15,5 +20,4 @@ internal static void Validate( "Lookback periods must be greater than 0 for Commodity Channel Index."); } } - } diff --git a/src/a-d/Cci/info.xml b/src/a-d/Cci/info.xml deleted file mode 100644 index e4a9bb6de..000000000 --- a/src/a-d/Cci/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Commodity Channel Index (CCI) is an oscillator depicting deviation from typical price range, often used to identify cyclical trends. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of CCI values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/ChaikinOsc/ChaikinOsc.Models.cs b/src/a-d/ChaikinOsc/ChaikinOsc.Models.cs index d30df99b6..31e593dd7 100644 --- a/src/a-d/ChaikinOsc/ChaikinOsc.Models.cs +++ b/src/a-d/ChaikinOsc/ChaikinOsc.Models.cs @@ -1,5 +1,13 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the Chaikin Oscillator calculation. +/// +/// The timestamp of the result. +/// The money flow multiplier value. +/// The money flow volume value. +/// The Accumulation/Distribution Line (ADL) value. +/// The Chaikin Oscillator value. [Serializable] public record ChaikinOscResult ( @@ -10,5 +18,6 @@ public record ChaikinOscResult double? Oscillator ) : IReusable { + /// public double Value => Oscillator.Null2NaN(); } diff --git a/src/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.cs b/src/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.cs index f3d1b3b6c..4df6f11b5 100644 --- a/src/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.cs +++ b/src/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.cs @@ -1,9 +1,18 @@ namespace Skender.Stock.Indicators; -// CHAIKIN OSCILLATOR (SERIES) - +/// +/// Provides methods for calculating the Chaikin Oscillator on a series of quotes. +/// public static partial class ChaikinOsc { + /// + /// Calculates the Chaikin Oscillator for a series of quotes. + /// + /// The type of the elements in the quotes list, which must implement . + /// The source list of quotes. + /// The number of periods to use for the fast EMA. Default is 3. + /// The number of periods to use for the slow EMA. Default is 10. + /// A read-only list of containing the Chaikin Oscillator calculation results. public static IReadOnlyList ToChaikinOsc( this IReadOnlyList quotes, int fastPeriods = 3, diff --git a/src/a-d/ChaikinOsc/ChaikinOsc.Utilities.cs b/src/a-d/ChaikinOsc/ChaikinOsc.Utilities.cs index 3e21a64c0..c4bdf59cc 100644 --- a/src/a-d/ChaikinOsc/ChaikinOsc.Utilities.cs +++ b/src/a-d/ChaikinOsc/ChaikinOsc.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// CHAIKIN OSCILLATOR (UTILITIES) - +/// +/// Provides utility methods for the Chaikin Oscillator indicator. +/// public static partial class ChaikinOsc { - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the Chaikin Oscillator results. + /// + /// The list of Chaikin Oscillator results. + /// A list of Chaikin Oscillator results with the warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -16,7 +20,15 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(s + 100); } - // parameter validation + /// + /// Validates the parameters for the Chaikin Oscillator calculation. + /// + /// The number of fast lookback periods for the calculation. + /// The number of slow lookback periods for the calculation. + /// + /// Thrown when the fast lookback periods are less than or equal to 0, + /// or the slow lookback periods are less than or equal to the fast lookback periods. + /// internal static void Validate( int fastPeriods, int slowPeriods) diff --git a/src/a-d/ChaikinOsc/info.xml b/src/a-d/ChaikinOsc/info.xml deleted file mode 100644 index 4d82225d4..000000000 --- a/src/a-d/ChaikinOsc/info.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Chaikin Oscillator is the difference between fast and slow Exponential Moving Averages (EMA) of the Accumulation/Distribution Line (ADL). - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for the ADL fast EMA. - Number of periods for the ADL slow EMA. - Time series of Chaikin Oscillator, Money Flow Volume, and ADL values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/Chandelier/Chandelier.Models.cs b/src/a-d/Chandelier/Chandelier.Models.cs index 8fc6b4209..ce2ae1688 100644 --- a/src/a-d/Chandelier/Chandelier.Models.cs +++ b/src/a-d/Chandelier/Chandelier.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the Chandelier Exit calculation. +/// +/// The timestamp of the result. +/// The Chandelier Exit value. [Serializable] public record ChandelierResult ( @@ -7,11 +12,22 @@ public record ChandelierResult double? ChandelierExit ) : IReusable { + /// public double Value => ChandelierExit.Null2NaN(); } +/// +/// Specifies the type of Chandelier Exit. +/// public enum ChandelierType { + /// + /// Represents a long Chandelier Exit. + /// Long = 0, + + /// + /// Represents a short Chandelier Exit. + /// Short = 1 } diff --git a/src/a-d/Chandelier/Chandelier.StaticSeries.cs b/src/a-d/Chandelier/Chandelier.StaticSeries.cs index 8f13ebeb8..4aa7190ec 100644 --- a/src/a-d/Chandelier/Chandelier.StaticSeries.cs +++ b/src/a-d/Chandelier/Chandelier.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// CHANDELIER EXIT (SERIES) - +/// +/// Provides methods for calculating the Chandelier Exit on a series of quotes. +/// public static partial class Chandelier { + /// + /// Calculates the Chandelier Exit for a series of quotes. + /// + /// The type of the elements in the quotes list, which must implement . + /// The source list of quotes. + /// The number of periods to use for the lookback window. Default is 22. + /// The multiplier to apply to the ATR. Default is 3. + /// The type of Chandelier Exit to calculate (Long or Short). Default is Long. + /// A read-only list of containing the Chandelier Exit calculation results. public static IReadOnlyList ToChandelier( this IReadOnlyList quotes, int lookbackPeriods = 22, @@ -13,6 +23,14 @@ public static IReadOnlyList ToChandelier( .ToQuoteDList() .CalcChandelier(lookbackPeriods, multiplier, type); + /// + /// Calculates the Chandelier Exit for a series of quotes. + /// + /// The source list of quotes. + /// The number of periods to use for the lookback window. + /// The multiplier to apply to the ATR. + /// The type of Chandelier Exit to calculate (Long or Short). + /// A list of containing the Chandelier Exit calculation results. private static List CalcChandelier( this IReadOnlyList source, int lookbackPeriods, diff --git a/src/a-d/Chandelier/Chandelier.Utilities.cs b/src/a-d/Chandelier/Chandelier.Utilities.cs index f61fe73ec..16d947ac3 100644 --- a/src/a-d/Chandelier/Chandelier.Utilities.cs +++ b/src/a-d/Chandelier/Chandelier.Utilities.cs @@ -1,10 +1,19 @@ namespace Skender.Stock.Indicators; -// CHANDELIER EXIT (UTILITIES) - +/// +/// Provides utility methods for the Chandelier Exit indicator. +/// public static partial class Chandelier { - // parameter validation + /// + /// Validates the parameters for the Chandelier Exit calculation. + /// + /// The number of periods to look back for the calculation. + /// The multiplier for the ATR calculation, must be greater than 0. + /// + /// Thrown when the lookback periods are less than or equal to 0, + /// or the multiplier is less than or equal to 0. + /// internal static void Validate( int lookbackPeriods, double multiplier) diff --git a/src/a-d/Chandelier/info.xml b/src/a-d/Chandelier/info.xml deleted file mode 100644 index 7f204bf65..000000000 --- a/src/a-d/Chandelier/info.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Chandelier Exit is typically used for stop-loss and can be computed for both long or short types. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Multiplier. - Short or Long variant selection. - Time series of Chandelier Exit values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/Chop/Chop.Models.cs b/src/a-d/Chop/Chop.Models.cs index 27aeb760a..4884ce187 100644 --- a/src/a-d/Chop/Chop.Models.cs +++ b/src/a-d/Chop/Chop.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the Choppiness Index (CHOP) calculation. +/// +/// The timestamp of the result. +/// The Choppiness Index value. [Serializable] public record ChopResult ( @@ -7,5 +12,6 @@ public record ChopResult double? Chop ) : IReusable { + /// public double Value => Chop.Null2NaN(); } diff --git a/src/a-d/Chop/Chop.StaticSeries.cs b/src/a-d/Chop/Chop.StaticSeries.cs index 2237f86c1..08ef400db 100644 --- a/src/a-d/Chop/Chop.StaticSeries.cs +++ b/src/a-d/Chop/Chop.StaticSeries.cs @@ -1,9 +1,17 @@ namespace Skender.Stock.Indicators; -// CHOPPINESS INDEX (SERIES) - +/// +/// Provides methods for calculating the Choppiness Index (CHOP) on a series of quotes. +/// public static partial class Chop { + /// + /// Calculates the Choppiness Index (CHOP) for a series of quotes. + /// + /// The type of the elements in the quotes list, which must implement . + /// The source list of quotes. + /// The number of periods to use for the lookback window. Default is 14. + /// A read-only list of containing the CHOP calculation results. public static IReadOnlyList ToChop( this IReadOnlyList quotes, int lookbackPeriods = 14) @@ -11,6 +19,12 @@ public static IReadOnlyList ToChop( .ToQuoteDList() .CalcChop(lookbackPeriods); + /// + /// Calculates the Choppiness Index (CHOP) for a series of quotes. + /// + /// The source list of quotes. + /// The number of periods to use for the lookback window. + /// A list of containing the CHOP calculation results. private static List CalcChop( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/a-d/Chop/Chop.Utilities.cs b/src/a-d/Chop/Chop.Utilities.cs index 08bdb70dc..e728f1ea6 100644 --- a/src/a-d/Chop/Chop.Utilities.cs +++ b/src/a-d/Chop/Chop.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// CHOPPINESS INDEX (UTILITIES) - +/// +/// Provides utility methods for the CHOP (Choppiness Index) indicator. +/// public static partial class Chop { - // parameter validation + /// + /// Validates the parameters for the CHOP calculation. + /// + /// The number of periods to look back for the calculation. + /// Thrown when the lookback periods are less than or equal to 1. internal static void Validate( int lookbackPeriods) { diff --git a/src/a-d/Chop/info.xml b/src/a-d/Chop/info.xml deleted file mode 100644 index 39b602121..000000000 --- a/src/a-d/Chop/info.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Choppiness Index (CHOP) measures the trendiness or choppiness over N lookback periods - on a scale of 0 to 100. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of CHOP values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/Cmf/Cmf.Models.cs b/src/a-d/Cmf/Cmf.Models.cs index 6159a061f..152044d23 100644 --- a/src/a-d/Cmf/Cmf.Models.cs +++ b/src/a-d/Cmf/Cmf.Models.cs @@ -1,5 +1,12 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the Chaikin Money Flow (CMF) calculation. +/// +/// The timestamp of the result. +/// The money flow multiplier value. +/// The money flow volume value. +/// The Chaikin Money Flow value. [Serializable] public record CmfResult ( @@ -9,5 +16,6 @@ public record CmfResult double? Cmf ) : IReusable { + /// public double Value => Cmf.Null2NaN(); } diff --git a/src/a-d/Cmf/Cmf.StaticSeries.cs b/src/a-d/Cmf/Cmf.StaticSeries.cs index 063d60b5b..dcb703fe5 100644 --- a/src/a-d/Cmf/Cmf.StaticSeries.cs +++ b/src/a-d/Cmf/Cmf.StaticSeries.cs @@ -1,9 +1,17 @@ namespace Skender.Stock.Indicators; -// CHAIKIN MONEY FLOW (SERIES) - +/// +/// Provides methods for calculating the Chaikin Money Flow (CMF) on a series of quotes. +/// public static partial class Cmf { + /// + /// Calculates the Chaikin Money Flow (CMF) for a series of quotes. + /// + /// The type of the elements in the quotes list, which must implement . + /// The source list of quotes. + /// The number of periods to use for the lookback window. Default is 20. + /// A read-only list of containing the CMF calculation results. public static IReadOnlyList ToCmf( this IReadOnlyList quotes, int lookbackPeriods = 20) @@ -11,6 +19,13 @@ public static IReadOnlyList ToCmf( .ToSortedList() .CalcCmf(lookbackPeriods); + /// + /// Calculates the Chaikin Money Flow (CMF) for a series of quotes. + /// + /// The type of the elements in the source list, which must implement . + /// The source list of quotes. + /// The number of periods to use for the lookback window. + /// A list of containing the CMF calculation results. private static List CalcCmf( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/a-d/Cmf/Cmf.Utilities.cs b/src/a-d/Cmf/Cmf.Utilities.cs index 360263a04..09c7ea62a 100644 --- a/src/a-d/Cmf/Cmf.Utilities.cs +++ b/src/a-d/Cmf/Cmf.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// CHAIKIN MONEY FLOW (UTILITIES) - +/// +/// Provides utility methods for the CMF (Chaikin Money Flow) indicator. +/// public static partial class Cmf { - // parameter validation + /// + /// Validates the parameters for the CMF calculation. + /// + /// The number of periods to look back for the calculation. + /// Thrown when the lookback periods are less than or equal to 0. internal static void Validate( int lookbackPeriods) { @@ -15,5 +20,4 @@ internal static void Validate( "Lookback periods must be greater than 0 for Chaikin Money Flow."); } } - } diff --git a/src/a-d/Cmf/info.xml b/src/a-d/Cmf/info.xml deleted file mode 100644 index 787e70c01..000000000 --- a/src/a-d/Cmf/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Chaikin Money Flow (CMF) is the simple moving average of Money Flow Volume (MFV). - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for the MFV moving average. - Time series of Chaikin Money Flow and MFV values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/Cmo/Cmo.Models.cs b/src/a-d/Cmo/Cmo.Models.cs index 2a9053965..10cdd33bd 100644 --- a/src/a-d/Cmo/Cmo.Models.cs +++ b/src/a-d/Cmo/Cmo.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the Chande Momentum Oscillator (CMO) calculation. +/// +/// The timestamp of the result. +/// The Chande Momentum Oscillator value. [Serializable] public record CmoResult ( @@ -7,5 +12,6 @@ public record CmoResult double? Cmo = null ) : IReusable { + /// public double Value => Cmo.Null2NaN(); } diff --git a/src/a-d/Cmo/Cmo.StaticSeries.cs b/src/a-d/Cmo/Cmo.StaticSeries.cs index 8cd1d448f..66dd31b0c 100644 --- a/src/a-d/Cmo/Cmo.StaticSeries.cs +++ b/src/a-d/Cmo/Cmo.StaticSeries.cs @@ -1,9 +1,17 @@ namespace Skender.Stock.Indicators; -// CHANDE MOMENTUM OSCILLATOR (SERIES) - +/// +/// Provides methods for calculating the Chande Momentum Oscillator (CMO) on a series of quotes. +/// public static partial class Cmo { + /// + /// Calculates the Chande Momentum Oscillator (CMO) for a series of quotes. + /// + /// The type of the elements in the source list, which must implement . + /// The source list of quotes. + /// The number of periods to use for the lookback window. + /// A read-only list of containing the CMO calculation results. public static IReadOnlyList ToCmo( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/a-d/Cmo/Cmo.Utilities.cs b/src/a-d/Cmo/Cmo.Utilities.cs index 5ed18a20a..d842f4c83 100644 --- a/src/a-d/Cmo/Cmo.Utilities.cs +++ b/src/a-d/Cmo/Cmo.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// CHANDE MOMENTUM OSCILLATOR (UTILITIES) - +/// +/// Provides utility methods for the CMO (Chande Momentum Oscillator) indicator. +/// public static partial class Cmo { - // parameter validation + /// + /// Validates the parameters for the CMO calculation. + /// + /// The number of periods to look back for the calculation. + /// Thrown when the lookback periods are less than or equal to 0. internal static void Validate( int lookbackPeriods) { diff --git a/src/a-d/Cmo/info.xml b/src/a-d/Cmo/info.xml deleted file mode 100644 index 8162c76c7..000000000 --- a/src/a-d/Cmo/info.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - The Chande Momentum Oscillator is a momentum indicator depicting the weighted percent of higher prices in financial markets. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of CMO values. - Invalid parameter value provided. - - - \ No newline at end of file diff --git a/src/a-d/ConnorsRsi/ConnorsRsi.Models.cs b/src/a-d/ConnorsRsi/ConnorsRsi.Models.cs index 9a8f29a08..6a4915acb 100644 --- a/src/a-d/ConnorsRsi/ConnorsRsi.Models.cs +++ b/src/a-d/ConnorsRsi/ConnorsRsi.Models.cs @@ -1,5 +1,14 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the Connors RSI calculation. +/// +/// The timestamp of the result. +/// The streak value. +/// The RSI value. +/// The RSI streak value. +/// The percent rank value. +/// The Connors RSI value. [Serializable] public record ConnorsRsiResult ( @@ -11,5 +20,6 @@ public record ConnorsRsiResult double? ConnorsRsi = null ) : IReusable { + /// public double Value => ConnorsRsi.Null2NaN(); } diff --git a/src/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.cs b/src/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.cs index 9f224edca..232fee056 100644 --- a/src/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.cs +++ b/src/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// CONNORS RSI (SERIES) - +/// +/// Provides methods for calculating the Connors RSI on a series of quotes. +/// public static partial class ConnorsRsi { + /// + /// Calculates the Connors RSI for a series of quotes. + /// + /// The type of the elements in the source list, which must implement . + /// The source list of quotes. + /// The number of periods to use for the RSI calculation. Default is 3. + /// The number of periods to use for the streak calculation. Default is 2. + /// The number of periods to use for the percent rank calculation. Default is 100. + /// A read-only list of containing the Connors RSI calculation results. public static IReadOnlyList ToConnorsRsi( this IReadOnlyList source, int rsiPeriods = 3, @@ -63,7 +73,14 @@ IReadOnlyList streakInfo return results; } - // calculate baseline streak and rank + /// + /// Calculates the baseline streak and rank for the Connors RSI. + /// + /// The type of the elements in the source list, which must implement . + /// The source list of quotes. + /// The number of periods to use for the RSI calculation. + /// The number of periods to use for the percent rank calculation. + /// A list of containing the baseline streak and rank calculation results. private static List CalcStreak( this IReadOnlyList source, int rsiPeriods, diff --git a/src/a-d/ConnorsRsi/ConnorsRsi.Utilities.cs b/src/a-d/ConnorsRsi/ConnorsRsi.Utilities.cs index 6fe17591f..0b34fdc82 100644 --- a/src/a-d/ConnorsRsi/ConnorsRsi.Utilities.cs +++ b/src/a-d/ConnorsRsi/ConnorsRsi.Utilities.cs @@ -1,10 +1,21 @@ namespace Skender.Stock.Indicators; -// CONNORS RSI (UTILITIES) - +/// +/// Provides utility methods for the Connors RSI indicator. +/// public static partial class ConnorsRsi { - // parameter validation + /// + /// Validates the parameters for the Connors RSI calculation. + /// + /// The number of periods for the RSI calculation on the close price. + /// The number of periods for the RSI calculation on the streak. + /// The number of periods for the percent rank calculation. + /// + /// Thrown when the RSI periods for the close price are less than or equal to 1, + /// the RSI periods for the streak are less than or equal to 1, + /// or the percent rank periods are less than or equal to 1. + /// internal static void Validate( int rsiPeriods, int streakPeriods, diff --git a/src/a-d/ConnorsRsi/info.xml b/src/a-d/ConnorsRsi/info.xml deleted file mode 100644 index 9adfe8b20..000000000 --- a/src/a-d/ConnorsRsi/info.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - ConnorsRSI is a composite oscillator that incorporates RSI, winning/losing streaks, and percentile gain metrics on scale of 0 to 100. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the RSI. - Number of periods for streak RSI. - Number of periods for the percentile ranking. - Time series of ConnorsRSI, RSI, Streak RSI, and Percent Rank values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/Correlation/Correlation.Models.cs b/src/a-d/Correlation/Correlation.Models.cs index 08f4f0808..3b9612bb8 100644 --- a/src/a-d/Correlation/Correlation.Models.cs +++ b/src/a-d/Correlation/Correlation.Models.cs @@ -1,5 +1,14 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the correlation calculation. +/// +/// The timestamp of the result. +/// The variance of series A. +/// The variance of series B. +/// The covariance between series A and B. +/// The correlation coefficient between series A and B. +/// The R-squared value of the correlation. [Serializable] public record CorrResult ( @@ -11,5 +20,6 @@ public record CorrResult double? RSquared = null ) : IReusable { + /// public double Value => Correlation.Null2NaN(); } diff --git a/src/a-d/Correlation/Correlation.StaticSeries.cs b/src/a-d/Correlation/Correlation.StaticSeries.cs index e82a4df70..ea240eb8f 100644 --- a/src/a-d/Correlation/Correlation.StaticSeries.cs +++ b/src/a-d/Correlation/Correlation.StaticSeries.cs @@ -1,9 +1,20 @@ namespace Skender.Stock.Indicators; -// CORRELATION COEFFICIENT (SERIES) - +/// +/// Provides methods for calculating the correlation coefficient on a series of quotes. +/// public static partial class Correlation { + /// + /// Calculates the correlation coefficient for two series of quotes. + /// + /// The type of the elements in the source lists, which must implement . + /// The first source list of quotes. + /// The second source list of quotes. + /// The number of periods to use for the lookback window. + /// A read-only list of containing the correlation calculation results. + /// Thrown when either sourceA or sourceB is null. + /// Thrown when the timestamps of sourceA and sourceB do not match. public static IReadOnlyList ToCorrelation( this IReadOnlyList sourceA, IReadOnlyList sourceB, @@ -63,7 +74,13 @@ public static IReadOnlyList ToCorrelation( return results; } - // calculate correlation + /// + /// Calculates the correlation for a given period. + /// + /// The timestamp of the result. + /// The data series A. + /// The data series B. + /// A containing the correlation calculation result for the given period. internal static CorrResult PeriodCorrelation( DateTime timestamp, double[] dataA, diff --git a/src/a-d/Correlation/info.xml b/src/a-d/Correlation/info.xml deleted file mode 100644 index 9325423a4..000000000 --- a/src/a-d/Correlation/info.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - Correlation Coefficient between two quote histories, based on price. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes A for comparison. - Historical price quotes B for comparison. - Number of periods in the lookback window. - - Time series of Correlation Coefficient values. - R², Variance, and Covariance are also included. - - Invalid parameter value provided. - Invalid quotes provided. - \ No newline at end of file diff --git a/src/a-d/Dema/Dema.Models.cs b/src/a-d/Dema/Dema.Models.cs index 3dc5047f1..84a85236a 100644 --- a/src/a-d/Dema/Dema.Models.cs +++ b/src/a-d/Dema/Dema.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a DEMA (Double Exponential Moving Average) calculation. +/// +/// The timestamp of the DEMA result. +/// The DEMA value. [Serializable] public record DemaResult ( @@ -7,5 +12,6 @@ public record DemaResult double? Dema = null ) : IReusable { + /// public double Value => Dema.Null2NaN(); } diff --git a/src/a-d/Dema/Dema.StaticSeries.cs b/src/a-d/Dema/Dema.StaticSeries.cs index 0a10cb5a9..8984dd548 100644 --- a/src/a-d/Dema/Dema.StaticSeries.cs +++ b/src/a-d/Dema/Dema.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// DOUBLE EXPONENTIAL MOVING AVERAGE (SERIES) - +/// +/// Provides methods for calculating the Double Exponential Moving Average (DEMA) on a series of data. +/// public static partial class Dema { + /// + /// Calculates the Double Exponential Moving Average (DEMA) for a series of data. + /// + /// The type of the elements in the source list, which must implement . + /// The source list of data points. + /// The number of periods to use for the lookback. + /// A list of containing the DEMA values. + /// Thrown when the source list is null. + /// Thrown when the lookback periods are not valid. public static IReadOnlyList ToDema( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/a-d/Dema/Dema.Utilities.cs b/src/a-d/Dema/Dema.Utilities.cs index e04ddcead..32e4c3c68 100644 --- a/src/a-d/Dema/Dema.Utilities.cs +++ b/src/a-d/Dema/Dema.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// DOUBLE EXPONENTIAL MOVING AVERAGE (UTILITIES) - +/// +/// Provides utility methods for the DEMA (Double Exponential Moving Average) indicator. +/// public static partial class Dema { - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the DEMA results. + /// + /// The list of DEMA results. + /// A list of DEMA results with the warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -16,7 +20,11 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(2 * n + 100); } - // parameter validation + /// + /// Validates the parameters for the DEMA calculation. + /// + /// The number of periods to look back for the calculation. + /// Thrown when the lookback periods are less than or equal to 0. internal static void Validate( int lookbackPeriods) { diff --git a/src/a-d/Dema/info.xml b/src/a-d/Dema/info.xml deleted file mode 100644 index 4e9187158..000000000 --- a/src/a-d/Dema/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Double Exponential Moving Average (DEMA) of the price. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of Double EMA values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/Doji/Doji.StaticSeries.cs b/src/a-d/Doji/Doji.StaticSeries.cs index 982d4f3eb..e15da634a 100644 --- a/src/a-d/Doji/Doji.StaticSeries.cs +++ b/src/a-d/Doji/Doji.StaticSeries.cs @@ -1,7 +1,8 @@ namespace Skender.Stock.Indicators; -// DOJI (SERIES) - +/// +/// Provides methods for identifying Doji candlestick patterns in a series of quotes. +/// public static partial class Doji { /// @@ -12,7 +13,7 @@ public static partial class Doji /// See Guide for more information. /// Historical price quotes. /// - /// Optional.Maximum absolute percent difference in open and close price. + /// Optional. Maximum absolute percent difference in open and close price. /// /// Time series of Doji values. /// diff --git a/src/a-d/Doji/Doji.Utilities.cs b/src/a-d/Doji/Doji.Utilities.cs index eb8b3988c..2d1b7e031 100644 --- a/src/a-d/Doji/Doji.Utilities.cs +++ b/src/a-d/Doji/Doji.Utilities.cs @@ -1,10 +1,17 @@ namespace Skender.Stock.Indicators; -// DOJI (UTILITIES) - +/// +/// Provides utility methods for Doji candlestick patterns. +/// public static partial class Doji { - // parameter validation + /// + /// Validates the maximum price change percentage for Doji candlestick patterns. + /// + /// The maximum price change percentage to validate. + /// + /// Thrown when the max price change percent is less than 0 or greater than 0.5. + /// internal static void Validate( double maxPriceChangePercent) { diff --git a/src/a-d/Donchian/Donchian.Models.cs b/src/a-d/Donchian/Donchian.Models.cs index f20ad4ba5..81de6a8d8 100644 --- a/src/a-d/Donchian/Donchian.Models.cs +++ b/src/a-d/Donchian/Donchian.Models.cs @@ -1,5 +1,13 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Donchian Channel calculation. +/// +/// The timestamp of the result. +/// The upper band value of the Donchian Channel. +/// The centerline value of the Donchian Channel. +/// The lower band value of the Donchian Channel. +/// The width of the Donchian Channel. [Serializable] public record DonchianResult ( diff --git a/src/a-d/Donchian/Donchian.StaticSeries.cs b/src/a-d/Donchian/Donchian.StaticSeries.cs index 483ac4a34..7b2041014 100644 --- a/src/a-d/Donchian/Donchian.StaticSeries.cs +++ b/src/a-d/Donchian/Donchian.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// DONCHIAN CHANNEL (SERIES) - +/// +/// Provides methods for calculating Donchian Channels. +/// public static partial class Donchian { + /// + /// Converts a list of quotes to Donchian Channel results. + /// + /// The type of the quote. + /// The list of quotes. + /// The number of periods to look back for the calculation. + /// A list of Donchian Channel results. + /// Thrown when the quotes list is null. + /// Thrown when the lookback periods are invalid. public static IReadOnlyList ToDonchian( this IReadOnlyList quotes, int lookbackPeriods = 20) @@ -57,7 +67,6 @@ public static IReadOnlyList ToDonchian( else { results.Add(new(q.Timestamp)); - } } diff --git a/src/a-d/Donchian/Donchian.Utilities.cs b/src/a-d/Donchian/Donchian.Utilities.cs index 5cd392a2e..173182a33 100644 --- a/src/a-d/Donchian/Donchian.Utilities.cs +++ b/src/a-d/Donchian/Donchian.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// DONCHIAN CHANNEL (UTILITIES) - +/// +/// Provides utility methods for Donchian Channel calculations. +/// public static partial class Donchian { - // CONDENSE (REMOVE null results) + /// + /// Removes empty (null) periods from the Donchian Channel results. + /// + /// The list of Donchian Channel results. + /// A list of Donchian Channel results with empty periods removed. /// public static IReadOnlyList Condense( this IReadOnlyList results) @@ -19,7 +24,11 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } - // remove recommended periods + /// + /// Removes the recommended warmup periods from the Donchian Channel results. + /// + /// The list of Donchian Channel results. + /// A list of Donchian Channel results with warmup periods removed. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) @@ -31,7 +40,13 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } - // parameter validation + /// + /// Validates the lookback periods for Donchian Channel calculations. + /// + /// The number of periods to look back for the calculation. + /// + /// Thrown when the lookback periods are less than or equal to 0. + /// internal static void Validate( int lookbackPeriods) { diff --git a/src/a-d/Donchian/info.xml b/src/a-d/Donchian/info.xml deleted file mode 100644 index a77e2c819..000000000 --- a/src/a-d/Donchian/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Donchian Channels, also called Price Channels, are derived from highest High and lowest Low values over a lookback window. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of Donchian Channel values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/Dpo/Dpo.Models.cs b/src/a-d/Dpo/Dpo.Models.cs index a6150372e..ee68569aa 100644 --- a/src/a-d/Dpo/Dpo.Models.cs +++ b/src/a-d/Dpo/Dpo.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Detrended Price Oscillator (DPO) calculation. +/// +/// The timestamp of the result. +/// The DPO value. +/// The Simple Moving Average (SMA) value. [Serializable] public record DpoResult ( @@ -8,5 +14,6 @@ public record DpoResult double? Sma = null ) : IReusable { + /// public double Value => Dpo.Null2NaN(); } diff --git a/src/a-d/Dpo/Dpo.StaticSeries.cs b/src/a-d/Dpo/Dpo.StaticSeries.cs index 1298baca3..823cb5965 100644 --- a/src/a-d/Dpo/Dpo.StaticSeries.cs +++ b/src/a-d/Dpo/Dpo.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// DETRENDED PRICE OSCILLATOR (SERIES) - +/// +/// Provides methods for calculating the Detrended Price Oscillator (DPO). +/// public static partial class Dpo { + /// + /// Converts a list of source data to Detrended Price Oscillator (DPO) results. + /// + /// The type of the source data. + /// The list of source data. + /// The number of periods to look back for the calculation. + /// A list of DPO results. + /// Thrown when the source list is null. + /// Thrown when the lookback periods are invalid. public static IReadOnlyList ToDpo( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/a-d/Dpo/Dpo.Utilities.cs b/src/a-d/Dpo/Dpo.Utilities.cs index d6170d70c..533b2a0e9 100644 --- a/src/a-d/Dpo/Dpo.Utilities.cs +++ b/src/a-d/Dpo/Dpo.Utilities.cs @@ -1,10 +1,17 @@ namespace Skender.Stock.Indicators; -// DETRENDED PRICE OSCILLATOR (UTILITIES) - +/// +/// Provides utility methods for Detrended Price Oscillator (DPO) calculations. +/// public static partial class Dpo { - // parameter validation + /// + /// Validates the lookback periods for DPO calculations. + /// + /// The number of periods to look back for the calculation. + /// + /// Thrown when the lookback periods are less than or equal to 0. + /// internal static void Validate( int lookbackPeriods) { @@ -15,5 +22,4 @@ internal static void Validate( "Lookback periods must be greater than 0 for DPO."); } } - } diff --git a/src/a-d/Dpo/info.xml b/src/a-d/Dpo/info.xml deleted file mode 100644 index b2a30d467..000000000 --- a/src/a-d/Dpo/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Detrended Price Oscillator (DPO) depicts the difference between price and an offset simple moving average. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of DPO values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/a-d/Dynamic/Dynamic.Models.cs b/src/a-d/Dynamic/Dynamic.Models.cs index f120c7156..62ad74196 100644 --- a/src/a-d/Dynamic/Dynamic.Models.cs +++ b/src/a-d/Dynamic/Dynamic.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Dynamic calculation. +/// +/// The timestamp of the result. +/// The Dynamic value. [Serializable] public record DynamicResult ( @@ -7,5 +12,6 @@ public record DynamicResult double? Dynamic ) : IReusable { + /// public double Value => Dynamic.Null2NaN(); } diff --git a/src/a-d/Dynamic/Dynamic.StaticSeries.cs b/src/a-d/Dynamic/Dynamic.StaticSeries.cs index 03c56c40e..44b2e67e7 100644 --- a/src/a-d/Dynamic/Dynamic.StaticSeries.cs +++ b/src/a-d/Dynamic/Dynamic.StaticSeries.cs @@ -1,9 +1,20 @@ namespace Skender.Stock.Indicators; -// McGINLEY DYNAMIC (SERIES) - +/// +/// Provides methods for calculating the McGinley Dynamic indicator. +/// public static partial class MgDynamic { + /// + /// Converts a list of source data to McGinley Dynamic results. + /// + /// The type of the source data. + /// The list of source data. + /// The number of periods to look back for the calculation. + /// The smoothing factor for the calculation. + /// A list of McGinley Dynamic results. + /// Thrown when the source list is null. + /// Thrown when the lookback periods or kFactor are invalid. public static IReadOnlyList ToDynamic( this IReadOnlyList source, int lookbackPeriods, diff --git a/src/a-d/Dynamic/Dynamic.Utilities.cs b/src/a-d/Dynamic/Dynamic.Utilities.cs index 5228d3c4d..47d753ff4 100644 --- a/src/a-d/Dynamic/Dynamic.Utilities.cs +++ b/src/a-d/Dynamic/Dynamic.Utilities.cs @@ -1,10 +1,18 @@ namespace Skender.Stock.Indicators; -// McGINLEY DYNAMIC (UTILITIES) - +/// +/// Provides utility methods for McGinley Dynamic calculations. +/// public static partial class MgDynamic { - // increment calculation + /// + /// Calculates the increment for the McGinley Dynamic. + /// + /// The number of periods to look back for the calculation. + /// The smoothing factor for the calculation. + /// The new value. + /// The previous dynamic value. + /// The calculated increment. public static double Increment( int lookbackPeriods, double kFactor, @@ -14,7 +22,14 @@ public static double Increment( (newVal - prevDyn) / (kFactor * lookbackPeriods * Math.Pow(newVal / prevDyn, 4))); - // parameter validation + /// + /// Validates the parameters for McGinley Dynamic calculations. + /// + /// The number of periods to look back for the calculation. + /// The smoothing factor for the calculation. + /// + /// Thrown when the lookback periods or kFactor are less than or equal to 0. + /// internal static void Validate( int lookbackPeriods, double kFactor) @@ -32,5 +47,4 @@ internal static void Validate( "K-Factor range adjustment must be greater than 0 for DYNAMIC."); } } - } diff --git a/src/a-d/Dynamic/info.xml b/src/a-d/Dynamic/info.xml deleted file mode 100644 index a056a4620..000000000 --- a/src/a-d/Dynamic/info.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - McGinley Dynamic is a more responsive variant of exponential moving average. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Optional. Range adjustment factor. - Time series of Dynamic values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/e-k/ElderRay/ElderRay.Models.cs b/src/e-k/ElderRay/ElderRay.Models.cs index 275bf9580..6c1cf774f 100644 --- a/src/e-k/ElderRay/ElderRay.Models.cs +++ b/src/e-k/ElderRay/ElderRay.Models.cs @@ -1,5 +1,12 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of an Elder Ray calculation. +/// +/// The timestamp of the result. +/// The Exponential Moving Average (EMA) value. +/// The Bull Power value. +/// The Bear Power value. [Serializable] public record ElderRayResult ( @@ -9,5 +16,6 @@ public record ElderRayResult double? BearPower ) : IReusable { + /// public double Value => (BullPower + BearPower).Null2NaN(); } diff --git a/src/e-k/ElderRay/ElderRay.StaticSeries.cs b/src/e-k/ElderRay/ElderRay.StaticSeries.cs index 785223db6..8378ea384 100644 --- a/src/e-k/ElderRay/ElderRay.StaticSeries.cs +++ b/src/e-k/ElderRay/ElderRay.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// ELDER-RAY (SERIES) - +/// +/// Provides methods for calculating the Elder Ray indicator. +/// public static partial class ElderRay { + /// + /// Converts a list of quotes to Elder Ray results. + /// + /// The type of the quote data. + /// The list of quotes. + /// The number of periods to look back for the calculation. + /// A list of Elder Ray results. + /// Thrown when the quotes list is null. + /// Thrown when the lookback periods are invalid. public static IReadOnlyList ToElderRay( this IReadOnlyList quotes, int lookbackPeriods = 13) @@ -11,6 +21,12 @@ public static IReadOnlyList ToElderRay( .ToQuoteDList() .CalcElderRay(lookbackPeriods); + /// + /// Calculates the Elder Ray indicator. + /// + /// The list of source data. + /// The number of periods to look back for the calculation. + /// A list of Elder Ray results. private static List CalcElderRay( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/e-k/ElderRay/ElderRay.Utilities.cs b/src/e-k/ElderRay/ElderRay.Utilities.cs index 5e789103b..4cdc9cd01 100644 --- a/src/e-k/ElderRay/ElderRay.Utilities.cs +++ b/src/e-k/ElderRay/ElderRay.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// ELDER-RAY (UTILITIES) - +/// +/// Provides utility methods for Elder Ray calculations. +/// public static partial class ElderRay { - // remove recommended periods + /// + /// Removes the recommended warmup periods from the Elder Ray results. + /// + /// The list of Elder Ray results. + /// A list of Elder Ray results with warmup periods removed. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) @@ -16,7 +21,13 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 100); } - // parameter validation + /// + /// Validates the lookback periods for Elder Ray calculations. + /// + /// The number of periods to look back for the calculation. + /// + /// Thrown when the lookback periods are less than or equal to 0. + /// internal static void Validate( int lookbackPeriods) { diff --git a/src/e-k/ElderRay/info.xml b/src/e-k/ElderRay/info.xml deleted file mode 100644 index 3af7954b4..000000000 --- a/src/e-k/ElderRay/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - The Elder-ray Index depicts buying and selling pressure, also known as Bull and Bear Power. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for the EMA. - Time series of Elder-ray Index values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/e-k/Ema/Ema.Increments.cs b/src/e-k/Ema/Ema.Increments.cs index 0df4706d5..de39ff2f1 100644 --- a/src/e-k/Ema/Ema.Increments.cs +++ b/src/e-k/Ema/Ema.Increments.cs @@ -1,16 +1,17 @@ namespace Skender.Stock.Indicators; -// EXPONENTIAL MOVING AVERAGE (INCREMENTING LIST) - /// -/// Exponential Moving Average (EMA) -/// from incremental reusable values. +/// Exponential Moving Average (EMA) from incremental reusable values. /// public class EmaList : List, IEma, IAddQuote, IAddReusable { private readonly Queue _buffer; private double _bufferSum; + /// + /// Initializes a new instance of the class. + /// + /// The number of periods to look back for the calculation. public EmaList(int lookbackPeriods) { Ema.Validate(lookbackPeriods); @@ -21,9 +22,21 @@ public EmaList(int lookbackPeriods) _bufferSum = 0; } + /// + /// Gets the number of periods to look back for the calculation. + /// public int LookbackPeriods { get; init; } + + /// + /// Gets the smoothing factor for the calculation. + /// public double K { get; init; } + /// + /// Adds a new value to the EMA list. + /// + /// The timestamp of the value. + /// The value to add. public void Add(DateTime timestamp, double value) { // update buffer @@ -56,12 +69,22 @@ public void Add(DateTime timestamp, double value) Ema.Increment(K, this[^1].Ema, value))); } + /// + /// Adds a new reusable value to the EMA list. + /// + /// The reusable value to add. + /// Thrown when the value is null. public void Add(IReusable value) { ArgumentNullException.ThrowIfNull(value); Add(value.Timestamp, value.Value); } + /// + /// Adds a list of reusable values to the EMA list. + /// + /// The list of reusable values to add. + /// Thrown when the values list is null. public void Add(IReadOnlyList values) { ArgumentNullException.ThrowIfNull(values); @@ -72,12 +95,22 @@ public void Add(IReadOnlyList values) } } + /// + /// Adds a new quote to the EMA list. + /// + /// The quote to add. + /// Thrown when the quote is null. public void Add(IQuote quote) { ArgumentNullException.ThrowIfNull(quote); Add(quote.Timestamp, quote.Value); } + /// + /// Adds a list of quotes to the EMA list. + /// + /// The list of quotes to add. + /// Thrown when the quotes list is null. public void Add(IReadOnlyList quotes) { ArgumentNullException.ThrowIfNull(quotes); diff --git a/src/e-k/Ema/Ema.Models.cs b/src/e-k/Ema/Ema.Models.cs index 7b5fca5c0..96f2f4029 100644 --- a/src/e-k/Ema/Ema.Models.cs +++ b/src/e-k/Ema/Ema.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of an Exponential Moving Average (EMA) calculation. +/// +/// The timestamp of the result. +/// The EMA value. [Serializable] public record EmaResult ( @@ -7,5 +12,6 @@ public record EmaResult double? Ema = null ) : IReusable { + /// public double Value => Ema.Null2NaN(); } diff --git a/src/e-k/Ema/Ema.StaticSeries.cs b/src/e-k/Ema/Ema.StaticSeries.cs index 49ad66637..d1c7e46df 100644 --- a/src/e-k/Ema/Ema.StaticSeries.cs +++ b/src/e-k/Ema/Ema.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// EXPONENTIAL MOVING AVERAGE (SERIES) - +/// +/// Provides methods for calculating the Exponential Moving Average (EMA) indicator. +/// public static partial class Ema { + /// + /// Converts a list of source data to EMA results. + /// + /// The type of the source data. + /// The list of source data. + /// The number of periods to look back for the calculation. + /// A list of EMA results. + /// Thrown when the source list is null. + /// Thrown when the lookback periods are invalid. public static IReadOnlyList ToEma( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/e-k/Ema/Ema.StreamHub.cs b/src/e-k/Ema/Ema.StreamHub.cs index c66697259..173bfabc8 100644 --- a/src/e-k/Ema/Ema.StreamHub.cs +++ b/src/e-k/Ema/Ema.StreamHub.cs @@ -1,34 +1,58 @@ namespace Skender.Stock.Indicators; -// EXPONENTIAL MOVING AVERAGE (STREAM HUB) - -#region hub interface and initializer - +/// +/// Interface for Exponential Moving Average (EMA) calculations. +/// public interface IEma { + /// + /// Gets the number of periods to look back for the calculation. + /// int LookbackPeriods { get; } + + /// + /// Gets the smoothing factor for the calculation. + /// double K { get; } } +/// +/// Provides methods for calculating the Exponential Moving Average (EMA) indicator. +/// public static partial class Ema { - // HUB, from Chain Provider + /// + /// Creates an EMA hub from a chain provider. + /// + /// The type of the reusable data. + /// The chain provider. + /// The number of periods to look back for the calculation. + /// An EMA hub. + /// Thrown when the chain provider is null. + /// Thrown when the lookback periods are invalid. public static EmaHub ToEma( this IChainProvider chainProvider, int lookbackPeriods) where T : IReusable => new(chainProvider, lookbackPeriods); } -#endregion - +/// +/// Represents a hub for Exponential Moving Average (EMA) calculations. +/// +/// The type of the input data. public class EmaHub : ChainProvider, IEma where TIn : IReusable { - #region constructors - private readonly string hubName; + /// + /// Initializes a new instance of the class. + /// + /// The chain provider. + /// The number of periods to look back for the calculation. + /// Thrown when the provider is null. + /// Thrown when the lookback periods are invalid. internal EmaHub( IChainProvider provider, int lookbackPeriods) : base(provider) @@ -40,15 +64,17 @@ internal EmaHub( Reinitialize(); } - #endregion + /// public int LookbackPeriods { get; init; } - public double K { get; init; } - // METHODS + /// + public double K { get; init; } + /// public override string ToString() => hubName; + /// protected override (EmaResult result, int index) ToIndicator(TIn item, int? indexHint) { diff --git a/src/e-k/Ema/Ema.Utilities.cs b/src/e-k/Ema/Ema.Utilities.cs index 6a8f8f1f0..67f29dc96 100644 --- a/src/e-k/Ema/Ema.Utilities.cs +++ b/src/e-k/Ema/Ema.Utilities.cs @@ -1,15 +1,30 @@ namespace Skender.Stock.Indicators; -// EXPONENTIAL MOVING AVERAGE (UTILITIES) - +/// +/// Provides utility methods for Exponential Moving Average (EMA) calculations. +/// public static partial class Ema { + /// + /// Increments the EMA value using the smoothing factor. + /// + /// The smoothing factor. + /// The last EMA value. + /// The new price value. + /// The incremented EMA value. public static double Increment( double k, double lastEma, double newPrice) => lastEma + k * (newPrice - lastEma); + /// + /// Increments the EMA value using the lookback periods. + /// + /// The number of periods to look back for the calculation. + /// The last EMA value. + /// The new price value. + /// The incremented EMA value. public static double Increment( int lookbackPeriods, double lastEma, @@ -19,13 +34,24 @@ public static double Increment( return Increment(k, lastEma, newPrice); } + /// + /// Increments the EMA value using the smoothing factor. + /// + /// The smoothing factor. + /// The last EMA value. + /// The new price value. + /// The incremented EMA value, or null if the last EMA value is null. public static double? Increment( double k, double? lastEma, double newPrice) => lastEma + k * (newPrice - lastEma); - // remove recommended periods + /// + /// Removes the recommended warmup periods from the EMA results. + /// + /// The list of EMA results. + /// A list of EMA results with warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -36,7 +62,13 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 100); } - // parameter validation + /// + /// Validates the lookback periods for EMA calculations. + /// + /// The number of periods to look back for the calculation. + /// + /// Thrown when the lookback periods are less than or equal to 0. + /// internal static void Validate( int lookbackPeriods) { diff --git a/src/e-k/Ema/info.xml b/src/e-k/Ema/info.xml deleted file mode 100644 index a0b526913..000000000 --- a/src/e-k/Ema/info.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - Exponential Moving Average (EMA) of price or any other specified OHLCV element. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of EMA values. - Invalid parameter value provided. - - diff --git a/src/e-k/Epma/Epma.Models.cs b/src/e-k/Epma/Epma.Models.cs index afd3c6e3c..b344aaeba 100644 --- a/src/e-k/Epma/Epma.Models.cs +++ b/src/e-k/Epma/Epma.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of an Exponential Polynomial Moving Average (EPMA) calculation. +/// +/// The timestamp of the result. +/// The EPMA value. [Serializable] public record EpmaResult ( @@ -7,5 +12,6 @@ public record EpmaResult double? Epma ) : IReusable { + /// public double Value => Epma.Null2NaN(); } diff --git a/src/e-k/Epma/Epma.StaticSeries.cs b/src/e-k/Epma/Epma.StaticSeries.cs index 131d601be..ee3c347aa 100644 --- a/src/e-k/Epma/Epma.StaticSeries.cs +++ b/src/e-k/Epma/Epma.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// ENDPOINT MOVING AVERAGE (SERIES) - +/// +/// Provides methods for calculating the EPMA (Endpoint Moving Average) indicator. +/// public static partial class Epma { + /// + /// Converts a list of source data to EPMA results. + /// + /// The type of the source data. + /// The list of source data. + /// The number of periods to look back for the calculation. + /// A list of EPMA results. + /// Thrown when the source list is null. + /// Thrown when the lookback periods are invalid. public static IReadOnlyList ToEpma( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/e-k/Epma/Epma.Utilities.cs b/src/e-k/Epma/Epma.Utilities.cs index 49bfc8370..068d17712 100644 --- a/src/e-k/Epma/Epma.Utilities.cs +++ b/src/e-k/Epma/Epma.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// ENDPOINT MOVING AVERAGE (UTILITIES) - +/// +/// Provides utility methods for Endpoint Moving Average (EPMA) calculations. +/// public static partial class Epma { - // remove recommended periods + /// + /// Removes the recommended warmup periods from the EPMA results. + /// + /// The list of EPMA results. + /// A list of EPMA results with warmup periods removed. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) @@ -16,7 +21,13 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } - // parameter validation + /// + /// Validates the lookback periods for EPMA calculations. + /// + /// The number of periods to look back for the calculation. + /// + /// Thrown when the lookback periods are less than or equal to 0. + /// internal static void Validate( int lookbackPeriods) { diff --git a/src/e-k/Epma/info.xml b/src/e-k/Epma/info.xml deleted file mode 100644 index d31fe51e9..000000000 --- a/src/e-k/Epma/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Endpoint Moving Average (EPMA), also known as Least Squares Moving Average (LSMA), plots the projected last point of a linear regression lookback window. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of Endpoint Moving Average values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/e-k/Fcb/Fcb.Models.cs b/src/e-k/Fcb/Fcb.Models.cs index 6dead53fd..c73e6dbe6 100644 --- a/src/e-k/Fcb/Fcb.Models.cs +++ b/src/e-k/Fcb/Fcb.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Fractal Channel Band (FCB) calculation. +/// +/// The timestamp of the result. +/// The upper band value. +/// The lower band value. [Serializable] public record FcbResult ( diff --git a/src/e-k/Fcb/Fcb.StaticSeries.cs b/src/e-k/Fcb/Fcb.StaticSeries.cs index 36ca6553e..5177a55e9 100644 --- a/src/e-k/Fcb/Fcb.StaticSeries.cs +++ b/src/e-k/Fcb/Fcb.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// FRACTAL CHAOS BANDS (SERIES) - +/// +/// Provides methods for calculating the Fractal Chaos Bands (FCB) indicator. +/// public static partial class Fcb { + /// + /// Converts a list of quotes to FCB results. + /// + /// The type of the quote data. + /// The list of quotes. + /// The window span for the calculation. Default is 2. + /// A list of FCB results. + /// Thrown when the quotes list is null. + /// Thrown when the window span is invalid. public static IReadOnlyList ToFcb( this IReadOnlyList quotes, int windowSpan = 2) diff --git a/src/e-k/Fcb/Fcb.Utilities.cs b/src/e-k/Fcb/Fcb.Utilities.cs index 6a3dc52b4..e78a1a90e 100644 --- a/src/e-k/Fcb/Fcb.Utilities.cs +++ b/src/e-k/Fcb/Fcb.Utilities.cs @@ -1,9 +1,15 @@ namespace Skender.Stock.Indicators; -// FRACTAL CHAOS BANDS (UTILITIES) - +/// +/// Provides utility methods for Fractal Chaos Bands (FCB) calculations. +/// public static partial class Fcb { + /// + /// Removes empty (null) periods from the FCB results. + /// + /// The list of FCB results. + /// A list of FCB results with empty periods removed. /// public static IReadOnlyList Condense( this IReadOnlyList results) @@ -18,6 +24,11 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } + /// + /// Removes the recommended warmup periods from the FCB results. + /// + /// The list of FCB results. + /// A list of FCB results with warmup periods removed. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) @@ -29,7 +40,13 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } - // parameter validation + /// + /// Validates the window span for FCB calculations. + /// + /// The window span for the calculation. + /// + /// Thrown when the window span is less than 2. + /// internal static void Validate( int windowSpan) { diff --git a/src/e-k/Fcb/info.xml b/src/e-k/Fcb/info.xml deleted file mode 100644 index 9b405b475..000000000 --- a/src/e-k/Fcb/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Fractal Chaos Bands outline high and low price channels to depict broad less-chaotic price movements. FCB is a channelized depiction of Williams Fractals. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of span periods in the evaluation window. - Time series of Fractal Chaos Band and Oscillator values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/e-k/FisherTransform/FisherTransform.Models.cs b/src/e-k/FisherTransform/FisherTransform.Models.cs index a02dc5ea3..406506ebc 100644 --- a/src/e-k/FisherTransform/FisherTransform.Models.cs +++ b/src/e-k/FisherTransform/FisherTransform.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Fisher Transform calculation. +/// +/// The timestamp of the result. +/// The Fisher Transform value. +/// The trigger value. [Serializable] public record FisherTransformResult ( @@ -8,5 +14,6 @@ public record FisherTransformResult double? Trigger ) : IReusable { + /// public double Value => Fisher.Null2NaN(); } diff --git a/src/e-k/FisherTransform/FisherTransform.StaticSeries.cs b/src/e-k/FisherTransform/FisherTransform.StaticSeries.cs index a9f542ba8..873d5ab46 100644 --- a/src/e-k/FisherTransform/FisherTransform.StaticSeries.cs +++ b/src/e-k/FisherTransform/FisherTransform.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// FISHER TRANSFORM (SERIES) - +/// +/// Provides methods for calculating the Fisher Transform indicator. +/// public static partial class FisherTransform { + /// + /// Converts a list of source data to Fisher Transform results. + /// + /// The type of the source data. + /// The list of source data. + /// The number of periods to look back for the calculation. Default is 10. + /// A list of Fisher Transform results. + /// Thrown when the source list is null. + /// Thrown when the lookback periods are invalid. public static IReadOnlyList ToFisherTransform( this IReadOnlyList source, int lookbackPeriods = 10) diff --git a/src/e-k/FisherTransform/FisherTransform.Utilities.cs b/src/e-k/FisherTransform/FisherTransform.Utilities.cs index 8d192b5db..fb8e89403 100644 --- a/src/e-k/FisherTransform/FisherTransform.Utilities.cs +++ b/src/e-k/FisherTransform/FisherTransform.Utilities.cs @@ -1,10 +1,17 @@ namespace Skender.Stock.Indicators; -// FISHER TRANSFORM (UTILITIES) - +/// +/// Provides utility methods for Fisher Transform calculations. +/// public static partial class FisherTransform { - // parameter validation + /// + /// Validates the lookback periods for Fisher Transform calculations. + /// + /// The number of periods to look back for the calculation. + /// + /// Thrown when the lookback periods are less than or equal to 0. + /// internal static void Validate( int lookbackPeriods) { diff --git a/src/e-k/FisherTransform/info.xml b/src/e-k/FisherTransform/info.xml deleted file mode 100644 index 67987cb7a..000000000 --- a/src/e-k/FisherTransform/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Ehlers Fisher Transform converts prices into a Gaussian normal distribution. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of Fisher Transform values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/e-k/ForceIndex/ForceIndex.Models.cs b/src/e-k/ForceIndex/ForceIndex.Models.cs index 0a02fcac7..b26364cca 100644 --- a/src/e-k/ForceIndex/ForceIndex.Models.cs +++ b/src/e-k/ForceIndex/ForceIndex.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Force Index calculation. +/// +/// The timestamp of the result. +/// The Force Index value. [Serializable] public record ForceIndexResult ( @@ -7,5 +12,6 @@ public record ForceIndexResult double? ForceIndex = null ) : IReusable { + /// public double Value => ForceIndex.Null2NaN(); } diff --git a/src/e-k/ForceIndex/ForceIndex.StaticSeries.cs b/src/e-k/ForceIndex/ForceIndex.StaticSeries.cs index 1bea16987..7803f1baf 100644 --- a/src/e-k/ForceIndex/ForceIndex.StaticSeries.cs +++ b/src/e-k/ForceIndex/ForceIndex.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// FORCE INDEX (SERIES) - +/// +/// Provides methods for calculating the Force Index indicator. +/// public static partial class ForceIndex { + /// + /// Converts a list of quotes to Force Index results. + /// + /// The type of the quote data. + /// The list of quotes. + /// The number of periods to look back for the calculation. Default is 2. + /// A list of Force Index results. + /// Thrown when the quotes list is null. + /// Thrown when the lookback periods are invalid. public static IReadOnlyList ToForceIndex( this IReadOnlyList quotes, int lookbackPeriods = 2) @@ -11,6 +21,14 @@ public static IReadOnlyList ToForceIndex( .ToQuoteDList() .CalcForceIndex(lookbackPeriods); + /// + /// Calculates the Force Index for a list of quotes. + /// + /// The list of quotes. + /// The number of periods to look back for the calculation. + /// A list of Force Index results. + /// Thrown when the source list is null. + /// Thrown when the lookback periods are invalid. private static List CalcForceIndex( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/e-k/ForceIndex/ForceIndex.Utilities.cs b/src/e-k/ForceIndex/ForceIndex.Utilities.cs index 025c244ca..27024a9c9 100644 --- a/src/e-k/ForceIndex/ForceIndex.Utilities.cs +++ b/src/e-k/ForceIndex/ForceIndex.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// FORCE INDEX (UTILITIES) - +/// +/// Provides utility methods for Force Index calculations. +/// public static partial class ForceIndex { - // remove recommended periods + /// + /// Removes the recommended warmup periods from the Force Index results. + /// + /// The list of Force Index results. + /// A list of Force Index results with warmup periods removed. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) @@ -16,7 +21,13 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 100); } - // parameter validation + /// + /// Validates the lookback periods for Force Index calculations. + /// + /// The number of periods to look back for the calculation. + /// + /// Thrown when the lookback periods are less than or equal to 0. + /// internal static void Validate( int lookbackPeriods) { diff --git a/src/e-k/ForceIndex/info.xml b/src/e-k/ForceIndex/info.xml deleted file mode 100644 index 144bff8e5..000000000 --- a/src/e-k/ForceIndex/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - The Force Index depicts volume-based buying and selling pressure. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for the EMA of Force Index. - Time series of Force Index values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/e-k/Fractal/Fractal.Models.cs b/src/e-k/Fractal/Fractal.Models.cs index 23fb6e239..82921c59b 100644 --- a/src/e-k/Fractal/Fractal.Models.cs +++ b/src/e-k/Fractal/Fractal.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Fractal calculation. +/// +/// The timestamp of the result. +/// The Fractal Bear value. +/// The Fractal Bull value. [Serializable] public record FractalResult ( diff --git a/src/e-k/Fractal/Fractal.StaticSeries.cs b/src/e-k/Fractal/Fractal.StaticSeries.cs index 2dd3b1615..a5a0e2c03 100644 --- a/src/e-k/Fractal/Fractal.StaticSeries.cs +++ b/src/e-k/Fractal/Fractal.StaticSeries.cs @@ -1,9 +1,20 @@ namespace Skender.Stock.Indicators; -// WILLIAMS FRACTAL (SERIES) - +/// +/// Provides methods for calculating the Williams Fractal indicator. +/// public static partial class Fractal { + /// + /// Converts a list of quotes to Fractal results using the same span for both left and right wings. + /// + /// The type of the quote data. + /// The list of quotes. + /// The number of periods to look back and forward for the calculation. Default is 2. + /// The type of price to use for the calculation. Default is HighLow. + /// A list of Fractal results. + /// Thrown when the quotes list is null. + /// Thrown when the window span is invalid. public static IReadOnlyList ToFractal( this IReadOnlyList quotes, int windowSpan = 2, @@ -11,6 +22,17 @@ public static IReadOnlyList ToFractal( where TQuote : IQuote => quotes .ToFractal(windowSpan, windowSpan, endType); + /// + /// Converts a list of quotes to Fractal results using different spans for left and right wings. + /// + /// The type of the quote data. + /// The list of quotes. + /// The number of periods to look back for the calculation. + /// The number of periods to look forward for the calculation. + /// The type of price to use for the calculation. Default is HighLow. + /// A list of Fractal results. + /// Thrown when the quotes list is null. + /// Thrown when the left or right span is invalid. public static IReadOnlyList ToFractal( this IReadOnlyList quotes, int leftSpan, diff --git a/src/e-k/Fractal/Fractal.Utilities.cs b/src/e-k/Fractal/Fractal.Utilities.cs index 9e36b3105..1b8b24be1 100644 --- a/src/e-k/Fractal/Fractal.Utilities.cs +++ b/src/e-k/Fractal/Fractal.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// WILLIAMS FRACTAL (UTILITIES) - +/// +/// Provides utility methods for Williams Fractal calculations. +/// public static partial class Fractal { - // CONDENSE (REMOVE null results) + /// + /// Removes empty (null) periods from the Fractal results. + /// + /// The list of Fractal results. + /// A list of Fractal results with empty periods removed. /// public static IReadOnlyList Condense( this IReadOnlyList results) @@ -19,7 +24,13 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } - // parameter validation + /// + /// Validates the window span for Fractal calculations. + /// + /// The number of periods to look back and forward for the calculation. + /// + /// Thrown when the window span is less than 2. + /// internal static void Validate( int windowSpan) { diff --git a/src/e-k/Fractal/info.xml b/src/e-k/Fractal/info.xml deleted file mode 100644 index febf92632..000000000 --- a/src/e-k/Fractal/info.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - Williams Fractal is a retrospective price pattern that identifies a central high or low point over a lookback window. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of span periods to the left and right of the evaluation period. - Determines use of Close or High/Low wicks for points. - Time series of Williams Fractal Bull/Bear values. - Invalid parameter value provided. - - - - - Williams Fractal is a retrospective price pattern that identifies a central high or low point over a lookback window. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of span periods to the left of the evaluation period. - Number of span periods to the right of the evaluation period. - Determines use of Close or High/Low wicks for points. - Time series of Williams Fractal Bull/Bear values. - Invalid parameter value provided. - - - \ No newline at end of file diff --git a/src/e-k/Gator/Gator.Models.cs b/src/e-k/Gator/Gator.Models.cs index 8e7311cf3..ea7e6cc2c 100644 --- a/src/e-k/Gator/Gator.Models.cs +++ b/src/e-k/Gator/Gator.Models.cs @@ -1,5 +1,13 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Gator Oscillator calculation. +/// +/// The timestamp of the result. +/// The upper value of the Gator Oscillator. +/// The lower value of the Gator Oscillator. +/// Indicates if the upper value is expanding. +/// Indicates if the lower value is expanding. [Serializable] public record GatorResult ( diff --git a/src/e-k/Gator/Gator.StaticSeries.cs b/src/e-k/Gator/Gator.StaticSeries.cs index 66fe5353b..6327650ee 100644 --- a/src/e-k/Gator/Gator.StaticSeries.cs +++ b/src/e-k/Gator/Gator.StaticSeries.cs @@ -1,19 +1,16 @@ namespace Skender.Stock.Indicators; -// GATOR OSCILLATOR (SERIES) - +/// +/// Provides methods for calculating the Gator Oscillator indicator. +/// public static partial class Gator { /// - /// Gator Oscillator is an expanded view of Williams Alligator. + /// Converts a list of time-series values to Gator Oscillator results. /// - /// - /// - /// - /// T must be or type - /// - /// Time-series values to transform. - /// Time series of Gator values. + /// The type of the time-series values, which must implement or . + /// The list of time-series values to transform. + /// A list of Gator Oscillator results. public static IReadOnlyList ToGator( this IReadOnlyList source) where T : IReusable @@ -21,7 +18,12 @@ public static IReadOnlyList ToGator( .ToAlligator() .ToGator(); - // from [custom] Alligator + /// + /// Converts a list of Alligator results to Gator Oscillator results. + /// + /// The list of Alligator results. + /// A list of Gator Oscillator results. + /// Thrown when the alligator list is null. public static IReadOnlyList ToGator( this IReadOnlyList alligator) { diff --git a/src/e-k/Gator/Gator.Utilities.cs b/src/e-k/Gator/Gator.Utilities.cs index a9b97545a..b3bbd5a77 100644 --- a/src/e-k/Gator/Gator.Utilities.cs +++ b/src/e-k/Gator/Gator.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// GATOR OSCILLATOR (UTILITIES) - +/// +/// Provides utility methods for Gator Oscillator calculations. +/// public static partial class Gator { - // CONDENSE (REMOVE null results) + /// + /// Removes empty (null) periods from the Gator Oscillator results. + /// + /// The list of Gator Oscillator results. + /// A list of Gator Oscillator results with empty periods removed. /// public static IReadOnlyList Condense( this IReadOnlyList results) @@ -19,7 +24,11 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } - // remove recommended periods + /// + /// Removes the recommended warmup periods from the Gator Oscillator results. + /// + /// The list of Gator Oscillator results. + /// A list of Gator Oscillator results with warmup periods removed. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) => results.Remove(150); diff --git a/src/e-k/HeikinAshi/HeikinAshi.Models.cs b/src/e-k/HeikinAshi/HeikinAshi.Models.cs index 13e1d047a..323f86264 100644 --- a/src/e-k/HeikinAshi/HeikinAshi.Models.cs +++ b/src/e-k/HeikinAshi/HeikinAshi.Models.cs @@ -1,5 +1,14 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Heikin-Ashi calculation. +/// +/// The timestamp of the result. +/// The open price of the Heikin-Ashi candle. +/// The high price of the Heikin-Ashi candle. +/// The low price of the Heikin-Ashi candle. +/// The close price of the Heikin-Ashi candle. +/// The volume of the Heikin-Ashi candle. [Serializable] public record HeikinAshiResult( DateTime Timestamp, diff --git a/src/e-k/HeikinAshi/HeikinAshi.StaticSeries.cs b/src/e-k/HeikinAshi/HeikinAshi.StaticSeries.cs index 946cb94eb..f59d18a1e 100644 --- a/src/e-k/HeikinAshi/HeikinAshi.StaticSeries.cs +++ b/src/e-k/HeikinAshi/HeikinAshi.StaticSeries.cs @@ -1,9 +1,17 @@ namespace Skender.Stock.Indicators; -// HEIKIN-ASHI (SERIES) - +/// +/// Provides methods for calculating the Heikin-Ashi indicator. +/// public static class HeikinAshi { + /// + /// Converts a list of quotes to Heikin-Ashi results. + /// + /// The type of the quote data. + /// The list of quotes. + /// A list of Heikin-Ashi results. + /// Thrown when the quotes list is null. public static IReadOnlyList ToHeikinAshi( this IReadOnlyList quotes) where TQuote : IQuote diff --git a/src/e-k/HeikinAshi/info.xml b/src/e-k/HeikinAshi/info.xml deleted file mode 100644 index edbe479f0..000000000 --- a/src/e-k/HeikinAshi/info.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - Heikin-Ashi is a modified candlestick pattern that uses prior day for smoothing. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Time series of Heikin-Ashi candlestick values. - \ No newline at end of file diff --git a/src/e-k/Hma/Hma.Models.cs b/src/e-k/Hma/Hma.Models.cs index 0bc754fa6..700672d3c 100644 --- a/src/e-k/Hma/Hma.Models.cs +++ b/src/e-k/Hma/Hma.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Hull Moving Average (HMA) calculation. +/// +/// The timestamp of the result. +/// The value of the Hull Moving Average. [Serializable] public record HmaResult ( @@ -7,5 +12,6 @@ public record HmaResult double? Hma = null ) : IReusable { + /// public double Value => Hma.Null2NaN(); } diff --git a/src/e-k/Hma/Hma.StaticSeries.cs b/src/e-k/Hma/Hma.StaticSeries.cs index a4c02ba66..987a84f4a 100644 --- a/src/e-k/Hma/Hma.StaticSeries.cs +++ b/src/e-k/Hma/Hma.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// HULL MOVING AVERAGE (SERIES) - +/// +/// Provides methods for calculating the Hull Moving Average (HMA) indicator. +/// public static partial class Hma { + /// + /// Converts a list of time-series values to Hull Moving Average (HMA) results. + /// + /// The type of the time-series values, which must implement . + /// The list of time-series values to transform. + /// The number of periods to look back for the calculation. + /// A list of HMA results. + /// Thrown when the source list is null. + /// Thrown when the lookback periods are less than 2. public static IReadOnlyList ToHma( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/e-k/Hma/Hma.Utilities.cs b/src/e-k/Hma/Hma.Utilities.cs index 2cb778625..d28dc039e 100644 --- a/src/e-k/Hma/Hma.Utilities.cs +++ b/src/e-k/Hma/Hma.Utilities.cs @@ -1,8 +1,17 @@ namespace Skender.Stock.Indicators; +/// +/// Provides utility methods for Hull Moving Average (HMA) calculations. +/// public static partial class Hma { - // parameter validation + /// + /// Validates the lookback periods for HMA calculations. + /// + /// The number of periods to look back for the calculation. + /// + /// Thrown when the lookback periods are less than or equal to 1. + /// internal static void Validate( int lookbackPeriods) { diff --git a/src/e-k/Hma/info.xml b/src/e-k/Hma/info.xml deleted file mode 100644 index 3778aceb8..000000000 --- a/src/e-k/Hma/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Hull Moving Average (HMA) is a modified weighted average of price over N lookback periods that reduces lag. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of HMA values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/e-k/HtTrendline/HtTrendline.Models.cs b/src/e-k/HtTrendline/HtTrendline.Models.cs index 0c73dd30d..0da6f2bc6 100644 --- a/src/e-k/HtTrendline/HtTrendline.Models.cs +++ b/src/e-k/HtTrendline/HtTrendline.Models.cs @@ -1,5 +1,12 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Hilbert Transform Trendline (HTL) calculation. +/// +/// The timestamp of the result. +/// The dominant cycle periods. +/// The value of the trendline. +/// The smoothed price. [Serializable] public record HtlResult ( @@ -9,5 +16,6 @@ public record HtlResult double? SmoothPrice ) : IReusable { + /// public double Value => Trendline.Null2NaN(); } diff --git a/src/e-k/HtTrendline/HtTrendline.StaticSeries.cs b/src/e-k/HtTrendline/HtTrendline.StaticSeries.cs index 0788b5181..a8fb86c03 100644 --- a/src/e-k/HtTrendline/HtTrendline.StaticSeries.cs +++ b/src/e-k/HtTrendline/HtTrendline.StaticSeries.cs @@ -1,19 +1,17 @@ namespace Skender.Stock.Indicators; -// HILBERT TRANSFORM - INSTANTANEOUS TRENDLINE (SERIES) - +/// +/// Provides methods for calculating the Hilbert Transform Instantaneous Trendline (HTL) indicator. +/// public static partial class HtTrendline { - // SERIES, from CHAIN /// - /// Hilbert Transform Instantaneous Trendline(HTL) is a 5-period trendline - /// of high/low price that uses signal processing to reduce noise. + /// Converts a list of time-series values to Hilbert Transform Instantaneous Trendline (HTL) results. /// - /// - /// T must be type - /// - /// Time-series values to transform. - /// Time series of HTL values and smoothed price. + /// The type of the time-series values, which must implement . + /// The list of time-series values to transform. + /// A list of HTL results and smoothed price. + /// Thrown when the source list is null. public static IReadOnlyList ToHtTrendline( this IReadOnlyList source) where T : IReusable diff --git a/src/e-k/HtTrendline/HtTrendline.Utilities.cs b/src/e-k/HtTrendline/HtTrendline.Utilities.cs index 6ca015ee2..4496c45ef 100644 --- a/src/e-k/HtTrendline/HtTrendline.Utilities.cs +++ b/src/e-k/HtTrendline/HtTrendline.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// HILBERT TRANSFORM - INSTANTANEOUS TRENDLINE (UTILITIES) - +/// +/// Provides utility methods for Hilbert Transform Instantaneous Trendline (HTL) calculations. +/// public static partial class HtTrendline { - // remove recommended periods + /// + /// Removes the recommended warmup periods from the HTL results. + /// + /// The list of HTL results. + /// A list of HTL results with the warmup periods removed. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) diff --git a/src/e-k/Hurst/Hurst.Models.cs b/src/e-k/Hurst/Hurst.Models.cs index 6b283a542..d74b766f8 100644 --- a/src/e-k/Hurst/Hurst.Models.cs +++ b/src/e-k/Hurst/Hurst.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Hurst Exponent calculation. +/// +/// The timestamp of the result. +/// The value of the Hurst Exponent. [Serializable] public record HurstResult ( @@ -7,5 +12,6 @@ public record HurstResult double? HurstExponent ) : IReusable { + /// public double Value => HurstExponent.Null2NaN(); } diff --git a/src/e-k/Hurst/Hurst.StaticSeries.cs b/src/e-k/Hurst/Hurst.StaticSeries.cs index 3b2ff650d..c79c7d8d3 100644 --- a/src/e-k/Hurst/Hurst.StaticSeries.cs +++ b/src/e-k/Hurst/Hurst.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// HURST EXPONENT (SERIES) - +/// +/// Provides methods for calculating the Hurst Exponent indicator. +/// public static partial class Hurst { + /// + /// Converts a list of time-series values to Hurst Exponent results. + /// + /// The type of the time-series values, which must implement . + /// The list of time-series values to transform. + /// The number of periods to look back for the calculation. Default is 100. + /// A list of Hurst Exponent results. + /// Thrown when the source list is null. + /// Thrown when the lookback periods are less than or equal to 1. public static IReadOnlyList ToHurst( this IReadOnlyList source, int lookbackPeriods = 100) @@ -54,6 +64,11 @@ public static IReadOnlyList ToHurst( return results; } + /// + /// Calculates the Hurst Exponent for a given window of values. + /// + /// The array of values to evaluate. + /// The calculated Hurst Exponent. private static double CalcHurstWindow(double[] values) { int totalSize = values.Length; diff --git a/src/e-k/Hurst/Hurst.Utilities.cs b/src/e-k/Hurst/Hurst.Utilities.cs index 92fc16415..f230c952a 100644 --- a/src/e-k/Hurst/Hurst.Utilities.cs +++ b/src/e-k/Hurst/Hurst.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// HURST EXPONENT (UTILITIES) - +/// +/// Provides utility methods for Hurst Exponent calculations. +/// public static partial class Hurst { - // remove recommended periods + /// + /// Removes the recommended warmup periods from the Hurst Exponent results. + /// + /// The list of Hurst Exponent results. + /// A list of Hurst Exponent results with the warmup periods removed. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) @@ -16,7 +21,13 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } - // parameter validation + /// + /// Validates the lookback periods for Hurst Exponent calculations. + /// + /// The number of periods to look back for the calculation. + /// + /// Thrown when the lookback periods are less than 20. + /// internal static void Validate( int lookbackPeriods) { diff --git a/src/e-k/Hurst/info.xml b/src/e-k/Hurst/info.xml deleted file mode 100644 index 7f0578daf..000000000 --- a/src/e-k/Hurst/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Hurst Exponent is a measure of randomness, trending, and mean-reverting tendencies of incremental return values. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of lookback periods. - Time series of Hurst Exponent values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/e-k/Ichimoku/Ichimoku.Models.cs b/src/e-k/Ichimoku/Ichimoku.Models.cs index 512c5690d..9851210ef 100644 --- a/src/e-k/Ichimoku/Ichimoku.Models.cs +++ b/src/e-k/Ichimoku/Ichimoku.Models.cs @@ -1,12 +1,21 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of an Ichimoku Cloud calculation. +/// +/// The timestamp of the result. +/// The value of the Tenkan-sen (conversion line). +/// The value of the Kijun-sen (base line). +/// The value of the Senkou Span A (leading span A). +/// The value of the Senkou Span B (leading span B). +/// The value of the Chikou Span (lagging span). [Serializable] public record IchimokuResult ( - DateTime Timestamp, - decimal? TenkanSen, // conversion line - decimal? KijunSen, // base line - decimal? SenkouSpanA, // leading span A - decimal? SenkouSpanB, // leading span B - decimal? ChikouSpan // lagging span + DateTime Timestamp, + decimal? TenkanSen, // conversion line + decimal? KijunSen, // base line + decimal? SenkouSpanA, // leading span A + decimal? SenkouSpanB, // leading span B + decimal? ChikouSpan // lagging span ) : ISeries; diff --git a/src/e-k/Ichimoku/Ichimoku.StaticSeries.cs b/src/e-k/Ichimoku/Ichimoku.StaticSeries.cs index f370ff6a2..76d2f3689 100644 --- a/src/e-k/Ichimoku/Ichimoku.StaticSeries.cs +++ b/src/e-k/Ichimoku/Ichimoku.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// ICHIMOKU CLOUD (SERIES) - +/// +/// Provides methods for calculating the Ichimoku Cloud indicator. +/// public static partial class Ichimoku { + /// + /// Converts a list of quotes to Ichimoku Cloud results using default parameters. + /// + /// The type of the quotes, which must implement . + /// The list of quotes to transform. + /// The number of periods for the Tenkan-sen (conversion line). Default is 9. + /// The number of periods for the Kijun-sen (base line). Default is 26. + /// The number of periods for the Senkou Span B (leading span B). Default is 52. + /// A list of Ichimoku Cloud results. public static IReadOnlyList ToIchimoku( this IReadOnlyList quotes, int tenkanPeriods = 9, @@ -18,6 +28,16 @@ public static IReadOnlyList ToIchimoku( kijunPeriods, kijunPeriods); + /// + /// Converts a list of quotes to Ichimoku Cloud results with specified parameters. + /// + /// The type of the quotes, which must implement . + /// The list of quotes to transform. + /// The number of periods for the Tenkan-sen (conversion line). + /// The number of periods for the Kijun-sen (base line). + /// The number of periods for the Senkou Span B (leading span B). + /// The number of periods for the offset. + /// A list of Ichimoku Cloud results. public static IReadOnlyList GetIchimoku( this IReadOnlyList quotes, int tenkanPeriods, @@ -33,6 +53,18 @@ public static IReadOnlyList GetIchimoku( offsetPeriods, offsetPeriods); + + /// + /// Converts a list of quotes to Ichimoku Cloud results with specified parameters. + /// + /// The type of the quotes, which must implement . + /// The list of quotes to transform. + /// The number of periods for the Tenkan-sen (conversion line). + /// The number of periods for the Kijun-sen (base line). + /// The number of periods for the Senkou Span B (leading span B). + /// The number of periods for the Senkou offset. + /// The number of periods for the Chikou offset. + /// A list of Ichimoku Cloud results. public static IReadOnlyList GetIchimoku( this IReadOnlyList quotes, int tenkanPeriods, @@ -49,6 +81,17 @@ public static IReadOnlyList GetIchimoku( senkouOffset, chikouOffset); + /// + /// Calculates the Ichimoku Cloud indicator. + /// + /// The type of the quotes, which must implement . + /// The list of quotes to transform. + /// The number of periods for the Tenkan-sen (conversion line). + /// The number of periods for the Kijun-sen (base line). + /// The number of periods for the Senkou Span B (leading span B). + /// The number of periods for the Senkou offset. + /// The number of periods for the Chikou offset. + /// A list of Ichimoku Cloud results. private static List CalcIchimoku( this IReadOnlyList quotes, int tenkanPeriods, @@ -127,6 +170,14 @@ private static List CalcIchimoku( return results; } + /// + /// Calculates the Tenkan-sen (conversion line) for the Ichimoku Cloud indicator. + /// + /// The type of the quotes, which must implement . + /// The current index in the quotes list. + /// The list of quotes to transform. + /// The number of periods for the Tenkan-sen (conversion line). + /// The Tenkan-sen value. private static decimal? CalcIchimokuTenkanSen( int i, IReadOnlyList quotes, int tenkanPeriods) where TQuote : IQuote @@ -158,6 +209,14 @@ private static List CalcIchimoku( } + /// + /// Calculates the Kijun-sen (base line) for the Ichimoku Cloud indicator. + /// + /// The type of the quotes, which must implement . + /// The current index in the quotes list. + /// The list of quotes to transform. + /// The number of periods for the Kijun-sen (base line). + /// The Kijun-sen value. private static decimal? CalcIchimokuKijunSen( int i, IReadOnlyList quotes, @@ -190,6 +249,15 @@ private static List CalcIchimoku( return min == decimal.MaxValue ? null : (min + max) / 2; } + /// + /// Calculates the Senkou Span B (leading span B) for the Ichimoku Cloud indicator. + /// + /// The type of the quotes, which must implement . + /// The current index in the quotes list. + /// The list of quotes to transform. + /// The number of periods for the Senkou offset. + /// The number of periods for the Senkou Span B (leading span B). + /// The Senkou Span B value. private static decimal? CalcIchimokuSenkouB( int i, IReadOnlyList quotes, diff --git a/src/e-k/Ichimoku/Ichimoku.Utilities.cs b/src/e-k/Ichimoku/Ichimoku.Utilities.cs index 5a09101db..d22ae6003 100644 --- a/src/e-k/Ichimoku/Ichimoku.Utilities.cs +++ b/src/e-k/Ichimoku/Ichimoku.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// ICHIMOKU CLOUD (UTILITIES) - +/// +/// Provides utility methods for Ichimoku Cloud calculations. +/// public static partial class Ichimoku { - // remove null results - /// + /// + /// Removes empty (null) periods from the Ichimoku Cloud results. + /// + /// The list of Ichimoku Cloud results to condense. + /// A condensed list of Ichimoku Cloud results without null periods. public static IReadOnlyList Condense( this IReadOnlyList results) { @@ -23,7 +27,17 @@ x.TenkanSen is null return resultsList.ToSortedList(); } - // validate parameters + /// + /// Validates the parameters for the Ichimoku Cloud calculation. + /// + /// The number of periods for the Tenkan-sen (conversion line). + /// The number of periods for the Kijun-sen (base line). + /// The number of periods for the Senkou Span B (leading span B). + /// The number of periods for the Senkou offset. + /// The number of periods for the Chikou offset. + /// + /// Thrown when any of the parameters are out of their valid range. + /// internal static void Validate( int tenkanPeriods, int kijunPeriods, diff --git a/src/e-k/Ichimoku/info.xml b/src/e-k/Ichimoku/info.xml deleted file mode 100644 index af7d18ee1..000000000 --- a/src/e-k/Ichimoku/info.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - Ichimoku Cloud, also known as Ichimoku Kinkō Hyō, is a collection of indicators that depict support and resistance, momentum, and trend direction. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the Tenkan-sen midpoint evaluation. - Number of periods in the shorter Kijun-sen midpoint evaluation. This value is also used to offset Senkou and Chinkou spans. - Number of periods in the longer Senkou leading span B midpoint evaluation. - Time series of Ichimoku Cloud values. - Invalid parameter value provided. - - - - - Ichimoku Cloud, also known as Ichimoku Kinkō Hyō, is a collection of indicators that depict support and resistance, momentum, and trend direction. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the Tenkan-sen midpoint evaluation. - Number of periods in the shorter Kijun-sen midpoint evaluation. - Number of periods in the longer Senkou leading span B midpoint evaluation. - Number of periods to displace the Senkou and Chikou Spans. - Time series of Ichimoku Cloud values. - Invalid parameter value provided. - - - - - Ichimoku Cloud, also known as Ichimoku Kinkō Hyō, is a collection of indicators that depict support and resistance, momentum, and trend direction. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the Tenkan-sen midpoint evaluation. - Number of periods in the shorter Kijun-sen midpoint evaluation. - Number of periods in the longer Senkou leading span B midpoint evaluation. - Number of periods to displace the Senkou Spans. - Number of periods in displace the Chikou Span. - Time series of Ichimoku Cloud values. - Invalid parameter value provided. - - - \ No newline at end of file diff --git a/src/e-k/Kama/Kama.Models.cs b/src/e-k/Kama/Kama.Models.cs index 27b62844c..ffba9b5f7 100644 --- a/src/e-k/Kama/Kama.Models.cs +++ b/src/e-k/Kama/Kama.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the KAMA (Kaufman's Adaptive Moving Average) calculation. +/// +/// The timestamp of the result. +/// The Efficiency Ratio (ER) value. +/// The KAMA value. [Serializable] public record KamaResult ( @@ -8,5 +14,6 @@ public record KamaResult double? Kama = null ) : IReusable { + /// public double Value => Kama.Null2NaN(); } diff --git a/src/e-k/Kama/Kama.StaticSeries.cs b/src/e-k/Kama/Kama.StaticSeries.cs index b321d4cbf..8794879a7 100644 --- a/src/e-k/Kama/Kama.StaticSeries.cs +++ b/src/e-k/Kama/Kama.StaticSeries.cs @@ -1,9 +1,21 @@ namespace Skender.Stock.Indicators; -// KAUFMAN's ADAPTIVE MOVING AVERAGE (SERIES) - +/// +/// Provides methods for calculating the Kaufman's Adaptive Moving Average (KAMA) indicator. +/// public static partial class Kama { + /// + /// Converts a list of source values to KAMA (Kaufman's Adaptive Moving Average) results. + /// + /// The type of the source values, which must implement . + /// The list of source values to transform. + /// The number of periods for the Efficiency Ratio (ER). Default is 10. + /// The number of periods for the fast EMA. Default is 2. + /// The number of periods for the slow EMA. Default is 30. + /// A list of KAMA results. + /// Thrown when the source list is null. + /// Thrown when any of the parameters are out of their valid range. public static IReadOnlyList ToKama( this IReadOnlyList source, int erPeriods = 10, diff --git a/src/e-k/Kama/Kama.Utilities.cs b/src/e-k/Kama/Kama.Utilities.cs index 93af9a74a..5dec74412 100644 --- a/src/e-k/Kama/Kama.Utilities.cs +++ b/src/e-k/Kama/Kama.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// KAUFMAN's ADAPTIVE MOVING AVERAGE (UTILITIES) - +/// +/// Provides utility methods for Kaufman's Adaptive Moving Average (KAMA) calculations. +/// public static partial class Kama { - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the KAMA results. + /// + /// The list of KAMA results to process. + /// A list of KAMA results with the warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -16,7 +20,15 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(Math.Max(erPeriods + 100, 10 * erPeriods)); } - // parameter validation + /// + /// Validates the parameters for the KAMA calculation. + /// + /// The number of periods for the Efficiency Ratio (ER). + /// The number of periods for the fast EMA. + /// The number of periods for the slow EMA. + /// + /// Thrown when any of the parameters are out of their valid range. + /// internal static void Validate( int erPeriods, int fastPeriods, diff --git a/src/e-k/Kama/info.xml b/src/e-k/Kama/info.xml deleted file mode 100644 index 4e7b8e632..000000000 --- a/src/e-k/Kama/info.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Kaufman’s Adaptive Moving Average (KAMA) is an volatility adaptive moving average of price over configurable lookback periods. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of Efficiency Ratio (volatility) periods. - Number of periods in the Fast EMA. - Number of periods in the Slow EMA. - Time series of KAMA values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/e-k/Keltner/Keltner.Models.cs b/src/e-k/Keltner/Keltner.Models.cs index 7acc1a4b6..e7ef85b92 100644 --- a/src/e-k/Keltner/Keltner.Models.cs +++ b/src/e-k/Keltner/Keltner.Models.cs @@ -1,5 +1,13 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the Keltner Channel calculation. +/// +/// The timestamp of the result. +/// The upper band value of the Keltner Channel. +/// The centerline value of the Keltner Channel. +/// The lower band value of the Keltner Channel. +/// The width of the Keltner Channel. [Serializable] public record KeltnerResult ( diff --git a/src/e-k/Keltner/Keltner.StaticSeries.cs b/src/e-k/Keltner/Keltner.StaticSeries.cs index baa1ba676..9c617636d 100644 --- a/src/e-k/Keltner/Keltner.StaticSeries.cs +++ b/src/e-k/Keltner/Keltner.StaticSeries.cs @@ -1,9 +1,20 @@ namespace Skender.Stock.Indicators; -// KELTNER CHANNELS (SERIES) - +/// +/// Provides methods for calculating the Keltner Channels for a series of quotes. +/// public static partial class Keltner { + /// + /// Converts a list of quotes to Keltner Channel results. + /// + /// The type of the quotes, which must implement . + /// The list of quotes to transform. + /// The number of periods for the EMA. Default is 20. + /// The multiplier for the ATR. Default is 2. + /// The number of periods for the ATR. Default is 10. + /// A list of Keltner Channel results. + /// Thrown when any of the parameters are out of their valid range. public static IReadOnlyList ToKeltner( this IReadOnlyList quotes, int emaPeriods = 20, @@ -13,6 +24,14 @@ public static IReadOnlyList ToKeltner( .ToQuoteDList() .CalcKeltner(emaPeriods, multiplier, atrPeriods); + /// + /// Calculates the Keltner Channel for a list of quotes. + /// + /// The list of quotes to process. + /// The number of periods for the EMA. + /// The multiplier for the ATR. + /// The number of periods for the ATR. + /// A list of Keltner Channel results. private static List CalcKeltner( this IReadOnlyList source, int emaPeriods, diff --git a/src/e-k/Keltner/Keltner.Utilities.cs b/src/e-k/Keltner/Keltner.Utilities.cs index 992bf4f5e..03426d82f 100644 --- a/src/e-k/Keltner/Keltner.Utilities.cs +++ b/src/e-k/Keltner/Keltner.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// KELTNER CHANNELS (UTILITIES) - +/// +/// Provides utility methods for the Keltner Channel indicator. +/// public static partial class Keltner { - // CONDENSE (REMOVE null results) - /// + /// + /// Removes empty (null) periods from the Keltner Channel results. + /// + /// The list of Keltner Channel results to condense. + /// A condensed list of Keltner Channel results without null periods. public static IReadOnlyList Condense( this IReadOnlyList results) { @@ -19,8 +23,11 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the Keltner Channel results. + /// + /// The list of Keltner Channel results to process. + /// A list of Keltner Channel results with the warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -31,7 +38,15 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(Math.Max(2 * n, n + 100)); } - // parameter validation + /// + /// Validates the parameters for the Keltner Channel calculation. + /// + /// The number of periods for the EMA. + /// The multiplier for the ATR. + /// The number of periods for the ATR. + /// + /// Thrown when any of the parameters are out of their valid range. + /// internal static void Validate( int emaPeriods, double multiplier, diff --git a/src/e-k/Keltner/info.xml b/src/e-k/Keltner/info.xml deleted file mode 100644 index 3ae58ddef..000000000 --- a/src/e-k/Keltner/info.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Keltner Channels are based on an EMA centerline and ATR band widths. See also STARC Bands for an SMA centerline equivalent. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for the centerline EMA. - ATR multiplier sets the width of the channel. - Number of periods in the ATR evaluation. - Time series of Keltner Channel values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/e-k/Kvo/Kvo.Models.cs b/src/e-k/Kvo/Kvo.Models.cs index 9588ad1c8..d4556f6f1 100644 --- a/src/e-k/Kvo/Kvo.Models.cs +++ b/src/e-k/Kvo/Kvo.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of the KVO (Klinger Volume Oscillator) calculation. +/// +/// The timestamp of the result. +/// The oscillator value of the KVO. +/// The signal value of the KVO. [Serializable] public record KvoResult ( @@ -8,5 +14,6 @@ public record KvoResult double? Signal = null ) : IReusable { + /// public double Value => Oscillator.Null2NaN(); } diff --git a/src/e-k/Kvo/Kvo.StaticSeries.cs b/src/e-k/Kvo/Kvo.StaticSeries.cs index 1b735fe2d..e50ab3b37 100644 --- a/src/e-k/Kvo/Kvo.StaticSeries.cs +++ b/src/e-k/Kvo/Kvo.StaticSeries.cs @@ -1,9 +1,20 @@ namespace Skender.Stock.Indicators; -// KLINGER VOLUME OSCILLATOR (SERIES) - +/// +/// Provides methods for calculating the Klinger Volume Oscillator (KVO) for a series of quotes. +/// public static partial class Kvo { + /// + /// Converts a list of quotes to KVO (Klinger Volume Oscillator) results. + /// + /// The type of the quotes, which must implement . + /// The list of quotes to transform. + /// The number of periods for the fast EMA. Default is 34. + /// The number of periods for the slow EMA. Default is 55. + /// The number of periods for the signal line. Default is 13. + /// A list of KVO results. + /// Thrown when any of the parameters are out of their valid range. public static IReadOnlyList ToKvo( this IReadOnlyList quotes, int fastPeriods = 34, @@ -13,6 +24,14 @@ public static IReadOnlyList ToKvo( .ToQuoteDList() .CalcKvo(fastPeriods, slowPeriods, signalPeriods); + /// + /// Calculates the KVO (Klinger Volume Oscillator) for a list of quotes. + /// + /// The list of quotes to process. + /// The number of periods for the fast EMA. + /// The number of periods for the slow EMA. + /// The number of periods for the signal line. + /// A list of KVO results. private static List CalcKvo( this IReadOnlyList source, int fastPeriods, diff --git a/src/e-k/Kvo/Kvo.Utilities.cs b/src/e-k/Kvo/Kvo.Utilities.cs index 022893924..5000ef542 100644 --- a/src/e-k/Kvo/Kvo.Utilities.cs +++ b/src/e-k/Kvo/Kvo.Utilities.cs @@ -4,8 +4,11 @@ namespace Skender.Stock.Indicators; public static partial class Kvo { - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the KVO (Klinger Volume Oscillator) results. + /// + /// The list of KVO results to process. + /// A list of KVO results with the warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -16,7 +19,15 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(l + 150); } - // parameter validation + /// + /// Validates the parameters for the KVO (Klinger Volume Oscillator) calculation. + /// + /// The number of periods for the fast EMA. + /// The number of periods for the slow EMA. + /// The number of periods for the signal line. + /// + /// Thrown when any of the parameters are out of their valid range. + /// internal static void Validate( int fastPeriods, int slowPeriods, diff --git a/src/e-k/Kvo/info.xml b/src/e-k/Kvo/info.xml deleted file mode 100644 index 8f10566e3..000000000 --- a/src/e-k/Kvo/info.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Klinger Oscillator depicts volume-based divergence between short and long-term money flow. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for the short EMA. - Number of periods for the long EMA. - Number of periods Signal line. - Time series of Klinger Oscillator values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/m-r/MaEnvelopes/MaEnvelopes.Models.cs b/src/m-r/MaEnvelopes/MaEnvelopes.Models.cs index c485dfe50..e6e03dd01 100644 --- a/src/m-r/MaEnvelopes/MaEnvelopes.Models.cs +++ b/src/m-r/MaEnvelopes/MaEnvelopes.Models.cs @@ -1,5 +1,12 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Moving Average Envelope calculation. +/// +/// The timestamp of the result. +/// The value of the centerline (moving average). +/// The value of the upper envelope. +/// The value of the lower envelope. [Serializable] public record MaEnvelopeResult ( diff --git a/src/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.cs b/src/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.cs index 52942bbbd..3c068db21 100644 --- a/src/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.cs +++ b/src/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.cs @@ -2,11 +2,21 @@ namespace Skender.Stock.Indicators; -// MOVING AVERAGE ENVELOPES (SERIES) - +/// +/// Provides methods for calculating Moving Average Envelopes for a series of quotes. +/// public static partial class MaEnvelopes { - // calculate series + /// + /// Converts a list of source values to Moving Average Envelope results. + /// + /// The type of the source values, which must implement . + /// The list of source values to transform. + /// The number of periods for the moving average. + /// The percentage offset for the envelopes. Default is 2.5. + /// The type of moving average to use. Default is SMA. + /// A list of Moving Average Envelope results. + /// Thrown when the moving average type is not supported. public static IReadOnlyList ToMaEnvelopes( this IReadOnlyList source, int lookbackPeriods, diff --git a/src/m-r/MaEnvelopes/MaEnvelopes.Utilities.cs b/src/m-r/MaEnvelopes/MaEnvelopes.Utilities.cs index 07b314c46..f7caed6cb 100644 --- a/src/m-r/MaEnvelopes/MaEnvelopes.Utilities.cs +++ b/src/m-r/MaEnvelopes/MaEnvelopes.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// MOVING AVERAGE ENVELOPES (UTILITIES) - +/// +/// Provides utility methods for the Moving Average Envelopes. +/// public static partial class MaEnvelopes { - // CONDENSE (REMOVE null results) - /// + /// + /// Removes empty (null) periods from the Moving Average Envelope results. + /// + /// The list of Moving Average Envelope results. + /// A list of Moving Average Envelope results with empty periods removed. public static IReadOnlyList Condense( this IReadOnlyList results) { @@ -19,7 +23,11 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } - // parameter validation + /// + /// Validates the parameters for the Moving Average Envelopes calculation. + /// + /// The percentage offset for the envelopes. + /// Thrown when the percent offset is less than or equal to 0. internal static void Validate( double percentOffset) { diff --git a/src/m-r/MaEnvelopes/info.xml b/src/m-r/MaEnvelopes/info.xml deleted file mode 100644 index 6b5df254e..000000000 --- a/src/m-r/MaEnvelopes/info.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Moving Average Envelopes is a price band overlay that is offset from the moving average of price over a lookback window. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Percent offset for envelope width. - Moving average type (e.g. EMA, HMA, TEMA, etc.). - Time series of MA Envelopes values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/m-r/Macd/Macd.Models.cs b/src/m-r/Macd/Macd.Models.cs index 5af8fb0df..e0cf88d8d 100644 --- a/src/m-r/Macd/Macd.Models.cs +++ b/src/m-r/Macd/Macd.Models.cs @@ -1,5 +1,14 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a MACD (Moving Average Convergence Divergence) calculation. +/// +/// The timestamp of the MACD result. +/// The MACD value. +/// The signal line value. +/// The histogram value. +/// The fast EMA value. +/// The slow EMA value. [Serializable] public record MacdResult ( @@ -14,5 +23,6 @@ public record MacdResult ) : IReusable { + /// public double Value => Macd.Null2NaN(); } diff --git a/src/m-r/Macd/Macd.StaticSeries.cs b/src/m-r/Macd/Macd.StaticSeries.cs index 45c4628d6..7c824cdd2 100644 --- a/src/m-r/Macd/Macd.StaticSeries.cs +++ b/src/m-r/Macd/Macd.StaticSeries.cs @@ -1,9 +1,21 @@ namespace Skender.Stock.Indicators; -// MOVING AVERAGE CONVERGENCE/DIVERGENCE (MACD) OSCILLATOR (SERIES) - +/// +/// Provides methods for calculating the MACD (Moving Average Convergence Divergence) indicator. +/// public static partial class Macd { + /// + /// Converts a list of source values to MACD (Moving Average Convergence Divergence) results. + /// + /// The type of the source values, which must implement . + /// The list of source values to transform. + /// The number of periods for the fast EMA. Default is 12. + /// The number of periods for the slow EMA. Default is 26. + /// The number of periods for the signal line. Default is 9. + /// A list of MACD results. + /// Thrown when the source list is null. + /// Thrown when any of the parameters are out of their valid range. public static IReadOnlyList ToMacd( this IReadOnlyList source, int fastPeriods = 12, diff --git a/src/m-r/Macd/Macd.Utilities.cs b/src/m-r/Macd/Macd.Utilities.cs index 4326aac2d..11791dcbb 100644 --- a/src/m-r/Macd/Macd.Utilities.cs +++ b/src/m-r/Macd/Macd.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// MOVING AVERAGE CONVERGENCE/DIVERGENCE (MACD) OSCILLATOR (UTILITIES) - +/// +/// Provides utility methods for the MACD (Moving Average Convergence Divergence) oscillator. +/// public static partial class Macd { - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the MACD results. + /// + /// The list of MACD results. + /// A list of MACD results with the warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -16,7 +20,15 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 250); } - // parameter validation + /// + /// Validates the parameters for the MACD calculation. + /// + /// The number of periods for the fast EMA. + /// The number of periods for the slow EMA. + /// The number of periods for the signal line. + /// + /// Thrown when any of the parameters are out of their valid range. + /// internal static void Validate( int fastPeriods, int slowPeriods, diff --git a/src/m-r/Macd/info.xml b/src/m-r/Macd/info.xml deleted file mode 100644 index 9b651d73f..000000000 --- a/src/m-r/Macd/info.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Moving Average Convergence/Divergence (MACD) is a simple oscillator view of two converging/diverging exponential moving averages. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the Fast EMA. - Number of periods in the Slow EMA. - Number of periods for the Signal moving average. - Time series of MACD values, including MACD, Signal, and Histogram. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/m-r/Mama/Mama.Models.cs b/src/m-r/Mama/Mama.Models.cs index 4a3f0f949..80a26e85f 100644 --- a/src/m-r/Mama/Mama.Models.cs +++ b/src/m-r/Mama/Mama.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a MAMA (MESA Adaptive Moving Average) calculation. +/// +/// The timestamp of the result. +/// The value of the MAMA. +/// The value of the FAMA. [Serializable] public record MamaResult ( @@ -8,5 +14,6 @@ public record MamaResult double? Fama = null ) : IReusable { + /// public double Value => Mama.Null2NaN(); } diff --git a/src/m-r/Mama/Mama.StaticSeries.cs b/src/m-r/Mama/Mama.StaticSeries.cs index 3110c5702..54378bedc 100644 --- a/src/m-r/Mama/Mama.StaticSeries.cs +++ b/src/m-r/Mama/Mama.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// MOTHER of ADAPTIVE MOVING AVERAGES (SERIES) - +/// +/// Provides methods for calculating the MESA Adaptive Moving Average (MAMA) for a series of quotes. +/// public static partial class Mama { + /// + /// Converts a list of source values to MESA Adaptive Moving Average (MAMA) results. + /// + /// The type of the source values, which must implement . + /// The list of source values to transform. + /// The fast limit for the MAMA calculation. Default is 0.5. + /// The slow limit for the MAMA calculation. Default is 0.05. + /// A list of MESA Adaptive Moving Average (MAMA) results. + /// Thrown when the fast or slow limit is out of range. public static IReadOnlyList ToMama( this IReadOnlyList source, double fastLimit = 0.5, diff --git a/src/m-r/Mama/Mama.Utilities.cs b/src/m-r/Mama/Mama.Utilities.cs index 2b9a21126..717229328 100644 --- a/src/m-r/Mama/Mama.Utilities.cs +++ b/src/m-r/Mama/Mama.Utilities.cs @@ -1,15 +1,28 @@ namespace Skender.Stock.Indicators; -// MOTHER of ADAPTIVE MOVING AVERAGES (UTILITIES) - +/// +/// Provides utility methods for the MESA Adaptive Moving Average (MAMA). +/// public static partial class Mama { - // remove recommended periods + /// + /// Removes the recommended warmup periods from the MAMA results. + /// + /// The list of MAMA results. + /// A list of MAMA results with the warmup periods removed. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) => results.Remove(50); - // parameter validation + /// + /// Validates the parameters for the MAMA calculation. + /// + /// The fast limit for the MAMA calculation. + /// The slow limit for the MAMA calculation. + /// + /// Thrown when the fast limit is less than or equal to the slow limit, or greater than or equal to 1, + /// or when the slow limit is less than or equal to 0. + /// internal static void Validate( double fastLimit, double slowLimit) diff --git a/src/m-r/Mama/info.xml b/src/m-r/Mama/info.xml deleted file mode 100644 index 35a306b7d..000000000 --- a/src/m-r/Mama/info.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - MESA Adaptive Moving Average (MAMA) is a 5-period adaptive moving average of high/low price. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Fast limit threshold. - Slow limit threshold. - Time series of MAMA values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/m-r/Marubozu/Marubozu.StaticSeries.cs b/src/m-r/Marubozu/Marubozu.StaticSeries.cs index 6cf34e122..95c5ad857 100644 --- a/src/m-r/Marubozu/Marubozu.StaticSeries.cs +++ b/src/m-r/Marubozu/Marubozu.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// MARUBOZU (SERIES) - +/// +/// Provides methods for identifying Marubozu candlestick patterns in a series of quotes. +/// public static partial class Marubozu { + /// + /// Converts a list of quotes to Marubozu candlestick pattern results. + /// + /// The type of the quotes, which must implement . + /// The list of quotes to analyze. + /// The minimum body percentage to qualify as a Marubozu. Default is 95. + /// A list of indicating the presence of Marubozu patterns. + /// Thrown when the quotes list is null. + /// Thrown when the minimum body percentage is out of range. public static IReadOnlyList ToMarubozu( this IReadOnlyList quotes, double minBodyPercent = 95) diff --git a/src/m-r/Marubozu/Marubozu.Utilities.cs b/src/m-r/Marubozu/Marubozu.Utilities.cs index 7d859c14c..01497aeb0 100644 --- a/src/m-r/Marubozu/Marubozu.Utilities.cs +++ b/src/m-r/Marubozu/Marubozu.Utilities.cs @@ -1,10 +1,17 @@ namespace Skender.Stock.Indicators; -// MARUBOZU (UTILITIES) - +/// +/// Provides utility methods for the Marubozu candlestick pattern. +/// public static partial class Marubozu { - // parameter validation + /// + /// Validates the parameters for the Marubozu calculation. + /// + /// The minimum body percentage to qualify as a Marubozu. + /// + /// Thrown when the minimum body percentage is greater than 100 or less than 80. + /// internal static void Validate( double minBodyPercent) { diff --git a/src/m-r/Marubozu/info.xml b/src/m-r/Marubozu/info.xml deleted file mode 100644 index 5a7e7a20c..000000000 --- a/src/m-r/Marubozu/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Marubozu is a single candlestick pattern that has no wicks, representing consistent directional movement. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Optional. Minimum candle body size as percentage. - Time series of Marubozu values. - Invalid parameter value provided. - diff --git a/src/m-r/Mfi/Mfi.Models.cs b/src/m-r/Mfi/Mfi.Models.cs index 0a0c8f29b..6091a0850 100644 --- a/src/m-r/Mfi/Mfi.Models.cs +++ b/src/m-r/Mfi/Mfi.Models.cs @@ -1,11 +1,17 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Money Flow Index (MFI) calculation. +/// +/// The timestamp of the result. +/// The value of the Money Flow Index (MFI). [Serializable] public record MfiResult ( - DateTime Timestamp, - double? Mfi + DateTime Timestamp, + double? Mfi ) : IReusable { + /// public double Value => Mfi.Null2NaN(); } diff --git a/src/m-r/Mfi/Mfi.StaticSeries.cs b/src/m-r/Mfi/Mfi.StaticSeries.cs index 2d9043a7c..cb52a3dd6 100644 --- a/src/m-r/Mfi/Mfi.StaticSeries.cs +++ b/src/m-r/Mfi/Mfi.StaticSeries.cs @@ -1,16 +1,32 @@ namespace Skender.Stock.Indicators; -// MONEY FLOW INDEX (SERIES) - +/// +/// Provides methods for calculating the Money Flow Index (MFI) for a series of quotes. +/// public static partial class Mfi { + /// + /// Converts a list of quotes to Money Flow Index (MFI) results. + /// + /// The type of the quotes, which must implement . + /// The list of quotes to analyze. + /// The number of periods to use for the MFI calculation. Default is 14. + /// A list of containing the MFI values. + /// Thrown when the quotes list is null. + /// Thrown when the lookback periods are out of range. public static IReadOnlyList ToMfi( - this IReadOnlyList quotes, - int lookbackPeriods = 14) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcMfi(lookbackPeriods); - + this IReadOnlyList quotes, + int lookbackPeriods = 14) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcMfi(lookbackPeriods); + + /// + /// Calculates the Money Flow Index (MFI) for a list of quotes. + /// + /// The list of quotes to analyze. + /// The number of periods to use for the MFI calculation. + /// A list of containing the MFI values. private static List CalcMfi( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/m-r/Mfi/Mfi.Utilities.cs b/src/m-r/Mfi/Mfi.Utilities.cs index e4768b450..2e769a231 100644 --- a/src/m-r/Mfi/Mfi.Utilities.cs +++ b/src/m-r/Mfi/Mfi.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// MONEY FLOW INDEX (UTILITIES) - +/// +/// Provides utility methods for the Money Flow Index (MFI). +/// public static partial class Mfi { - // remove recommended periods + /// + /// Removes the recommended warmup periods from the MFI results. + /// + /// The list of MFI results. + /// A list of MFI results with the warmup periods removed. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) @@ -16,7 +21,13 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } - // parameter validation + /// + /// Validates the parameters for the MFI calculation. + /// + /// The number of periods to use for the MFI calculation. + /// + /// Thrown when the lookback periods are less than or equal to 1. + /// internal static void Validate( int lookbackPeriods) { diff --git a/src/m-r/Mfi/info.xml b/src/m-r/Mfi/info.xml deleted file mode 100644 index 5c26f9d14..000000000 --- a/src/m-r/Mfi/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Money Flow Index (MFI) is a price-volume oscillator that shows buying and selling momentum. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of MFI values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/m-r/Obv/Obv.Models.cs b/src/m-r/Obv/Obv.Models.cs index 458caa43a..337574945 100644 --- a/src/m-r/Obv/Obv.Models.cs +++ b/src/m-r/Obv/Obv.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of an On-Balance Volume (OBV) calculation. +/// +/// The timestamp of the result. +/// The value of the On-Balance Volume (OBV). [Serializable] public record ObvResult ( @@ -7,5 +12,6 @@ public record ObvResult double Obv ) : IReusable { + /// public double Value => Obv; } diff --git a/src/m-r/Obv/Obv.StaticSeries.cs b/src/m-r/Obv/Obv.StaticSeries.cs index 48456b11d..0c68de5c4 100644 --- a/src/m-r/Obv/Obv.StaticSeries.cs +++ b/src/m-r/Obv/Obv.StaticSeries.cs @@ -1,15 +1,27 @@ namespace Skender.Stock.Indicators; -// ON-BALANCE VOLUME (SERIES) - +/// +/// Provides methods for calculating the On-Balance Volume (OBV) indicator. +/// public static partial class Obv { + /// + /// Converts a list of quotes to OBV results. + /// + /// The type of the quote. + /// The list of quotes. + /// A list of OBV results. public static IReadOnlyList ToObv( this IReadOnlyList quotes) where TQuote : IQuote => quotes .ToQuoteDList() .CalcObv(); + /// + /// Calculates the OBV for a list of quotes. + /// + /// The list of quotes. + /// A list of OBV results. private static List CalcObv( this IReadOnlyList source) { diff --git a/src/m-r/Obv/info.xml b/src/m-r/Obv/info.xml deleted file mode 100644 index 378ee0b7f..000000000 --- a/src/m-r/Obv/info.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - On-balance Volume (OBV) is a rolling accumulation of volume based on Close price direction. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Time series of OBV values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/m-r/ParabolicSar/ParabolicSar.Models.cs b/src/m-r/ParabolicSar/ParabolicSar.Models.cs index 8fad331a8..63407cdae 100644 --- a/src/m-r/ParabolicSar/ParabolicSar.Models.cs +++ b/src/m-r/ParabolicSar/ParabolicSar.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Parabolic SAR calculation. +/// +/// The timestamp of the result. +/// The value of the Parabolic SAR. +/// Indicates whether a reversal has occurred. [Serializable] public record ParabolicSarResult ( @@ -8,5 +14,6 @@ public record ParabolicSarResult bool? IsReversal = null ) : IReusable { + /// public double Value => Sar.Null2NaN(); } diff --git a/src/m-r/ParabolicSar/ParabolicSar.StaticSeries.cs b/src/m-r/ParabolicSar/ParabolicSar.StaticSeries.cs index 53eca2d91..d6d1c465f 100644 --- a/src/m-r/ParabolicSar/ParabolicSar.StaticSeries.cs +++ b/src/m-r/ParabolicSar/ParabolicSar.StaticSeries.cs @@ -1,20 +1,42 @@ namespace Skender.Stock.Indicators; -// PARABOLIC SAR (SERIES) - +/// +/// Provides methods for calculating the Parabolic SAR for a series of quotes. +/// public static partial class ParabolicSar { + /// + /// Converts a list of quotes to Parabolic SAR results. + /// + /// The type of the quotes, which must implement . + /// The list of quotes to analyze. + /// The acceleration step for the SAR calculation. Default is 0.02. + /// The maximum acceleration factor for the SAR calculation. Default is 0.2. + /// A list of containing the SAR values. + /// Thrown when the quotes list is null. + /// Thrown when the acceleration step or maximum acceleration factor are out of range. public static IReadOnlyList ToParabolicSar( - this IReadOnlyList quotes, - double accelerationStep = 0.02, - double maxAccelerationFactor = 0.2) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcParabolicSar( - accelerationStep, - maxAccelerationFactor, - accelerationStep); - + this IReadOnlyList quotes, + double accelerationStep = 0.02, + double maxAccelerationFactor = 0.2) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcParabolicSar( + accelerationStep, + maxAccelerationFactor, + accelerationStep); + + /// + /// Gets the Parabolic SAR results for a list of quotes. + /// + /// The type of the quotes, which must implement . + /// The list of quotes to analyze. + /// The acceleration step for the SAR calculation. + /// The maximum acceleration factor for the SAR calculation. + /// The initial acceleration factor for the SAR calculation. + /// A list of containing the SAR values. + /// Thrown when the quotes list is null. + /// Thrown when the acceleration step, maximum acceleration factor, or initial factor are out of range. public static IReadOnlyList GetParabolicSar( this IReadOnlyList quotes, double accelerationStep, @@ -27,6 +49,14 @@ public static IReadOnlyList GetParabolicSar( maxAccelerationFactor, initialFactor); + /// + /// Calculates the Parabolic SAR for a list of quotes. + /// + /// The list of quotes to analyze. + /// The acceleration step for the SAR calculation. + /// The maximum acceleration factor for the SAR calculation. + /// The initial acceleration factor for the SAR calculation. + /// A list of containing the SAR values. private static List CalcParabolicSar( this IReadOnlyList source, double accelerationStep, diff --git a/src/m-r/ParabolicSar/ParabolicSar.Utilities.cs b/src/m-r/ParabolicSar/ParabolicSar.Utilities.cs index afa732021..c79bd27d6 100644 --- a/src/m-r/ParabolicSar/ParabolicSar.Utilities.cs +++ b/src/m-r/ParabolicSar/ParabolicSar.Utilities.cs @@ -2,13 +2,22 @@ namespace Skender.Stock.Indicators; -// PARABOLIC SAR (UTILITIES) - +/// +/// Provides utility methods for the Parabolic SAR. +/// public static partial class ParabolicSar { private static readonly CultureInfo invariantCulture = CultureInfo.InvariantCulture; - // parameter validation + /// + /// Validates the parameters for the Parabolic SAR calculation. + /// + /// The acceleration step for the SAR calculation. + /// The maximum acceleration factor for the SAR calculation. + /// The initial acceleration factor for the SAR calculation. + /// + /// Thrown when the acceleration step, maximum acceleration factor, or initial factor are out of range. + /// internal static void Validate( double accelerationStep, double maxAccelerationFactor, diff --git a/src/m-r/ParabolicSar/info.xml b/src/m-r/ParabolicSar/info.xml deleted file mode 100644 index d1971b4dc..000000000 --- a/src/m-r/ParabolicSar/info.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - Parabolic SAR (stop and reverse) is a price-time based indicator used to determine trend direction and reversals. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Incremental step size. - Maximum step threshold. - Time series of Parabolic SAR values. - Invalid parameter value provided. - - - - Parabolic SAR (stop and reverse) is a price-time based indicator used to determine trend direction and reversals. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Incremental step size. - Maximum step threshold. - Initial starting acceleration factor. - Time series of Parabolic SAR values. - Invalid parameter value provided. - - \ No newline at end of file diff --git a/src/m-r/PivotPoints/PivotPoints.Models.cs b/src/m-r/PivotPoints/PivotPoints.Models.cs index f14c31cdc..4a067120c 100644 --- a/src/m-r/PivotPoints/PivotPoints.Models.cs +++ b/src/m-r/PivotPoints/PivotPoints.Models.cs @@ -1,60 +1,147 @@ namespace Skender.Stock.Indicators; +/// +/// Interface representing pivot points. +/// internal interface IPivotPoint { + /// + /// Gets the fourth resistance level. + /// decimal? R4 { get; } + + /// + /// Gets the third resistance level. + /// decimal? R3 { get; } + + /// + /// Gets the second resistance level. + /// decimal? R2 { get; } + + /// + /// Gets the first resistance level. + /// decimal? R1 { get; } + + /// + /// Gets the pivot point. + /// decimal? PP { get; } + + /// + /// Gets the first support level. + /// decimal? S1 { get; } + + /// + /// Gets the second support level. + /// decimal? S2 { get; } + + /// + /// Gets the third support level. + /// decimal? S3 { get; } + + /// + /// Gets the fourth support level. + /// decimal? S4 { get; } } +/// +/// Represents the result of pivot points calculation. +/// [Serializable] -public record PivotPointsResult : ISeries, IPivotPoint +public record PivotPointsResult : IPivotPoint, ISeries { + /// + /// Gets the timestamp of the result. + /// public DateTime Timestamp { get; init; } + /// public decimal? PP { get; init; } + /// public decimal? S1 { get; init; } + /// public decimal? S2 { get; init; } + /// public decimal? S3 { get; init; } + /// public decimal? S4 { get; init; } + /// public decimal? R1 { get; init; } + /// public decimal? R2 { get; init; } + /// public decimal? R3 { get; init; } + /// public decimal? R4 { get; init; } } +/// +/// Represents a window point for pivot points calculation. +/// internal record WindowPoint : IPivotPoint { + /// public decimal? PP { get; init; } + /// public decimal? S1 { get; init; } + /// public decimal? S2 { get; init; } + /// public decimal? S3 { get; init; } + /// public decimal? S4 { get; init; } + /// public decimal? R1 { get; init; } + /// public decimal? R2 { get; init; } + /// public decimal? R3 { get; init; } + /// public decimal? R4 { get; init; } } +/// +/// Enum representing different types of pivot points. +/// public enum PivotPointType { // do not modify numbers, // just add new random numbers if extending + /// + /// Standard pivot points. + /// Standard = 0, + + /// + /// Camarilla pivot points. + /// Camarilla = 1, + + /// + /// Demark pivot points. + /// Demark = 2, + + /// + /// Fibonacci pivot points. + /// Fibonacci = 3, + + /// + /// Woodie pivot points. + /// Woodie = 4 } diff --git a/src/m-r/PivotPoints/PivotPoints.StaticSeries.cs b/src/m-r/PivotPoints/PivotPoints.StaticSeries.cs index 7b540b12f..04b2021e9 100644 --- a/src/m-r/PivotPoints/PivotPoints.StaticSeries.cs +++ b/src/m-r/PivotPoints/PivotPoints.StaticSeries.cs @@ -1,9 +1,18 @@ namespace Skender.Stock.Indicators; -// PIVOT POINTS (SERIES) - +/// +/// Provides methods for calculating pivot points from a series of quotes. +/// public static partial class PivotPoints { + /// + /// Converts a series of quotes to pivot points. + /// + /// The type of the quote. + /// The series of quotes. + /// The size of the window for pivot point calculation. + /// The type of pivot point calculation to use. + /// A list of pivot point results. public static IReadOnlyList ToPivotPoints( this IReadOnlyList quotes, PeriodSize windowSize, @@ -99,7 +108,15 @@ PivotPointsResult r return results; } - // pivot point lookup + /// + /// Gets the pivot point based on the specified type and price values. + /// + /// The type of pivot point calculation to use. + /// The opening price. + /// The highest price. + /// The lowest price. + /// The closing price. + /// A WindowPoint object containing the calculated pivot points. internal static WindowPoint GetPivotPoint( PivotPointType pointType, decimal open, decimal high, decimal low, decimal close) => pointType switch { @@ -114,7 +131,12 @@ internal static WindowPoint GetPivotPoint( nameof(pointType), pointType, "Invalid pointType provided.") }; - // window size lookup + /// + /// Gets the window number based on the date and window size. + /// + /// The date. + /// The size of the window. + /// The window number. private static int GetWindowNumber(DateTime d, PeriodSize windowSize) => windowSize switch { @@ -135,7 +157,13 @@ private static int GetWindowNumber(DateTime d, PeriodSize windowSize) Enum.GetName(typeof(PeriodSize), windowSize))) }; - // pivot point variants + /// + /// Gets the standard pivot point based on the high, low, and close prices. + /// + /// The highest price. + /// The lowest price. + /// The closing price. + /// A WindowPoint object containing the calculated pivot points. private static WindowPoint GetPivotPointStandard( decimal high, decimal low, decimal close) { @@ -152,6 +180,13 @@ private static WindowPoint GetPivotPointStandard( }; } + /// + /// Gets the Camarilla pivot point based on the high, low, and close prices. + /// + /// The highest price. + /// The lowest price. + /// The closing price. + /// A WindowPoint object containing the calculated pivot points. private static WindowPoint GetPivotPointCamarilla( decimal high, decimal low, decimal close) => new() { @@ -166,6 +201,14 @@ private static WindowPoint GetPivotPointCamarilla( R4 = close + (1.1m / 2 * (high - low)) }; + /// + /// Gets the Demark pivot point based on the open, high, low, and close prices. + /// + /// The opening price. + /// The highest price. + /// The lowest price. + /// The closing price. + /// A WindowPoint object containing the calculated pivot points. internal static WindowPoint GetPivotPointDemark( decimal open, decimal high, decimal low, decimal close) { @@ -182,6 +225,13 @@ internal static WindowPoint GetPivotPointDemark( }; } + /// + /// Gets the Fibonacci pivot point based on the high, low, and close prices. + /// + /// The highest price. + /// The lowest price. + /// The closing price. + /// A WindowPoint object containing the calculated pivot points. private static WindowPoint GetPivotPointFibonacci( decimal high, decimal low, decimal close) { @@ -198,6 +248,13 @@ private static WindowPoint GetPivotPointFibonacci( }; } + /// + /// Gets the Woodie pivot point based on the current open, high, and low prices. + /// + /// The current opening price. + /// The highest price. + /// The lowest price. + /// A WindowPoint object containing the calculated pivot points. private static WindowPoint GetPivotPointWoodie( decimal currentOpen, decimal high, decimal low) { diff --git a/src/m-r/PivotPoints/PivotPoints.Utilities.cs b/src/m-r/PivotPoints/PivotPoints.Utilities.cs index c562b0da9..c2f69043d 100644 --- a/src/m-r/PivotPoints/PivotPoints.Utilities.cs +++ b/src/m-r/PivotPoints/PivotPoints.Utilities.cs @@ -2,23 +2,28 @@ namespace Skender.Stock.Indicators; -// PIVOT POINTS (UTILITIES) - +/// +/// Provides utility methods for pivot points calculations. +/// public static partial class PivotPoints { - private static readonly CultureInfo invariantCulture = CultureInfo.InvariantCulture; + private static readonly CultureInfo invariantCulture + = CultureInfo.InvariantCulture; - private static readonly Calendar calendar = invariantCulture.Calendar; + private static readonly Calendar calendar + = invariantCulture.Calendar; - // Gets the DTFI properties required by GetWeekOfYear. private static readonly CalendarWeekRule calendarWeekRule = invariantCulture.DateTimeFormat.CalendarWeekRule; private static readonly DayOfWeek firstDayOfWeek = invariantCulture.DateTimeFormat.FirstDayOfWeek; - // remove recommended periods - /// + /// + /// Removes the warmup periods from the pivot points results. + /// + /// The list of pivot points results. + /// A list of pivot points results without the warmup periods. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { diff --git a/src/m-r/PivotPoints/info.xml b/src/m-r/PivotPoints/info.xml deleted file mode 100644 index 4c401047d..000000000 --- a/src/m-r/PivotPoints/info.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Pivot Points depict support and resistance levels, based on the prior lookback window. You can specify window size (e.g. month, week, day, etc). - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Calendar size of the lookback window. - Pivot Point type. - Time series of Pivot Points values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/m-r/Pivots/Pivots.Models.cs b/src/m-r/Pivots/Pivots.Models.cs index 8561851c7..99ca7b469 100644 --- a/src/m-r/Pivots/Pivots.Models.cs +++ b/src/m-r/Pivots/Pivots.Models.cs @@ -1,21 +1,49 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a pivot points calculation. +/// +/// The timestamp of the pivot point. +/// The high point value. +/// The low point value. +/// The high line value. +/// The low line value. +/// The high trend direction. +/// The low trend direction. [Serializable] -public record struct PivotsResult +public record PivotsResult ( - DateTime Timestamp, - decimal? HighPoint, - decimal? LowPoint, - decimal? HighLine, - decimal? LowLine, - PivotTrend? HighTrend, - PivotTrend? LowTrend + DateTime Timestamp, + decimal? HighPoint, + decimal? LowPoint, + decimal? HighLine, + decimal? LowLine, + PivotTrend? HighTrend, + PivotTrend? LowTrend ) : ISeries; +/// +/// Represents the trend direction of a pivot point. +/// public enum PivotTrend { - Hh, // higher high - Lh, // lower high - Hl, // higher low - Ll // lower low + /// + /// Higher high trend. + /// + Hh, + + /// + /// Lower high trend. + /// + Lh, + + /// + /// Higher low trend. + /// + Hl, + + /// + /// Lower low trend. + /// + Ll } diff --git a/src/m-r/Pivots/Pivots.StaticSeries.cs b/src/m-r/Pivots/Pivots.StaticSeries.cs index 00a833b10..88adfefa3 100644 --- a/src/m-r/Pivots/Pivots.StaticSeries.cs +++ b/src/m-r/Pivots/Pivots.StaticSeries.cs @@ -1,9 +1,21 @@ namespace Skender.Stock.Indicators; -// PIVOTS (SERIES) - +/// +/// Provides methods for calculating pivot points series. +/// public static partial class Pivots { + /// + /// Converts a list of quotes to a list of pivot points results. + /// + /// The type of the quote. + /// The list of quotes. + /// The number of periods to the left of the pivot point. + /// The number of periods to the right of the pivot point. + /// The maximum number of periods for trend calculation. + /// The type of end point for the pivot calculation. + /// A list of pivot points results. + /// Thrown when the quotes list is null. public static IReadOnlyList ToPivots( this IReadOnlyList quotes, int leftSpan = 2, diff --git a/src/m-r/Pivots/Pivots.Utilities.cs b/src/m-r/Pivots/Pivots.Utilities.cs index 4030ac0a0..0b6048310 100644 --- a/src/m-r/Pivots/Pivots.Utilities.cs +++ b/src/m-r/Pivots/Pivots.Utilities.cs @@ -1,9 +1,15 @@ namespace Skender.Stock.Indicators; -// PIVOTS (UTILITIES) - +/// +/// Provides utility methods for the Pivot Points indicator. +/// public static partial class Pivots { + /// + /// Removes empty (null) periods from the pivot points results. + /// + /// The list of pivot points results. + /// A list of pivot points results without empty periods. /// public static IReadOnlyList Condense( this IReadOnlyList results) @@ -18,7 +24,14 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } - // parameter validation + /// + /// Validates the parameters for pivot points calculations. + /// + /// The number of periods to the left of the pivot point. + /// The number of periods to the right of the pivot point. + /// The maximum number of periods for trend calculation. + /// The name of the calling method. + /// Thrown when any parameter is out of range. internal static void Validate( int leftSpan, int rightSpan, diff --git a/src/m-r/Pivots/info.xml b/src/m-r/Pivots/info.xml deleted file mode 100644 index 9603cf895..000000000 --- a/src/m-r/Pivots/info.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - Pivots is an extended version of Williams Fractal that includes identification of Higher High, Lower Low, Higher Low, and Lower Low trends between pivots in a lookback window. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of span periods to the left of the evaluation period. - Number of span periods to the right of the evaluation period. - Number of periods in the lookback window. - Determines use of Close or High/Low wicks for points. - Time series of Pivots values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/m-r/Pmo/Pmo.Models.cs b/src/m-r/Pmo/Pmo.Models.cs index 422add37b..e5f248011 100644 --- a/src/m-r/Pmo/Pmo.Models.cs +++ b/src/m-r/Pmo/Pmo.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Price Momentum Oscillator (PMO) calculation. +/// +/// The timestamp of the PMO result. +/// The PMO value. +/// The signal line value. [Serializable] public record PmoResult ( @@ -8,5 +14,6 @@ public record PmoResult double? Signal ) : IReusable { + /// public double Value => Pmo.Null2NaN(); } diff --git a/src/m-r/Pmo/Pmo.StaticSeries.cs b/src/m-r/Pmo/Pmo.StaticSeries.cs index feaea3a64..7ae23d266 100644 --- a/src/m-r/Pmo/Pmo.StaticSeries.cs +++ b/src/m-r/Pmo/Pmo.StaticSeries.cs @@ -1,9 +1,20 @@ namespace Skender.Stock.Indicators; -// PRICE MOMENTUM OSCILLATOR (SERIES) - +/// +/// Provides methods for calculating the Price Momentum Oscillator (PMO) series. +/// public static partial class Pmo { + /// + /// Converts a list of source values to a list of PMO results. + /// + /// The type of the source values. + /// The list of source values. + /// The number of periods for the time span. + /// The number of periods for smoothing. + /// The number of periods for the signal line. + /// A list of PMO results. + /// Thrown when the source list is null. public static IReadOnlyList ToPmo( this IReadOnlyList source, int timePeriods = 35, diff --git a/src/m-r/Pmo/Pmo.Utilities.cs b/src/m-r/Pmo/Pmo.Utilities.cs index 3ca068f94..db7b59b32 100644 --- a/src/m-r/Pmo/Pmo.Utilities.cs +++ b/src/m-r/Pmo/Pmo.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// PRICE MOMENTUM OSCILLATOR (UTILITIES) - +/// +/// Provides utility methods for the Price Momentum Oscillator (PMO). +/// public static partial class Pmo { - // remove recommended periods + /// + /// Removes the recommended warmup periods from the PMO results. + /// + /// The list of PMO results. + /// A list of PMO results without the warmup periods. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) @@ -16,7 +21,13 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(ts + 250); } - // parameter validation + /// + /// Validates the parameters for PMO calculations. + /// + /// The number of periods for the time span. + /// The number of periods for smoothing. + /// The number of periods for the signal line. + /// Thrown when any parameter is out of range. internal static void Validate( int timePeriods, int smoothPeriods, diff --git a/src/m-r/Pmo/info.xml b/src/m-r/Pmo/info.xml deleted file mode 100644 index 00bd39e89..000000000 --- a/src/m-r/Pmo/info.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Price Momentum Oscillator (PMO) is double-smoothed ROC based momentum indicator. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for ROC EMA smoothing. - Number of periods for PMO EMA smoothing. - Number of periods for Signal line EMA. - Time series of PMO values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/m-r/Prs/Prs.Models.cs b/src/m-r/Prs/Prs.Models.cs index e565d7e56..a6aaa1fcf 100644 --- a/src/m-r/Prs/Prs.Models.cs +++ b/src/m-r/Prs/Prs.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Price Relative Strength (PRS) calculation. +/// +/// The timestamp of the PRS result. +/// The PRS value. +/// The PRS percentage value. [Serializable] public record PrsResult ( @@ -8,5 +14,6 @@ public record PrsResult double? PrsPercent ) : IReusable { + /// public double Value => Prs.Null2NaN(); } diff --git a/src/m-r/Prs/Prs.StaticSeries.cs b/src/m-r/Prs/Prs.StaticSeries.cs index 40a6ab70d..1b8938a5c 100644 --- a/src/m-r/Prs/Prs.StaticSeries.cs +++ b/src/m-r/Prs/Prs.StaticSeries.cs @@ -1,9 +1,20 @@ namespace Skender.Stock.Indicators; -// PRICE RELATIVE STRENGTH (SERIES) - +/// +/// Provides methods for calculating the Price Relative Strength (PRS) series. +/// public static partial class Prs { + /// + /// Converts a list of evaluation source values and base source values to a list of PRS results. + /// + /// The type of the source values. + /// The list of evaluation source values. + /// The list of base source values. + /// The number of periods for the lookback calculation. + /// A list of PRS results. + /// Thrown when the source list is null. + /// Thrown when the timestamp sequence does not match. public static IReadOnlyList ToPrs( this IReadOnlyList sourceEval, IReadOnlyList sourceBase, diff --git a/src/m-r/Prs/Prs.Utilities.cs b/src/m-r/Prs/Prs.Utilities.cs index 497f2e649..42593691a 100644 --- a/src/m-r/Prs/Prs.Utilities.cs +++ b/src/m-r/Prs/Prs.Utilities.cs @@ -2,13 +2,22 @@ namespace Skender.Stock.Indicators; -// PRICE RELATIVE STRENGTH (UTILITIES) - +/// +/// Provides utility methods for the PRS (Price Relative Strength) indicator. +/// public static partial class Prs { private static readonly CultureInfo invariantCulture = CultureInfo.InvariantCulture; - // parameter validation + /// + /// Validates the parameters for PRS calculations. + /// + /// The type of the source values. + /// The list of evaluation quotes. + /// The list of base quotes. + /// The number of periods for the lookback calculation. + /// Thrown when the lookback periods are less than or equal to 0. + /// Thrown when there are insufficient quotes or mismatched quote counts. internal static void Validate( IReadOnlyList quotesEval, IReadOnlyList quotesBase, diff --git a/src/m-r/Prs/info.xml b/src/m-r/Prs/info.xml deleted file mode 100644 index e9a4e9e7c..000000000 --- a/src/m-r/Prs/info.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - Price Relative Strength (PRS), also called Comparative Relative Strength, - shows the ratio of two quote histories. It is often used to compare - against a market index or sector ETF. When using the optional lookbackPeriods, - this also return relative percent change over the specified periods. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes for evaluation. - This is usually market index data, but could be any baseline data that you might use for comparison. - Optional. Number of periods for % difference. - Time series of PRS values. - Invalid parameter value provided. - Invalid quotes provided. - \ No newline at end of file diff --git a/src/m-r/Pvo/Pvo.Models.cs b/src/m-r/Pvo/Pvo.Models.cs index 39fb9966b..ed3bf7f3e 100644 --- a/src/m-r/Pvo/Pvo.Models.cs +++ b/src/m-r/Pvo/Pvo.Models.cs @@ -1,5 +1,12 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Percentage Volume Oscillator (PVO) calculation. +/// +/// The timestamp of the PVO result. +/// The PVO value. +/// The signal line value. +/// The histogram value. [Serializable] public record PvoResult ( @@ -9,5 +16,6 @@ public record PvoResult double? Histogram ) : IReusable { + /// public double Value => Pvo.Null2NaN(); } diff --git a/src/m-r/Pvo/Pvo.StaticSeries.cs b/src/m-r/Pvo/Pvo.StaticSeries.cs index 4f02630e7..c57741917 100644 --- a/src/m-r/Pvo/Pvo.StaticSeries.cs +++ b/src/m-r/Pvo/Pvo.StaticSeries.cs @@ -1,18 +1,38 @@ namespace Skender.Stock.Indicators; -// PRICE VOLUME OSCILLATOR (SERIES) - +/// +/// Provides methods for calculating the Percentage Volume Oscillator (PVO) series. +/// public static partial class Pvo { + /// + /// Converts a list of quotes to a list of PVO results. + /// + /// The type of the quote values. + /// The list of quotes. + /// The number of periods for the fast EMA. + /// The number of periods for the slow EMA. + /// The number of periods for the signal line. + /// A list of PVO results. + /// Thrown when the quotes list is null. public static IReadOnlyList ToPvo( - this IReadOnlyList quotes, - int fastPeriods = 12, - int slowPeriods = 26, - int signalPeriods = 9) - where TQuote : IQuote => quotes - .Use(CandlePart.Volume) - .CalcPvo(fastPeriods, slowPeriods, signalPeriods); - + this IReadOnlyList quotes, + int fastPeriods = 12, + int slowPeriods = 26, + int signalPeriods = 9) + where TQuote : IQuote => quotes + .Use(CandlePart.Volume) + .CalcPvo(fastPeriods, slowPeriods, signalPeriods); + + /// + /// Calculates the PVO values. + /// + /// The type of the source values. + /// The list of volume values. + /// The number of periods for the fast EMA. + /// The number of periods for the slow EMA. + /// The number of periods for the signal line. + /// A list of PVO results. private static List CalcPvo( this IReadOnlyList source, // volume int fastPeriods, diff --git a/src/m-r/Pvo/Pvo.Utilities.cs b/src/m-r/Pvo/Pvo.Utilities.cs index 3731ccb5d..03f3c4637 100644 --- a/src/m-r/Pvo/Pvo.Utilities.cs +++ b/src/m-r/Pvo/Pvo.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// PRICE VOLUME OSCILLATOR (UTILITIES) - +/// +/// Provides utility methods for the Percentage Volume Oscillator (PVO). +/// public static partial class Pvo { - // remove recommended periods + /// + /// Removes the recommended warmup periods from the PVO results. + /// + /// The list of PVO results. + /// A list of PVO results without the warmup periods. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) @@ -16,7 +21,13 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 250); } - // parameter validation + /// + /// Validates the parameters for PVO calculations. + /// + /// The number of periods for the fast EMA. + /// The number of periods for the slow EMA. + /// The number of periods for the signal line. + /// Thrown when any parameter is out of range. internal static void Validate( int fastPeriods, int slowPeriods, diff --git a/src/m-r/Pvo/info.xml b/src/m-r/Pvo/info.xml deleted file mode 100644 index db0e596b3..000000000 --- a/src/m-r/Pvo/info.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Percentage Volume Oscillator (PVO) is a simple oscillator view of two converging/diverging exponential moving averages of Volume. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the Fast moving average. - Number of periods in the Slow moving average. - Number of periods for the PVO SMA signal line. - Time series of PVO values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/m-r/Renko/Renko.Models.cs b/src/m-r/Renko/Renko.Models.cs index d8fe874d3..4a3ffd201 100644 --- a/src/m-r/Renko/Renko.Models.cs +++ b/src/m-r/Renko/Renko.Models.cs @@ -1,5 +1,15 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Renko chart calculation. +/// +/// The timestamp of the Renko brick. +/// The opening price of the Renko brick. +/// The highest price of the Renko brick. +/// The lowest price of the Renko brick. +/// The closing price of the Renko brick. +/// The volume of the Renko brick. +/// Indicates whether the Renko brick is an up brick. /// [Serializable] public record RenkoResult diff --git a/src/m-r/Renko/Renko.StaticSeries.cs b/src/m-r/Renko/Renko.StaticSeries.cs index 6aaa45bc5..b20ac9368 100644 --- a/src/m-r/Renko/Renko.StaticSeries.cs +++ b/src/m-r/Renko/Renko.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// RENKO CHART (SERIES) - +/// +/// Provides methods for generating Renko chart series. +/// public static partial class Renko { + /// + /// Converts a list of quotes to a list of Renko chart results. + /// + /// The type of the quote values. + /// The list of quotes. + /// The size of each Renko brick. + /// The price candle end type to use as the brick threshold. + /// A list of Renko chart results. + /// Thrown when the quotes list is null. public static IReadOnlyList ToRenko( this IReadOnlyList quotes, decimal brickSize, diff --git a/src/m-r/Renko/Renko.StreamHub.cs b/src/m-r/Renko/Renko.StreamHub.cs index 8486bf51f..f310a03b5 100644 --- a/src/m-r/Renko/Renko.StreamHub.cs +++ b/src/m-r/Renko/Renko.StreamHub.cs @@ -1,17 +1,37 @@ namespace Skender.Stock.Indicators; -// RENKO CHART (STREAM HUB) - #region hub interface and initializer +/// +/// Represents a hub for generating Renko chart results. +/// public interface IRenkoHub { + /// + /// Gets the size of each Renko brick. + /// decimal BrickSize { get; } + + /// + /// Gets the price candle end type used to determine when threshold + /// is met to generate new bricks. + /// EndType EndType { get; } } +/// +/// Provides methods for generating Renko chart series in a streaming manner. +/// public static partial class Renko { + /// + /// Converts a quote provider to a Renko hub. + /// + /// The type of the quote values. + /// The quote provider. + /// The size of each Renko brick. + /// The price candle end type to use as the brick threshold. + /// A Renko hub. public static RenkoHub ToRenko( this IQuoteProvider quoteProvider, decimal brickSize, @@ -21,6 +41,10 @@ public static RenkoHub ToRenko( } #endregion +/// +/// Represents a hub for generating Renko chart results from a stream of quotes. +/// +/// The type of the quote values. public class RenkoHub : QuoteProvider, IRenkoHub where TIn : IQuote @@ -33,6 +57,12 @@ private RenkoResult lastBrick = new(default, default, default, default, default, default, default); + /// + /// Initializes a new instance of the class. + /// + /// The quote provider. + /// The size of each Renko brick. + /// The type of price to use for the end of the brick. internal RenkoHub( IQuoteProvider provider, decimal brickSize, @@ -48,40 +78,34 @@ internal RenkoHub( #endregion /// - /// Renko hub settings. Since it can produce 0 or many bricks per quote, + /// Renko hub settings. Since it can produce 0 or many bricks per quote, /// the default 1:1 in/out is not used and must be skipped to prevent - /// same-date triggerred rebuilds when caching. + /// same-date triggered rebuilds when caching. /// public override BinarySettings Properties { get; init; } = new(0b00000010); // custom - /// - /// Standard brick size for Renko chart. - /// + /// public decimal BrickSize { get; } - /// - /// Close or High/Low price used to determine when threshold - /// is met to generate new bricks. - /// + /// public EndType EndType { get; } - // METHODS - + /// public override string ToString() => hubName; + /// public override void OnAdd(TIn item, bool notify, int? indexHint) => ToIndicator(item, notify, indexHint); + /// protected override (RenkoResult result, int index) ToIndicator(TIn item, int? indexHint) => throw new InvalidOperationException(); // not used - // TODO: see if returning array of results is possible ^^ - // for all indicators, so we don't have to do this goofy override - /// - /// Restore last brick marker. + /// Restores the last brick marker to the state at the specified timestamp. /// + /// The timestamp to restore to. /// protected override void RollbackState(DateTime timestamp) { diff --git a/src/m-r/Renko/Renko.Utilities.cs b/src/m-r/Renko/Renko.Utilities.cs index d8463910b..e23424a95 100644 --- a/src/m-r/Renko/Renko.Utilities.cs +++ b/src/m-r/Renko/Renko.Utilities.cs @@ -1,10 +1,20 @@ namespace Skender.Stock.Indicators; -// RENKO CHART (UTILITIES) - +/// +/// Provides utility methods for Renko chart calculations. +/// public static partial class Renko { - // calculate brick size + /// + /// Calculates the number of new bricks to be added based on the current quote and the last brick. + /// + /// The type of the quote values. + /// The current quote. + /// The last Renko brick. + /// The size of each Renko brick. + /// The type of price to use for the end of the brick. + /// The number of new bricks to be added. + /// Thrown when the end type is out of range. internal static int GetNewBrickQuantity( TQuote q, RenkoResult lastBrick, @@ -47,7 +57,11 @@ internal static int GetNewBrickQuantity( return brickQuantity; } - // parameter validation + /// + /// Validates the parameters for Renko chart calculations. + /// + /// The size of each Renko brick. + /// Thrown when the brick size is less than or equal to 0. internal static void Validate( decimal brickSize) { @@ -59,5 +73,4 @@ internal static void Validate( "Brick size must be greater than 0 for Renko Charts."); } } - } diff --git a/src/m-r/Renko/info.xml b/src/m-r/Renko/info.xml deleted file mode 100644 index 7167d652e..000000000 --- a/src/m-r/Renko/info.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - Renko Chart is a modified Japanese candlestick pattern that uses time-lapsed bricks. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Fixed brick size ($). - End type. See documentation. - Time series of Renko Chart candlestick values. - Invalid parameter value provided. - - - - - The ATR Renko Chart is a modified Japanese candlestick pattern based on Average True Range brick size. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Lookback periods for the ATR evaluation. - End type. See documentation. - Time series of Renko Chart candlestick values. - - - \ No newline at end of file diff --git a/src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs b/src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs index 479278122..56b577f8b 100644 --- a/src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs +++ b/src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs @@ -1,9 +1,18 @@ namespace Skender.Stock.Indicators; -// RENKO CHART - ATR (SERIES) - +/// +/// Provides methods for generating Renko chart series using ATR (Average True Range). +/// public static partial class Renko { + /// + /// Converts a list of quotes to a list of Renko chart results using ATR for brick size. + /// + /// The type of the quote values. + /// The list of quotes. + /// The number of periods for calculating ATR. + /// The price candle end type to use as the brick threshold. + /// A list of Renko chart results. public static IReadOnlyList GetRenkoAtr( this IReadOnlyList quotes, int atrPeriods, diff --git a/src/m-r/Roc/Roc.Models.cs b/src/m-r/Roc/Roc.Models.cs index c7040df8d..ed913bc2b 100644 --- a/src/m-r/Roc/Roc.Models.cs +++ b/src/m-r/Roc/Roc.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Rate of Change (ROC) calculation. +/// +/// The timestamp of the ROC result. +/// The momentum value of the ROC result. +/// The rate of change value of the ROC result. [Serializable] public record RocResult ( @@ -8,5 +14,6 @@ public record RocResult double? Roc ) : IReusable { + /// public double Value => Roc.Null2NaN(); } diff --git a/src/m-r/Roc/Roc.StaticSeries.cs b/src/m-r/Roc/Roc.StaticSeries.cs index aacfbf368..bd99c6648 100644 --- a/src/m-r/Roc/Roc.StaticSeries.cs +++ b/src/m-r/Roc/Roc.StaticSeries.cs @@ -1,9 +1,18 @@ namespace Skender.Stock.Indicators; -// RATE OF CHANGE (SERIES) - +/// +/// Provides methods for calculating the Rate of Change (ROC) series. +/// public static partial class Roc { + /// + /// Converts a list of reusable values to a list of ROC results. + /// + /// The type of the reusable values. + /// The list of reusable values. + /// The number of periods to look back for the ROC calculation. + /// A list of ROC results. + /// Thrown when the source is null. public static IReadOnlyList ToRoc( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/m-r/Roc/Roc.Utilities.cs b/src/m-r/Roc/Roc.Utilities.cs index 2256c42ec..e5f3dcd5c 100644 --- a/src/m-r/Roc/Roc.Utilities.cs +++ b/src/m-r/Roc/Roc.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// RATE OF CHANGE (UTILITIES) - +/// +/// Provides utility methods for Rate of Change (ROC) calculations. +/// public static partial class Roc { - // parameter validation + /// + /// Validates the parameters for ROC calculations. + /// + /// The number of periods to look back for the ROC calculation. + /// Thrown when the lookback periods are less than or equal to 0. internal static void Validate( int lookbackPeriods) { diff --git a/src/m-r/Roc/info.xml b/src/m-r/Roc/info.xml deleted file mode 100644 index c1021b2dd..000000000 --- a/src/m-r/Roc/info.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - Rate of Change (ROC), also known as Momentum Oscillator, is the percent change of price over a lookback window. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of ROC values. - Invalid parameter value provided. - - - \ No newline at end of file diff --git a/src/m-r/RocWb/RocWb.Models.cs b/src/m-r/RocWb/RocWb.Models.cs index 6ed030dbb..6b61b169d 100644 --- a/src/m-r/RocWb/RocWb.Models.cs +++ b/src/m-r/RocWb/RocWb.Models.cs @@ -1,5 +1,13 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Rate of Change with Bands (RocWb) calculation. +/// +/// The timestamp of the RocWb result. +/// The rate of change value of the RocWb result. +/// The exponential moving average of the rate of change. +/// The upper band value of the RocWb result. +/// The lower band value of the RocWb result. [Serializable] public record RocWbResult ( @@ -10,5 +18,6 @@ public record RocWbResult double? LowerBand ) : IReusable { + /// public double Value => Roc.Null2NaN(); } diff --git a/src/m-r/RocWb/RocWb.StaticSeries.cs b/src/m-r/RocWb/RocWb.StaticSeries.cs index 5fa46b88f..dd9ea73b3 100644 --- a/src/m-r/RocWb/RocWb.StaticSeries.cs +++ b/src/m-r/RocWb/RocWb.StaticSeries.cs @@ -1,9 +1,20 @@ namespace Skender.Stock.Indicators; -// RATE OF CHANGE (ROC) WITH BANDS (SERIES) - +/// +/// Provides methods for calculating the Rate of Change with Bands (RocWb) series. +/// public static partial class RocWb { + /// + /// Converts a list of reusable values to a list of RocWb results. + /// + /// The type of the reusable values. + /// The list of reusable values. + /// The number of periods to look back for the ROC calculation. + /// The number of periods for the exponential moving average calculation. + /// The number of periods for the standard deviation calculation. + /// A list of RocWb results. + /// Thrown when the source is null. public static IReadOnlyList ToRocWb( this IReadOnlyList source, int lookbackPeriods, diff --git a/src/m-r/RocWb/RocWb.Utilities.cs b/src/m-r/RocWb/RocWb.Utilities.cs index 3b94d7257..728e568cb 100644 --- a/src/m-r/RocWb/RocWb.Utilities.cs +++ b/src/m-r/RocWb/RocWb.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// RATE OF CHANGE (ROC) WITH BANDS (UTILITIES) - +/// +/// Provides utility methods for Rate of Change with Bands (RocWb) calculations. +/// public static partial class RocWb { - // remove recommended periods + /// + /// Removes the recommended warmup periods from the RocWb results. + /// + /// The list of RocWb results. + /// A list of RocWb results with the warmup periods removed. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) @@ -16,7 +21,13 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 100); } - // parameter validation + /// + /// Validates the parameters for RocWb calculations. + /// + /// The number of periods to look back for the ROC calculation. + /// The number of periods for the exponential moving average calculation. + /// The number of periods for the standard deviation calculation. + /// Thrown when any of the parameters are out of range. internal static void Validate( int lookbackPeriods, int emaPeriods, diff --git a/src/m-r/RocWb/info.xml b/src/m-r/RocWb/info.xml deleted file mode 100644 index e61bd6484..000000000 --- a/src/m-r/RocWb/info.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - Rate of Change with Bands (ROCWB) is the percent change of price over a lookback window with standard deviation bands. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Number of periods for the ROC EMA line. - Number of periods the standard deviation for upper/lower band lines. - Time series of ROCWB values. - Invalid parameter value provided. - - - \ No newline at end of file diff --git a/src/m-r/RollingPivots/RollingPivots.Models.cs b/src/m-r/RollingPivots/RollingPivots.Models.cs index c5782f236..7efb500c4 100644 --- a/src/m-r/RollingPivots/RollingPivots.Models.cs +++ b/src/m-r/RollingPivots/RollingPivots.Models.cs @@ -1,19 +1,58 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Rolling Pivots calculation. +/// [Serializable] public record RollingPivotsResult : ISeries, IPivotPoint { + /// + /// Gets the timestamp of the Rolling Pivots result. + /// public DateTime Timestamp { get; init; } + /// + /// Gets the pivot point (PP) value. + /// public decimal? PP { get; init; } + /// + /// Gets the first support level (S1). + /// public decimal? S1 { get; init; } + + /// + /// Gets the second support level (S2). + /// public decimal? S2 { get; init; } + + /// + /// Gets the third support level (S3). + /// public decimal? S3 { get; init; } + + /// + /// Gets the fourth support level (S4). + /// public decimal? S4 { get; init; } + /// + /// Gets the first resistance level (R1). + /// public decimal? R1 { get; init; } + + /// + /// Gets the second resistance level (R2). + /// public decimal? R2 { get; init; } + + /// + /// Gets the third resistance level (R3). + /// public decimal? R3 { get; init; } + + /// + /// Gets the fourth resistance level (R4). + /// public decimal? R4 { get; init; } } diff --git a/src/m-r/RollingPivots/RollingPivots.StaticSeries.cs b/src/m-r/RollingPivots/RollingPivots.StaticSeries.cs index efbc07fde..bb70d8188 100644 --- a/src/m-r/RollingPivots/RollingPivots.StaticSeries.cs +++ b/src/m-r/RollingPivots/RollingPivots.StaticSeries.cs @@ -1,9 +1,20 @@ namespace Skender.Stock.Indicators; -// ROLLING PIVOT POINTS (SERIES) - +/// +/// Provides methods for calculating the Rolling Pivot Points series. +/// public static partial class RollingPivots { + /// + /// Converts a list of quotes to a list of Rolling Pivot Points results. + /// + /// The type of the quote values. + /// The list of quotes. + /// The number of periods in the rolling window. + /// The number of periods to offset the window. + /// The type of pivot point calculation to use. + /// A list of Rolling Pivot Points results. + /// Thrown when the quotes are null. public static IReadOnlyList ToRollingPivots( this IReadOnlyList quotes, int windowPeriods, diff --git a/src/m-r/RollingPivots/RollingPivots.Utilities.cs b/src/m-r/RollingPivots/RollingPivots.Utilities.cs index 95a88b44b..287367293 100644 --- a/src/m-r/RollingPivots/RollingPivots.Utilities.cs +++ b/src/m-r/RollingPivots/RollingPivots.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// ROLLING PIVOT POINTS (UTILITIES) - +/// +/// Provides utility methods for Rolling Pivot Points calculations. +/// public static partial class RollingPivots { - // remove recommended periods + /// + /// Removes the recommended warmup periods from the Rolling Pivots results. + /// + /// The list of Rolling Pivots results. + /// A list of Rolling Pivots results with the warmup periods removed. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) @@ -16,7 +21,12 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } - // parameter validation + /// + /// Validates the parameters for Rolling Pivots calculations. + /// + /// The number of periods in the rolling window. + /// The number of periods to offset the window. + /// Thrown when any of the parameters are out of range. internal static void Validate( int windowPeriods, int offsetPeriods) diff --git a/src/m-r/RollingPivots/info.xml b/src/m-r/RollingPivots/info.xml deleted file mode 100644 index 90fd90e70..000000000 --- a/src/m-r/RollingPivots/info.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - Rolling Pivot Points is a modern update to traditional fixed calendar window Pivot Points. - It depicts support and resistance levels, based on a defined rolling window and offset. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the evaluation window. - Number of periods to offset the window from the current period. - Pivot Point type. - Time series of Rolling Pivot Points values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/m-r/Rsi/Rsi.Models.cs b/src/m-r/Rsi/Rsi.Models.cs index 2689789e5..c3147245c 100644 --- a/src/m-r/Rsi/Rsi.Models.cs +++ b/src/m-r/Rsi/Rsi.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Relative Strength Index (RSI) calculation. +/// +/// The timestamp of the RSI result. +/// The RSI value. [Serializable] public record RsiResult ( @@ -7,5 +12,6 @@ public record RsiResult double? Rsi = null ) : IReusable { + /// public double Value => Rsi.Null2NaN(); } diff --git a/src/m-r/Rsi/Rsi.StaticSeries.cs b/src/m-r/Rsi/Rsi.StaticSeries.cs index fe0163cdf..265e8db5d 100644 --- a/src/m-r/Rsi/Rsi.StaticSeries.cs +++ b/src/m-r/Rsi/Rsi.StaticSeries.cs @@ -1,9 +1,18 @@ namespace Skender.Stock.Indicators; -// RELATIVE STRENGTH INDEX (SERIES) - +/// +/// Provides methods for calculating the Relative Strength Index (RSI) series. +/// public static partial class Rsi { + /// + /// Converts a list of reusable values to a list of RSI results. + /// + /// The type of the reusable values. + /// The list of reusable values. + /// The number of periods to look back for the RSI calculation. + /// A list of RSI results. + /// Thrown when the source is null. public static IReadOnlyList ToRsi( this IReadOnlyList source, int lookbackPeriods = 14) diff --git a/src/m-r/Rsi/Rsi.Utilities.cs b/src/m-r/Rsi/Rsi.Utilities.cs index c82bab854..3eea67678 100644 --- a/src/m-r/Rsi/Rsi.Utilities.cs +++ b/src/m-r/Rsi/Rsi.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// RELATIVE STRENGTH INDEX (UTILITIES) - +/// +/// Provides utility methods for Relative Strength Index (RSI) calculations. +/// public static partial class Rsi { - // remove recommended periods + /// + /// Removes the recommended warmup periods from the RSI results. + /// + /// The list of RSI results. + /// A list of RSI results with the warmup periods removed. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) @@ -16,7 +21,11 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(10 * n); } - // parameter validation + /// + /// Validates the parameters for RSI calculations. + /// + /// The number of periods to look back for the RSI calculation. + /// Thrown when the lookback periods are less than 1. internal static void Validate( int lookbackPeriods) { diff --git a/src/m-r/Rsi/info.xml b/src/m-r/Rsi/info.xml deleted file mode 100644 index bdfe61ebc..000000000 --- a/src/m-r/Rsi/info.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Relative Strength Index (RSI) measures strength of the winning/losing streak over N lookback periods - on a scale of 0 to 100, to depict overbought and oversold conditions. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of RSI values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/s-z/Slope/Slope.Models.cs b/src/s-z/Slope/Slope.Models.cs index a9488d2c7..6f220837b 100644 --- a/src/s-z/Slope/Slope.Models.cs +++ b/src/s-z/Slope/Slope.Models.cs @@ -1,5 +1,14 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Slope calculation. +/// +/// The timestamp of the data point. +/// The value of the slope at this point. +/// The intercept value at this point. +/// The standard deviation value at this point. +/// The R-squared value at this point. +/// The value of the last line segment only. [Serializable] public record SlopeResult ( @@ -11,5 +20,6 @@ public record SlopeResult decimal? Line = null // last line segment only ) : IReusable { + /// public double Value => Slope.Null2NaN(); } diff --git a/src/s-z/Slope/Slope.StaticSeries.cs b/src/s-z/Slope/Slope.StaticSeries.cs index 6082405ed..47e550f03 100644 --- a/src/s-z/Slope/Slope.StaticSeries.cs +++ b/src/s-z/Slope/Slope.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// SLOPE AND LINEAR REGRESSION (SERIES) - +/// +/// Provides methods for calculating the Slope and Linear Regression for a given source list and lookback period. +/// public static partial class Slope { + /// + /// Calculates the Slope and Linear Regression for a given source list and lookback period. + /// + /// The type of the source items, must implement IReusable. + /// The source list to analyze. + /// The number of periods to look back for the calculation. + /// A read-only list of Slope results. + /// Thrown when the source list is null. + /// Thrown when the lookback period is less than 1. public static IReadOnlyList ToSlope( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/s-z/Slope/Slope.Utilities.cs b/src/s-z/Slope/Slope.Utilities.cs index f04ed9b5e..e4f576e8f 100644 --- a/src/s-z/Slope/Slope.Utilities.cs +++ b/src/s-z/Slope/Slope.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// SLOPE AND LINEAR REGRESSION (UTILITIES) - +/// +/// Provides utility methods for Slope and Linear Regression calculations. +/// public static partial class Slope { - // parameter validation + /// + /// Validates the lookback periods parameter. + /// + /// The number of lookback periods to validate. + /// Thrown when the lookback periods are less than or equal to 1. internal static void Validate( int lookbackPeriods) { diff --git a/src/s-z/Slope/info.xml b/src/s-z/Slope/info.xml deleted file mode 100644 index 2ca1d12ea..000000000 --- a/src/s-z/Slope/info.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Slope of the best fit line is determined by an ordinary least-squares simple linear regression on price. - It can be used to help identify trend strength and direction. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of Slope values, including Slope, Standard Deviation, R², and a best-fit Line (for the last lookback segment). - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/s-z/Sma/Sma.Models.cs b/src/s-z/Sma/Sma.Models.cs index f0dfcd857..43ecc2306 100644 --- a/src/s-z/Sma/Sma.Models.cs +++ b/src/s-z/Sma/Sma.Models.cs @@ -1,10 +1,16 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Simple Moving Average (SMA) calculation. +/// +/// The timestamp of the data point. +/// The value of the SMA at this point. [Serializable] public record SmaResult( DateTime Timestamp, double? Sma ) : IReusable { + /// public double Value => Sma.Null2NaN(); } diff --git a/src/s-z/Sma/Sma.StaticSeries.cs b/src/s-z/Sma/Sma.StaticSeries.cs index a0b7a5c39..c27543612 100644 --- a/src/s-z/Sma/Sma.StaticSeries.cs +++ b/src/s-z/Sma/Sma.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// SIMPLE MOVING AVERAGE (SERIES) - +/// +/// Provides methods for calculating Simple Moving Average (SMA). +/// public static partial class Sma { + /// + /// Calculates the Simple Moving Average (SMA) for a given source list and lookback period. + /// + /// The type of the source items, must implement IReusable. + /// The source list to analyze. + /// The number of periods to look back for the SMA calculation. + /// A read-only list of SMA results. + /// Thrown when the source list is null. + /// Thrown when the lookback period is less than 1. public static IReadOnlyList ToSma( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/s-z/Sma/Sma.StreamHub.cs b/src/s-z/Sma/Sma.StreamHub.cs index 150609bb2..9689361d9 100644 --- a/src/s-z/Sma/Sma.StreamHub.cs +++ b/src/s-z/Sma/Sma.StreamHub.cs @@ -4,13 +4,29 @@ namespace Skender.Stock.Indicators; #region hub interface and initializer +/// +/// Interface for Simple Moving Average (SMA) hub. +/// public interface ISmaHub { + /// + /// Gets the number of lookback periods. + /// int LookbackPeriods { get; } } +/// +/// Provides methods for creating SMA hubs. +/// public static partial class Sma { + /// + /// Converts the chain provider to an SMA hub. + /// + /// The type of the input. + /// The chain provider. + /// The number of lookback periods. + /// An SMA hub. public static SmaHub ToSma( this IChainProvider chainProvider, int lookbackPeriods) @@ -19,6 +35,10 @@ public static SmaHub ToSma( } #endregion +/// +/// Represents a Simple Moving Average (SMA) stream hub. +/// +/// The type of the input. public class SmaHub : ChainProvider, ISmaHub where TIn : IReusable @@ -27,6 +47,11 @@ public class SmaHub private readonly string hubName; + /// + /// Initializes a new instance of the class. + /// + /// The chain provider. + /// The number of lookback periods. internal SmaHub( IChainProvider provider, int lookbackPeriods) : base(provider) @@ -39,12 +64,17 @@ internal SmaHub( } #endregion + /// + /// Gets the number of lookback periods. + /// public int LookbackPeriods { get; init; } // METHODS + /// public override string ToString() => hubName; + /// protected override (SmaResult result, int index) ToIndicator(TIn item, int? indexHint) { diff --git a/src/s-z/Sma/Sma.Utilities.cs b/src/s-z/Sma/Sma.Utilities.cs index fe202fc86..7792a9b1c 100644 --- a/src/s-z/Sma/Sma.Utilities.cs +++ b/src/s-z/Sma/Sma.Utilities.cs @@ -3,26 +3,19 @@ namespace Skender.Stock.Indicators; -// SIMPLE MOVING AVERAGE (UTILITIES) - +/// +/// Provides utility methods for Simple Moving Average (SMA) calculations. +/// public static partial class Sma { /// - /// Simple moving average calculation + /// Simple moving average calculation. /// - /// List of chainable values - /// - /// Window to evaluate, prior to 'endIndex' - /// - /// - /// Index position to evaluate or last position when . - /// - /// IReusable (chainable) type - /// - /// Simple moving average or - /// if incalculable - /// values are in range. - /// + /// List of chainable values. + /// Window to evaluate, prior to 'endIndex'. + /// Index position to evaluate or last position when . + /// IReusable (chainable) type. + /// Simple moving average or if incalculable values are in range. public static double? Average( // public API only this IReadOnlyList values, int lookbackPeriods, @@ -39,16 +32,13 @@ public static partial class Sma } /// - /// Simple moving average calculation + /// Simple moving average calculation. /// - /// List of chainable values - /// Window to evaluate, prior to 'endIndex' + /// List of chainable values. + /// Window to evaluate, prior to 'endIndex'. /// Index position to evaluate. - /// IReusable (chainable) type - /// - /// Simple moving average or - /// when incalculable. - /// + /// IReusable (chainable) type. + /// Simple moving average or when incalculable. internal static double Increment( IReadOnlyList source, int lookbackPeriods, @@ -71,6 +61,13 @@ internal static double Increment( // TODO: apply this SMA increment method more widely in other indicators (see EMA example) } + /// + /// Simple moving average calculation using SIMD. + /// + /// Array of prices. + /// Period to evaluate. + /// Array of simple moving averages. + /// Caution: this experimental method has rounding errors. [ExcludeFromCodeCoverage] // experimental SIMD code internal static double[] Increment(this double[] prices, int period) { @@ -104,7 +101,11 @@ internal static double[] Increment(this double[] prices, int period) return sma; } - // parameter validation + /// + /// Validates the lookback periods parameter. + /// + /// The number of lookback periods to validate. + /// Thrown when the lookback periods are less than or equal to 0. internal static void Validate( int lookbackPeriods) { diff --git a/src/s-z/Sma/info.xml b/src/s-z/Sma/info.xml deleted file mode 100644 index 2e257ef90..000000000 --- a/src/s-z/Sma/info.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - Simple Moving Average (SMA) of the price. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of SMA values. - Invalid parameter value provided. - - - - - Simple Moving Average (SMA) is the average of price over a lookback window. This extended variant includes mean absolute deviation (MAD), mean square error (MSE), and mean absolute percentage error (MAPE). - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of SMA, MAD, MSE, and MAPE values. - Invalid parameter value provided. - - - diff --git a/src/s-z/SmaAnalysis/SmaAnalysis.Models.cs b/src/s-z/SmaAnalysis/SmaAnalysis.Models.cs index 055f5c004..5caabc160 100644 --- a/src/s-z/SmaAnalysis/SmaAnalysis.Models.cs +++ b/src/s-z/SmaAnalysis/SmaAnalysis.Models.cs @@ -1,13 +1,14 @@ namespace Skender.Stock.Indicators; /// -/// SMA with extended analysis. +/// Represents the result of a Simple Moving Average (SMA) calculation +/// with extended analysis values. /// -/// Timestamp -/// Simple moving average -/// Mean absolute deviation -/// Mean square error -/// Mean absolute percentage error +/// The timestamp of the data point. +/// The Simple Moving Average (SMA) at this point. +/// The Mean Absolute Deviation (MAD) at this point. +/// The Mean Square Error (MSE) at this point. +/// The Mean Absolute Percentage Error (MAPE) at this point. [Serializable] public record SmaAnalysis ( diff --git a/src/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.cs b/src/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.cs index d43770795..50a6fa85c 100644 --- a/src/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.cs +++ b/src/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.cs @@ -1,9 +1,17 @@ namespace Skender.Stock.Indicators; -// SIMPLE MOVING AVERAGE (ANALYSIS) - +/// +/// Provides methods for calculating Simple Moving Average (SMA) with extended analysis. +/// public static partial class Sma { + /// + /// Converts a source list to a list of SMA analysis results. + /// + /// The type of the source items, must implement IReusable. + /// The source list to analyze. + /// The number of periods to look back for the SMA calculation. + /// A read-only list of SMA analysis results. public static IReadOnlyList ToSmaAnalysis( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/s-z/Smi/Smi.Models.cs b/src/s-z/Smi/Smi.Models.cs index 739c082fc..d39772499 100644 --- a/src/s-z/Smi/Smi.Models.cs +++ b/src/s-z/Smi/Smi.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Stochastic Momentum Index (SMI) calculation. +/// +/// The timestamp of the data point. +/// The value of the SMI at this point. +/// The signal line value at this point. [Serializable] public record SmiResult ( @@ -8,5 +14,6 @@ public record SmiResult double? Signal ) : IReusable { + /// public double Value => Smi.Null2NaN(); } diff --git a/src/s-z/Smi/Smi.StaticSeries.cs b/src/s-z/Smi/Smi.StaticSeries.cs index 9d80f0be2..2856f4549 100644 --- a/src/s-z/Smi/Smi.StaticSeries.cs +++ b/src/s-z/Smi/Smi.StaticSeries.cs @@ -1,9 +1,20 @@ namespace Skender.Stock.Indicators; -// STOCHASTIC MOMENTUM INDEX (SERIES) - +/// +/// Provides methods for calculating the Stochastic Momentum Index (SMI) indicator. +/// public static partial class Smi { + /// + /// Converts a list of quotes to SMI results. + /// + /// The type of the quote. + /// The list of quotes. + /// The number of periods for the lookback window. + /// The number of periods for the first smoothing. + /// The number of periods for the second smoothing. + /// The number of periods for the signal line smoothing. + /// A list of SMI results. public static IReadOnlyList ToSmi( this IReadOnlyList quotes, int lookbackPeriods = 13, @@ -18,6 +29,15 @@ public static IReadOnlyList ToSmi( secondSmoothPeriods, signalPeriods); + /// + /// Calculates the SMI for a list of quotes. + /// + /// The list of quotes. + /// The number of periods for the lookback window. + /// The number of periods for the first smoothing. + /// The number of periods for the second smoothing. + /// The number of periods for the signal line smoothing. + /// A list of SMI results. private static List CalcSmi( this IReadOnlyList source, int lookbackPeriods, diff --git a/src/s-z/Smi/Smi.Utilities.cs b/src/s-z/Smi/Smi.Utilities.cs index b22632495..23a0eecfd 100644 --- a/src/s-z/Smi/Smi.Utilities.cs +++ b/src/s-z/Smi/Smi.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// STOCHASTIC MOMENTUM INDEX (UTILITIES) - +/// +/// Provides utility methods for Stochastic Momentum Index (SMI) calculations. +/// public static partial class Smi { - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the results. + /// + /// The list of SMI results. + /// A list of SMI results with the warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -16,7 +20,16 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods + 2 + 100); } - // parameter validation + /// + /// Validates the parameters for the SMI calculation. + /// + /// The number of lookback periods. + /// The number of first smoothing periods. + /// The number of second smoothing periods. + /// The number of signal periods. + /// + /// Thrown when any of the parameters are less than or equal to 0. + /// internal static void Validate( int lookbackPeriods, int firstSmoothPeriods, diff --git a/src/s-z/Smi/info.xml b/src/s-z/Smi/info.xml deleted file mode 100644 index 07d357b24..000000000 --- a/src/s-z/Smi/info.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - Stochastic Momentum Index is a double-smoothed variant of the Stochastic Oscillator on a scale from -100 to 100. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for the Stochastic lookback. - Number of periods in the first smoothing. - Number of periods in the second smoothing. - Number of periods in the EMA of SMI. - Time series of Stochastic Momentum Index values. - Invalid parameter value provided. - - \ No newline at end of file diff --git a/src/s-z/Smma/Smma.Models.cs b/src/s-z/Smma/Smma.Models.cs index 3ea1c2e02..1e6e558a5 100644 --- a/src/s-z/Smma/Smma.Models.cs +++ b/src/s-z/Smma/Smma.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Smoothed Moving Average (SMMA) calculation. +/// +/// The timestamp of the data point. +/// The value of the SMMA at this point. [Serializable] public record SmmaResult ( @@ -7,5 +12,6 @@ public record SmmaResult double? Smma = null ) : IReusable { + /// public double Value => Smma.Null2NaN(); } diff --git a/src/s-z/Smma/Smma.StaticSeries.cs b/src/s-z/Smma/Smma.StaticSeries.cs index e2503ef6c..26d4ac76d 100644 --- a/src/s-z/Smma/Smma.StaticSeries.cs +++ b/src/s-z/Smma/Smma.StaticSeries.cs @@ -1,13 +1,20 @@ namespace Skender.Stock.Indicators; -// SMOOTHED MOVING AVERAGE (SERIES) - +/// +/// Provides methods for calculating the Smoothed Moving Average (SMMA) for a series of data. +/// public static partial class Smma { - public static IReadOnlyList ToSmma( - this IReadOnlyList source, - int lookbackPeriods) - where T : IReusable + /// + /// Calculates the Smoothed Moving Average (SMMA) for a series of data. + /// + /// The type of the elements in the source list, which must implement . + /// The source list of data. + /// The number of periods to look back for the SMMA calculation. + /// A list of containing the SMMA values. + /// Thrown when the source list is null. + /// Thrown when the lookback periods are less than 1. + public static IReadOnlyList ToSmma(this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments ArgumentNullException.ThrowIfNull(source); diff --git a/src/s-z/Smma/Smma.Utilities.cs b/src/s-z/Smma/Smma.Utilities.cs index 5b5336023..e29555514 100644 --- a/src/s-z/Smma/Smma.Utilities.cs +++ b/src/s-z/Smma/Smma.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// SMOOTHED MOVING AVERAGE (UTILITIES) - +/// +/// Provides utility methods for Smoothed Moving Average (SMMA) calculations. +/// public static partial class Smma { - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the results. + /// + /// The list of SMMA results. + /// A list of SMMA results with the warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -16,7 +20,11 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 100); } - // parameter validation + /// + /// Validates the lookback periods parameter. + /// + /// The number of lookback periods to validate. + /// Thrown when the lookback periods are less than or equal to 0. internal static void Validate( int lookbackPeriods) { diff --git a/src/s-z/Smma/info.xml b/src/s-z/Smma/info.xml deleted file mode 100644 index dd3c0204b..000000000 --- a/src/s-z/Smma/info.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - Smoothed Moving Average (SMMA) is the average of price over a lookback window using a smoothing method. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of SMMA values. - Invalid parameter value provided. - - \ No newline at end of file diff --git a/src/s-z/StarcBands/StarcBands.Models.cs b/src/s-z/StarcBands/StarcBands.Models.cs index aafb95c00..9d1a032d2 100644 --- a/src/s-z/StarcBands/StarcBands.Models.cs +++ b/src/s-z/StarcBands/StarcBands.Models.cs @@ -1,5 +1,12 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a STARC Bands calculation. +/// +/// The timestamp of the data point. +/// The value of the upper band at this point. +/// The value of the centerline at this point. +/// The value of the lower band at this point. [Serializable] public record StarcBandsResult ( diff --git a/src/s-z/StarcBands/StarcBands.StaticSeries.cs b/src/s-z/StarcBands/StarcBands.StaticSeries.cs index 3a28837e4..df5a21f4e 100644 --- a/src/s-z/StarcBands/StarcBands.StaticSeries.cs +++ b/src/s-z/StarcBands/StarcBands.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// STARC BANDS (SERIES) - +/// +/// Provides methods for STARC Band indicator calculations. +/// public static partial class StarcBands { + /// + /// Converts a series of quotes to STARC Bands. + /// + /// The type of the quote, which must implement . + /// The source series of quotes. + /// The number of periods for the Simple Moving Average (SMA). + /// The multiplier for the Average True Range (ATR). + /// The number of periods for the ATR calculation. + /// A list of containing the STARC Bands values. public static IReadOnlyList ToStarcBands( this IReadOnlyList quotes, int smaPeriods, @@ -13,6 +23,14 @@ public static IReadOnlyList ToStarcBands( .ToQuoteDList() .CalcStarcBands(smaPeriods, multiplier, atrPeriods); + /// + /// Calculates the STARC Bands for a series of quotes. + /// + /// The source series of quotes. + /// The number of periods for the Simple Moving Average (SMA). + /// The multiplier for the Average True Range (ATR). + /// The number of periods for the ATR calculation. + /// A list of containing the STARC Bands values. private static List CalcStarcBands( this IReadOnlyList source, int smaPeriods, diff --git a/src/s-z/StarcBands/StarcBands.Utilities.cs b/src/s-z/StarcBands/StarcBands.Utilities.cs index c30e4d801..3ce7056e1 100644 --- a/src/s-z/StarcBands/StarcBands.Utilities.cs +++ b/src/s-z/StarcBands/StarcBands.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// STARC BANDS (UTILITIES) - +/// +/// Provides utility methods for STARC Bands calculations. +/// public static partial class StarcBands { - // CONDENSE (REMOVE null results) - /// + /// + /// Removes empty (null) periods from the results. + /// + /// The list of STARC Bands results. + /// A condensed list of STARC Bands results. public static IReadOnlyList Condense( this IReadOnlyList results) { @@ -19,8 +23,11 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the results. + /// + /// The list of STARC Bands results. + /// A list of STARC Bands results with warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -31,7 +38,13 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 150); } - // parameter validation + /// + /// Validates the parameters for STARC Bands calculation. + /// + /// The number of periods for the simple moving average. + /// The multiplier for the ATR. + /// The number of periods for the average true range. + /// Thrown when a parameter is out of range. internal static void Validate( int smaPeriods, double multiplier, diff --git a/src/s-z/StarcBands/info.xml b/src/s-z/StarcBands/info.xml deleted file mode 100644 index 10b45b107..000000000 --- a/src/s-z/StarcBands/info.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Stoller Average Range Channel (STARC) Bands, are based on an SMA centerline and ATR band widths. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for the centerline SMA. - ATR multiplier sets the width of the channel. - Number of periods in the ATR evaluation. - Time series of STARC Bands values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/s-z/Stc/Stc.Models.cs b/src/s-z/Stc/Stc.Models.cs index c60915501..a330e29e2 100644 --- a/src/s-z/Stc/Stc.Models.cs +++ b/src/s-z/Stc/Stc.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Schaff Trend Cycle (STC) calculation. +/// +/// The timestamp of the data point. +/// The value of the STC at this point. [Serializable] public record StcResult ( @@ -7,5 +12,6 @@ public record StcResult double? Stc ) : IReusable { + /// public double Value => Stc.Null2NaN(); } diff --git a/src/s-z/Stc/Stc.StaticSeries.cs b/src/s-z/Stc/Stc.StaticSeries.cs index bb103d5bb..266c69ad9 100644 --- a/src/s-z/Stc/Stc.StaticSeries.cs +++ b/src/s-z/Stc/Stc.StaticSeries.cs @@ -1,9 +1,21 @@ namespace Skender.Stock.Indicators; -// SCHAFF TREND CYCLE (SERIES) - +/// +/// Provides methods for calculating the Schaff Trend Cycle (STC) indicator. +/// public static partial class Stc { + /// + /// Converts a series of values to Schaff Trend Cycle (STC) series. + /// + /// The type of the source values, which must implement . + /// The source series of values. + /// The number of periods for the cycle. + /// The number of fast periods for the MACD calculation. + /// The number of slow periods for the MACD calculation. + /// A list of containing the STC values. + /// Thrown when the source series is null. + /// Thrown when any of the parameters are less than or equal to 0. public static IReadOnlyList ToStc( this IReadOnlyList source, int cyclePeriods = 10, diff --git a/src/s-z/Stc/Stc.Utilities.cs b/src/s-z/Stc/Stc.Utilities.cs index ecc8a0004..49f416000 100644 --- a/src/s-z/Stc/Stc.Utilities.cs +++ b/src/s-z/Stc/Stc.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// SCHAFF TREND CYCLE (UTILITIES) - +/// +/// Provides utility methods for Schaff Trend Cycle (STC) calculations. +/// public static partial class Stc { - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the results. + /// + /// The list of STC results. + /// A list of STC results with warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -16,7 +20,13 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 250); } - // parameter validation + /// + /// Validates the parameters for STC calculation. + /// + /// The number of periods for the cycle. + /// The number of fast periods for the MACD calculation. + /// The number of slow periods for the MACD calculation. + /// Thrown when a parameter is out of range. internal static void Validate( int cyclePeriods, int fastPeriods, diff --git a/src/s-z/Stc/info.xml b/src/s-z/Stc/info.xml deleted file mode 100644 index 1e3ebbd3a..000000000 --- a/src/s-z/Stc/info.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Schaff Trend Cycle is a stochastic oscillator view of two converging/diverging exponential moving averages. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for the Trend Cycle. - Number of periods in the Fast EMA. - Number of periods in the Slow EMA. - Time series of MACD values, including MACD, Signal, and Histogram. - Invalid parameter value provided. - diff --git a/src/s-z/StdDev/StdDev.Models.cs b/src/s-z/StdDev/StdDev.Models.cs index 5d4dbb6ee..0354b1e41 100644 --- a/src/s-z/StdDev/StdDev.Models.cs +++ b/src/s-z/StdDev/StdDev.Models.cs @@ -1,5 +1,12 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Standard Deviation calculation. +/// +/// The timestamp of the data point. +/// The value of the standard deviation at this point. +/// The mean value at this point. +/// The Z-score value at this point. [Serializable] public record StdDevResult ( @@ -9,5 +16,6 @@ public record StdDevResult double? ZScore ) : IReusable { + /// public double Value => StdDev.Null2NaN(); } diff --git a/src/s-z/StdDev/StdDev.StaticSeries.cs b/src/s-z/StdDev/StdDev.StaticSeries.cs index f1a9aba47..5abf2198f 100644 --- a/src/s-z/StdDev/StdDev.StaticSeries.cs +++ b/src/s-z/StdDev/StdDev.StaticSeries.cs @@ -1,12 +1,20 @@ namespace Skender.Stock.Indicators; -// STANDARD DEVIATION (SERIES) - +/// +/// Provides methods for calculating standard deviation on a series of data. +/// public static partial class StdDev { - public static IReadOnlyList ToStdDev( - this IReadOnlyList source, - int lookbackPeriods) + /// + /// Calculates the standard deviation for a series of data. + /// + /// The type of the data series, which must implement IReusable. + /// The source data series. + /// The number of periods to look back for the calculation. + /// A list of StdDevResult containing the standard deviation, mean, and z-score for each data point. + /// Thrown when the source is null. + /// Thrown when lookbackPeriods is less than 1. + public static IReadOnlyList ToStdDev(this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments diff --git a/src/s-z/StdDev/StdDev.Utilities.cs b/src/s-z/StdDev/StdDev.Utilities.cs index bd42630e0..f013f2117 100644 --- a/src/s-z/StdDev/StdDev.Utilities.cs +++ b/src/s-z/StdDev/StdDev.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// STANDARD DEVIATION (UTILITIES) - +/// +/// Provides utility methods for Standard Deviation (StdDev) calculations. +/// public static partial class StdDev { - // parameter validation + /// + /// Validates the lookback periods parameter. + /// + /// The number of lookback periods to validate. + /// Thrown when the lookback periods are less than or equal to 1. internal static void Validate( int lookbackPeriods) { diff --git a/src/s-z/StdDev/info.xml b/src/s-z/StdDev/info.xml deleted file mode 100644 index 71c861cf5..000000000 --- a/src/s-z/StdDev/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Rolling Standard Deviation of price over a lookback window. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of Standard Deviations values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/s-z/StdDevChannels/StdDevChannels.Models.cs b/src/s-z/StdDevChannels/StdDevChannels.Models.cs index 34be76c47..2bc094720 100644 --- a/src/s-z/StdDevChannels/StdDevChannels.Models.cs +++ b/src/s-z/StdDevChannels/StdDevChannels.Models.cs @@ -1,5 +1,13 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Standard Deviation Channels calculation. +/// +/// The timestamp of the data point. +/// The value of the centerline at this point. +/// The value of the upper channel at this point. +/// The value of the lower channel at this point. +/// Indicates if the current point is a breakpoint. [Serializable] public record StdDevChannelsResult ( diff --git a/src/s-z/StdDevChannels/StdDevChannels.StaticSeries.cs b/src/s-z/StdDevChannels/StdDevChannels.StaticSeries.cs index 067c32127..c8ec922f7 100644 --- a/src/s-z/StdDevChannels/StdDevChannels.StaticSeries.cs +++ b/src/s-z/StdDevChannels/StdDevChannels.StaticSeries.cs @@ -1,9 +1,18 @@ namespace Skender.Stock.Indicators; -// STANDARD DEVIATION CHANNELS (SERIES) - +/// +/// Provides methods for calculating Standard Deviation Channels. +/// public static partial class StdDevChannels { + /// + /// Converts a series of quotes to Standard Deviation Channels. + /// + /// The type of the quote, which must implement . + /// The source series of quotes. + /// The number of periods for the lookback. Default is 20. + /// The number of standard deviations for the channel width. Default is 2. + /// A list of containing the Standard Deviation Channels values. public static IReadOnlyList ToStdDevChannels( this IReadOnlyList source, int? lookbackPeriods = 20, diff --git a/src/s-z/StdDevChannels/StdDevChannels.Utilities.cs b/src/s-z/StdDevChannels/StdDevChannels.Utilities.cs index 70d382c48..f8bb8a1a2 100644 --- a/src/s-z/StdDevChannels/StdDevChannels.Utilities.cs +++ b/src/s-z/StdDevChannels/StdDevChannels.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// STANDARD DEVIATION CHANNELS (UTILITIES) - +/// +/// Provides utility methods Standard Deviation Channels calculations. +/// public static partial class StdDevChannels { - // CONDENSE (REMOVE null results) - /// + /// + /// Removes empty (null) periods from the results. + /// + /// The list of results to condense. + /// A condensed list of results. public static IReadOnlyList Condense( this IReadOnlyList results) { @@ -22,8 +26,11 @@ x.UpperChannel is null return resultsList.ToSortedList(); } - // remove recommended periods - /// + /// + /// Removes recommended warmup periods from the results. + /// + /// The list of results to process. + /// A list of results with warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -34,7 +41,12 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } - // parameter validation + /// + /// Validates the parameters for Standard Deviation Channels. + /// + /// The number of lookback periods. + /// The number of standard deviations. + /// Thrown when parameters are out of range. internal static void Validate( int? lookbackPeriods, double stdDeviations) diff --git a/src/s-z/StdDevChannels/info.xml b/src/s-z/StdDevChannels/info.xml deleted file mode 100644 index a5ab722da..000000000 --- a/src/s-z/StdDevChannels/info.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Standard Deviation Channels are based on an linear regression centerline and standard deviations band widths. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Size of the evaluation window. - Width of bands. Number of Standard Deviations from the regression line. - Time series of Standard Deviation Channels values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/s-z/Stoch/Stoch.Models.cs b/src/s-z/Stoch/Stoch.Models.cs index ee9c63f99..d3578a20b 100644 --- a/src/s-z/Stoch/Stoch.Models.cs +++ b/src/s-z/Stoch/Stoch.Models.cs @@ -1,5 +1,12 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Stochastic Oscillator calculation. +/// +/// The timestamp of the data point. +/// The Stochastic Oscillator (%K) at this point. +/// The %D signal at this point. +/// The %J at this point. [Serializable] public record StochResult ( @@ -9,10 +16,26 @@ public record StochResult double? PercentJ ) : IReusable { + /// public double Value => Oscillator.Null2NaN(); // aliases + + /// + /// Gets the value of the Stochastic Oscillator (%K) at this point. + /// It is an alias of the property. + /// public double? K => Oscillator; + + /// + /// Gets the signal line value (%D) at this point. + /// It is an alias of the property. + /// public double? D => Signal; + + /// + /// Gets the value of the %J line at this point. + /// It is an alias of the property. + /// public double? J => PercentJ; } diff --git a/src/s-z/Stoch/Stoch.StaticSeries.cs b/src/s-z/Stoch/Stoch.StaticSeries.cs index b272dfb4a..d99c58f10 100644 --- a/src/s-z/Stoch/Stoch.StaticSeries.cs +++ b/src/s-z/Stoch/Stoch.StaticSeries.cs @@ -1,21 +1,43 @@ namespace Skender.Stock.Indicators; -// STOCHASTIC OSCILLATOR (SERIES) - +/// +/// Provides methods for calculating the Stochastic Oscillator. +/// public static partial class Stoch { + /// + /// Calculates the Stochastic Oscillator for a series of quotes. + /// + /// The type of the quote. + /// The list of quotes. + /// The lookback period for the oscillator. + /// The signal period for the oscillator. + /// The smoothing period for the oscillator. + /// A list of StochResult containing the oscillator values. public static IReadOnlyList ToStoch( - this IReadOnlyList quotes, - int lookbackPeriods = 14, - int signalPeriods = 3, - int smoothPeriods = 3) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcStoch( - lookbackPeriods, - signalPeriods, - smoothPeriods, 3, 2, MaType.SMA); - + this IReadOnlyList quotes, + int lookbackPeriods = 14, + int signalPeriods = 3, + int smoothPeriods = 3) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcStoch( + lookbackPeriods, + signalPeriods, + smoothPeriods, 3, 2, MaType.SMA); + + /// + /// Calculates the Stochastic Oscillator for a series of quotes with specified factors and moving average type. + /// + /// The type of the quote. + /// The list of quotes. + /// The lookback period for the oscillator. + /// The signal period for the oscillator. + /// The smoothing period for the oscillator. + /// The factor for the %K line. + /// The factor for the %D line. + /// The type of moving average to use. + /// A list of StochResult containing the oscillator values. public static IReadOnlyList ToStoch( this IReadOnlyList quotes, int lookbackPeriods, @@ -34,6 +56,17 @@ public static IReadOnlyList ToStoch( dFactor, movingAverageType); + /// + /// Calculates the Stochastic Oscillator for a series of quotes. + /// + /// The list of quotes. + /// The lookback period for the oscillator. + /// The signal period for the oscillator. + /// The smoothing period for the oscillator. + /// The factor for the %K line. + /// The factor for the %D line. + /// The type of moving average to use. + /// A list of StochResult containing the oscillator values. internal static List CalcStoch( this IReadOnlyList source, int lookbackPeriods, diff --git a/src/s-z/Stoch/Stoch.Utilities.cs b/src/s-z/Stoch/Stoch.Utilities.cs index bb1fcc708..f7ab5add2 100644 --- a/src/s-z/Stoch/Stoch.Utilities.cs +++ b/src/s-z/Stoch/Stoch.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// STOCHASTIC OSCILLATOR (UTILITIES) - +/// +/// Provides utility methods for the Stochastic Oscillator. +/// public static partial class Stoch { - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the results. + /// + /// The list of Stochastic Oscillator results. + /// A list of Stochastic Oscillator results with warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -16,7 +20,16 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } - // parameter validation + /// + /// Validates the parameters for Stochastic Oscillator calculation. + /// + /// The number of periods for the lookback. + /// The number of periods for the signal line. + /// The number of periods for smoothing. + /// The K factor for the Stochastic calculation. + /// The D factor for the Stochastic calculation. + /// The type of moving average to use. + /// Thrown when a parameter is out of range. internal static void Validate( int lookbackPeriods, int signalPeriods, diff --git a/src/s-z/Stoch/info.xml b/src/s-z/Stoch/info.xml deleted file mode 100644 index 00d8ae563..000000000 --- a/src/s-z/Stoch/info.xml +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - Stochastic Oscillator is a momentum indicator that looks back N periods to produce a scale of 0 to 100. - %J is also included for the KDJ Index extension. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for the Oscillator. - Smoothing period for the %D signal line. - Smoothing period for the %K Oscillator. Use 3 for Slow or 1 for Fast. - Time series of Stochastic Oscillator values. - Invalid parameter value provided. - - - - - Stochastic Oscillator is a momentum indicator that looks back N periods to produce a scale of 0 to 100. - %J is also included for the KDJ Index extension. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for the Oscillator. - Smoothing period for the %D signal line. - Smoothing period for the %K Oscillator. Use 3 for Slow or 1 for Fast. - Weight of %K in the %J calculation. Default is 3. - Weight of %K in the %J calculation. Default is 2. - Type of moving average to use. Default is MaType.SMA. See docs for instructions and options. - Time series of Stochastic Oscillator values. - Invalid parameter value provided. - - - - - Stochastic indicator results includes aliases for those who prefer the simpler K,D,J outputs. - - See - documentation - for more information. - - - - Standard output properties: - - - Oscillator - %K Oscillator over prior lookback periods. - - - Signal - %D Simple moving average of %K Oscillator. - - - PercentJ - - %J is the weighted divergence of %K and %D: %J=3×%K-2×%D - - - - These are the aliases of the above properties: - - - K - Same as Oscillator. - - - D - Same as Signal. - - - J - Same as PercentJ. - - - - - - diff --git a/src/s-z/StochRsi/StochRsi.Models.cs b/src/s-z/StochRsi/StochRsi.Models.cs index f365f5748..56b14e965 100644 --- a/src/s-z/StochRsi/StochRsi.Models.cs +++ b/src/s-z/StochRsi/StochRsi.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Stochastic RSI calculation. +/// +/// The timestamp of the data point. +/// The value of the Stochastic RSI at this point. +/// The signal line value at this point. [Serializable] public record StochRsiResult ( @@ -8,5 +14,6 @@ public record StochRsiResult double? Signal = null ) : IReusable { + /// public double Value => StochRsi.Null2NaN(); } diff --git a/src/s-z/StochRsi/StochRsi.StaticSeries.cs b/src/s-z/StochRsi/StochRsi.StaticSeries.cs index 85837f5bf..db57b53af 100644 --- a/src/s-z/StochRsi/StochRsi.StaticSeries.cs +++ b/src/s-z/StochRsi/StochRsi.StaticSeries.cs @@ -1,9 +1,21 @@ namespace Skender.Stock.Indicators; -// STOCHASTIC RSI (SERIES) - +/// +/// Provides methods for Stochastic RSI (Relative Strength Index) calculations. +/// public static partial class StochRsi { + /// + /// Converts a source list to a list of StochRsiResult. + /// + /// The type of the source list elements. + /// The source list. + /// The number of periods for RSI calculation. + /// The number of periods for Stochastic calculation. + /// The number of periods for the signal line. + /// The number of periods for smoothing (default is 1). + /// A list of StochRsiResult. + /// Thrown when the source list is null. public static IReadOnlyList ToStochRsi( this IReadOnlyList source, int rsiPeriods, diff --git a/src/s-z/StochRsi/StochRsi.Utilities.cs b/src/s-z/StochRsi/StochRsi.Utilities.cs index d7d71e061..dfcfea2fb 100644 --- a/src/s-z/StochRsi/StochRsi.Utilities.cs +++ b/src/s-z/StochRsi/StochRsi.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// STOCHASTIC RSI (UTILITIES) - +/// +/// Provides utility methods for the Stochastic RSI indicator. +/// public static partial class StochRsi { - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the results. + /// + /// The list of Stochastic RSI results. + /// A list of Stochastic RSI results with warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -16,7 +20,14 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 100); } - // parameter validation + /// + /// Validates the parameters for Stochastic RSI calculation. + /// + /// The number of periods for the RSI calculation. + /// The number of periods for the Stochastic calculation. + /// The number of periods for the signal line. + /// The number of periods for smoothing. + /// Thrown when a parameter is out of range. internal static void Validate( int rsiPeriods, int stochPeriods, diff --git a/src/s-z/StochRsi/info.xml b/src/s-z/StochRsi/info.xml deleted file mode 100644 index 9edb4cd4a..000000000 --- a/src/s-z/StochRsi/info.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - Stochastic RSI is a Stochastic interpretation of the Relative Strength Index. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for the RSI. - Number of periods for the Stochastic. - Number of periods for the Stochastic RSI SMA signal line. - Number of periods for Stochastic Smoothing. Use 1 for Fast or 3 for Slow. - Time series of Stochastic RSI values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/s-z/SuperTrend/SuperTrend.Models.cs b/src/s-z/SuperTrend/SuperTrend.Models.cs index f50d8dceb..d0cba4311 100644 --- a/src/s-z/SuperTrend/SuperTrend.Models.cs +++ b/src/s-z/SuperTrend/SuperTrend.Models.cs @@ -1,5 +1,12 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a SuperTrend indicator calculation. +/// +/// The timestamp of the data point. +/// The value of the SuperTrend at this point. +/// The upper band value at this point. +/// The lower band value at this point. [Serializable] public record SuperTrendResult ( diff --git a/src/s-z/SuperTrend/SuperTrend.StaticSeries.cs b/src/s-z/SuperTrend/SuperTrend.StaticSeries.cs index 98e6fbee6..b84ab9717 100644 --- a/src/s-z/SuperTrend/SuperTrend.StaticSeries.cs +++ b/src/s-z/SuperTrend/SuperTrend.StaticSeries.cs @@ -1,9 +1,18 @@ namespace Skender.Stock.Indicators; -// SUPERTREND (SERIES) - +/// +/// SuperTrend indicator calculation. +/// public static partial class SuperTrend { + /// + /// Converts a list of quotes to a list of SuperTrend results. + /// + /// The type of the quote. + /// The list of quotes. + /// The number of lookback periods. + /// The multiplier for the ATR. + /// A list of SuperTrend results. public static IReadOnlyList ToSuperTrend( this IReadOnlyList quotes, int lookbackPeriods = 10, @@ -12,6 +21,13 @@ public static IReadOnlyList ToSuperTrend( .ToQuoteDList() .CalcSuperTrend(lookbackPeriods, multiplier); + /// + /// Calculates the SuperTrend indicator. + /// + /// The list of quotes. + /// The number of lookback periods. + /// The multiplier for the ATR. + /// A list of SuperTrend results. private static List CalcSuperTrend( this IReadOnlyList source, int lookbackPeriods, @@ -49,7 +65,6 @@ private static List CalcSuperTrend( double? lowerEval = mid - multiplier * atr; // initial values - // TODO: update healing, without requiring specific indexing if (i == lookbackPeriods) { isBullish = q.Close >= mid; diff --git a/src/s-z/SuperTrend/SuperTrend.Utilities.cs b/src/s-z/SuperTrend/SuperTrend.Utilities.cs index 052d097b9..7bfbc1581 100644 --- a/src/s-z/SuperTrend/SuperTrend.Utilities.cs +++ b/src/s-z/SuperTrend/SuperTrend.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// SUPERTREND (UTILITIES) - +/// +/// Provides utility methods for the SuperTrend indicator. +/// public static partial class SuperTrend { - // CONDENSE (REMOVE null results) - /// + /// + /// Removes empty (null) periods from the results. + /// + /// The list of SuperTrend results. + /// A condensed list of SuperTrend results without null periods. public static IReadOnlyList Condense( this IReadOnlyList results) { @@ -19,8 +23,11 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the results. + /// + /// The list of SuperTrend results. + /// A list of SuperTrend results with warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -31,7 +38,12 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } - // parameter validation + /// + /// Validates the parameters for SuperTrend calculation. + /// + /// The number of periods for the lookback. + /// The multiplier for the SuperTrend calculation. + /// Thrown when a parameter is out of range. internal static void Validate( int lookbackPeriods, double multiplier) diff --git a/src/s-z/SuperTrend/info.xml b/src/s-z/SuperTrend/info.xml deleted file mode 100644 index dfbf19073..000000000 --- a/src/s-z/SuperTrend/info.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - SuperTrend attempts to determine the primary trend of prices by using - Average True Range (ATR) band thresholds around an HL2 midline. It can indicate a buy/sell signal or a - trailing stop when the trend changes. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for ATR. - Multiplier sets the ATR band width. - Time series of SuperTrend values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/s-z/T3/T3.Models.cs b/src/s-z/T3/T3.Models.cs index de026e889..7859b91a9 100644 --- a/src/s-z/T3/T3.Models.cs +++ b/src/s-z/T3/T3.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a T3 indicator calculation. +/// +/// The timestamp of the data point. +/// The value of the T3 indicator at this point. [Serializable] public record T3Result ( @@ -7,5 +12,6 @@ public record T3Result double? T3 ) : IReusable { + /// public double Value => T3.Null2NaN(); } diff --git a/src/s-z/T3/T3.StaticSeries.cs b/src/s-z/T3/T3.StaticSeries.cs index a85bf64ac..c11f21d83 100644 --- a/src/s-z/T3/T3.StaticSeries.cs +++ b/src/s-z/T3/T3.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// TILLSON T3 MOVING AVERAGE (SERIES) - +/// +/// Provides methods for calculating the T3 moving average indicator. +/// public static partial class T3 { + /// + /// Calculates the T3 moving average for a series of data. + /// + /// The type of the elements in the source list, which must implement IReusable. + /// The source list of data. + /// The number of lookback periods. Default is 5. + /// The volume factor. Default is 0.7. + /// A list of T3Result containing the T3 moving average values. + /// Thrown when the source is null. public static IReadOnlyList ToT3( this IReadOnlyList source, int lookbackPeriods = 5, diff --git a/src/s-z/T3/T3.Utilities.cs b/src/s-z/T3/T3.Utilities.cs index 70c4264aa..402077a06 100644 --- a/src/s-z/T3/T3.Utilities.cs +++ b/src/s-z/T3/T3.Utilities.cs @@ -1,10 +1,16 @@ namespace Skender.Stock.Indicators; -// TILLSON T3 MOVING AVERAGE (UTILITIES) - +/// +/// Provides utility methods for the T3 moving average indicator. +/// public static partial class T3 { - // parameter validation + /// + /// Validates the parameters for the T3 calculation. + /// + /// The number of lookback periods. + /// The volume factor. + /// Thrown when the lookback periods or volume factor are less than or equal to 0. internal static void Validate( int lookbackPeriods, double volumeFactor) diff --git a/src/s-z/T3/info.xml b/src/s-z/T3/info.xml deleted file mode 100644 index 65e385b1e..000000000 --- a/src/s-z/T3/info.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Tillson T3 is a smooth moving average that reduces both lag and overshooting. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for the EMA smoothing. - Size of the Volume Factor. - Time series of T3 values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/s-z/Tema/Tema.Models.cs b/src/s-z/Tema/Tema.Models.cs index 825eecffe..d51821acb 100644 --- a/src/s-z/Tema/Tema.Models.cs +++ b/src/s-z/Tema/Tema.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Triple Exponential Moving Average (TEMA) calculation. +/// +/// The timestamp of the data point. +/// The value of the TEMA at this point. [Serializable] public record TemaResult ( @@ -7,5 +12,6 @@ public record TemaResult double? Tema = null ) : IReusable { + /// public double Value => Tema.Null2NaN(); } diff --git a/src/s-z/Tema/Tema.StaticSeries.cs b/src/s-z/Tema/Tema.StaticSeries.cs index f14a28512..de422bfb2 100644 --- a/src/s-z/Tema/Tema.StaticSeries.cs +++ b/src/s-z/Tema/Tema.StaticSeries.cs @@ -1,10 +1,18 @@ namespace Skender.Stock.Indicators; -// TRIPLE EXPONENTIAL MOVING AVERAGE (SERIES) - +/// +/// Provides methods for calculating the Triple Exponential Moving Average (TEMA) series. +/// public static partial class Tema { - // calculate series + /// + /// Calculates the TEMA series for the given source data. + /// + /// The type of the source data, which must implement IReusable. + /// The source data to calculate the TEMA for. + /// The number of lookback periods for the TEMA calculation. + /// A read-only list of TEMA results. + /// Thrown when the source data is null. public static IReadOnlyList ToTema( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/s-z/Tema/Tema.Utilities.cs b/src/s-z/Tema/Tema.Utilities.cs index 963479cb1..d497fe72e 100644 --- a/src/s-z/Tema/Tema.Utilities.cs +++ b/src/s-z/Tema/Tema.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// TRIPLE EXPONENTIAL MOVING AVERAGE (UTILITIES) - +/// +/// Provides utility methods for the Triple Exponential Moving Average (TEMA) indicator. +/// public static partial class Tema { - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the results. + /// + /// The list of TEMA results. + /// A list of TEMA results with warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -16,7 +20,11 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(3 * n + 100); } - // parameter validation + /// + /// Validates the parameters for TEMA calculation. + /// + /// The number of periods for the lookback. + /// Thrown when a parameter is out of range. internal static void Validate( int lookbackPeriods) { diff --git a/src/s-z/Tema/info.xml b/src/s-z/Tema/info.xml deleted file mode 100644 index a7ef91568..000000000 --- a/src/s-z/Tema/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Triple Exponential Moving Average (TEMA) of the price. Note: TEMA is often confused with the alternative TRIX oscillator. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of Triple EMA values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/s-z/Tr/Tr.Models.cs b/src/s-z/Tr/Tr.Models.cs index 49dff2582..849450e72 100644 --- a/src/s-z/Tr/Tr.Models.cs +++ b/src/s-z/Tr/Tr.Models.cs @@ -1,10 +1,16 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a True Range (TR) calculation. +/// +/// The timestamp of the data point. +/// The value of the True Range at this point. [Serializable] public record TrResult( DateTime Timestamp, double? Tr ) : IReusable { + /// public double Value => Tr.Null2NaN(); } diff --git a/src/s-z/Tr/Tr.StaticSeries.cs b/src/s-z/Tr/Tr.StaticSeries.cs index 79fa0dbaf..738b93000 100644 --- a/src/s-z/Tr/Tr.StaticSeries.cs +++ b/src/s-z/Tr/Tr.StaticSeries.cs @@ -1,15 +1,27 @@ namespace Skender.Stock.Indicators; -// TRUE RANGE (SERIES) - +/// +/// Provides methods for calculating True Range (TR) from a list of quotes. +/// public static partial class Tr { + /// + /// Converts a list of quotes to a list of True Range (TR) results. + /// + /// The type of quote. + /// The list of quotes. + /// A list of True Range (TR) results. public static IReadOnlyList ToTr( this IReadOnlyList quotes) where TQuote : IQuote => quotes .ToQuoteDList() .CalcTr(); + /// + /// Calculates the True Range (TR) for a list of quotes. + /// + /// The list of quotes. + /// A list of True Range (TR) results. private static List CalcTr( this IReadOnlyList source) { diff --git a/src/s-z/Tr/Tr.StreamHub.cs b/src/s-z/Tr/Tr.StreamHub.cs index ea0803e9b..b6a5f6132 100644 --- a/src/s-z/Tr/Tr.StreamHub.cs +++ b/src/s-z/Tr/Tr.StreamHub.cs @@ -6,6 +6,12 @@ namespace Skender.Stock.Indicators; public static partial class Tr { + /// + /// Converts a quote provider to a True Range (TR) hub. + /// + /// The type of quote. + /// The quote provider. + /// A True Range (TR) hub. public static TrHub ToTr( this IQuoteProvider quoteProvider) where TIn : IQuote @@ -13,6 +19,10 @@ public static TrHub ToTr( } #endregion +/// +/// Represents a True Range (TR) hub for streaming data. +/// +/// The type of quote. public class TrHub : ChainProvider where TIn : IQuote @@ -21,6 +31,10 @@ public class TrHub private const string hubName = "TRUE RANGE"; + /// + /// Initializes a new instance of the class. + /// + /// The quote provider. internal TrHub(IQuoteProvider provider) : base(provider) { @@ -30,8 +44,10 @@ internal TrHub(IQuoteProvider provider) // METHODS + /// public override string ToString() => hubName; + /// protected override (TrResult result, int index) ToIndicator(TIn item, int? indexHint) { diff --git a/src/s-z/Tr/Tr.Utilities.cs b/src/s-z/Tr/Tr.Utilities.cs index d6f9f4d09..cb2360de3 100644 --- a/src/s-z/Tr/Tr.Utilities.cs +++ b/src/s-z/Tr/Tr.Utilities.cs @@ -1,9 +1,17 @@ namespace Skender.Stock.Indicators; -// TRUE RANGE (UTILITIES) - +/// +/// Provides utility methods for calculating True Range (TR). +/// public static partial class Tr { + /// + /// Calculates the True Range increment. + /// + /// The high price. + /// The low price. + /// The previous close price. + /// The True Range increment. public static double Increment( double high, double low, diff --git a/src/s-z/Tr/info.xml b/src/s-z/Tr/info.xml deleted file mode 100644 index cacf17b58..000000000 --- a/src/s-z/Tr/info.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - True Range (TR) is a measure of volatility that captures gaps and limits between periods. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Time series of True Range (TR) values. - Invalid parameter value provided. - - - Get the next incremental True Range (TR) value. - - See - documentation - for more information. - - Last Close price, from prior period. - High price, current period. - Low price, current period. - New TR value. - - \ No newline at end of file diff --git a/src/s-z/Trix/Trix.Models.cs b/src/s-z/Trix/Trix.Models.cs index ad12f3937..1fae80466 100644 --- a/src/s-z/Trix/Trix.Models.cs +++ b/src/s-z/Trix/Trix.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a TRIX indicator calculation. +/// +/// The timestamp of the data point. +/// The value of the triple-smoothed EMA at this point. +/// The value of the TRIX indicator at this point. [Serializable] public record TrixResult ( @@ -8,5 +14,6 @@ public record TrixResult double? Trix = null ) : IReusable { + /// public double Value => Trix.Null2NaN(); } diff --git a/src/s-z/Trix/Trix.StaticSeries.cs b/src/s-z/Trix/Trix.StaticSeries.cs index a94818133..fb5f86c6a 100644 --- a/src/s-z/Trix/Trix.StaticSeries.cs +++ b/src/s-z/Trix/Trix.StaticSeries.cs @@ -1,12 +1,19 @@ namespace Skender.Stock.Indicators; -// TRIPLE EMA OSCILLATOR - TRIX (SERIES) - +/// +/// Provides methods for calculating the Triple Exponential Moving Average (TRIX) oscillator. +/// public static partial class Trix { - public static IReadOnlyList ToTrix( - this IReadOnlyList source, - int lookbackPeriods) + /// + /// Converts a source list to a list of TRIX results. + /// + /// The type of the source elements, which must implement . + /// The source list of elements. + /// The number of lookback periods for the TRIX calculation. + /// A read-only list of . + /// Thrown when the source list is null. + public static IReadOnlyList ToTrix(this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments diff --git a/src/s-z/Trix/Trix.Utilities.cs b/src/s-z/Trix/Trix.Utilities.cs index ce869af82..268b89d7b 100644 --- a/src/s-z/Trix/Trix.Utilities.cs +++ b/src/s-z/Trix/Trix.Utilities.cs @@ -1,11 +1,15 @@ namespace Skender.Stock.Indicators; -// TRIPLE EMA OSCILLATOR - TRIX (UTILITIES) - +/// +/// Provides utility methods for the Triple EMA Oscillator (TRIX) indicator. +/// public static partial class Trix { - // remove recommended periods - /// + /// + /// Removes the recommended warmup periods from the TRIX results. + /// + /// The list of TRIX results. + /// A list of TRIX results with the warmup periods removed. public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) { @@ -16,7 +20,11 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(3 * n + 100); } - // parameter validation + /// + /// Validates the parameters for the TRIX calculation. + /// + /// The number of lookback periods. + /// Thrown when the lookback periods are less than or equal to 0. internal static void Validate( int lookbackPeriods) { diff --git a/src/s-z/Trix/info.xml b/src/s-z/Trix/info.xml deleted file mode 100644 index 00f9697cb..000000000 --- a/src/s-z/Trix/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Triple EMA Oscillator (TRIX) is the rate of change for a 3 EMA smoothing of the price over a lookback window. TRIX is often confused with TEMA. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of TRIX values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/s-z/Tsi/Tsi.Models.cs b/src/s-z/Tsi/Tsi.Models.cs index 35a2caf2c..be8e360b6 100644 --- a/src/s-z/Tsi/Tsi.Models.cs +++ b/src/s-z/Tsi/Tsi.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a True Strength Index (TSI) calculation. +/// +/// The timestamp of the data point. +/// The value of the TSI at this point. +/// The signal line value at this point. [Serializable] public record TsiResult ( @@ -8,5 +14,6 @@ public record TsiResult double? Signal = null ) : IReusable { + /// public double Value => Tsi.Null2NaN(); } diff --git a/src/s-z/Tsi/Tsi.StaticSeries.cs b/src/s-z/Tsi/Tsi.StaticSeries.cs index 578f6ab88..28dc8f920 100644 --- a/src/s-z/Tsi/Tsi.StaticSeries.cs +++ b/src/s-z/Tsi/Tsi.StaticSeries.cs @@ -1,9 +1,20 @@ namespace Skender.Stock.Indicators; -// TRUE STRENGTH INDEX (SERIES) - +/// +/// Provides methods for calculating the True Strength Index (TSI) indicator. +/// public static partial class Tsi { + /// + /// Calculates the True Strength Index (TSI) for a given source of data. + /// + /// The type of the elements in the source list, which must implement IReusable. + /// The source list of data. + /// The number of periods for the lookback calculation. Default is 25. + /// The number of periods for the smoothing calculation. Default is 13. + /// The number of periods for the signal calculation. Default is 7. + /// A list of TsiResult containing the TSI and signal values. + /// Thrown when the source is null. public static IReadOnlyList ToTsi( this IReadOnlyList source, int lookbackPeriods = 25, diff --git a/src/s-z/Tsi/Tsi.Utilities.cs b/src/s-z/Tsi/Tsi.Utilities.cs index 16b08c8ef..e2e1dd8c1 100644 --- a/src/s-z/Tsi/Tsi.Utilities.cs +++ b/src/s-z/Tsi/Tsi.Utilities.cs @@ -1,10 +1,16 @@ namespace Skender.Stock.Indicators; -// TRUE STRENGTH INDEX (UTILITIES) - +/// +/// Provides utility methods for calculating the True Strength Index (TSI). +/// public static partial class Tsi { // remove recommended periods + /// + /// Removes the warmup periods from the TSI results. + /// + /// The TSI results. + /// The TSI results without the warmup periods. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) @@ -17,6 +23,13 @@ public static IReadOnlyList RemoveWarmupPeriods( } // parameter validation + /// + /// Validates the parameters for the TSI calculation. + /// + /// The lookback periods. + /// The smoothing periods. + /// The signal periods. + /// Thrown when any of the parameters are out of range. internal static void Validate( int lookbackPeriods, int smoothPeriods, diff --git a/src/s-z/Tsi/info.xml b/src/s-z/Tsi/info.xml deleted file mode 100644 index 26c995001..000000000 --- a/src/s-z/Tsi/info.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - True Strength Index (TSI) is a momentum oscillator that depicts trends in price changes. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods for the first EMA. - Number of periods in the second smoothing. - Number of periods in the TSI SMA signal line. - Time series of TSI values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/s-z/UlcerIndex/UlcerIndex.Models.cs b/src/s-z/UlcerIndex/UlcerIndex.Models.cs index 72332f6a0..cc1fe8469 100644 --- a/src/s-z/UlcerIndex/UlcerIndex.Models.cs +++ b/src/s-z/UlcerIndex/UlcerIndex.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of an Ulcer Index calculation. +/// +/// The timestamp of the data point. +/// The value of the Ulcer Index at this point. [Serializable] public record UlcerIndexResult ( @@ -7,8 +12,12 @@ public record UlcerIndexResult double? UlcerIndex ) : IReusable { + /// public double Value => UlcerIndex.Null2NaN(); + /// + /// Gets the value of the Ulcer Index. + /// [Obsolete("Rename UI to UlcerIndex")] // v3.0.0 public double? UI => UlcerIndex; } diff --git a/src/s-z/UlcerIndex/UlcerIndex.StaticSeries.cs b/src/s-z/UlcerIndex/UlcerIndex.StaticSeries.cs index 29f7a3bc3..741cc6255 100644 --- a/src/s-z/UlcerIndex/UlcerIndex.StaticSeries.cs +++ b/src/s-z/UlcerIndex/UlcerIndex.StaticSeries.cs @@ -1,9 +1,18 @@ namespace Skender.Stock.Indicators; -// ULCER INDEX (SERIES) - +/// +/// Provides methods for calculating the Ulcer Index indicator. +/// public static partial class UlcerIndex { + /// + /// Calculates the Ulcer Index for a series of data. + /// + /// The type of the elements in the source list, which must implement IReusable. + /// The source list of data. + /// The number of lookback periods. Default is 14. + /// A list of UlcerIndexResult containing the Ulcer Index values. + /// Thrown when the source is null. public static IReadOnlyList ToUlcerIndex( this IReadOnlyList source, int lookbackPeriods = 14) diff --git a/src/s-z/UlcerIndex/UlcerIndex.Utilities.cs b/src/s-z/UlcerIndex/UlcerIndex.Utilities.cs index 26679c7a6..456e372d2 100644 --- a/src/s-z/UlcerIndex/UlcerIndex.Utilities.cs +++ b/src/s-z/UlcerIndex/UlcerIndex.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// ULCER INDEX (UTILITIES) - +/// +/// Provides utility methods for the Ulcer Index indicator. +/// public static partial class UlcerIndex { - // parameter validation + /// + /// Validates the parameters for the Ulcer Index calculation. + /// + /// The number of lookback periods. + /// Thrown when the lookback periods are less than or equal to 0. internal static void Validate( int lookbackPeriods) { diff --git a/src/s-z/UlcerIndex/info.xml b/src/s-z/UlcerIndex/info.xml deleted file mode 100644 index 93532e732..000000000 --- a/src/s-z/UlcerIndex/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Ulcer Index (UI) is a measure of downside price volatility over a lookback window. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of Ulcer Index values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/s-z/Ultimate/Ultimate.Models.cs b/src/s-z/Ultimate/Ultimate.Models.cs index b8b82ba3f..eedfc23bb 100644 --- a/src/s-z/Ultimate/Ultimate.Models.cs +++ b/src/s-z/Ultimate/Ultimate.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of an Ultimate Oscillator calculation. +/// +/// The timestamp of the data point. +/// The value of the Ultimate Oscillator at this point. [Serializable] public record UltimateResult ( @@ -7,5 +12,6 @@ public record UltimateResult double? Ultimate ) : IReusable { + /// public double Value => Ultimate.Null2NaN(); } diff --git a/src/s-z/Ultimate/Ultimate.StaticSeries.cs b/src/s-z/Ultimate/Ultimate.StaticSeries.cs index a4978492b..d07ce18ba 100644 --- a/src/s-z/Ultimate/Ultimate.StaticSeries.cs +++ b/src/s-z/Ultimate/Ultimate.StaticSeries.cs @@ -1,9 +1,20 @@ namespace Skender.Stock.Indicators; -// ULTIMATE OSCILLATOR (SERIES) - +/// +/// Provides methods for calculating the Ultimate Oscillator indicator. +/// public static partial class Ultimate { + /// + /// Calculates the Ultimate Oscillator for a series of quotes. + /// + /// The type of the elements in the source list, which must implement IQuote. + /// The source list of quotes. + /// The number of short lookback periods. Default is 7. + /// The number of middle lookback periods. Default is 14. + /// The number of long lookback periods. Default is 28. + /// A list of UltimateResult containing the Ultimate Oscillator values. + /// Thrown when the source is null. public static IReadOnlyList ToUltimate( this IReadOnlyList quotes, int shortPeriods = 7, @@ -13,6 +24,14 @@ public static IReadOnlyList ToUltimate( .ToQuoteDList() .CalcUltimate(shortPeriods, middlePeriods, longPeriods); + /// + /// Calculates the Ultimate Oscillator for a series of quotes. + /// + /// The source list of quotes. + /// The number of short lookback periods. + /// The number of middle lookback periods. + /// The number of long lookback periods. + /// A list of UltimateResult containing the Ultimate Oscillator values. private static List CalcUltimate( this IReadOnlyList source, int shortPeriods, diff --git a/src/s-z/Ultimate/Ultimate.Utilities.cs b/src/s-z/Ultimate/Ultimate.Utilities.cs index 63c6da0ff..c30337dba 100644 --- a/src/s-z/Ultimate/Ultimate.Utilities.cs +++ b/src/s-z/Ultimate/Ultimate.Utilities.cs @@ -1,10 +1,20 @@ namespace Skender.Stock.Indicators; -// ULTIMATE OSCILLATOR (UTILITIES) - +/// +/// Provides utility methods for the Ultimate Oscillator indicator. +/// public static partial class Ultimate { // parameter validation + /// + /// Validates the parameters for the Ultimate Oscillator calculation. + /// + /// The number of short lookback periods. + /// The number of middle lookback periods. + /// The number of long lookback periods. + /// + /// Thrown when any of the periods are less than or equal to 0, or when the periods are not in increasing order. + /// internal static void Validate( int shortPeriods, int middleAverage, diff --git a/src/s-z/Ultimate/info.xml b/src/s-z/Ultimate/info.xml deleted file mode 100644 index bf1b054ca..000000000 --- a/src/s-z/Ultimate/info.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Ultimate Oscillator uses several lookback periods to weigh buying power against True Range price to produce on oversold / overbought oscillator. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the smallest window. - Number of periods in the middle-sized window. - Number of periods in the largest window. - Time series of Ultimate Oscillator values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/s-z/VolatilityStop/VolatilityStop.Models.cs b/src/s-z/VolatilityStop/VolatilityStop.Models.cs index 2c729a914..36128a4d5 100644 --- a/src/s-z/VolatilityStop/VolatilityStop.Models.cs +++ b/src/s-z/VolatilityStop/VolatilityStop.Models.cs @@ -1,5 +1,13 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Volatility Stop indicator calculation. +/// +/// The timestamp of the data point. +/// The stop and reverse (SAR) value at this point. +/// Indicates if the current point is a stop. +/// The upper band value at this point. +/// The lower band value at this point. [Serializable] public record VolatilityStopResult ( @@ -13,5 +21,6 @@ public record VolatilityStopResult ) : IReusable { + /// public double Value => Sar.Null2NaN(); } diff --git a/src/s-z/VolatilityStop/VolatilityStop.StaticSeries.cs b/src/s-z/VolatilityStop/VolatilityStop.StaticSeries.cs index 6693ca1a4..4dc7fd302 100644 --- a/src/s-z/VolatilityStop/VolatilityStop.StaticSeries.cs +++ b/src/s-z/VolatilityStop/VolatilityStop.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// VOLATILITY SYSTEM/STOP (SERIES) - +/// +/// Provides methods for calculating the Volatility Stop indicator. +/// public static partial class VolatilityStop { + /// + /// Calculates the Volatility Stop for a series of quotes. + /// + /// The type of the elements in the source list, which must implement IQuote. + /// The source list of quotes. + /// The number of lookback periods. Default is 7. + /// The multiplier for the Average True Range. Default is 3. + /// A list of VolatilityStopResult containing the Volatility Stop values. + /// Thrown when the source is null. public static IReadOnlyList ToVolatilityStop( this IReadOnlyList quotes, int lookbackPeriods = 7, @@ -12,6 +22,13 @@ public static IReadOnlyList ToVolatilityStop( .ToQuoteDList() .CalcVolatilityStop(lookbackPeriods, multiplier); + /// + /// Calculates the Volatility Stop for a series of quotes. + /// + /// The source list of quotes. + /// The number of lookback periods. + /// The multiplier for the Average True Range. + /// A list of VolatilityStopResult containing the Volatility Stop values. private static List CalcVolatilityStop( this IReadOnlyList source, int lookbackPeriods, diff --git a/src/s-z/VolatilityStop/VolatilityStop.Utilities.cs b/src/s-z/VolatilityStop/VolatilityStop.Utilities.cs index ec65d0404..577abc846 100644 --- a/src/s-z/VolatilityStop/VolatilityStop.Utilities.cs +++ b/src/s-z/VolatilityStop/VolatilityStop.Utilities.cs @@ -1,10 +1,16 @@ namespace Skender.Stock.Indicators; -// VOLATILITY SYSTEM/STOP (UTILITIES) - +/// +/// Provides utility methods for the Volatility Stop indicator. +/// public static partial class VolatilityStop { // remove recommended periods + /// + /// Removes the warmup periods from the Volatility Stop results. + /// + /// The list of Volatility Stop results. + /// A list of Volatility Stop results with the warmup periods removed. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) @@ -19,6 +25,14 @@ public static IReadOnlyList RemoveWarmupPeriods( } // parameter validation + /// + /// Validates the parameters for the Volatility Stop calculation. + /// + /// The number of lookback periods. + /// The multiplier for the Average True Range. + /// + /// Thrown when the lookback periods are less than or equal to 1, or when the multiplier is less than or equal to 0. + /// internal static void Validate( int lookbackPeriods, double multiplier) diff --git a/src/s-z/VolatilityStop/info.xml b/src/s-z/VolatilityStop/info.xml deleted file mode 100644 index 98cbd33c4..000000000 --- a/src/s-z/VolatilityStop/info.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Volatility Stop is an ATR based indicator used to determine trend direction, stops, and reversals. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - ATR offset amount. - Time series of Volatility Stop values. - Invalid parameter value provided. - diff --git a/src/s-z/Vortex/Vortex.Models.cs b/src/s-z/Vortex/Vortex.Models.cs index 29fa3cf0a..097b0a5c8 100644 --- a/src/s-z/Vortex/Vortex.Models.cs +++ b/src/s-z/Vortex/Vortex.Models.cs @@ -1,5 +1,11 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Vortex indicator calculation. +/// +/// The timestamp of the data point. +/// The positive vortex indicator value at this point. +/// The negative vortex indicator value at this point. [Serializable] public record VortexResult ( diff --git a/src/s-z/Vortex/Vortex.StaticSeries.cs b/src/s-z/Vortex/Vortex.StaticSeries.cs index 2d6ce4549..41fd22df1 100644 --- a/src/s-z/Vortex/Vortex.StaticSeries.cs +++ b/src/s-z/Vortex/Vortex.StaticSeries.cs @@ -1,9 +1,18 @@ namespace Skender.Stock.Indicators; -// VORTEX INDICATOR (SERIES) - +/// +/// Provides methods for calculating the Vortex indicator. +/// public static partial class Vortex { + /// + /// Calculates the Vortex indicator for a series of quotes. + /// + /// The type of the elements in the source list, which must implement IQuote. + /// The source list of quotes. + /// The number of lookback periods. + /// A list of VortexResult containing the Vortex indicator values. + /// Thrown when the source is null. public static IReadOnlyList ToVortex( this IReadOnlyList quotes, int lookbackPeriods) @@ -11,6 +20,12 @@ public static IReadOnlyList ToVortex( .ToQuoteDList() .CalcVortex(lookbackPeriods); + /// + /// Calculates the Vortex indicator for a series of quotes. + /// + /// The source list of quotes. + /// The number of lookback periods. + /// A list of VortexResult containing the Vortex indicator values. private static List CalcVortex( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/s-z/Vortex/Vortex.Utilities.cs b/src/s-z/Vortex/Vortex.Utilities.cs index 22e449b50..f7f964f47 100644 --- a/src/s-z/Vortex/Vortex.Utilities.cs +++ b/src/s-z/Vortex/Vortex.Utilities.cs @@ -1,10 +1,16 @@ namespace Skender.Stock.Indicators; -// VORTEX INDICATOR (UTILITIES) - +/// +/// Provides utility methods for the Vortex indicator. +/// public static partial class Vortex { - // CONDENSE (REMOVE null results) + // remove empty (null) periods + /// + /// Condenses the Vortex results by removing periods with null values. + /// + /// The list of Vortex results. + /// A condensed list of Vortex results with null values removed. /// public static IReadOnlyList Condense( this IReadOnlyList results) @@ -20,6 +26,11 @@ public static IReadOnlyList Condense( } // remove recommended periods + /// + /// Removes the warmup periods from the Vortex results. + /// + /// The list of Vortex results. + /// A list of Vortex results with the warmup periods removed. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) @@ -32,6 +43,13 @@ public static IReadOnlyList RemoveWarmupPeriods( } // parameter validation + /// + /// Validates the parameters for the Vortex calculation. + /// + /// The number of lookback periods. + /// + /// Thrown when the lookback periods are less than or equal to 1. + /// internal static void Validate( int lookbackPeriods) { diff --git a/src/s-z/Vortex/info.xml b/src/s-z/Vortex/info.xml deleted file mode 100644 index b206f9ee9..000000000 --- a/src/s-z/Vortex/info.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Vortex Indicator (VI) is a measure of price directional movement. - It includes positive and negative indicators, and is often used to identify trends and reversals. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of VI+ and VI- vortex movement indicator values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/s-z/Vwap/Vwap.Models.cs b/src/s-z/Vwap/Vwap.Models.cs index 5b4400319..4f304aa68 100644 --- a/src/s-z/Vwap/Vwap.Models.cs +++ b/src/s-z/Vwap/Vwap.Models.cs @@ -1,11 +1,17 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Volume Weighted Average Price (VWAP) calculation. +/// +/// The timestamp of the data point. +/// The value of the VWAP at this point. [Serializable] public record VwapResult ( - DateTime Timestamp, - double? Vwap + DateTime Timestamp, + double? Vwap ) : IReusable { + /// public double Value => Vwap.Null2NaN(); } diff --git a/src/s-z/Vwap/Vwap.StaticSeries.cs b/src/s-z/Vwap/Vwap.StaticSeries.cs index 0f31b1505..856c536e9 100644 --- a/src/s-z/Vwap/Vwap.StaticSeries.cs +++ b/src/s-z/Vwap/Vwap.StaticSeries.cs @@ -1,9 +1,18 @@ namespace Skender.Stock.Indicators; -// VOLUME WEIGHTED AVERAGE PRICE (SERIES) - +/// +/// Provides methods for calculating the VWAP (Volume Weighted Average Price) indicator. +/// public static partial class Vwap { + /// + /// Calculates the VWAP for a series of quotes. + /// + /// The type of the elements in the source list, which must implement IQuote. + /// The source list of quotes. + /// The optional start date for the VWAP calculation. If not provided, the calculation starts from the first quote. + /// A list of VwapResult containing the VWAP values. + /// Thrown when the source is null. public static IReadOnlyList ToVwap( this IReadOnlyList quotes, DateTime? startDate = null) @@ -11,6 +20,12 @@ public static IReadOnlyList ToVwap( .ToQuoteDList() .CalcVwap(startDate); + /// + /// Calculates the VWAP for a series of quotes. + /// + /// The source list of quotes. + /// The optional start date for the VWAP calculation. If not provided, the calculation starts from the first quote. + /// A list of VwapResult containing the VWAP values. private static List CalcVwap( this IReadOnlyList source, DateTime? startDate = null) diff --git a/src/s-z/Vwap/Vwap.Utilities.cs b/src/s-z/Vwap/Vwap.Utilities.cs index bf24c9fea..6a631c21a 100644 --- a/src/s-z/Vwap/Vwap.Utilities.cs +++ b/src/s-z/Vwap/Vwap.Utilities.cs @@ -1,10 +1,16 @@ namespace Skender.Stock.Indicators; -// VOLUME WEIGHTED AVERAGE PRICE (UTILITIES) - +/// +/// Provides utility methods for the VWAP (Volume Weighted Average Price) indicator. +/// public static partial class Vwap { // remove recommended periods + /// + /// Removes the warmup periods from the VWAP results. + /// + /// The list of VWAP results. + /// A list of VWAP results with the warmup periods removed. /// public static IReadOnlyList RemoveWarmupPeriods( this IReadOnlyList results) @@ -17,6 +23,14 @@ public static IReadOnlyList RemoveWarmupPeriods( } // parameter validation + /// + /// Validates the parameters for the VWAP calculation. + /// + /// The source list of quotes. + /// The optional start date for the VWAP calculation. + /// + /// Thrown when the start date is earlier than the first quote's timestamp. + /// internal static void Validate( IReadOnlyList quotes, DateTime? startDate) diff --git a/src/s-z/Vwap/info.xml b/src/s-z/Vwap/info.xml deleted file mode 100644 index 9b2937e5a..000000000 --- a/src/s-z/Vwap/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Volume Weighted Average Price (VWAP) is a Volume weighted average of price, typically used on intraday data. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Optional anchor date. If not provided, the first date in quotes is used. - Time series of VWAP values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/s-z/Vwma/Vwma.Models.cs b/src/s-z/Vwma/Vwma.Models.cs index 647136fbe..cde0a5cfc 100644 --- a/src/s-z/Vwma/Vwma.Models.cs +++ b/src/s-z/Vwma/Vwma.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Volume Weighted Moving Average (VWMA) calculation. +/// +/// The timestamp of the data point. +/// The value of the VWMA at this point. [Serializable] public record VwmaResult ( @@ -7,5 +12,6 @@ public record VwmaResult double? Vwma ) : IReusable { + /// public double Value => Vwma.Null2NaN(); } diff --git a/src/s-z/Vwma/Vwma.StaticSeries.cs b/src/s-z/Vwma/Vwma.StaticSeries.cs index 20c4c689e..578f4b9a6 100644 --- a/src/s-z/Vwma/Vwma.StaticSeries.cs +++ b/src/s-z/Vwma/Vwma.StaticSeries.cs @@ -1,9 +1,18 @@ namespace Skender.Stock.Indicators; -// VOLUME WEIGHTED MOVING AVERAGE (SERIES) - +/// +/// Provides methods for calculating the VWMA (Volume Weighted Moving Average) indicator. +/// public static partial class Vwma { + /// + /// Calculates the VWMA for a series of quotes. + /// + /// The type of the elements in the source list, which must implement IQuote. + /// The source list of quotes. + /// The number of lookback periods. + /// A list of VwmaResult containing the VWMA values. + /// Thrown when the source is null. public static IReadOnlyList ToVwma( this IReadOnlyList quotes, int lookbackPeriods) @@ -11,6 +20,12 @@ public static IReadOnlyList ToVwma( .ToQuoteDList() .CalcVwma(lookbackPeriods); + /// + /// Calculates the VWMA for a series of quotes. + /// + /// The source list of quotes. + /// The number of lookback periods. + /// A list of VwmaResult containing the VWMA values. private static List CalcVwma( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/s-z/Vwma/Vwma.Utilities.cs b/src/s-z/Vwma/Vwma.Utilities.cs index dd43909b3..77e244616 100644 --- a/src/s-z/Vwma/Vwma.Utilities.cs +++ b/src/s-z/Vwma/Vwma.Utilities.cs @@ -1,10 +1,18 @@ namespace Skender.Stock.Indicators; -// VOLUME WEIGHTED MOVING AVERAGE (UTILITIES) - +/// +/// Provides utility methods for the VWMA (Volume Weighted Moving Average) indicator. +/// public static partial class Vwma { // parameter validation + /// + /// Validates the parameters for the VWMA calculation. + /// + /// The number of lookback periods. + /// + /// Thrown when the lookback periods are less than or equal to 0. + /// internal static void Validate( int lookbackPeriods) { diff --git a/src/s-z/Vwma/info.xml b/src/s-z/Vwma/info.xml deleted file mode 100644 index 2f4d29d01..000000000 --- a/src/s-z/Vwma/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Volume Weighted Moving Average is the volume adjusted average price over a lookback window. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of Volume Weighted Moving Average values. - Invalid parameter value provided. - diff --git a/src/s-z/WilliamsR/WilliamsR.Models.cs b/src/s-z/WilliamsR/WilliamsR.Models.cs index 0fb8b05aa..5c3c85a4e 100644 --- a/src/s-z/WilliamsR/WilliamsR.Models.cs +++ b/src/s-z/WilliamsR/WilliamsR.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Williams %R calculation. +/// +/// The timestamp of the data point. +/// The value of the Williams %R at this point. [Serializable] public record WilliamsResult ( @@ -7,5 +12,6 @@ public record WilliamsResult double? WilliamsR ) : IReusable { + /// public double Value => WilliamsR.Null2NaN(); } diff --git a/src/s-z/WilliamsR/WilliamsR.StaticSeries.cs b/src/s-z/WilliamsR/WilliamsR.StaticSeries.cs index 23becb293..e171cb3a2 100644 --- a/src/s-z/WilliamsR/WilliamsR.StaticSeries.cs +++ b/src/s-z/WilliamsR/WilliamsR.StaticSeries.cs @@ -1,9 +1,18 @@ namespace Skender.Stock.Indicators; -// WILLIAM %R OSCILLATOR (SERIES) - +/// +/// Provides methods for calculating the Williams %R indicator. +/// public static partial class WilliamsR { + /// + /// Calculates the Williams %R for a series of quotes. + /// + /// The type of the elements in the source list, which must implement IQuote. + /// The source list of quotes. + /// The number of lookback periods. Default is 14. + /// A list of WilliamsResult containing the Williams %R values. + /// Thrown when the source is null. public static IReadOnlyList ToWilliamsR( this IReadOnlyList quotes, int lookbackPeriods = 14) @@ -11,6 +20,12 @@ public static IReadOnlyList ToWilliamsR( .ToQuoteDList() .CalcWilliamsR(lookbackPeriods); + /// + /// Calculates the Williams %R for a series of quotes. + /// + /// The source list of quotes. + /// The number of lookback periods. + /// A list of WilliamsResult containing the Williams %R values. private static List CalcWilliamsR( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/s-z/WilliamsR/WilliamsR.Utilities.cs b/src/s-z/WilliamsR/WilliamsR.Utilities.cs index 49f8d8af1..8d46d8b5a 100644 --- a/src/s-z/WilliamsR/WilliamsR.Utilities.cs +++ b/src/s-z/WilliamsR/WilliamsR.Utilities.cs @@ -1,10 +1,18 @@ namespace Skender.Stock.Indicators; -// WILLIAM %R OSCILLATOR (UTILITIES) - +/// +/// Provides utility methods for the Williams %R indicator. +/// public static partial class WilliamsR { // parameter validation + /// + /// Validates the parameters for the Williams %R calculation. + /// + /// The number of lookback periods. + /// + /// Thrown when the lookback periods are less than or equal to 0. + /// internal static void Validate( int lookbackPeriods) { diff --git a/src/s-z/WilliamsR/info.xml b/src/s-z/WilliamsR/info.xml deleted file mode 100644 index 80ad93a41..000000000 --- a/src/s-z/WilliamsR/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Williams %R momentum indicator is a stochastic oscillator with scale of -100 to 0. It is exactly the same as the Fast variant of Stochastic Oscillator, but with a different scaling. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of Williams %R values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/src/s-z/Wma/Wma.Models.cs b/src/s-z/Wma/Wma.Models.cs index 41b2e6638..a5033851b 100644 --- a/src/s-z/Wma/Wma.Models.cs +++ b/src/s-z/Wma/Wma.Models.cs @@ -1,5 +1,10 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a Weighted Moving Average (WMA) calculation. +/// +/// The timestamp of the data point. +/// The value of the WMA at this point. [Serializable] public record WmaResult ( @@ -7,5 +12,6 @@ public record WmaResult double? Wma ) : IReusable { + /// public double Value => Wma.Null2NaN(); } diff --git a/src/s-z/Wma/Wma.StaticSeries.cs b/src/s-z/Wma/Wma.StaticSeries.cs index 95fcc8aac..6e0636c2d 100644 --- a/src/s-z/Wma/Wma.StaticSeries.cs +++ b/src/s-z/Wma/Wma.StaticSeries.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// WEIGHTED MOVING AVERAGE (SERIES) - +/// +/// Provides methods for calculating the WMA (Weighted Moving Average) indicator. +/// public static partial class Wma { + /// + /// Calculates the WMA for a series of reusable data. + /// + /// The type of the elements in the source list, which must implement IReusable. + /// The source list of reusable data. + /// The number of lookback periods. + /// A list of WmaResult containing the WMA values. + /// Thrown when the source is null. + /// Thrown when the lookback periods are less than or equal to 0. public static IReadOnlyList ToWma( this IReadOnlyList source, int lookbackPeriods) diff --git a/src/s-z/Wma/Wma.Utilities.cs b/src/s-z/Wma/Wma.Utilities.cs index cd7b65c13..7b0a64db7 100644 --- a/src/s-z/Wma/Wma.Utilities.cs +++ b/src/s-z/Wma/Wma.Utilities.cs @@ -1,10 +1,18 @@ namespace Skender.Stock.Indicators; -// WEIGHTED MOVING AVERAGE (UTILITIES) - +/// +/// Provides utility methods for the WMA (Weighted Moving Average) indicator. +/// public static partial class Wma { // parameter validation + /// + /// Validates the parameters for the WMA calculation. + /// + /// The number of lookback periods. + /// + /// Thrown when the lookback periods are less than or equal to 0. + /// internal static void Validate( int lookbackPeriods) { diff --git a/src/s-z/Wma/info.xml b/src/s-z/Wma/info.xml deleted file mode 100644 index a42d645a5..000000000 --- a/src/s-z/Wma/info.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Weighted Moving Average (WMA) is the linear weighted average of price over N lookback periods. This also called Linear Weighted Moving Average (LWMA). - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Number of periods in the lookback window. - Time series of WMA values. - Invalid parameter value provided. - diff --git a/src/s-z/ZigZag/ZigZag.Models.cs b/src/s-z/ZigZag/ZigZag.Models.cs index 6e0a410a5..8eee805b5 100644 --- a/src/s-z/ZigZag/ZigZag.Models.cs +++ b/src/s-z/ZigZag/ZigZag.Models.cs @@ -1,28 +1,65 @@ namespace Skender.Stock.Indicators; +/// +/// Represents the result of a ZigZag indicator calculation. +/// +/// The timestamp of the data point. +/// The value of the ZigZag line at this point. +/// The type of point (e.g., H: high or L: low). +/// The value of the retrace high line at this point. +/// The value of the retrace low line at this point. [Serializable] public record ZigZagResult ( - DateTime Timestamp, - decimal? ZigZag = null, // zig zag line - string? PointType = null, // indicates a specific point and type e.g. H or L - decimal? RetraceHigh = null, // zig zag retrace high line - decimal? RetraceLow = null // zig zag retrace low line + DateTime Timestamp, + decimal? ZigZag = null, + string? PointType = null, + decimal? RetraceHigh = null, + decimal? RetraceLow = null ) : IReusable { + /// public double Value => ZigZag.Null2NaN(); } +/// +/// Represents an evaluation point used in the ZigZag calculation. +/// internal class ZigZagEval { + /// + /// Gets or sets the index of the evaluation point. + /// internal int Index { get; init; } + + /// + /// Gets or sets the high value at the evaluation point. + /// internal decimal? High { get; set; } + + /// + /// Gets or sets the low value at the evaluation point. + /// internal decimal? Low { get; set; } } +/// +/// Represents a specific point in the ZigZag calculation. +/// internal class ZigZagPoint { + /// + /// Gets or sets the index of the point. + /// internal int Index { get; set; } + + /// + /// Gets or sets the value at the point. + /// internal decimal? Value { get; set; } + + /// + /// Gets or sets the type of the point (e.g., High or Low). + /// internal string? PointType { get; set; } } diff --git a/src/s-z/ZigZag/ZigZag.StaticSeries.cs b/src/s-z/ZigZag/ZigZag.StaticSeries.cs index 8d249e916..c8bb2a7de 100644 --- a/src/s-z/ZigZag/ZigZag.StaticSeries.cs +++ b/src/s-z/ZigZag/ZigZag.StaticSeries.cs @@ -1,9 +1,18 @@ namespace Skender.Stock.Indicators; -// ZIG ZAG (SERIES) - +/// +/// Provides methods to convert a list of quotes to a ZigZag series. +/// public static partial class ZigZag { + /// + /// Converts a list of quotes to a ZigZag series. + /// + /// The type of the quote. + /// The list of quotes. + /// The type of end to use (Close or HighLow). + /// The percentage change threshold for ZigZag points. + /// A list of ZigZag results. public static IReadOnlyList ToZigZag( this IReadOnlyList quotes, EndType endType = EndType.Close, @@ -104,7 +113,15 @@ public static IReadOnlyList ToZigZag( return results; } - // internals + /// + /// Evaluates the next ZigZag point. + /// + /// The type of the quote. + /// The list of quotes. + /// The type of end to use (Close or HighLow). + /// The percentage change threshold for ZigZag points. + /// The last ZigZag point. + /// The next ZigZag point. private static ZigZagPoint EvaluateNextPoint( IReadOnlyList quotesList, EndType endType, @@ -183,6 +200,14 @@ private static ZigZagPoint EvaluateNextPoint( return extremePoint; } + /// + /// Draws a ZigZag line between two points. + /// + /// The type of the quote. + /// The list of ZigZag results. + /// The list of quotes. + /// The last ZigZag point. + /// The next ZigZag point. private static void DrawZigZagLine( List results, IReadOnlyList quotes, ZigZagPoint lastPoint, ZigZagPoint nextPoint) @@ -219,6 +244,14 @@ private static void DrawZigZagLine( lastPoint.PointType = nextPoint.PointType; } + /// + /// Draws a retrace line between two points. + /// + /// The list of ZigZag results. + /// The direction of the last ZigZag point. + /// The last low ZigZag point. + /// The last high ZigZag point. + /// The next ZigZag point. private static void DrawRetraceLine( List results, string lastDirection, @@ -298,6 +331,14 @@ private static void DrawRetraceLine( } } + /// + /// Gets the ZigZag evaluation for a quote. + /// + /// The type of the quote. + /// The type of end to use (Close or HighLow). + /// The index of the quote. + /// The quote. + /// The ZigZag evaluation. private static ZigZagEval GetZigZagEval( EndType endType, int index, diff --git a/src/s-z/ZigZag/ZigZag.Utilities.cs b/src/s-z/ZigZag/ZigZag.Utilities.cs index b3af14695..8112ebf7d 100644 --- a/src/s-z/ZigZag/ZigZag.Utilities.cs +++ b/src/s-z/ZigZag/ZigZag.Utilities.cs @@ -1,10 +1,15 @@ namespace Skender.Stock.Indicators; -// ZIG ZAG (UTILITIES) - +/// +/// Provides utility methods for the ZigZag indicator. +/// public static partial class ZigZag { - // CONDENSE (REMOVE null results) + /// + /// Removes empty (null) periods from the ZigZag results. + /// + /// The list of ZigZag results. + /// A condensed list of ZigZag results without null periods. /// public static IReadOnlyList Condense( this IReadOnlyList results) @@ -19,7 +24,11 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } - // parameter validation + /// + /// Validates the parameters for the ZigZag indicator. + /// + /// The percentage change threshold for ZigZag points. + /// Thrown when the percent change is less than or equal to 0. internal static void Validate( decimal percentChange) { diff --git a/src/s-z/ZigZag/info.xml b/src/s-z/ZigZag/info.xml deleted file mode 100644 index 2f5035bd7..000000000 --- a/src/s-z/ZigZag/info.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Zig Zag is a price chart overlay that simplifies the up and down movements and transitions based on a percent change smoothing threshold. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Determines use of Close or High/Low wicks for extreme points. - Percent price change to set threshold for minimum size movements. - Time series of Zig Zag values. - Invalid parameter value provided. - \ No newline at end of file diff --git a/tests/.editorconfig b/tests/.editorconfig new file mode 100644 index 000000000..d23bcbe4e --- /dev/null +++ b/tests/.editorconfig @@ -0,0 +1,5 @@ +# code analysis for TESTS only +root = false + +[*.cs] +dotnet_diagnostic.CA1303.severity = none # Do not pass literals as localized parameters diff --git a/tests/Directory.Packages.props b/tests/Directory.Packages.props new file mode 100644 index 000000000..4c332fa76 --- /dev/null +++ b/tests/Directory.Packages.props @@ -0,0 +1,17 @@ + + + true + true + $(NoWarn);NU1507 + + + + + + + + + + + + diff --git a/tests/external/application/Test.Application.csproj b/tests/external/application/Test.Application.csproj index 86cd6da93..1f9ae6243 100644 --- a/tests/external/application/Test.Application.csproj +++ b/tests/external/application/Test.Application.csproj @@ -1,8 +1,8 @@ - Exe net8.0 + Exe enable enable diff --git a/tests/external/integration/Tests.Integration.csproj b/tests/external/integration/Tests.Integration.csproj index de6f586ca..52eb61c5e 100644 --- a/tests/external/integration/Tests.Integration.csproj +++ b/tests/external/integration/Tests.Integration.csproj @@ -19,15 +19,12 @@ - - - - - - - all - runtime; build; native; contentfiles; analyzers - + + + + + + diff --git a/tests/external/public-api/Tests.PublicApi.csproj b/tests/external/public-api/Tests.PublicApi.csproj index abd2b8ede..0469a9624 100644 --- a/tests/external/public-api/Tests.PublicApi.csproj +++ b/tests/external/public-api/Tests.PublicApi.csproj @@ -19,14 +19,11 @@ - - - - - - all - runtime; build; native; contentfiles; analyzers - + + + + + diff --git a/tests/indicators/Tests.Indicators.csproj b/tests/indicators/Tests.Indicators.csproj index ef89704b7..da997a20b 100644 --- a/tests/indicators/Tests.Indicators.csproj +++ b/tests/indicators/Tests.Indicators.csproj @@ -16,18 +16,12 @@ - - - - - - all - runtime; build; native; contentfiles; analyzers - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + + + + + diff --git a/tests/indicators/_common/Math/Numerical.Tests.cs b/tests/indicators/_common/Math/Numerical.Tests.cs index e116fe11f..fc1f3e212 100644 --- a/tests/indicators/_common/Math/Numerical.Tests.cs +++ b/tests/indicators/_common/Math/Numerical.Tests.cs @@ -7,8 +7,8 @@ public class Numericals : TestBase .Select(x => (double)x.Close) .ToArray(); - private readonly double[] _x = { 1, 2, 3, 4, 5 }; - private readonly double[] _y = { 0, 0, 0, 0 }; + private readonly double[] _x = [1, 2, 3, 4, 5]; + private readonly double[] _y = [0, 0, 0, 0]; [TestMethod] public void StdDev() diff --git a/tests/indicators/_common/Observables/StreamHub.Stackoverflow.Tests.cs b/tests/indicators/_common/Observables/StreamHub.Stackoverflow.Tests.cs index 0e61b8fcc..ea2414bf7 100644 --- a/tests/indicators/_common/Observables/StreamHub.Stackoverflow.Tests.cs +++ b/tests/indicators/_common/Observables/StreamHub.Stackoverflow.Tests.cs @@ -17,11 +17,11 @@ public void FatLongStack() QuoteHub provider = new(); // setup: define ~10 subscribers (flat) - List<(string label, IReadOnlyList results, bool irregular)> subscribers = new() - { + List<(string label, IReadOnlyList results, bool irregular)> subscribers = + [ HubRef(provider.ToAdl()), HubRef(provider.ToEma(14)) - }; + ]; // all USEs foreach (CandlePart candlePart in Enum.GetValues()) @@ -176,14 +176,14 @@ public void ManySubscribers() // setup: define all possible subscribers // TODO: add to this as more Hubs come online - List<(string label, IReadOnlyList results, bool irregular)> subscribers = new() - { + List<(string label, IReadOnlyList results, bool irregular)> subscribers = + [ HubRef(provider.ToAdl()), HubRef(provider.ToAlligator()), HubRef(provider.ToEma(14)), //HubRef(provider.ToRenko(2.1m), irregular: true), HubRef(provider.ToQuote()) - }; + ]; // all QuoteParts foreach (CandlePart candlePart in Enum.GetValues()) diff --git a/tests/indicators/s-z/Sma/Sma.Utilities.Tests.cs b/tests/indicators/s-z/Sma/Sma.Utilities.Tests.cs index dd8e344b6..6d7bcd776 100644 --- a/tests/indicators/s-z/Sma/Sma.Utilities.Tests.cs +++ b/tests/indicators/s-z/Sma/Sma.Utilities.Tests.cs @@ -5,12 +5,12 @@ public partial class Sma : StaticSeriesTestBase [TestMethod] public void Average() { - QuotePart[] results = new[] - { + QuotePart[] results = + [ new QuotePart(Quotes[0].Timestamp, 0.0), new QuotePart(Quotes[1].Timestamp, 4.0), new QuotePart(Quotes[2].Timestamp, 8.0) - }; + ]; // sut double? mid = results.Average(2, 1); diff --git a/tests/performance/Tests.Performance.csproj b/tests/performance/Tests.Performance.csproj index 18fb733e2..3124d795b 100644 --- a/tests/performance/Tests.Performance.csproj +++ b/tests/performance/Tests.Performance.csproj @@ -6,6 +6,7 @@ Performance.Program enable + disable true latest @@ -21,7 +22,7 @@ - + diff --git a/tests/simulate/Test.Simulation.csproj b/tests/simulate/Test.Simulation.csproj index 6c4315d35..1ebfaa471 100644 --- a/tests/simulate/Test.Simulation.csproj +++ b/tests/simulate/Test.Simulation.csproj @@ -1,8 +1,8 @@ - Exe net8.0 + Exe enable enable From 3d7bbf6d0ccb183135e227b220c3a5152c358718 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Mon, 11 Nov 2024 18:08:49 -0500 Subject: [PATCH 12/18] feat: Buffer-style ADX incremental (#1271) --- .github/workflows/test-performance.yml | 4 +- .../{IIncremental.cs => IBufferList.cs} | 8 +- src/a-d/Adx/Adx.BufferList.cs | 199 ++++++++++++++++++ src/a-d/Adx/Adx.Models.cs | 2 + src/a-d/Adx/Adx.StaticSeries.cs | 2 + src/a-d/Adx/IAdx.cs | 12 ++ .../{Ema.Increments.cs => Ema.BufferList.cs} | 15 +- src/e-k/Ema/Ema.StreamHub.cs | 2 +- tests/README.md | 2 +- tests/indicators/README.md | 2 +- tests/indicators/TestBase.cs | 4 +- tests/indicators/a-d/Adx/Adx.Calc.xlsx | Bin 193500 -> 193655 bytes .../a-d/Adx/Adx.Increments.Tests.cs | 33 +++ .../a-d/Adx/Adx.StaticSeries.Tests.cs | 23 ++ .../e-k/Ema/Ema.Increments.Tests.cs | 2 +- tests/performance/Perf.BufferList.cs | 44 ++++ tests/performance/Perf.Increments.cs | 95 --------- 17 files changed, 336 insertions(+), 113 deletions(-) rename src/_common/Incrementals/{IIncremental.cs => IBufferList.cs} (89%) create mode 100644 src/a-d/Adx/Adx.BufferList.cs create mode 100644 src/a-d/Adx/IAdx.cs rename src/e-k/Ema/{Ema.Increments.cs => Ema.BufferList.cs} (90%) create mode 100644 tests/indicators/a-d/Adx/Adx.Increments.Tests.cs create mode 100644 tests/performance/Perf.BufferList.cs delete mode 100644 tests/performance/Perf.Increments.cs diff --git a/.github/workflows/test-performance.yml b/.github/workflows/test-performance.yml index 6682f6b06..8f014c325 100644 --- a/.github/workflows/test-performance.yml +++ b/.github/workflows/test-performance.yml @@ -63,8 +63,8 @@ jobs: echo "## Stream indicators (with Quote caching)" >> $GITHUB_STEP_SUMMARY cat Performance.StreamIndicators-report-github.md >> $GITHUB_STEP_SUMMARY - echo "## Incremental indicators (with buffer)" >> $GITHUB_STEP_SUMMARY - cat Performance.Incrementals-report-github.md >> $GITHUB_STEP_SUMMARY + echo "## Incrementing buffer-style indicators" >> $GITHUB_STEP_SUMMARY + cat Performance.BufferLists-report-github.md >> $GITHUB_STEP_SUMMARY echo "## Utilities" >> $GITHUB_STEP_SUMMARY cat Performance.Utility-report-github.md >> $GITHUB_STEP_SUMMARY diff --git a/src/_common/Incrementals/IIncremental.cs b/src/_common/Incrementals/IBufferList.cs similarity index 89% rename from src/_common/Incrementals/IIncremental.cs rename to src/_common/Incrementals/IBufferList.cs index b9ad914a0..a930e2305 100644 --- a/src/_common/Incrementals/IIncremental.cs +++ b/src/_common/Incrementals/IBufferList.cs @@ -1,9 +1,9 @@ namespace Skender.Stock.Indicators; /// -/// Interface for adding reusable incremental values to a list. +/// Interface for adding input values to a buffered list. /// -public interface IAddReusable +public interface IBufferReusable { /// /// Converts an incremental value into the next incremental indicator value and adds it to the list. @@ -26,9 +26,9 @@ public interface IAddReusable } /// -/// Interface for adding incremental quotes to a list. +/// Interface for adding buffered quotes to a list. /// -public interface IAddQuote +public interface IBufferQuote { /// /// Converts an incremental quote into the next incremental indicator value and adds it to the list. diff --git a/src/a-d/Adx/Adx.BufferList.cs b/src/a-d/Adx/Adx.BufferList.cs new file mode 100644 index 000000000..17a9d8d1d --- /dev/null +++ b/src/a-d/Adx/Adx.BufferList.cs @@ -0,0 +1,199 @@ +namespace Skender.Stock.Indicators; + +/// +/// Average Directional Index (ADX) from incremental reusable values. +/// +public class AdxList : List, IAdx, IBufferQuote +{ + private readonly Queue _buffer; + + /// + /// Initializes a new instance of the class. + /// + /// The number of periods to look back for the calculation. + public AdxList(int lookbackPeriods) + { + Adx.Validate(lookbackPeriods); + LookbackPeriods = lookbackPeriods; + + _buffer = new Queue(lookbackPeriods); + } + + /// + /// Gets the number of periods to look back for the calculation. + /// + public int LookbackPeriods { get; init; } + + /// + /// Adds a new quote to the ADX list. + /// + /// The quote to add. + /// Thrown when the quote is null. + public void Add(IQuote quote) + { + ArgumentNullException.ThrowIfNull(quote); + + // update buffer + if (_buffer.Count == LookbackPeriods) + { + _buffer.Dequeue(); + } + + DateTime timestamp = quote.Timestamp; + + AdxBuffer curr = new( + (double)quote.High, + (double)quote.Low, + (double)quote.Close); + + // skip first period + if (Count == 0) + { + _buffer.Enqueue(curr); + base.Add(new AdxResult(timestamp)); + return; + } + + // get last, then add current object + AdxBuffer last = _buffer.Last(); + _buffer.Enqueue(curr); + + // calculate TR, PDM, and MDM + double hmpc = Math.Abs(curr.High - last.Close); + double lmpc = Math.Abs(curr.Low - last.Close); + double hmph = curr.High - last.High; + double plml = last.Low - curr.Low; + + curr.Tr = Math.Max(curr.High - curr.Low, Math.Max(hmpc, lmpc)); + + curr.Pdm1 = hmph > plml ? Math.Max(hmph, 0) : 0; + curr.Mdm1 = plml > hmph ? Math.Max(plml, 0) : 0; + + // skip incalculable + if (Count < LookbackPeriods) + { + base.Add(new AdxResult(timestamp)); + return; + } + + // re/initialize smooth TR and DM + if (Count >= LookbackPeriods && last.Trs == 0) + { + foreach (AdxBuffer buffer in _buffer) + { + curr.Trs += buffer.Tr; + curr.Pdm += buffer.Pdm1; + curr.Mdm += buffer.Mdm1; + } + } + + // normal movement calculations + else + { + curr.Trs = last.Trs - (last.Trs / LookbackPeriods) + curr.Tr; + curr.Pdm = last.Pdm - (last.Pdm / LookbackPeriods) + curr.Pdm1; + curr.Mdm = last.Mdm - (last.Mdm / LookbackPeriods) + curr.Mdm1; + } + + // skip incalculable periods + if (curr.Trs == 0) + { + base.Add(new AdxResult(timestamp)); + return; + } + + // directional increments + double pdi = 100 * curr.Pdm / curr.Trs; + double mdi = 100 * curr.Mdm / curr.Trs; + + // calculate directional index (DX) + curr.Dx = pdi - mdi == 0 + ? 0 + : pdi + mdi != 0 + ? 100 * Math.Abs(pdi - mdi) / (pdi + mdi) + : double.NaN; + + // skip incalculable ADX periods + if (Count < (2 * LookbackPeriods) - 1) + { + base.Add(new AdxResult(timestamp, + Pdi: pdi.NaN2Null(), + Mdi: mdi.NaN2Null(), + Dx: curr.Dx.NaN2Null())); + + return; + } + + double adxr = double.NaN; + + // re/initialize ADX + if (Count >= (2 * LookbackPeriods) - 1 && double.IsNaN(last.Adx)) + { + double sumDx = 0; + + foreach (AdxBuffer buffer in _buffer) + { + sumDx += buffer.Dx; + } + + curr.Adx = sumDx / LookbackPeriods; + } + + // normal ADX calculation + else + { + curr.Adx + = ((last.Adx * (LookbackPeriods - 1)) + curr.Dx) + / LookbackPeriods; + + AdxBuffer first = _buffer.Peek(); + adxr = (curr.Adx + first.Adx) / 2; + } + + AdxResult r = new( + Timestamp: timestamp, + Pdi: pdi, + Mdi: mdi, + Dx: curr.Dx.NaN2Null(), + Adx: curr.Adx.NaN2Null(), + Adxr: adxr.NaN2Null()); + + base.Add(r); + } + + /// + /// Adds a list of quotes to the ADX list. + /// + /// The list of quotes to add. + /// Thrown when the quotes list is null. + public void Add(IReadOnlyList quotes) + { + ArgumentNullException.ThrowIfNull(quotes); + + for (int i = 0; i < quotes.Count; i++) + { + Add(quotes[i]); + } + } + + internal class AdxBuffer( + double high, + double low, + double close) + { + internal double High { get; init; } = high; + internal double Low { get; init; } = low; + internal double Close { get; init; } = close; + + internal double Tr { get; set; } = double.NaN; + internal double Pdm1 { get; set; } = double.NaN; + internal double Mdm1 { get; set; } = double.NaN; + + internal double Trs { get; set; } + internal double Pdm { get; set; } + internal double Mdm { get; set; } + + internal double Dx { get; set; } = double.NaN; + internal double Adx { get; set; } = double.NaN; + } +} diff --git a/src/a-d/Adx/Adx.Models.cs b/src/a-d/Adx/Adx.Models.cs index 783a741fd..9f65ac570 100644 --- a/src/a-d/Adx/Adx.Models.cs +++ b/src/a-d/Adx/Adx.Models.cs @@ -6,6 +6,7 @@ namespace Skender.Stock.Indicators; /// Gets the timestamp of the result. /// Gets the Positive Directional Indicator (PDI) value. /// Gets the Negative Directional Indicator (MDI) value. +/// Gets the Directional Index (DX) value. /// Gets the Average Directional Index (ADX) value. /// Gets the Average Directional Movement Rating (ADXR) value. [Serializable] @@ -14,6 +15,7 @@ public record AdxResult DateTime Timestamp, double? Pdi = null, double? Mdi = null, + double? Dx = null, double? Adx = null, double? Adxr = null ) : IReusable diff --git a/src/a-d/Adx/Adx.StaticSeries.cs b/src/a-d/Adx/Adx.StaticSeries.cs index 35d94dd7e..177da87e9 100644 --- a/src/a-d/Adx/Adx.StaticSeries.cs +++ b/src/a-d/Adx/Adx.StaticSeries.cs @@ -157,6 +157,7 @@ private static List CalcAdx( // ADX initialization period // TODO: update healing, without requiring specific indexing + // see ADX BufferList for hint else { sumDx += dx; @@ -166,6 +167,7 @@ private static List CalcAdx( Timestamp: q.Timestamp, Pdi: pdi, Mdi: mdi, + Dx: dx.NaN2Null(), Adx: adx.NaN2Null(), Adxr: adxr.NaN2Null()); diff --git a/src/a-d/Adx/IAdx.cs b/src/a-d/Adx/IAdx.cs new file mode 100644 index 000000000..b02cc1644 --- /dev/null +++ b/src/a-d/Adx/IAdx.cs @@ -0,0 +1,12 @@ +namespace Skender.Stock.Indicators; + +/// +/// Interface for Average Directional Index (ADX) streaming and buffered list. +/// +public interface IAdx +{ + /// + /// Gets the number of periods to look back for the calculation. + /// + int LookbackPeriods { get; } +} diff --git a/src/e-k/Ema/Ema.Increments.cs b/src/e-k/Ema/Ema.BufferList.cs similarity index 90% rename from src/e-k/Ema/Ema.Increments.cs rename to src/e-k/Ema/Ema.BufferList.cs index de39ff2f1..2b9e4957c 100644 --- a/src/e-k/Ema/Ema.Increments.cs +++ b/src/e-k/Ema/Ema.BufferList.cs @@ -3,7 +3,7 @@ namespace Skender.Stock.Indicators; /// /// Exponential Moving Average (EMA) from incremental reusable values. /// -public class EmaList : List, IEma, IAddQuote, IAddReusable +public class EmaList : List, IEma, IBufferQuote, IBufferReusable { private readonly Queue _buffer; private double _bufferSum; @@ -18,7 +18,7 @@ public EmaList(int lookbackPeriods) LookbackPeriods = lookbackPeriods; K = 2d / (lookbackPeriods + 1); - _buffer = new(lookbackPeriods); + _buffer = new Queue(lookbackPeriods); _bufferSum = 0; } @@ -30,7 +30,7 @@ public EmaList(int lookbackPeriods) /// /// Gets the smoothing factor for the calculation. /// - public double K { get; init; } + public double K { get; private init; } /// /// Adds a new value to the EMA list. @@ -44,6 +44,7 @@ public void Add(DateTime timestamp, double value) { _bufferSum -= _buffer.Dequeue(); } + _buffer.Enqueue(value); _bufferSum += value; @@ -54,8 +55,10 @@ public void Add(DateTime timestamp, double value) return; } + double? lastEma = this[^1].Ema; + // re/initialize as SMA - if (this[^1].Ema is null) + if (lastEma is null) { base.Add(new EmaResult( timestamp, @@ -66,7 +69,7 @@ public void Add(DateTime timestamp, double value) // calculate EMA normally base.Add(new EmaResult( timestamp, - Ema.Increment(K, this[^1].Ema, value))); + Ema.Increment(K, lastEma.Value, value))); } /// @@ -117,7 +120,7 @@ public void Add(IReadOnlyList quotes) for (int i = 0; i < quotes.Count; i++) { - Add(quotes[i]); + Add(quotes[i].Timestamp, quotes[i].Value); } } } diff --git a/src/e-k/Ema/Ema.StreamHub.cs b/src/e-k/Ema/Ema.StreamHub.cs index 173bfabc8..4751741d0 100644 --- a/src/e-k/Ema/Ema.StreamHub.cs +++ b/src/e-k/Ema/Ema.StreamHub.cs @@ -69,7 +69,7 @@ internal EmaHub( public int LookbackPeriods { get; init; } /// - public double K { get; init; } + public double K { get; private init; } /// public override string ToString() => hubName; diff --git a/tests/README.md b/tests/README.md index 321dcb53d..3ae3dee44 100644 --- a/tests/README.md +++ b/tests/README.md @@ -43,7 +43,7 @@ dotnet run -c Release --filter ** dotnet run --list ... # Available Benchmarks: - #0 Incrementals + #0 BufferList #1 SeriesIndicators #2 StreamExternal #3 StreamIndicators diff --git a/tests/indicators/README.md b/tests/indicators/README.md index c44b96c5e..1f055c051 100644 --- a/tests/indicators/README.md +++ b/tests/indicators/README.md @@ -43,7 +43,7 @@ dotnet run -c Release --filter ** dotnet run --list ... # Available Benchmarks: - #0 Incrementals + #0 BufferLists #1 SeriesIndicators #2 StreamExternal #3 StreamIndicators diff --git a/tests/indicators/TestBase.cs b/tests/indicators/TestBase.cs index adf3a165f..bb0d9c501 100644 --- a/tests/indicators/TestBase.cs +++ b/tests/indicators/TestBase.cs @@ -45,9 +45,9 @@ public abstract class StaticSeriesTestBase : TestBase } /// -/// Base tests that all static indicators (series) should have. +/// Base tests that all buffered list indicators should have. /// -public abstract class IncrementsTestBase : TestBase +public abstract class BufferListTestBase : TestBase { public abstract void FromQuote(); diff --git a/tests/indicators/a-d/Adx/Adx.Calc.xlsx b/tests/indicators/a-d/Adx/Adx.Calc.xlsx index 67504cd442db4b7fdeac85513b0696365e26f29b..bb8908c777baf28e2d13d4a089bf06a3435a8fdf 100644 GIT binary patch delta 165038 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ˎBtb&KPy)*yojuo7%YZ|l`QJg|_+QZHI{SS;Y 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 jBqZ8kQD^=u3|dahC series + = Quotes.ToAdx(lookbackPeriods); + + [TestMethod] + public override void FromQuote() + { + AdxList sut = new(lookbackPeriods); + + foreach (Quote q in Quotes) { sut.Add(q); } + + sut.Should().HaveCount(Quotes.Count); + sut.Should().BeEquivalentTo(series); + } + + [TestMethod] + public override void FromQuoteBatch() + { + AdxList sut = new(lookbackPeriods) { Quotes }; + + IReadOnlyList series + = Quotes.ToAdx(lookbackPeriods); + + sut.Should().HaveCount(Quotes.Count); + sut.Should().BeEquivalentTo(series); + } +} diff --git a/tests/indicators/a-d/Adx/Adx.StaticSeries.Tests.cs b/tests/indicators/a-d/Adx/Adx.StaticSeries.Tests.cs index ca0a5c213..c89d64a17 100644 --- a/tests/indicators/a-d/Adx/Adx.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Adx/Adx.StaticSeries.Tests.cs @@ -10,18 +10,39 @@ public override void Standard() // proper quantities Assert.AreEqual(502, results.Count); + Assert.AreEqual(488, results.Count(x => x.Dx != null)); Assert.AreEqual(475, results.Count(x => x.Adx != null)); Assert.AreEqual(462, results.Count(x => x.Adxr != null)); // sample values + AdxResult r13 = results[13]; + Assert.AreEqual(null, r13.Pdi); + Assert.AreEqual(null, r13.Mdi); + Assert.AreEqual(null, r13.Dx); + Assert.AreEqual(null, r13.Adx); + + AdxResult r14 = results[14]; + Assert.AreEqual(21.9669, r14.Pdi.Round(4)); + Assert.AreEqual(18.5462, r14.Mdi.Round(4)); + Assert.AreEqual(8.4433, r14.Dx.Round(4)); + Assert.AreEqual(null, r14.Adx); + AdxResult r19 = results[19]; Assert.AreEqual(21.0361, r19.Pdi.Round(4)); Assert.AreEqual(25.0124, r19.Mdi.Round(4)); + Assert.AreEqual(8.6351, r19.Dx.Round(4)); Assert.AreEqual(null, r19.Adx); + AdxResult r26 = results[26]; + Assert.AreEqual(null, r26.Adx); + + AdxResult r27 = results[27]; + Assert.AreEqual(15.9459, r27.Adx.Round(4)); + AdxResult r29 = results[29]; Assert.AreEqual(37.9719, r29.Pdi.Round(4)); Assert.AreEqual(14.1658, r29.Mdi.Round(4)); + Assert.AreEqual(45.6600, r29.Dx.Round(4)); Assert.AreEqual(19.7949, r29.Adx.Round(4)); AdxResult r39 = results[39]; @@ -33,12 +54,14 @@ public override void Standard() AdxResult r248 = results[248]; Assert.AreEqual(32.3167, r248.Pdi.Round(4)); Assert.AreEqual(18.2471, r248.Mdi.Round(4)); + Assert.AreEqual(27.8255, r248.Dx.Round(4)); Assert.AreEqual(30.5903, r248.Adx.Round(4)); Assert.AreEqual(29.1252, r248.Adxr.Round(4)); AdxResult r501 = results[501]; Assert.AreEqual(17.7565, r501.Pdi.Round(4)); Assert.AreEqual(31.1510, r501.Mdi.Round(4)); + Assert.AreEqual(27.3873, r501.Dx.Round(4)); Assert.AreEqual(34.2987, r501.Adx.Round(4)); } diff --git a/tests/indicators/e-k/Ema/Ema.Increments.Tests.cs b/tests/indicators/e-k/Ema/Ema.Increments.Tests.cs index b19ce4c69..bfb52c7c0 100644 --- a/tests/indicators/e-k/Ema/Ema.Increments.Tests.cs +++ b/tests/indicators/e-k/Ema/Ema.Increments.Tests.cs @@ -1,7 +1,7 @@ namespace Increments; [TestClass] -public class Ema : IncrementsTestBase +public class Ema : BufferListTestBase { private const int lookbackPeriods = 14; diff --git a/tests/performance/Perf.BufferList.cs b/tests/performance/Perf.BufferList.cs new file mode 100644 index 000000000..ba4c27800 --- /dev/null +++ b/tests/performance/Perf.BufferList.cs @@ -0,0 +1,44 @@ +namespace Performance; + +// BUFFER-STYLE INCREMENTING INDICATORS + +[ShortRunJob] +public class BufferLists +{ + private static readonly IReadOnlyList quotes + = Data.GetDefault(); + + private static readonly QuoteHub provider = new(); + + private const int n = 14; + + [GlobalSetup] + public void Setup() => provider.Add(quotes); + + [GlobalCleanup] + public void Cleanup() + { + provider.EndTransmission(); + provider.Cache.Clear(); + } + + [Benchmark] + public AdxList AdxBuffer() + => new(n) { quotes }; + + [Benchmark] + public IReadOnlyList AdxSeries() + => quotes.ToAdx(n); + + [Benchmark] + public EmaList EmaBuffer() + => new(n) { quotes }; + + [Benchmark] + public IReadOnlyList EmaSeries() + => quotes.ToEma(n); + + [Benchmark] + public IReadOnlyList EmaStream() + => provider.ToEma(n).Results; +} diff --git a/tests/performance/Perf.Increments.cs b/tests/performance/Perf.Increments.cs deleted file mode 100644 index cb4ffdef1..000000000 --- a/tests/performance/Perf.Increments.cs +++ /dev/null @@ -1,95 +0,0 @@ -namespace Performance; - -// TIME-SERIES INDICATORS - -[ShortRunJob] -public class Incrementals -{ - private static readonly IReadOnlyList quotes - = Data.GetDefault(); - - private static readonly IReadOnlyList reusables - = quotes - .Cast() - .ToList(); - - private readonly QuoteHub provider = new(); - - [GlobalSetup] - public void Setup() => provider.Add(quotes); - - [GlobalCleanup] - public void Cleanup() - { - provider.EndTransmission(); - provider.Cache.Clear(); - } - - [Benchmark] - public object EmaIncRusBatch() - { - EmaList sut = new(14) { reusables }; - return sut; - } - - [Benchmark] - public object EmaIncRusItem() - { - EmaList sut = new(14); - - for (int i = 0; i < reusables.Count; i++) - { - sut.Add(reusables[i]); - } - - return sut; - } - - [Benchmark] - public object EmaIncRusSplit() - { - EmaList sut = new(14); - - for (int i = 0; i < reusables.Count; i++) - { - sut.Add(reusables[i].Timestamp, reusables[i].Value); - } - - return sut; - } - - [Benchmark] - public object EmaIncQotBatch() - { - EmaList sut = new(14) { quotes }; - return sut; - } - - [Benchmark] - public object EmaIncQot() - { - EmaList sut = new(14); - - for (int i = 0; i < quotes.Count; i++) - { - sut.Add(quotes[i]); - } - - return sut; - } - - // TIME-SERIES EQUIVALENTS - - [Benchmark] - public object EmaSeries() => quotes.ToEma(14); - - [Benchmark] - public object EmaIncrem() - { - EmaList ema = new(14) { quotes }; - return ema; - } - - [Benchmark] - public object EmaStream() => provider.ToEma(14).Results; -} From 98334681c724ac2440bb50e16a2b9b44d77d44f4 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Fri, 15 Nov 2024 23:53:05 -0500 Subject: [PATCH 13/18] ci: Add .NET 9 build actions (#1275) --- .gitattributes | 2 +- .github/workflows/deploy-package.yml | 2 +- .github/workflows/test-indicators.yml | 7 ++++--- .github/workflows/test-integration.yml | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.gitattributes b/.gitattributes index 462913965..9a5d6f371 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ # Normalize line endings. -* text=lf +* text=auto eol=lf diff --git a/.github/workflows/deploy-package.yml b/.github/workflows/deploy-package.yml index 3f226014b..58fe9f27d 100644 --- a/.github/workflows/deploy-package.yml +++ b/.github/workflows/deploy-package.yml @@ -37,7 +37,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: "8.x" + dotnet-version: "9.x" dotnet-quality: "ga" - name: Setup GitVersion diff --git a/.github/workflows/test-indicators.yml b/.github/workflows/test-indicators.yml index f69d693c2..9dfb63fe1 100644 --- a/.github/workflows/test-indicators.yml +++ b/.github/workflows/test-indicators.yml @@ -21,7 +21,8 @@ jobs: matrix: os: [windows-latest, ubuntu-latest, macos-latest] dotnet-version: ["9.x"] - # TODO: restore when runners have 9.x on all + # TODO: restore when GitHub Action runners + # have .NET 9.x SDK default installed # ["2.1.x", "6.x", "9.x"] env: @@ -29,8 +30,8 @@ jobs: # identifying primary configuration so only one reports coverage IS_PRIMARY: ${{ matrix.os == 'ubuntu-latest' && matrix.dotnet-version == '9.x' }} - # .NET SDK versions in the matrix that support `ga` quality spec - # versions before 5.x do not support it + # .NET SDK versions in the matrix that support + # `dotnet-quality: 'ga'`, not supported before 5.x SUPPORT_GA: ${{ contains(fromJson('["6.x", "9.x"]'), matrix.dotnet-version) }} steps: diff --git a/.github/workflows/test-integration.yml b/.github/workflows/test-integration.yml index 6da929b33..0117abb07 100644 --- a/.github/workflows/test-integration.yml +++ b/.github/workflows/test-integration.yml @@ -26,7 +26,7 @@ jobs: id: dotnet-new uses: actions/setup-dotnet@v4 with: - dotnet-version: "8.x" + dotnet-version: "9.x" dotnet-quality: "ga" - name: Build library From 204e959f11adeac68b12a2863ec34a67e539f1e8 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Sun, 1 Dec 2024 02:49:06 -0500 Subject: [PATCH 14/18] refactor: Naming standardization (#1273) Signed-off-by: Dave Skender <8432125+DaveSkender@users.noreply.github.com> --- .editorconfig | 95 +- src/Directory.Packages.props | 10 - src/Indicators.csproj | 7 +- .../IBufferList.cs | 0 .../Observables/StreamHub.Utilities.cs | 12 +- src/_common/Observables/StreamHub.cs | 2 +- src/_common/ObsoleteV3.Indicators.cs | 1237 +++++++++++++++++ src/_common/ObsoleteV3.cs | 139 +- src/_common/ObsoleteV3.md | 156 +-- src/_common/Quotes/Quote.Models.cs | 19 +- src/_common/Quotes/Quote.StreamHub.cs | 2 +- src/_common/Reusable/Reusable.Utilities.cs | 2 +- src/a-d/Adl/Adl.StreamHub.cs | 2 +- src/a-d/Adx/Adx.BufferList.cs | 2 +- src/a-d/Alligator/Alligator.StreamHub.cs | 2 +- src/a-d/Atr/Atr.StreamHub.cs | 2 +- src/a-d/AtrStop/AtrStop.StreamHub.cs | 4 +- src/e-k/Ema/Ema.StreamHub.cs | 2 +- .../ParabolicSar/ParabolicSar.StaticSeries.cs | 2 +- src/m-r/Renko/Renko.StreamHub.cs | 2 +- src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs | 2 +- src/s-z/Sma/Sma.StreamHub.cs | 2 +- .../StdDevChannels.StaticSeries.cs | 4 +- src/s-z/Tr/Tr.StreamHub.cs | 2 +- src/s-z/UlcerIndex/UlcerIndex.Models.cs | 6 +- tests/Directory.Packages.props | 2 - tests/external/application/Program.cs | 428 +++--- tests/external/application/README.md | 2 +- .../application/Test.Application.csproj | 6 +- .../integration/Tests.Integration.csproj | 2 + .../public-api/Tests.PublicApi.csproj | 2 + tests/indicators/Tests.Indicators.csproj | 2 + ....Tests.cs => StreamHub.Utilities.Tests.cs} | 16 +- .../Reusable/Reusable.Utilities.Tests.cs | 2 +- .../e-k/Kvo/Kvo.StaticSeries.Tests.cs | 2 +- .../ParabolicSar.StaticSeries.Tests.cs | 6 +- .../m-r/Renko/Renko.StaticSeries.Tests.cs | 2 +- tests/other/GlobalSuppressions.cs | 6 - tests/performance/Perf.StaticSeries.cs | 3 - tests/performance/Perf.Utility.cs | 4 +- tests/performance/Tests.Performance.csproj | 2 + tests/simulate/Simulator.cs | 214 +++ tests/simulate/Test.Simulation.csproj | 8 + .../_testdata/TestData.Getter.cs | 2 + .../_testdata/TestData.Imports.cs | 1 + .../_testdata/data/compare.csv | 0 .../_testdata/data/default.csv | 0 .../_testdata/data/intraday.csv | 0 .../_testdata/data/longest.csv | 0 .../_testdata/data/longish.csv | 0 50 files changed, 1955 insertions(+), 472 deletions(-) delete mode 100644 src/Directory.Packages.props rename src/_common/{Incrementals => BufferLists}/IBufferList.cs (100%) create mode 100644 src/_common/ObsoleteV3.Indicators.cs rename tests/indicators/_common/Observables/{StreamHub.Utilities.StaticSeries.Tests.cs => StreamHub.Utilities.Tests.cs} (88%) delete mode 100644 tests/other/GlobalSuppressions.cs create mode 100644 tests/simulate/Simulator.cs rename tests/{external/application => simulate}/_testdata/TestData.Getter.cs (98%) rename tests/{external/application => simulate}/_testdata/TestData.Imports.cs (97%) rename tests/{external/application => simulate}/_testdata/data/compare.csv (100%) rename tests/{external/application => simulate}/_testdata/data/default.csv (100%) rename tests/{external/application => simulate}/_testdata/data/intraday.csv (100%) rename tests/{external/application => simulate}/_testdata/data/longest.csv (100%) rename tests/{external/application => simulate}/_testdata/data/longish.csv (100%) diff --git a/.editorconfig b/.editorconfig index 795cdde3b..27719425c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,34 +1,67 @@ -# top-most EditorConfig file +# EditorConfig: https://EditorConfig.org + +# Top-level EditorConfig file root = true #### Core EditorConfig Options #### + +# Global settings for all files [*] charset = utf-8 end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true indent_style = space indent_size = 2 tab_width = 2 -line_length = 150 -trim_trailing_whitespace = true -insert_final_newline = true +max_line_length = 150 -# .NET Globals -[*.cs] +#### File Type Overrides #### + +# Markdown files +[*.md] + +# Allow trailing whitespace for line breaks +trim_trailing_whitespace = false + +# Batch and command files +[*.{cmd,bat}] + +# Use CRLF for Windows specific files +end_of_line = crlf + +# .NET source files +[*.{cs,vb}] + + # set indentation to 4 spaces indent_size = 4 tab_width = 4 #### .NET Coding Conventions #### -# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/overview +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ -# Organize usings -dotnet_separate_import_directive_groups = false -dotnet_sort_system_directives_first = true +# File headers are disabled file_header_template = unset +# Organize 'using' directives +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false + +# Simplify type names and remove unnecessary qualifiers +dotnet_diagnostic.IDE0001.severity = warning # Simplify type names +dotnet_style_qualification_for_field = false:warning # Prefer 'field' over 'this.field' +dotnet_style_qualification_for_property = false:warning # Prefer 'property' over 'this.property' +dotnet_style_qualification_for_method = false:warning # Prefer 'method()' over 'this.method()' +dotnet_style_qualification_for_event = false:warning # Prefer 'event' over 'this.event' + # Modifier preferences -dotnet_style_require_accessibility_modifiers = for_non_interface_members +dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning # Require modifiers except for interface members + +# Ensure space around colon in named arguments +dotnet_style_space_around_colon_in_named_argument = true:suggestion +dotnet_diagnostic.IDE0040.severity = suggestion # Expression-level preferences dotnet_style_coalesce_expression = true:suggestion @@ -38,19 +71,21 @@ dotnet_style_namespace_match_folder = false:none dotnet_style_null_propagation = true:suggestion dotnet_style_object_initializer = true:suggestion dotnet_style_prefer_auto_properties = true:suggestion -dotnet_style_prefer_compound_assignment = true -dotnet_style_prefer_conditional_expression_over_assignment = true -dotnet_style_prefer_conditional_expression_over_return = true -dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed -dotnet_style_prefer_inferred_anonymous_type_member_names = true -dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion -dotnet_style_prefer_simplified_boolean_expressions = true -dotnet_style_prefer_simplified_interpolation = true +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion # Prefer 'int' over 'Int32' # Collection expression preferences dotnet_style_prefer_collection_expression = true:suggestion -dotnet_diagnostic.IDE0305.severity = none # exclude collection expression for fluent +dotnet_diagnostic.IDE0305.severity = none # Exclude collection expression for fluent APIs + csharp_space_between_square_brackets = false csharp_space_between_empty_square_brackets = false @@ -87,13 +122,21 @@ dotnet_naming_style.begins_with_i.capitalization = pascal_case dotnet_code_quality_explicit_tuple_names = true:suggestion dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +# Diagnostic analyzer suppressions +dotnet_diagnostic.IDE0058.severity = none # Unused expression value, use _ discard variable + +# remove this when no-longer supporting pre-9.0 frameworks +dotnet_diagnostic.IDE0330.severity = none # Prefer 'System.Threading.Lock' + #### C# Coding Conventions #### # https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options +[*.cs] + # var preferences -csharp_style_var_elsewhere = false -csharp_style_var_for_built_in_types = false -csharp_style_var_when_type_is_apparent = false +csharp_style_var_elsewhere = false # Do not prefer 'var' elsewhere +csharp_style_var_for_built_in_types = false # Do not prefer 'var' for built-in types +csharp_style_var_when_type_is_apparent = false # Do not prefer 'var' when type is apparent # Labeling and using directives csharp_indent_labels = one_less_than_current @@ -147,9 +190,3 @@ csharp_new_line_before_finally = true csharp_new_line_before_members_in_object_initializers = false csharp_new_line_before_members_in_anonymous_types = false csharp_new_line_between_query_expression_clauses = false - -# Diagnostic analyzer suppressions -dotnet_diagnostic.IDE0058.severity = none # Unused expression value, use _ discard variable - -# remove this when no-longer supporting pre-9.0 frameworks -dotnet_diagnostic.IDE0330.severity = none # Prefer 'System.Threading.Lock' diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props deleted file mode 100644 index 11f9313e2..000000000 --- a/src/Directory.Packages.props +++ /dev/null @@ -1,10 +0,0 @@ - - - true - true - $(NoWarn);NU1507 - - - - - diff --git a/src/Indicators.csproj b/src/Indicators.csproj index 3d4896b3b..74b2f62f9 100644 --- a/src/Indicators.csproj +++ b/src/Indicators.csproj @@ -22,7 +22,7 @@ https://dotnet.stockindicators.dev Skender.Stock.Indicators - Stock;Market;Indicators;Technical;Analysis;Algorithmic;Trading;Trade;Finance; + Stock;Market;Indicators;Technical;Analysis;Algorithmic;Trading;Trade;Finance; README.md Apache-2.0 @@ -43,7 +43,8 @@ true true true - true + $(NoWarn);NU1507 + @@ -66,7 +67,7 @@ - + diff --git a/src/_common/Incrementals/IBufferList.cs b/src/_common/BufferLists/IBufferList.cs similarity index 100% rename from src/_common/Incrementals/IBufferList.cs rename to src/_common/BufferLists/IBufferList.cs diff --git a/src/_common/Observables/StreamHub.Utilities.cs b/src/_common/Observables/StreamHub.Utilities.cs index 1721a775a..4890760c3 100644 --- a/src/_common/Observables/StreamHub.Utilities.cs +++ b/src/_common/Observables/StreamHub.Utilities.cs @@ -19,7 +19,7 @@ internal static bool TryFindIndex( out int index) where T : ISeries { - index = cache.GetIndex(timestamp, false); + index = cache.IndexOf(timestamp, false); return index != -1; } @@ -32,7 +32,7 @@ internal static bool TryFindIndex( /// Throw exception when item is not found. /// Index position. /// When item is not found (should never happen). - internal static int GetIndex( + internal static int IndexOf( this IReadOnlyList cache, T cachedItem, bool throwOnFail) @@ -105,7 +105,7 @@ internal static int GetIndex( /// /// Only use this when you are looking for a point in time /// without a matching item for context. In most cases - /// is more appropriate. + /// is more appropriate. /// /// Type of the items in the cache, must implement ISeries. /// The cache to search. @@ -113,7 +113,7 @@ internal static int GetIndex( /// Throw exception when timestamp is not found. /// Index position. /// When timestamp is not found (should never happen). - internal static int GetIndex( + internal static int IndexOf( this IReadOnlyList cache, DateTime timestamp, bool throwOnFail) @@ -154,13 +154,13 @@ internal static int GetIndex( /// /// Only use this when you are looking for a point in time /// without a matching item for context. In most cases - /// is more appropriate. + /// is more appropriate. /// /// Type of the items in the cache, must implement ISeries. /// The cache to search. /// Timestamp of cached item. /// First index position or -1 if not found. - internal static int GetIndexGte( + internal static int IndexGte( this IReadOnlyList cache, DateTime timestamp) where T : ISeries diff --git a/src/_common/Observables/StreamHub.cs b/src/_common/Observables/StreamHub.cs index 5f9ae3a89..d1437cf01 100644 --- a/src/_common/Observables/StreamHub.cs +++ b/src/_common/Observables/StreamHub.cs @@ -360,7 +360,7 @@ public void Rebuild(DateTime fromTimestamp) RemoveRange(fromTimestamp, notify: false); // get provider position - int provIndex = ProviderCache.GetIndexGte(fromTimestamp); + int provIndex = ProviderCache.IndexGte(fromTimestamp); // rebuild if (provIndex >= 0) diff --git a/src/_common/ObsoleteV3.Indicators.cs b/src/_common/ObsoleteV3.Indicators.cs new file mode 100644 index 000000000..f69cc2a86 --- /dev/null +++ b/src/_common/ObsoleteV3.Indicators.cs @@ -0,0 +1,1237 @@ +using System.Diagnostics.CodeAnalysis; + +#pragma warning disable CS1591 // Missing XML comments + +namespace Skender.Stock.Indicators; + +// OBSOLETE IN v3.0.0 +public static partial class Indicator +{ + // GENERAL INDICATOR METHODS + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetAdl(..)` to `ToAdl(..)`", false)] + public static IEnumerable GetAdl( + this IEnumerable quotes) + where TQuote : IQuote + => quotes.ToSortedList().ToAdl(); + + [ExcludeFromCodeCoverage] + [Obsolete("Use a chained `results.ToSma(smaPeriods)` for moving averages.", true)] + public static IEnumerable GetAdl( + this IEnumerable quotes, int smaPeriods) + where TQuote : IQuote + => quotes.ToSortedList().ToAdl(); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetAdx(..)` to `ToAdx(..)`", false)] + public static IEnumerable GetAdx( + this IEnumerable quotes, int lookbackPeriods = 14) + where TQuote : IQuote + => quotes.ToSortedList().ToAdx(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetAlligator(..)` to `ToAlligator(..)`", false)] + public static IEnumerable GetAlligator( + this IEnumerable quotes, + int jawPeriods = 13, + int jawOffset = 8, + int teethPeriods = 8, + int teethOffset = 5, + int lipsPeriods = 5, + int lipsOffset = 3) + where TQuote : IQuote + => quotes.ToSortedList().ToAlligator( + jawPeriods, jawOffset, + teethPeriods, teethOffset, + lipsPeriods, lipsOffset); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToAlligator(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetAlligator( + this IEnumerable<(DateTime d, double v)> priceTuples, + int jawPeriods = 13, + int jawOffset = 8, + int teethPeriods = 8, + int teethOffset = 5, + int lipsPeriods = 5, + int lipsOffset = 3) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToAlligator( + jawPeriods, jawOffset, + teethPeriods, teethOffset, + lipsPeriods, lipsOffset); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetAlma(..)` to `ToAlma(..)`", false)] + public static IEnumerable GetAlma( + this IEnumerable quotes, + int lookbackPeriods = 9, + double offset = 0.85, + double sigma = 6) + where TQuote : IQuote + => quotes.ToSortedList().ToAlma(lookbackPeriods, offset, sigma); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToAlma(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetAlma( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods = 9, + double offset = 0.85, + double sigma = 6) => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToAlma(lookbackPeriods, offset, sigma); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetAroon(..)` to `ToAroon(..)`", false)] + public static IEnumerable GetAroon( + this IEnumerable quotes, int lookbackPeriods = 25) + where TQuote : IQuote + => quotes.ToSortedList().ToAroon(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetAtr(..)` to `ToAtr(..)`", false)] + public static IEnumerable GetAtr( + this IEnumerable quotes, int lookbackPeriods = 14) + where TQuote : IQuote + => quotes.ToSortedList().ToAtr(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetAtrStop(..)` to `ToAtrStop(..)`", false)] + public static IEnumerable GetAtrStop( + this IEnumerable quotes, + int lookbackPeriods = 21, + double multiplier = 3, + EndType endType = EndType.Close) + where TQuote : IQuote + => quotes.ToSortedList().ToAtrStop(lookbackPeriods, multiplier, endType); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetAwesome(..)` to `ToAwesome(..)`", false)] + public static IEnumerable GetAwesome( + this IEnumerable quotes, int fastPeriods = 5, int slowPeriods = 34) + where TQuote : IQuote + => quotes.ToSortedList().ToAwesome(fastPeriods, slowPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToAwesome(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetAwesome( + this IEnumerable<(DateTime d, double v)> priceTuples, + int fastPeriods = 5, + int slowPeriods = 34) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToAwesome(fastPeriods, slowPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetBeta(..)` to `ToBeta(..)`", false)] + public static IEnumerable GetBeta( + this IEnumerable quotesEval, + IEnumerable quotesMarket, + int lookbackPeriods, + BetaType type = BetaType.Standard) + where TQuote : IQuote + => quotesEval + .ToSortedList() + .ToBeta(quotesMarket.ToSortedList(), lookbackPeriods, type); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToBeta(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetBeta( + this IEnumerable<(DateTime d, double v)> evalTuple, + IEnumerable<(DateTime d, double v)> mrktTuple, + int lookbackPeriods, + BetaType type = BetaType.Standard) + => evalTuple + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToBeta( + mrktTuple + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList(), + lookbackPeriods, + type); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetBollingerBands(..)` to `ToBollingerBands(..)`", false)] + public static IEnumerable GetBollingerBands( + this IEnumerable quotes, + int lookbackPeriods = 20, + double standardDeviations = 2) + where TQuote : IQuote + => quotes.ToSortedList() + .ToBollingerBands( + lookbackPeriods, + standardDeviations); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToBollingerBands(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetBollingerBands( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods = 20, + double standardDeviations = 2) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToBollingerBands(lookbackPeriods, standardDeviations); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetBop(..)` to `ToBop(..)`", false)] + public static IEnumerable GetBop( + this IEnumerable quotes, int smoothPeriods = 14) + where TQuote : IQuote + => quotes.ToSortedList().ToBop(smoothPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetCci(..)` to `ToCci(..)`", false)] + public static IEnumerable GetCci( + this IEnumerable quotes, int lookbackPeriods = 20) + where TQuote : IQuote + => quotes.ToSortedList().ToCci(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetChaikinOsc(..)` to `ToChaikinOsc(..)`", false)] + public static IEnumerable GetChaikinOsc( + this IEnumerable quotes, int fastPeriods = 3, int slowPeriods = 10) + where TQuote : IQuote + => quotes.ToSortedList().ToChaikinOsc(fastPeriods, slowPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetChandelier(..)` to `ToChandelier(..)`", false)] + public static IEnumerable GetChandelier( + this IEnumerable quotes, + int lookbackPeriods = 22, + double multiplier = 3, + ChandelierType type = ChandelierType.Long) + where TQuote : IQuote + => quotes.ToSortedList() + .ToChandelier(lookbackPeriods, multiplier, type); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetChop(..)` to `ToChop(..)`", false)] + public static IEnumerable GetChop( + this IEnumerable quotes, int lookbackPeriods = 14) + where TQuote : IQuote + => quotes.ToSortedList().ToChop(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetCmf(..)` to `ToCmf(..)`", false)] + public static IEnumerable GetCmf( + this IEnumerable quotes, int lookbackPeriods = 20) + where TQuote : IQuote + => quotes.ToSortedList().ToCmf(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetCmo(..)` to `ToCmo(..)`", false)] + public static IEnumerable GetCmo( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote + => quotes.ToSortedList().ToCmo(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToCmo(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetCmo( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToCmo(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetConnorsRsi(..)` to `ToConnorsRsi(..)`", false)] + public static IEnumerable GetConnorsRsi( + this IEnumerable quotes, + int rsiPeriods = 3, int streakPeriods = 2, int rankPeriods = 100) + where TQuote : IQuote + => quotes.ToSortedList() + .ToConnorsRsi(rsiPeriods, streakPeriods, rankPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToConnorsRsi(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetConnorsRsi( + this IEnumerable<(DateTime d, double v)> priceTuples, + int rsiPeriods = 3, + int streakPeriods = 2, + int rankPeriods = 100) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToConnorsRsi(rsiPeriods, streakPeriods, rankPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetCorrelation(..)` to `ToCorrelation(..)`", false)] + public static IEnumerable GetCorrelation( + this IEnumerable quotesA, + IEnumerable quotesB, int lookbackPeriods) + where TQuote : IQuote + => quotesA.ToSortedList().ToCorrelation(quotesB.ToSortedList(), lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToCorrelation(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetCorrelation( + this IEnumerable<(DateTime d, double v)> tuplesA, + IEnumerable<(DateTime d, double v)> tuplesB, + int lookbackPeriods) + => tuplesA + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToCorrelation( + tuplesB + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList(), + lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetDema(..)` to `ToDema(..)`", false)] + public static IEnumerable GetDema( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote + => quotes.ToSortedList().ToDema(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToDema(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetDema( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToDema(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetDoji(..)` to `ToDoji(..)`", false)] + public static IEnumerable GetDoji( + this IEnumerable quotes, double maxPriceChangePercent = 0.1) + where TQuote : IQuote + => quotes.ToSortedList().ToDoji(maxPriceChangePercent); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetDonchian(..)` to `ToDonchian(..)`", false)] + public static IEnumerable GetDonchian( + this IEnumerable quotes, int lookbackPeriods = 20) + where TQuote : IQuote + => quotes.ToSortedList().ToDonchian(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetDpo(..)` to `ToDpo(..)`", false)] + public static IEnumerable GetDpo( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote + => quotes.ToSortedList().ToDpo(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToDpo(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetDpo( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToDpo(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetDynamic(..)` to `ToDynamic(..)`", false)] + public static IEnumerable GetDynamic( + this IEnumerable quotes, int lookbackPeriods, double kFactor = 0.6) + where TQuote : IQuote + => quotes.ToSortedList().ToDynamic(lookbackPeriods, kFactor); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToDynamic(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetDynamic( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods, + double kFactor = 0.6) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToDynamic(lookbackPeriods, kFactor); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetElderRay(..)` to `ToElderRay(..)`", false)] + public static IEnumerable GetElderRay( + this IEnumerable quotes, int lookbackPeriods = 13) + where TQuote : IQuote + => quotes.ToSortedList().ToElderRay(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetEma(..)` to `ToEma(..)`", false)] + public static IEnumerable GetEma( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote + => quotes.ToSortedList().ToEma(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToEma(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetEma( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToEma(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetEpma(..)` to `ToEpma(..)`", false)] + public static IEnumerable GetEpma( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote + => quotes.ToSortedList().ToEpma(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToEpma(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetEpma( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToEpma(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetFcb(..)` to `ToFcb(..)`", false)] + public static IEnumerable GetFcb( + this IEnumerable quotes, int windowSpan = 2) + where TQuote : IQuote + => quotes.ToSortedList().ToFcb(windowSpan); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetFisherTransform(..)` to `ToFisherTransform(..)`", false)] + public static IEnumerable GetFisherTransform( + this IEnumerable quotes, int lookbackPeriods = 10) + where TQuote : IQuote + => quotes.ToSortedList().ToFisherTransform(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToFisherTransform(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetFisherTransform( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods = 10) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToFisherTransform(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetForceIndex(..)` to `ToForceIndex(..)`", false)] + public static IEnumerable GetForceIndex( + this IEnumerable quotes, int lookbackPeriods = 2) + where TQuote : IQuote + => quotes.ToSortedList().ToForceIndex(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetFractal(..)` to `ToFractal(..)`", false)] + public static IEnumerable GetFractal( + this IEnumerable quotes, int windowSpan = 2, EndType endType = EndType.HighLow) + where TQuote : IQuote + => quotes.ToSortedList().ToFractal(windowSpan, windowSpan, endType); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetFractal(..)` to `ToFractal(..)`", false)] + public static IEnumerable GetFractal( + this IEnumerable quotes, int leftSpan, int rightSpan, EndType endType = EndType.HighLow) + where TQuote : IQuote + => quotes.ToSortedList().ToFractal(leftSpan, rightSpan, endType); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetGator(..)` to `ToGator(..)`", false)] + public static IEnumerable GetGator( + this IEnumerable quotes) + where TQuote : IQuote => quotes.ToSortedList().ToGator(); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToGator(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetGator( + this IEnumerable<(DateTime d, double v)> priceTuples) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToGator(); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetHeikinAshi(..)` to `ToHeikinAshi(..)`", false)] + public static IEnumerable GetHeikinAshi( + this IEnumerable quotes) + where TQuote : IQuote => quotes.ToSortedList().ToHeikinAshi(); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetHma(..)` to `ToHma(..)`", false)] + public static IEnumerable GetHma( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote => quotes.ToSortedList().ToHma(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToHma(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetHma( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToHma(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetHtTrendline(..)` to `ToHtTrendline(..)`", false)] + public static IEnumerable GetHtTrendline( + this IEnumerable quotes) + where TQuote : IQuote => quotes.ToSortedList().ToHtTrendline(); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToHtTrendline(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetHtTrendline( + this IEnumerable<(DateTime d, double v)> priceTuples) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToHtTrendline(); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetHurst(..)` to `ToHurst(..)`", false)] + public static IEnumerable GetHurst( + this IEnumerable quotes, int lookbackPeriods = 100) + where TQuote : IQuote + => quotes.ToSortedList().ToHurst(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToHurst(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetHurst( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods = 100) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToHurst(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetIchimoku(..)` to `ToIchimoku(..)`", false)] + public static IEnumerable GetIchimoku( + this IEnumerable quotes, + int tenkanPeriods = 9, + int kijunPeriods = 26, + int senkouBPeriods = 52) + where TQuote : IQuote + => quotes.ToSortedList() + .ToIchimoku(tenkanPeriods, kijunPeriods, senkouBPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetKama(..)` to `ToKama(..)`", false)] + public static IEnumerable GetKama( + this IEnumerable quotes, + int erPeriods = 10, + int fastPeriods = 2, + int slowPeriods = 30) + where TQuote : IQuote + => quotes.ToSortedList().ToKama(erPeriods, fastPeriods, slowPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToKama(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetKama( + this IEnumerable<(DateTime d, double v)> priceTuples, + int erPeriods = 10, + int fastPeriods = 2, + int slowPeriods = 30) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToKama(erPeriods, fastPeriods, slowPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetKeltner(..)` to `ToKeltner(..)`", false)] + public static IEnumerable GetKeltner( + this IEnumerable quotes, + int emaPeriods = 20, + int multiplier = 2, + int atrPeriods = 10) + where TQuote : IQuote + => quotes.ToSortedList().ToKeltner(emaPeriods, multiplier, atrPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetKvo(..)` to `ToKvo(..)`", false)] + public static IEnumerable GetKvo( + this IEnumerable quotes, + int fastPeriods = 34, + int slowPeriods = 55, + int signalPeriods = 13) + where TQuote : IQuote + => quotes.ToSortedList() + .ToKvo(fastPeriods, slowPeriods, signalPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetMacd(..)` to `ToMacd(..)`", false)] + public static IEnumerable GetMacd( + this IEnumerable quotes, + int fastPeriods = 12, + int slowPeriods = 26, + int signalPeriods = 9) + where TQuote : IQuote + => quotes.ToSortedList().ToMacd(fastPeriods, slowPeriods, signalPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToMacd(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetMacd( + this IEnumerable<(DateTime d, double v)> priceTuples, + int fastPeriods = 12, + int slowPeriods = 26, + int signalPeriods = 9) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToMacd(fastPeriods, slowPeriods, signalPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetMaEnvelopes(..)` to `ToMaEnvelopes(..)`", false)] + public static IEnumerable GetMaEnvelopes( + this IEnumerable quotes, + int lookbackPeriods, + double percentOffset = 2.5, + MaType movingAverageType = MaType.SMA) + where TQuote : IQuote + => quotes.ToSortedList() + .ToMaEnvelopes(lookbackPeriods, percentOffset, movingAverageType); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToMaEnvelopes(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetMaEnvelopes( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods = 20, + double percentOffset = 2.5, + MaType movingAverageType = MaType.SMA) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToMaEnvelopes(lookbackPeriods, percentOffset, movingAverageType); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetMama(..)` to `ToMama(..)`", false)] + public static IEnumerable GetMama( + this IEnumerable quotes, + double fastLimit = 0.5, + double slowLimit = 0.05) + where TQuote : IQuote + => quotes.ToSortedList().ToMama(fastLimit, slowLimit); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToMama(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetMama( + this IEnumerable<(DateTime d, double v)> priceTuples, + double fastLimit = 0.5, + double slowLimit = 0.05) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToMama(fastLimit, slowLimit); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetMarubozu(..)` to `ToMarubozu(..)`", false)] + public static IEnumerable GetMarubozu( + this IEnumerable quotes, double minBodyPercent = 95) + where TQuote : IQuote + => quotes.ToSortedList().ToMarubozu(minBodyPercent); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetMfi(..)` to `ToMfi(..)`", false)] + public static IEnumerable GetMfi( + this IEnumerable quotes, int lookbackPeriods = 14) + where TQuote : IQuote + => quotes.ToSortedList().ToMfi(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetObv(..)` to `ToObv(..)`", false)] + public static IEnumerable GetObv( + this IEnumerable quotes) + where TQuote : IQuote => quotes.ToSortedList().ToObv(); + + [ExcludeFromCodeCoverage] + [Obsolete("Use a chained `results.ToSma(smaPeriods)` for moving averages.", true)] + public static IEnumerable GetObv( + this IEnumerable quotes, int smaPeriods) + where TQuote : IQuote + => quotes.ToSortedList().ToObv(); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetParabolicSar(..)` to `ToParabolicSar(..)`", false)] + public static IEnumerable GetParabolicSar( + this IEnumerable quotes, + double accelerationStep = 0.02, + double maxAccelerationFactor = 0.2) + where TQuote : IQuote + => quotes.ToSortedList() + .ToParabolicSar(accelerationStep, maxAccelerationFactor); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetParabolicSar(..)` to `ToParabolicSar(..)`", false)] + public static IEnumerable GetParabolicSar( + this IEnumerable quotes, + double accelerationStep, + double maxAccelerationFactor, + double initialFactor) + where TQuote : IQuote + => quotes.ToSortedList() + .ToParabolicSar(accelerationStep, maxAccelerationFactor, initialFactor); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetPivotPoints(..)` to `ToPivotPoints(..)`", false)] + public static IEnumerable GetPivotPoints( + this IEnumerable quotes, + PeriodSize windowSize, + PivotPointType pointType = PivotPointType.Standard) + where TQuote : IQuote + => quotes.ToSortedList().ToPivotPoints(windowSize, pointType); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetPivots(..)` to `ToPivots(..)`", false)] + public static IEnumerable GetPivots( + this IEnumerable quotes, + int leftSpan = 2, + int rightSpan = 2, + int maxTrendPeriods = 20, + EndType endType = EndType.HighLow) + where TQuote : IQuote + => quotes.ToSortedList() + .ToPivots(leftSpan, rightSpan, maxTrendPeriods, endType); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetPmo(..)` to `ToPmo(..)`", false)] + public static IEnumerable GetPmo( + this IEnumerable quotes, + int timePeriods = 35, + int smoothPeriods = 20, + int signalPeriods = 10) + where TQuote : IQuote + => quotes.ToSortedList().ToPmo(timePeriods, smoothPeriods, signalPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToPmo(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetPmo( + this IEnumerable<(DateTime d, double v)> priceTuples, + int timePeriods = 35, + int smoothPeriods = 20, + int signalPeriods = 10) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToPmo(timePeriods, smoothPeriods, signalPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetPrs(..)` to `ToPrs(..)`", false)] + public static IEnumerable GetPrs( + this IEnumerable quotesEval, + IEnumerable quotesBase, int? lookbackPeriods = null) + where TQuote : IQuote + => quotesBase.ToSortedList() + .ToPrs(quotesEval.ToSortedList(), lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use a chained `results.ToSma(smaPeriods)` for moving averages.", true)] + public static IEnumerable GetPrs( + this IEnumerable quotesEval, + IEnumerable quotesBase, int? lookbackPeriods, int? smaPeriods = null) + where TQuote : IQuote + => quotesEval + .ToSortedList() + .Use(CandlePart.Close) + .ToPrs(quotesBase.ToSortedList().Use(CandlePart.Close), lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToPrs(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetPrs( + this IEnumerable<(DateTime d, double v)> tupleEval, + IEnumerable<(DateTime d, double v)> tupleBase, + int lookbackPeriods = 0, + int smaPeriods = 0) + => tupleEval + .Select(t => new QuotePart(t.d, t.v)).ToSortedList() + .ToPrs(tupleBase + .Select(t => new QuotePart(t.d, t.v)).ToSortedList(), + lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetPvo(..)` to `ToPvo(..)`", false)] + public static IEnumerable GetPvo( + this IEnumerable quotes, + int fastPeriods = 9, int slowPeriods = 12, int signalPeriods = 9) + where TQuote : IQuote + => quotes.ToSortedList().ToPvo(fastPeriods, slowPeriods, signalPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetRenko(..)` to `ToRenko(..)`", false)] + public static IEnumerable GetRenko( + this IEnumerable quotes, decimal brickSize, EndType endType = EndType.Close) + where TQuote : IQuote + => quotes.ToSortedList().ToRenko(brickSize, endType); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetRenkoAtr(..)` to `ToRenkoAtr(..)`", false)] + public static IEnumerable GetRenkoAtr( + this IEnumerable quotes, int atrPeriods, EndType endType = EndType.Close) + where TQuote : IQuote + => quotes.ToSortedList().ToRenkoAtr(atrPeriods, endType); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetRoc(..)` to `ToRoc(..)`", false)] + public static IEnumerable GetRoc( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote + => quotes.ToSortedList().ToRoc(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use a chained `results.ToSma(smaPeriods)` for moving averages.", true)] + public static IEnumerable GetRoc( + this IEnumerable quotes, int lookbackPeriods, int smaPeriods) + where TQuote : IQuote + => quotes.ToSortedList().Use(CandlePart.Close).ToRoc(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToRoc(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetRoc( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods, + int? smaPeriods = null) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToRoc(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetRocWb(..)` to `ToRocWb(..)`", false)] + public static IEnumerable GetRocWb( + this IEnumerable quotes, + int lookbackPeriods, int emaPeriods, int stdDevPeriods) + where TQuote : IQuote + => quotes.ToSortedList() + .ToRocWb(lookbackPeriods, emaPeriods, stdDevPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToRocWb(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetRocWb( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods, + int emaPeriods, + int stdDevPeriods) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToRocWb(lookbackPeriods, emaPeriods, stdDevPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetRollingPivots(..)` to `ToRollingPivots(..)`", false)] + public static IEnumerable GetRollingPivots( + this IEnumerable quotes, + int windowPeriods, + int offsetPeriods, + PivotPointType pointType = PivotPointType.Standard) + where TQuote : IQuote + => quotes.ToSortedList() + .ToRollingPivots(windowPeriods, offsetPeriods, pointType); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetRsi(..)` to `ToRsi(..)`", false)] + public static IEnumerable GetRsi( + this IEnumerable quotes, int lookbackPeriods = 14) + where TQuote : IQuote + => quotes.ToSortedList().ToRsi(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToRsi(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetRsi( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToRsi(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetSlope(..)` to `ToSlope(..)`", false)] + public static IEnumerable GetSlope( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote + => quotes.ToSortedList().ToSlope(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToSlope(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetSlope( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToSlope(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetSma(..)` to `ToSma(..)`", false)] + public static IEnumerable GetSma( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote + => quotes.ToSortedList().ToSma(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToSma(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetSma( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToSma(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetSmaAnalysis(..)` to `ToSmaAnalysis(..)`", false)] + public static IEnumerable GetSmaAnalysis( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote + => quotes.ToSortedList().ToSmaAnalysis(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToSmaAnalysis(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetSmaAnalysis( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToSmaAnalysis(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetSmi(..)` to `ToSmi(..)`", false)] + public static IEnumerable GetSmi( + this IEnumerable quotes, + int lookbackPeriods = 13, + int firstSmoothPeriods = 25, + int secondSmoothPeriods = 2, + int signalPeriods = 3) + where TQuote : IQuote + => quotes.ToSortedList() + .ToSmi(lookbackPeriods, firstSmoothPeriods, secondSmoothPeriods, signalPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetSmma(..)` to `ToSmma(..)`", false)] + public static IEnumerable GetSmma( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote + => quotes.ToSortedList().ToSmma(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToSmma(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetSmma( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToSmma(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetStarcBands(..)` to `ToStarcBands(..)`", false)] + public static IEnumerable GetStarcBands( + this IEnumerable quotes, + int smaPeriods, + double multiplier = 2, + int atrPeriods = 10) + where TQuote : IQuote + => quotes.ToSortedList().ToStarcBands(smaPeriods, multiplier, atrPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetStc(..)` to `ToStc(..)`", false)] + public static IEnumerable GetStc( + this IEnumerable quotes, + int cyclePeriods = 10, + int fastPeriods = 23, + int slowPeriods = 50) + where TQuote : IQuote + => quotes.ToSortedList().ToStc(cyclePeriods, fastPeriods, slowPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToStc(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetStc( + this IEnumerable<(DateTime d, double v)> priceTuples, + int cyclePeriods = 10, + int fastPeriods = 23, + int slowPeriods = 50) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToStc(cyclePeriods, fastPeriods, slowPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetStdDev(..)` to `ToStdDev(..)`", false)] + public static IEnumerable GetStdDev( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote + => quotes.ToSortedList().ToStdDev(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use a chained `results.ToSma(smaPeriods)` for moving averages.", true)] + public static IEnumerable GetStdDev( + this IEnumerable quotes, int lookbackPeriods, int smaPeriods) + where TQuote : IQuote + => quotes.ToSortedList().Use(CandlePart.Close).ToStdDev(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToStdDev(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetStdDev( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToStdDev(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetStdDevChannels(..)` to `ToStdDevChannels(..)`", false)] + public static IEnumerable GetStdDevChannels( + this IEnumerable quotes, int? lookbackPeriods = 20, double stdDeviations = 2) + where TQuote : IQuote + => quotes.ToSortedList().ToStdDevChannels(lookbackPeriods, stdDeviations); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToStdDevChannels(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetStdDevChannels( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods = 20, + double stdDeviations = 2) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToStdDevChannels(lookbackPeriods, stdDeviations); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetStoch(..)` to `ToStoch(..)`", false)] + public static IEnumerable GetStoch( + this IEnumerable quotes, + int lookbackPeriods = 14, + int signalPeriods = 3, + int smoothPeriods = 3) + where TQuote : IQuote + => quotes.ToSortedList().ToStoch(lookbackPeriods, signalPeriods, smoothPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToStochRsi(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetStochRsi( + this IEnumerable<(DateTime d, double v)> priceTuples, + int rsiPeriods = 14, + int stochPeriods = 14, + int signalPeriods = 3, + int smoothPeriods = 1) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToStochRsi(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetStoch(..)` to `ToStoch(..)`", false)] + public static IEnumerable GetStoch( + this IEnumerable quotes, + int lookbackPeriods, + int signalPeriods, + int smoothPeriods, + double kFactor, + double dFactor, + MaType movingAverageType) + where TQuote : IQuote + => quotes.ToSortedList().ToStoch( + lookbackPeriods, + signalPeriods, + smoothPeriods, + kFactor, + dFactor, + movingAverageType); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetStochRsi(..)` to `ToStochRsi(..)`", false)] + public static IEnumerable GetStochRsi( + this IEnumerable quotes, + int rsiPeriods, + int stochPeriods, + int signalPeriods, + int smoothPeriods = 1) + where TQuote : IQuote + => quotes.ToSortedList().ToStochRsi( + rsiPeriods, + stochPeriods, + signalPeriods, + smoothPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetSuperTrend(..)` to `ToSuperTrend(..)`", false)] + public static IEnumerable GetSuperTrend( + this IEnumerable quotes, int lookbackPeriods = 10, double multiplier = 3) + where TQuote : IQuote + => quotes.ToSortedList().ToSuperTrend(lookbackPeriods, multiplier); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetT3(..)` to `ToT3(..)`", false)] + public static IEnumerable GetT3( + this IEnumerable quotes, int lookbackPeriods = 5, double volumeFactor = 0.7) + where TQuote : IQuote + => quotes.ToSortedList().ToT3(lookbackPeriods, volumeFactor); + + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToT3(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetT3( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods = 5, + double volumeFactor = 0.7) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToT3(lookbackPeriods, volumeFactor); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetTema(..)` to `ToTema(..)`", false)] + public static IEnumerable GetTema( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote + => quotes.ToSortedList().ToTema(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToTema(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetTema( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToTema(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetTr(..)` to `ToTr(..)`", false)] + public static IEnumerable GetTr( + this IEnumerable quotes) + where TQuote : IQuote => quotes.ToSortedList().ToTr(); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetTrix(..)` to `ToTrix(..)`", false)] + public static IEnumerable GetTrix( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote => quotes.ToSortedList().ToTrix(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToTrix(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetTrix( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToTrix(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use a chained `results.ToSma(smaPeriods)` for moving averages.", true)] + public static IEnumerable GetTrix( + this IEnumerable quotes, int lookbackPeriods, int signalPeriods) + where TQuote : IQuote + => quotes.ToSortedList().Use(CandlePart.Close).ToTrix(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetTsi(..)` to `ToTsi(..)`", false)] + public static IEnumerable GetTsi( + this IEnumerable quotes, + int lookbackPeriods = 25, + int smoothPeriods = 13, + int signalPeriods = 7) + where TQuote : IQuote + => quotes.ToSortedList() + .ToTsi(lookbackPeriods, smoothPeriods, signalPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToTsi(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetTsi( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods = 25, + int smoothPeriods = 13, + int signalPeriods = 7) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToTsi(lookbackPeriods, smoothPeriods, signalPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetUlcerIndex(..)` to `ToUlcerIndex(..)`", false)] + public static IEnumerable GetUlcerIndex( + this IEnumerable quotes, int lookbackPeriods = 14) + where TQuote : IQuote + => quotes.ToSortedList().ToUlcerIndex(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToUlcerIndex(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetUlcerIndex( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods = 14) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToUlcerIndex(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetUltimate(..)` to `ToUltimate(..)`", false)] + public static IEnumerable GetUltimate( + this IEnumerable quotes, + int shortPeriods = 7, + int middlePeriods = 14, + int longPeriods = 28) + where TQuote : IQuote + => quotes.ToSortedList().ToUltimate(shortPeriods, middlePeriods, longPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetVolatilityStop(..)` to `ToVolatilityStop(..)`", false)] + public static IEnumerable GetVolatilityStop( + this IEnumerable quotes, + int lookbackPeriods = 7, + double multiplier = 3) + where TQuote : IQuote + => quotes.ToSortedList().ToVolatilityStop(lookbackPeriods, multiplier); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetVortex(..)` to `ToVortex(..)`", false)] + public static IEnumerable GetVortex( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote => quotes.ToSortedList().ToVortex(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetVwap(..)` to `ToVwap(..)`", false)] + public static IEnumerable GetVwap( + this IEnumerable quotes, DateTime? startDate = null) + where TQuote : IQuote => quotes.ToSortedList().ToVwap(startDate); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetVwma(..)` to `ToVwma(..)`", false)] + public static IEnumerable GetVwma( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote => quotes.ToSortedList().ToVwma(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetWilliamsR(..)` to `ToWilliamsR(..)`", false)] + public static IEnumerable GetWilliamsR( + this IEnumerable quotes, int lookbackPeriods = 14) + where TQuote : IQuote => quotes.ToSortedList().ToWilliamsR(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetWma(..)` to `ToWma(..)`", false)] + public static IEnumerable GetWma( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote => quotes.ToSortedList().ToWma(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Use 'ToWma(..)' method. Tuple arguments were removed.", false)] + public static IEnumerable GetWma( + this IEnumerable<(DateTime d, double v)> priceTuples, + int lookbackPeriods) + => priceTuples + .Select(t => new QuotePart(t.d, t.v)) + .ToSortedList() + .ToWma(lookbackPeriods); + + [ExcludeFromCodeCoverage] + [Obsolete("Rename `GetZigZag(..)` to `ToZigZag(..)`", false)] + public static IEnumerable GetZigZag( + this IEnumerable quotes, + EndType endType = EndType.Close, + decimal percentChange = 5) + where TQuote : IQuote => quotes.ToSortedList().ToZigZag(endType, percentChange); +} diff --git a/src/_common/ObsoleteV3.cs b/src/_common/ObsoleteV3.cs index 828eec155..93c7dd115 100644 --- a/src/_common/ObsoleteV3.cs +++ b/src/_common/ObsoleteV3.cs @@ -6,128 +6,105 @@ namespace Skender.Stock.Indicators; -// OBSOLETE IN v3 +// OBSOLETE IN v3.0.0 public static partial class Indicator { - // GENERAL INDICATOR METHODS + // UTILITIES [ExcludeFromCodeCoverage] - [Obsolete("Use alternate 'ToAlligator' variant. Tuple arguments were removed.", false)] // v3.0.0 - public static IEnumerable GetAlligator( - this IEnumerable<(DateTime d, double v)> priceTuples, - int jawPeriods = 13, - int jawOffset = 8, - int teethPeriods = 8, - int teethOffset = 5, - int lipsPeriods = 5, - int lipsOffset = 3) - => priceTuples - .Select(t => new QuotePart(t.d, t.v)) - .ToList() - .ToAlligator( - jawPeriods, jawOffset, - teethPeriods, teethOffset, - lipsPeriods, lipsOffset); + [Obsolete("This method no longer supports IEnumerable and tuple return types.", false)] + public static IEnumerable<(DateTime Timestamp, double Value)> Use( + this IEnumerable quotes, + CandlePart candlePart = CandlePart.Close) + where TQuote : IQuote + => quotes.Use(candlePart); [ExcludeFromCodeCoverage] - [Obsolete("Replace `GetEma(..)` with `ToEma(..)`", false)] // v3.0.0 - public static IEnumerable GetEma( - this IReadOnlyList quotes, int lookbackPeriods) + [Obsolete("This method no longer defaults to Close. Rename Use() to Use(CandlePart.Close) for an explicit conversion.", false)] + public static IEnumerable<(DateTime Timestamp, double Value)> Use( + this IReadOnlyList quotes) where TQuote : IQuote - => quotes.ToSortedList().ToEma(lookbackPeriods); - - // REMOVAL OF INTEGRATED SMAs (evaluates to ERRORs) + => quotes.Select(x => (x.Timestamp, x.Value)); [ExcludeFromCodeCoverage] - [Obsolete("Use a chained `results.ToSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 - public static IEnumerable GetAdl( - this IReadOnlyList quotes, int smaPeriods) - where TQuote : IQuote - => quotes.ToSortedList().ToAdl(); + [Obsolete("Refactor to use `ToSortedList()`", false)] + public static Collection ToSortedCollection( + this IEnumerable series) + where TSeries : ISeries + => series + .OrderBy(x => x.Timestamp) + .ToCollection(); [ExcludeFromCodeCoverage] - [Obsolete("Use a chained `results.ToSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 - public static IEnumerable GetObv( - this IReadOnlyList quotes, int smaPeriods) - where TQuote : IQuote - => quotes.ToObv(); + [Obsolete("Refactor to use `ToReusable()`", false)] + public static Collection<(DateTime Timestamp, double Value)> ToTupleChainable( + this IEnumerable reusable) + where TResult : IReusable + => reusable + .Select(x => (x.Timestamp, x.Value)) + .OrderBy(x => x.Timestamp) + .ToCollection(); [ExcludeFromCodeCoverage] - [Obsolete("Use a chained `results.ToSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 - public static IEnumerable GetPrs( - this IEnumerable quotesEval, IEnumerable quotesBase, int lookbackPeriods, int smaPeriods) - where TQuote : IQuote - => quotesEval + [Obsolete("Reference the `Value` property. Conversion is obsolete.", false)] + public static Collection<(DateTime Date, double Value)> ToTupleNaN( + this IEnumerable reusable) + => reusable .ToSortedList() - .Use(CandlePart.Close) - .ToPrs( - quotesBase.ToSortedList() - .Use(CandlePart.Close), lookbackPeriods); + .Select(x => (x.Timestamp, x.Value)) + .ToCollection(); [ExcludeFromCodeCoverage] - [Obsolete("Use a chained `results.ToSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 - public static IEnumerable GetRoc( - this IReadOnlyList quotes, int lookbackPeriods, int smaPeriods) + [Obsolete("Refactor to use `ToReusable()`", false)] + public static Collection<(DateTime Timestamp, double Value)> ToTupleCollection( + this IEnumerable quotes, + CandlePart candlePart = CandlePart.Close) where TQuote : IQuote - => quotes.Use(CandlePart.Close).ToRoc(lookbackPeriods); + => quotes + .ToList() + .ToReusable(candlePart) + .Select(x => (x.Timestamp, x.Value)) + .ToCollection(); [ExcludeFromCodeCoverage] - [Obsolete("Use a chained `results.ToSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 - public static IEnumerable GetStdDev( - this IReadOnlyList quotes, int lookbackPeriods, int smaPeriods) + [Obsolete("Refactor to use `List quotes` input.", false)] + public static IReadOnlyList Validate( + this IEnumerable quotes) where TQuote : IQuote - => quotes.Use(CandlePart.Close).ToStdDev(lookbackPeriods); + => quotes.ToSortedList().Validate(); [ExcludeFromCodeCoverage] - [Obsolete("Use a chained `results.ToSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 - public static IEnumerable GetTrix( - this IReadOnlyList quotes, int lookbackPeriods, int smaPeriods) + [Obsolete("Refactor to use `List quotes` input.", false)] + public static IEnumerable Aggregate( + this IEnumerable quotes, PeriodSize newSize) where TQuote : IQuote - => quotes.Use(CandlePart.Close).ToTrix(lookbackPeriods); - - // UTILITIES + => quotes.ToSortedList().Aggregate(newSize); [ExcludeFromCodeCoverage] - [Obsolete("This method no longer defaults to Close. Rename Use() to Use(CandlePart.Close) for an explicit conversion.", false)] // v3.0.0 - public static IEnumerable<(DateTime Timestamp, double Value)> Use( - this IReadOnlyList quotes) + [Obsolete("Refactor to use `List quotes` input.", false)] + public static IEnumerable ToCandles( + this IEnumerable quotes) where TQuote : IQuote - => quotes.Select(x => (x.Timestamp, x.Value)); - - [ExcludeFromCodeCoverage] - [Obsolete("Refactor to use `ToSortedList()`", true)] // v3.0.0 - public static Collection ToSortedCollection( - this IReadOnlyList series) - where TSeries : ISeries - => series - .OrderBy(x => x.Timestamp) - .ToCollection(); - - [ExcludeFromCodeCoverage] - [Obsolete("Refactor to use `ToReusable()`", true)] // v3.0.0 - public static Collection<(DateTime Timestamp, double Value)> ToTupleChainable( - this IEnumerable reusable) - where TResult : IReusable - => reusable.Select(x => (x.Timestamp, x.Value)).OrderBy(x => x.Timestamp).ToCollection(); + => quotes.ToSortedList().ToCandles(); [ExcludeFromCodeCoverage] - [Obsolete("Refactor to use `List.First(c => c.Timestamp == lookupDate)`", false)] // v3.0.0 + [Obsolete("Refactor to use `List.First(c => c.Timestamp == lookupDate)`", false)] public static TSeries Find(this IEnumerable series, DateTime lookupDate) where TSeries : ISeries => series.First(x => x.Timestamp == lookupDate); [ExcludeFromCodeCoverage] - [Obsolete("Refactor to use `List.FindIndex(c => c.Timestamp == lookupDate)`", false)] // v3.0.0 + [Obsolete("Refactor to use `List.FindIndex(c => c.Timestamp == lookupDate)`", false)] public static int FindIndex(this List series, DateTime lookupDate) where TSeries : ISeries => series?.FindIndex(x => x.Timestamp == lookupDate) ?? -1; } // CLASSES AND INTERFACES -[Obsolete("Rename `IReusableResult` to `IReusable`", true)] // v3.0.0 +[Obsolete("Rename `IReusableResult` to `IReusable`", true)] public interface IReusableResult : IReusable; [ExcludeFromCodeCoverage] -[Obsolete("Rename `BasicData` to `QuotePart`", true)] // v3.0.0 +[Obsolete("Rename `BasicData` to `QuotePart`", true)] public sealed class BasicData : IReusable { public DateTime Timestamp { get; set; } diff --git a/src/_common/ObsoleteV3.md b/src/_common/ObsoleteV3.md index 9561066da..2a5360157 100644 --- a/src/_common/ObsoleteV3.md +++ b/src/_common/ObsoleteV3.md @@ -1,107 +1,97 @@ -# v3 migration guide +# v3 Migration Guide (DRAFT IN-PROGRESS) -We've discontinued all bridge features for v1 backwards compatibility. -Correct all Warnings from this library before migrating from v2 to v3. -If you are still using v1, migrate to v2 first, to ease the transition to v3. +> This is some rough items that were partly generated by AI, and are still quite difficult to follow. Improvement needed. -Where possible, we've added bridge features to ease transition from v2 to v3. -You will see supporting migration Warning in your compiler with additional instructions for [deprecated changes](#deprecation-changes). +This guide provides a migration path from v2 to v3 of the Stock Indicators library. It includes a summary of all technical changes to the public API, enumerates syntax changes, and provides specific examples of deprecated and breaking changes. Follow the instructions below to update your code to be compatible with the new version. -In addition there are [breaking changes](#breaking-changes) that will require your attention. - -## Deprecation changes - -See your compiler `Warning` to identify these in your code. - -- All static time-series API method prefix were renamed from `GetX()` to `ToX()` to better reflect their purpose as a conversion utility. The former "Get" prefix innaccurately implied a retrieval operation. +## Summary of Technical Changes +- All static time-series API method prefixes were renamed from `GetX()` to `ToX()`. - `Use()` method parameter `candlePart` is now required and no longer defaults to `CandlePart.Close`. -- `Use()` now returns a chainable `QuotePart` instead of a tuple. These also replace the redundant `GetBaseQuote()` and `BaseQuote` items, respectively. - -- `UlcerIndexResult` property `UI` was renamed to `UlcerIndex` - -- **Deprecated 'GetX' tuple interfaces**. - -- **Deprecated internal signals**: several indicators were originally built with integrated but optional - moving averages, often by specifying an optional `smaPeriods` parameter. With more moving average chaining options, - these are obsolete, so we've removed them for simplification. These were persisted to avoid breaking your code; - however, you will see a compiler `Warnings` to help you identify areas to refactor. Check for use in ADL, OBV, ROC, STDDEV, TRIX, and others. - Future versions will not support the old API and will produce compiler `Errors`. - - ```csharp - // To refactor, here's an example replacement for ADL: - - var results = quotes.ToAdl(10); - var adlSma = results.ToSma(5); - - // ref: old usage example - - var results = quotes - .GetAdl(lookbackPeriods: 10, smaPeriods: 5); - ``` - -## Breaking changes - -Not all, but some of these will be shown as compiler `Errors` in your code. -Items marked with 🚩 require special attention since they will not produce compiler Errors or Warnings. - -- all v1 backwards compatibility accommodations were removed. - -- no longer supporting .NET Standard 2.0 for older .NET Framework compatibility. - -### Common breaking changes +- `Use()` now returns a chainable `QuotePart` instead of a tuple. +- `UlcerIndexResult` property `UI` was renamed to `UlcerIndex`. +- Deprecated 'GetX' tuple interfaces. +- Deprecated internal signals for several indicators. +- `Quote` type was changed to an immutable `record` type. +- `IQuote` interface `Date` property was renamed to `Timestamp`. +- `IQuote` is now a reusable (chainable) type. +- `TQuote` custom quote types now have to implement the `IReusable` interface. +- `IReusableResult` was renamed to `IReusable`. +- `IReusableResult.Value` property was changed to non-nullable and returns `double.NaN` instead of `null`. +- Indicator return types were changed from `sealed class` to immutable `record` types. +- Return type for the `Use()` utility method was renamed from `UseResult` to `QuotePart`. +- `Numerixs` class was renamed to `Numerical`. +- `GetBaseQuote()` indicator and related `BasicData` return types were removed. +- `AtrStopResult` values were changed from `decimal` to `double`. +- `SyncSeries()` utility function and related `SyncType` enum were removed. +- `ToTupleCollection()` utility method was deprecated. +- `Find()` and `FindIndex()` utility methods were removed. -- `Quote` type (built-in) was changed to an _**immutable**_ `record` type; and its `IQuote` interface `Date` property was widely renamed to `Timestamp`, to avoid a conflict with a C# reserved name. This will break your implementation if you were using `Quote` as an inherited base type. To fix, define your custom `TQuote` type on the `IQuote` interface instead (example below). +## Exact Syntax Changes -- `IQuote` is now a reusable (chainable) type. It auto-selects `Close` price as the _default_ consumed value. +### General Changes -- `TQuote` custom quote types now have to implement the `IReusable` interface to support chaining operations. The best way to fix is to change your `TQuote` to implement the `IReusable.Value` pointer to your `IQuote.Close` price. See [the Guide](/guide) for more information. Example: +- All static time-series API method prefixes were renamed from `GetX()` to `ToX()`. +- `Use()` method parameter `candlePart` is now required and no longer defaults to `CandlePart.Close`. +- `Use()` now returns a chainable `QuotePart` instead of a tuple. - ```csharp - public record MyCustomQuote ( +### Indicator-Specific Changes - // `IQuote` properties - DateTime Timestamp, - decimal Open, - decimal High, - decimal Low, - decimal MyClose, // custom - decimal Volume, +- `UlcerIndexResult` property `UI` was renamed to `UlcerIndex`. +- Deprecated 'GetX' tuple interfaces. +- Deprecated internal signals for several indicators. - // custom properties - string? MyCustomProperty = default +### Common Breaking Changes - ) : IQuote // » IQuote now inherits an IReusable - { - // custom mapped properties - decimal IQuote.Close - => MyClose; +- `Quote` type was changed to an immutable `record` type. +- `IQuote` interface `Date` property was renamed to `Timestamp`. +- `IQuote` is now a reusable (chainable) type. +- `TQuote` custom quote types now have to implement the `IReusable` interface. +- `IReusableResult` was renamed to `IReusable`. +- `IReusableResult.Value` property was changed to non-nullable and returns `double.NaN` instead of `null`. +- Indicator return types were changed from `sealed class` to immutable `record` types. +- Return type for the `Use()` utility method was renamed from `UseResult` to `QuotePart`. +- `Numerixs` class was renamed to `Numerical`. +- `GetBaseQuote()` indicator and related `BasicData` return types were removed. +- `AtrStopResult` values were changed from `decimal` to `double`. +- `SyncSeries()` utility function and related `SyncType` enum were removed. +- `ToTupleCollection()` utility method was deprecated. +- `Find()` and `FindIndex()` utility methods were removed. - // `IReusable` requires a default 'Value' property. - // Map it to your 'Close' price. - double IReusable.Value - => (double)Close; - } - ``` +## Migration Path -- `IReusableResult` was renamed to `IReusable` since it is no longer limited to _result_ types. +### General Migration Steps -- 🚩 `IReusableResult.Value` property was changed to non-nullable and returns `double.NaN` instead of `null` for incalculable periods. The standard results (e.g. `EmaResult.Ema`) continue to return `null` for incalculable periods. This was done to improve internal chaining and streaming performance. +1. Update all static time-series API method prefixes from `GetX()` to `ToX()`. +2. Update `Use()` method calls to include the `candlePart` parameter. +3. Update `Use()` method calls to handle the new `QuotePart` return type. +4. Update `UlcerIndexResult` property `UI` to `UlcerIndex`. +5. Refactor code to remove deprecated 'GetX' tuple interfaces. +6. Refactor code to remove deprecated internal signals for indicators. -- Indicator return types, like `EmaResult`, were changed from `sealed class` to _**immutable**_ `record` types to improve internal chaining and streaming performance. This should not impact negatively; however, these can now be inherited as base classes. +### Specific Migration Steps -### Less common breaking changes +#### Updating `Quote` Type -- Return type for the `Use()` utility method was renamed from `UseResult` to `QuotePart` for clarity and for of its wider purpose. +1. Change `Quote` type to an immutable `record` type. +2. Rename `IQuote` interface `Date` property to `Timestamp`. +3. Implement the `IReusable` interface for custom `TQuote` types. -- `Numerixs` class was renamed to `Numerical`. +#### Updating `IReusableResult` -- `GetBaseQuote()` indicator and related `BasicData` return types were removed since they are redundant to the `Use()` method and `QuotePart` return types, respectively. +1. Rename `IReusableResult` to `IReusable`. +2. Update `IReusableResult.Value` property to handle `double.NaN` instead of `null`. -- `AtrStopResult` values were changed from `decimal` to `double` numeric return types. +#### Updating Indicator Return Types -- `SyncSeries()` utility function and related `SyncType` enum were removed. This was primarily an internal utility, but was part of the public API to support user who wanted to build custom indicator development. Internally, we've refactored indicators to auto-initialize and heal, so they no longer require re-sizing to support explicit warmup periods. +1. Change indicator return types from `sealed class` to immutable `record` types. -- `ToTupleCollection()` utility method was deprecated. This was available to support custom indicator development, but is no longer needed. We've discontinued using _tuples_ as an interface to chainable indicators. +#### Updating Utility Methods -- `Find()` and `FindIndex()` utility methods were removed. These were redundant to the native C# `List.Find()` method and `List.FindIndex()` methods, respectively. +1. Rename return type for the `Use()` utility method from `UseResult` to `QuotePart`. +2. Rename `Numerixs` class to `Numerical`. +3. Remove `GetBaseQuote()` indicator and related `BasicData` return types. +4. Change `AtrStopResult` values from `decimal` to `double`. +5. Remove `SyncSeries()` utility function and related `SyncType` enum. +6. Deprecate `ToTupleCollection()` utility method. +7. Remove `Find()` and `FindIndex()` utility methods. diff --git a/src/_common/Quotes/Quote.Models.cs b/src/_common/Quotes/Quote.Models.cs index 887ebaa67..94b084aaf 100644 --- a/src/_common/Quotes/Quote.Models.cs +++ b/src/_common/Quotes/Quote.Models.cs @@ -12,7 +12,7 @@ namespace Skender.Stock.Indicators; /// /// For chaining compatibility ( /// compliance), add the following TQuote property -/// (pointer) to your price. +/// (pointer) to your price. /// /// double IReusable.Value => (double)Close; /// @@ -86,10 +86,19 @@ decimal Volume /// public double Value => (double)Close; - // TODO: add [Obsolete] auto-getter/setter for 'Date' property - // but only for a short transition period. See if there can be - // a full overload of 'Quote' that has the 'Date' property and - // can support new(){ ... } initialization. + /// + [Obsolete("Use 'Timestamp' property instead.")] + public DateTime Date + { + get => Timestamp; + init => Timestamp = value; + } + + // TODO: this can be removed when Date is removed + // It allows old `new Quote { Date: ... }` initialization. + /// + public Quote() + : this(default, default, default, default, default, default) { } } ///

    diff --git a/src/_common/Quotes/Quote.StreamHub.cs b/src/_common/Quotes/Quote.StreamHub.cs index d729cbbd5..a3e3c7030 100644 --- a/src/_common/Quotes/Quote.StreamHub.cs +++ b/src/_common/Quotes/Quote.StreamHub.cs @@ -47,7 +47,7 @@ protected override (TQuote result, int index) ToIndicator(TQuote item, int? indexHint) { int index = indexHint - ?? Cache.GetIndexGte(item.Timestamp); + ?? Cache.IndexGte(item.Timestamp); return (item, index == -1 ? Cache.Count : index); } diff --git a/src/_common/Reusable/Reusable.Utilities.cs b/src/_common/Reusable/Reusable.Utilities.cs index b28a791cf..df994ba02 100644 --- a/src/_common/Reusable/Reusable.Utilities.cs +++ b/src/_common/Reusable/Reusable.Utilities.cs @@ -12,7 +12,7 @@ public static partial class Reusable /// The list of quotes. /// The part of the candle to use. /// A list of reusable types. - public static IReadOnlyList ToReusableList( + public static IReadOnlyList ToReusable( this IReadOnlyList quotes, CandlePart candlePart) where TQuote : IQuote diff --git a/src/a-d/Adl/Adl.StreamHub.cs b/src/a-d/Adl/Adl.StreamHub.cs index e5b7fdaab..7a378cf87 100644 --- a/src/a-d/Adl/Adl.StreamHub.cs +++ b/src/a-d/Adl/Adl.StreamHub.cs @@ -38,7 +38,7 @@ internal AdlHub(IQuoteProvider provider) protected override (AdlResult result, int index) ToIndicator(TIn item, int? indexHint) { - int i = indexHint ?? ProviderCache.GetIndex(item, true); + int i = indexHint ?? ProviderCache.IndexOf(item, true); // candidate result AdlResult r = Adl.Increment( diff --git a/src/a-d/Adx/Adx.BufferList.cs b/src/a-d/Adx/Adx.BufferList.cs index 17a9d8d1d..c7fd3dc32 100644 --- a/src/a-d/Adx/Adx.BufferList.cs +++ b/src/a-d/Adx/Adx.BufferList.cs @@ -22,7 +22,7 @@ public AdxList(int lookbackPeriods) /// /// Gets the number of periods to look back for the calculation. /// - public int LookbackPeriods { get; init; } + public int LookbackPeriods { get; private init; } /// /// Adds a new quote to the ADX list. diff --git a/src/a-d/Alligator/Alligator.StreamHub.cs b/src/a-d/Alligator/Alligator.StreamHub.cs index 099c4f59c..68272a247 100644 --- a/src/a-d/Alligator/Alligator.StreamHub.cs +++ b/src/a-d/Alligator/Alligator.StreamHub.cs @@ -165,7 +165,7 @@ protected override (AlligatorResult result, int index) double lips = double.NaN; double teeth = double.NaN; - int i = indexHint ?? ProviderCache.GetIndex(item, true); + int i = indexHint ?? ProviderCache.IndexOf(item, true); // calculate alligator's jaw, when in range if (i >= JawPeriods + JawOffset - 1) diff --git a/src/a-d/Atr/Atr.StreamHub.cs b/src/a-d/Atr/Atr.StreamHub.cs index d4a39b9f0..58a5e6e09 100644 --- a/src/a-d/Atr/Atr.StreamHub.cs +++ b/src/a-d/Atr/Atr.StreamHub.cs @@ -72,7 +72,7 @@ internal AtrHub(IQuoteProvider provider, protected override (AtrResult result, int index) ToIndicator(TIn item, int? indexHint) { - int i = indexHint ?? ProviderCache.GetIndex(item, true); + int i = indexHint ?? ProviderCache.IndexOf(item, true); // skip incalculable periods if (i == 0) diff --git a/src/a-d/AtrStop/AtrStop.StreamHub.cs b/src/a-d/AtrStop/AtrStop.StreamHub.cs index d74c521c6..3897ee0bf 100644 --- a/src/a-d/AtrStop/AtrStop.StreamHub.cs +++ b/src/a-d/AtrStop/AtrStop.StreamHub.cs @@ -116,7 +116,7 @@ protected override (AtrStopResult result, int index) { // reminder: should only process "new" instructions - int i = indexHint ?? ProviderCache.GetIndex(item, true); + int i = indexHint ?? ProviderCache.IndexOf(item, true); // handle warmup periods if (i < LookbackPeriods) @@ -232,7 +232,7 @@ protected override (AtrStopResult result, int index) /// protected override void RollbackState(DateTime timestamp) { - int i = ProviderCache.GetIndexGte(timestamp); + int i = ProviderCache.IndexGte(timestamp); // restore prior stop point if (i > LookbackPeriods) diff --git a/src/e-k/Ema/Ema.StreamHub.cs b/src/e-k/Ema/Ema.StreamHub.cs index 4751741d0..90671d5e2 100644 --- a/src/e-k/Ema/Ema.StreamHub.cs +++ b/src/e-k/Ema/Ema.StreamHub.cs @@ -78,7 +78,7 @@ internal EmaHub( protected override (EmaResult result, int index) ToIndicator(TIn item, int? indexHint) { - int i = indexHint ?? ProviderCache.GetIndex(item, true); + int i = indexHint ?? ProviderCache.IndexOf(item, true); double ema = i >= LookbackPeriods - 1 ? Cache[i - 1].Ema is not null diff --git a/src/m-r/ParabolicSar/ParabolicSar.StaticSeries.cs b/src/m-r/ParabolicSar/ParabolicSar.StaticSeries.cs index b9c936f15..9e8a7f777 100644 --- a/src/m-r/ParabolicSar/ParabolicSar.StaticSeries.cs +++ b/src/m-r/ParabolicSar/ParabolicSar.StaticSeries.cs @@ -37,7 +37,7 @@ public static IReadOnlyList ToParabolicSar( /// A list of containing the SAR values. /// Thrown when the quotes list is null. /// Thrown when the acceleration step, maximum acceleration factor, or initial factor are out of range. - public static IReadOnlyList GetParabolicSar( + public static IReadOnlyList ToParabolicSar( this IReadOnlyList quotes, double accelerationStep, double maxAccelerationFactor, diff --git a/src/m-r/Renko/Renko.StreamHub.cs b/src/m-r/Renko/Renko.StreamHub.cs index f310a03b5..fb4b050be 100644 --- a/src/m-r/Renko/Renko.StreamHub.cs +++ b/src/m-r/Renko/Renko.StreamHub.cs @@ -183,7 +183,7 @@ int newBrickQty decimal sumV = 0; // cumulative // by aggregating provider cache range - int lastBrickIndex = ProviderCache.GetIndex(lastBrick.Timestamp, true); + int lastBrickIndex = ProviderCache.IndexOf(lastBrick.Timestamp, true); for (int w = lastBrickIndex + 1; w <= providerIndex; w++) { diff --git a/src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs b/src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs index 56b577f8b..d1b9881e0 100644 --- a/src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs +++ b/src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs @@ -13,7 +13,7 @@ public static partial class Renko /// The number of periods for calculating ATR. /// The price candle end type to use as the brick threshold. /// A list of Renko chart results. - public static IReadOnlyList GetRenkoAtr( + public static IReadOnlyList ToRenkoAtr( this IReadOnlyList quotes, int atrPeriods, EndType endType = EndType.Close) diff --git a/src/s-z/Sma/Sma.StreamHub.cs b/src/s-z/Sma/Sma.StreamHub.cs index 9689361d9..9ea40d7d1 100644 --- a/src/s-z/Sma/Sma.StreamHub.cs +++ b/src/s-z/Sma/Sma.StreamHub.cs @@ -78,7 +78,7 @@ internal SmaHub( protected override (SmaResult result, int index) ToIndicator(TIn item, int? indexHint) { - int i = indexHint ?? ProviderCache.GetIndex(item, true); + int i = indexHint ?? ProviderCache.IndexOf(item, true); // candidate result SmaResult r = new( diff --git a/src/s-z/StdDevChannels/StdDevChannels.StaticSeries.cs b/src/s-z/StdDevChannels/StdDevChannels.StaticSeries.cs index c8ec922f7..b779a954a 100644 --- a/src/s-z/StdDevChannels/StdDevChannels.StaticSeries.cs +++ b/src/s-z/StdDevChannels/StdDevChannels.StaticSeries.cs @@ -10,7 +10,9 @@ public static partial class StdDevChannels /// /// The type of the quote, which must implement . /// The source series of quotes. - /// The number of periods for the lookback. Default is 20. + /// + /// The number of periods for the lookback. Default is 20. + /// Spans all provided prices when . /// The number of standard deviations for the channel width. Default is 2. /// A list of containing the Standard Deviation Channels values. public static IReadOnlyList ToStdDevChannels( diff --git a/src/s-z/Tr/Tr.StreamHub.cs b/src/s-z/Tr/Tr.StreamHub.cs index b6a5f6132..1db365bd1 100644 --- a/src/s-z/Tr/Tr.StreamHub.cs +++ b/src/s-z/Tr/Tr.StreamHub.cs @@ -51,7 +51,7 @@ internal TrHub(IQuoteProvider provider) protected override (TrResult result, int index) ToIndicator(TIn item, int? indexHint) { - int i = indexHint ?? ProviderCache.GetIndex(item, true); + int i = indexHint ?? ProviderCache.IndexOf(item, true); // skip first period if (i == 0) diff --git a/src/s-z/UlcerIndex/UlcerIndex.Models.cs b/src/s-z/UlcerIndex/UlcerIndex.Models.cs index cc1fe8469..9f117918a 100644 --- a/src/s-z/UlcerIndex/UlcerIndex.Models.cs +++ b/src/s-z/UlcerIndex/UlcerIndex.Models.cs @@ -15,9 +15,7 @@ public record UlcerIndexResult /// public double Value => UlcerIndex.Null2NaN(); - /// - /// Gets the value of the Ulcer Index. - /// - [Obsolete("Rename UI to UlcerIndex")] // v3.0.0 + /// + [Obsolete("Rename UI to UlcerIndex")] public double? UI => UlcerIndex; } diff --git a/tests/Directory.Packages.props b/tests/Directory.Packages.props index 6b9636fa0..4ccae5838 100644 --- a/tests/Directory.Packages.props +++ b/tests/Directory.Packages.props @@ -1,7 +1,5 @@ - true - true $(NoWarn);NU1507 diff --git a/tests/external/application/Program.cs b/tests/external/application/Program.cs index b0171ca8f..4fa51b88e 100644 --- a/tests/external/application/Program.cs +++ b/tests/external/application/Program.cs @@ -1,3 +1,6 @@ +using System.Collections.ObjectModel; +using System.Globalization; + namespace Test.Application; public class Program @@ -9,211 +12,224 @@ private static void Main(string[] args) Console.WriteLine(args); } - string scenario = "C"; - - Go go = new(); - - switch (scenario) - { - case "A": go.QuoteHub(); break; - case "B": go.EmaHub(); break; - case "C": go.MultipleSubscribers(); break; - default: break; - } - } -} - -public class Go -{ - private readonly bool verbose = true; - - private static readonly QuoteHub provider = new(); - - private static readonly IReadOnlyList quotesList = Data.Data.GetDefault(); - - private static readonly int quotesLength = quotesList.Count; - - public Go() - { - if (verbose) - { - Prefill(); - } - } - - private static void Prefill() - { - // prefill quotes to provider - for (int i = 0; i < quotesLength; i++) - { - provider.Add(quotesList[i]); - } - } - - internal void QuoteHub() - { - EmaHub emaHub = provider.ToEma(14); - - if (!verbose) - { - return; - } - - // initialize console display - Console.WriteLine(""" - Date Close price - -------------------- - """); - - // add quotes to provider - for (int i = 0; i < quotesLength; i++) - { - Quote q = quotesList[i]; - provider.Add(q); - - // wait for next quote - Timewarp(); - - // send to console - SendToConsole(q); - } - } - - private static void SendToConsole(Quote q) - { - string m = $"{q.Timestamp:yyyy-MM-dd} ${q.Close:N2}"; - Console.WriteLine(m); - } - - - internal void EmaHub() - { - EmaHub emaHub = provider.ToEma(14); - - if (!verbose) - { - return; - } - - // initialize console display - Console.WriteLine(""" - Date Close price EMA(14) - ------------------------------ - """); - - // add quotes to provider - for (int i = 0; i < quotesLength; i++) - { - Quote q = quotesList[i]; - provider.Add(q); - - // wait for next quote - Timewarp(); - - // send to console - SendToConsole(q, emaHub); - } - } - - private static void SendToConsole(Quote q, EmaHub emaHub) - where T : IReusable - { - string m = $"{q.Timestamp:yyyy-MM-dd} ${q.Close:N2}"; - - EmaResult e = emaHub.Results[^1]; - - if (e.Ema is not null) - { - m += $"{e.Ema,10:N3}"; - } - else - { - m += $"{"[null]",10}"; - } - - Console.WriteLine(m); - } - - internal void MultipleSubscribers() - { - SmaHub smaHub = provider.ToSma(3); - EmaHub emaHub = provider.ToEma(5); - EmaHub useChain = provider.ToQuotePart(CandlePart.HL2).ToEma(7); - EmaHub emaChain = provider.ToSma(4).ToEma(4); - - if (!verbose) - { - return; - } - - // initialize console display - Console.WriteLine(""" - Date Close price SMA(3) EMA(5) EMA(7,HL2) SMA/EMA(8) - ------------------------------------------------------------- - """); - - // add quotes to provider - for (int i = 0; i < quotesLength; i++) - { - Quote q = quotesList[i]; - provider.Add(q); - - // wait for next quote - Timewarp(); - - // send to console - SendToConsole(q, smaHub, emaHub, useChain, emaChain); - } - } - - private static void SendToConsole( - Quote q, - SmaHub smaHub, - EmaHub emaHub, - EmaHub useChain, - EmaHub emaChain) - { - string m = $"{q.Timestamp:yyyy-MM-dd} ${q.Close:N2}"; - - SmaResult s = smaHub.Results[^1]; - EmaResult e = emaHub.Results[^1]; - EmaResult u = useChain.Results[^1]; - EmaResult c = emaChain.Results[^1]; - - if (s.Sma is not null) - { - m += $"{s.Sma,8:N1}"; - } - - if (e.Ema is not null) - { - m += $"{e.Ema,8:N1}"; - } - - if (u.Ema is not null) - { - m += $"{u.Ema,12:N1}"; - } - - if (c.Ema is not null) - { - m += $"{c.Ema,12:N1}"; - } - - Console.WriteLine(m); - } - - /// - /// Emulate quote arrival rate. - /// Use '0' to disable. - /// - private static void Timewarp(int quotesPerMinute = 0) - { - if (quotesPerMinute == 0) - { - return; - } - - Thread.Sleep(60000 / quotesPerMinute); + // QUOTES for testing + CultureInfo culture = CultureInfo.InvariantCulture; + IEnumerable quotes = + [ + new Quote { Date = DateTime.Parse("2023-01-02", culture), Open = 100, High = 110, Low = 90, Close = 105, Volume = 1000 }, // Monday + new Quote { Date = DateTime.Parse("2023-01-03", culture), Open = 106, High = 112, Low = 104, Close = 108, Volume = 1500 }, // Tuesday + new Quote { Date = DateTime.Parse("2023-01-04", culture), Open = 109, High = 115, Low = 107, Close = 112, Volume = 1200 }, // Wednesday + new Quote { Date = DateTime.Parse("2023-01-05", culture), Open = 113, High = 118, Low = 110, Close = 116, Volume = 1300 }, // Thursday + new Quote { Date = DateTime.Parse("2023-01-06", culture), Open = 117, High = 120, Low = 114, Close = 119, Volume = 1400 }, // Friday + new Quote { Date = DateTime.Parse("2023-01-09", culture), Open = 120, High = 125, Low = 118, Close = 123, Volume = 1500 }, // Monday + new Quote { Date = DateTime.Parse("2023-01-10", culture), Open = 124, High = 128, Low = 122, Close = 127, Volume = 1600 }, // Tuesday + new Quote { Date = DateTime.Parse("2023-01-11", culture), Open = 128, High = 132, Low = 126, Close = 130, Volume = 1700 }, // Wednesday + new Quote { Date = DateTime.Parse("2023-01-12", culture), Open = 131, High = 135, Low = 129, Close = 133, Volume = 1800 }, // Thursday + new Quote { Date = DateTime.Parse("2023-01-13", culture), Open = 134, High = 138, Low = 132, Close = 136, Volume = 1900 }, // Friday + new Quote { Date = DateTime.Parse("2023-01-16", culture), Open = 137, High = 141, Low = 135, Close = 139, Volume = 2000 }, // Monday + new Quote { Date = DateTime.Parse("2023-01-17", culture), Open = 140, High = 144, Low = 138, Close = 142, Volume = 2100 }, // Tuesday + new Quote { Date = DateTime.Parse("2023-01-18", culture), Open = 143, High = 147, Low = 141, Close = 145, Volume = 2200 }, // Wednesday + new Quote { Date = DateTime.Parse("2023-01-19", culture), Open = 146, High = 150, Low = 144, Close = 148, Volume = 2300 }, // Thursday + new Quote { Date = DateTime.Parse("2023-01-20", culture), Open = 149, High = 153, Low = 147, Close = 151, Volume = 2400 } // Friday + ]; + + // TUPLES for testing + IEnumerable<(DateTime d, double v)> tuples = quotes.Select(x => (x.Date, (double)x.Close)); + + // SERIES INDICATORS + IEnumerable adl1 = quotes.GetAdl(); + IEnumerable adx1 = quotes.GetAdx(lookbackPeriods: 14); + IEnumerable alligator1 = quotes.GetAlligator(); + IEnumerable alligator2 = quotes.GetAlligator(jawPeriods: 13, jawOffset: 8, teethPeriods: 8, teethOffset: 5, lipsPeriods: 5, lipsOffset: 3); + IEnumerable alligatorT = tuples.GetAlligator(jawPeriods: 13, jawOffset: 8, teethPeriods: 8, teethOffset: 5, lipsPeriods: 5, lipsOffset: 3); + IEnumerable alma1 = quotes.GetAlma(lookbackPeriods: 9, offset: 0.85, sigma: 6); + IEnumerable almaT = tuples.GetAlma(lookbackPeriods: 9, offset: 0.85, sigma: 6); + IEnumerable aroon1 = quotes.GetAroon(lookbackPeriods: 25); + IEnumerable atr1 = quotes.GetAtr(lookbackPeriods: 14); + IEnumerable atrStop1 = quotes.GetAtrStop(lookbackPeriods: 14); + IEnumerable atrStop2 = quotes.GetAtrStop(lookbackPeriods: 21, multiplier: 3, endType: EndType.Close); + IEnumerable awesome1 = quotes.GetAwesome(); + IEnumerable awesome2 = quotes.GetAwesome(fastPeriods: 5, slowPeriods: 34); + IEnumerable awesomeT = tuples.GetAwesome(fastPeriods: 5, slowPeriods: 34); + IEnumerable beta1 = quotes.GetBeta(quotesMarket: quotes, lookbackPeriods: 20); + IEnumerable beta1All = quotes.GetBeta(quotesMarket: quotes, lookbackPeriods: 20, type: BetaType.All); + IEnumerable beta1Dwn = quotes.GetBeta(quotesMarket: quotes, lookbackPeriods: 20, type: BetaType.Down); + IEnumerable beta1Std = quotes.GetBeta(quotesMarket: quotes, lookbackPeriods: 20, type: BetaType.Standard); + IEnumerable beta1Ups = quotes.GetBeta(quotesMarket: quotes, lookbackPeriods: 20, type: BetaType.Up); + IEnumerable betaT = tuples.GetBeta(mrktTuple: tuples, lookbackPeriods: 20); + IEnumerable bb1 = quotes.GetBollingerBands(lookbackPeriods: 20, standardDeviations: 2); + IEnumerable bbT = tuples.GetBollingerBands(lookbackPeriods: 20, standardDeviations: 2); + IEnumerable bop1 = quotes.GetBop(); + IEnumerable bop2 = quotes.GetBop(smoothPeriods: 14); + IEnumerable doji = quotes.GetDoji(maxPriceChangePercent: 0.1); + IEnumerable marubozu = quotes.GetMarubozu(minBodyPercent: 95); + IEnumerable cci1 = quotes.GetCci(lookbackPeriods: 20); + IEnumerable chaikinOsc1 = quotes.GetChaikinOsc(); + IEnumerable chaikinOsc2 = quotes.GetChaikinOsc(fastPeriods: 3, slowPeriods: 10); + IEnumerable chandelier1 = quotes.GetChandelier(lookbackPeriods: 22, multiplier: 3, type: ChandelierType.Long); + IEnumerable chop1 = quotes.GetChop(lookbackPeriods: 14); + IEnumerable cmf1 = quotes.GetCmf(lookbackPeriods: 20); + IEnumerable cmo1 = quotes.GetCmo(lookbackPeriods: 14); + IEnumerable cmoT = tuples.GetCmo(lookbackPeriods: 14); + IEnumerable cRsi1 = quotes.GetConnorsRsi(rsiPeriods: 3, streakPeriods: 2, rankPeriods: 100); + IEnumerable cRsiT = tuples.GetConnorsRsi(rsiPeriods: 3, streakPeriods: 2, rankPeriods: 100); + IEnumerable corr1 = quotes.GetCorrelation(quotesB: quotes, lookbackPeriods: 20); + IEnumerable corrT = tuples.GetCorrelation(tuplesB: tuples, lookbackPeriods: 20); + IEnumerable dema1 = quotes.GetDema(lookbackPeriods: 20); + IEnumerable demaT = tuples.GetDema(lookbackPeriods: 20); + IEnumerable donchian1 = quotes.GetDonchian(lookbackPeriods: 20); + IEnumerable dpo1 = quotes.GetDpo(lookbackPeriods: 20); + IEnumerable dpoT = tuples.GetDpo(lookbackPeriods: 20); + IEnumerable dynamic1 = quotes.GetDynamic(lookbackPeriods: 20); + IEnumerable dynamic2 = quotes.GetDynamic(lookbackPeriods: 20, kFactor: 0.6); + IEnumerable dynamicT = tuples.GetDynamic(lookbackPeriods: 20, kFactor: 0.6); + IEnumerable elderRay1 = quotes.GetElderRay(lookbackPeriods: 13); + IEnumerable ema1 = quotes.GetEma(lookbackPeriods: 20); + IEnumerable emaT = tuples.GetEma(lookbackPeriods: 20); + IEnumerable epma1 = quotes.GetEpma(lookbackPeriods: 20); + IEnumerable epmaT = tuples.GetEpma(lookbackPeriods: 20); + IEnumerable fcb1 = quotes.GetFcb(windowSpan: 2); + IEnumerable fisherTransform = quotes.GetFisherTransform(lookbackPeriods: 10); + IEnumerable fisherTransformT = tuples.GetFisherTransform(lookbackPeriods: 10); + IEnumerable forceIndex1 = quotes.GetForceIndex(lookbackPeriods: 2); + IEnumerable fractal1 = quotes.GetFractal(); + IEnumerable fractal2 = quotes.GetFractal(leftSpan: 2, rightSpan: 2, endType: EndType.HighLow); + IEnumerable gator1 = quotes.GetGator(); + IEnumerable gatorT = tuples.GetGator(); + IEnumerable heikinAshi1 = quotes.GetHeikinAshi(); + IEnumerable hma1 = quotes.GetHma(lookbackPeriods: 20); + IEnumerable hmaT = tuples.GetHma(lookbackPeriods: 20); + IEnumerable htTrendline1 = quotes.GetHtTrendline(); + IEnumerable htTrendlineT = tuples.GetHtTrendline(); + IEnumerable hurst1 = quotes.GetHurst(lookbackPeriods: 100); + IEnumerable hurstT = tuples.GetHurst(lookbackPeriods: 100); + IEnumerable ichimoku1 = quotes.GetIchimoku(); + IEnumerable ichimoku2 = quotes.GetIchimoku(tenkanPeriods: 9, kijunPeriods: 26, senkouBPeriods: 52); + IEnumerable kama1 = quotes.GetKama(erPeriods: 10, fastPeriods: 2, slowPeriods: 30); + IEnumerable kamaT = tuples.GetKama(erPeriods: 10, fastPeriods: 2, slowPeriods: 30); + IEnumerable keltner1 = quotes.GetKeltner(emaPeriods: 20, multiplier: 2, atrPeriods: 10); + IEnumerable kvo1 = quotes.GetKvo(); + IEnumerable kvo2 = quotes.GetKvo(fastPeriods: 34, slowPeriods: 55, signalPeriods: 13); + IEnumerable macd1 = quotes.GetMacd(fastPeriods: 12, slowPeriods: 26, signalPeriods: 9); + IEnumerable macdT = tuples.GetMacd(fastPeriods: 12, slowPeriods: 26, signalPeriods: 9); + IEnumerable maEnv1 = quotes.GetMaEnvelopes(lookbackPeriods: 20, percentOffset: 3.5, movingAverageType: MaType.SMA); + IEnumerable maEnvT = tuples.GetMaEnvelopes(lookbackPeriods: 20, percentOffset: 3.5, movingAverageType: MaType.SMA); + IEnumerable mama1 = quotes.GetMama(); + IEnumerable mama2 = quotes.GetMama(fastLimit: 0.5, slowLimit: 0.05); + IEnumerable mamaT = tuples.GetMama(fastLimit: 0.5, slowLimit: 0.05); + IEnumerable mfi1 = quotes.GetMfi(lookbackPeriods: 14); + IEnumerable obv1 = quotes.GetObv(); + IEnumerable obv2 = quotes.GetObv(smaPeriods: 3); + IEnumerable pSar1 = quotes.GetParabolicSar(accelerationStep: 0.02, maxAccelerationFactor: 0.2); + IEnumerable pSar2 = quotes.GetParabolicSar(accelerationStep: 0.02, maxAccelerationFactor: 0.2, initialFactor: 0.02); + IEnumerable pivotPoints1 = quotes.GetPivotPoints(windowSize: PeriodSize.Week); + IEnumerable pivotPoints2 = quotes.GetPivotPoints(windowSize: PeriodSize.Week, pointType: PivotPointType.Standard); + IEnumerable pivots1 = quotes.GetPivots(); + IEnumerable pivots2 = quotes.GetPivots(leftSpan: 2, rightSpan: 2, maxTrendPeriods: 20, endType: EndType.HighLow); + IEnumerable pmo1 = quotes.GetPmo(timePeriods: 35, smoothPeriods: 20, signalPeriods: 10); + IEnumerable pmoT = tuples.GetPmo(timePeriods: 35, smoothPeriods: 20, signalPeriods: 10); + IEnumerable prs1 = quotes.GetPrs(quotesBase: quotes); + IEnumerable prs2 = quotes.GetPrs(quotesBase: quotes, lookbackPeriods: 5, smaPeriods: 3); + IEnumerable prsT = tuples.GetPrs(tupleBase: tuples, lookbackPeriods: 5, smaPeriods: 3); + IEnumerable pvo1 = quotes.GetPvo(); + IEnumerable pvo2 = quotes.GetPvo(fastPeriods: 12, slowPeriods: 26, signalPeriods: 9); + IEnumerable renko1 = quotes.GetRenko(brickSize: 2.0m); + IEnumerable renko2 = quotes.GetRenko(brickSize: 2.0m, endType: EndType.Close); + IEnumerable renkoAtr1 = quotes.GetRenkoAtr(atrPeriods: 14, endType: EndType.Close); + IEnumerable roc1 = quotes.GetRoc(lookbackPeriods: 20); + IEnumerable roc2 = quotes.GetRoc(lookbackPeriods: 20, smaPeriods: 3); + IEnumerable rocT = tuples.GetRoc(lookbackPeriods: 20); + IEnumerable rocWb1 = quotes.GetRocWb(lookbackPeriods: 20, emaPeriods: 9, stdDevPeriods: 2); + IEnumerable rocWbT = tuples.GetRocWb(lookbackPeriods: 20, emaPeriods: 9, stdDevPeriods: 2); + IEnumerable rollingPivots1 = quotes.GetRollingPivots(windowPeriods: 20, offsetPeriods: 2); + IEnumerable rollingPivots2 = quotes.GetRollingPivots(windowPeriods: 14, offsetPeriods: 7, pointType: PivotPointType.Standard); + IEnumerable rsi1 = quotes.GetRsi(lookbackPeriods: 14); + IEnumerable rsiT = tuples.GetRsi(lookbackPeriods: 14); + IEnumerable rsiT2 = tuples.GetRsi(lookbackPeriods: 14); + IEnumerable slope1 = quotes.GetSlope(lookbackPeriods: 20); + IEnumerable slopeT = tuples.GetSlope(lookbackPeriods: 20); + IEnumerable smaAnalysis1 = quotes.GetSmaAnalysis(lookbackPeriods: 20); + IEnumerable smaAnalysisT = tuples.GetSmaAnalysis(lookbackPeriods: 20); + IEnumerable smaAnalysisT2 = tuples.GetSmaAnalysis(lookbackPeriods: 20); + IEnumerable sma1 = quotes.GetSma(lookbackPeriods: 20); + IEnumerable smaT = tuples.GetSma(lookbackPeriods: 20); + IEnumerable smi1 = quotes.GetSmi(lookbackPeriods: 20); + IEnumerable smi2 = quotes.GetSmi(lookbackPeriods: 13, firstSmoothPeriods: 25, secondSmoothPeriods: 2, signalPeriods: 3); + IEnumerable smma1 = quotes.GetSmma(lookbackPeriods: 20); + IEnumerable smmaT = tuples.GetSmma(lookbackPeriods: 20); + IEnumerable starcBands1 = quotes.GetStarcBands(smaPeriods: 20, multiplier: 2, atrPeriods: 10); + IEnumerable stc1 = quotes.GetStc(cyclePeriods: 10, fastPeriods: 23, slowPeriods: 50); + IEnumerable stcT = tuples.GetStc(cyclePeriods: 10, fastPeriods: 23, slowPeriods: 50); + IEnumerable stdDevCh1 = quotes.GetStdDevChannels(lookbackPeriods: 20, stdDeviations: 2); + IEnumerable stdDevChT = tuples.GetStdDevChannels(lookbackPeriods: 20, stdDeviations: 2); + IEnumerable stdDev1 = quotes.GetStdDev(lookbackPeriods: 20); + IEnumerable stdDev2 = quotes.GetStdDev(lookbackPeriods: 20, smaPeriods: 20); + IEnumerable stdDevT = tuples.GetStdDev(lookbackPeriods: 20); + IEnumerable stoch1 = quotes.GetStoch(lookbackPeriods: 14, signalPeriods: 3, smoothPeriods: 3); + IEnumerable stoch2 = quotes.GetStoch(lookbackPeriods: 14, signalPeriods: 3, smoothPeriods: 3, kFactor: 3, dFactor: 2, movingAverageType: MaType.SMA); + IEnumerable stochRsi1 = quotes.GetStochRsi(rsiPeriods: 14, stochPeriods: 14, signalPeriods: 3, smoothPeriods: 3); + IEnumerable stochRsiT = tuples.GetStochRsi(rsiPeriods: 14, stochPeriods: 14, signalPeriods: 3, smoothPeriods: 3); + IEnumerable superTrend1 = quotes.GetSuperTrend(lookbackPeriods: 10, multiplier: 3); + IEnumerable t3a = quotes.GetT3(); + IEnumerable t3b = quotes.GetT3(lookbackPeriods: 5, volumeFactor: 0.7); + IEnumerable t3T = tuples.GetT3(lookbackPeriods: 5, volumeFactor: 0.7); + IEnumerable tema1 = quotes.GetTema(lookbackPeriods: 20); + IEnumerable temaT = tuples.GetTema(lookbackPeriods: 20); + IEnumerable trix1 = quotes.GetTrix(lookbackPeriods: 15); + IEnumerable trix2 = quotes.GetTrix(lookbackPeriods: 20, signalPeriods: 3); + IEnumerable trixT = tuples.GetTrix(lookbackPeriods: 20); + IEnumerable tr = quotes.GetTr(); + IEnumerable tsi1 = quotes.GetTsi(); + IEnumerable tsi2 = quotes.GetTsi(lookbackPeriods: 20, smoothPeriods: 13, signalPeriods: 7); + IEnumerable tsiT = tuples.GetTsi(lookbackPeriods: 20, smoothPeriods: 13, signalPeriods: 7); + IEnumerable ui1 = quotes.GetUlcerIndex(); + IEnumerable ui2 = quotes.GetUlcerIndex(lookbackPeriods: 14); + IEnumerable uiT = tuples.GetUlcerIndex(lookbackPeriods: 14); + IEnumerable ultimate1 = quotes.GetUltimate(); + IEnumerable ultimate2 = quotes.GetUltimate(shortPeriods: 7, middlePeriods: 14, longPeriods: 28); + IEnumerable volStop1 = quotes.GetVolatilityStop(); + IEnumerable volStop2 = quotes.GetVolatilityStop(lookbackPeriods: 7, multiplier: 3); + IEnumerable vortex1 = quotes.GetVortex(lookbackPeriods: 14); + IEnumerable vwap1 = quotes.GetVwap(); + IEnumerable vwma1 = quotes.GetVwma(lookbackPeriods: 20); + IEnumerable williamsR = quotes.GetWilliamsR(lookbackPeriods: 14); + IEnumerable wma1 = quotes.GetWma(lookbackPeriods: 20); + IEnumerable wmaT = tuples.GetWma(lookbackPeriods: 20); + IEnumerable zigZag1 = quotes.GetZigZag(endType: EndType.Close, percentChange: 5m); + + // QUOTE UTILITIES + + IEnumerable<(DateTime Date, double Value)> useResults = quotes.Use(candlePart: CandlePart.Close); + Collection sortedCollection = quotes.ToSortedCollection(); + Collection<(DateTime, double)> tupleCollection = quotes.ToTupleCollection(candlePart: CandlePart.Close); + IEnumerable quoteAggs = quotes.Aggregate(newSize: PeriodSize.Week); + IEnumerable validQuotes = quotes.Validate(); + + Quote quote = new() { + Date = DateTime.Parse("2023-01-02", culture), + Open = 100, + High = 110, + Low = 90, + Close = 105, + Volume = 1000 + }; + + CandleProperties tuple = quote.ToCandle(); + IEnumerable candles = quotes.ToCandles(); + + Quote? foundQuote = quotes.Find(DateTime.Parse("2023-01-05", culture)); + EmaResult? foundResult = ema1.Find(DateTime.Parse("2023-01-03", culture)); + + // RESULT UTILITIES + Collection<(DateTime Date, double Value)> reusable = sma1.ToTupleChainable(); + Collection<(DateTime Date, double Value)> nanny = sma1.ToTupleNaN(); + + /* OTHER NOTES + * There were no breaking changes in: + * - NullMath + * - Numerix + * - RemoveWarmupPeriods() » resolved with new IReadOnlyList return type + * - Condense() » resolved with new IReadOnlyList return type + */ } } diff --git a/tests/external/application/README.md b/tests/external/application/README.md index 9f30b7217..e55abf102 100644 --- a/tests/external/application/README.md +++ b/tests/external/application/README.md @@ -1,3 +1,3 @@ # Test application -This is only used for performance profiling and testing. +This is only used for testing API breaking changes. It contains instantiations of all public classes and methods in the library. diff --git a/tests/external/application/Test.Application.csproj b/tests/external/application/Test.Application.csproj index 3a6685979..1902d0fec 100644 --- a/tests/external/application/Test.Application.csproj +++ b/tests/external/application/Test.Application.csproj @@ -13,10 +13,14 @@ true true true + + + false - + + diff --git a/tests/external/integration/Tests.Integration.csproj b/tests/external/integration/Tests.Integration.csproj index be51fd444..9d3dd27ac 100644 --- a/tests/external/integration/Tests.Integration.csproj +++ b/tests/external/integration/Tests.Integration.csproj @@ -7,6 +7,8 @@ enable enable + true + true latest AllEnabledByDefault diff --git a/tests/external/public-api/Tests.PublicApi.csproj b/tests/external/public-api/Tests.PublicApi.csproj index 3c37ec3a2..4182d6a32 100644 --- a/tests/external/public-api/Tests.PublicApi.csproj +++ b/tests/external/public-api/Tests.PublicApi.csproj @@ -7,6 +7,8 @@ enable disable + true + true latest AllEnabledByDefault diff --git a/tests/indicators/Tests.Indicators.csproj b/tests/indicators/Tests.Indicators.csproj index 416687f33..d7dbc7271 100644 --- a/tests/indicators/Tests.Indicators.csproj +++ b/tests/indicators/Tests.Indicators.csproj @@ -7,6 +7,8 @@ enable disable + true + true latest AllEnabledByDefault diff --git a/tests/indicators/_common/Observables/StreamHub.Utilities.StaticSeries.Tests.cs b/tests/indicators/_common/Observables/StreamHub.Utilities.Tests.cs similarity index 88% rename from tests/indicators/_common/Observables/StreamHub.Utilities.StaticSeries.Tests.cs rename to tests/indicators/_common/Observables/StreamHub.Utilities.Tests.cs index 17e87a1dd..ace29ab57 100644 --- a/tests/indicators/_common/Observables/StreamHub.Utilities.StaticSeries.Tests.cs +++ b/tests/indicators/_common/Observables/StreamHub.Utilities.Tests.cs @@ -110,8 +110,8 @@ public void GetIndex() // find position of quote Quote q = quotesList[4]; - int itemIndexEx = provider.Cache.GetIndex(q, true); - int timeIndexEx = provider.Cache.GetIndex(q.Timestamp, true); + int itemIndexEx = provider.Cache.IndexOf(q, true); + int timeIndexEx = provider.Cache.IndexOf(q.Timestamp, true); // assert: same index itemIndexEx.Should().Be(4); @@ -121,22 +121,22 @@ public void GetIndex() Quote o = Quotes[10]; Assert.ThrowsException(() => { - provider.Cache.GetIndex(o, true); + provider.Cache.IndexOf(o, true); }); Assert.ThrowsException(() => { - provider.Cache.GetIndex(o.Timestamp, true); + provider.Cache.IndexOf(o.Timestamp, true); }); // out of range (no exceptions) - int itemIndexNo = provider.Cache.GetIndex(o, false); - int timeIndexNo = provider.Cache.GetIndex(o.Timestamp, false); + int itemIndexNo = provider.Cache.IndexOf(o, false); + int timeIndexNo = provider.Cache.IndexOf(o.Timestamp, false); itemIndexNo.Should().Be(-1); timeIndexNo.Should().Be(-1); - int timeInsertOut = provider.Cache.GetIndexGte(o.Timestamp); - int timeInsertIn = provider.Cache.GetIndexGte(quotesList[2].Timestamp); + int timeInsertOut = provider.Cache.IndexGte(o.Timestamp); + int timeInsertIn = provider.Cache.IndexGte(quotesList[2].Timestamp); timeInsertOut.Should().Be(-1); timeInsertIn.Should().Be(2); diff --git a/tests/indicators/_common/Reusable/Reusable.Utilities.Tests.cs b/tests/indicators/_common/Reusable/Reusable.Utilities.Tests.cs index f8bba07d7..06e4cc14a 100644 --- a/tests/indicators/_common/Reusable/Reusable.Utilities.Tests.cs +++ b/tests/indicators/_common/Reusable/Reusable.Utilities.Tests.cs @@ -31,7 +31,7 @@ IReadOnlyList results public void ToReusableList() { IReadOnlyList reusableList = Quotes - .ToReusableList(CandlePart.Close); + .ToReusable(CandlePart.Close); Assert.IsNotNull(reusableList); Assert.AreEqual(502, reusableList.Count); diff --git a/tests/indicators/e-k/Kvo/Kvo.StaticSeries.Tests.cs b/tests/indicators/e-k/Kvo/Kvo.StaticSeries.Tests.cs index 6bbd46fe2..c82b9ea00 100644 --- a/tests/indicators/e-k/Kvo/Kvo.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/Kvo/Kvo.StaticSeries.Tests.cs @@ -1,7 +1,7 @@ namespace StaticSeries; [TestClass] -public class Klinger : StaticSeriesTestBase +public class Kvo : StaticSeriesTestBase { [TestMethod] public override void Standard() diff --git a/tests/indicators/m-r/ParabolicSar/ParabolicSar.StaticSeries.Tests.cs b/tests/indicators/m-r/ParabolicSar/ParabolicSar.StaticSeries.Tests.cs index 7780e35d8..e98f80520 100644 --- a/tests/indicators/m-r/ParabolicSar/ParabolicSar.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/ParabolicSar/ParabolicSar.StaticSeries.Tests.cs @@ -43,7 +43,7 @@ public void Extended() double initialStep = 0.01; List results = - Quotes.GetParabolicSar( + Quotes.ToParabolicSar( acclerationStep, maxAccelerationFactor, initialStep) .ToList(); @@ -110,7 +110,7 @@ public void InsufficientQuotes() public override void BadData() { IReadOnlyList r = BadQuotes - .GetParabolicSar(0.2, 0.2, 0.2); + .ToParabolicSar(0.2, 0.2, 0.2); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Sar is double.NaN)); @@ -165,6 +165,6 @@ public void Exceptions() // insufficient initial factor Assert.ThrowsException( - () => Quotes.GetParabolicSar(0.02, 0.5, 0)); + () => Quotes.ToParabolicSar(0.02, 0.5, 0)); } } diff --git a/tests/indicators/m-r/Renko/Renko.StaticSeries.Tests.cs b/tests/indicators/m-r/Renko/Renko.StaticSeries.Tests.cs index 1a2ed70ac..a5414e1df 100644 --- a/tests/indicators/m-r/Renko/Renko.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/Renko/Renko.StaticSeries.Tests.cs @@ -81,7 +81,7 @@ public void StandardHighLow() public void Atr() { IReadOnlyList results = Quotes - .GetRenkoAtr(14); + .ToRenkoAtr(14); // proper quantities Assert.AreEqual(29, results.Count); diff --git a/tests/other/GlobalSuppressions.cs b/tests/other/GlobalSuppressions.cs deleted file mode 100644 index 03782e35e..000000000 --- a/tests/other/GlobalSuppressions.cs +++ /dev/null @@ -1,6 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage( - "Maintainability", - "CA1515:Consider making public types internal", - Justification = "Test projects use public class types.")] diff --git a/tests/performance/Perf.StaticSeries.cs b/tests/performance/Perf.StaticSeries.cs index 6892ef6e4..ac3128842 100644 --- a/tests/performance/Perf.StaticSeries.cs +++ b/tests/performance/Perf.StaticSeries.cs @@ -131,9 +131,6 @@ public class SeriesIndicators [Benchmark] public object ToKama() => q.ToKama(); - [Benchmark] - public object ToKlinger() => q.ToKvo(); - [Benchmark] public object ToKeltner() => q.ToKeltner(); diff --git a/tests/performance/Perf.Utility.cs b/tests/performance/Perf.Utility.cs index eb1401be1..eca434507 100644 --- a/tests/performance/Perf.Utility.cs +++ b/tests/performance/Perf.Utility.cs @@ -15,10 +15,10 @@ public class Utility public object ToListQuoteD() => q.ToQuoteDList(); [Benchmark] - public object ToReusableClose() => q.ToReusableList(CandlePart.Close); + public object ToReusableClose() => q.ToReusable(CandlePart.Close); [Benchmark] - public object ToReusableOhlc4() => q.ToReusableList(CandlePart.OHLC4); + public object ToReusableOhlc4() => q.ToReusable(CandlePart.OHLC4); [Benchmark] public object ToCandleResults() => q.ToCandles(); diff --git a/tests/performance/Tests.Performance.csproj b/tests/performance/Tests.Performance.csproj index 974a936ca..69cb3da43 100644 --- a/tests/performance/Tests.Performance.csproj +++ b/tests/performance/Tests.Performance.csproj @@ -8,6 +8,8 @@ enable disable + true + true latest AllEnabledByDefault diff --git a/tests/simulate/Simulator.cs b/tests/simulate/Simulator.cs new file mode 100644 index 000000000..4303e4cd3 --- /dev/null +++ b/tests/simulate/Simulator.cs @@ -0,0 +1,214 @@ + +using Skender.Stock.Indicators; + +namespace Test.Application; + +public static class Simulate +{ + public static void Menu(string scenario = "C") + { + Go go = new(); + + switch (scenario) + { + case "A": go.QuoteHub(); break; + case "B": go.EmaHub(); break; + case "C": go.MultipleSubscribers(); break; + default: break; + } + } +} + +public class Go +{ + private readonly bool verbose = true; + private static readonly QuoteHub provider = new(); + + private static readonly IReadOnlyList quotesList = Data.Data.GetDefault(); + + private static readonly int quotesLength = quotesList.Count; + + public Go() + { + if (verbose) + { + Prefill(); + } + } + + private static void Prefill() + { + // prefill quotes to provider + for (int i = 0; i < quotesLength; i++) + { + provider.Add(quotesList[i]); + } + } + + internal void QuoteHub() + { + EmaHub emaHub = provider.ToEma(14); + + if (!verbose) + { + return; + } + + // initialize console display + Console.WriteLine(""" + Date Close price + -------------------- + """); + + // add quotes to provider + for (int i = 0; i < quotesLength; i++) + { + Quote q = quotesList[i]; + provider.Add(q); + + // wait for next quote + Timewarp(); + + // send to console + SendToConsole(q); + } + } + + private static void SendToConsole(Quote q) + { + string m = $"{q.Timestamp:yyyy-MM-dd} ${q.Close:N2}"; + Console.WriteLine(m); + } + + + internal void EmaHub() + { + EmaHub emaHub = provider.ToEma(14); + + if (!verbose) + { + return; + } + + // initialize console display + Console.WriteLine(""" + Date Close price EMA(14) + ------------------------------ + """); + + // add quotes to provider + for (int i = 0; i < quotesLength; i++) + { + Quote q = quotesList[i]; + provider.Add(q); + + // wait for next quote + Timewarp(); + + // send to console + SendToConsole(q, emaHub); + } + } + + private static void SendToConsole(Quote q, EmaHub emaHub) + where T : IReusable + { + string m = $"{q.Timestamp:yyyy-MM-dd} ${q.Close:N2}"; + + EmaResult e = emaHub.Results[^1]; + + if (e.Ema is not null) + { + m += $"{e.Ema,10:N3}"; + } + else + { + m += $"{"[null]",10}"; + } + + Console.WriteLine(m); + } + + internal void MultipleSubscribers() + { + SmaHub smaHub = provider.ToSma(3); + EmaHub emaHub = provider.ToEma(5); + EmaHub useChain = provider.ToQuotePart(CandlePart.HL2).ToEma(7); + EmaHub emaChain = provider.ToSma(4).ToEma(4); + + if (!verbose) + { + return; + } + + // initialize console display + Console.WriteLine(""" + Date Close price SMA(3) EMA(5) EMA(7,HL2) SMA/EMA(8) + ------------------------------------------------------------- + """); + + // add quotes to provider + for (int i = 0; i < quotesLength; i++) + { + Quote q = quotesList[i]; + provider.Add(q); + + // wait for next quote + Timewarp(); + + // send to console + SendToConsole(q, smaHub, emaHub, useChain, emaChain); + } + } + + private static void SendToConsole( + Quote q, + SmaHub smaHub, + EmaHub emaHub, + EmaHub useChain, + EmaHub emaChain) + { + string m = $"{q.Timestamp:yyyy-MM-dd} ${q.Close:N2}"; + + SmaResult s = smaHub.Results[^1]; + EmaResult e = emaHub.Results[^1]; + EmaResult u = useChain.Results[^1]; + EmaResult c = emaChain.Results[^1]; + + if (s.Sma is not null) + { + m += $"{s.Sma,8:N1}"; + } + + if (e.Ema is not null) + { + m += $"{e.Ema,8:N1}"; + } + + if (u.Ema is not null) + { + m += $"{u.Ema,12:N1}"; + } + + if (c.Ema is not null) + { + m += $"{c.Ema,12:N1}"; + } + + Console.WriteLine(m); + } + + /// + /// Emulate quote arrival rate. + /// Use '0' to disable. + /// + private static void Timewarp(int quotesPerMinute = 0) + { + if (quotesPerMinute == 0) + { + return; + } + + Thread.Sleep(60000 / quotesPerMinute); + } +} diff --git a/tests/simulate/Test.Simulation.csproj b/tests/simulate/Test.Simulation.csproj index b9f7b92ef..2119e65ec 100644 --- a/tests/simulate/Test.Simulation.csproj +++ b/tests/simulate/Test.Simulation.csproj @@ -7,6 +7,8 @@ enable enable + true + true latest AllEnabledByDefault @@ -19,4 +21,10 @@ + + + Always + + + diff --git a/tests/external/application/_testdata/TestData.Getter.cs b/tests/simulate/_testdata/TestData.Getter.cs similarity index 98% rename from tests/external/application/_testdata/TestData.Getter.cs rename to tests/simulate/_testdata/TestData.Getter.cs index dcc0a79bc..e8a6221e0 100644 --- a/tests/external/application/_testdata/TestData.Getter.cs +++ b/tests/simulate/_testdata/TestData.Getter.cs @@ -1,3 +1,5 @@ +using Skender.Stock.Indicators; + namespace Test.Data; // TEST QUOTE GETTERs diff --git a/tests/external/application/_testdata/TestData.Imports.cs b/tests/simulate/_testdata/TestData.Imports.cs similarity index 97% rename from tests/external/application/_testdata/TestData.Imports.cs rename to tests/simulate/_testdata/TestData.Imports.cs index 222ccd511..9e90569e1 100644 --- a/tests/external/application/_testdata/TestData.Imports.cs +++ b/tests/simulate/_testdata/TestData.Imports.cs @@ -1,4 +1,5 @@ using System.Globalization; +using Skender.Stock.Indicators; namespace Test.Data; diff --git a/tests/external/application/_testdata/data/compare.csv b/tests/simulate/_testdata/data/compare.csv similarity index 100% rename from tests/external/application/_testdata/data/compare.csv rename to tests/simulate/_testdata/data/compare.csv diff --git a/tests/external/application/_testdata/data/default.csv b/tests/simulate/_testdata/data/default.csv similarity index 100% rename from tests/external/application/_testdata/data/default.csv rename to tests/simulate/_testdata/data/default.csv diff --git a/tests/external/application/_testdata/data/intraday.csv b/tests/simulate/_testdata/data/intraday.csv similarity index 100% rename from tests/external/application/_testdata/data/intraday.csv rename to tests/simulate/_testdata/data/intraday.csv diff --git a/tests/external/application/_testdata/data/longest.csv b/tests/simulate/_testdata/data/longest.csv similarity index 100% rename from tests/external/application/_testdata/data/longest.csv rename to tests/simulate/_testdata/data/longest.csv diff --git a/tests/external/application/_testdata/data/longish.csv b/tests/simulate/_testdata/data/longish.csv similarity index 100% rename from tests/external/application/_testdata/data/longish.csv rename to tests/simulate/_testdata/data/longish.csv From 55e521b314952f45f31f41bd9750d3ecf122bc3a Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Sun, 1 Dec 2024 03:12:26 -0500 Subject: [PATCH 15/18] minor update to ADL docs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Dave Skender <8432125+DaveSkender@users.noreply.github.com> --- docs/_indicators/Adl.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_indicators/Adl.md b/docs/_indicators/Adl.md index eeb10b3e5..e5e24714f 100644 --- a/docs/_indicators/Adl.md +++ b/docs/_indicators/Adl.md @@ -38,7 +38,7 @@ IReadOnlyList ### AdlResult -**`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` +**`Timestamp`** _`DateTime`_ - Date from evaluated `TQuote` **`MoneyFlowMultiplier`** _`double`_ - Money Flow Multiplier From 630bcdfce92ae92ab04370b510aa15dd8a7785f3 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Sun, 15 Dec 2024 23:53:08 -0500 Subject: [PATCH 16/18] feat: Max cache size for StreamHub (#1290) --- src/_common/Observables/IStreamObservable.cs | 5 ++ src/_common/Observables/IStreamObserver.cs | 10 +++- .../Observables/StreamHub.Observable.cs | 21 ++++++-- src/_common/Observables/StreamHub.Observer.cs | 14 ++++- src/_common/Observables/StreamHub.cs | 38 +++++++++++-- src/_common/Quotes/Quote.StreamHub.cs | 47 +++++++++++----- .../Observables/StreamHub.CacheMgmt.Tests.cs | 54 +++++++++++++++++++ 7 files changed, 165 insertions(+), 24 deletions(-) diff --git a/src/_common/Observables/IStreamObservable.cs b/src/_common/Observables/IStreamObservable.cs index 1fe143665..bcdc18206 100644 --- a/src/_common/Observables/IStreamObservable.cs +++ b/src/_common/Observables/IStreamObservable.cs @@ -60,6 +60,11 @@ public interface IStreamObservable /// BinarySettings Properties { get; } + /// + /// Gets the maximum size of the Cache list. + /// + int MaxCacheSize { get; } + /// /// Gets the current number of subscribers. /// diff --git a/src/_common/Observables/IStreamObserver.cs b/src/_common/Observables/IStreamObserver.cs index ed5c89a30..4e1a7e568 100644 --- a/src/_common/Observables/IStreamObserver.cs +++ b/src/_common/Observables/IStreamObserver.cs @@ -39,7 +39,15 @@ public interface IStreamObserver /// /// Starting point in timeline to rebuild. /// - void OnChange(DateTime fromTimestamp); + void OnRebuild(DateTime fromTimestamp); + + /// + /// Provides the observer with notification to prune data. + /// + /// + /// Ending point in timeline to prune. + /// + void OnPrune(DateTime toTimestamp); /// /// Provides the observer with errors from the provider diff --git a/src/_common/Observables/StreamHub.Observable.cs b/src/_common/Observables/StreamHub.Observable.cs index efc79e41b..a0534fc8c 100644 --- a/src/_common/Observables/StreamHub.Observable.cs +++ b/src/_common/Observables/StreamHub.Observable.cs @@ -15,6 +15,9 @@ public abstract partial class StreamHub : IStreamObservable /// public IReadOnlyList ReadCache => Cache; + /// + public int MaxCacheSize { get; init; } + /// public virtual BinarySettings Properties { get; init; } = new(0); // default 0b00000000 @@ -94,12 +97,24 @@ private void NotifyObserversOnAdd(TOut item, int? indexHint) /// /// Sends rebuilds point in time to all subscribers. /// - /// Rebuild starting positions. - private void NotifyObserversOnChange(DateTime fromTimestamp) + /// Rebuild starting date. + private void NotifyObserversOnRebuild(DateTime fromTimestamp) + { + foreach (IStreamObserver o in _observers.ToArray()) + { + o.OnRebuild(fromTimestamp); + } + } + + /// + /// Sends prune notification to all subscribers. + /// + /// Prune ending date. + private void NotifyObserversOnPrune(DateTime toTimestamp) { foreach (IStreamObserver o in _observers.ToArray()) { - o.OnChange(fromTimestamp); + o.OnPrune(toTimestamp); } } diff --git a/src/_common/Observables/StreamHub.Observer.cs b/src/_common/Observables/StreamHub.Observer.cs index 522c3a3d8..ccd107218 100644 --- a/src/_common/Observables/StreamHub.Observer.cs +++ b/src/_common/Observables/StreamHub.Observer.cs @@ -35,9 +35,21 @@ public virtual void OnAdd(TIn item, bool notify, int? indexHint) } /// - public void OnChange(DateTime fromTimestamp) + public void OnRebuild(DateTime fromTimestamp) => Rebuild(fromTimestamp); + /// + public void OnPrune(DateTime toTimestamp) + { + while (Cache.Count > 0 && Cache[0].Timestamp <= toTimestamp) + { + Cache.RemoveAt(0); + } + + // notify observers + NotifyObserversOnPrune(toTimestamp); + } + /// public void OnError(Exception exception) => throw exception; diff --git a/src/_common/Observables/StreamHub.cs b/src/_common/Observables/StreamHub.cs index d1437cf01..7e58343e4 100644 --- a/src/_common/Observables/StreamHub.cs +++ b/src/_common/Observables/StreamHub.cs @@ -25,6 +25,9 @@ private protected StreamHub(IStreamObservable provider) // inherit settings (reinstantiate struct on heap) Properties = Properties.Combine(provider.Properties); + + // inherit max cache size + MaxCacheSize = provider.MaxCacheSize; } #endregion @@ -129,7 +132,7 @@ public void Insert(TIn newIn) } Cache.Insert(index, result); - NotifyObserversOnChange(result.Timestamp); + NotifyObserversOnRebuild(result.Timestamp); } // normal add @@ -247,6 +250,9 @@ Provider terminated. return !Properties[1]; } + // maintenance pruning + PruneCache(); + // not repeating OverflowCount = 0; LastItem = item; @@ -264,7 +270,7 @@ Provider terminated. public void Remove(TOut cachedItem) { Cache.Remove(cachedItem); - NotifyObserversOnChange(cachedItem.Timestamp); + NotifyObserversOnRebuild(cachedItem.Timestamp); } /// @@ -276,7 +282,7 @@ public void RemoveAt(int cacheIndex) { TOut cachedItem = Cache[cacheIndex]; Cache.RemoveAt(cacheIndex); - NotifyObserversOnChange(cachedItem.Timestamp); + NotifyObserversOnRebuild(cachedItem.Timestamp); } /// @@ -296,7 +302,7 @@ public void RemoveRange(DateTime fromTimestamp, bool notify) // notify observers if (notify) { - NotifyObserversOnChange(fromTimestamp); + NotifyObserversOnRebuild(fromTimestamp); } } @@ -321,6 +327,27 @@ public void RemoveRange(int fromIndex, bool notify) RemoveRange(fromTimestamp, notify); } + + /// + /// Prunes the cache to the maximum size. + /// + protected void PruneCache() + { + if (Cache.Count < MaxCacheSize) + { + return; + } + + DateTime toTimestamp = DateTime.MinValue; + + while (Cache.Count >= MaxCacheSize) + { + toTimestamp = Cache[0].Timestamp; + Cache.RemoveAt(0); + } + + NotifyObserversOnPrune(toTimestamp); + } #endregion #region REBUILD & REINITIALIZE @@ -372,7 +399,7 @@ public void Rebuild(DateTime fromTimestamp) } // notify observers - NotifyObserversOnChange(fromTimestamp); + NotifyObserversOnRebuild(fromTimestamp); } /// @@ -413,6 +440,7 @@ protected virtual void RollbackState(DateTime timestamp) #region chain and quote variants /// +/// Streaming data provider. public abstract class QuoteProvider( IStreamObservable provider ) : StreamHub(provider), IQuoteProvider diff --git a/src/_common/Quotes/Quote.StreamHub.cs b/src/_common/Quotes/Quote.StreamHub.cs index a3e3c7030..81004d021 100644 --- a/src/_common/Quotes/Quote.StreamHub.cs +++ b/src/_common/Quotes/Quote.StreamHub.cs @@ -25,9 +25,26 @@ public class QuoteHub where TQuote : IQuote { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the base, without its own provider. /// - public QuoteHub() : base(new EmptyQuoteProvider()) { } + /// Maximum in-memory cache size. + public QuoteHub(int? maxCacheSize = null) + : base(new BaseProvider()) + { + + const int maxCacheSizeDefault = (int)(0.9 * int.MaxValue); + + if (maxCacheSize is not null and > maxCacheSizeDefault) + { + string message + = $"'{nameof(maxCacheSize)}' must be less than {maxCacheSizeDefault}."; + + throw new ArgumentOutOfRangeException( + nameof(maxCacheSize), maxCacheSize, message); + } + + MaxCacheSize = maxCacheSize ?? maxCacheSizeDefault; + } /// /// Initializes a new instance of the class with a specified provider. @@ -58,20 +75,22 @@ public override string ToString() } /// -/// Empty quote provider for base Quote Hub initialization. +/// Inert provider for base Hub initialization. /// -/// Internal use only. Do not use directly. -/// -public class EmptyQuoteProvider - : IQuoteProvider - where TQuote : IQuote +/// +public class BaseProvider + : IStreamObservable + where T : IReusable { /// - /// Default quote provider is parent-less Quote Hub. + /// Inert quote provider is parent-less Hub. /// It does not transfer its setting to its children. /// public BinarySettings Properties { get; } = new(0b00000001, 0b11111110); + /// + public int MaxCacheSize => 0; + /// /// Gets the number of observers. /// @@ -85,27 +104,27 @@ public class EmptyQuoteProvider /// /// Gets the list of quotes. /// - public IReadOnlyList Quotes { get; } = Array.Empty(); + public IReadOnlyList Quotes { get; } = Array.Empty(); /// /// Gets a reference to the cache. /// /// A read-only list of quotes. - public IReadOnlyList GetCacheRef() => Array.Empty(); + public IReadOnlyList GetCacheRef() => Array.Empty(); /// /// Determines whether the specified observer is a subscriber. /// /// The observer to check. /// true if the observer is a subscriber; otherwise, false. - public bool HasSubscriber(IStreamObserver observer) => false; + public bool HasSubscriber(IStreamObserver observer) => false; /// /// Subscribes the specified observer. /// /// The observer to subscribe. /// A disposable object that can be used to unsubscribe. - public IDisposable Subscribe(IStreamObserver observer) + public IDisposable Subscribe(IStreamObserver observer) => throw new InvalidOperationException(); /// @@ -113,7 +132,7 @@ public IDisposable Subscribe(IStreamObserver observer) /// /// The observer to unsubscribe. /// true if the observer was unsubscribed; otherwise, false. - public bool Unsubscribe(IStreamObserver observer) + public bool Unsubscribe(IStreamObserver observer) => throw new InvalidOperationException(); /// diff --git a/tests/indicators/_common/Observables/StreamHub.CacheMgmt.Tests.cs b/tests/indicators/_common/Observables/StreamHub.CacheMgmt.Tests.cs index 188ba1470..73cc1ff19 100644 --- a/tests/indicators/_common/Observables/StreamHub.CacheMgmt.Tests.cs +++ b/tests/indicators/_common/Observables/StreamHub.CacheMgmt.Tests.cs @@ -147,4 +147,58 @@ public void OverflowedAndReset() provider.EndTransmission(); } + + [TestMethod] + public void MaxCacheSize() + { + int maxCacheSize = 30; + + // initialize + QuoteHub provider = new(maxCacheSize); + SmaHub observer = provider.ToSma(20); + + // sets max cache size + provider.MaxCacheSize.Should().Be(maxCacheSize); + + // inherits max cache size + observer.MaxCacheSize.Should().Be(maxCacheSize); + } + + [TestMethod] + public void PrunedCache() + { + int maxCacheSize = 30; + + // initialize + QuoteHub provider = new(maxCacheSize); + SmaHub observer = provider.ToSma(20); + IReadOnlyList seriesList = Quotes.ToSma(20); + + // add quotes + provider.Add(Quotes.Take(maxCacheSize)); + + // assert: cache size is full size + provider.Quotes.Should().HaveCount(maxCacheSize); + observer.Results.Should().HaveCount(maxCacheSize); + + // add more quotes to exceed max cache size + provider.Add(Quotes.Skip(maxCacheSize).Take(10)); + + // assert: cache size is pruned + provider.Results.Should().HaveCount(maxCacheSize); + observer.Results.Should().HaveCount(maxCacheSize); + + // assert: correct values remain + provider.Quotes.Should().BeEquivalentTo( + Quotes.Skip(10).Take(maxCacheSize)); + + observer.Results.Should().BeEquivalentTo( + seriesList.Skip(10).Take(maxCacheSize)); + } + + [TestMethod] + public void PrunedAsymmetric() => + // TODO: asymetric results (e.g. Renko) + // pruned to correct date, instead of count + Assert.Inconclusive("not implemented"); } From ab01d6714140e7a735e79055e753c347cd86212c Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Mon, 23 Dec 2024 19:49:21 -0500 Subject: [PATCH 17/18] feat: ToStringOut() for series (#1150) Signed-off-by: Dave Skender <8432125+DaveSkender@users.noreply.github.com> --- src/GlobalUsings.cs | 1 + src/_common/Enums.cs | 21 + src/_common/Generics/StringOut.List.cs | 384 ++++++++++++++++ src/_common/Generics/StringOut.Type.cs | 237 ++++++++++ src/_common/Math/Numerical.cs | 37 ++ src/_common/ObsoleteV3.cs | 2 + src/_common/Quotes/Quote.Models.cs | 4 +- src/_common/Reusable/IReusable.cs | 3 + src/a-d/Adl/Adl.Models.cs | 1 + src/a-d/Adx/Adx.Models.cs | 1 + src/a-d/Alma/Alma.Models.cs | 1 + src/a-d/Aroon/Aroon.Models.cs | 1 + src/a-d/Atr/Atr.Models.cs | 1 + src/a-d/Awesome/Awesome.Models.cs | 1 + src/a-d/Beta/Beta.Models.cs | 1 + .../BollingerBands/BollingerBands.Models.cs | 1 + src/a-d/Bop/Bop.Models.cs | 1 + src/a-d/Cci/Cci.Models.cs | 1 + src/a-d/ChaikinOsc/ChaikinOsc.Models.cs | 1 + src/a-d/Chandelier/Chandelier.Models.cs | 1 + src/a-d/Chop/Chop.Models.cs | 1 + src/a-d/Cmf/Cmf.Models.cs | 1 + src/a-d/Cmo/Cmo.Models.cs | 1 + src/a-d/ConnorsRsi/ConnorsRsi.Models.cs | 1 + src/a-d/Correlation/Correlation.Models.cs | 1 + src/a-d/Dema/Dema.Models.cs | 1 + src/a-d/Dpo/Dpo.Models.cs | 1 + src/a-d/Dynamic/Dynamic.Models.cs | 1 + src/e-k/ElderRay/ElderRay.Models.cs | 1 + src/e-k/Ema/Ema.Models.cs | 1 + src/e-k/Epma/Epma.Models.cs | 1 + .../FisherTransform/FisherTransform.Models.cs | 1 + src/e-k/ForceIndex/ForceIndex.Models.cs | 1 + src/e-k/Hma/Hma.Models.cs | 1 + src/e-k/HtTrendline/HtTrendline.Models.cs | 1 + src/e-k/Hurst/Hurst.Models.cs | 1 + src/e-k/Kama/Kama.Models.cs | 1 + src/e-k/Kvo/Kvo.Models.cs | 1 + src/m-r/Macd/Macd.Models.cs | 1 + src/m-r/Mama/Mama.Models.cs | 1 + src/m-r/Mfi/Mfi.Models.cs | 1 + src/m-r/Obv/Obv.Models.cs | 1 + src/m-r/ParabolicSar/ParabolicSar.Models.cs | 1 + src/m-r/Pmo/Pmo.Models.cs | 1 + src/m-r/Prs/Prs.Models.cs | 1 + src/m-r/Pvo/Pvo.Models.cs | 1 + src/m-r/Roc/Roc.Models.cs | 1 + src/m-r/RocWb/RocWb.Models.cs | 1 + src/m-r/Rsi/Rsi.Models.cs | 1 + src/s-z/Slope/Slope.Models.cs | 1 + src/s-z/Sma/Sma.Models.cs | 1 + src/s-z/Smi/Smi.Models.cs | 1 + src/s-z/Smma/Smma.Models.cs | 1 + src/s-z/Stc/Stc.Models.cs | 1 + src/s-z/StdDev/StdDev.Models.cs | 1 + src/s-z/Stoch/Stoch.Models.cs | 1 + src/s-z/StochRsi/StochRsi.Models.cs | 1 + src/s-z/T3/T3.Models.cs | 1 + src/s-z/Tema/Tema.Models.cs | 1 + src/s-z/Tr/Tr.Models.cs | 1 + src/s-z/Trix/Trix.Models.cs | 1 + src/s-z/Tsi/Tsi.Models.cs | 1 + src/s-z/UlcerIndex/UlcerIndex.Models.cs | 1 + src/s-z/Ultimate/Ultimate.Models.cs | 1 + .../VolatilityStop/VolatilityStop.Models.cs | 1 + src/s-z/Vwap/Vwap.Models.cs | 1 + src/s-z/Vwma/Vwma.Models.cs | 1 + src/s-z/WilliamsR/WilliamsR.Models.cs | 1 + src/s-z/Wma/Wma.Models.cs | 1 + src/s-z/ZigZag/ZigZag.Models.cs | 1 + tests/indicators/TestBase.cs | 1 + tests/indicators/Tests.Indicators.csproj | 4 +- .../_common/Generics/StringOut.Tests.cs | 430 ++++++++++++++++++ tests/indicators/_testdata/TestData.Getter.cs | 10 +- tests/indicators/_testdata/TestData.Random.cs | 95 +++- .../_testdata/TestData.Utilities.cs | 9 + tests/performance/Perf.Utility.cs | 25 +- 77 files changed, 1288 insertions(+), 37 deletions(-) create mode 100644 src/GlobalUsings.cs create mode 100644 src/_common/Generics/StringOut.List.cs create mode 100644 src/_common/Generics/StringOut.Type.cs create mode 100644 tests/indicators/_common/Generics/StringOut.Tests.cs create mode 100644 tests/indicators/_testdata/TestData.Utilities.cs diff --git a/src/GlobalUsings.cs b/src/GlobalUsings.cs new file mode 100644 index 000000000..6abfda8ed --- /dev/null +++ b/src/GlobalUsings.cs @@ -0,0 +1 @@ +global using System.Text.Json.Serialization; diff --git a/src/_common/Enums.cs b/src/_common/Enums.cs index 16bc408ac..be6b267ff 100644 --- a/src/_common/Enums.cs +++ b/src/_common/Enums.cs @@ -206,6 +206,27 @@ public enum MaType WMA } +/// +/// String output format type. +/// +public enum OutType +{ + /// + /// Fixed width format. + /// + FixedWidth, + + /// + /// Comma-separated values format. + /// + CSV, + + /// + /// JSON format. + /// + JSON +} + /// /// Period size, usually referring to the time period represented in a quote candle. /// diff --git a/src/_common/Generics/StringOut.List.cs b/src/_common/Generics/StringOut.List.cs new file mode 100644 index 000000000..8009d9112 --- /dev/null +++ b/src/_common/Generics/StringOut.List.cs @@ -0,0 +1,384 @@ +using System.Reflection; +using System.Text; + + +namespace Skender.Stock.Indicators; + +/// +/// Provides extension methods for converting ISeries lists to formatted strings. +/// +public static partial class StringOut +{ + private static readonly string[] IndexHeaderName = ["i"]; + + /// + /// Default formats for numeric and date properties. + /// 'Key' value is either property type or property name. + /// 'Value' is the format string for the property's ToString(). + /// + /// The last matching key is used, so when users provide + /// an 'args' dictionary, it will override these defaults. + /// + /// + private static readonly Dictionary defaultArgs = new() + { + { "Decimal" , "auto" }, + { "Double" , "N6" }, + { "Single" , "N6" }, + { "Int16" , "N0" }, + { "Int32" , "N0" }, + { "Int64" , "N0" }, + { "DateOnly", "yyyy-MM-dd" }, + { "DateTime", "yyyy-MM-dd HH:mm:ss" }, + { "DateTimeOffset", "yyyy-MM-dd HH:mm:ss" }, + { "Timestamp", "auto" } + }; + + /// + /// Converts a list of ISeries to a fixed-width formatted string and writes it to the console. + /// + /// The type of elements in the list, which must implement ISeries. + /// The list of ISeries elements to convert. + /// The fixed-width formatted string representation of the list. + public static string ToConsole( + this IReadOnlyList source) + where T : ISeries + { + string? output = source.ToStringOut(); + Console.WriteLine(output); + return output ?? string.Empty; + } + + /// + /// Converts a list of ISeries to a fixed-width formatted string. + /// + /// The type of elements in the list, which must implement ISeries. + /// The list of ISeries elements to convert. + /// Optional overrides for `ToString()` formatter. Key values can be type or property name. + /// A fixed-width formatted string representation of the list. + /// + /// Examples: + /// + /// Dictionary<string, string> args = new() + /// { + /// { "Decimal", "N2" }, + /// { "DateTime", "MM/dd/yyyy" }, + /// { "MyPropertyName", "C" } + /// }; + /// + /// + public static string ToStringOut( + this IEnumerable source, IDictionary? args = null) + where T : ISeries => source.ToList().ToStringOut(0, int.MaxValue, args); + + /// + /// Converts a list of ISeries to a fixed-width formatted string. + /// + /// The type of elements in the list, which must implement ISeries. + /// The list of ISeries elements to convert. + /// The maximum number of elements to include in the output. + /// Optional overrides for `ToString()` formatter. Key values can be type or property name. + /// A fixed-width formatted string representation of the list. + /// + /// Examples: + /// + /// Dictionary<string, string> args = new() + /// { + /// { "Decimal", "N2" }, + /// { "DateTime", "MM/dd/yyyy" }, + /// { "MyPropertyName", "C" } + /// }; + /// + /// + public static string ToStringOut( + this IEnumerable source, int limitQty, IDictionary? args = null) + where T : ISeries => source.ToList().ToStringOut(0, limitQty - 1, args); + + /// + /// Converts a list of ISeries to a fixed-width formatted string. + /// + /// The type of elements in the list, which must implement ISeries. + /// The list of ISeries elements to convert. + /// The starting index of the elements to include in the output. + /// The ending index of the elements to include in the output. + /// Optional overrides for `ToString()` formatter. Key values can be type or property name. + /// A fixed-width formatted string representation of the list. + /// + /// Examples: + /// + /// Dictionary<string, string> args = new() + /// { + /// { "Decimal", "N2" }, + /// { "DateTime", "MM/dd/yyyy" }, + /// { "MyPropertyName", "C" } + /// }; + /// + /// + public static string ToStringOut( + this IEnumerable source, int startIndex, int endIndex, IDictionary? args = null) + where T : ISeries => source.ToList().ToStringOut(startIndex, endIndex, args); + + /// + /// Converts a list of ISeries to a fixed-width formatted string. + /// + /// The type of elements in the list, which must implement ISeries. + /// The list of ISeries elements to convert. + /// Optional overrides for `ToString()` formatter. Key values can be type or property name. + /// A fixed-width formatted string representation of the list. + /// + /// Examples: + /// + /// Dictionary<string, string> args = new() + /// { + /// { "Decimal", "N2" }, + /// { "DateTime", "MM/dd/yyyy" }, + /// { "MyPropertyName", "C" } + /// }; + /// + /// + public static string ToStringOut( + this IReadOnlyList source, + IDictionary? args = null) + where T : ISeries => source.ToStringOut(0, int.MaxValue, args); + + /// + /// Converts a list of ISeries to a fixed-width formatted string. + /// + /// The type of elements in the list, which must implement ISeries. + /// The list of ISeries elements to convert. + /// The maximum number of elements to include in the output. + /// Optional overrides for `ToString()` formatter. Key values can be type or property name. + /// A fixed-width formatted string representation of the list. + /// + /// Examples: + /// + /// Dictionary<string, string> args = new() + /// { + /// { "Decimal", "N2" }, + /// { "DateTime", "MM/dd/yyyy" }, + /// { "MyPropertyName", "C" } + /// }; + /// + /// + public static string ToStringOut( + this IReadOnlyList source, + int limitQty, + IDictionary? args = null) + where T : ISeries => source.ToStringOut(0, limitQty - 1, args); + + /// + /// Converts a list of ISeries to a fixed-width formatted string. + /// + /// The type of elements in the list, which must implement ISeries. + /// The list of ISeries elements to convert. + /// The starting index of the elements to include in the output. + /// The ending index of the elements to include in the output. + /// Optional overrides for `ToString()` formatter. Key values can be type or property name. + /// A fixed-width formatted string representation of the list. + /// + /// Examples: + /// + /// Dictionary<string, string> args = new() + /// { + /// { "Decimal", "N2" }, + /// { "DateTime", "MM/dd/yyyy" }, + /// { "MyPropertyName", "C" } + /// }; + /// + /// + public static string ToStringOut( + this IReadOnlyList source, + int startIndex, + int endIndex, + IDictionary? args = null) + where T : ISeries + { + ArgumentNullException.ThrowIfNull(source); + + int endIndexReal = Math.Min(endIndex, source.Count - 1); + T[] sourceSubset = source.Skip(startIndex).Take(endIndexReal - startIndex + 1).ToArray(); + + Dictionary formatArgs = defaultArgs + .Concat(args ?? Enumerable.Empty>()) + .GroupBy(kvp => kvp.Key.ToUpperInvariant()) + .ToDictionary(g => g.Key, g => g.Last().Value); + + // Get properties of the object + PropertyInfo[] properties = GetStringOutProperties(typeof(T)); + + // Define header values + string[] headers = IndexHeaderName + .Concat(properties.Select(p => p.Name)) + .ToArray(); + + int columnCount = headers.Length; + + // Set formatting for each column + string[] formats = new string[columnCount]; + bool[] alignLeft = new bool[columnCount]; + int[] columnWidth = headers.Select(header => header.Length).ToArray(); + + formats[0] = "N0"; // index is always an integer + alignLeft[0] = false; // index is always right-aligned + + for (int i = 1; i < columnCount; i++) + { + PropertyInfo property = properties[i - 1]; + + // try by property type + formats[i] = formatArgs.TryGetValue( + ColloquialTypeName(property.PropertyType).ToUpperInvariant(), + out string? typeFormat) + ? typeFormat + : string.Empty; + + // try by property name (overrides type) + formats[i] = formatArgs.TryGetValue( + property.Name.ToUpperInvariant(), + out string? nameFormat) + ? nameFormat + : formats[i]; + + // handle auto-detect + if (formats[i] == "auto") + { + formats[i] = AutoFormat(property, sourceSubset); + } + + // set alignment + alignLeft[i] = !property.PropertyType.IsNumeric(); + } + + // Compile formatted values + string[][] dataRows = sourceSubset.Select((item, index) => { + string[] row = new string[columnCount]; + + row[0] = (index + startIndex).ToString(formats[0], culture); + + for (int i = 1; i < columnCount; i++) + { + object? value = properties[i - 1].GetValue(item); + row[i] = value is IFormattable formattable + ? formattable.ToString(formats[i], culture) ?? string.Empty + : value?.ToString() ?? string.Empty; + + columnWidth[i] = Math.Max(columnWidth[i], row[i].Length); + } + + return row; + }).ToArray(); + + columnWidth[0] = dataRows.Max(row => row[0].Length); + + // Compile formatted string + StringBuilder sb = new(); + + // Create header line with proper alignment + sb.AppendLine(string.Join(" ", + headers.Select((header, index) => alignLeft[index] + ? header.PadRight(columnWidth[index]) + : header.PadLeft(columnWidth[index]) + ))); + + // Create separator + sb.AppendLine(new string('-', columnWidth.Sum(w => w + 2) - 2)); + + // Create data lines with proper alignment + foreach (string[] row in dataRows) + { + sb.AppendLine(string.Join(" ", + row.Select((value, index) => alignLeft[index] + ? value.PadRight(columnWidth[index]) + : value.PadLeft(columnWidth[index]) + ))); + } + + return sb.ToString(); // includes a trailing newline + } + + /// + /// Determines the appropriate date precision or decimal places + /// based on the first 1,000 actual values. + /// + /// The type of elements in the list, which must implement ISeries. + /// The array of PropertyInfo objects representing the properties of the type. + /// The list of ISeries elements to analyze. + /// Format to be used in ToString() + private static string AutoFormat( + PropertyInfo property, + IReadOnlyList list) + where T : ISeries + { + Type propertyType = property.PropertyType; + + // auto-detect date format from precision + if (propertyType == typeof(DateOnly)) + { + return "yyyy-MM-dd"; + } + + else if (propertyType == typeof(DateTime) || propertyType == typeof(DateTimeOffset)) + { + List dateValues = list + .Take(1000) + .Select(item => ((DateTime)property.GetValue(item)!).ToString("o", culture)) + .ToList(); + + bool sameHour = dateValues.Select(d => d.Substring(11, 2)).Distinct().Count() == 1; + bool sameMinute = dateValues.Select(d => d.Substring(14, 2)).Distinct().Count() == 1; + bool sameSecond = dateValues.Select(d => d.Substring(17, 2)).Distinct().Count() == 1; + + return sameHour && sameMinute && sameSecond + ? "yyyy-MM-dd" + : sameSecond + ? "yyyy-MM-dd HH:mm" + : "yyyy-MM-dd HH:mm:ss"; + } + + // auto-detect decimal places + else if (propertyType == typeof(decimal)) + { + int decimalPlaces = list + .Take(1000) + .Select(item => ((decimal)property.GetValue(item)!).GetDecimalPlaces()) + .Max(); + + return $"N{decimalPlaces}"; + } + else + { + return string.Empty; + } + } + + /// + /// Returns the colloquial type name for a given type. + /// + /// The type to get the colloquial name for. + /// The colloquial type name. + public static string ColloquialTypeName(Type type) + { + if (type == null) + { + return string.Empty; + } + + // Handle nullable types + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + type = Nullable.GetUnderlyingType(type) ?? type; // Extract the underlying type + } + + // Return the type's C# alias if it exists, or the type's name otherwise + if (type.IsPrimitive || type == typeof(string) || type == typeof(decimal) || type == typeof(DateTime)) + { + // Return the type's C# alias if it exists, or the type's name otherwise + return type.Name; + } + else + { + // Return the type's C# alias if it exists, or the type's name otherwise + return type.Name; + } + } +} diff --git a/src/_common/Generics/StringOut.Type.cs b/src/_common/Generics/StringOut.Type.cs new file mode 100644 index 000000000..a56c5247d --- /dev/null +++ b/src/_common/Generics/StringOut.Type.cs @@ -0,0 +1,237 @@ +using System.Globalization; +using System.Reflection; +using System.Text; +using System.Xml.Linq; + +namespace Skender.Stock.Indicators; + +/// +/// Provides extension methods for converting ISeries instances to formatted strings. +/// +public static partial class StringOut +{ + private static readonly CultureInfo culture = CultureInfo.InvariantCulture; + + /// + /// Writes the string representation of an ISeries instance to the console. + /// + /// The type of the ISeries instance. + /// The ISeries instance to write to the console. + /// The string representation of the ISeries instance. + public static string ToConsole(this T obj) where T : ISeries + { + string? output = obj.ToStringOut(); + Console.WriteLine(output); + return output ?? string.Empty; + } + + /// + /// Converts an ISeries instance to a formatted string. + /// + /// The type of the ISeries instance. + /// The ISeries instance to convert. + /// A formatted string representation of the ISeries instance. + public static string ToStringOut(this T obj) where T : ISeries + { + ArgumentNullException.ThrowIfNull(obj); + StringBuilder sb = new(); + + // Header names + string[] headers = ["Property", "Type", "Value", "Description"]; + + // Get properties of the object + PropertyInfo[] properties = GetStringOutProperties(typeof(T)); + + // Lists to hold column data + List names = new(properties.Length); + List types = new(properties.Length); + List values = new(properties.Length); + List descriptions = new(properties.Length); + + // Get descriptions from XML documentation + Dictionary descriptionDict + = GetPropertyDescriptionsFromXml(typeof(T)); + + // Populate the lists + foreach (PropertyInfo prop in properties) + { + string name = prop.Name; + string type = prop.PropertyType.Name; + object? value = prop.GetValue(obj); + + // add values to lists + names.Add(name); + types.Add(type); + + switch (value) + { + case DateTime dateTimeValue: + values.Add(dateTimeValue.Kind == DateTimeKind.Utc + ? dateTimeValue.ToString("u", culture) + : dateTimeValue.ToString("s", culture)); + break; + + case DateOnly dateOnlyValue: + values.Add(dateOnlyValue.ToString("yyyy-MM-dd", culture)); + break; + + case DateTimeOffset dateTimeOffsetValue: + values.Add(dateTimeOffsetValue.ToString("o", culture)); + break; + + case string stringValue: + // limit string size + if (stringValue.Length > 35) + { + stringValue = string.Concat(stringValue.AsSpan(0, 32), "..."); + } + + values.Add(stringValue); + break; + + default: + values.Add(value?.ToString() ?? string.Empty); + break; + } + + // get/add description from XML documentation + descriptionDict.TryGetValue(name, out string? description); + + description = description == null + ? string.Empty + : description.Length > 50 + ? string.Concat(description.AsSpan(0, 47), "...") + : description; + + descriptions.Add(description); + } + + // Calculate the maximum width for each column + int widthOfName = MaxWidth(headers[0], names); + int widthOfType = MaxWidth(headers[1], types); + int widthOfValue = MaxWidth(headers[2], values); + int widthOfDesc = MaxWidth(headers[3], descriptions); + + // Ensure at least 2 spaces between columns + string format = $"{{0,-{widthOfName}}} {{1,-{widthOfType}}} {{2,{widthOfValue}}} {{3}}"; + + // Build the header + sb.AppendLine(string.Format(culture, format, headers[0], headers[1], headers[2], headers[3])); + + // Build the separator line + int totalWidth = widthOfName + widthOfType + widthOfValue + Math.Min(widthOfDesc, 30) + 6; // +6 for spaces + sb.AppendLine(new string('-', totalWidth)); + + // Build each row + for (int i = 0; i < names.Count; i++) + { + string row = string.Format(culture, format, names[i], types[i], values[i], descriptions[i]); + sb.AppendLine(row); + } + + return sb.ToString().TrimEnd(); + } + + /// + /// Calculates the maximum width of a column based on the header and values. + /// + /// The header of the column. + /// The list of values in the column. + /// The maximum width of the column. + private static int MaxWidth(string header, List values) + { + int maxValue = values.Count != 0 ? values.Max(v => v.Length) : 0; + return Math.Max(header.Length, maxValue); + } + + /// + /// Retrieves property descriptions from the XML documentation file. + /// + /// The type whose property descriptions are to be retrieved. + /// A dictionary containing property names and their descriptions. + private static Dictionary GetPropertyDescriptionsFromXml(Type type) + { + Dictionary descriptions = []; + + // Get the assembly of the type + Assembly assembly = type.Assembly; + string? assemblyLocation = assembly.Location; + + // Assume the XML documentation file is in the same directory as the assembly + string xmlFilePath = Path.ChangeExtension(assemblyLocation, ".xml"); + + if (!File.Exists(xmlFilePath)) + { + // XML documentation file not found + return descriptions; + } + + // Load the XML documentation file + XDocument xdoc = XDocument.Load(xmlFilePath); + + // Build the prefix for property members + string memberPrefix = "P:" + type.FullName + "."; + + // Query all member elements + foreach (XElement memberElement in xdoc.Descendants("member")) + { + string? nameAttribute = memberElement.Attribute("name")?.Value; + + if (nameAttribute != null && nameAttribute.StartsWith(memberPrefix, StringComparison.Ordinal)) + { + string propName = nameAttribute[memberPrefix.Length..]; + + // Get the summary element + XElement? summaryElement = memberElement.Element("summary"); + descriptions[propName] = summaryElement?.ParseXmlElement() ?? string.Empty; + } + } + + return descriptions; + } + + /// + /// Ensures that the text content of an XML documentation properly + /// converts HTML refs like and ."/> + /// + /// to be cleaned. + /// The cleaned text content of the XML documentation. + private static string ParseXmlElement(this XElement? summaryElement) + { + if (summaryElement == null) + { + return string.Empty; + } + + // Handle elements + foreach (XNode node in summaryElement.DescendantNodes().ToList()) + { + if (node is XElement element && element.Name.LocalName == "see") + { + foreach (XAttribute attribute in element.Attributes().ToList()) + { + string word = attribute.Value.Split('.').Last(); + element.ReplaceWith($"'{new XText(word)}'"); + } + } + } + + // Return summary text without line breaks + return summaryElement.Value + .Replace("\n", " ", StringComparison.Ordinal) + .Replace("\r", " ", StringComparison.Ordinal) + .Trim(); + } + + /// + /// Retrieves the public instance properties of a type that are not marked with + /// or . + /// + /// The type whose properties are to be retrieved. + /// An array of objects representing the properties of the type. + private static PropertyInfo[] GetStringOutProperties(Type type) + => type.GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(p => p.GetCustomAttribute() == null + && p.GetCustomAttribute() == null) + .ToArray(); +} diff --git a/src/_common/Math/Numerical.cs b/src/_common/Math/Numerical.cs index f55dcf473..bbcde9944 100644 --- a/src/_common/Math/Numerical.cs +++ b/src/_common/Math/Numerical.cs @@ -1,5 +1,6 @@ namespace Skender.Stock.Indicators; +#pragma warning disable IDE0066 // Convert switch statement to expression #pragma warning disable IDE0072 // Missing cases in switch statement /// @@ -153,4 +154,40 @@ internal static int GetDecimalPlaces(this decimal n) return decimalPlaces; } + + /// + /// Determines if a type is a numeric non-date type. + /// + /// The data + /// True if numeric type. + internal static bool IsNumeric(this Type type) + { + + if (type == typeof(DateTime) || + type == typeof(DateTimeOffset) || + type == typeof(DateOnly)) + { + return false; + } + + Type realType = Nullable.GetUnderlyingType(type) ?? type; + + switch (Type.GetTypeCode(realType)) + { + case TypeCode.Byte: + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + default: + return false; + } + } } diff --git a/src/_common/ObsoleteV3.cs b/src/_common/ObsoleteV3.cs index 93c7dd115..d94116f21 100644 --- a/src/_common/ObsoleteV3.cs +++ b/src/_common/ObsoleteV3.cs @@ -108,5 +108,7 @@ public interface IReusableResult : IReusable; public sealed class BasicData : IReusable { public DateTime Timestamp { get; set; } + + [JsonIgnore] public double Value { get; set; } } diff --git a/src/_common/Quotes/Quote.Models.cs b/src/_common/Quotes/Quote.Models.cs index 94b084aaf..ee5718a55 100644 --- a/src/_common/Quotes/Quote.Models.cs +++ b/src/_common/Quotes/Quote.Models.cs @@ -54,7 +54,7 @@ public interface IQuote : IReusable /// Built-in Quote type, representing an OHLCV aggregate price period. /// /// -/// Close date/time of the aggregate period +/// Close date/time of the aggregate /// /// /// Aggregate bar's first tick price @@ -84,6 +84,7 @@ decimal Volume ) : IQuote { /// + [JsonIgnore] public double Value => (double)Close; /// @@ -117,5 +118,6 @@ double Volume ) : IReusable { /// + [JsonIgnore] public double Value => Close; } diff --git a/src/_common/Reusable/IReusable.cs b/src/_common/Reusable/IReusable.cs index fdd04b2a7..4b36421e3 100644 --- a/src/_common/Reusable/IReusable.cs +++ b/src/_common/Reusable/IReusable.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Serialization; + namespace Skender.Stock.Indicators; /// @@ -9,5 +11,6 @@ public interface IReusable : ISeries /// /// Value that is passed to chained indicators. /// + [JsonIgnore] double Value { get; } } diff --git a/src/a-d/Adl/Adl.Models.cs b/src/a-d/Adl/Adl.Models.cs index c10375293..6f3a3b3f0 100644 --- a/src/a-d/Adl/Adl.Models.cs +++ b/src/a-d/Adl/Adl.Models.cs @@ -17,5 +17,6 @@ public record AdlResult ) : IReusable { /// + [JsonIgnore] public double Value => Adl; } diff --git a/src/a-d/Adx/Adx.Models.cs b/src/a-d/Adx/Adx.Models.cs index 9f65ac570..a29ca40dd 100644 --- a/src/a-d/Adx/Adx.Models.cs +++ b/src/a-d/Adx/Adx.Models.cs @@ -21,5 +21,6 @@ public record AdxResult ) : IReusable { /// + [JsonIgnore] public double Value => Adx.Null2NaN(); } diff --git a/src/a-d/Alma/Alma.Models.cs b/src/a-d/Alma/Alma.Models.cs index 29db95aec..611b72ea5 100644 --- a/src/a-d/Alma/Alma.Models.cs +++ b/src/a-d/Alma/Alma.Models.cs @@ -14,5 +14,6 @@ public record AlmaResult ) : IReusable { /// + [JsonIgnore] public double Value => Alma.Null2NaN(); } diff --git a/src/a-d/Aroon/Aroon.Models.cs b/src/a-d/Aroon/Aroon.Models.cs index fb80f80fb..a99fc9fbf 100644 --- a/src/a-d/Aroon/Aroon.Models.cs +++ b/src/a-d/Aroon/Aroon.Models.cs @@ -17,5 +17,6 @@ public record AroonResult ) : IReusable { /// + [JsonIgnore] public double Value => Oscillator.Null2NaN(); } diff --git a/src/a-d/Atr/Atr.Models.cs b/src/a-d/Atr/Atr.Models.cs index 7a0da44d0..e7281222f 100644 --- a/src/a-d/Atr/Atr.Models.cs +++ b/src/a-d/Atr/Atr.Models.cs @@ -17,5 +17,6 @@ public record AtrResult ) : IReusable { /// + [JsonIgnore] public double Value => Atrp.Null2NaN(); } diff --git a/src/a-d/Awesome/Awesome.Models.cs b/src/a-d/Awesome/Awesome.Models.cs index 2c9d37276..28dbb9ab6 100644 --- a/src/a-d/Awesome/Awesome.Models.cs +++ b/src/a-d/Awesome/Awesome.Models.cs @@ -15,5 +15,6 @@ public record AwesomeResult ) : IReusable { /// + [JsonIgnore] public double Value => Oscillator.Null2NaN(); } diff --git a/src/a-d/Beta/Beta.Models.cs b/src/a-d/Beta/Beta.Models.cs index 907d60a52..59f681ef1 100644 --- a/src/a-d/Beta/Beta.Models.cs +++ b/src/a-d/Beta/Beta.Models.cs @@ -24,6 +24,7 @@ public record BetaResult( ) : IReusable { /// + [JsonIgnore] public double Value => Beta.Null2NaN(); } diff --git a/src/a-d/BollingerBands/BollingerBands.Models.cs b/src/a-d/BollingerBands/BollingerBands.Models.cs index 63f93597a..4682f93cf 100644 --- a/src/a-d/BollingerBands/BollingerBands.Models.cs +++ b/src/a-d/BollingerBands/BollingerBands.Models.cs @@ -23,5 +23,6 @@ public record BollingerBandsResult ) : IReusable { /// + [JsonIgnore] public double Value => PercentB.Null2NaN(); } diff --git a/src/a-d/Bop/Bop.Models.cs b/src/a-d/Bop/Bop.Models.cs index e6eada31b..488fcdcc1 100644 --- a/src/a-d/Bop/Bop.Models.cs +++ b/src/a-d/Bop/Bop.Models.cs @@ -13,5 +13,6 @@ public record BopResult ) : IReusable { /// + [JsonIgnore] public double Value => Bop.Null2NaN(); } diff --git a/src/a-d/Cci/Cci.Models.cs b/src/a-d/Cci/Cci.Models.cs index a29070b1b..2ede77358 100644 --- a/src/a-d/Cci/Cci.Models.cs +++ b/src/a-d/Cci/Cci.Models.cs @@ -13,5 +13,6 @@ public record CciResult ) : IReusable { /// + [JsonIgnore] public double Value => Cci.Null2NaN(); } diff --git a/src/a-d/ChaikinOsc/ChaikinOsc.Models.cs b/src/a-d/ChaikinOsc/ChaikinOsc.Models.cs index 31e593dd7..a382addfa 100644 --- a/src/a-d/ChaikinOsc/ChaikinOsc.Models.cs +++ b/src/a-d/ChaikinOsc/ChaikinOsc.Models.cs @@ -19,5 +19,6 @@ public record ChaikinOscResult ) : IReusable { /// + [JsonIgnore] public double Value => Oscillator.Null2NaN(); } diff --git a/src/a-d/Chandelier/Chandelier.Models.cs b/src/a-d/Chandelier/Chandelier.Models.cs index ce2ae1688..2e13972ca 100644 --- a/src/a-d/Chandelier/Chandelier.Models.cs +++ b/src/a-d/Chandelier/Chandelier.Models.cs @@ -13,6 +13,7 @@ public record ChandelierResult ) : IReusable { /// + [JsonIgnore] public double Value => ChandelierExit.Null2NaN(); } diff --git a/src/a-d/Chop/Chop.Models.cs b/src/a-d/Chop/Chop.Models.cs index 4884ce187..82e44194d 100644 --- a/src/a-d/Chop/Chop.Models.cs +++ b/src/a-d/Chop/Chop.Models.cs @@ -13,5 +13,6 @@ public record ChopResult ) : IReusable { /// + [JsonIgnore] public double Value => Chop.Null2NaN(); } diff --git a/src/a-d/Cmf/Cmf.Models.cs b/src/a-d/Cmf/Cmf.Models.cs index 152044d23..c903b7158 100644 --- a/src/a-d/Cmf/Cmf.Models.cs +++ b/src/a-d/Cmf/Cmf.Models.cs @@ -17,5 +17,6 @@ public record CmfResult ) : IReusable { /// + [JsonIgnore] public double Value => Cmf.Null2NaN(); } diff --git a/src/a-d/Cmo/Cmo.Models.cs b/src/a-d/Cmo/Cmo.Models.cs index 10cdd33bd..f768d600e 100644 --- a/src/a-d/Cmo/Cmo.Models.cs +++ b/src/a-d/Cmo/Cmo.Models.cs @@ -13,5 +13,6 @@ public record CmoResult ) : IReusable { /// + [JsonIgnore] public double Value => Cmo.Null2NaN(); } diff --git a/src/a-d/ConnorsRsi/ConnorsRsi.Models.cs b/src/a-d/ConnorsRsi/ConnorsRsi.Models.cs index 6a4915acb..8f79aa69a 100644 --- a/src/a-d/ConnorsRsi/ConnorsRsi.Models.cs +++ b/src/a-d/ConnorsRsi/ConnorsRsi.Models.cs @@ -21,5 +21,6 @@ public record ConnorsRsiResult ) : IReusable { /// + [JsonIgnore] public double Value => ConnorsRsi.Null2NaN(); } diff --git a/src/a-d/Correlation/Correlation.Models.cs b/src/a-d/Correlation/Correlation.Models.cs index 3b9612bb8..2b292cd1f 100644 --- a/src/a-d/Correlation/Correlation.Models.cs +++ b/src/a-d/Correlation/Correlation.Models.cs @@ -21,5 +21,6 @@ public record CorrResult ) : IReusable { /// + [JsonIgnore] public double Value => Correlation.Null2NaN(); } diff --git a/src/a-d/Dema/Dema.Models.cs b/src/a-d/Dema/Dema.Models.cs index 84a85236a..a6cef1200 100644 --- a/src/a-d/Dema/Dema.Models.cs +++ b/src/a-d/Dema/Dema.Models.cs @@ -13,5 +13,6 @@ public record DemaResult ) : IReusable { /// + [JsonIgnore] public double Value => Dema.Null2NaN(); } diff --git a/src/a-d/Dpo/Dpo.Models.cs b/src/a-d/Dpo/Dpo.Models.cs index ee68569aa..f32890f49 100644 --- a/src/a-d/Dpo/Dpo.Models.cs +++ b/src/a-d/Dpo/Dpo.Models.cs @@ -15,5 +15,6 @@ public record DpoResult ) : IReusable { /// + [JsonIgnore] public double Value => Dpo.Null2NaN(); } diff --git a/src/a-d/Dynamic/Dynamic.Models.cs b/src/a-d/Dynamic/Dynamic.Models.cs index 62ad74196..1419316fd 100644 --- a/src/a-d/Dynamic/Dynamic.Models.cs +++ b/src/a-d/Dynamic/Dynamic.Models.cs @@ -13,5 +13,6 @@ public record DynamicResult ) : IReusable { /// + [JsonIgnore] public double Value => Dynamic.Null2NaN(); } diff --git a/src/e-k/ElderRay/ElderRay.Models.cs b/src/e-k/ElderRay/ElderRay.Models.cs index 6c1cf774f..e3a939e20 100644 --- a/src/e-k/ElderRay/ElderRay.Models.cs +++ b/src/e-k/ElderRay/ElderRay.Models.cs @@ -17,5 +17,6 @@ public record ElderRayResult ) : IReusable { /// + [JsonIgnore] public double Value => (BullPower + BearPower).Null2NaN(); } diff --git a/src/e-k/Ema/Ema.Models.cs b/src/e-k/Ema/Ema.Models.cs index 96f2f4029..288fcc772 100644 --- a/src/e-k/Ema/Ema.Models.cs +++ b/src/e-k/Ema/Ema.Models.cs @@ -13,5 +13,6 @@ public record EmaResult ) : IReusable { /// + [JsonIgnore] public double Value => Ema.Null2NaN(); } diff --git a/src/e-k/Epma/Epma.Models.cs b/src/e-k/Epma/Epma.Models.cs index b344aaeba..2ad873f6e 100644 --- a/src/e-k/Epma/Epma.Models.cs +++ b/src/e-k/Epma/Epma.Models.cs @@ -13,5 +13,6 @@ public record EpmaResult ) : IReusable { /// + [JsonIgnore] public double Value => Epma.Null2NaN(); } diff --git a/src/e-k/FisherTransform/FisherTransform.Models.cs b/src/e-k/FisherTransform/FisherTransform.Models.cs index 406506ebc..159833872 100644 --- a/src/e-k/FisherTransform/FisherTransform.Models.cs +++ b/src/e-k/FisherTransform/FisherTransform.Models.cs @@ -15,5 +15,6 @@ public record FisherTransformResult ) : IReusable { /// + [JsonIgnore] public double Value => Fisher.Null2NaN(); } diff --git a/src/e-k/ForceIndex/ForceIndex.Models.cs b/src/e-k/ForceIndex/ForceIndex.Models.cs index b26364cca..4ce3bd9fc 100644 --- a/src/e-k/ForceIndex/ForceIndex.Models.cs +++ b/src/e-k/ForceIndex/ForceIndex.Models.cs @@ -13,5 +13,6 @@ public record ForceIndexResult ) : IReusable { /// + [JsonIgnore] public double Value => ForceIndex.Null2NaN(); } diff --git a/src/e-k/Hma/Hma.Models.cs b/src/e-k/Hma/Hma.Models.cs index 700672d3c..b57486380 100644 --- a/src/e-k/Hma/Hma.Models.cs +++ b/src/e-k/Hma/Hma.Models.cs @@ -13,5 +13,6 @@ public record HmaResult ) : IReusable { /// + [JsonIgnore] public double Value => Hma.Null2NaN(); } diff --git a/src/e-k/HtTrendline/HtTrendline.Models.cs b/src/e-k/HtTrendline/HtTrendline.Models.cs index 0da6f2bc6..91a72888e 100644 --- a/src/e-k/HtTrendline/HtTrendline.Models.cs +++ b/src/e-k/HtTrendline/HtTrendline.Models.cs @@ -17,5 +17,6 @@ public record HtlResult ) : IReusable { /// + [JsonIgnore] public double Value => Trendline.Null2NaN(); } diff --git a/src/e-k/Hurst/Hurst.Models.cs b/src/e-k/Hurst/Hurst.Models.cs index d74b766f8..634340b4f 100644 --- a/src/e-k/Hurst/Hurst.Models.cs +++ b/src/e-k/Hurst/Hurst.Models.cs @@ -13,5 +13,6 @@ public record HurstResult ) : IReusable { /// + [JsonIgnore] public double Value => HurstExponent.Null2NaN(); } diff --git a/src/e-k/Kama/Kama.Models.cs b/src/e-k/Kama/Kama.Models.cs index ffba9b5f7..f9f1fbfe4 100644 --- a/src/e-k/Kama/Kama.Models.cs +++ b/src/e-k/Kama/Kama.Models.cs @@ -15,5 +15,6 @@ public record KamaResult ) : IReusable { /// + [JsonIgnore] public double Value => Kama.Null2NaN(); } diff --git a/src/e-k/Kvo/Kvo.Models.cs b/src/e-k/Kvo/Kvo.Models.cs index d4556f6f1..939252fa6 100644 --- a/src/e-k/Kvo/Kvo.Models.cs +++ b/src/e-k/Kvo/Kvo.Models.cs @@ -15,5 +15,6 @@ public record KvoResult ) : IReusable { /// + [JsonIgnore] public double Value => Oscillator.Null2NaN(); } diff --git a/src/m-r/Macd/Macd.Models.cs b/src/m-r/Macd/Macd.Models.cs index e0cf88d8d..860942479 100644 --- a/src/m-r/Macd/Macd.Models.cs +++ b/src/m-r/Macd/Macd.Models.cs @@ -24,5 +24,6 @@ public record MacdResult ) : IReusable { /// + [JsonIgnore] public double Value => Macd.Null2NaN(); } diff --git a/src/m-r/Mama/Mama.Models.cs b/src/m-r/Mama/Mama.Models.cs index 80a26e85f..53fe55d90 100644 --- a/src/m-r/Mama/Mama.Models.cs +++ b/src/m-r/Mama/Mama.Models.cs @@ -15,5 +15,6 @@ public record MamaResult ) : IReusable { /// + [JsonIgnore] public double Value => Mama.Null2NaN(); } diff --git a/src/m-r/Mfi/Mfi.Models.cs b/src/m-r/Mfi/Mfi.Models.cs index 6091a0850..18fe070a2 100644 --- a/src/m-r/Mfi/Mfi.Models.cs +++ b/src/m-r/Mfi/Mfi.Models.cs @@ -13,5 +13,6 @@ public record MfiResult ) : IReusable { /// + [JsonIgnore] public double Value => Mfi.Null2NaN(); } diff --git a/src/m-r/Obv/Obv.Models.cs b/src/m-r/Obv/Obv.Models.cs index 337574945..fa6c4c984 100644 --- a/src/m-r/Obv/Obv.Models.cs +++ b/src/m-r/Obv/Obv.Models.cs @@ -13,5 +13,6 @@ double Obv ) : IReusable { /// + [JsonIgnore] public double Value => Obv; } diff --git a/src/m-r/ParabolicSar/ParabolicSar.Models.cs b/src/m-r/ParabolicSar/ParabolicSar.Models.cs index 63407cdae..a330d8102 100644 --- a/src/m-r/ParabolicSar/ParabolicSar.Models.cs +++ b/src/m-r/ParabolicSar/ParabolicSar.Models.cs @@ -15,5 +15,6 @@ public record ParabolicSarResult ) : IReusable { /// + [JsonIgnore] public double Value => Sar.Null2NaN(); } diff --git a/src/m-r/Pmo/Pmo.Models.cs b/src/m-r/Pmo/Pmo.Models.cs index e5f248011..e93eb03d4 100644 --- a/src/m-r/Pmo/Pmo.Models.cs +++ b/src/m-r/Pmo/Pmo.Models.cs @@ -15,5 +15,6 @@ public record PmoResult ) : IReusable { /// + [JsonIgnore] public double Value => Pmo.Null2NaN(); } diff --git a/src/m-r/Prs/Prs.Models.cs b/src/m-r/Prs/Prs.Models.cs index a6aaa1fcf..fd7af538c 100644 --- a/src/m-r/Prs/Prs.Models.cs +++ b/src/m-r/Prs/Prs.Models.cs @@ -15,5 +15,6 @@ public record PrsResult ) : IReusable { /// + [JsonIgnore] public double Value => Prs.Null2NaN(); } diff --git a/src/m-r/Pvo/Pvo.Models.cs b/src/m-r/Pvo/Pvo.Models.cs index ed3bf7f3e..230bc9fcc 100644 --- a/src/m-r/Pvo/Pvo.Models.cs +++ b/src/m-r/Pvo/Pvo.Models.cs @@ -17,5 +17,6 @@ public record PvoResult ) : IReusable { /// + [JsonIgnore] public double Value => Pvo.Null2NaN(); } diff --git a/src/m-r/Roc/Roc.Models.cs b/src/m-r/Roc/Roc.Models.cs index ed913bc2b..882427dfb 100644 --- a/src/m-r/Roc/Roc.Models.cs +++ b/src/m-r/Roc/Roc.Models.cs @@ -15,5 +15,6 @@ public record RocResult ) : IReusable { /// + [JsonIgnore] public double Value => Roc.Null2NaN(); } diff --git a/src/m-r/RocWb/RocWb.Models.cs b/src/m-r/RocWb/RocWb.Models.cs index 6b61b169d..318aaae9b 100644 --- a/src/m-r/RocWb/RocWb.Models.cs +++ b/src/m-r/RocWb/RocWb.Models.cs @@ -19,5 +19,6 @@ public record RocWbResult ) : IReusable { /// + [JsonIgnore] public double Value => Roc.Null2NaN(); } diff --git a/src/m-r/Rsi/Rsi.Models.cs b/src/m-r/Rsi/Rsi.Models.cs index c3147245c..a9e3a5c9b 100644 --- a/src/m-r/Rsi/Rsi.Models.cs +++ b/src/m-r/Rsi/Rsi.Models.cs @@ -13,5 +13,6 @@ public record RsiResult ) : IReusable { /// + [JsonIgnore] public double Value => Rsi.Null2NaN(); } diff --git a/src/s-z/Slope/Slope.Models.cs b/src/s-z/Slope/Slope.Models.cs index 6f220837b..54b93da9e 100644 --- a/src/s-z/Slope/Slope.Models.cs +++ b/src/s-z/Slope/Slope.Models.cs @@ -21,5 +21,6 @@ public record SlopeResult ) : IReusable { /// + [JsonIgnore] public double Value => Slope.Null2NaN(); } diff --git a/src/s-z/Sma/Sma.Models.cs b/src/s-z/Sma/Sma.Models.cs index 43ecc2306..f760eb364 100644 --- a/src/s-z/Sma/Sma.Models.cs +++ b/src/s-z/Sma/Sma.Models.cs @@ -12,5 +12,6 @@ public record SmaResult( ) : IReusable { /// + [JsonIgnore] public double Value => Sma.Null2NaN(); } diff --git a/src/s-z/Smi/Smi.Models.cs b/src/s-z/Smi/Smi.Models.cs index d39772499..9ad9b6788 100644 --- a/src/s-z/Smi/Smi.Models.cs +++ b/src/s-z/Smi/Smi.Models.cs @@ -15,5 +15,6 @@ public record SmiResult ) : IReusable { /// + [JsonIgnore] public double Value => Smi.Null2NaN(); } diff --git a/src/s-z/Smma/Smma.Models.cs b/src/s-z/Smma/Smma.Models.cs index 1e6e558a5..16a7a68c5 100644 --- a/src/s-z/Smma/Smma.Models.cs +++ b/src/s-z/Smma/Smma.Models.cs @@ -13,5 +13,6 @@ public record SmmaResult ) : IReusable { /// + [JsonIgnore] public double Value => Smma.Null2NaN(); } diff --git a/src/s-z/Stc/Stc.Models.cs b/src/s-z/Stc/Stc.Models.cs index a330e29e2..75efc7b62 100644 --- a/src/s-z/Stc/Stc.Models.cs +++ b/src/s-z/Stc/Stc.Models.cs @@ -13,5 +13,6 @@ public record StcResult ) : IReusable { /// + [JsonIgnore] public double Value => Stc.Null2NaN(); } diff --git a/src/s-z/StdDev/StdDev.Models.cs b/src/s-z/StdDev/StdDev.Models.cs index 0354b1e41..450e8fedd 100644 --- a/src/s-z/StdDev/StdDev.Models.cs +++ b/src/s-z/StdDev/StdDev.Models.cs @@ -17,5 +17,6 @@ public record StdDevResult ) : IReusable { /// + [JsonIgnore] public double Value => StdDev.Null2NaN(); } diff --git a/src/s-z/Stoch/Stoch.Models.cs b/src/s-z/Stoch/Stoch.Models.cs index d3578a20b..a08bcc317 100644 --- a/src/s-z/Stoch/Stoch.Models.cs +++ b/src/s-z/Stoch/Stoch.Models.cs @@ -17,6 +17,7 @@ public record StochResult ) : IReusable { /// + [JsonIgnore] public double Value => Oscillator.Null2NaN(); // aliases diff --git a/src/s-z/StochRsi/StochRsi.Models.cs b/src/s-z/StochRsi/StochRsi.Models.cs index 56b14e965..7c86bfeda 100644 --- a/src/s-z/StochRsi/StochRsi.Models.cs +++ b/src/s-z/StochRsi/StochRsi.Models.cs @@ -15,5 +15,6 @@ public record StochRsiResult ) : IReusable { /// + [JsonIgnore] public double Value => StochRsi.Null2NaN(); } diff --git a/src/s-z/T3/T3.Models.cs b/src/s-z/T3/T3.Models.cs index 7859b91a9..3e4b13b86 100644 --- a/src/s-z/T3/T3.Models.cs +++ b/src/s-z/T3/T3.Models.cs @@ -13,5 +13,6 @@ public record T3Result ) : IReusable { /// + [JsonIgnore] public double Value => T3.Null2NaN(); } diff --git a/src/s-z/Tema/Tema.Models.cs b/src/s-z/Tema/Tema.Models.cs index d51821acb..348bcc3cf 100644 --- a/src/s-z/Tema/Tema.Models.cs +++ b/src/s-z/Tema/Tema.Models.cs @@ -13,5 +13,6 @@ public record TemaResult ) : IReusable { /// + [JsonIgnore] public double Value => Tema.Null2NaN(); } diff --git a/src/s-z/Tr/Tr.Models.cs b/src/s-z/Tr/Tr.Models.cs index 849450e72..41fd02c15 100644 --- a/src/s-z/Tr/Tr.Models.cs +++ b/src/s-z/Tr/Tr.Models.cs @@ -12,5 +12,6 @@ public record TrResult( ) : IReusable { /// + [JsonIgnore] public double Value => Tr.Null2NaN(); } diff --git a/src/s-z/Trix/Trix.Models.cs b/src/s-z/Trix/Trix.Models.cs index 1fae80466..0558baef6 100644 --- a/src/s-z/Trix/Trix.Models.cs +++ b/src/s-z/Trix/Trix.Models.cs @@ -15,5 +15,6 @@ public record TrixResult ) : IReusable { /// + [JsonIgnore] public double Value => Trix.Null2NaN(); } diff --git a/src/s-z/Tsi/Tsi.Models.cs b/src/s-z/Tsi/Tsi.Models.cs index be8e360b6..5b3600371 100644 --- a/src/s-z/Tsi/Tsi.Models.cs +++ b/src/s-z/Tsi/Tsi.Models.cs @@ -15,5 +15,6 @@ public record TsiResult ) : IReusable { /// + [JsonIgnore] public double Value => Tsi.Null2NaN(); } diff --git a/src/s-z/UlcerIndex/UlcerIndex.Models.cs b/src/s-z/UlcerIndex/UlcerIndex.Models.cs index 9f117918a..d4df2d384 100644 --- a/src/s-z/UlcerIndex/UlcerIndex.Models.cs +++ b/src/s-z/UlcerIndex/UlcerIndex.Models.cs @@ -13,6 +13,7 @@ public record UlcerIndexResult ) : IReusable { /// + [JsonIgnore] public double Value => UlcerIndex.Null2NaN(); /// diff --git a/src/s-z/Ultimate/Ultimate.Models.cs b/src/s-z/Ultimate/Ultimate.Models.cs index eedfc23bb..30d3dc340 100644 --- a/src/s-z/Ultimate/Ultimate.Models.cs +++ b/src/s-z/Ultimate/Ultimate.Models.cs @@ -13,5 +13,6 @@ public record UltimateResult ) : IReusable { /// + [JsonIgnore] public double Value => Ultimate.Null2NaN(); } diff --git a/src/s-z/VolatilityStop/VolatilityStop.Models.cs b/src/s-z/VolatilityStop/VolatilityStop.Models.cs index 36128a4d5..d1efc95e3 100644 --- a/src/s-z/VolatilityStop/VolatilityStop.Models.cs +++ b/src/s-z/VolatilityStop/VolatilityStop.Models.cs @@ -22,5 +22,6 @@ public record VolatilityStopResult ) : IReusable { /// + [JsonIgnore] public double Value => Sar.Null2NaN(); } diff --git a/src/s-z/Vwap/Vwap.Models.cs b/src/s-z/Vwap/Vwap.Models.cs index 4f304aa68..9db2e5bf3 100644 --- a/src/s-z/Vwap/Vwap.Models.cs +++ b/src/s-z/Vwap/Vwap.Models.cs @@ -13,5 +13,6 @@ public record VwapResult ) : IReusable { /// + [JsonIgnore] public double Value => Vwap.Null2NaN(); } diff --git a/src/s-z/Vwma/Vwma.Models.cs b/src/s-z/Vwma/Vwma.Models.cs index cde0a5cfc..71eac28da 100644 --- a/src/s-z/Vwma/Vwma.Models.cs +++ b/src/s-z/Vwma/Vwma.Models.cs @@ -13,5 +13,6 @@ public record VwmaResult ) : IReusable { /// + [JsonIgnore] public double Value => Vwma.Null2NaN(); } diff --git a/src/s-z/WilliamsR/WilliamsR.Models.cs b/src/s-z/WilliamsR/WilliamsR.Models.cs index 5c3c85a4e..e28f0c02a 100644 --- a/src/s-z/WilliamsR/WilliamsR.Models.cs +++ b/src/s-z/WilliamsR/WilliamsR.Models.cs @@ -13,5 +13,6 @@ public record WilliamsResult ) : IReusable { /// + [JsonIgnore] public double Value => WilliamsR.Null2NaN(); } diff --git a/src/s-z/Wma/Wma.Models.cs b/src/s-z/Wma/Wma.Models.cs index a5033851b..0082997bc 100644 --- a/src/s-z/Wma/Wma.Models.cs +++ b/src/s-z/Wma/Wma.Models.cs @@ -13,5 +13,6 @@ public record WmaResult ) : IReusable { /// + [JsonIgnore] public double Value => Wma.Null2NaN(); } diff --git a/src/s-z/ZigZag/ZigZag.Models.cs b/src/s-z/ZigZag/ZigZag.Models.cs index 8eee805b5..1a1b348ce 100644 --- a/src/s-z/ZigZag/ZigZag.Models.cs +++ b/src/s-z/ZigZag/ZigZag.Models.cs @@ -19,6 +19,7 @@ public record ZigZagResult ) : IReusable { /// + [JsonIgnore] public double Value => ZigZag.Null2NaN(); } diff --git a/tests/indicators/TestBase.cs b/tests/indicators/TestBase.cs index bb0d9c501..c2f573fcd 100644 --- a/tests/indicators/TestBase.cs +++ b/tests/indicators/TestBase.cs @@ -15,6 +15,7 @@ namespace Test.Data; internal static readonly CultureInfo invariantCulture = CultureInfo.InvariantCulture; internal static readonly IReadOnlyList Quotes = Data.GetDefault(); + internal static readonly IReadOnlyList Intraday = Data.GetIntraday(); internal static readonly IReadOnlyList OtherQuotes = Data.GetCompare(); internal static readonly IReadOnlyList BadQuotes = Data.GetBad(); internal static readonly IReadOnlyList BigQuotes = Data.GetTooBig(); diff --git a/tests/indicators/Tests.Indicators.csproj b/tests/indicators/Tests.Indicators.csproj index d7dbc7271..e0cd73900 100644 --- a/tests/indicators/Tests.Indicators.csproj +++ b/tests/indicators/Tests.Indicators.csproj @@ -8,7 +8,9 @@ disable true - + true + $(NoWarn);NU1507;CS1591 + true latest AllEnabledByDefault diff --git a/tests/indicators/_common/Generics/StringOut.Tests.cs b/tests/indicators/_common/Generics/StringOut.Tests.cs new file mode 100644 index 000000000..61364cd19 --- /dev/null +++ b/tests/indicators/_common/Generics/StringOut.Tests.cs @@ -0,0 +1,430 @@ +using System.Globalization; +using Test.Utilities; + +namespace Tests.Common; + +[TestClass] +public class StringOutputs : TestBase +{ + [TestMethod] + public void ToConsoleQuoteType() + { + DateTime timestamp = DateTime.TryParse( + "2017-02-03", CultureInfo.InvariantCulture, out DateTime d) ? d : default; + + Quote quote = new(timestamp, 216.1579m, 216.875m, 215.84m, 216.67m, 98765432832); + + string sut = quote.ToConsole(); + string val = quote.ToStringOut(); + + sut.Should().Be(val); + } + + [TestMethod] + public void ToConsoleQuoteList() + { + string sut = Quotes.ToConsole(); + string val = Quotes.ToStringOut(); + int length = sut.Split(Environment.NewLine).Length; + + sut.Should().Be(val); + length.Should().Be(505); // 2 headers + 502 data rows + 1 eof line + } + + [TestMethod] + public void ToStringOutQuoteType() + { + DateTime timestamp = DateTime.TryParse( + "2017-02-03", CultureInfo.InvariantCulture, out DateTime d) ? d : default; + + Quote quote = new(timestamp, 216.1m, 216.875m, 215.84m, 216.67m, 85273832); + + string sut = StringOut.ToStringOut(quote); + Console.WriteLine(sut); + + // note description has max of 30 "-" characters + string expected = """ + Property Type Value Description + ------------------------------------------------------------------------ + Timestamp DateTime 2017-02-03T00:00:00 Close date/time of the aggregate + Open Decimal 216.1 Aggregate bar's first tick price + High Decimal 216.875 Aggregate bar's highest tick price + Low Decimal 215.84 Aggregate bar's lowest tick price + Close Decimal 216.67 Aggregate bar's last tick price + Volume Decimal 85273832 Aggregate bar's tick volume + """.WithDefaultLineEndings(); + + sut.Should().Be(expected); + } + + [TestMethod] + public void ToStringOutMostTypes() + { + AllTypes allTypes = new(); + string sut = StringOut.ToStringOut(allTypes); + Console.WriteLine(sut); + + string expected = """ + Property Type Value Description + ----------------------------------------------------------------------------------------------------------- + Timestamp DateTime 2023-01-01 14:30:00Z A 'DateTime' type with time (UTC) + DateTimeProperty DateTime 2023-01-01T09:30:00 A 'DateTime' type with time + DateProperty DateOnly 2023-01-01 A 'DateOnly' type without time. + DateTimeOffsetProperty DateTimeOffset 2023-01-01T09:30:00.0000000-05:00 A 'DateTimeOffset' type with time and offset. + TimeSpanProperty TimeSpan 01:02:03 A 'TimeSpan' type + ByteProperty Byte 255 A 'Byte' type + ShortProperty Int16 32767 A 'Int16' short integer type + IntProperty Int32 -2147483648 A 'Int32' integer type + LongProperty Int64 9223372036854775803 'get' the 'Int64' long integer type + FloatProperty Single -125.25143 A get of 'Single' floating point type + DoubleProperty Double 5.251426433759354 A 'Double' floating point type + DecimalProperty Decimal 7922815.2514264337593543950335 A 'Decimal' type + CharProperty Char A A 'Char' type + BoolProperty Boolean True A 'Boolean' type + NoXmlProperty Boolean False + StringProperty String The lazy dog jumped over the sly... A 'String' type + """.WithDefaultLineEndings(); + + sut.Should().Be(expected); + } + + [TestMethod] + public void ToFixedWidthQuoteStandard() + { + /* based on what we know about the test data precision */ + + string output = Quotes.ToStringOut(limitQty: 12); + Console.WriteLine(output); + + string expected = """ + i Timestamp Open High Low Close Volume + ---------------------------------------------------------- + 0 2017-01-03 212.61 213.35 211.52 212.80 96,708,880 + 1 2017-01-04 213.16 214.22 213.15 214.06 83,348,752 + 2 2017-01-05 213.77 214.06 213.02 213.89 82,961,968 + 3 2017-01-06 214.02 215.17 213.42 214.66 75,744,152 + 4 2017-01-09 214.38 214.53 213.91 213.95 49,684,316 + 5 2017-01-10 213.97 214.89 213.52 213.95 67,500,792 + 6 2017-01-11 213.86 214.55 213.13 214.55 79,014,928 + 7 2017-01-12 213.99 214.22 212.53 214.02 76,329,760 + 8 2017-01-13 214.21 214.84 214.17 214.51 66,385,084 + 9 2017-01-17 213.81 214.25 213.33 213.75 64,821,664 + 10 2017-01-18 214.02 214.27 213.42 214.22 57,997,156 + 11 2017-01-19 214.31 214.46 212.96 213.43 70,503,512 + + """.WithDefaultLineEndings(); + + output.Should().Be(expected); + } + + [TestMethod] + public void ToFixedWidthQuoteWithArgs() + { + Dictionary args = new() + { + { "decimal", "N4" }, + { "Close", "N3" }, + { "Volume", "N0" } + }; + + string output = Quotes.Take(12).ToStringOut(args); + Console.WriteLine(output); + + string expected = """ + i Timestamp Open High Low Close Volume + ----------------------------------------------------------------- + 0 2017-01-03 212.6100 213.3500 211.5200 212.800 96,708,880 + 1 2017-01-04 213.1600 214.2200 213.1500 214.060 83,348,752 + 2 2017-01-05 213.7700 214.0600 213.0200 213.890 82,961,968 + 3 2017-01-06 214.0200 215.1700 213.4200 214.660 75,744,152 + 4 2017-01-09 214.3800 214.5300 213.9100 213.950 49,684,316 + 5 2017-01-10 213.9700 214.8900 213.5200 213.950 67,500,792 + 6 2017-01-11 213.8600 214.5500 213.1300 214.550 79,014,928 + 7 2017-01-12 213.9900 214.2200 212.5300 214.020 76,329,760 + 8 2017-01-13 214.2100 214.8400 214.1700 214.510 66,385,084 + 9 2017-01-17 213.8100 214.2500 213.3300 213.750 64,821,664 + 10 2017-01-18 214.0200 214.2700 213.4200 214.220 57,997,156 + 11 2017-01-19 214.3100 214.4600 212.9600 213.430 70,503,512 + + """.WithDefaultLineEndings(); + + output.Should().Be(expected); + } + + [TestMethod] + public void ToFixedWidthQuoteIntraday() + { + string output = Intraday.ToStringOut(limitQty: 12); + Console.WriteLine(output); + + string expected = """ + i Timestamp Open High Low Close Volume + ---------------------------------------------------------------- + 0 2020-12-15 09:30 367.400 367.620 367.360 367.46 407,870 + 1 2020-12-15 09:31 367.480 367.480 367.190 367.19 173,406 + 2 2020-12-15 09:32 367.190 367.400 367.020 367.35 149,240 + 3 2020-12-15 09:33 367.345 367.640 367.345 367.59 197,941 + 4 2020-12-15 09:34 367.590 367.610 367.320 367.43 147,919 + 5 2020-12-15 09:35 367.430 367.650 367.260 367.34 170,552 + 6 2020-12-15 09:36 367.350 367.560 367.150 367.53 200,528 + 7 2020-12-15 09:37 367.535 367.720 367.340 367.47 117,417 + 8 2020-12-15 09:38 367.480 367.480 367.190 367.42 127,936 + 9 2020-12-15 09:39 367.440 367.600 367.300 367.57 150,339 + 10 2020-12-15 09:40 367.580 367.775 367.560 367.61 136,414 + 11 2020-12-15 09:41 367.610 367.640 367.450 367.60 98,185 + + """.WithDefaultLineEndings(); + + output.Should().Be(expected); + } + + [TestMethod] + public void ToFixedWidthQuoteMinutes() + { + List quotes = []; + for (int i = 0; i < 20; i++) + { + DateTime timestamp = new DateTime(2023, 1, 1, 9, 30, 0).AddMinutes(i); + quotes.Add(new Quote(timestamp, 100 + i, 105 + i, 95 + i, 102 + i, 1000 + i)); + } + + string expected = """ + i Timestamp Open High Low Close Volume + ---------------------------------------------------- + 0 2023-01-01 09:30 100 105 95 102 1,000 + 1 2023-01-01 09:31 101 106 96 103 1,001 + 2 2023-01-01 09:32 102 107 97 104 1,002 + 3 2023-01-01 09:33 103 108 98 105 1,003 + 4 2023-01-01 09:34 104 109 99 106 1,004 + 5 2023-01-01 09:35 105 110 100 107 1,005 + 6 2023-01-01 09:36 106 111 101 108 1,006 + 7 2023-01-01 09:37 107 112 102 109 1,007 + 8 2023-01-01 09:38 108 113 103 110 1,008 + 9 2023-01-01 09:39 109 114 104 111 1,009 + 10 2023-01-01 09:40 110 115 105 112 1,010 + 11 2023-01-01 09:41 111 116 106 113 1,011 + 12 2023-01-01 09:42 112 117 107 114 1,012 + 13 2023-01-01 09:43 113 118 108 115 1,013 + 14 2023-01-01 09:44 114 119 109 116 1,014 + 15 2023-01-01 09:45 115 120 110 117 1,015 + 16 2023-01-01 09:46 116 121 111 118 1,016 + 17 2023-01-01 09:47 117 122 112 119 1,017 + 18 2023-01-01 09:48 118 123 113 120 1,018 + 19 2023-01-01 09:49 119 124 114 121 1,019 + + """.WithDefaultLineEndings(); + + string output = quotes.ToStringOut(); + Console.WriteLine(output); + + string[] lines = output.Split(Environment.NewLine); + lines.Length.Should().Be(23); // 2 headers + 20 data rows + 1 eof line + + output.Should().Be(expected); + } + + [TestMethod] + public void ToFixedWidthQuoteSeconds() + { + List quotes = []; + for (int i = 0; i < 20; i++) + { + DateTime timestamp = new DateTime(2023, 1, 1, 9, 30, 0).AddSeconds(i); + quotes.Add(new Quote(timestamp, 100 + i, 105 + i, 95 + i, 102 + i, 1000 + i)); + } + + string expected = """ + i Timestamp Open High Low Close Volume + ------------------------------------------------------- + 0 2023-01-01 09:30:00 100 105 95 102 1,000 + 1 2023-01-01 09:30:01 101 106 96 103 1,001 + 2 2023-01-01 09:30:02 102 107 97 104 1,002 + 3 2023-01-01 09:30:03 103 108 98 105 1,003 + 4 2023-01-01 09:30:04 104 109 99 106 1,004 + 5 2023-01-01 09:30:05 105 110 100 107 1,005 + 6 2023-01-01 09:30:06 106 111 101 108 1,006 + 7 2023-01-01 09:30:07 107 112 102 109 1,007 + 8 2023-01-01 09:30:08 108 113 103 110 1,008 + 9 2023-01-01 09:30:09 109 114 104 111 1,009 + 10 2023-01-01 09:30:10 110 115 105 112 1,010 + 11 2023-01-01 09:30:11 111 116 106 113 1,011 + 12 2023-01-01 09:30:12 112 117 107 114 1,012 + 13 2023-01-01 09:30:13 113 118 108 115 1,013 + 14 2023-01-01 09:30:14 114 119 109 116 1,014 + 15 2023-01-01 09:30:15 115 120 110 117 1,015 + 16 2023-01-01 09:30:16 116 121 111 118 1,016 + 17 2023-01-01 09:30:17 117 122 112 119 1,017 + 18 2023-01-01 09:30:18 118 123 113 120 1,018 + 19 2023-01-01 09:30:19 119 124 114 121 1,019 + + """.WithDefaultLineEndings(); + + string output = quotes.ToStringOut(); + Console.WriteLine(output); + + string[] lines = output.Split(Environment.NewLine); + lines.Length.Should().Be(23); // 2 headers + 20 data rows + 1 eof line + + output.Should().Be(expected); + } + + [TestMethod] + public void ToFixedWidthResultEma() + { + IReadOnlyList ema = Quotes.ToEma(14); + string output = ema.ToStringOut(startIndex: ema.Count - 21, endIndex: ema.Count - 1); + Console.WriteLine(output); + + // TODO: fix after adding index range + + string expected = """ + i Timestamp Ema + --------------------------- + 481 2018-11-29 264.114847 + 482 2018-11-30 264.760868 + 483 2018-12-03 265.795419 + 484 2018-12-04 265.514696 + 485 2018-12-06 265.218070 + 486 2018-12-07 264.144994 + 487 2018-12-10 263.280328 + 488 2018-12-11 262.538951 + 489 2018-12-12 262.068424 + 490 2018-12-13 261.649968 + 491 2018-12-14 260.649972 + 492 2018-12-17 259.117976 + 493 2018-12-18 257.754246 + 494 2018-12-19 256.075013 + 495 2018-12-20 254.087678 + 496 2018-12-21 251.706654 + 497 2018-12-24 248.811100 + 498 2018-12-26 247.850954 + 499 2018-12-27 247.265493 + 500 2018-12-28 246.716761 + 501 2018-12-31 246.525193 + + """.WithDefaultLineEndings(); + + output.Should().Be(expected); + } + + [TestMethod] + public void ToFixedWidthResultHtTrendline() + { + string output = Quotes.ToHtTrendline().ToStringOut(startIndex: 90, endIndex: 110); + Console.WriteLine(output); + + // TODO: fix after adding index range + + string expected = """ + i Timestamp DcPeriods Trendline SmoothPrice + --------------------------------------------------- + 90 2017-05-12 18 225.587904 226.912000 + 91 2017-05-15 19 225.755992 227.174500 + 92 2017-05-16 19 225.969113 227.481500 + 93 2017-05-17 19 226.155297 226.608000 + 94 2017-05-18 20 226.224826 225.659000 + 95 2017-05-19 21 226.246929 225.548000 + 96 2017-05-22 22 226.251725 226.017000 + 97 2017-05-23 22 226.340184 226.802000 + 98 2017-05-24 22 226.487975 227.505000 + 99 2017-05-25 22 226.646455 228.305000 + 100 2017-05-26 23 226.790405 228.846000 + 101 2017-05-30 24 226.905861 229.084500 + 102 2017-05-31 25 226.999587 229.089000 + 103 2017-06-01 26 227.098513 229.479000 + 104 2017-06-02 26 227.227763 230.233000 + 105 2017-06-05 25 227.413835 230.913000 + 106 2017-06-06 24 227.634324 231.168500 + 107 2017-06-07 23 227.889454 231.138500 + 108 2017-06-08 22 228.143057 231.170500 + 109 2017-06-09 21 228.386085 231.095000 + 110 2017-06-12 21 228.603337 230.852000 + + """.WithDefaultLineEndings(); + + output.Should().Be(expected); + } +} + +/// +/// A test class implementing containing properties of various data types. +/// +public class AllTypes : ISeries +{ + /// + /// A type with time (UTC) + /// + public DateTime Timestamp { get; } = new DateTime(2023, 1, 1, 14, 30, 0, DateTimeKind.Utc); + + /// + /// A type with time + /// + public DateTime DateTimeProperty { get; } = new DateTime(2023, 1, 1, 9, 30, 0); + + /// + /// A type without time. + /// + public DateOnly DateProperty { get; } = new DateOnly(2023, 1, 1); + + /// + /// A type with time and offset. + /// + public DateTimeOffset DateTimeOffsetProperty { get; } = new DateTimeOffset(2023, 1, 1, 9, 30, 0, TimeSpan.FromHours(-5)); + + /// + /// A type + /// + public TimeSpan TimeSpanProperty { get; } = new TimeSpan(1, 2, 3); + + /// + /// A type + /// + public byte ByteProperty { get; } = 255; + + /// + /// A short integer type + /// + public short ShortProperty { get; } = 32767; + + /// + /// A integer type + /// + public int IntProperty { get; } = -2147483648; + + /// + /// the long integer type + /// + public long LongProperty { get; } = 9223372036854775803L; + + /// + /// A get of floating point type + /// + public float FloatProperty { get; } = -125.25143f; + + /// + /// A floating point type + /// + public double DoubleProperty { get; } = 5.251426433759354d; + + /// + /// A type + /// + public decimal DecimalProperty { get; } = 7922815.2514264337593543950335m; + + /// + /// A type + /// + public char CharProperty { get; } = 'A'; + + /// + /// A type + /// + public bool BoolProperty { get; } = true; + + public bool NoXmlProperty { get; } // false + + /// + /// A type + /// + public string StringProperty { get; } = "The lazy dog jumped over the sly brown fox."; +} diff --git a/tests/indicators/_testdata/TestData.Getter.cs b/tests/indicators/_testdata/TestData.Getter.cs index 69d763678..5883c90a2 100644 --- a/tests/indicators/_testdata/TestData.Getter.cs +++ b/tests/indicators/_testdata/TestData.Getter.cs @@ -14,8 +14,14 @@ internal static IReadOnlyList GetDefault(int days = 502) .ToSortedList(); // RANDOM: gaussian brownaian motion - internal static IReadOnlyList GetRandom(int days = 502) - => new RandomGbm(bars: days); + internal static IReadOnlyList GetRandom( + int bars = 502, + PeriodSize periodSize = PeriodSize.OneMinute, + bool includeWeekends = true) + => new RandomGbm( + bars: bars, + periodSize: periodSize, + includeWeekends: includeWeekends); // sorted by filename diff --git a/tests/indicators/_testdata/TestData.Random.cs b/tests/indicators/_testdata/TestData.Random.cs index 7a8d1cb5f..6cf6bd92c 100644 --- a/tests/indicators/_testdata/TestData.Random.cs +++ b/tests/indicators/_testdata/TestData.Random.cs @@ -3,44 +3,85 @@ namespace Test.Data; /// /// Geometric Brownian Motion (GMB) is a random simulator of market movement. /// GBM can be used for testing indicators, validation and Monte Carlo simulations of strategies. -/// -/// -/// Sample usage: -/// -/// RandomGbm data = new(); // generates 1 year (252) list of bars -/// RandomGbm data = new(Bars: 1000); // generates 1,000 bars -/// RandomGbm data = new(Bars: 252, Volatility: 0.05, Drift: 0.0005, Seed: 100.0) -/// -/// Parameters: -/// -/// Bars: number of bars (quotes) requested -/// Volatility: how dymamic/volatile the series should be; default is 1 -/// Drift: incremental drift due to annual interest rate; default is 5% -/// Seed: starting value of the random series; should not be 0. -/// - +/// internal class RandomGbm : List { private readonly double _volatility; private readonly double _drift; private double _seed; + private static readonly Random _random = new((int)DateTime.UtcNow.Ticks); + /// + /// Initializes a new instance of the class. + /// + /// Sample usage: + /// + /// RandomGbm data = new(); // generates 1 year (252) list of bars + /// RandomGbm data = new(Bars: 1000); // generates 1,000 bars + /// RandomGbm data = new(Bars: 252, Volatility: 0.05, Drift: 0.0005, Seed: 100.0) + /// + /// + /// Number of bars (quotes) requested. + /// How dynamic/volatile the series should be; default is 1. + /// Incremental drift due to annual interest rate; default is 5%. + /// Starting value of the random series; should not be 0. + /// The period size for the quotes. + /// Whether to include weekends in the generated data. + /// Thrown when an invalid argument is provided. public RandomGbm( int bars = 250, double volatility = 1.0, double drift = 0.01, - double seed = 1000.0) + double seed = 1000.0, + PeriodSize periodSize = PeriodSize.OneMinute, + bool includeWeekends = true) { + // validation + if (bars <= 0) + { + throw new ArgumentException("Number of bars must be greater than zero.", nameof(bars)); + } + + if (volatility <= 0) + { + throw new ArgumentException("Volatility must be greater than zero.", nameof(volatility)); + } + + if (seed <= 0) + { + throw new ArgumentException("Seed must be greater than zero.", nameof(seed)); + } + + TimeSpan frequency = periodSize.ToTimeSpan(); + + if (!includeWeekends && (frequency < TimeSpan.FromHours(1) || frequency >= TimeSpan.FromDays(7))) + { + throw new ArgumentException("Weekends can only be excluded for period sizes between OneHour and OneWeek.", nameof(includeWeekends)); + } + _seed = seed; _volatility = volatility * 0.01; _drift = drift * 0.001; - for (int i = 0; i < bars; i++) + + DateTime date = DateTime.Today.Add(frequency * -bars); + int generatedBars = 0; + + while (generatedBars < bars) { - DateTime date = DateTime.Today.AddMinutes(i - bars); - Add(date); + if (includeWeekends || frequency < TimeSpan.FromHours(1) || frequency >= TimeSpan.FromDays(7) || (date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday)) + { + Add(date); + generatedBars++; + } + + date = date.Add(frequency); } } + /// + /// Adds a new quote to the list. + /// + /// The timestamp of the quote. public void Add(DateTime timestamp) { double open = Price(_seed, _volatility * _volatility, _drift); @@ -54,7 +95,7 @@ public void Add(DateTime timestamp) double low = Price(_seed, _volatility * 0.5, 0); low = low > ocMin ? (2 * ocMin) - low : low; - double volume = Price(_seed * 10, _volatility * 2, drift: 0); + double volume = Price(_seed * 1000, _volatility * 2, drift: 0); Quote quote = new( Timestamp: timestamp, @@ -68,11 +109,17 @@ public void Add(DateTime timestamp) _seed = close; } + /// + /// Generates a random price based on the seed, volatility, and drift. + /// + /// The seed value. + /// The volatility value. + /// The drift value. + /// A random price. private static double Price(double seed, double volatility, double drift) { - Random rnd = new((int)DateTime.UtcNow.Ticks); - double u1 = 1.0 - rnd.NextDouble(); - double u2 = 1.0 - rnd.NextDouble(); + double u1 = 1.0 - _random.NextDouble(); + double u2 = 1.0 - _random.NextDouble(); double z = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); return seed * Math.Exp(drift - (volatility * volatility * 0.5) + (volatility * z)); } diff --git a/tests/indicators/_testdata/TestData.Utilities.cs b/tests/indicators/_testdata/TestData.Utilities.cs new file mode 100644 index 000000000..39b3638f4 --- /dev/null +++ b/tests/indicators/_testdata/TestData.Utilities.cs @@ -0,0 +1,9 @@ +namespace Test.Utilities; + +internal static class StringUtilities +{ + internal static string WithDefaultLineEndings(this string input) + => input + .Replace("\r\n", "\n", StringComparison.Ordinal) + .Replace("\n", Environment.NewLine, StringComparison.Ordinal); +} diff --git a/tests/performance/Perf.Utility.cs b/tests/performance/Perf.Utility.cs index eca434507..e1b2b030a 100644 --- a/tests/performance/Perf.Utility.cs +++ b/tests/performance/Perf.Utility.cs @@ -5,27 +5,34 @@ namespace Performance; [ShortRunJob] public class Utility { - private static readonly IReadOnlyList q = Data.GetDefault(); - private static readonly IReadOnlyList i = Data.GetIntraday(); + private static readonly IReadOnlyList quotes = Data.GetDefault(); + private static readonly IReadOnlyList intraday = Data.GetIntraday(); + private static readonly Quote quote = quotes[0]; [Benchmark] - public object ToSortedList() => q.ToSortedList(); + public object ToSortedList() => quotes.ToSortedList(); [Benchmark] - public object ToListQuoteD() => q.ToQuoteDList(); + public object ToListQuoteD() => quotes.ToQuoteDList(); [Benchmark] - public object ToReusableClose() => q.ToReusable(CandlePart.Close); + public object ToReusableClose() => quotes.ToReusable(CandlePart.Close); [Benchmark] - public object ToReusableOhlc4() => q.ToReusable(CandlePart.OHLC4); + public object ToReusableOhlc4() => quotes.ToReusable(CandlePart.OHLC4); [Benchmark] - public object ToCandleResults() => q.ToCandles(); + public object ToCandleResults() => quotes.ToCandles(); [Benchmark] - public object Validate() => q.Validate(); + public object ToStringOutType() => quote.ToStringOut(); [Benchmark] - public object Aggregate() => i.Aggregate(PeriodSize.FifteenMinutes); + public object ToStringOutList() => quotes.ToStringOut(); + + [Benchmark] + public object Validate() => quotes.Validate(); + + [Benchmark] + public object Aggregate() => intraday.Aggregate(PeriodSize.FifteenMinutes); } From b326fc533607a2df228ccea0d317ffa84fde763f Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Mon, 23 Dec 2024 23:56:33 -0500 Subject: [PATCH 18/18] ci: Update build triggers (#1295) Signed-off-by: Dave Skender <8432125+DaveSkender@users.noreply.github.com> --- .github/workflows/lint-pull-request.yml | 2 +- .github/workflows/test-codeql.yml | 6 +++--- .github/workflows/test-examples.yml | 8 ++++---- .github/workflows/test-indicators.yml | 4 ++-- .github/workflows/test-integration.yml | 4 ++-- .github/workflows/test-website-a11y.yml | 21 ++++++++------------- .github/workflows/test-website-links.yml | 12 +++++------- .vscode/settings.json | 2 +- 8 files changed, 26 insertions(+), 33 deletions(-) diff --git a/.github/workflows/lint-pull-request.yml b/.github/workflows/lint-pull-request.yml index 1d78109ba..dd068cb0c 100644 --- a/.github/workflows/lint-pull-request.yml +++ b/.github/workflows/lint-pull-request.yml @@ -1,4 +1,4 @@ -name: "Pull request" +name: Pull request on: pull_request_target: diff --git a/.github/workflows/test-codeql.yml b/.github/workflows/test-codeql.yml index 99a0a94ef..9a585cb6e 100644 --- a/.github/workflows/test-codeql.yml +++ b/.github/workflows/test-codeql.yml @@ -1,11 +1,11 @@ -name: "CodeQL" +name: CodeQL on: push: - branches: [ "main", "v3" ] + branches: ["main"] pull_request: - branches: [ "main", "v3" ] + branches: ["*"] schedule: - cron: '37 14 * * 3' diff --git a/.github/workflows/test-examples.yml b/.github/workflows/test-examples.yml index 0735a40af..454d8ec71 100644 --- a/.github/workflows/test-examples.yml +++ b/.github/workflows/test-examples.yml @@ -2,19 +2,19 @@ name: Examples on: push: - branches: ["main","v3"] + branches: ["main"] paths: - docs/examples/** pull_request: - branches: ["main","v3"] + branches: ["*"] paths: - docs/examples/** - - ".github/workflows/test-examples.yml" + - .github/workflows/test-examples.yml jobs: build: - name: build only + name: build runs-on: ubuntu-latest steps: diff --git a/.github/workflows/test-indicators.yml b/.github/workflows/test-indicators.yml index 9dfb63fe1..f5a0b9f74 100644 --- a/.github/workflows/test-indicators.yml +++ b/.github/workflows/test-indicators.yml @@ -2,10 +2,10 @@ name: Indicators on: push: - branches: ["main","v3"] + branches: ["main"] pull_request: - types: [opened, synchronize, reopened] + branches: ["*"] jobs: test: diff --git a/.github/workflows/test-integration.yml b/.github/workflows/test-integration.yml index 0117abb07..2d37cb256 100644 --- a/.github/workflows/test-integration.yml +++ b/.github/workflows/test-integration.yml @@ -2,10 +2,10 @@ name: Indicators on: push: - branches: ["main","v3"] + branches: ["main"] pull_request: - types: [opened, synchronize, reopened] + branches: ["*"] jobs: test: diff --git a/.github/workflows/test-website-a11y.yml b/.github/workflows/test-website-a11y.yml index c00dd6d8c..55fe4faba 100644 --- a/.github/workflows/test-website-a11y.yml +++ b/.github/workflows/test-website-a11y.yml @@ -1,22 +1,23 @@ -name: Website +name: Website a11y on: - push: - branches: ["v3"] pull_request: - branches: ["main"] + branches: ["*"] paths: - - 'docs/**' - - ".github/workflows/test-website-a11y.yml" + - docs/** + - .github/workflows/test-website-a11y.yml env: JEKYLL_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: test: - name: test a11y + name: test runs-on: ubuntu-latest + env: + BUNDLE_GEMFILE: ${{github.workspace}}/docs/GemFile + steps: - name: Checkout source uses: actions/checkout@v4 @@ -29,22 +30,16 @@ jobs: - name: Install GEMs working-directory: docs - env: - BUNDLE_GEMFILE: ${{github.workspace}}/docs/GemFile run: | pwd bundle install - name: Build site working-directory: docs - env: - BUNDLE_GEMFILE: ${{github.workspace}}/docs/GemFile run: bundle exec jekyll build - name: Serve site working-directory: docs - env: - BUNDLE_GEMFILE: ${{github.workspace}}/docs/GemFile run: bundle exec jekyll serve --detach - name: Show environment diff --git a/.github/workflows/test-website-links.yml b/.github/workflows/test-website-links.yml index 10319897e..d110d769a 100644 --- a/.github/workflows/test-website-links.yml +++ b/.github/workflows/test-website-links.yml @@ -1,20 +1,18 @@ -name: Website +name: Website URLs on: - push: - branches: ["v3"] pull_request: - branches: ["main"] + branches: ["*"] paths: - - 'docs/**' - - ".github/workflows/test-website-links.yml" + - docs/** + - .github/workflows/test-website-links.yml env: JEKYLL_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: test: - name: test URLs + name: test runs-on: ubuntu-latest steps: diff --git a/.vscode/settings.json b/.vscode/settings.json index f02c7ba81..082556180 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { // file settings - "files.autoSave": "afterDelay", + "files.autoSave": "off", "files.autoSaveDelay": 3000, "files.encoding": "utf8", "files.exclude": {

    k>ORN|;nEirFUS0a8 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≫eSD;_$nwdFY04f3_k{UHSBb4;^ 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:Xf7^l+MX8?+u*pjgRB@ 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�=vXmVZ|(a!(cPO+NwK4+l{ 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�*7d8O_@EN`>*E4x{*3tPrVVprxJdSI2*1*apCPE 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 Zy0V(+z5|nng{&u*gOP;d7%&9q{{cI6+m`?U delta 164972 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