diff --git a/BingX.Net/BingX.Net.csproj b/BingX.Net/BingX.Net.csproj
index c3c6906..0ac4bbe 100644
--- a/BingX.Net/BingX.Net.csproj
+++ b/BingX.Net/BingX.Net.csproj
@@ -48,10 +48,10 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
\ No newline at end of file
diff --git a/BingX.Net/BingX.Net.xml b/BingX.Net/BingX.Net.xml
index 12b76b8..1d1b85e 100644
--- a/BingX.Net/BingX.Net.xml
+++ b/BingX.Net/BingX.Net.xml
@@ -59,6 +59,16 @@
Urls to the API documentation
+
+
+ Format a base and quote asset to a BingX recognized symbol
+
+ Base asset
+ Quote asset
+ Trading mode
+ Delivery time for delivery futures
+
+
Rate limiter configuration for the BingX API
@@ -74,6 +84,26 @@
Event for when a rate limit is triggered
+
+
+
+
+
+ ctor
+
+
+
+
+ ctor
+
+ Service provider for resolving logging and clients
+
+
+
+
+
+
+
@@ -2666,6 +2696,14 @@
Perpetual Futures order book factory methods
+
+
+ Create a SymbolOrderBook for the symbol
+
+ The symbol
+ Book options
+
+
Create a new futures local order book instance
@@ -2682,6 +2720,30 @@
+
+
+ Tracker factory
+
+
+
+
+ Create a new kline tracker
+
+ The symbol
+ Kline interval
+ The max amount of klines to retain
+ The max period the data should be retained
+
+
+
+
+ Create a new trade tracker for a symbol
+
+ The symbol
+ The max amount of klines to retain
+ The max period the data should be retained
+
+
Api addresses
@@ -6477,6 +6539,9 @@
+
+
+
diff --git a/BingX.Net/BingXExchange.cs b/BingX.Net/BingXExchange.cs
index c4a518a..ffa71fd 100644
--- a/BingX.Net/BingXExchange.cs
+++ b/BingX.Net/BingXExchange.cs
@@ -2,6 +2,7 @@
using CryptoExchange.Net.RateLimiting;
using CryptoExchange.Net.RateLimiting.Guards;
using CryptoExchange.Net.RateLimiting.Interfaces;
+using CryptoExchange.Net.SharedApis;
using System;
using System.Collections.Generic;
@@ -29,6 +30,19 @@ public static class BingXExchange
"https://bingx-api.github.io/docs"
};
+ ///
+ /// Format a base and quote asset to a BingX recognized symbol
+ ///
+ /// Base asset
+ /// Quote asset
+ /// Trading mode
+ /// Delivery time for delivery futures
+ ///
+ public static string FormatSymbol(string baseAsset, string quoteAsset, TradingMode tradingMode, DateTime? deliverTime = null)
+ {
+ return baseAsset.ToUpperInvariant() + "-" + quoteAsset.ToUpperInvariant();
+ }
+
///
/// Rate limiter configuration for the BingX API
///
diff --git a/BingX.Net/BingXTrackerFactory.cs b/BingX.Net/BingXTrackerFactory.cs
new file mode 100644
index 0000000..4a03bc2
--- /dev/null
+++ b/BingX.Net/BingXTrackerFactory.cs
@@ -0,0 +1,92 @@
+using BingX.Net.Clients;
+using BingX.Net.Interfaces;
+using BingX.Net.Interfaces.Clients;
+using CryptoExchange.Net.SharedApis;
+using CryptoExchange.Net.Trackers.Klines;
+using CryptoExchange.Net.Trackers.Trades;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using System;
+
+namespace BingX.Net
+{
+ ///
+ public class BingXTrackerFactory : IBingXTrackerFactory
+ {
+ private readonly IServiceProvider? _serviceProvider;
+
+ ///
+ /// ctor
+ ///
+ public BingXTrackerFactory()
+ {
+ }
+
+ ///
+ /// ctor
+ ///
+ /// Service provider for resolving logging and clients
+ public BingXTrackerFactory(IServiceProvider serviceProvider)
+ {
+ _serviceProvider = serviceProvider;
+ }
+
+ ///
+ public IKlineTracker CreateKlineTracker(SharedSymbol symbol, SharedKlineInterval interval, int? limit = null, TimeSpan? period = null)
+ {
+ var restClient = _serviceProvider?.GetRequiredService() ?? new BingXRestClient();
+ var socketClient = _serviceProvider?.GetRequiredService() ?? new BingXSocketClient();
+
+ IKlineRestClient sharedRestClient;
+ IKlineSocketClient sharedSocketClient;
+ if (symbol.TradingMode == TradingMode.Spot)
+ {
+ sharedRestClient = restClient.SpotApi.SharedClient;
+ sharedSocketClient = socketClient.SpotApi.SharedClient;
+ }
+ else
+ {
+ sharedRestClient = restClient.PerpetualFuturesApi.SharedClient;
+ sharedSocketClient = socketClient.PerpetualFuturesApi.SharedClient;
+ }
+
+ return new KlineTracker(
+ _serviceProvider?.GetRequiredService().CreateLogger(restClient.Exchange),
+ sharedRestClient,
+ sharedSocketClient,
+ symbol,
+ interval,
+ limit,
+ period
+ );
+ }
+
+ ///
+ public ITradeTracker CreateTradeTracker(SharedSymbol symbol, int? limit = null, TimeSpan? period = null)
+ {
+ var restClient = _serviceProvider?.GetRequiredService() ?? new BingXRestClient();
+ var socketClient = _serviceProvider?.GetRequiredService() ?? new BingXSocketClient();
+
+ IRecentTradeRestClient sharedRestClient;
+ ITradeSocketClient sharedSocketClient;
+ if (symbol.TradingMode == TradingMode.Spot) {
+ sharedRestClient = restClient.SpotApi.SharedClient;
+ sharedSocketClient = socketClient.SpotApi.SharedClient;
+ }
+ else {
+ sharedRestClient = restClient.PerpetualFuturesApi.SharedClient;
+ sharedSocketClient = socketClient.PerpetualFuturesApi.SharedClient;
+ }
+
+ return new TradeTracker(
+ _serviceProvider?.GetRequiredService().CreateLogger(restClient.Exchange),
+ sharedRestClient,
+ null,
+ sharedSocketClient,
+ symbol,
+ limit,
+ period
+ );
+ }
+ }
+}
diff --git a/BingX.Net/Clients/PerpetualFuturesApi/BingXRestClientPerpetualFuturesApi.cs b/BingX.Net/Clients/PerpetualFuturesApi/BingXRestClientPerpetualFuturesApi.cs
index b663068..11129f3 100644
--- a/BingX.Net/Clients/PerpetualFuturesApi/BingXRestClientPerpetualFuturesApi.cs
+++ b/BingX.Net/Clients/PerpetualFuturesApi/BingXRestClientPerpetualFuturesApi.cs
@@ -55,7 +55,9 @@ internal BingXRestClientPerpetualFuturesApi(ILogger logger, HttpClient? httpClie
#endregion
///
- public override string FormatSymbol(string baseAsset, string quoteAsset, TradingMode tradingMode, DateTime? deliverTime = null) => baseAsset.ToUpperInvariant() + "-" + quoteAsset.ToUpperInvariant();
+ public override string FormatSymbol(string baseAsset, string quoteAsset, TradingMode tradingMode, DateTime? deliverTime = null)
+ => BingXExchange.FormatSymbol(baseAsset, quoteAsset, tradingMode, deliverTime);
+
public IBingXRestClientPerpetualFuturesApiShared SharedClient => this;
///
diff --git a/BingX.Net/Clients/PerpetualFuturesApi/BingXRestClientPerpetualFuturesApiShared.cs b/BingX.Net/Clients/PerpetualFuturesApi/BingXRestClientPerpetualFuturesApiShared.cs
index c91f1a3..8ccaa47 100644
--- a/BingX.Net/Clients/PerpetualFuturesApi/BingXRestClientPerpetualFuturesApiShared.cs
+++ b/BingX.Net/Clients/PerpetualFuturesApi/BingXRestClientPerpetualFuturesApiShared.cs
@@ -181,7 +181,10 @@ async Task>> IRecentTradeRestClient.G
if (!result)
return result.AsExchangeResult>(Exchange, null, default);
- return result.AsExchangeResult>(Exchange, request.Symbol.TradingMode, result.Data.Reverse().Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)).ToArray());
+ return result.AsExchangeResult>(Exchange, request.Symbol.TradingMode, result.Data.Reverse().Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)
+ {
+ Side = x.BuyerIsMaker ? SharedOrderSide.Buy : SharedOrderSide.Sell
+ }).ToArray());
}
#endregion
diff --git a/BingX.Net/Clients/PerpetualFuturesApi/BingXSocketClientPerpetualFuturesApi.cs b/BingX.Net/Clients/PerpetualFuturesApi/BingXSocketClientPerpetualFuturesApi.cs
index c1124c5..758c309 100644
--- a/BingX.Net/Clients/PerpetualFuturesApi/BingXSocketClientPerpetualFuturesApi.cs
+++ b/BingX.Net/Clients/PerpetualFuturesApi/BingXSocketClientPerpetualFuturesApi.cs
@@ -50,7 +50,8 @@ internal BingXSocketClientPerpetualFuturesApi(ILogger logger, BingXSocketOptions
#endregion
///
- public override string FormatSymbol(string baseAsset, string quoteAsset, TradingMode tradingMode, DateTime? deliverTime = null) => baseAsset.ToUpperInvariant() + "-" + quoteAsset.ToUpperInvariant();
+ public override string FormatSymbol(string baseAsset, string quoteAsset, TradingMode tradingMode, DateTime? deliverTime = null)
+ => BingXExchange.FormatSymbol(baseAsset, quoteAsset, tradingMode, deliverTime);
///
protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials)
diff --git a/BingX.Net/Clients/PerpetualFuturesApi/BingXSocketClientPerpetualFuturesApiShared.cs b/BingX.Net/Clients/PerpetualFuturesApi/BingXSocketClientPerpetualFuturesApiShared.cs
index 7c6b5e4..c19b147 100644
--- a/BingX.Net/Clients/PerpetualFuturesApi/BingXSocketClientPerpetualFuturesApiShared.cs
+++ b/BingX.Net/Clients/PerpetualFuturesApi/BingXSocketClientPerpetualFuturesApiShared.cs
@@ -44,13 +44,42 @@ async Task> ITradeSocketClient.SubscribeToTra
return new ExchangeResult(Exchange, validationError);
var symbol = request.Symbol.GetSymbol(FormatSymbol);
- var result = await SubscribeToTradeUpdatesAsync(symbol, update => handler(update.AsExchangeEvent>(Exchange, update.Data.Select(x => new SharedTrade(x.Quantity, x.Price, x.TradeTime)).ToArray())), ct).ConfigureAwait(false);
+ var result = await SubscribeToTradeUpdatesAsync(symbol, update => handler(update.AsExchangeEvent>(Exchange, update.Data.Select(x => new SharedTrade(x.Quantity, x.Price, x.TradeTime)
+ {
+ Side = x.BuyerIsMaker ? SharedOrderSide.Buy : SharedOrderSide.Sell
+ }).ToArray())), ct).ConfigureAwait(false);
return new ExchangeResult(Exchange, result);
}
#endregion
+ #region Kline client
+ SubscribeKlineOptions IKlineSocketClient.SubscribeKlineOptions { get; } = new SubscribeKlineOptions(false);
+ async Task> IKlineSocketClient.SubscribeToKlineUpdatesAsync(SubscribeKlineRequest request, Action> handler, CancellationToken ct)
+ {
+ var interval = (Enums.KlineInterval)request.Interval;
+ if (!Enum.IsDefined(typeof(Enums.KlineInterval), interval))
+ return new ExchangeResult(Exchange, new ArgumentError("Interval not supported"));
+
+ var validationError = ((IKlineSocketClient)this).SubscribeKlineOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes);
+ if (validationError != null)
+ return new ExchangeResult(Exchange, validationError);
+
+ var symbol = request.Symbol.GetSymbol(FormatSymbol);
+ var result = await SubscribeToKlineUpdatesAsync(symbol, interval, update =>
+ {
+ if (update.UpdateType == SocketUpdateType.Snapshot)
+ return;
+
+ foreach (var item in update.Data)
+ handler(update.AsExchangeEvent(Exchange, new SharedKline(item.Timestamp, item.ClosePrice, item.HighPrice, item.LowPrice, item.OpenPrice, item.Volume)));
+ }, ct).ConfigureAwait(false);
+
+ return new ExchangeResult(Exchange, result);
+ }
+ #endregion
+
#region Book Ticker client
EndpointOptions IBookTickerSocketClient.SubscribeBookTickerOptions { get; } = new EndpointOptions(false);
diff --git a/BingX.Net/Clients/SpotApi/BingXRestClientSpotApi.cs b/BingX.Net/Clients/SpotApi/BingXRestClientSpotApi.cs
index fbce924..76f600b 100644
--- a/BingX.Net/Clients/SpotApi/BingXRestClientSpotApi.cs
+++ b/BingX.Net/Clients/SpotApi/BingXRestClientSpotApi.cs
@@ -66,7 +66,8 @@ internal BingXRestClientSpotApi(ILogger logger, HttpClient? httpClient, BingXRes
#endregion
///
- public override string FormatSymbol(string baseAsset, string quoteAsset, TradingMode tradingMode, DateTime? deliverTime = null) => baseAsset.ToUpperInvariant() + "-" + quoteAsset.ToUpperInvariant();
+ public override string FormatSymbol(string baseAsset, string quoteAsset, TradingMode tradingMode, DateTime? deliverTime = null)
+ => BingXExchange.FormatSymbol(baseAsset, quoteAsset, tradingMode, deliverTime);
///
protected override IMessageSerializer CreateSerializer() => new SystemTextJsonMessageSerializer();
diff --git a/BingX.Net/Clients/SpotApi/BingXRestClientSpotApiShared.cs b/BingX.Net/Clients/SpotApi/BingXRestClientSpotApiShared.cs
index 96baadd..9170062 100644
--- a/BingX.Net/Clients/SpotApi/BingXRestClientSpotApiShared.cs
+++ b/BingX.Net/Clients/SpotApi/BingXRestClientSpotApiShared.cs
@@ -152,7 +152,10 @@ async Task>> IRecentTradeRestClient.G
if (!result)
return result.AsExchangeResult>(Exchange, null, default);
- return result.AsExchangeResult>(Exchange, request.Symbol.TradingMode, result.Data.Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)).ToArray());
+ return result.AsExchangeResult>(Exchange, request.Symbol.TradingMode, result.Data.Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)
+ {
+ Side = x.BuyerIsMaker ? SharedOrderSide.Buy : SharedOrderSide.Sell
+ }).ToArray());
}
#endregion
diff --git a/BingX.Net/Clients/SpotApi/BingXSocketClientSpotApi.cs b/BingX.Net/Clients/SpotApi/BingXSocketClientSpotApi.cs
index 5278363..44833fc 100644
--- a/BingX.Net/Clients/SpotApi/BingXSocketClientSpotApi.cs
+++ b/BingX.Net/Clients/SpotApi/BingXSocketClientSpotApi.cs
@@ -54,7 +54,8 @@ protected override AuthenticationProvider CreateAuthenticationProvider(ApiCreden
=> new BingXAuthenticationProvider(credentials);
///
- public override string FormatSymbol(string baseAsset, string quoteAsset, TradingMode tradingMode, DateTime? deliverTime = null) => baseAsset.ToUpperInvariant() + "-" + quoteAsset.ToUpperInvariant();
+ public override string FormatSymbol(string baseAsset, string quoteAsset, TradingMode tradingMode, DateTime? deliverTime = null)
+ => BingXExchange.FormatSymbol(baseAsset, quoteAsset, tradingMode, deliverTime);
public IBingXSocketClientSpotApiShared SharedClient => this;
diff --git a/BingX.Net/Clients/SpotApi/BingXSocketClientSpotApiShared.cs b/BingX.Net/Clients/SpotApi/BingXSocketClientSpotApiShared.cs
index ae48e49..f831205 100644
--- a/BingX.Net/Clients/SpotApi/BingXSocketClientSpotApiShared.cs
+++ b/BingX.Net/Clients/SpotApi/BingXSocketClientSpotApiShared.cs
@@ -45,13 +45,35 @@ async Task> ITradeSocketClient.SubscribeToTra
return new ExchangeResult(Exchange, validationError);
var symbol = request.Symbol.GetSymbol(FormatSymbol);
- var result = await SubscribeToTradeUpdatesAsync(symbol, update => handler(update.AsExchangeEvent>(Exchange, new[] { new SharedTrade(update.Data.Quantity, update.Data.Price, update.Data.TradeTime) })), ct).ConfigureAwait(false);
+ var result = await SubscribeToTradeUpdatesAsync(symbol, update => handler(update.AsExchangeEvent>(Exchange, new[] { new SharedTrade(update.Data.Quantity, update.Data.Price, update.Data.TradeTime)
+ {
+ Side = update.Data.BuyerIsMaker ? SharedOrderSide.Buy : SharedOrderSide.Sell
+ } })), ct).ConfigureAwait(false);
return new ExchangeResult(Exchange, result);
}
#endregion
+ #region Kline client
+ SubscribeKlineOptions IKlineSocketClient.SubscribeKlineOptions { get; } = new SubscribeKlineOptions(false);
+ async Task> IKlineSocketClient.SubscribeToKlineUpdatesAsync(SubscribeKlineRequest request, Action> handler, CancellationToken ct)
+ {
+ var interval = (Enums.KlineInterval)request.Interval;
+ if (!Enum.IsDefined(typeof(Enums.KlineInterval), interval))
+ return new ExchangeResult(Exchange, new ArgumentError("Interval not supported"));
+
+ var validationError = ((IKlineSocketClient)this).SubscribeKlineOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes);
+ if (validationError != null)
+ return new ExchangeResult(Exchange, validationError);
+
+ var symbol = request.Symbol.GetSymbol(FormatSymbol);
+ var result = await SubscribeToKlineUpdatesAsync(symbol, interval, update => handler(update.AsExchangeEvent(Exchange, new SharedKline(update.Data.Kline.OpenTime, update.Data.Kline.ClosePrice, update.Data.Kline.HighPrice, update.Data.Kline.LowPrice, update.Data.Kline.OpenPrice, update.Data.Kline.Volume))), ct).ConfigureAwait(false);
+
+ return new ExchangeResult(Exchange, result);
+ }
+ #endregion
+
#region Book Ticker client
EndpointOptions IBookTickerSocketClient.SubscribeBookTickerOptions { get; } = new EndpointOptions(false);
diff --git a/BingX.Net/ExtensionMethods/ServiceCollectionExtensions.cs b/BingX.Net/ExtensionMethods/ServiceCollectionExtensions.cs
index 161553c..5c14ae1 100644
--- a/BingX.Net/ExtensionMethods/ServiceCollectionExtensions.cs
+++ b/BingX.Net/ExtensionMethods/ServiceCollectionExtensions.cs
@@ -9,6 +9,7 @@
using BingX.Net.Objects.Options;
using BingX.Net.SymbolOrderBooks;
using CryptoExchange.Net;
+using BingX.Net;
namespace Microsoft.Extensions.DependencyInjection
{
@@ -62,6 +63,7 @@ public static IServiceCollection AddBingX(
services.AddTransient();
services.AddSingleton();
services.AddTransient();
+ services.AddTransient();
services.AddTransient(x => x.GetRequiredService().SpotApi.CommonSpotClient);
services.RegisterSharedRestInterfaces(x => x.GetRequiredService().SpotApi.SharedClient);
diff --git a/BingX.Net/Interfaces/Clients/PerpetualFuturesApi/IBingXSocketClientPerpetualFuturesApiShared.cs b/BingX.Net/Interfaces/Clients/PerpetualFuturesApi/IBingXSocketClientPerpetualFuturesApiShared.cs
index 2471092..89928be 100644
--- a/BingX.Net/Interfaces/Clients/PerpetualFuturesApi/IBingXSocketClientPerpetualFuturesApiShared.cs
+++ b/BingX.Net/Interfaces/Clients/PerpetualFuturesApi/IBingXSocketClientPerpetualFuturesApiShared.cs
@@ -11,7 +11,8 @@ public interface IBingXSocketClientPerpetualFuturesApiShared :
IBookTickerSocketClient,
IBalanceSocketClient,
IPositionSocketClient,
- IFuturesOrderSocketClient
+ IFuturesOrderSocketClient,
+ IKlineSocketClient
{
}
}
diff --git a/BingX.Net/Interfaces/Clients/SpotApi/IBingXSocketClientSpotApiShared.cs b/BingX.Net/Interfaces/Clients/SpotApi/IBingXSocketClientSpotApiShared.cs
index c5efcb9..ebfb9a3 100644
--- a/BingX.Net/Interfaces/Clients/SpotApi/IBingXSocketClientSpotApiShared.cs
+++ b/BingX.Net/Interfaces/Clients/SpotApi/IBingXSocketClientSpotApiShared.cs
@@ -10,7 +10,8 @@ public interface IBingXSocketClientSpotApiShared :
ITradeSocketClient,
IBookTickerSocketClient,
IBalanceSocketClient,
- ISpotOrderSocketClient
+ ISpotOrderSocketClient,
+ IKlineSocketClient
{
}
}
diff --git a/BingX.Net/Interfaces/IBingXOrderBookFactory.cs b/BingX.Net/Interfaces/IBingXOrderBookFactory.cs
index cb64e7d..0c8e544 100644
--- a/BingX.Net/Interfaces/IBingXOrderBookFactory.cs
+++ b/BingX.Net/Interfaces/IBingXOrderBookFactory.cs
@@ -1,6 +1,7 @@
using CryptoExchange.Net.Interfaces;
using System;
using BingX.Net.Objects.Options;
+using CryptoExchange.Net.SharedApis;
namespace BingX.Net.Interfaces
{
@@ -18,6 +19,14 @@ public interface IBingXOrderBookFactory
///
public IOrderBookFactory PerpetualFutures { get; }
+ ///
+ /// Create a SymbolOrderBook for the symbol
+ ///
+ /// The symbol
+ /// Book options
+ ///
+ ISymbolOrderBook Create(SharedSymbol symbol, Action? options = null);
+
///
/// Create a new futures local order book instance
///
diff --git a/BingX.Net/Interfaces/IBingXTrackerFactory.cs b/BingX.Net/Interfaces/IBingXTrackerFactory.cs
new file mode 100644
index 0000000..fc481ae
--- /dev/null
+++ b/BingX.Net/Interfaces/IBingXTrackerFactory.cs
@@ -0,0 +1,34 @@
+using CryptoExchange.Net.SharedApis;
+using CryptoExchange.Net.Trackers.Klines;
+using CryptoExchange.Net.Trackers.Trades;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace BingX.Net.Interfaces
+{
+ ///
+ /// Tracker factory
+ ///
+ public interface IBingXTrackerFactory
+ {
+ ///
+ /// Create a new kline tracker
+ ///
+ /// The symbol
+ /// Kline interval
+ /// The max amount of klines to retain
+ /// The max period the data should be retained
+ ///
+ IKlineTracker CreateKlineTracker(SharedSymbol symbol, SharedKlineInterval interval, int? limit = null, TimeSpan? period = null);
+
+ ///
+ /// Create a new trade tracker for a symbol
+ ///
+ /// The symbol
+ /// The max amount of klines to retain
+ /// The max period the data should be retained
+ ///
+ ITradeTracker CreateTradeTracker(SharedSymbol symbol, int? limit = null, TimeSpan? period = null);
+ }
+}
diff --git a/BingX.Net/SymbolOrderBooks/BingXOrderBookFactory.cs b/BingX.Net/SymbolOrderBooks/BingXOrderBookFactory.cs
index e57a474..0bf8d08 100644
--- a/BingX.Net/SymbolOrderBooks/BingXOrderBookFactory.cs
+++ b/BingX.Net/SymbolOrderBooks/BingXOrderBookFactory.cs
@@ -6,6 +6,7 @@
using BingX.Net.Interfaces.Clients;
using BingX.Net.Objects.Options;
using CryptoExchange.Net.OrderBook;
+using CryptoExchange.Net.SharedApis;
namespace BingX.Net.SymbolOrderBooks
{
@@ -24,8 +25,13 @@ public BingXOrderBookFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
- Spot = new OrderBookFactory((symbol, options) => CreateSpot(symbol, options), (baseAsset, quoteAsset, options) => CreateSpot(baseAsset + "-" + quoteAsset, options));
- PerpetualFutures = new OrderBookFactory((symbol, options) => CreatePerpetualFutures(symbol, options), (baseAsset, quoteAsset, options) => CreatePerpetualFutures(baseAsset + quoteAsset, options));
+ Spot = new OrderBookFactory(
+ CreateSpot,
+ (sharedSymbol, options) => CreateSpot(BingXExchange.FormatSymbol(sharedSymbol.BaseAsset, sharedSymbol.QuoteAsset, sharedSymbol.TradingMode, sharedSymbol.DeliverTime), options));
+
+ PerpetualFutures = new OrderBookFactory(
+ CreatePerpetualFutures,
+ (sharedSymbol, options) => CreatePerpetualFutures(BingXExchange.FormatSymbol(sharedSymbol.BaseAsset, sharedSymbol.QuoteAsset, sharedSymbol.TradingMode, sharedSymbol.DeliverTime), options));
}
///
@@ -33,6 +39,16 @@ public BingXOrderBookFactory(IServiceProvider serviceProvider)
///
public IOrderBookFactory PerpetualFutures { get; }
+ ///
+ public ISymbolOrderBook Create(SharedSymbol symbol, Action? options = null)
+ {
+ var symbolName = BingXExchange.FormatSymbol(symbol.BaseAsset, symbol.QuoteAsset, symbol.TradingMode, symbol.DeliverTime);
+ if (symbol.TradingMode == TradingMode.Spot)
+ return CreateSpot(symbolName, options);
+
+ return CreatePerpetualFutures(symbolName, options);
+ }
+
///
public ISymbolOrderBook CreateSpot(string symbol, Action? options = null)
=> new BingXSpotSymbolOrderBook(symbol,
diff --git a/Examples/BingX.Examples.Api/BingX.Examples.Api.csproj b/Examples/BingX.Examples.Api/BingX.Examples.Api.csproj
index 093e78f..da64028 100644
--- a/Examples/BingX.Examples.Api/BingX.Examples.Api.csproj
+++ b/Examples/BingX.Examples.Api/BingX.Examples.Api.csproj
@@ -1,16 +1,19 @@
- net7.0
+ net8.0
enable
enable
true
-
+
+
+
+
diff --git a/Examples/BingX.Examples.Console/BingX.Examples.Console.csproj b/Examples/BingX.Examples.Console/BingX.Examples.Console.csproj
index 3351840..bf23bce 100644
--- a/Examples/BingX.Examples.Console/BingX.Examples.Console.csproj
+++ b/Examples/BingX.Examples.Console/BingX.Examples.Console.csproj
@@ -2,13 +2,13 @@
Exe
- net7.0
+ net8.0
enable
enable
-
+
diff --git a/Examples/BingX.Examples.OrderBook/BingX.Examples.OrderBook.csproj b/Examples/BingX.Examples.OrderBook/BingX.Examples.OrderBook.csproj
new file mode 100644
index 0000000..5a74ae1
--- /dev/null
+++ b/Examples/BingX.Examples.OrderBook/BingX.Examples.OrderBook.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Examples/BingX.Examples.OrderBook/Program.cs b/Examples/BingX.Examples.OrderBook/Program.cs
new file mode 100644
index 0000000..8b21c1e
--- /dev/null
+++ b/Examples/BingX.Examples.OrderBook/Program.cs
@@ -0,0 +1,52 @@
+using BingX.Net.Interfaces;
+using CryptoExchange.Net;
+using CryptoExchange.Net.SharedApis;
+using Microsoft.Extensions.DependencyInjection;
+using Spectre.Console;
+
+var collection = new ServiceCollection();
+collection.AddBingX();
+var provider = collection.BuildServiceProvider();
+
+var bookFactory = provider.GetRequiredService();
+
+// Create and start the order book
+var book = bookFactory.Create(new SharedSymbol(TradingMode.Spot, "ETH", "USDT"));
+var result = await book.StartAsync();
+if (!result.Success)
+{
+ Console.WriteLine(result);
+ return;
+}
+
+// Create Spectre table
+var table = new Table();
+table.ShowRowSeparators = true;
+table.AddColumn("Bid Quantity", x => { x.RightAligned(); })
+ .AddColumn("Bid Price", x => { x.RightAligned(); })
+ .AddColumn("Ask Price", x => { x.LeftAligned(); })
+ .AddColumn("Ask Quantity", x => { x.LeftAligned(); });
+
+for(var i = 0; i < 10; i++)
+ table.AddEmptyRow();
+
+await AnsiConsole.Live(table)
+ .StartAsync(async ctx =>
+ {
+ while (true)
+ {
+ var snapshot = book.Book;
+ for (var i = 0; i < 10; i++)
+ {
+ var bid = snapshot.bids.ElementAt(i);
+ var ask = snapshot.asks.ElementAt(i);
+ table.UpdateCell(i, 0, ExchangeHelpers.Normalize(bid.Quantity).ToString());
+ table.UpdateCell(i, 1, ExchangeHelpers.Normalize(bid.Price).ToString());
+ table.UpdateCell(i, 2, ExchangeHelpers.Normalize(ask.Price).ToString());
+ table.UpdateCell(i, 3, ExchangeHelpers.Normalize(ask.Quantity).ToString());
+ }
+
+ ctx.Refresh();
+ await Task.Delay(500);
+ }
+ });
diff --git a/Examples/BingX.Examples.Tracker/BingX.Examples.Tracker.csproj b/Examples/BingX.Examples.Tracker/BingX.Examples.Tracker.csproj
new file mode 100644
index 0000000..5a74ae1
--- /dev/null
+++ b/Examples/BingX.Examples.Tracker/BingX.Examples.Tracker.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Examples/BingX.Examples.Tracker/Program.cs b/Examples/BingX.Examples.Tracker/Program.cs
new file mode 100644
index 0000000..5e7495f
--- /dev/null
+++ b/Examples/BingX.Examples.Tracker/Program.cs
@@ -0,0 +1,104 @@
+using BingX.Net.Interfaces;
+using CryptoExchange.Net.SharedApis;
+using Microsoft.Extensions.DependencyInjection;
+using Spectre.Console;
+using System.Globalization;
+
+var collection = new ServiceCollection();
+collection.AddBingX();
+var provider = collection.BuildServiceProvider();
+
+var trackerFactory = provider.GetRequiredService();
+
+// Create and start the tracker, keep track of the last 10 minutes
+var tracker = trackerFactory.CreateTradeTracker(new SharedSymbol(TradingMode.Spot, "ETH", "USDT"), period: TimeSpan.FromMinutes(10));
+var result = await tracker.StartAsync();
+if (!result.Success)
+{
+ Console.WriteLine(result);
+ return;
+}
+
+// Create Spectre table
+var table = new Table();
+table.ShowRowSeparators = true;
+table.AddColumn("5 Min Data").AddColumn("-5 Min", x => { x.RightAligned(); })
+ .AddColumn("Now", x => { x.RightAligned(); })
+ .AddColumn("Dif", x => { x.RightAligned(); });
+
+table.AddRow("Count", "", "", "");
+table.AddRow("Average price", "", "", "");
+table.AddRow("Average weighted price", "", "", "");
+table.AddRow("Buy/Sell Ratio", "", "", "");
+table.AddRow("Volume", "", "", "");
+table.AddRow("Value", "", "", "");
+table.AddRow("Complete", "", "", "");
+table.AddRow("", "", "", "");
+table.AddRow("Status", "", "", "");
+table.AddRow("Synced From", "", "", "");
+
+// Set default culture for currency display
+CultureInfo ci = new CultureInfo("en-US");
+Thread.CurrentThread.CurrentCulture = ci;
+Thread.CurrentThread.CurrentUICulture = ci;
+
+await AnsiConsole.Live(table)
+ .StartAsync(async ctx =>
+ {
+ while (true)
+ {
+ // Get the stats from 10 minutes until 5 minutes ago
+ var secondLastMinute = tracker.GetStats(DateTime.UtcNow.AddMinutes(-10), DateTime.UtcNow.AddMinutes(-5));
+
+ // Get the stats from 5 minutes ago until now
+ var lastMinute = tracker.GetStats(DateTime.UtcNow.AddMinutes(-5));
+
+ // Get the differences between them
+ var compare = secondLastMinute.CompareTo(lastMinute);
+
+ // Update the columns
+ UpdateDec(0, 1, secondLastMinute.TradeCount);
+ UpdateDec(0, 2, lastMinute.TradeCount);
+ UpdateStr(0, 3, $"[{(compare.TradeCountDif.Difference < 0 ? "red" : "green")}]{compare.TradeCountDif.Difference} / {compare.TradeCountDif.PercentageDifference}%[/]");
+
+ UpdateStr(1, 1, secondLastMinute.AveragePrice?.ToString("C"));
+ UpdateStr(1, 2, lastMinute.AveragePrice?.ToString("C"));
+ UpdateStr(1, 3, $"[{(compare.AveragePriceDif?.Difference < 0 ? "red" : "green")}]{compare.AveragePriceDif?.Difference?.ToString("C")} / {compare.AveragePriceDif?.PercentageDifference}%[/]");
+
+ UpdateStr(2, 1, secondLastMinute.VolumeWeightedAveragePrice?.ToString("C"));
+ UpdateStr(2, 2, lastMinute.VolumeWeightedAveragePrice?.ToString("C"));
+ UpdateStr(2, 3, $"[{(compare.VolumeWeightedAveragePriceDif?.Difference < 0 ? "red" : "green")}]{compare.VolumeWeightedAveragePriceDif?.Difference?.ToString("C")} / {compare.VolumeWeightedAveragePriceDif?.PercentageDifference}%[/]");
+
+ UpdateDec(3, 1, secondLastMinute.BuySellRatio);
+ UpdateDec(3, 2, lastMinute.BuySellRatio);
+ UpdateStr(3, 3, $"[{(compare.BuySellRatioDif?.Difference < 0 ? "red" : "green")}]{compare.BuySellRatioDif?.Difference} / {compare.BuySellRatioDif?.PercentageDifference}%[/]");
+
+ UpdateDec(4, 1, secondLastMinute.Volume);
+ UpdateDec(4, 2, lastMinute.Volume);
+ UpdateStr(4, 3, $"[{(compare.VolumeDif.Difference < 0 ? "red" : "green")}]{compare.VolumeDif.Difference} / {compare.VolumeDif.PercentageDifference}%[/]");
+
+ UpdateStr(5, 1, secondLastMinute.QuoteVolume.ToString("C"));
+ UpdateStr(5, 2, lastMinute.QuoteVolume.ToString("C"));
+ UpdateStr(5, 3, $"[{(compare.QuoteVolumeDif.Difference < 0 ? "red" : "green")}]{compare.QuoteVolumeDif.Difference?.ToString("C")} / {compare.QuoteVolumeDif.PercentageDifference}%[/]");
+
+ UpdateStr(6, 1, secondLastMinute.Complete.ToString());
+ UpdateStr(6, 2, lastMinute.Complete.ToString());
+
+ UpdateStr(8, 1, tracker.Status.ToString());
+ UpdateStr(9, 1, tracker.SyncedFrom?.ToString());
+
+ ctx.Refresh();
+ await Task.Delay(500);
+ }
+ });
+
+
+void UpdateDec(int row, int col, decimal? val)
+{
+ table.UpdateCell(row, col, val?.ToString() ?? string.Empty);
+}
+
+void UpdateStr(int row, int col, string? val)
+{
+ table.UpdateCell(row, col, val ?? string.Empty);
+}
diff --git a/Examples/Examples.sln b/Examples/Examples.sln
index 590ca1c..084dfbc 100644
--- a/Examples/Examples.sln
+++ b/Examples/Examples.sln
@@ -7,6 +7,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BingX.Examples.Api", "BingX
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BingX.Examples.Console", "BingX.Examples.Console\BingX.Examples.Console.csproj", "{FD4F95C8-D9B7-4F81-9245-4CE667DFD421}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BingX.Net", "..\BingX.Net\BingX.Net.csproj", "{90651429-48DD-454F-BEAA-14058E43877A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BingX.Examples.OrderBook", "BingX.Examples.OrderBook\BingX.Examples.OrderBook.csproj", "{33D58C29-15EB-48E2-A6E9-FC9FD84B43E8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BingX.Examples.Tracker", "BingX.Examples.Tracker\BingX.Examples.Tracker.csproj", "{0051E490-FA97-4B88-BB14-30AAF17DAAA1}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +27,18 @@ Global
{FD4F95C8-D9B7-4F81-9245-4CE667DFD421}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FD4F95C8-D9B7-4F81-9245-4CE667DFD421}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FD4F95C8-D9B7-4F81-9245-4CE667DFD421}.Release|Any CPU.Build.0 = Release|Any CPU
+ {90651429-48DD-454F-BEAA-14058E43877A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {90651429-48DD-454F-BEAA-14058E43877A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {90651429-48DD-454F-BEAA-14058E43877A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {90651429-48DD-454F-BEAA-14058E43877A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {33D58C29-15EB-48E2-A6E9-FC9FD84B43E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {33D58C29-15EB-48E2-A6E9-FC9FD84B43E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {33D58C29-15EB-48E2-A6E9-FC9FD84B43E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {33D58C29-15EB-48E2-A6E9-FC9FD84B43E8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0051E490-FA97-4B88-BB14-30AAF17DAAA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0051E490-FA97-4B88-BB14-30AAF17DAAA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0051E490-FA97-4B88-BB14-30AAF17DAAA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0051E490-FA97-4B88-BB14-30AAF17DAAA1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Examples/README.md b/Examples/README.md
index ba25ea5..bd922af 100644
--- a/Examples/README.md
+++ b/Examples/README.md
@@ -4,4 +4,10 @@
A minimal API showing how to integrate BingX.Net in a web API project
### BingX.Examples.Console
-A simple console client demonstrating basic usage
\ No newline at end of file
+A simple console client demonstrating basic usage
+
+### BingX.Examples.OrderBook
+Example of using the client side order book implementation
+
+### BingX.Examples.Tracker
+Example of using the trade tracker
\ No newline at end of file