Skip to content

Commit fbc97b9

Browse files
committed
Refactor error handling and enhance order management.
Introduced the `Exceptional` class to streamline error encapsulation and handling in observable streams. Enhanced the trading client with additional order configurations, including `OtocoConfig`, `LinkedOrderType`, and `TriggerFillCondition`. Updated `DeribitAuthenticationSession` to support asynchronous disposal, improving resource management.
1 parent 6f90f40 commit fbc97b9

File tree

6 files changed

+145
-10
lines changed

6 files changed

+145
-10
lines changed

src/Prodigy.Solutions.Deribit.Client/Authentication/DeribitAuthenticationSession.cs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,40 @@
11
namespace Prodigy.Solutions.Deribit.Client.Authentication;
22

3-
public class DeribitAuthenticationSession : IDisposable
3+
public class DeribitAuthenticationSession : IAsyncDisposable
44
{
55
private CancellationTokenSource _cts = new();
6+
private readonly CancellationTokenSource _disposedCancellation = new();
67
private Task? _expireTask;
78

89
public bool IsAuthenticated { get; private set; }
910

1011
public AuthResponse? LastResponse { get; private set; }
1112

12-
public void Dispose()
13+
public async ValueTask DisposeAsync()
1314
{
14-
_expireTask?.Dispose();
15+
await _disposedCancellation.CancelAsync();
16+
await CastAndDispose(_cts);
17+
if (_expireTask != null) await CastAndDispose(_expireTask);
18+
19+
return;
20+
21+
static async ValueTask CastAndDispose(IDisposable resource)
22+
{
23+
if (resource is IAsyncDisposable resourceAsyncDisposable)
24+
await resourceAsyncDisposable.DisposeAsync();
25+
else
26+
resource.Dispose();
27+
}
1528
}
1629

1730
public void SetAuthenticated(AuthResponse authResponse)
1831
{
1932
_cts.Cancel();
20-
_cts = new CancellationTokenSource();
33+
_cts = CancellationTokenSource.CreateLinkedTokenSource(_disposedCancellation.Token);
2134
_expireTask = Task.Delay((authResponse.ExpiresIn - 1) * 1000, _cts.Token)
2235
.ContinueWith(_ =>
2336
{
37+
if (_disposedCancellation.Token.IsCancellationRequested) return;
2438
Reset();
2539
OnTokenExpired();
2640
}, _cts.Token, TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default);
@@ -33,12 +47,14 @@ public void SetAuthenticated(AuthResponse authResponse)
3347

3448
public void SetLoggedOut()
3549
{
50+
if (_disposedCancellation.Token.IsCancellationRequested) return;
3651
Reset();
3752
OnLoggedOut();
3853
}
3954

4055
public void SetDisconnected()
4156
{
57+
if (_disposedCancellation.Token.IsCancellationRequested) return;
4258
Reset();
4359
OnDisconnected();
4460
}
@@ -52,21 +68,25 @@ private void Reset()
5268

5369
private void OnTokenExpired()
5470
{
71+
if (_disposedCancellation.Token.IsCancellationRequested) return;
5572
TokenExpired?.Invoke(this, EventArgs.Empty);
5673
}
5774

5875
private void OnAuthenticated(AuthResponse response)
5976
{
77+
if (_disposedCancellation.Token.IsCancellationRequested) return;
6078
Authenticated?.Invoke(this, response);
6179
}
6280

6381
private void OnLoggedOut()
6482
{
83+
if (_disposedCancellation.Token.IsCancellationRequested) return;
6584
LoggedOut?.Invoke(this, EventArgs.Empty);
6685
}
6786

6887
private void OnDisconnected()
6988
{
89+
if (_disposedCancellation.Token.IsCancellationRequested) return;
7090
Disconnected?.Invoke(this, EventArgs.Empty);
7191
}
7292

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
using System.Reactive.Linq;
2+
using Prodigy.Solutions.Deribit.Client.Observable;
23

34
namespace Prodigy.Solutions.Deribit.Client.Extensions;
45

56
public static class SubscriptionObservableExtensions
67
{
7-
public static IObservable<T?> ToTypedMessage<T>(this IObservable<SubscriptionMessage> source)
8+
public static IObservable<Exceptional<T?>> ToTypedMessage<T>(this IObservable<SubscriptionMessage> source)
89
{
9-
return source.Select(m => m.Data.ToObject<T>(Constants.JsonSerializer));
10+
return source.Select(m => Exceptional.From(() => m.Data.ToObject<T>(Constants.JsonSerializer)));
1011
}
1112
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
namespace Prodigy.Solutions.Deribit.Client.Observable;
2+
3+
public class Exceptional
4+
{
5+
public static Exceptional<T> From<T>(T value) => new Exceptional<T>(value);
6+
public static Exceptional<T> From<T>(Exception ex) => new Exceptional<T>(ex);
7+
public static Exceptional<T> From<T>(Func<T> factory) => new Exceptional<T>(factory);
8+
}
9+
10+
public class Exceptional<T>
11+
{
12+
public bool HasException { get; private set; }
13+
public Exception? Exception { get; private set; }
14+
public T? Value { get; private set; }
15+
16+
public Exceptional(T value)
17+
{
18+
this.HasException = false;
19+
this.Value = value;
20+
}
21+
22+
public Exceptional(Exception exception)
23+
{
24+
this.HasException = true;
25+
this.Exception = exception;
26+
}
27+
28+
public Exceptional(Func<T> factory)
29+
{
30+
try
31+
{
32+
this.Value = factory();
33+
this.HasException = false;
34+
}
35+
catch (Exception ex)
36+
{
37+
this.Exception = ex;
38+
this.HasException = true;
39+
}
40+
}
41+
42+
public override string? ToString() =>
43+
this.HasException
44+
? this.Exception?.GetType().Name
45+
: this.Value != null ? this.Value.ToString() : "null";
46+
}
47+
48+
49+
public static class ExceptionalExtensions
50+
{
51+
public static Exceptional<T> ToExceptional<T>(this T value) => Exceptional.From(value);
52+
53+
public static Exceptional<T> ToExceptional<T>(this Func<T> factory) => Exceptional.From(factory);
54+
55+
public static Exceptional<U> Select<T, U>(this Exceptional<T> value, Func<T?, U> m) =>
56+
value.SelectMany(t => Exceptional.From(() => m(t)));
57+
58+
public static Exceptional<U> SelectMany<T, U>(this Exceptional<T> value, Func<T?, Exceptional<U>> k) =>
59+
value.HasException ? Exceptional.From<U>(value.Exception!) : k(value.Value);
60+
61+
public static Exceptional<V> SelectMany<T, U, V>(this Exceptional<T> value, Func<T?, Exceptional<U>> k, Func<T?, U?, V> m) =>
62+
value.SelectMany(t => k(t).SelectMany(u => Exceptional.From(() => m(t, u))));
63+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<Nullable>enable</Nullable>
77
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
88
<PackageProjectUrl>https://github.com/prodigy-sln/Deribit.Client</PackageProjectUrl>
9-
<Version>0.0.2</Version>
9+
<Version>0.0.8</Version>
1010
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
1111
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
1212
<OutputType>Library</OutputType>

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,17 @@ public async Task<string> UnsubscribeAllAsync()
123123

124124
private async Task<IObservable<TResult>> SubscribeToMessagesInternalAsync<TResult>(string channel)
125125
{
126-
var observable = _deribitClient.GetSubscriptionMessages()?.Where(m => m.Channel == channel)
127-
.ToTypedMessage<TResult>().WhereNotNull();
126+
var observable = _deribitClient.GetSubscriptionMessages()?
127+
.Where(m => m.Channel == channel)
128+
.ToTypedMessage<TResult>()
129+
.Select(x =>
130+
{
131+
if (!x.HasException) return x.Value;
132+
133+
_logger.LogError(x.Exception, "Error in subscription");
134+
return default;
135+
})
136+
.WhereNotNull();
128137
if (observable == null) throw new InvalidOperationException("could not subscribe to messages channel");
129138

130139
if (!_subscribedChannels.Contains(channel))

src/Prodigy.Solutions.Deribit.Client/Trading/DeribitTradingClient.cs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,44 @@ public class PlaceOrderRequest
130130
public bool? Mmp { get; init; }
131131

132132
public long? ValidUntil { get; init; }
133+
134+
public LinkedOrderType? LinkedOrderType { get; init; }
135+
136+
public TriggerFillCondition? TriggerFillCondition { get; init; }
137+
138+
public OtocoConfig[]? OtocoConfig { get; init; }
139+
}
140+
141+
public class OtocoConfig
142+
{
143+
public decimal? Amount { get; init; }
144+
public OrderDirection Direction { get; init; }
145+
public OrderType Type { get; init; }
146+
public string? Label { get; init; }
147+
public decimal? Price { get; init; }
148+
public bool ReduceOnly { get; init; }
149+
public TimeInForce? TimeInForce { get; init; }
150+
public bool PostOnly { get; init; }
151+
public bool RejectPostOnly { get; init; }
152+
public decimal? TriggerPrice { get; init; }
153+
public decimal? TriggerOffset { get; init; }
154+
public TriggerType? Trigger { get; init; }
155+
}
156+
157+
public enum TriggerFillCondition
158+
{
159+
Undefined,
160+
FirstHit,
161+
CompleteFill,
162+
Incremental
163+
}
164+
165+
public enum LinkedOrderType
166+
{
167+
Undefined,
168+
OneTriggersOther,
169+
OneCancelsOther,
170+
OneTriggersOneCancelsOther
133171
}
134172

135173
public enum OptionsAdvancedOrderType
@@ -230,6 +268,7 @@ public class OrderResponse
230268
public bool IsRebalance { get; init; }
231269
public decimal AveragePrice { get; init; }
232270
public OptionsAdvancedOrderType? Advanced { get; init; }
271+
public bool IsSecondaryOto { get; init; }
233272
}
234273

235274
public enum OrderDirection
@@ -251,7 +290,10 @@ public enum OrderCancelReason
251290
PositionLocked,
252291
MmpTrigger,
253292
MmpConfigCurtailment,
254-
EditPostOnlyReject
293+
EditPostOnlyReject,
294+
OtoPrimaryClosed,
295+
OcoOtherClosed,
296+
Settlement
255297
}
256298

257299
public enum OrderState

0 commit comments

Comments
 (0)