Skip to content

Commit 0d0d8fd

Browse files
committed
Add market data classes and update Deribit client
Added new classes for market data retrieval and handling. These classes include InstrumentKind, InstrumentResponse, InstrumentType, and TickSizeStep. Updated the DeribitSubscriptionClient to allow unsubscribing from channels. The Utilities class now includes a ToLowerSnakeCase method.
1 parent 4c5f96f commit 0d0d8fd

File tree

8 files changed

+581
-65
lines changed

8 files changed

+581
-65
lines changed

src/Prodigy.Solutions.Deribit.Client/MarketData/DeribitMarketDataClient.cs

Lines changed: 447 additions & 56 deletions
Large diffs are not rendered by default.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace Prodigy.Solutions.Deribit.Client.MarketData;
2+
3+
public enum InstrumentKind
4+
{
5+
Undefined,
6+
Future,
7+
Option,
8+
Spot,
9+
FutureCombo,
10+
OptionCombo
11+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Prodigy.Solutions.Deribit.Client.MarketData;
4+
5+
public class InstrumentResponse
6+
{
7+
public required string BaseCurrency { get; init; }
8+
public decimal? BlockTradeCommission { get; init; }
9+
public decimal? BlockTradeMinTradeAmount { get; init; }
10+
public decimal? BlockTradeTickSize { get; init; }
11+
public long ContractSize { get; init; }
12+
public string? CounterCurrency { get; init; }
13+
public long CreationTimestamp { get; init; }
14+
[JsonIgnore]
15+
public DateTimeOffset CreationDate => DateTimeOffset.FromUnixTimeMilliseconds(CreationTimestamp);
16+
public long ExpirationTimestamp { get; init; }
17+
[JsonIgnore]
18+
public DateTimeOffset ExpirationDate => DateTimeOffset.FromUnixTimeMilliseconds(ExpirationTimestamp);
19+
public long InstrumentId { get; init; }
20+
public required string InstrumentName { get; init; }
21+
public InstrumentType InstrumentType { get; init; }
22+
public bool IsActive { get; init; }
23+
public InstrumentKind Kind { get; init; }
24+
public decimal? MakerCommission { get; init; }
25+
public decimal MaxLeverage { get; init; }
26+
public decimal? MaxLiquidationCommission { get; init; }
27+
public decimal? MinTradeAmount { get; init; }
28+
public string? OptionType { get; init; }
29+
public string? PriceIndex { get; init; }
30+
public string? QuoteCurrency { get; init; }
31+
public bool Rfq { get; init; }
32+
public string? SettlementCurrency { get; init; }
33+
public string? SettlementPeriod { get; init; }
34+
public decimal? Strike { get; init; }
35+
public decimal? TakerCommission { get; init; }
36+
public decimal? TickSize { get; init; }
37+
public IReadOnlyCollection<TickSizeStep>? TickSizeSteps { get; init; }
38+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace Prodigy.Solutions.Deribit.Client.MarketData;
2+
3+
public enum InstrumentType
4+
{
5+
Undefined,
6+
Linear,
7+
Reversed
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace Prodigy.Solutions.Deribit.Client.MarketData;
2+
3+
public class TickSizeStep
4+
{
5+
public decimal AbovePrice { get; init; }
6+
7+
public decimal TickSize { get; init; }
8+
}

src/Prodigy.Solutions.Deribit.Client/Prodigy.Solutions.Deribit.Client.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
<TargetFramework>net8.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
7+
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
8+
<PackageProjectUrl>https://github.com/prodigy-sln/Deribit.Client</PackageProjectUrl>
9+
<Version>0.0.1</Version>
710
</PropertyGroup>
811

912
<ItemGroup>

src/Prodigy.Solutions.Deribit.Client/Subscriptions/DeribitSubscriptionClient.cs

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,11 @@ private void SessionOnDisconnected(object? sender, EventArgs e)
4444

4545
public async Task<IObservable<OrderResponse>> SubscribeToOrderUpdatesAsync(string instrument, SubscriptionInterval interval = SubscriptionInterval.MilliSeconds100)
4646
{
47-
if (interval == SubscriptionInterval.Raw) throw new ArgumentOutOfRangeException(nameof(interval), $"Use {nameof(SubscribeToOrderUpdatesRawAsync)} for raw instead");
4847
var channel = $"user.orders.{instrument}.{interval.GetApiStringValue()}";
4948
var observable = await SubscribeToMessagesInternalAsync<OrderResponse[]>(channel);
5049
return observable.SelectMany(r => r);
5150
}
5251

53-
public async Task<IObservable<OrderResponse>> SubscribeToOrderUpdatesRawAsync(string instrument)
54-
{
55-
var channel = $"user.orders.{instrument}.raw";
56-
var observable = await SubscribeToMessagesInternalAsync<OrderResponse>(channel);
57-
return observable;
58-
}
59-
6052
public async Task<IObservable<TradeRespone>> SubscribeToTradesAsync(string instrument, SubscriptionInterval interval = SubscriptionInterval.MilliSeconds100)
6153
{
6254
var channel = $"user.trades.{instrument}.{interval.GetApiStringValue()}";
@@ -79,6 +71,43 @@ public async Task<IObservable<QuoteResponse>> SubscribeToQuoteAsync(string instr
7971
return await SubscribeToMessagesInternalAsync<QuoteResponse>($"quote.{instrument}");
8072
}
8173

74+
public async Task<bool> UnsubscribeAsync(string channel)
75+
{
76+
return (await UnsubscribeAsync(new[] { channel })).Contains(channel);
77+
}
78+
79+
public async Task<string[]> UnsubscribeAsync(IReadOnlyList<string> channels)
80+
{
81+
_logger.LogInformation("Unsubscribing from channels: {channels}", string.Join(", ", channels));
82+
83+
var unsubscribedChannels = await _deribitClient.InvokeAsync<string[]>(GetFullEndpoint("unsubscribe"), new { channels });
84+
if (unsubscribedChannels == null)
85+
{
86+
return [];
87+
}
88+
89+
foreach (var channel in unsubscribedChannels)
90+
{
91+
_subscribedChannels.Remove(channel);
92+
}
93+
94+
return unsubscribedChannels;
95+
}
96+
97+
public async Task<string> UnsubscribeAllAsync()
98+
{
99+
var result = await _deribitClient.InvokeAsync<string>(GetFullEndpoint("unsubscribe_all"));
100+
101+
if (result == null)
102+
{
103+
throw new Exception("Unsubscribe All request failed");
104+
}
105+
106+
_subscribedChannels.Clear();
107+
108+
return result;
109+
}
110+
82111
private async Task<IObservable<TResult>> SubscribeToMessagesInternalAsync<TResult>(string channel)
83112
{
84113
var observable = _deribitClient.GetSubscriptionMessages()?.Where(m => m.Channel == channel).ToTypedMessage<TResult>().WhereNotNull();

src/Prodigy.Solutions.Deribit.Client/Utilities.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Diagnostics;
22
using System.Dynamic;
3+
using System.Text;
34
using Prodigy.Solutions.Deribit.Client.Authentication;
45

56
namespace Prodigy.Solutions.Deribit.Client;
@@ -39,10 +40,37 @@ public static ExpandoObject ConvertParametersToExpandoObject(params object?[] ar
3940

4041
var value = args[i];
4142
if (value != null) {
42-
((IDictionary<string,object?>)expando)[param.Name] = value;
43+
((IDictionary<string,object?>)expando)[param.Name.ToLowerSnakeCase()] = value;
4344
}
4445
}
4546

4647
return expando;
4748
}
49+
50+
public static string ToLowerSnakeCase(this string str)
51+
{
52+
if (string.IsNullOrEmpty(str))
53+
{
54+
return str;
55+
}
56+
57+
var sb = new StringBuilder();
58+
for (var i = 0; i < str.Length; i++)
59+
{
60+
if (char.IsUpper(str[i]))
61+
{
62+
if (i != 0 && !char.IsUpper(str[i - 1]))
63+
{
64+
sb.Append("_");
65+
}
66+
sb.Append(char.ToLowerInvariant(str[i]));
67+
}
68+
else
69+
{
70+
sb.Append(str[i]);
71+
}
72+
}
73+
74+
return sb.ToString();
75+
}
4876
}

0 commit comments

Comments
 (0)