Skip to content

Commit

Permalink
Merge branch 'main' into async
Browse files Browse the repository at this point in the history
  • Loading branch information
DaveSkender authored Nov 24, 2023
2 parents ac93e9b + 256922d commit c68046d
Show file tree
Hide file tree
Showing 15 changed files with 242 additions and 30 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/build-indicators.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ jobs:
-warnAsError
- name: Test indicators
id: test-library
env:
ALPACA_KEY: ${{ secrets.ALPACA_KEY }}
ALPACA_SECRET: ${{ secrets.ALPACA_SECRET }}
run: >
dotnet test tests/indicators/Tests.Indicators.csproj
--configuration Release
Expand All @@ -49,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
Expand All @@ -59,7 +64,7 @@ jobs:
- name: Update tests summary
uses: bibipkins/[email protected]
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: ""
Expand Down
6 changes: 3 additions & 3 deletions docs/examples/Backtest/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ public static void Main()
*/

// fetch historical quotes from data provider
List<Quote> quotesList = GetQuotesFromFeed()
.ToList();
List<Quote> quotesList = [.. GetQuotesFromFeed()
];

// calculate Stochastic RSI
List<StochRsiResult> resultsList =
Expand Down Expand Up @@ -92,7 +92,7 @@ public static void Main()
}
}

private static IEnumerable<Quote> GetQuotesFromFeed()
private static Collection<Quote> GetQuotesFromFeed()
{
/************************************************************
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/ConsoleApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ with the same ordinal position.
}
}

private static IEnumerable<Quote> GetQuotesFromFeed()
private static Collection<Quote> GetQuotesFromFeed()
{
/************************************************************
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/CustomIndicatorsUsage/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public static void Main()
}
}

private static IEnumerable<Quote> GetQuotesFromFeed()
private static Collection<Quote> GetQuotesFromFeed()
{
/************************************************************
Expand Down
1 change: 0 additions & 1 deletion docs/examples/ObserveStream/ObserveStream.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
Expand Down
16 changes: 7 additions & 9 deletions docs/examples/ObserveStream/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ internal class Program
{
private static async Task Main(string[] args)
{
if (args.Any())
if (args.Length != 0)
{
Console.WriteLine(args);
}
Expand All @@ -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,
Expand All @@ -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<IBar> quoteSubscription
= client.GetMinuteBarSubscription(symbol);
Expand Down
Binary file modified docs/examples/Skender.Stock.Indicators-Examples.zip
Binary file not shown.
16 changes: 10 additions & 6 deletions docs/examples/UseQuoteApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,21 @@ or ICollection<Quote> 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
Expand Down
1 change: 0 additions & 1 deletion docs/examples/UseQuoteApi/UseQuoteApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions tests/indicators/Tests.Indicators.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Alpaca.Markets" Version="7.0.0-rc2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
Expand Down
75 changes: 75 additions & 0 deletions tests/indicators/_common/Helper.LiveQuotes.cs
Original file line number Diff line number Diff line change
@@ -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<IEnumerable<Quote>> GetQuotes(string symbol)
=> await GetQuotes(symbol, 365 * 2)
.ConfigureAwait(false);

internal static async Task<IEnumerable<Quote>> 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<IBar> barSet = await client
.ListHistoricalBarsAsync(request)
.ConfigureAwait(false);

// convert library compatible quotes
IEnumerable<Quote> 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;
}
}
67 changes: 65 additions & 2 deletions tests/indicators/s-z/Stoch/Stoch.Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ public void Standard() // Slow
int signalPeriods = 3;
int smoothPeriods = 3;

List<StochResult> results =
quotes.GetStoch(lookbackPeriods, signalPeriods, smoothPeriods)
List<StochResult> results = quotes
.GetStoch(lookbackPeriods, signalPeriods, smoothPeriods)
.ToList();

// proper quantities
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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<StochResult> 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()
{
Expand Down
Loading

0 comments on commit c68046d

Please sign in to comment.