Skip to content

Commit 27d9b08

Browse files
committed
Support for Binance futures, bump version to 2.15
1 parent 718b8db commit 27d9b08

5 files changed

Lines changed: 118 additions & 14 deletions

File tree

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<PropertyGroup>
66
<LangVersion>latest</LangVersion>
7-
<Version>2.14.0</Version>
7+
<Version>2.15.0</Version>
88
</PropertyGroup>
99

1010
</Project>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
<ItemGroup>
2828
<PackageReference Include="Aster.Client.Websocket" Version="1.0.1" />
29-
<PackageReference Include="Binance.Client.Websocket" Version="2.4.1" />
29+
<PackageReference Include="Binance.Client.Websocket" Version="2.5.0" />
3030
<PackageReference Include="Bitfinex.Client.Websocket" Version="4.3.1" />
3131
<PackageReference Include="Bitmex.Client.Websocket" Version="3.3.1" />
3232
<PackageReference Include="Bitstamp.Client.Websocket" Version="1.2.1" />

src/Crypto.Websocket.Extensions/Orders/Sources/BinanceOrderSource.cs

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using Binance.Client.Websocket.Client;
45
using Binance.Client.Websocket.Responses.Orders;
@@ -16,9 +17,10 @@ namespace Crypto.Websocket.Extensions.Orders.Sources
1617
/// </summary>
1718
public class BinanceOrderSource : OrderSourceBase
1819
{
19-
private readonly CryptoOrderCollection _partiallyFilledOrders = new CryptoOrderCollection();
20+
private readonly CryptoOrderCollection _partiallyFilledOrders = new();
2021
private BinanceWebsocketClient _client = null!;
2122
private IDisposable? _subscription;
23+
private IDisposable? _subscriptionFutures;
2224

2325
/// <inheritdoc />
2426
public BinanceOrderSource(BinanceWebsocketClient client) : base(client.Logger)
@@ -38,12 +40,14 @@ public void ChangeClient(BinanceWebsocketClient client)
3840

3941
_client = client;
4042
_subscription?.Dispose();
43+
_subscriptionFutures?.Dispose();
4144
Subscribe();
4245
}
4346

4447
private void Subscribe()
4548
{
4649
_subscription = _client.Streams.OrderUpdateStream.Subscribe(HandleOrdersSafe);
50+
_subscriptionFutures = _client.Streams.FuturesOrderUpdateStream.Subscribe(HandleOrdersSafe);
4751
}
4852

4953
private void HandleOrdersSafe(OrderUpdate response)
@@ -57,12 +61,30 @@ private void HandleOrdersSafe(OrderUpdate response)
5761
_client.Logger.LogError(e, "[Binance] Failed to handle order info, error: '{error}'", e.Message);
5862
}
5963
}
64+
65+
private void HandleOrdersSafe(FuturesOrderUpdate response)
66+
{
67+
try
68+
{
69+
HandleOrders(response);
70+
}
71+
catch (Exception e)
72+
{
73+
_client.Logger.LogError(e, "[Binance] Failed to handle futures order info, error: '{error}'", e.Message);
74+
}
75+
}
6076

6177
private void HandleOrders(OrderUpdate response)
6278
{
6379
var order = ConvertOrder(response);
6480
OrderUpdatedSubject.OnNext(order);
6581
}
82+
83+
private void HandleOrders(FuturesOrderUpdate response)
84+
{
85+
var order = ConvertOrder(response);
86+
OrderUpdatedSubject.OnNext(order);
87+
}
6688

6789
/// <summary>
6890
/// Convert Binance orders to crypto orders
@@ -80,8 +102,8 @@ public CryptoOrder[] ConvertOrders(OrderUpdate[] orders)
80102
public CryptoOrder ConvertOrder(OrderUpdate order)
81103
{
82104
var id = order.Id.ToString();
83-
var existingCurrent = ExistingOrders.ContainsKey(id) ? ExistingOrders[id] : null;
84-
var existingPartial = _partiallyFilledOrders.ContainsKey(id) ? _partiallyFilledOrders[id] : null;
105+
var existingCurrent = ExistingOrders.GetValueOrDefault(id);
106+
var existingPartial = _partiallyFilledOrders.GetValueOrDefault(id);
85107
var existing = existingPartial ?? existingCurrent;
86108

87109
var price = Math.Abs(FirstNonZero(order.LastPriceFilled, order.Price, existing?.Price) ?? 0);
@@ -133,6 +155,65 @@ public CryptoOrder ConvertOrder(OrderUpdate order)
133155

134156
return newOrder;
135157
}
158+
159+
/// <summary>
160+
/// Convert Binance order to crypto order
161+
/// </summary>
162+
public CryptoOrder ConvertOrder(FuturesOrderUpdate orderUpdate)
163+
{
164+
var order = orderUpdate.Order;
165+
166+
var id = order.OrderId.ToString();
167+
var existingCurrent = ExistingOrders.GetValueOrDefault(id);
168+
var existingPartial = _partiallyFilledOrders.GetValueOrDefault(id);
169+
var existing = existingPartial ?? existingCurrent;
170+
171+
var price = Math.Abs(FirstNonZero(order.LastFilledPrice, order.Price, existing?.Price) ?? 0);
172+
var priceAvg = Math.Abs(FirstNonZero(order.AveragePrice, order.LastFilledPrice, order.Price, existing?.PriceAverage) ?? 0);
173+
174+
var amountQuote = FirstNonZero(order.Quantity * order.Price, existing?.AmountOrigQuote);
175+
var amountFilledQuote = FirstNonZero(order.LastFilledQuantity * order.LastFilledPrice);
176+
var amountFilledQuoteCumulative = FirstNonZero(order.AccumulatedFilledQuantity * priceAvg, existing?.AmountFilledCumulativeQuote);
177+
178+
var currentStatus = ConvertOrderStatus(order);
179+
180+
var newOrder = new CryptoOrder
181+
{
182+
Id = id,
183+
GroupId = existing?.GroupId ?? null,
184+
ClientId = !string.IsNullOrWhiteSpace(order.ClientOrderId) ?
185+
order.ClientOrderId : existing?.ClientId,
186+
Pair = order.Symbol ?? existing?.Pair ?? string.Empty,
187+
Side = order.Side == OrderSide.Sell ? CryptoOrderSide.Ask : CryptoOrderSide.Bid,
188+
AmountFilled = order.LastFilledQuantity,
189+
AmountFilledCumulative = order.AccumulatedFilledQuantity,
190+
AmountOrig = FirstNonZero(order.Quantity, existing?.AmountOrig),
191+
AmountFilledQuote = amountFilledQuote,
192+
AmountFilledCumulativeQuote = amountFilledQuoteCumulative,
193+
AmountOrigQuote = amountQuote,
194+
Created = order.TradeTime ?? existing?.Created,
195+
Updated = orderUpdate.TransactionTime ?? orderUpdate.EventTime,
196+
Price = price,
197+
PriceAverage = priceAvg,
198+
Fee = order.Commission,
199+
FeeCurrency = order.CommissionAsset,
200+
OrderStatus = currentStatus,
201+
OrderStatusRaw = order.Status.ToString(),
202+
CanceledReason = order.ExecutionType.ToString(),
203+
Type = ConvertOrderType(order.Type) ?? existing?.Type ?? CryptoOrderType.Undefined,
204+
TypePrev = existing?.TypePrev ?? existing?.Type ?? ConvertOrderType(order.Type) ?? CryptoOrderType.Undefined,
205+
OnMargin = existing?.OnMargin ?? false
206+
};
207+
208+
209+
if (currentStatus == CryptoOrderStatus.PartiallyFilled)
210+
{
211+
// save partially filled orders
212+
_partiallyFilledOrders[newOrder.Id] = newOrder;
213+
}
214+
215+
return newOrder;
216+
}
136217

137218

138219
/// <summary>
@@ -178,6 +259,25 @@ public static CryptoOrderStatus ConvertOrderStatus(OrderUpdate order)
178259
return CryptoOrderStatus.Canceled;
179260
}
180261
}
262+
263+
/// <summary>
264+
/// Convert order status
265+
/// </summary>
266+
public static CryptoOrderStatus ConvertOrderStatus(FuturesOrderData order)
267+
{
268+
var status = order.Status;
269+
switch (status)
270+
{
271+
case OrderStatus.New:
272+
return CryptoOrderStatus.Active;
273+
case OrderStatus.PartiallyFilled:
274+
return CryptoOrderStatus.PartiallyFilled;
275+
case OrderStatus.Filled:
276+
return CryptoOrderStatus.Executed;
277+
default:
278+
return CryptoOrderStatus.Canceled;
279+
}
280+
}
181281

182282

183283
private static double? FirstNonZero(params double?[] numbers)

test_integration/Crypto.Websocket.Extensions.Sample/OrderBookExample.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public static async Task RunEverything()
4949

5050
var bitmexOb = await StartBitmex("XBTUSD", optimized, l2OrderBook);
5151
var bitfinexOb = await StartBitfinex("BTCUSD", optimized, l2OrderBook);
52-
var binanceOb = await StartBinance("BTCUSDT", optimized, l2OrderBook);
52+
var binanceOb = await StartBinance("BTCUSDT", optimized, l2OrderBook, BinanceUserStreamType.Spot);
5353
//var coinbaseOb = await StartCoinbase("BTC-USD", optimized, l2OrderBook);
5454
var bitstampOb = await StartBitstamp("BTCUSD", optimized, l2OrderBook);
5555
//var huobiOb = await StartHuobi("btcusdt", optimized, l2OrderBook);
@@ -78,14 +78,14 @@ public static async Task RunOnlyOne(bool displayFullOb)
7878
var l2OrderBook = false;
7979

8080
//var ob = await StartBitmex("XBTUSD", optimized, l2OrderBook);
81-
//var ob = await StartBinance("BTCUSDT", optimized, l2OrderBook);
81+
var ob = await StartBinance("BTCUSDC", optimized, l2OrderBook, BinanceUserStreamType.Futures);
8282
//var ob = await StartBitfinex("BTCUSD", optimized, l2OrderBook);
8383
//var ob = await StartCoinbase("BTC-USD", optimized, l2OrderBook);
8484
//var ob = await StartBitstamp("BTCUSD", optimized, l2OrderBook);
8585
//var ob = await StartHuobi("btcusdt", optimized, l2OrderBook);}}
8686
//var ob = await StartHuobi("btcusdt", optimized, l2OrderBook);}}
8787
//var ob = await StartHyperliquid("BTC", optimized, l2OrderBook);
88-
var ob = await StartAster("BTCUSDT", optimized, l2OrderBook);
88+
//var ob = await StartAster("BTCUSDT", optimized, l2OrderBook);
8989

9090
Log.Information("Waiting for price change...");
9191

@@ -219,9 +219,11 @@ private static async Task<ICryptoOrderBook> StartBitfinex(string pair, bool opti
219219
return orderBook;
220220
}
221221

222-
private static async Task<ICryptoOrderBook> StartBinance(string pair, bool optimized, bool l2Optimized)
222+
private static async Task<ICryptoOrderBook> StartBinance(string pair, bool optimized, bool l2Optimized, BinanceUserStreamType type)
223223
{
224-
var url = BinanceValues.ApiWebsocketUrl;
224+
var url = type == BinanceUserStreamType.Spot ?
225+
BinanceValues.ApiWebsocketUrl :
226+
BinanceValues.FuturesApiWebsocketUrl;
225227
var communicator = new BinanceWebsocketCommunicator(url, Program.Logger.CreateLogger<BinanceWebsocketCommunicator>()) { Name = "Binance" };
226228
var client = new BinanceWebsocketClient(communicator, Program.Logger.CreateLogger<BinanceWebsocketClient>());
227229

test_integration/Crypto.Websocket.Extensions.Sample/OrdersExample.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ public static class OrdersExample
3535
public static async Task RunEverything()
3636
{
3737
//var ordBitmex = await StartBitmex(false, HandleOrderChanged, HandleWalletsChanged, HandlePositionsChanged);
38-
//var ordBinance = await StartBinance(HandleOrderChanged);
38+
var ordBinance = await StartBinance(HandleOrderChanged, BinanceUserStreamType.Futures);
3939
//var ordHyperliquid = await StartHyperliquid(HandleOrderChanged);
40-
var ordAster = await StartAster(HandleOrderChanged);
40+
//var ordAster = await StartAster(HandleOrderChanged);
4141

4242
Log.Information("Waiting for orders...");
4343
}
@@ -123,9 +123,11 @@ private static async Task<ICryptoOrders> StartBitmex(bool isTestnet, Action<Cryp
123123
return orders;
124124
}
125125

126-
private static async Task<ICryptoOrders> StartBinance(Action<CryptoOrder> handler)
126+
private static async Task<ICryptoOrders> StartBinance(Action<CryptoOrder> handler, BinanceUserStreamType type)
127127
{
128-
var url = BinanceValues.ApiWebsocketUrl;
128+
var url = type == BinanceUserStreamType.Spot ?
129+
BinanceValues.ApiWebsocketUrl :
130+
BinanceValues.FuturesApiWebsocketUrl;
129131
var communicator = new BinanceWebsocketCommunicator(url, Program.Logger.CreateLogger<BinanceWebsocketCommunicator>()) { Name = "Binance" };
130132
var client = new BinanceWebsocketClient(communicator, Program.Logger.CreateLogger<BinanceWebsocketClient>());
131133

0 commit comments

Comments
 (0)