From 96708d2e74d105bd81e2aaf2f68f0071242d1959 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Tue, 2 Jan 2024 20:49:09 -0500 Subject: [PATCH 1/4] ci: Rename docs environment (#1138) --- .github/workflows/deploy-docs.yml | 4 ++-- .github/workflows/semantic-pr-linter.yml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) 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. From 7de4279ad9151915c9d654c50d7ec3256d7a9d73 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Wed, 3 Jan 2024 00:36:03 -0500 Subject: [PATCH 2/4] chore: Code cleanup (#1135) Signed-off-by: Dave Skender <8432125+DaveSkender@users.noreply.github.com> --- .editorconfig | 29 ++++++++++--------- .github/workflows/build-indicators.yml | 10 ++++++- docs/GemFile.lock | 18 ++++++------ src/GlobalSuppressions.cs | 5 ---- src/Indicators.csproj | 7 +---- src/_common/Enums.cs | 2 +- src/_common/Math/NullMath.cs | 8 ++--- src/_common/Math/Numerix.cs | 8 ++--- 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/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/Tema/Tema.Api.cs | 2 +- tests/indicators/GlobalUsings.cs | 3 ++ tests/indicators/Tests.Indicators.csproj | 2 +- 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.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.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/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.Indicators.cs | 26 +++++++---------- tests/performance/Perf.Internals.cs | 11 ++----- tests/performance/Program.cs | 1 + tests/performance/Tests.Performance.csproj | 6 ++-- 134 files changed, 98 insertions(+), 499 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/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 b475b80d0..76fb16060 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( diff --git a/src/Indicators.csproj b/src/Indicators.csproj index e0a63f23f..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 - @@ -81,7 +76,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + 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/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 a166aadff..f5ae8c2de 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(); } @@ -43,7 +43,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 38ca78960..e0fe70cf7 100644 --- a/src/_common/Results/info.xml +++ b/src/_common/Results/info.xml @@ -18,7 +18,7 @@ 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/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/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/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/indicators/_Initialize.cs b/tests/indicators/_Initialize.cs index d8e149490..3b09c9e0f 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 f2b16fabd..631be6603 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 e2ef4cbe1..d8144ce4e 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 1f8a5204c..f75f5e4d3 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 c7ee494e0..1881f11f1 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 a20b604da..bf7d9cb55 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.Tests.cs b/tests/indicators/e-k/Ema/Ema.Tests.cs index 41c67e1cc..5631816c2 100644 --- a/tests/indicators/e-k/Ema/Ema.Tests.cs +++ b/tests/indicators/e-k/Ema/Ema.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.Tests.cs b/tests/indicators/s-z/Sma/Sma.Tests.cs index da1fcded7..49f7b4ac6 100644 --- a/tests/indicators/s-z/Sma/Sma.Tests.cs +++ b/tests/indicators/s-z/Sma/Sma.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/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 877787aaa..e515fb269 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.Indicators.cs b/tests/performance/Perf.Indicators.cs index 030f31a2e..e2a7d8ad7 100644 --- a/tests/performance/Perf.Indicators.cs +++ b/tests/performance/Perf.Indicators.cs @@ -1,7 +1,3 @@ -using BenchmarkDotNet.Attributes; -using Skender.Stock.Indicators; -using Tests.Common; - namespace Tests.Performance; public class IndicatorPerformance @@ -19,8 +15,8 @@ public static void Setup() hList = h.ToList(); } - [GlobalSetup(Targets = new[] - { + [GlobalSetup(Targets = + [ nameof(GetBeta), nameof(GetBetaUp), nameof(GetBetaDown), @@ -28,7 +24,7 @@ public static void Setup() nameof(GetCorrelation), nameof(GetPrs), nameof(GetPrsWithSma) - })] + ])] public static void SetupCompare() { h = TestData.GetDefault(); @@ -65,16 +61,16 @@ public static void SetupCompare() public object GetAwesome() => h.GetAwesome(); [Benchmark] - public object GetBeta() => Indicator.GetBeta(h, ho, 20, BetaType.Standard); + public object GetBeta() => h.GetBeta(ho, 20); [Benchmark] - public object GetBetaUp() => Indicator.GetBeta(h, ho, 20, BetaType.Up); + public object GetBetaUp() => h.GetBeta(ho, 20, BetaType.Up); [Benchmark] - public object GetBetaDown() => Indicator.GetBeta(h, ho, 20, BetaType.Down); + public object GetBetaDown() => h.GetBeta(ho, 20, BetaType.Down); [Benchmark] - public object GetBetaAll() => Indicator.GetBeta(h, ho, 20, BetaType.All); + public object GetBetaAll() => h.GetBeta(ho, 20, BetaType.All); [Benchmark] public object GetBollingerBands() => h.GetBollingerBands(); @@ -145,7 +141,7 @@ public object GetEmaStream() public object GetFcb() => h.GetFcb(14); [Benchmark] - public object GetFisherTransform() => h.GetFisherTransform(10); + public object GetFisherTransform() => h.GetFisherTransform(); [Benchmark] public object GetForceIndex() => h.GetForceIndex(13); @@ -187,7 +183,7 @@ public object GetEmaStream() public object GetMacd() => h.GetMacd(); [Benchmark] - public object GetMaEnvelopes() => h.GetMaEnvelopes(20, 2.5, MaType.SMA); + public object GetMaEnvelopes() => h.GetMaEnvelopes(20); [Benchmark] public object GetMama() => h.GetMama(); @@ -208,7 +204,7 @@ public object GetEmaStream() public object GetParabolicSar() => h.GetParabolicSar(); [Benchmark] - public object GetPivotPoints() => h.GetPivotPoints(PeriodSize.Month, PivotPointType.Standard); + public object GetPivotPoints() => h.GetPivotPoints(PeriodSize.Month); [Benchmark] public object GetPivots() => h.GetPivots(); @@ -256,7 +252,7 @@ public object GetEmaStream() public object GetSmaAnalysis() => h.GetSmaAnalysis(10); [Benchmark] - public object GetSmi() => h.GetSmi(5, 20, 5, 3); + public object GetSmi() => h.GetSmi(5, 20, 5); [Benchmark] public object GetSmma() => h.GetSmma(10); 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 bb68eb478..ec1a5c91b 100644 --- a/tests/performance/Tests.Performance.csproj +++ b/tests/performance/Tests.Performance.csproj @@ -14,7 +14,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -22,8 +22,8 @@ - - + + From adcc42db25b403111f8b230ffff9b1a40348e3e0 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Wed, 3 Jan 2024 00:49:47 -0500 Subject: [PATCH 3/4] merge `main` into `v3` (#1140) --- Stock.Indicators.sln | 11 + gitversion.yml | 1 + 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 | 11 +- tests/performance/Perf.Indicators.Static.cs | 323 +++++++++++++++++ tests/performance/Perf.Indicators.Stream.cs | 62 ++++ tests/performance/Perf.Indicators.cs | 331 ------------------ tests/performance/Tests.Performance.csproj | 2 +- 44 files changed, 2166 insertions(+), 585 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} (79%) 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 0337bb528..9dac3c139 100644 --- a/Stock.Indicators.sln +++ b/Stock.Indicators.sln @@ -24,6 +24,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{3A4158F9 .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 @@ -48,6 +55,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/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)' diff --git a/src/GlobalSuppressions.cs b/src/GlobalSuppressions.cs index 76fb16060..2d7951d4f 100644 --- a/src/GlobalSuppressions.cs +++ b/src/GlobalSuppressions.cs @@ -44,3 +44,23 @@ "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", + 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)")] \ No newline at end of file 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 f5ae8c2de..eca3f3ac3 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 = []; - 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 = []; diff --git a/src/_common/Results/info.xml b/src/_common/Results/info.xml index e0fe70cf7..11ad40769 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 handling 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 fad65dc99..000000000 --- a/src/e-k/Ema/Ema.Stream.cs +++ /dev/null @@ -1,82 +0,0 @@ -namespace Skender.Stock.Indicators; - -// EXPONENTIAL MOVING AVERAGE (STREAMING) -public 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 3b09c9e0f..8fdecf061 100644 --- a/tests/indicators/_Initialize.cs +++ b/tests/indicators/_Initialize.cs @@ -6,6 +6,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 631be6603..cb312d597 100644 --- a/tests/indicators/_common/Generics/Seek.Tests.cs +++ b/tests/indicators/_common/Generics/Seek.Tests.cs @@ -4,7 +4,7 @@ namespace Tests.Common; public class Seeking : TestBase { [TestMethod] - public void Find() + public void FindSeries() { IEnumerable quotes = TestData.GetDefault(); IEnumerable emaResults = quotes.GetEma(20); @@ -15,4 +15,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 d8144ce4e..0e238dcfb 100644 --- a/tests/indicators/_common/Helper.Random.cs +++ b/tests/indicators/_common/Helper.Random.cs @@ -25,15 +25,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 f75f5e4d3..44a79bd47 100644 --- a/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs @@ -1,7 +1,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 1881f11f1..f4dc08592 100644 --- a/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs @@ -3,7 +3,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 bf7d9cb55..e9b0b716f 100644 --- a/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs @@ -1,7 +1,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 5631816c2..7987bae6d 100644 --- a/tests/indicators/e-k/Ema/Ema.Tests.cs +++ b/tests/indicators/e-k/Ema/Ema.Static.Tests.cs @@ -1,7 +1,7 @@ namespace Tests.Indicators; [TestClass] -public class EmaTests : TestBase +public class EmaStaticTests : TestBase { [TestMethod] public void Standard() @@ -25,6 +25,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() { @@ -76,40 +102,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) @@ -135,29 +128,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() { @@ -200,16 +170,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 79% rename from tests/indicators/s-z/Sma/Sma.Tests.cs rename to tests/indicators/s-z/Sma/Sma.Static.Tests.cs index 49f7b4ac6..e6defb30a 100644 --- a/tests/indicators/s-z/Sma/Sma.Tests.cs +++ b/tests/indicators/s-z/Sma/Sma.Static.Tests.cs @@ -16,11 +16,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] @@ -36,11 +36,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] @@ -136,7 +136,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..a2af5efa2 --- /dev/null +++ b/tests/observe/Observe.Streaming.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.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 e515fb269..b08cd767f 100644 --- a/tests/performance/Perf.Helpers.cs +++ b/tests/performance/Perf.Helpers.cs @@ -23,14 +23,17 @@ public class HelperPerformance public object ToListQuoteD() => h.ToQuoteD(); [Benchmark] - public object Validate() => h.Validate(); + public object ToTupleClose() => h.ToTuple(CandlePart.Close); [Benchmark] - public object Aggregate() => i.Aggregate(PeriodSize.FifteenMinutes); + public object ToTupleOHLC4() => h.ToTuple(CandlePart.OHLC4); [Benchmark] - public object ToTuple() => h.ToTuple(CandlePart.Close); + public object ToCandleResults() => h.ToCandleResults(); [Benchmark] - public object ToCandleResults() => h.ToCandleResults(); + 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..65412f505 --- /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(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 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 e2a7d8ad7..000000000 --- a/tests/performance/Perf.Indicators.cs +++ /dev/null @@ -1,331 +0,0 @@ -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 = - [ - 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() => h.GetBeta(ho, 20); - - [Benchmark] - public object GetBetaUp() => h.GetBeta(ho, 20, BetaType.Up); - - [Benchmark] - public object GetBetaDown() => h.GetBeta(ho, 20, BetaType.Down); - - [Benchmark] - public object GetBetaAll() => h.GetBeta(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(); - - [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); - - [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); - - [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); - - [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(); -} diff --git a/tests/performance/Tests.Performance.csproj b/tests/performance/Tests.Performance.csproj index ec1a5c91b..bd65b4df8 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 0d3e8b4334c77faf0635dfbfe010bb69af4420a6 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Wed, 3 Jan 2024 00:56:05 -0500 Subject: [PATCH 4/4] revert: Prior merge `main` into `v3` (#1141) --- Stock.Indicators.sln | 11 - gitversion.yml | 1 - 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.Static.Tests.cs => Ema.Tests.cs} | 99 ++++-- tests/indicators/s-z/Sma/Sma.Obs.Tests.cs | 126 ------- .../Sma/{Sma.Static.Tests.cs => Sma.Tests.cs} | 22 +- tests/observe/Observe.Streaming.csproj | 19 - tests/observe/Program.cs | 117 ------- tests/observe/README.md | 10 - tests/performance/Perf.Helpers.cs | 11 +- tests/performance/Perf.Indicators.Static.cs | 323 ----------------- tests/performance/Perf.Indicators.Stream.cs | 62 ---- tests/performance/Perf.Indicators.cs | 331 ++++++++++++++++++ tests/performance/Tests.Performance.csproj | 2 +- 44 files changed, 585 insertions(+), 2166 deletions(-) delete mode 100644 src/_common/Observables/ChainProvider.cs delete mode 100644 src/_common/Observables/QuoteObserver.cs delete mode 100644 src/_common/Observables/QuoteProvider.cs delete mode 100644 src/_common/Observables/TupleObserver.cs delete mode 100644 src/_common/Observables/TupleProvider.cs delete mode 100644 src/_common/Quotes/Use.Api.cs delete mode 100644 src/_common/Quotes/Use.Observer.cs delete mode 100644 src/e-k/Ema/Ema.Observer.cs create mode 100644 src/e-k/Ema/Ema.Stream.cs delete mode 100644 src/s-z/Sma/Sma.Observer.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/e-k/Ema/Ema.Obs.Tests.cs rename tests/indicators/e-k/Ema/{Ema.Static.Tests.cs => Ema.Tests.cs} (75%) delete mode 100644 tests/indicators/s-z/Sma/Sma.Obs.Tests.cs rename tests/indicators/s-z/Sma/{Sma.Static.Tests.cs => Sma.Tests.cs} (79%) delete mode 100644 tests/observe/Observe.Streaming.csproj delete mode 100644 tests/observe/Program.cs delete mode 100644 tests/observe/README.md delete mode 100644 tests/performance/Perf.Indicators.Static.cs delete mode 100644 tests/performance/Perf.Indicators.Stream.cs create mode 100644 tests/performance/Perf.Indicators.cs diff --git a/Stock.Indicators.sln b/Stock.Indicators.sln index 9dac3c139..0337bb528 100644 --- a/Stock.Indicators.sln +++ b/Stock.Indicators.sln @@ -24,13 +24,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{3A4158F9 .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 @@ -55,10 +48,6 @@ 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/gitversion.yml b/gitversion.yml index 3c3ed762a..4cb24ba14 100644 --- a/gitversion.yml +++ b/gitversion.yml @@ -1,5 +1,4 @@ mode: ContinuousDelivery -next-version: 3.0.0 commit-message-incrementing: Enabled major-version-bump-message: '\+semver:\s?(breaking|major)' diff --git a/src/GlobalSuppressions.cs b/src/GlobalSuppressions.cs index 2d7951d4f..76fb16060 100644 --- a/src/GlobalSuppressions.cs +++ b/src/GlobalSuppressions.cs @@ -44,23 +44,3 @@ "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", - 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)")] \ No newline at end of file diff --git a/src/_common/Generics/Seek.cs b/src/_common/Generics/Seek.cs index 0b0387b71..c2fdd5b0a 100644 --- a/src/_common/Generics/Seek.cs +++ b/src/_common/Generics/Seek.cs @@ -4,22 +4,12 @@ namespace Skender.Stock.Indicators; public static class Seeking { - // FIND SERIES by DATE - /// + // FIND 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 3d2e60d7a..e50729fba 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,21 +10,8 @@ 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. - + First + record in the series on the date specified. 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 deleted file mode 100644 index de73cfdbc..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 = 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 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 6f9d4c01c..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 = 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 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 38edc7ab7..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 = 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 07f45c7bf..b73c22b9b 100644 --- a/src/_common/Quotes/Quote.Converters.cs +++ b/src/_common/Quotes/Quote.Converters.cs @@ -9,6 +9,17 @@ 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 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/Results/Result.Utilities.cs b/src/_common/Results/Result.Utilities.cs index eca3f3ac3..f5ae8c2de 100644 --- a/src/_common/Results/Result.Utilities.cs +++ b/src/_common/Results/Result.Utilities.cs @@ -26,19 +26,17 @@ public static IEnumerable Condense( // CONVERT TO TUPLE (default with pruning) /// /// - public static Collection<(DateTime Date, double Value)> ToTupleChainable( - this IEnumerable reusable) - where TResult : IReusableResult + public static Collection<(DateTime Date, double Value)> ToTupleChainable( + this IEnumerable reusable) => reusable .ToTuple() .ToCollection(); - internal static List<(DateTime Date, double Value)> ToTuple( - this IEnumerable reusable) - where TResult : IReusableResult + internal static List<(DateTime Date, double Value)> ToTuple( + this IEnumerable reusable) { List<(DateTime date, double value)> prices = []; - List reList = reusable.ToList(); + List reList = reusable.ToList(); // find first non-nulled int first = reList.FindIndex(x => x.Value != null); @@ -55,11 +53,10 @@ 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) - where TResult : IReusableResult + public static Collection<(DateTime Date, double Value)> ToTupleNaN( + this IEnumerable reusable) { - List reList = reusable.ToSortedList(); + List reList = reusable.ToSortedList(); int length = reList.Count; Collection<(DateTime Date, double Value)> results = []; diff --git a/src/_common/Results/info.xml b/src/_common/Results/info.xml index 11ad40769..e0fe70cf7 100644 --- a/src/_common/Results/info.xml +++ b/src/_common/Results/info.xml @@ -2,13 +2,11 @@ - 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. @@ -18,7 +16,6 @@ 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 handling of nulls, without pruning. @@ -49,7 +46,8 @@ 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 765314080..e57464f92 100644 --- a/src/e-k/Ema/Ema.Api.cs +++ b/src/e-k/Ema/Ema.Api.cs @@ -1,7 +1,6 @@ namespace Skender.Stock.Indicators; // EXPONENTIAL MOVING AVERAGE (API) - public static partial class Indicator { // SERIES, from TQuote @@ -29,24 +28,30 @@ public static IEnumerable GetEma( .ToSortedList() .CalcEma(lookbackPeriods); - // OBSERVER, from Quote Provider - /// + // STREAM INITIALIZATION, from TQuote + /// /// - public static EmaObserver GetEma( - this QuoteProvider provider, + internal static EmaBase InitEma( + this IEnumerable quotes, int lookbackPeriods) + where TQuote : IQuote { - UseObserver useObserver = provider - .Use(CandlePart.Close); + // convert quotes + List<(DateTime, double)> tpList + = quotes.ToTuple(CandlePart.Close); - return new(useObserver, lookbackPeriods); + return new EmaBase(tpList, lookbackPeriods); } - // OBSERVER, from Chain Provider - /// - /// - public static EmaObserver GetEma( - this TupleProvider tupleProvider, + // STREAM INITIALIZATION, from CHAIN + internal static EmaBase InitEma( + this IEnumerable results, int lookbackPeriods) - => new(tupleProvider, lookbackPeriods); + { + // convert results + List<(DateTime, double)> tpList + = results.ToTuple(); + + return new EmaBase(tpList, lookbackPeriods); + } } diff --git a/src/e-k/Ema/Ema.Observer.cs b/src/e-k/Ema/Ema.Observer.cs deleted file mode 100644 index 0ad2e7ba3..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 = 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 77952750d..b54f3e79e 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 - EmaObserver.Validate(lookbackPeriods); + EmaBase.Validate(lookbackPeriods); // initialize int length = tpList.Count; @@ -35,7 +35,7 @@ internal static List CalcEma( if (i + 1 > lookbackPeriods) { - double ema = EmaObserver.Increment(value, lastEma, k); + double ema = EmaBase.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 new file mode 100644 index 000000000..fad65dc99 --- /dev/null +++ b/src/e-k/Ema/Ema.Stream.cs @@ -0,0 +1,82 @@ +namespace Skender.Stock.Indicators; + +// EXPONENTIAL MOVING AVERAGE (STREAMING) +public 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 5e3e3be73..a9e87b466 100644 --- a/src/e-k/Ema/info.xml +++ b/src/e-k/Ema/info.xml @@ -16,32 +16,19 @@ Time series of EMA values. Invalid parameter value provided. - + - Establish an observable streaming Exponential Moving Average (EMA). + Extablish a streaming base for 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. + Configurable Quote type. See Guide for more information. + Historical price quotes. Number of periods in the lookback window. - Observable EMA instance. + EMA base that you can add Quotes to with the .Add(quote) method. 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 5e21293dd..c0f3d40b5 100644 --- a/src/s-z/Sma/Sma.Api.cs +++ b/src/s-z/Sma/Sma.Api.cs @@ -1,7 +1,6 @@ namespace Skender.Stock.Indicators; // SIMPLE MOVING AVERAGE (API) - public static partial class Indicator { // SERIES, from TQuote @@ -29,27 +28,6 @@ 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 deleted file mode 100644 index cc496dd92..000000000 --- a/src/s-z/Sma/Sma.Observer.cs +++ /dev/null @@ -1,144 +0,0 @@ -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 2541de516..0662b9e5d 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 - SmaObserver.Validate(lookbackPeriods); + ValidateSma(lookbackPeriods); // initialize List results = new(tpList.Count); @@ -21,11 +21,31 @@ internal static List CalcSma( SmaResult result = new(date); results.Add(result); - result.Sma = SmaObserver - .Increment(tpList, i, lookbackPeriods) - .NaN2Null(); + 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; } + + // 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 386418d41..fb6d7271a 100644 --- a/src/s-z/Sma/info.xml +++ b/src/s-z/Sma/info.xml @@ -17,34 +17,7 @@ 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 8fdecf061..3b09c9e0f 100644 --- a/tests/indicators/_Initialize.cs +++ b/tests/indicators/_Initialize.cs @@ -6,7 +6,6 @@ [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 cb312d597..631be6603 100644 --- a/tests/indicators/_common/Generics/Seek.Tests.cs +++ b/tests/indicators/_common/Generics/Seek.Tests.cs @@ -4,7 +4,7 @@ namespace Tests.Common; public class Seeking : TestBase { [TestMethod] - public void FindSeries() + public void Find() { IEnumerable quotes = TestData.GetDefault(); IEnumerable emaResults = quotes.GetEma(20); @@ -15,31 +15,4 @@ public void FindSeries() 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 0e238dcfb..d8144ce4e 100644 --- a/tests/indicators/_common/Helper.Random.cs +++ b/tests/indicators/_common/Helper.Random.cs @@ -25,15 +25,15 @@ internal class RandomGbm : List public RandomGbm( int bars = 250, double volatility = 1.0, - double drift = 0.01, - double seed = 1000.0) + double drift = 0.05, + double seed = 10000000.0) { this.seed = seed; this.volatility = volatility * 0.01; - this.drift = drift * 0.001; + this.drift = drift * 0.01; for (int i = 0; i < bars; i++) { - DateTime date = DateTime.Today.AddMinutes(i - bars); + DateTime date = DateTime.Today.AddDays(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 44a79bd47..f75f5e4d3 100644 --- a/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs @@ -1,7 +1,7 @@ namespace Tests.Common; [TestClass] -public class QuoteAggregateTests : TestBase +public class QuoteHistory : 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 f4dc08592..1881f11f1 100644 --- a/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs @@ -3,7 +3,7 @@ namespace Tests.Common; [TestClass] -public class QuoteUtilityTests : TestBase +public class QuoteUtility : 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 e9b0b716f..bf7d9cb55 100644 --- a/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs @@ -1,7 +1,7 @@ namespace Tests.Common; [TestClass] -public class QuoteValidationTests : TestBase +public class QuoteValidation : 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 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/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.Tests.cs similarity index 75% rename from tests/indicators/e-k/Ema/Ema.Static.Tests.cs rename to tests/indicators/e-k/Ema/Ema.Tests.cs index 7987bae6d..5631816c2 100644 --- a/tests/indicators/e-k/Ema/Ema.Static.Tests.cs +++ b/tests/indicators/e-k/Ema/Ema.Tests.cs @@ -1,7 +1,7 @@ namespace Tests.Indicators; [TestClass] -public class EmaStaticTests : TestBase +public class EmaTests : TestBase { [TestMethod] public void Standard() @@ -25,32 +25,6 @@ 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() { @@ -102,7 +76,40 @@ public void Chainor() } [TestMethod] - public void ChaineeMore() + 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() { List results = quotes .GetRsi(14) @@ -128,6 +135,29 @@ public void ChaineeMore() 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() { @@ -170,9 +200,16 @@ public void Removed() Assert.AreEqual(249.3519, last.Ema.Round(4)); } - // bad lookback period [TestMethod] public void Exceptions() - => Assert.ThrowsException(() - => quotes.GetEma(0)); + { + // bad lookback period + Assert.ThrowsException(() => + quotes.GetEma(0)); + + // null quote added + EmaBase emaBase = quotes.InitEma(14); + Assert.ThrowsException(() => + emaBase.Add(null)); + } } 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.Tests.cs similarity index 79% rename from tests/indicators/s-z/Sma/Sma.Static.Tests.cs rename to tests/indicators/s-z/Sma/Sma.Tests.cs index e6defb30a..49f7b4ac6 100644 --- a/tests/indicators/s-z/Sma/Sma.Static.Tests.cs +++ b/tests/indicators/s-z/Sma/Sma.Tests.cs @@ -16,11 +16,11 @@ public void Standard() // sample values Assert.IsNull(results[18].Sma); - 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)); + 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] @@ -36,11 +36,11 @@ public void CandlePartOpen() // sample values Assert.IsNull(results[18].Sma); - 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)); + 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] @@ -136,7 +136,7 @@ public void Removed() // assertions Assert.AreEqual(502 - 19, results.Count); - Assert.AreEqual(251.8600, results.LastOrDefault().Sma.Round(4)); + Assert.AreEqual(251.8600, Math.Round(results.LastOrDefault().Sma.Value, 4)); } // bad lookback period diff --git a/tests/observe/Observe.Streaming.csproj b/tests/observe/Observe.Streaming.csproj deleted file mode 100644 index a2af5efa2..000000000 --- a/tests/observe/Observe.Streaming.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - Exe - net8.0 - enable - enable - - - - - - - - - - - - diff --git a/tests/observe/Program.cs b/tests/observe/Program.cs deleted file mode 100644 index 2df87d5e8..000000000 --- a/tests/observe/Program.cs +++ /dev/null @@ -1,117 +0,0 @@ -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 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/performance/Perf.Helpers.cs b/tests/performance/Perf.Helpers.cs index b08cd767f..e515fb269 100644 --- a/tests/performance/Perf.Helpers.cs +++ b/tests/performance/Perf.Helpers.cs @@ -23,17 +23,14 @@ public class HelperPerformance public object ToListQuoteD() => h.ToQuoteD(); [Benchmark] - public object ToTupleClose() => h.ToTuple(CandlePart.Close); - - [Benchmark] - public object ToTupleOHLC4() => h.ToTuple(CandlePart.OHLC4); + public object Validate() => h.Validate(); [Benchmark] - public object ToCandleResults() => h.ToCandleResults(); + public object Aggregate() => i.Aggregate(PeriodSize.FifteenMinutes); [Benchmark] - public object Validate() => h.Validate(); + public object ToTuple() => h.ToTuple(CandlePart.Close); [Benchmark] - public object Aggregate() => i.Aggregate(PeriodSize.FifteenMinutes); + public object ToCandleResults() => h.ToCandleResults(); } diff --git a/tests/performance/Perf.Indicators.Static.cs b/tests/performance/Perf.Indicators.Static.cs deleted file mode 100644 index 65412f505..000000000 --- a/tests/performance/Perf.Indicators.Static.cs +++ /dev/null @@ -1,323 +0,0 @@ -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(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.Indicators.cs b/tests/performance/Perf.Indicators.cs new file mode 100644 index 000000000..e2a7d8ad7 --- /dev/null +++ b/tests/performance/Perf.Indicators.cs @@ -0,0 +1,331 @@ +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 = + [ + 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() => h.GetBeta(ho, 20); + + [Benchmark] + public object GetBetaUp() => h.GetBeta(ho, 20, BetaType.Up); + + [Benchmark] + public object GetBetaDown() => h.GetBeta(ho, 20, BetaType.Down); + + [Benchmark] + public object GetBetaAll() => h.GetBeta(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(); + + [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); + + [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); + + [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); + + [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(); +} diff --git a/tests/performance/Tests.Performance.csproj b/tests/performance/Tests.Performance.csproj index bd65b4df8..ec1a5c91b 100644 --- a/tests/performance/Tests.Performance.csproj +++ b/tests/performance/Tests.Performance.csproj @@ -14,7 +14,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive