From 9fd2940321505cd778824801f19203a12ecfcaf9 Mon Sep 17 00:00:00 2001 From: Oleg Rakhmatulin Date: Mon, 6 Jan 2025 00:56:30 +0100 Subject: [PATCH] Issue #740 - The helper function for reading all pages of historical corporate action data was added. --- .../Alpaca.Markets.Extensions.Tests.csproj | 4 +- .../Alpaca.Markets.Extensions.csproj | 12 +- .../Pagination/AlpacaDataClientExtensions.cs | 89 +++++++++++++ .../PublicAPI.Shipped.txt | 2 + Alpaca.Markets.Extensions/packages.lock.json | 126 ++++++++++-------- .../Alpaca.Markets.Tests.csproj | 4 +- 6 files changed, 169 insertions(+), 68 deletions(-) diff --git a/Alpaca.Markets.Extensions.Tests/Alpaca.Markets.Extensions.Tests.csproj b/Alpaca.Markets.Extensions.Tests/Alpaca.Markets.Extensions.Tests.csproj index 2fb682c7f..165778c55 100644 --- a/Alpaca.Markets.Extensions.Tests/Alpaca.Markets.Extensions.Tests.csproj +++ b/Alpaca.Markets.Extensions.Tests/Alpaca.Markets.Extensions.Tests.csproj @@ -16,12 +16,12 @@ - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Alpaca.Markets.Extensions/Alpaca.Markets.Extensions.csproj b/Alpaca.Markets.Extensions/Alpaca.Markets.Extensions.csproj index a228150de..40c593686 100644 --- a/Alpaca.Markets.Extensions/Alpaca.Markets.Extensions.csproj +++ b/Alpaca.Markets.Extensions/Alpaca.Markets.Extensions.csproj @@ -12,18 +12,18 @@ - 7.1.1.0 - 7.1.1.0 - 7.1.1 + 7.2.0.0 + 7.2.0.0 + 7.2.0-beta1 -- Service release - the dependencies upgraded to be more up-to-date with the latest fixes. +- The `AlpacaDataClientExtensions.ListCorporateActionsAsAsyncEnumerable` helper method was added to simplify corporate actions pagination handling. C# SDK for Alpaca Trade API https://docs.alpaca.markets/ https://github.com/alpacahq/alpaca-trade-api-csharp - © 2018-2024 Alpaca Securities LLC. All rights reserved. + © 2018-2025 Alpaca Securities LLC. All rights reserved. false Aplaca API SDK REST WebSocket trading Extansions for .NET SDK for Alpaca Trade API @@ -99,7 +99,7 @@ - + diff --git a/Alpaca.Markets.Extensions/Pagination/AlpacaDataClientExtensions.cs b/Alpaca.Markets.Extensions/Pagination/AlpacaDataClientExtensions.cs index fcdf140b4..455c8f5b2 100644 --- a/Alpaca.Markets.Extensions/Pagination/AlpacaDataClientExtensions.cs +++ b/Alpaca.Markets.Extensions/Pagination/AlpacaDataClientExtensions.cs @@ -423,6 +423,75 @@ public static IAsyncEnumerable + /// Gets all items provided by in pagination + /// mode as single stream of items (in form of interface) so they + /// can be consumed by the await foreach statement on the caller side. + /// + /// Target instance of the interface. + /// Original historical minute bars request (with empty next page token). + /// + /// The argument contains invalid data or some required data is missing, unable to create a valid HTTP request. + /// + /// + /// The request failed due to an underlying issue such as network connectivity, DNS failure, server certificate validation or timeout. + /// + /// + /// The response contains an error message or the received response cannot be deserialized properly due to JSON schema mismatch. + /// + /// + /// The initial TPC socket connection failed due to an underlying low-level network connectivity issue. + /// + /// + /// .NET Core and .NET 5 and later only: The request failed due to timeout. + /// + /// + /// The or argument is null. + /// + [UsedImplicitly] + [CLSCompliant(false)] + public static IAsyncEnumerable ListCorporateActionsAsAsyncEnumerable( + this IAlpacaDataClient client, + CorporateActionsRequest request) => + ListCorporateActionsAsAsyncEnumerable(client, request, CancellationToken.None); + + /// + /// Gets all items provided by in pagination + /// mode as single stream of items (in form of interface) so they + /// can be consumed by the await foreach statement on the caller side. + /// + /// Target instance of the interface. + /// Original historical minute bars request (with empty next page token). + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// The argument contains invalid data or some required data is missing, unable to create a valid HTTP request. + /// + /// + /// The request failed due to an underlying issue such as network connectivity, DNS failure, server certificate validation or timeout. + /// + /// + /// The response contains an error message or the received response cannot be deserialized properly due to JSON schema mismatch. + /// + /// + /// The initial TPC socket connection failed due to an underlying low-level network connectivity issue. + /// + /// + /// .NET Core and .NET 5 and later only: The request failed due to timeout. + /// + /// + /// + /// The or argument is null. + /// + [UsedImplicitly] + [CLSCompliant(false)] + public static IAsyncEnumerable ListCorporateActionsAsAsyncEnumerable( + this IAlpacaDataClient client, + CorporateActionsRequest request, + CancellationToken cancellationToken) => + getAllCorporateActionsPages( + client.EnsureNotNull(), getValidatedRequestWithoutPageToken(request.EnsureNotNull()), cancellationToken); + private static NewsArticlesRequest getValidatedRequestWithoutPageToken( IHistoricalRequest request) => request.GetValidatedRequestWithoutPageToken(); @@ -430,4 +499,24 @@ private static NewsArticlesRequest getValidatedRequestWithoutPageToken( private static HistoricalAuctionsRequest getValidatedRequestWithoutPageToken( IHistoricalRequest request) => request.GetValidatedRequestWithoutPageToken(); + + private static CorporateActionsRequest getValidatedRequestWithoutPageToken( + IHistoricalRequest request) => + request.GetValidatedRequestWithoutPageToken(); + + private static async IAsyncEnumerable getAllCorporateActionsPages( + IAlpacaDataClient client, + CorporateActionsRequest request, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + do + { + var page = await client.ListCorporateActionsAsync( + request, cancellationToken).ConfigureAwait(false); + + yield return page; + + request.Pagination.Token = page.NextPageToken ?? String.Empty; + } while (!String.IsNullOrEmpty(request.Pagination.Token)); + } } diff --git a/Alpaca.Markets.Extensions/PublicAPI.Shipped.txt b/Alpaca.Markets.Extensions/PublicAPI.Shipped.txt index 393b01cbf..480646153 100644 --- a/Alpaca.Markets.Extensions/PublicAPI.Shipped.txt +++ b/Alpaca.Markets.Extensions/PublicAPI.Shipped.txt @@ -51,6 +51,8 @@ static Alpaca.Markets.Extensions.AlpacaDataClientExtensions.GetNewsArticlesAsAsy static Alpaca.Markets.Extensions.AlpacaDataClientExtensions.GetNewsArticlesAsAsyncEnumerable(this Alpaca.Markets.IAlpacaDataClient! client, Alpaca.Markets.NewsArticlesRequest! request, System.Threading.CancellationToken cancellationToken) -> System.Collections.Generic.IAsyncEnumerable! static Alpaca.Markets.Extensions.AlpacaDataClientExtensions.GetNewsArticlesPagesAsAsyncEnumerable(this Alpaca.Markets.IAlpacaDataClient! client, Alpaca.Markets.NewsArticlesRequest! request) -> System.Collections.Generic.IAsyncEnumerable!>! static Alpaca.Markets.Extensions.AlpacaDataClientExtensions.GetNewsArticlesPagesAsAsyncEnumerable(this Alpaca.Markets.IAlpacaDataClient! client, Alpaca.Markets.NewsArticlesRequest! request, System.Threading.CancellationToken cancellationToken) -> System.Collections.Generic.IAsyncEnumerable!>! +static Alpaca.Markets.Extensions.AlpacaDataClientExtensions.ListCorporateActionsAsAsyncEnumerable(this Alpaca.Markets.IAlpacaDataClient! client, Alpaca.Markets.CorporateActionsRequest! request) -> System.Collections.Generic.IAsyncEnumerable! +static Alpaca.Markets.Extensions.AlpacaDataClientExtensions.ListCorporateActionsAsAsyncEnumerable(this Alpaca.Markets.IAlpacaDataClient! client, Alpaca.Markets.CorporateActionsRequest! request, System.Threading.CancellationToken cancellationToken) -> System.Collections.Generic.IAsyncEnumerable! static Alpaca.Markets.Extensions.AlpacaDataStreamingClientExtensions.GetCancellationSubscription(this Alpaca.Markets.IAlpacaDataStreamingClient! client, params string![]! symbols) -> Alpaca.Markets.IAlpacaDataSubscription! static Alpaca.Markets.Extensions.AlpacaDataStreamingClientExtensions.GetCancellationSubscription(this Alpaca.Markets.IAlpacaDataStreamingClient! client, System.Collections.Generic.IEnumerable! symbols) -> Alpaca.Markets.IAlpacaDataSubscription! static Alpaca.Markets.Extensions.AlpacaDataStreamingClientExtensions.GetCorrectionSubscription(this Alpaca.Markets.IAlpacaDataStreamingClient! client, params string![]! symbols) -> Alpaca.Markets.IAlpacaDataSubscription! diff --git a/Alpaca.Markets.Extensions/packages.lock.json b/Alpaca.Markets.Extensions/packages.lock.json index b7dac4873..7ac1ac618 100644 --- a/Alpaca.Markets.Extensions/packages.lock.json +++ b/Alpaca.Markets.Extensions/packages.lock.json @@ -4,16 +4,16 @@ ".NETFramework,Version=v4.6.2": { "Alpaca.Markets": { "type": "Direct", - "requested": "[7.1.0, )", - "resolved": "7.1.0", - "contentHash": "NDWBglvCoR7iKkfK3/8ka6OoDtuVpi8j7uzeJhODmVCCtZzw4bmQO1G2w63OcbBqHLupKEVXHkcTowtBF7wsFg==", + "requested": "[7.2.0-beta1, )", + "resolved": "7.2.0-beta1", + "contentHash": "JI2IRQc+s05y8mGAgPGXL3yU++L9rhOUxsAmlSvwqQ/Fln5reKrh/Prf4GmSglM+Psiw7ur2cC7t0y2yLfhS0g==", "dependencies": { "Newtonsoft.Json": "13.0.3", - "Polly": "8.3.1", - "Portable.System.DateTimeOnly": "8.0.1", - "System.IO.Pipelines": "8.0.0", - "System.Net.Http.WinHttpHandler": "8.0.0", - "System.Threading.Channels": "8.0.0" + "Polly": "8.5.0", + "Portable.System.DateTimeOnly": "8.0.2", + "System.IO.Pipelines": "9.0.0", + "System.Net.Http.WinHttpHandler": "9.0.0", + "System.Threading.Channels": "9.0.0" } }, "IsExternalInit": { @@ -215,16 +215,16 @@ }, "Polly": { "type": "Transitive", - "resolved": "8.3.1", - "contentHash": "xN9AxOudus8u4a//Tfu6Wxd5Oj7S4pjh/651S6FfIiVrbxQ8TeM+ieZC8c0y7Qto70zKBM5g8rBTqbAJnbetOA==", + "resolved": "8.5.0", + "contentHash": "GBNZPy7i7OpkaIruWPRJ0+AWzdGDQDnKY91b7Ic2aAch4lKhPjUc5KSffpH9krIWe0MoyghqaRxwRC0Uwz2PkA==", "dependencies": { - "Polly.Core": "8.3.1" + "Polly.Core": "8.5.0" } }, "Polly.Core": { "type": "Transitive", - "resolved": "8.3.1", - "contentHash": "b9mMAjqXuCXFCtf/RtRPwFUkAm/sz37s7G+taDvS5EqfkJsLiWfO2xO2cSISjIgIY+88oIZTGguP6UrRGRlqzg==", + "resolved": "8.5.0", + "contentHash": "VYYMZNitZ85UEhwOKkTQI63WEMvzUqwQc74I2mm8h/DBVAMcBBxqYPni4DmuRtbCwngmuONuK2yBJfWNRKzI+A==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "6.0.0", "Microsoft.Bcl.TimeProvider": "8.0.0", @@ -262,8 +262,8 @@ }, "System.IO.Pipelines": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA==", + "resolved": "9.0.0", + "contentHash": "eA3cinogwaNB4jdjQHOP3Z3EuyiDII7MT35jgtnsA4vkn0LUrrSHsU0nzHTzFzmaFYeKV7MYyMxOocFzsBHpTw==", "dependencies": { "System.Buffers": "4.5.1", "System.Memory": "4.5.5", @@ -282,8 +282,8 @@ }, "System.Net.Http.WinHttpHandler": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "dAtcyQzDpi34VdR1BeEV8yCOeXVEyekYYK6lJZIzG/N5aqEGgT6AB2DsbiidMp8cB6Y7DqqcmQFZaSGUdoubvQ==", + "resolved": "9.0.0", + "contentHash": "H7UZoctQ2xFQgU6eGFRavMDJQGebLaqG9OqWg5dm3WFpPJ8mhdTDcJhz/ihxzUZm1I+wiykzp+tu3hYtQ0omMQ==", "dependencies": { "System.Buffers": "4.5.1", "System.Memory": "4.5.5" @@ -316,15 +316,15 @@ ".NETStandard,Version=v2.0": { "Alpaca.Markets": { "type": "Direct", - "requested": "[7.1.0, )", - "resolved": "7.1.0", - "contentHash": "NDWBglvCoR7iKkfK3/8ka6OoDtuVpi8j7uzeJhODmVCCtZzw4bmQO1G2w63OcbBqHLupKEVXHkcTowtBF7wsFg==", + "requested": "[7.2.0-beta1, )", + "resolved": "7.2.0-beta1", + "contentHash": "JI2IRQc+s05y8mGAgPGXL3yU++L9rhOUxsAmlSvwqQ/Fln5reKrh/Prf4GmSglM+Psiw7ur2cC7t0y2yLfhS0g==", "dependencies": { "Newtonsoft.Json": "13.0.3", - "Polly": "8.3.1", - "Portable.System.DateTimeOnly": "8.0.1", - "System.IO.Pipelines": "8.0.0", - "System.Threading.Channels": "8.0.0" + "Polly": "8.5.0", + "Portable.System.DateTimeOnly": "8.0.2", + "System.IO.Pipelines": "9.0.0", + "System.Threading.Channels": "9.0.0" } }, "IsExternalInit": { @@ -523,16 +523,16 @@ }, "Polly": { "type": "Transitive", - "resolved": "8.3.1", - "contentHash": "xN9AxOudus8u4a//Tfu6Wxd5Oj7S4pjh/651S6FfIiVrbxQ8TeM+ieZC8c0y7Qto70zKBM5g8rBTqbAJnbetOA==", + "resolved": "8.5.0", + "contentHash": "GBNZPy7i7OpkaIruWPRJ0+AWzdGDQDnKY91b7Ic2aAch4lKhPjUc5KSffpH9krIWe0MoyghqaRxwRC0Uwz2PkA==", "dependencies": { - "Polly.Core": "8.3.1" + "Polly.Core": "8.5.0" } }, "Polly.Core": { "type": "Transitive", - "resolved": "8.3.1", - "contentHash": "b9mMAjqXuCXFCtf/RtRPwFUkAm/sz37s7G+taDvS5EqfkJsLiWfO2xO2cSISjIgIY+88oIZTGguP6UrRGRlqzg==", + "resolved": "8.5.0", + "contentHash": "VYYMZNitZ85UEhwOKkTQI63WEMvzUqwQc74I2mm8h/DBVAMcBBxqYPni4DmuRtbCwngmuONuK2yBJfWNRKzI+A==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "6.0.0", "Microsoft.Bcl.TimeProvider": "8.0.0", @@ -569,8 +569,8 @@ }, "System.IO.Pipelines": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA==", + "resolved": "9.0.0", + "contentHash": "eA3cinogwaNB4jdjQHOP3Z3EuyiDII7MT35jgtnsA4vkn0LUrrSHsU0nzHTzFzmaFYeKV7MYyMxOocFzsBHpTw==", "dependencies": { "System.Buffers": "4.5.1", "System.Memory": "4.5.5", @@ -609,15 +609,15 @@ ".NETStandard,Version=v2.1": { "Alpaca.Markets": { "type": "Direct", - "requested": "[7.1.0, )", - "resolved": "7.1.0", - "contentHash": "NDWBglvCoR7iKkfK3/8ka6OoDtuVpi8j7uzeJhODmVCCtZzw4bmQO1G2w63OcbBqHLupKEVXHkcTowtBF7wsFg==", + "requested": "[7.2.0-beta1, )", + "resolved": "7.2.0-beta1", + "contentHash": "JI2IRQc+s05y8mGAgPGXL3yU++L9rhOUxsAmlSvwqQ/Fln5reKrh/Prf4GmSglM+Psiw7ur2cC7t0y2yLfhS0g==", "dependencies": { "Newtonsoft.Json": "13.0.3", - "Polly": "8.3.1", - "Portable.System.DateTimeOnly": "8.0.1", - "System.IO.Pipelines": "8.0.0", - "System.Threading.Channels": "8.0.0" + "Polly": "8.5.0", + "Portable.System.DateTimeOnly": "8.0.2", + "System.IO.Pipelines": "9.0.0", + "System.Threading.Channels": "9.0.0" } }, "IsExternalInit": { @@ -782,16 +782,16 @@ }, "Polly": { "type": "Transitive", - "resolved": "8.3.1", - "contentHash": "xN9AxOudus8u4a//Tfu6Wxd5Oj7S4pjh/651S6FfIiVrbxQ8TeM+ieZC8c0y7Qto70zKBM5g8rBTqbAJnbetOA==", + "resolved": "8.5.0", + "contentHash": "GBNZPy7i7OpkaIruWPRJ0+AWzdGDQDnKY91b7Ic2aAch4lKhPjUc5KSffpH9krIWe0MoyghqaRxwRC0Uwz2PkA==", "dependencies": { - "Polly.Core": "8.3.1" + "Polly.Core": "8.5.0" } }, "Polly.Core": { "type": "Transitive", - "resolved": "8.3.1", - "contentHash": "b9mMAjqXuCXFCtf/RtRPwFUkAm/sz37s7G+taDvS5EqfkJsLiWfO2xO2cSISjIgIY+88oIZTGguP6UrRGRlqzg==", + "resolved": "8.5.0", + "contentHash": "VYYMZNitZ85UEhwOKkTQI63WEMvzUqwQc74I2mm8h/DBVAMcBBxqYPni4DmuRtbCwngmuONuK2yBJfWNRKzI+A==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "6.0.0", "Microsoft.Bcl.TimeProvider": "8.0.0", @@ -828,8 +828,8 @@ }, "System.IO.Pipelines": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA==", + "resolved": "9.0.0", + "contentHash": "eA3cinogwaNB4jdjQHOP3Z3EuyiDII7MT35jgtnsA4vkn0LUrrSHsU0nzHTzFzmaFYeKV7MYyMxOocFzsBHpTw==", "dependencies": { "System.Buffers": "4.5.1", "System.Memory": "4.5.5", @@ -868,14 +868,14 @@ "net6.0": { "Alpaca.Markets": { "type": "Direct", - "requested": "[7.1.0, )", - "resolved": "7.1.0", - "contentHash": "NDWBglvCoR7iKkfK3/8ka6OoDtuVpi8j7uzeJhODmVCCtZzw4bmQO1G2w63OcbBqHLupKEVXHkcTowtBF7wsFg==", + "requested": "[7.2.0-beta1, )", + "resolved": "7.2.0-beta1", + "contentHash": "JI2IRQc+s05y8mGAgPGXL3yU++L9rhOUxsAmlSvwqQ/Fln5reKrh/Prf4GmSglM+Psiw7ur2cC7t0y2yLfhS0g==", "dependencies": { "Newtonsoft.Json": "13.0.3", - "Polly": "8.3.1", - "System.IO.Pipelines": "8.0.0", - "System.Threading.Channels": "8.0.0" + "Polly": "8.5.0", + "System.IO.Pipelines": "9.0.0", + "System.Threading.Channels": "9.0.0" } }, "IsExternalInit": { @@ -1049,16 +1049,16 @@ }, "Polly": { "type": "Transitive", - "resolved": "8.3.1", - "contentHash": "xN9AxOudus8u4a//Tfu6Wxd5Oj7S4pjh/651S6FfIiVrbxQ8TeM+ieZC8c0y7Qto70zKBM5g8rBTqbAJnbetOA==", + "resolved": "8.5.0", + "contentHash": "GBNZPy7i7OpkaIruWPRJ0+AWzdGDQDnKY91b7Ic2aAch4lKhPjUc5KSffpH9krIWe0MoyghqaRxwRC0Uwz2PkA==", "dependencies": { - "Polly.Core": "8.3.1" + "Polly.Core": "8.5.0" } }, "Polly.Core": { "type": "Transitive", - "resolved": "8.3.1", - "contentHash": "b9mMAjqXuCXFCtf/RtRPwFUkAm/sz37s7G+taDvS5EqfkJsLiWfO2xO2cSISjIgIY+88oIZTGguP6UrRGRlqzg==", + "resolved": "8.5.0", + "contentHash": "VYYMZNitZ85UEhwOKkTQI63WEMvzUqwQc74I2mm8h/DBVAMcBBxqYPni4DmuRtbCwngmuONuK2yBJfWNRKzI+A==", "dependencies": { "Microsoft.Bcl.TimeProvider": "8.0.0" } @@ -1092,8 +1092,13 @@ }, "System.IO.Pipelines": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA==" + "resolved": "9.0.0", + "contentHash": "eA3cinogwaNB4jdjQHOP3Z3EuyiDII7MT35jgtnsA4vkn0LUrrSHsU0nzHTzFzmaFYeKV7MYyMxOocFzsBHpTw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5", + "System.Threading.Tasks.Extensions": "4.5.4" + } }, "System.Memory": { "type": "Transitive", @@ -1104,6 +1109,11 @@ "type": "Transitive", "resolved": "6.0.0", "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" } } } diff --git a/Alpaca.Markets.Tests/Alpaca.Markets.Tests.csproj b/Alpaca.Markets.Tests/Alpaca.Markets.Tests.csproj index f43644505..d1ddf62b6 100644 --- a/Alpaca.Markets.Tests/Alpaca.Markets.Tests.csproj +++ b/Alpaca.Markets.Tests/Alpaca.Markets.Tests.csproj @@ -15,11 +15,11 @@ - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all