From 36ce7a3a0c4c777aa9d9afe8def4559e649050bd Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Wed, 15 Nov 2023 18:16:13 -0500 Subject: [PATCH 1/3] chore: refactor examples for C# v12 (#1128) --- docs/examples/Backtest/Program.cs | 6 +++--- docs/examples/ConsoleApp/Program.cs | 2 +- .../CustomIndicatorsLibrary.csproj | 1 - .../CustomIndicatorsUsage.csproj | 1 - .../examples/CustomIndicatorsUsage/Program.cs | 2 +- .../ObserveStream/ObserveStream.csproj | 1 - docs/examples/ObserveStream/Program.cs | 16 +++++++--------- .../Skender.Stock.Indicators-Examples.zip | Bin 153710 -> 153769 bytes docs/examples/UseQuoteApi/Program.cs | 16 ++++++++++------ docs/examples/UseQuoteApi/UseQuoteApi.csproj | 1 - 10 files changed, 22 insertions(+), 24 deletions(-) diff --git a/docs/examples/Backtest/Program.cs b/docs/examples/Backtest/Program.cs index 71fe1314b..0f5849ed6 100644 --- a/docs/examples/Backtest/Program.cs +++ b/docs/examples/Backtest/Program.cs @@ -23,8 +23,8 @@ public static void Main() */ // fetch historical quotes from data provider - List quotesList = GetQuotesFromFeed() - .ToList(); + List quotesList = [.. GetQuotesFromFeed() +]; // calculate Stochastic RSI List resultsList = @@ -92,7 +92,7 @@ public static void Main() } } - private static IEnumerable GetQuotesFromFeed() + private static Collection GetQuotesFromFeed() { /************************************************************ diff --git a/docs/examples/ConsoleApp/Program.cs b/docs/examples/ConsoleApp/Program.cs index 88cc004fd..652ee5e7f 100644 --- a/docs/examples/ConsoleApp/Program.cs +++ b/docs/examples/ConsoleApp/Program.cs @@ -67,7 +67,7 @@ with the same ordinal position. } } - private static IEnumerable GetQuotesFromFeed() + private static Collection GetQuotesFromFeed() { /************************************************************ diff --git a/docs/examples/CustomIndicators/CustomIndicatorsLibrary.csproj b/docs/examples/CustomIndicators/CustomIndicatorsLibrary.csproj index 3bcf14387..a05805299 100644 --- a/docs/examples/CustomIndicators/CustomIndicatorsLibrary.csproj +++ b/docs/examples/CustomIndicators/CustomIndicatorsLibrary.csproj @@ -3,7 +3,6 @@ net8.0 enable - enable diff --git a/docs/examples/CustomIndicatorsUsage/CustomIndicatorsUsage.csproj b/docs/examples/CustomIndicatorsUsage/CustomIndicatorsUsage.csproj index 3c1fd397b..391b7239a 100644 --- a/docs/examples/CustomIndicatorsUsage/CustomIndicatorsUsage.csproj +++ b/docs/examples/CustomIndicatorsUsage/CustomIndicatorsUsage.csproj @@ -4,7 +4,6 @@ Exe net8.0 enable - disable diff --git a/docs/examples/CustomIndicatorsUsage/Program.cs b/docs/examples/CustomIndicatorsUsage/Program.cs index df6dbee10..9246a8900 100644 --- a/docs/examples/CustomIndicatorsUsage/Program.cs +++ b/docs/examples/CustomIndicatorsUsage/Program.cs @@ -47,7 +47,7 @@ public static void Main() } } - private static IEnumerable GetQuotesFromFeed() + private static Collection GetQuotesFromFeed() { /************************************************************ diff --git a/docs/examples/ObserveStream/ObserveStream.csproj b/docs/examples/ObserveStream/ObserveStream.csproj index c4c176fc3..8d1a47c95 100644 --- a/docs/examples/ObserveStream/ObserveStream.csproj +++ b/docs/examples/ObserveStream/ObserveStream.csproj @@ -4,7 +4,6 @@ Exe net8.0 enable - enable diff --git a/docs/examples/ObserveStream/Program.cs b/docs/examples/ObserveStream/Program.cs index 1c7dd0f6c..738ac16f7 100644 --- a/docs/examples/ObserveStream/Program.cs +++ b/docs/examples/ObserveStream/Program.cs @@ -7,7 +7,7 @@ internal class Program { private static async Task Main(string[] args) { - if (args.Any()) + if (args.Length != 0) { Console.WriteLine(args); } @@ -22,17 +22,17 @@ private static async Task Main(string[] args) public static async Task SubscribeToQuotes(string symbol) { // get and validate keys, see README.md - string? alpacaApiKey = Environment.GetEnvironmentVariable("AlpacaApiKey"); - string? alpacaSecret = Environment.GetEnvironmentVariable("AlpacaSecret"); + string alpacaApiKey = Environment.GetEnvironmentVariable("AlpacaApiKey"); + string alpacaSecret = Environment.GetEnvironmentVariable("AlpacaSecret"); - if (alpacaApiKey == null) + if (string.IsNullOrEmpty(alpacaApiKey)) { throw new ArgumentNullException( alpacaApiKey, $"API KEY missing, use `setx AlpacaApiKey \"ALPACA_API_KEY\"` to set."); } - if (alpacaSecret == null) + if (string.IsNullOrEmpty(alpacaSecret)) { throw new ArgumentNullException( alpacaSecret, @@ -58,10 +58,8 @@ IAlpacaCryptoStreamingClient client await client.ConnectAndAuthenticateAsync(); - AutoResetEvent[] waitObjects = new[] // TODO: is this needed? - { - new AutoResetEvent(false) - }; + // TODO: is this needed? + AutoResetEvent[] waitObjects = [new AutoResetEvent(false)]; IAlpacaDataSubscription quoteSubscription = client.GetMinuteBarSubscription(symbol); diff --git a/docs/examples/Skender.Stock.Indicators-Examples.zip b/docs/examples/Skender.Stock.Indicators-Examples.zip index 9945eafc49dce494b612eef87e9a6d8addae9ecd..b56a3050c984d1630fd0726154419f30684489d2 100644 GIT binary patch delta 9039 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 or other IEnumerable compatible types. ************************************************************/ // get and validate keys, see README.md - string? alpacaApiKey = Environment.GetEnvironmentVariable("AlpacaApiKey"); - string? alpacaSecret = Environment.GetEnvironmentVariable("AlpacaSecret"); + string alpacaApiKey = Environment.GetEnvironmentVariable("AlpacaApiKey"); + string alpacaSecret = Environment.GetEnvironmentVariable("AlpacaSecret"); - if (alpacaApiKey == null) + if (string.IsNullOrEmpty(alpacaApiKey)) { - throw new ArgumentNullException(alpacaApiKey); + throw new ArgumentNullException( + alpacaApiKey, + $"API KEY missing, use `setx AlpacaApiKey \"ALPACA_API_KEY\"` to set."); } - if (alpacaSecret == null) + if (string.IsNullOrEmpty(alpacaSecret)) { - throw new ArgumentNullException(alpacaSecret); + throw new ArgumentNullException( + alpacaSecret, + $"API SECRET missing, use `setx AlpacaApiSecret \"ALPACA_SECRET\"` to set."); } // connect to Alpaca REST API diff --git a/docs/examples/UseQuoteApi/UseQuoteApi.csproj b/docs/examples/UseQuoteApi/UseQuoteApi.csproj index a1e4f8a38..cda3d5aec 100644 --- a/docs/examples/UseQuoteApi/UseQuoteApi.csproj +++ b/docs/examples/UseQuoteApi/UseQuoteApi.csproj @@ -4,7 +4,6 @@ Exe net8.0 enable - enable From 82e750139d350e22368870642c647cb9030a12e6 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Thu, 16 Nov 2023 00:16:11 -0500 Subject: [PATCH 2/3] tests for Williams %R, Stochastic boundary (#1129) --- .github/workflows/build-indicators.yml | 3 + tests/indicators/Tests.Indicators.csproj | 1 + tests/indicators/_common/Helper.LiveQuotes.cs | 75 ++++++++++++++++++ tests/indicators/s-z/Stoch/Stoch.Tests.cs | 67 +++++++++++++++- .../s-z/WilliamsR/WilliamsR.Tests.cs | 76 ++++++++++++++++++- 5 files changed, 217 insertions(+), 5 deletions(-) create mode 100644 tests/indicators/_common/Helper.LiveQuotes.cs diff --git a/.github/workflows/build-indicators.yml b/.github/workflows/build-indicators.yml index f33ae1c78..c300c8cc0 100644 --- a/.github/workflows/build-indicators.yml +++ b/.github/workflows/build-indicators.yml @@ -39,6 +39,9 @@ jobs: -warnAsError - name: Test indicators + env: + ALPACA_KEY: ${{ secrets.ALPACA_KEY }} + ALPACA_SECRET: ${{ secrets.ALPACA_SECRET }} run: > dotnet test tests/indicators/Tests.Indicators.csproj --configuration Release diff --git a/tests/indicators/Tests.Indicators.csproj b/tests/indicators/Tests.Indicators.csproj index 849fdbc73..64012bb63 100644 --- a/tests/indicators/Tests.Indicators.csproj +++ b/tests/indicators/Tests.Indicators.csproj @@ -14,6 +14,7 @@ + diff --git a/tests/indicators/_common/Helper.LiveQuotes.cs b/tests/indicators/_common/Helper.LiveQuotes.cs new file mode 100644 index 000000000..37bab744a --- /dev/null +++ b/tests/indicators/_common/Helper.LiveQuotes.cs @@ -0,0 +1,75 @@ +using Alpaca.Markets; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Skender.Stock.Indicators; + +namespace Tests.Indicators; + +internal class FeedData +{ + internal static async Task> GetQuotes(string symbol) + => await GetQuotes(symbol, 365 * 2) + .ConfigureAwait(false); + + internal static async Task> GetQuotes(string symbol, int days) + { + /* This won't run if environment variables not set. + Use FeedData.InconclusiveIfNotSetup() in tests. + + (1) get your API keys + https://alpaca.markets/docs/market-data/getting-started/ + + (2) manually install in your environment (replace value) + + setx ALPACA_KEY "y0ur_Alp@ca_K3Y_v@lue" + setx ALPACA_SECRET "y0ur_Alp@ca_S3cret_v@lue" + + ****************************************************/ + + // get and validate keys + string alpacaApiKey = Environment.GetEnvironmentVariable("ALPACA_KEY"); + string alpacaSecret = Environment.GetEnvironmentVariable("ALPACA_SECRET"); + + if (string.IsNullOrEmpty(alpacaApiKey) || string.IsNullOrEmpty(alpacaSecret)) + { + Assert.Inconclusive("Data feed unusable. Environment variables missing."); + } + + ArgumentException.ThrowIfNullOrEmpty(nameof(alpacaApiKey)); + ArgumentException.ThrowIfNullOrEmpty(nameof(alpacaSecret)); + + // connect to Alpaca REST API + SecretKey secretKey = new(alpacaApiKey, alpacaSecret); + + IAlpacaDataClient client = Environments + .Paper + .GetAlpacaDataClient(secretKey); + + // compose request + // (excludes last 15 minutes for free delayed quotes) + DateTime into = DateTime.Now.Subtract(TimeSpan.FromMinutes(16)); + DateTime from = into.Subtract(TimeSpan.FromDays(days)); + + HistoricalBarsRequest request = new(symbol, from, into, BarTimeFrame.Day); + + // fetch minute-bar quotes in Alpaca's format + IPage barSet = await client + .ListHistoricalBarsAsync(request) + .ConfigureAwait(false); + + // convert library compatible quotes + IEnumerable quotes = barSet + .Items + .Select(bar => new Quote + { + Date = bar.TimeUtc, + Open = bar.Open, + High = bar.High, + Low = bar.Low, + Close = bar.Close, + Volume = bar.Volume + }) + .OrderBy(x => x.Date); + + return quotes; + } +} diff --git a/tests/indicators/s-z/Stoch/Stoch.Tests.cs b/tests/indicators/s-z/Stoch/Stoch.Tests.cs index f62679fa9..11d9c9c5d 100644 --- a/tests/indicators/s-z/Stoch/Stoch.Tests.cs +++ b/tests/indicators/s-z/Stoch/Stoch.Tests.cs @@ -14,8 +14,8 @@ public void Standard() // Slow int signalPeriods = 3; int smoothPeriods = 3; - List results = - quotes.GetStoch(lookbackPeriods, signalPeriods, smoothPeriods) + List results = quotes + .GetStoch(lookbackPeriods, signalPeriods, smoothPeriods) .ToList(); // proper quantities @@ -48,6 +48,31 @@ public void Standard() // Slow Assert.AreEqual(43.1353, r501.Oscillator.Round(4)); Assert.AreEqual(35.5674, r501.Signal.Round(4)); Assert.AreEqual(58.2712, r501.PercentJ.Round(4)); + + // test boundary condition + + for (int i = 0; i < results.Count; i++) + { + StochResult r = results[i]; + + if (r.Oscillator is not null) + { + Assert.IsTrue(r.Oscillator >= 0); + Assert.IsTrue(r.Oscillator <= 100); + } + + if (r.Signal is not null) + { + Assert.IsTrue(r.Signal >= 0); + Assert.IsTrue(r.Signal <= 100); + } + + if (r.PercentJ is not null) + { + Assert.IsTrue(r.Signal >= 0); + Assert.IsTrue(r.Signal <= 100); + } + } } [TestMethod] @@ -213,6 +238,44 @@ public void Removed() Assert.AreEqual(58.2712, last.PercentJ.Round(4)); } + [TestMethod] + public void Boundary() + { + int lookbackPeriods = 14; + int signalPeriods = 3; + int smoothPeriods = 3; + + List results = TestData + .GetRandom(2500) + .GetStoch(lookbackPeriods, signalPeriods, smoothPeriods) + .ToList(); + + // test boundary condition + + for (int i = 0; i < results.Count; i++) + { + StochResult r = results[i]; + + if (r.Oscillator is not null) + { + Assert.IsTrue(r.Oscillator >= 0); + Assert.IsTrue(r.Oscillator <= 100); + } + + if (r.Signal is not null) + { + Assert.IsTrue(r.Signal >= 0); + Assert.IsTrue(r.Signal <= 100); + } + + if (r.PercentJ is not null) + { + Assert.IsTrue(r.Signal >= 0); + Assert.IsTrue(r.Signal <= 100); + } + } + } + [TestMethod] public void Exceptions() { diff --git a/tests/indicators/s-z/WilliamsR/WilliamsR.Tests.cs b/tests/indicators/s-z/WilliamsR/WilliamsR.Tests.cs index 42011fc82..944a6aa44 100644 --- a/tests/indicators/s-z/WilliamsR/WilliamsR.Tests.cs +++ b/tests/indicators/s-z/WilliamsR/WilliamsR.Tests.cs @@ -24,6 +24,18 @@ public void Standard() WilliamsResult r2 = results[501]; Assert.AreEqual(-52.0121, r2.WilliamsR.Round(4)); + + // test boundary condition + for (int i = 0; i < results.Count; i++) + { + WilliamsResult r = results[i]; + + if (r.WilliamsR is not null) + { + Assert.IsTrue(r.WilliamsR <= 0); + Assert.IsTrue(r.WilliamsR >= -100); + } + } } [TestMethod] @@ -41,12 +53,15 @@ public void Chainor() [TestMethod] public void BadData() { - List r = badQuotes + List quotes = badQuotes + .ToSortedList(); + + List results = badQuotes .GetWilliamsR(20) .ToList(); - Assert.AreEqual(502, r.Count); - Assert.AreEqual(0, r.Count(x => x.WilliamsR is double and double.NaN)); + Assert.AreEqual(502, results.Count); + Assert.AreEqual(0, results.Count(x => x.WilliamsR is double and double.NaN)); } [TestMethod] @@ -80,6 +95,61 @@ public void Removed() Assert.AreEqual(-52.0121, last.WilliamsR.Round(4)); } + [TestMethod] + public void Boundary() + { + List results = TestData + .GetRandom(2500) + .GetWilliamsR(14) + .ToList(); + + // analyze boundary + for (int i = 0; i < results.Count; i++) + { + WilliamsResult r = results[i]; + + if (r.WilliamsR is not null) + { + Assert.IsTrue(r.WilliamsR <= 0); + Assert.IsTrue(r.WilliamsR >= -100); + } + } + } + + [TestMethod] + public async Task Issue1127() + { + // initialize + IEnumerable quotes = await FeedData + .GetQuotes("A", 365 * 3) + .ConfigureAwait(false); + + List quotesList = quotes.ToList(); + int length = quotesList.Count; + + // get indicators + List resultsList = quotes + .GetWilliamsR(14) + .ToList(); + + Console.WriteLine($"%R from {length} quotes."); + + // analyze boundary + for (int i = 0; i < length; i++) + { + Quote q = quotesList[i]; + WilliamsResult r = resultsList[i]; + + Console.WriteLine($"{q.Date:s} {r.WilliamsR}"); + + if (r.WilliamsR is not null) + { + Assert.IsTrue(r.WilliamsR <= 0); + Assert.IsTrue(r.WilliamsR >= -100); + } + } + } + // bad lookback period [TestMethod] public void Exceptions() From 256922d0a94df243b522699de218e605c8eeb70f Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Sat, 18 Nov 2023 14:59:09 -0500 Subject: [PATCH 3/3] build: always run test reporter (#1130) --- .github/workflows/build-indicators.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-indicators.yml b/.github/workflows/build-indicators.yml index c300c8cc0..a763d4cde 100644 --- a/.github/workflows/build-indicators.yml +++ b/.github/workflows/build-indicators.yml @@ -39,6 +39,7 @@ jobs: -warnAsError - name: Test indicators + id: test-library env: ALPACA_KEY: ${{ secrets.ALPACA_KEY }} ALPACA_SECRET: ${{ secrets.ALPACA_SECRET }} @@ -52,6 +53,7 @@ jobs: --results-directory ./test-indicators - name: Test other items + id: test-other run: > dotnet test tests/other/Tests.Other.csproj --configuration Release @@ -62,7 +64,7 @@ jobs: - name: Update tests summary uses: bibipkins/dotnet-test-reporter@v1.3.3 - if: github.event_name == 'pull_request' + if: ${{ github.event_name == 'pull_request' && (success() || (failure() && (steps.test-library.conclusion == 'failure' || steps.test-other.conclusion == 'failure'))) }} with: github-token: ${{ secrets.GITHUB_TOKEN }} comment-title: ""