Skip to content

Commit bcb3f0d

Browse files
Add Bybit exchange with simple support for IOrderBookSource only
1 parent 27d296b commit bcb3f0d

3 files changed

Lines changed: 141 additions & 0 deletions

File tree

Crypto.Websocket.Extensions.sln.DotSettings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<s:Boolean x:Key="/Default/UserDictionary/Words/=BTCEUR/@EntryIndexedValue">True</s:Boolean>
1414
<s:Boolean x:Key="/Default/UserDictionary/Words/=BTCUSD/@EntryIndexedValue">True</s:Boolean>
1515
<s:Boolean x:Key="/Default/UserDictionary/Words/=btcusdt/@EntryIndexedValue">True</s:Boolean>
16+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Bybit/@EntryIndexedValue">True</s:Boolean>
1617
<s:Boolean x:Key="/Default/UserDictionary/Words/=Coinbase/@EntryIndexedValue">True</s:Boolean>
1718
<s:Boolean x:Key="/Default/UserDictionary/Words/=Cryptocurrency/@EntryIndexedValue">True</s:Boolean>
1819
<s:Boolean x:Key="/Default/UserDictionary/Words/=Delimeter/@EntryIndexedValue">True</s:Boolean>

src/Crypto.Websocket.Extensions/Crypto.Websocket.Extensions.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<PackageReference Include="Bitfinex.Client.Websocket" Version="4.2.0" />
3030
<PackageReference Include="Bitmex.Client.Websocket" Version="3.2.1" />
3131
<PackageReference Include="Bitstamp.Client.Websocket" Version="1.1.1" />
32+
<PackageReference Include="Bybit.Client.Websocket" Version="1.0.1" />
3233
<PackageReference Include="Coinbase.Client.Websocket" Version="2.2.1" />
3334
<PackageReference Include="Luno.Client.Websocket" Version="8.0.1" />
3435
<PackageReference Include="Valr.Client.Websocket" Version="5.3.0" />
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using System.Linq;
5+
using System.Threading.Tasks;
6+
using Bybit.Client.Websocket.Client;
7+
using Bybit.Client.Websocket.Responses;
8+
using Crypto.Websocket.Extensions.Core.Models;
9+
using Crypto.Websocket.Extensions.Core.OrderBooks;
10+
using Crypto.Websocket.Extensions.Core.OrderBooks.Models;
11+
using Crypto.Websocket.Extensions.Core.OrderBooks.Sources;
12+
using Crypto.Websocket.Extensions.Core.Validations;
13+
using Microsoft.Extensions.Logging;
14+
15+
namespace Crypto.Websocket.Extensions.OrderBooks.Sources
16+
{
17+
/// <summary>
18+
/// Bybit order book source - based on snapshot plus diffs
19+
/// </summary>
20+
public class BybitOrderBookSource : OrderBookSourceBase
21+
{
22+
private IBybitPublicWebsocketClient _client = null!;
23+
private IDisposable? _snapshotSubscription;
24+
private IDisposable? _updateSubscription;
25+
26+
/// <inheritdoc />
27+
public BybitOrderBookSource(IBybitPublicWebsocketClient client, ILogger logger) : base(logger)
28+
{
29+
ChangeClient(client);
30+
}
31+
32+
/// <inheritdoc />
33+
public override string ExchangeName => "bybit";
34+
35+
/// <summary>
36+
/// Change client and resubscribe to the new streams
37+
/// </summary>
38+
public void ChangeClient(IBybitPublicWebsocketClient client)
39+
{
40+
CryptoValidations.ValidateInput(client, nameof(client));
41+
42+
_client = client;
43+
_snapshotSubscription?.Dispose();
44+
_updateSubscription?.Dispose();
45+
Subscribe();
46+
}
47+
48+
/// <inheritdoc />
49+
public override void Dispose()
50+
{
51+
base.Dispose();
52+
_snapshotSubscription?.Dispose();
53+
}
54+
55+
private void Subscribe()
56+
{
57+
_snapshotSubscription = _client.Streams.OrderBookSnapshotStream.Subscribe(HandleSnapshot);
58+
_updateSubscription = _client.Streams.OrderBookDeltaStream.Subscribe(HandleDelta);
59+
}
60+
61+
private void HandleSnapshot(OrderBookSnapshotResponse response)
62+
{
63+
// received snapshot, convert and stream
64+
var levels = ConvertLevels(response);
65+
var bulk = new OrderBookLevelBulk(OrderBookAction.Insert, levels, CryptoOrderBookType.L2);
66+
FillBulk(response, bulk);
67+
StreamSnapshot(bulk);
68+
}
69+
70+
private void HandleDelta(OrderBookDeltaResponse response)
71+
{
72+
BufferData(response);
73+
}
74+
75+
private static OrderBookLevel[] ConvertLevels(OrderBookResponse response)
76+
{
77+
var bids = response.Data.Bids
78+
.Select(x => ConvertLevel(x, CryptoOrderSide.Bid, response.Data.Symbol))
79+
.ToArray();
80+
var asks = response.Data.Asks
81+
.Select(x => ConvertLevel(x, CryptoOrderSide.Ask, response.Data.Symbol))
82+
.ToArray();
83+
return bids.Concat(asks).ToArray();
84+
}
85+
86+
private static OrderBookLevel ConvertLevel(double[] x, CryptoOrderSide side, string pair)
87+
{
88+
return new
89+
(
90+
x[0].ToString(CultureInfo.InvariantCulture),
91+
side,
92+
x[0],
93+
x[1],
94+
null,
95+
pair
96+
);
97+
}
98+
99+
private static OrderBookAction RecognizeAction(OrderBookLevel level) => level.Amount > 0 ? OrderBookAction.Update : OrderBookAction.Delete;
100+
101+
/// <inheritdoc />
102+
protected override Task<OrderBookLevelBulk?> LoadSnapshotInternal(string? pair, int count = 1000) => Task.FromResult<OrderBookLevelBulk?>(null);
103+
104+
private IEnumerable<OrderBookLevelBulk> ConvertDiff(OrderBookDeltaResponse response)
105+
{
106+
var levels = ConvertLevels(response);
107+
var group = levels.GroupBy(RecognizeAction).ToArray();
108+
foreach (var actionGroup in group)
109+
{
110+
var bulk = new OrderBookLevelBulk(actionGroup.Key, actionGroup.ToArray(), CryptoOrderBookType.L2);
111+
FillBulk(response, bulk);
112+
yield return bulk;
113+
}
114+
}
115+
116+
private void FillBulk(OrderBookResponse response, OrderBookLevelBulk bulk)
117+
{
118+
bulk.ExchangeName = ExchangeName;
119+
bulk.ServerTimestamp = response.CreatedTimestamp;
120+
bulk.ServerSequence = response.Data.Sequence;
121+
}
122+
123+
/// <inheritdoc />
124+
protected override OrderBookLevelBulk[] ConvertData(object[] data)
125+
{
126+
var result = new List<OrderBookLevelBulk>();
127+
foreach (var response in data)
128+
{
129+
if (response is not OrderBookDeltaResponse orderBookDeltaResponse)
130+
continue;
131+
132+
var converted = ConvertDiff(orderBookDeltaResponse);
133+
result.AddRange(converted);
134+
}
135+
136+
return result.ToArray();
137+
}
138+
}
139+
}

0 commit comments

Comments
 (0)