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