diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs
index 7821988..3ce68c8 100644
--- a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs
+++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs
@@ -59,7 +59,8 @@ internal CoinExRestClientFuturesApi(ILogger logger, HttpClient? httpClient, Coin
#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)
+ => CoinExExchange.FormatSymbol(baseAsset, quoteAsset, tradingMode, deliverTime);
///
protected override IStreamMessageAccessor CreateAccessor() => new SystemTextJsonStreamMessageAccessor();
diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiShared.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiShared.cs
index 510cf73..77e001d 100644
--- a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiShared.cs
+++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiShared.cs
@@ -584,7 +584,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.Side == OrderSide.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell
+ }).ToArray());
}
#endregion
diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs b/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs
index 1d0913f..25c00af 100644
--- a/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs
+++ b/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs
@@ -52,8 +52,9 @@ protected override AuthenticationProvider CreateAuthenticationProvider(ApiCreden
=> new CoinExV2AuthenticationProvider(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)
+ => CoinExExchange.FormatSymbol(baseAsset, quoteAsset, tradingMode, deliverTime);
+
#region methods
///
diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApiShared.cs b/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApiShared.cs
index cd18a3e..6d6b719 100644
--- a/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApiShared.cs
+++ b/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApiShared.cs
@@ -63,7 +63,10 @@ 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.Timestamp)))), ct).ConfigureAwait(false);
+ var result = await SubscribeToTradeUpdatesAsync(symbol, update => handler(update.AsExchangeEvent(Exchange, update.Data.Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)
+ {
+ Side = x.Side == OrderSide.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell
+ }))), ct).ConfigureAwait(false);
return new ExchangeResult(Exchange, result);
}
diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs
index c7f7049..6a079a9 100644
--- a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs
+++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs
@@ -82,7 +82,8 @@ protected override AuthenticationProvider CreateAuthenticationProvider(ApiCreden
=> new CoinExV2AuthenticationProvider(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)
+ => CoinExExchange.FormatSymbol(baseAsset, quoteAsset, tradingMode, deliverTime);
#region methods
internal async Task ExecuteAsync(Uri uri, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false)
diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiShared.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiShared.cs
index 892eca0..4fa2ea0 100644
--- a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiShared.cs
+++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiShared.cs
@@ -149,7 +149,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.Side == OrderSide.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell
+ }).ToArray());
}
#endregion
diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs
index 4052f20..a528065 100644
--- a/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs
+++ b/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs
@@ -54,8 +54,9 @@ protected override AuthenticationProvider CreateAuthenticationProvider(ApiCreden
public ICoinExSocketClientSpotApiShared SharedClient => this;
///
- 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)
+ => CoinExExchange.FormatSymbol(baseAsset, quoteAsset, tradingMode, deliverTime);
+
#region methods
///
diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApiShared.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApiShared.cs
index 73e9a6d..bf290ce 100644
--- a/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApiShared.cs
+++ b/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApiShared.cs
@@ -69,7 +69,10 @@ async Task> ITradeSocketClient.SubscribeToTra
if (update.UpdateType == SocketUpdateType.Snapshot)
return;
- handler(update.AsExchangeEvent>(Exchange, update.Data.Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)).ToArray()));
+ handler(update.AsExchangeEvent>(Exchange, update.Data.Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)
+ {
+ Side = x.Side == OrderSide.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell
+ }).ToArray()));
}, ct).ConfigureAwait(false);
return new ExchangeResult(Exchange, result);
diff --git a/CoinEx.Net/CoinEx.Net.csproj b/CoinEx.Net/CoinEx.Net.csproj
index f5d5982..fc8d82a 100644
--- a/CoinEx.Net/CoinEx.Net.csproj
+++ b/CoinEx.Net/CoinEx.Net.csproj
@@ -1,4 +1,4 @@
-
+
netstandard2.0;netstandard2.1
10.0
@@ -48,11 +48,11 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
\ No newline at end of file
diff --git a/CoinEx.Net/CoinEx.Net.xml b/CoinEx.Net/CoinEx.Net.xml
index bbf722c..8715b57 100644
--- a/CoinEx.Net/CoinEx.Net.xml
+++ b/CoinEx.Net/CoinEx.Net.xml
@@ -914,6 +914,16 @@
Urls to the API documentation
+
+
+ Format a base and quote asset to a CoinEx recognized symbol
+
+ Base asset
+ Quote asset
+ Trading mode
+ Delivery time for delivery futures
+
+
CoinEx helpers
@@ -933,6 +943,23 @@
+
+
+
+
+
+ ctor
+
+
+
+
+ ctor
+
+ Service provider for resolving logging and clients
+
+
+
+
Account type
@@ -3710,6 +3737,14 @@
Futures order book factory methods
+
+
+ Create a SymbolOrderBook for the symbol
+
+ The symbol
+ Book options
+
+
Create a SymbolOrderBook for the Spot API
@@ -3726,6 +3761,20 @@
Order book options
+
+
+ Tracker factory
+
+
+
+
+ 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 usable for the CoinEx clients
@@ -7579,6 +7628,9 @@
Service provider for resolving logging and clients
+
+
+
diff --git a/CoinEx.Net/CoinExExchange.cs b/CoinEx.Net/CoinExExchange.cs
index fd779b9..be6936e 100644
--- a/CoinEx.Net/CoinExExchange.cs
+++ b/CoinEx.Net/CoinExExchange.cs
@@ -1,4 +1,7 @@
-namespace CoinEx.Net
+using CryptoExchange.Net.SharedApis;
+using System;
+
+namespace CoinEx.Net
{
///
/// CoinEx exchange information and configuration
@@ -22,5 +25,18 @@ public static class CoinExExchange
"https://viabtc.github.io/coinex_api_en_doc/",
"https://docs.coinex.com/api/v2/"
};
+
+ ///
+ /// Format a base and quote asset to a CoinEx 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()}";
+ }
}
}
diff --git a/CoinEx.Net/CoinExTrackerFactory.cs b/CoinEx.Net/CoinExTrackerFactory.cs
new file mode 100644
index 0000000..8f055e6
--- /dev/null
+++ b/CoinEx.Net/CoinExTrackerFactory.cs
@@ -0,0 +1,63 @@
+using CoinEx.Net.Clients;
+using CoinEx.Net.Interfaces;
+using CoinEx.Net.Interfaces.Clients;
+using CryptoExchange.Net.SharedApis;
+using CryptoExchange.Net.Trackers.Trades;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using System;
+
+namespace CoinEx.Net
+{
+ ///
+ public class CoinExTrackerFactory : ICoinExTrackerFactory
+ {
+ private readonly IServiceProvider? _serviceProvider;
+
+ ///
+ /// ctor
+ ///
+ public CoinExTrackerFactory()
+ {
+ }
+
+ ///
+ /// ctor
+ ///
+ /// Service provider for resolving logging and clients
+ public CoinExTrackerFactory(IServiceProvider serviceProvider)
+ {
+ _serviceProvider = serviceProvider;
+ }
+
+ ///
+ public ITradeTracker CreateTradeTracker(SharedSymbol symbol, int? limit = null, TimeSpan? period = null)
+ {
+ var restClient = _serviceProvider?.GetRequiredService() ?? new CoinExRestClient();
+ var socketClient = _serviceProvider?.GetRequiredService() ?? new CoinExSocketClient();
+
+ IRecentTradeRestClient sharedRestClient;
+ ITradeSocketClient sharedSocketClient;
+ if (symbol.TradingMode == TradingMode.Spot)
+ {
+ sharedRestClient = restClient.SpotApiV2.SharedClient;
+ sharedSocketClient = socketClient.SpotApiV2.SharedClient;
+ }
+ else
+ {
+ sharedRestClient = restClient.FuturesApi.SharedClient;
+ sharedSocketClient = socketClient.FuturesApi.SharedClient;
+ }
+
+ return new TradeTracker(
+ _serviceProvider?.GetRequiredService().CreateLogger(restClient.Exchange),
+ sharedRestClient,
+ null,
+ sharedSocketClient,
+ symbol,
+ limit,
+ period
+ );
+ }
+ }
+}
diff --git a/CoinEx.Net/ExtensionMethods/ServiceCollectionExtensions.cs b/CoinEx.Net/ExtensionMethods/ServiceCollectionExtensions.cs
index 336ca99..ec9b424 100644
--- a/CoinEx.Net/ExtensionMethods/ServiceCollectionExtensions.cs
+++ b/CoinEx.Net/ExtensionMethods/ServiceCollectionExtensions.cs
@@ -1,4 +1,5 @@
-using CoinEx.Net.Clients;
+using CoinEx.Net;
+using CoinEx.Net.Clients;
using CoinEx.Net.Interfaces;
using CoinEx.Net.Interfaces.Clients;
using CoinEx.Net.Objects.Options;
@@ -61,6 +62,7 @@ public static IServiceCollection AddCoinEx(
services.AddTransient();
services.AddTransient();
services.AddTransient();
+ services.AddTransient();
services.AddTransient(x => x.GetRequiredService().SpotApiV2.CommonSpotClient);
services.RegisterSharedRestInterfaces(x => x.GetRequiredService().SpotApiV2.SharedClient);
diff --git a/CoinEx.Net/Interfaces/ICoinExOrderBookFactory.cs b/CoinEx.Net/Interfaces/ICoinExOrderBookFactory.cs
index f78f32b..f44ff76 100644
--- a/CoinEx.Net/Interfaces/ICoinExOrderBookFactory.cs
+++ b/CoinEx.Net/Interfaces/ICoinExOrderBookFactory.cs
@@ -1,5 +1,6 @@
using CoinEx.Net.Objects.Options;
using CryptoExchange.Net.Interfaces;
+using CryptoExchange.Net.SharedApis;
using System;
namespace CoinEx.Net.Interfaces
@@ -19,6 +20,14 @@ public interface ICoinExOrderBookFactory
///
public IOrderBookFactory Futures { get; }
+ ///
+ /// Create a SymbolOrderBook for the symbol
+ ///
+ /// The symbol
+ /// Book options
+ ///
+ ISymbolOrderBook Create(SharedSymbol symbol, Action? options = null);
+
///
/// Create a SymbolOrderBook for the Spot API
///
diff --git a/CoinEx.Net/Interfaces/ICoinExTrackerFactory.cs b/CoinEx.Net/Interfaces/ICoinExTrackerFactory.cs
new file mode 100644
index 0000000..36dc6b7
--- /dev/null
+++ b/CoinEx.Net/Interfaces/ICoinExTrackerFactory.cs
@@ -0,0 +1,24 @@
+using CryptoExchange.Net.SharedApis;
+using CryptoExchange.Net.Trackers.Klines;
+using CryptoExchange.Net.Trackers.Trades;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace CoinEx.Net.Interfaces
+{
+ ///
+ /// Tracker factory
+ ///
+ public interface ICoinExTrackerFactory
+ {
+ ///
+ /// 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/CoinEx.Net/SymbolOrderBooks/CoinExOrderBookFactory.cs b/CoinEx.Net/SymbolOrderBooks/CoinExOrderBookFactory.cs
index df0b4ad..94b3329 100644
--- a/CoinEx.Net/SymbolOrderBooks/CoinExOrderBookFactory.cs
+++ b/CoinEx.Net/SymbolOrderBooks/CoinExOrderBookFactory.cs
@@ -3,6 +3,7 @@
using CoinEx.Net.Objects.Options;
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.OrderBook;
+using CryptoExchange.Net.SharedApis;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
@@ -28,8 +29,22 @@ public CoinExOrderBookFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
- Spot = new OrderBookFactory((symbol, options) => CreateSpot(symbol, options), (baseAsset, quoteAsset, options) => CreateSpot(baseAsset + quoteAsset, options));
- Futures = new OrderBookFactory((symbol, options) => CreateFutures(symbol, options), (baseAsset, quoteAsset, options) => CreateFutures(baseAsset + quoteAsset, options));
+ Spot = new OrderBookFactory(
+ CreateSpot,
+ (sharedSymbol, options) => CreateSpot(CoinExExchange.FormatSymbol(sharedSymbol.BaseAsset, sharedSymbol.QuoteAsset, sharedSymbol.TradingMode, sharedSymbol.DeliverTime), options));
+ Futures = new OrderBookFactory(
+ CreateFutures,
+ (sharedSymbol, options) => CreateFutures(CoinExExchange.FormatSymbol(sharedSymbol.BaseAsset, sharedSymbol.QuoteAsset, sharedSymbol.TradingMode, sharedSymbol.DeliverTime), options));
+ }
+
+ ///
+ public ISymbolOrderBook Create(SharedSymbol symbol, Action? options = null)
+ {
+ var symbolName = CoinExExchange.FormatSymbol(symbol.BaseAsset, symbol.QuoteAsset, symbol.TradingMode, symbol.DeliverTime);
+ if (symbol.TradingMode == TradingMode.Spot)
+ return CreateSpot(symbolName, options);
+
+ return CreateFutures(symbolName, options);
}
///
diff --git a/Examples/CoinEx.Examples.Api/CoinEx.Examples.Api.csproj b/Examples/CoinEx.Examples.Api/CoinEx.Examples.Api.csproj
index 11b593c..c650731 100644
--- a/Examples/CoinEx.Examples.Api/CoinEx.Examples.Api.csproj
+++ b/Examples/CoinEx.Examples.Api/CoinEx.Examples.Api.csproj
@@ -1,16 +1,19 @@
- net7.0
+ net8.0
enable
enable
true
-
+
+
+
+
diff --git a/Examples/CoinEx.Examples.Console/CoinEx.Examples.Console.csproj b/Examples/CoinEx.Examples.Console/CoinEx.Examples.Console.csproj
index 70a2eaf..5e0d759 100644
--- a/Examples/CoinEx.Examples.Console/CoinEx.Examples.Console.csproj
+++ b/Examples/CoinEx.Examples.Console/CoinEx.Examples.Console.csproj
@@ -2,13 +2,13 @@
Exe
- net7.0
+ net8.0
enable
enable
-
+
diff --git a/Examples/CoinEx.Examples.OrderBook/CoinEx.Examples.OrderBook.csproj b/Examples/CoinEx.Examples.OrderBook/CoinEx.Examples.OrderBook.csproj
new file mode 100644
index 0000000..77d2dd5
--- /dev/null
+++ b/Examples/CoinEx.Examples.OrderBook/CoinEx.Examples.OrderBook.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Examples/CoinEx.Examples.OrderBook/Program.cs b/Examples/CoinEx.Examples.OrderBook/Program.cs
new file mode 100644
index 0000000..9bbba19
--- /dev/null
+++ b/Examples/CoinEx.Examples.OrderBook/Program.cs
@@ -0,0 +1,52 @@
+using CoinEx.Net.Interfaces;
+using CryptoExchange.Net;
+using CryptoExchange.Net.SharedApis;
+using Microsoft.Extensions.DependencyInjection;
+using Spectre.Console;
+
+var collection = new ServiceCollection();
+collection.AddCoinEx();
+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/CoinEx.Examples.Tracker/CoinEx.Examples.Tracker.csproj b/Examples/CoinEx.Examples.Tracker/CoinEx.Examples.Tracker.csproj
new file mode 100644
index 0000000..77d2dd5
--- /dev/null
+++ b/Examples/CoinEx.Examples.Tracker/CoinEx.Examples.Tracker.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Examples/CoinEx.Examples.Tracker/Program.cs b/Examples/CoinEx.Examples.Tracker/Program.cs
new file mode 100644
index 0000000..6d3551c
--- /dev/null
+++ b/Examples/CoinEx.Examples.Tracker/Program.cs
@@ -0,0 +1,111 @@
+using CoinEx.Net.Interfaces;
+using CryptoExchange.Net.Objects;
+using CryptoExchange.Net.SharedApis;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Spectre.Console;
+using System.Globalization;
+
+var collection = new ServiceCollection();
+collection.AddCoinEx();
+collection.AddLogging(x =>
+{
+ x.SetMinimumLevel(LogLevel.Trace);
+ x.AddProvider(new TraceLoggerProvider());
+});
+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 94e1a2a..a87247e 100644
--- a/Examples/Examples.sln
+++ b/Examples/Examples.sln
@@ -7,6 +7,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoinEx.Examples.Api", "Coin
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoinEx.Examples.Console", "CoinEx.Examples.Console\CoinEx.Examples.Console.csproj", "{FD4F95C8-D9B7-4F81-9245-4CE667DFD421}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoinEx.Net", "..\CoinEx.Net\CoinEx.Net.csproj", "{22411347-12D7-4993-A7BA-E8CC6D52CA4D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoinEx.Examples.OrderBook", "CoinEx.Examples.OrderBook\CoinEx.Examples.OrderBook.csproj", "{2668FF12-1C29-4CB0-B944-A938805CDF66}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoinEx.Examples.Tracker", "CoinEx.Examples.Tracker\CoinEx.Examples.Tracker.csproj", "{93B8A062-C9D7-497E-BD98-ACBF40666D8A}"
+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
+ {22411347-12D7-4993-A7BA-E8CC6D52CA4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {22411347-12D7-4993-A7BA-E8CC6D52CA4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {22411347-12D7-4993-A7BA-E8CC6D52CA4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {22411347-12D7-4993-A7BA-E8CC6D52CA4D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2668FF12-1C29-4CB0-B944-A938805CDF66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2668FF12-1C29-4CB0-B944-A938805CDF66}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2668FF12-1C29-4CB0-B944-A938805CDF66}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2668FF12-1C29-4CB0-B944-A938805CDF66}.Release|Any CPU.Build.0 = Release|Any CPU
+ {93B8A062-C9D7-497E-BD98-ACBF40666D8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {93B8A062-C9D7-497E-BD98-ACBF40666D8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {93B8A062-C9D7-497E-BD98-ACBF40666D8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {93B8A062-C9D7-497E-BD98-ACBF40666D8A}.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 c4a348c..db6b41d 100644
--- a/Examples/README.md
+++ b/Examples/README.md
@@ -4,4 +4,10 @@
A minimal API showing how to integrate CoinEx.Net in a web API project
### CoinEx.Examples.Console
-A simple console client demonstrating basic usage
\ No newline at end of file
+A simple console client demonstrating basic usage
+
+### CoinEx.Examples.OrderBook
+Example of using the client side order book implementation
+
+### CoinEx.Examples.Tracker
+Example of using the trade tracker
\ No newline at end of file