Skip to content

Commit

Permalink
Issue #745 - The new OptionSnapshotRequest class now has a proper p…
Browse files Browse the repository at this point in the history
…agination support logic.
  • Loading branch information
OlegRa committed Apr 28, 2024
1 parent 238c834 commit b88bf9a
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 7 deletions.
12 changes: 10 additions & 2 deletions Alpaca.Markets.Tests/AlpacaOptionsDataClientTest.Snapshots.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public async Task ListSnapshotsAsyncWorks()
using var mock = mockClientsFactory.GetAlpacaOptionsDataClientMock();

// TODO: olegra - create special method for option snapshots
mock.AddCryptoSnapshotsExpectation(PathPrefix, _symbols);
mock.AddOptionSnapshotsExpectation(PathPrefix, _symbols);

var snapshots = await mock.Client.ListSnapshotsAsync(
new OptionSnapshotRequest(_symbols));
Expand Down Expand Up @@ -58,6 +58,14 @@ private static void validate(
Assert.True(snapshot.Trade.Validate(symbol));
Assert.True(snapshot.Quote.Validate(symbol));

// TODO: add validation for greeks and IV
Assert.NotNull(snapshot.ImpliedVolatility);
Assert.True(snapshot.ImpliedVolatility > 0.0M);

Assert.NotNull(snapshot.Greeks);
Assert.NotNull(snapshot.Greeks.Delta);
Assert.NotNull(snapshot.Greeks.Gamma);
Assert.NotNull(snapshot.Greeks.Theta);
Assert.NotNull(snapshot.Greeks.Vega);
Assert.NotNull(snapshot.Greeks.Rho);
}
}
27 changes: 26 additions & 1 deletion Alpaca.Markets.Tests/HistoricalDataHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Alpaca.Markets.Tests;
[SuppressMessage("ReSharper", "ParameterOnlyUsedForPreconditionCheck.Global")]
[SuppressMessage("ReSharper", "ParameterOnlyUsedForPreconditionCheck.Local")]
#pragma warning restore IDE0079 // Remove unnecessary suppression
[SuppressMessage("Usage", "xUnit1047:Avoid using TheoryDataRow arguments that might not be serializable")]
internal static class HistoricalDataHelpers
{
private static readonly String _condition = Guid.NewGuid().ToString("D");
Expand Down Expand Up @@ -34,6 +35,8 @@ internal static class HistoricalDataHelpers

private const Decimal Volume = 1_000M;

private const Decimal Greeks = 0.01M;

private const String Bars = "bars";

private const Decimal Size = 42M;
Expand Down Expand Up @@ -113,11 +116,17 @@ internal static void AddCryptoSnapshotsExpectation(
new JProperty("snapshots", new JObject(
symbols.Select(name => new JProperty(name, createSnapshot()))))));

internal static void AddOptionSnapshotsExpectation(
this IMock mock, String pathPrefix, IEnumerable<String> symbols) =>
mock.AddGet($"{pathPrefix}/snapshots", new JObject(
new JProperty("snapshots", new JObject(
symbols.Select(name => new JProperty(name, createOptionSnapshot()))))));

internal static void AddOptionChainExpectation(
this IMock mock, String pathPrefix, IEnumerable<String> symbols) =>
mock.AddGet($"{pathPrefix}/snapshots/*", new JObject(
new JProperty("snapshots", new JObject(
symbols.Select(name => new JProperty(name, createSnapshot()))))));
symbols.Select(name => new JProperty(name, createOptionSnapshot()))))));

internal static void AddOrderBooksExpectation(
this IMock mock, String pathPrefix, IEnumerable<String> symbols) =>
Expand Down Expand Up @@ -439,4 +448,20 @@ private static JObject createSnapshot(
new JProperty("minuteBar", CreateBar()),
new JProperty("dailyBar", CreateBar()),
new JProperty("symbol", symbol));

private static JObject createOptionSnapshot() =>
new(
new JProperty("latestQuote", CreateQuote()),
new JProperty("latestTrade", CreateTrade()),
new JProperty("impliedVolatility", Volume),
new JProperty("greeks", createGreeks()),
new JProperty("symbol", String.Empty));

private static JObject createGreeks() =>
new(
new JProperty("delta", Greeks),
new JProperty("gamma", Greeks),
new JProperty("theta", Greeks),
new JProperty("vega", Greeks),
new JProperty("rho", Greeks));
}
20 changes: 19 additions & 1 deletion Alpaca.Markets.Tests/RequestValidationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,24 @@ public void PortfolioHistoryRequestEmptySymbolValidationWorks() =>
}
.WithInterval(new Interval<DateTime>(DateTime.Today, DateTime.Today)));

[Fact]
public void OptionContactsRequestExpirationDateValidationWorks() =>
validate(new OptionContractsRequest
{
ExpirationDateGreaterThanOrEqualTo = DateOnly.FromDateTime(DateTime.Today),
ExpirationDateLessThanOrEqualTo = DateOnly.FromDateTime(DateTime.Today),
ExpirationDateEqualTo = DateOnly.FromDateTime(DateTime.Today),
});

[Fact]
public void OptionChainRequestExpirationDateValidationWorks() =>
validate(new OptionChainRequest(Symbol)
{
ExpirationDateGreaterThanOrEqualTo = DateOnly.FromDateTime(DateTime.Today),
ExpirationDateLessThanOrEqualTo = DateOnly.FromDateTime(DateTime.Today),
ExpirationDateEqualTo = DateOnly.FromDateTime(DateTime.Today),
});

[Fact]
public void CalendarRequestConstructorWorks()
{
Expand Down Expand Up @@ -134,7 +152,7 @@ public void ListOrdersRequestWithSymbolsWorks()
Assert.NotNull(request.Symbols);
Assert.Empty(request.Symbols);

request.WithSymbols(new[] { Symbol });
request.WithSymbols([Symbol]);

Assert.NotNull(request.Symbols);
Assert.NotEmpty(request.Symbols);
Expand Down
4 changes: 2 additions & 2 deletions Alpaca.Markets/AlpacaOptionsDataClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ public async Task<IDictionaryPage<IOptionSnapshot>> ListSnapshotsAsync(
OptionSnapshotRequest request,
CancellationToken cancellationToken = default) =>
await HttpClient.GetAsync<IDictionaryPage<IOptionSnapshot>, JsonOptionsSnapshotData>(
await request.GetUriBuilderAsync(HttpClient).ConfigureAwait(false),
await request.EnsureNotNull().Validate().GetUriBuilderAsync(HttpClient).ConfigureAwait(false),
RateLimitHandler, cancellationToken).ConfigureAwait(false);

public async Task<IDictionaryPage<IOptionSnapshot>> GetOptionChainAsync(
OptionChainRequest request,
CancellationToken cancellationToken = default) =>
await HttpClient.GetAsync<IDictionaryPage<IOptionSnapshot>, JsonOptionsSnapshotData>(
await request.GetUriBuilderAsync(HttpClient).ConfigureAwait(false),
await request.EnsureNotNull().Validate().GetUriBuilderAsync(HttpClient).ConfigureAwait(false),
RateLimitHandler, cancellationToken).ConfigureAwait(false);

private async Task<IReadOnlyDictionary<String, TApi>> getLatestAsync<TApi, TJson>(
Expand Down
8 changes: 7 additions & 1 deletion Alpaca.Markets/Parameters/OptionSnapshotRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,17 @@ public OptionSnapshotRequest(
[ExcludeFromCodeCoverage]
public OptionsFeed? OptionsFeed { get; set; }

/// <summary>
/// Gets the pagination parameters for the request (page size and token).
/// </summary>
[UsedImplicitly]
public Pagination Pagination { get; } = new();

internal async ValueTask<UriBuilder> GetUriBuilderAsync(
HttpClient httpClient) =>
new UriBuilder(httpClient.BaseAddress!)
{
Query = await new QueryBuilder()
Query = await Pagination.QueryBuilder
.AddParameter("symbols", Symbols.ToList())
.AddParameter("feed", OptionsFeed)
.AsStringAsync().ConfigureAwait(false)
Expand Down
1 change: 1 addition & 0 deletions Alpaca.Markets/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,7 @@ Alpaca.Markets.OptionSnapshotRequest
Alpaca.Markets.OptionSnapshotRequest.OptionsFeed.get -> Alpaca.Markets.OptionsFeed?
Alpaca.Markets.OptionSnapshotRequest.OptionsFeed.set -> void
Alpaca.Markets.OptionSnapshotRequest.OptionSnapshotRequest(System.Collections.Generic.IEnumerable<string!>! symbols) -> void
Alpaca.Markets.OptionSnapshotRequest.Pagination.get -> Alpaca.Markets.Pagination!
Alpaca.Markets.OptionSnapshotRequest.Symbols.get -> System.Collections.Generic.IReadOnlyCollection<string!>!
Alpaca.Markets.OptionStyle
Alpaca.Markets.OptionStyle.American = 0 -> Alpaca.Markets.OptionStyle
Expand Down

0 comments on commit b88bf9a

Please sign in to comment.