From 75087a358a79acecdbbdf03ec0d6ab1d83ae38e2 Mon Sep 17 00:00:00 2001 From: JKorf Date: Thu, 28 Mar 2024 21:58:42 +0100 Subject: [PATCH 01/17] wip --- CoinEx.Net/Clients/CoinExRestClient.cs | 4 + CoinEx.Net/Clients/CoinExSocketClient.cs | 7 +- .../SpotApi/CoinExRestClientSpotApi.cs | 710 +++++++++--------- .../SpotApi/CoinExRestClientSpotApiAccount.cs | 89 +-- .../CoinExRestClientSpotApiExchangeData.cs | 134 +--- .../SpotApi/CoinExRestClientSpotApiTrading.cs | 198 +---- .../SpotApiV1/CoinExRestClientSpotApiV1.cs | 464 ++++++++++++ .../CoinExRestClientSpotApiV1Account.cs | 107 +++ .../CoinExRestClientSpotApiV1ExchangeData.cs | 154 ++++ .../CoinExRestClientSpotApiV1Trading.cs | 227 ++++++ .../CoinExSocketClientSpotApiV1.cs} | 6 +- CoinEx.Net/CoinEx.Net.xml | 482 +++++++++--- .../ServiceCollectionExtensions.cs | 2 +- .../Interfaces/Clients/ICoinExRestClient.cs | 6 +- .../Interfaces/Clients/ICoinExSocketClient.cs | 2 +- .../SpotApi/ICoinExClientSpotApiAccount.cs | 63 +- .../ICoinExClientSpotApiExchangeData.cs | 102 +-- .../SpotApi/ICoinExClientSpotApiTrading.cs | 125 +-- .../SpotApiV1/ICoinExClientSpotApiV1.cs | 33 + .../ICoinExClientSpotApiV1Account.cs | 77 ++ .../ICoinExClientSpotApiV1ExchangeData.cs | 114 +++ .../ICoinExClientSpotApiV1Trading.cs | 139 ++++ .../ICoinExSocketClientSpotApiV1.cs} | 2 +- .../Objects/Internal/CoinExApiResult.cs | 7 +- CoinEx.Net/Objects/Models/V2/CoinExSymbol.cs | 61 ++ CoinEx.Net/Objects/Models/V2/CoinExTicker.cs | 66 ++ .../CoinExSpotSymbolOrderBook.cs | 2 +- 27 files changed, 2199 insertions(+), 1184 deletions(-) create mode 100644 CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1.cs create mode 100644 CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Account.cs create mode 100644 CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1ExchangeData.cs create mode 100644 CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Trading.cs rename CoinEx.Net/Clients/{SpotApi/CoinExSocketClientSpotApi.cs => SpotApiV1/CoinExSocketClientSpotApiV1.cs} (97%) create mode 100644 CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1.cs create mode 100644 CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1Account.cs create mode 100644 CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1ExchangeData.cs create mode 100644 CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1Trading.cs rename CoinEx.Net/Interfaces/Clients/{SpotApi/ICoinExSocketClientSpotApi.cs => SpotApiV1/ICoinExSocketClientSpotApiV1.cs} (99%) create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExSymbol.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExTicker.cs diff --git a/CoinEx.Net/Clients/CoinExRestClient.cs b/CoinEx.Net/Clients/CoinExRestClient.cs index aa45c98..9c7b415 100644 --- a/CoinEx.Net/Clients/CoinExRestClient.cs +++ b/CoinEx.Net/Clients/CoinExRestClient.cs @@ -15,6 +15,8 @@ public class CoinExRestClient : BaseRestClient, ICoinExRestClient { #region Api clients /// + public ICoinExClientSpotApiV1 SpotApiV1 { get; } + /// public ICoinExClientSpotApi SpotApi { get; } #endregion @@ -42,6 +44,7 @@ public CoinExRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, A Initialize(options); SpotApi = AddApiClient(new CoinExRestClientSpotApi(_logger, httpClient, options)); + SpotApiV1 = AddApiClient(new CoinExRestClientSpotApiV1(_logger, httpClient, options)); } #endregion @@ -61,6 +64,7 @@ public static void SetDefaultOptions(Action optionsDelegate) public void SetApiCredentials(ApiCredentials credentials) { SpotApi.SetApiCredentials(credentials); + SpotApiV1.SetApiCredentials(credentials); } #endregion } diff --git a/CoinEx.Net/Clients/CoinExSocketClient.cs b/CoinEx.Net/Clients/CoinExSocketClient.cs index da2cdde..9690dc1 100644 --- a/CoinEx.Net/Clients/CoinExSocketClient.cs +++ b/CoinEx.Net/Clients/CoinExSocketClient.cs @@ -15,7 +15,8 @@ public class CoinExSocketClient : BaseSocketClient, ICoinExSocketClient #region Api clients /// - public ICoinExSocketClientSpotApi SpotApi { get; } + //public ICoinExSocketClientSpotApi SpotApi { get; } + public ICoinExSocketClientSpotApiV1 SpotApiV1 { get; } #endregion @@ -48,7 +49,7 @@ public CoinExSocketClient(Action optionsDelegate, ILoggerFa optionsDelegate(options); Initialize(options); - SpotApi = AddApiClient(new CoinExSocketClientSpotApi(_logger, options)); + SpotApiV1 = AddApiClient(new CoinExSocketClientSpotApiV1(_logger, options)); } #endregion @@ -66,7 +67,7 @@ public static void SetDefaultOptions(Action optionsDelegate /// public void SetApiCredentials(ApiCredentials credentials) { - SpotApi.SetApiCredentials(credentials); + SpotApiV1.SetApiCredentials(credentials); } } } diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs index 3c792db..9747450 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs @@ -20,11 +20,12 @@ using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Converters.MessageParsing; using CryptoExchange.Net.Clients; +using CryptoExchange.Net.Converters.SystemTextJson; namespace CoinEx.Net.Clients.SpotApi { - /// - public class CoinExRestClientSpotApi : RestApiClient, ICoinExClientSpotApi, ISpotClient + /// + public class CoinExRestClientSpotApi : RestApiClient, ICoinExClientSpotApi//, ISpotClient { #region fields /// @@ -69,13 +70,17 @@ internal CoinExRestClientSpotApi(ILogger logger, HttpClient? httpClient, CoinExR } #endregion + /// + protected override IStreamMessageAccessor CreateAccessor() => new SystemTextJsonStreamMessageAccessor(); + /// + protected override IMessageSerializer CreateSerializer() => new SystemTextJsonMessageSerializer(); + /// protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials) => new CoinExAuthenticationProvider(credentials, ClientOptions.NonceProvider ?? new CoinExNonceProvider()); #region methods - #region private - internal async Task> Execute(Uri uri, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false) where T : class + internal async Task> ExecuteAsync(Uri uri, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false) where T : class { var result = await SendRequestAsync>(uri, method, ct, parameters, signed).ConfigureAwait(false); if (!result) @@ -87,370 +92,341 @@ internal async Task> Execute(Uri uri, HttpMethod method, Can return result.As(result.Data.Data); } - internal async Task Execute(Uri uri, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false) - { - var result = await SendRequestAsync(uri, method, ct, parameters, signed).ConfigureAwait(false); - if (!result) - return result.AsDataless(); - - if (result.Data.Code != 0) - return result.AsDatalessError(new ServerError(result.Data.Code, result.Data.Message!)); - - return result.AsDataless(); - } - - internal async Task>> ExecutePaged(Uri uri, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false) where T : class - { - var result = await SendRequestAsync>>(uri, method, ct, parameters, signed).ConfigureAwait(false); - if (!result) - return result.As>(default); - - if (result.Data.Code != 0) - return result.AsError>(new ServerError(result.Data.Code, result.Data.Message!)); - - return result.As(result.Data.Data); - } - - internal Uri GetUrl(string endpoint) - { - return new Uri(BaseAddress.AppendPath("v1").AppendPath(endpoint)); - } - - internal void InvokeOrderPlaced(OrderId id) - { - OnOrderPlaced?.Invoke(id); - } - - internal void InvokeOrderCanceled(OrderId id) - { - OnOrderCanceled?.Invoke(id); - } - - #endregion - #endregion - - #region common interface - - /// - /// Get the name of a symbol for CoinEx based on the base and quote asset - /// - /// - /// - /// - public string GetSymbolName(string baseAsset, string quoteAsset) => (baseAsset + quoteAsset).ToUpperInvariant(); - - async Task>> IBaseRestClient.GetSymbolsAsync(CancellationToken ct) - { - var symbols = await ExchangeData.GetSymbolInfoAsync(ct: ct).ConfigureAwait(false); - if (!symbols) - return symbols.As>(null); - - return symbols.As(symbols.Data.Select(d => new Symbol - { - SourceObject = d, - Name = d.Key, - MinTradeQuantity = d.Value.MinQuantity, - PriceDecimals = d.Value.PricingDecimal, - QuantityDecimals = d.Value.TradingDecimal - })); - } - - async Task> IBaseRestClient.GetTickerAsync(string symbol, CancellationToken ct) - { - if (string.IsNullOrEmpty(symbol)) - throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetTickerAsync), nameof(symbol)); - - var tickers = await ExchangeData.GetTickerAsync(symbol, ct: ct).ConfigureAwait(false); - if (!tickers) - return tickers.As(null); - - return tickers.As(new Ticker - { - SourceObject = tickers.Data, - Symbol = symbol, - HighPrice = tickers.Data.Ticker.HighPrice, - LowPrice = tickers.Data.Ticker.LowPrice, - LastPrice = tickers.Data.Ticker.LastPrice, - Price24H = tickers.Data.Ticker.OpenPrice, - Volume = tickers.Data.Ticker.Volume - }); - } - - async Task>> IBaseRestClient.GetTickersAsync(CancellationToken ct) - { - var tickers = await ExchangeData.GetTickersAsync(ct: ct).ConfigureAwait(false); - if (!tickers) - return tickers.As>(null); - - return tickers.As(tickers.Data.Tickers.Select(t => - new Ticker - { - SourceObject = t, - Symbol = t.Key, - HighPrice = t.Value.HighPrice, - LowPrice = t.Value.LowPrice, - LastPrice = t.Value.LastPrice, - Price24H = t.Value.OpenPrice, - Volume = t.Value.Volume - })); - } - - async Task>> IBaseRestClient.GetKlinesAsync(string symbol, TimeSpan timespan, DateTime? startTime, DateTime? endTime, int? limit, CancellationToken ct) - { - if (startTime != null || endTime != null) - throw new ArgumentException("CoinEx does not support time based klines requesting", startTime != null ? nameof(startTime) : nameof(endTime)); - - if (string.IsNullOrEmpty(symbol)) - throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetKlinesAsync), nameof(symbol)); - - var klines = await ExchangeData.GetKlinesAsync(symbol, GetKlineIntervalFromTimespan(timespan), limit, ct: ct).ConfigureAwait(false); - if (!klines) - return klines.As>(null); - - return klines.As(klines.Data.Select(t => - new Kline - { - SourceObject = t, - OpenPrice = t.OpenPrice, - LowPrice = t.LowPrice, - OpenTime = t.OpenTime, - Volume = t.Volume, - ClosePrice = t.ClosePrice, - HighPrice = t.HighPrice - })); - } - - async Task> IBaseRestClient.GetOrderBookAsync(string symbol, CancellationToken ct) - { - if (string.IsNullOrEmpty(symbol)) - throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetOrderBookAsync), nameof(symbol)); - - var book = await ExchangeData.GetOrderBookAsync(symbol, 0, ct: ct).ConfigureAwait(false); - if (!book) - return book.As(null); - - return book.As(new OrderBook - { - SourceObject = book.Data, - Asks = book.Data.Asks.Select(a => new OrderBookEntry { Price = a.Price, Quantity = a.Quantity }), - Bids = book.Data.Bids.Select(b => new OrderBookEntry { Price = b.Price, Quantity = b.Quantity }) - }); - } - - async Task>> IBaseRestClient.GetRecentTradesAsync(string symbol, CancellationToken ct) - { - if (string.IsNullOrEmpty(symbol)) - throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetRecentTradesAsync), nameof(symbol)); - - var trades = await ExchangeData.GetTradeHistoryAsync(symbol, ct: ct).ConfigureAwait(false); - if (!trades) - return trades.As>(null); - - return trades.As(trades.Data.Select(t => - new Trade - { - SourceObject = t, - Price = t.Price, - Quantity = t.Quantity, - Symbol = symbol, - Timestamp = t.Timestamp - })); - } - - async Task> ISpotClient.PlaceOrderAsync(string symbol, CommonOrderSide side, CommonOrderType type, decimal quantity, decimal? price, string? accountId, string? clientOrderId, CancellationToken ct) - { - if (price == null && type == CommonOrderType.Limit) - throw new ArgumentException("Price parameter null while placing a limit order", nameof(price)); - - if (string.IsNullOrEmpty(symbol)) - throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.PlaceOrderAsync), nameof(symbol)); - - var result = await Trading.PlaceOrderAsync( - symbol, - side == CommonOrderSide.Sell ? OrderSide.Sell : OrderSide.Buy, - type == CommonOrderType.Limit ? OrderType.Limit : OrderType.Market, - quantity, - price, - clientOrderId: clientOrderId, - ct: ct).ConfigureAwait(false); - if (!result) - return result.As(null); - - return result.As(new OrderId { SourceObject = result.Data, Id = result.Data.Id.ToString(CultureInfo.InvariantCulture) }); - } - - async Task> IBaseRestClient.GetOrderAsync(string orderId, string? symbol, CancellationToken ct) - { - if (string.IsNullOrEmpty(symbol)) - throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetOrderAsync), nameof(symbol)); - - if (!long.TryParse(orderId, out var id)) - throw new ArgumentException($"Invalid order id for CoinEx {nameof(ISpotClient.GetOrderAsync)}", nameof(orderId)); - - var order = await Trading.GetOrderAsync(symbol!, id, ct: ct).ConfigureAwait(false); - if (!order) - return order.As(null); - - return order.As(new Order - { - SourceObject = order.Data, - Id = order.Data.Id.ToString(CultureInfo.InvariantCulture), - Price = order.Data.Price, - Quantity = order.Data.Quantity, - QuantityFilled = order.Data.QuantityFilled, - Timestamp = order.Data.CreateTime, - Symbol = order.Data.Symbol, - Side = order.Data.Side == OrderSide.Buy ? CommonOrderSide.Buy: CommonOrderSide.Sell, - Status = order.Data.Status == OrderStatus.Canceled ? CommonOrderStatus.Canceled: order.Data.Status == OrderStatus.Executed ? CommonOrderStatus.Filled: CommonOrderStatus.Active, - Type = order.Data.OrderType == OrderType.Market ? CommonOrderType.Market: order.Data.OrderType == OrderType.Limit ? CommonOrderType.Limit: CommonOrderType.Other - }); - } - - async Task>> IBaseRestClient.GetOrderTradesAsync(string orderId, string? symbol, CancellationToken ct) - { - if (!long.TryParse(orderId, out var id)) - throw new ArgumentException($"Invalid order id for CoinEx {nameof(ISpotClient.GetOrderAsync)}", nameof(orderId)); - - var result = await Trading.GetOrderTradesAsync(id, 1, 100, ct: ct).ConfigureAwait(false); - if (!result) - return result.As>(null); - - return result.As(result.Data.Data.Select(d => - new UserTrade - { - SourceObject = d, - Id = d.Id.ToString(CultureInfo.InvariantCulture), - Price = d.Price, - Quantity = d.Quantity, - Fee = d.Fee, - FeeAsset = d.FeeAsset, - OrderId = d.OrderId?.ToString(CultureInfo.InvariantCulture), - Symbol = symbol ?? string.Empty, - Timestamp = d.Timestamp - })); - } - - async Task>> IBaseRestClient.GetOpenOrdersAsync(string? symbol, CancellationToken ct) - { - if (string.IsNullOrEmpty(symbol)) - throw new ArgumentException($"CoinEx needs the {nameof(symbol)} parameter for the method {nameof(ISpotClient.GetOpenOrdersAsync)}"); - - var openOrders = await Trading.GetOpenOrdersAsync(symbol!, 1, 100, ct: ct).ConfigureAwait(false); - if (!openOrders) - return openOrders.As>(null); - - return openOrders.As(openOrders.Data.Data.Select(o => new Order - { - SourceObject = o, - Id = o.Id.ToString(CultureInfo.InvariantCulture), - Price = o.Price, - Quantity = o.Quantity, - QuantityFilled = o.QuantityFilled, - Timestamp = o.CreateTime, - Symbol = o.Symbol, - Side = o.Side == OrderSide.Buy ? CommonOrderSide.Buy : CommonOrderSide.Sell, - Status = o.Status == OrderStatus.Canceled ? CommonOrderStatus.Canceled : o.Status == OrderStatus.Executed ? CommonOrderStatus.Filled : CommonOrderStatus.Active, - Type = o.OrderType == OrderType.Market ? CommonOrderType.Market : o.OrderType == OrderType.Limit ? CommonOrderType.Limit : CommonOrderType.Other - })); - } - - async Task>> IBaseRestClient.GetClosedOrdersAsync(string? symbol, CancellationToken ct) - { - if (string.IsNullOrEmpty(symbol)) - throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetClosedOrdersAsync), nameof(symbol)); - - if (string.IsNullOrEmpty(symbol)) - throw new ArgumentException($"CoinEx needs the {nameof(symbol)} parameter for the method {nameof(ISpotClient.GetClosedOrdersAsync)}"); - - var result = await Trading.GetClosedOrdersAsync(symbol!, 1, 100, ct: ct).ConfigureAwait(false); - if (!result) - return result.As>(null); - - return result.As(result.Data.Data.Select(o => new Order - { - SourceObject = o, - Id = o.Id.ToString(CultureInfo.InvariantCulture), - Price = o.Price, - Quantity = o.Quantity, - QuantityFilled = o.QuantityFilled, - Timestamp = o.CreateTime, - Symbol = o.Symbol, - Side = o.Side == OrderSide.Buy ? CommonOrderSide.Buy : CommonOrderSide.Sell, - Status = o.Status == OrderStatus.Canceled ? CommonOrderStatus.Canceled : o.Status == OrderStatus.Executed ? CommonOrderStatus.Filled : CommonOrderStatus.Active, - Type = o.OrderType == OrderType.Market ? CommonOrderType.Market : o.OrderType == OrderType.Limit ? CommonOrderType.Limit : CommonOrderType.Other - })); - } - - async Task> IBaseRestClient.CancelOrderAsync(string orderId, string? symbol, CancellationToken ct) - { - if (string.IsNullOrEmpty(symbol)) - throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.CancelOrderAsync), nameof(symbol)); - - if (!long.TryParse(orderId, out var id)) - throw new ArgumentException($"Invalid order id for CoinEx {nameof(ISpotClient.GetOrderAsync)}", nameof(orderId)); - - var result = await Trading.CancelOrderAsync(symbol!, id, ct: ct).ConfigureAwait(false); - if (!result) - return result.As(null); - - return result.As(new OrderId - { - SourceObject = result.Data, - Id = result.Data.Id.ToString(CultureInfo.InvariantCulture) - }); - } - - async Task>> IBaseRestClient.GetBalancesAsync(string? accountId, CancellationToken ct) - { - var balances = await Account.GetBalancesAsync(ct: ct).ConfigureAwait(false); - if (!balances) - return balances.As>(null); - - return balances.As(balances.Data.Select(d => new Balance - { - SourceObject = d, - Asset = d.Key, - Available = d.Value.Available, - Total = d.Value.Frozen + d.Value.Available - })); - } - - private static KlineInterval GetKlineIntervalFromTimespan(TimeSpan timeSpan) - { - if (timeSpan == TimeSpan.FromMinutes(1)) return KlineInterval.OneMinute; - if (timeSpan == TimeSpan.FromMinutes(3)) return KlineInterval.ThreeMinutes; - if (timeSpan == TimeSpan.FromMinutes(5)) return KlineInterval.FiveMinutes; - if (timeSpan == TimeSpan.FromMinutes(15)) return KlineInterval.FiveMinutes; - if (timeSpan == TimeSpan.FromMinutes(30)) return KlineInterval.ThirtyMinutes; - if (timeSpan == TimeSpan.FromHours(1)) return KlineInterval.OneHour; - if (timeSpan == TimeSpan.FromHours(2)) return KlineInterval.TwoHours; - if (timeSpan == TimeSpan.FromHours(4)) return KlineInterval.FourHours; - if (timeSpan == TimeSpan.FromHours(6)) return KlineInterval.SixHours; - if (timeSpan == TimeSpan.FromHours(12)) return KlineInterval.TwelveHours; - if (timeSpan == TimeSpan.FromDays(1)) return KlineInterval.OneDay; - if (timeSpan == TimeSpan.FromDays(3)) return KlineInterval.ThreeDays; - if (timeSpan == TimeSpan.FromDays(7)) return KlineInterval.OneWeek; - - throw new ArgumentException("Unsupported timespan for CoinEx Klines, check supported intervals using CoinEx.Net.Objects.KlineInterval"); - } + internal Uri GetUri(string path) => new Uri(BaseAddress.AppendPath(path)); #endregion - /// - protected override Error ParseErrorResponse(int httpStatusCode, IEnumerable>> responseHeaders, IMessageAccessor accessor) - { - if (!accessor.IsJson) - return new ServerError(accessor.GetOriginalString()); - - var code = accessor.GetValue(MessagePath.Get().Property("code")); - var msg = accessor.GetValue(MessagePath.Get().Property("message")); - if (msg == null) - return new ServerError(accessor.GetOriginalString()); - - if (code == null) - return new ServerError(msg); - - return new ServerError(code.Value, msg); - } + //#region common interface + + //internal void InvokeOrderPlaced(OrderId id) + //{ + // OnOrderPlaced?.Invoke(id); + //} + + //internal void InvokeOrderCanceled(OrderId id) + //{ + // OnOrderCanceled?.Invoke(id); + //} + + ///// + ///// Get the name of a symbol for CoinEx based on the base and quote asset + ///// + ///// + ///// + ///// + //public string GetSymbolName(string baseAsset, string quoteAsset) => (baseAsset + quoteAsset).ToUpperInvariant(); + + //async Task>> IBaseRestClient.GetSymbolsAsync(CancellationToken ct) + //{ + // var symbols = await ExchangeData.GetSymbolInfoAsync(ct: ct).ConfigureAwait(false); + // if (!symbols) + // return symbols.As>(null); + + // return symbols.As(symbols.Data.Select(d => new Symbol + // { + // SourceObject = d, + // Name = d.Key, + // MinTradeQuantity = d.Value.MinQuantity, + // PriceDecimals = d.Value.PricingDecimal, + // QuantityDecimals = d.Value.TradingDecimal + // })); + //} + + //async Task> IBaseRestClient.GetTickerAsync(string symbol, CancellationToken ct) + //{ + // if (string.IsNullOrEmpty(symbol)) + // throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetTickerAsync), nameof(symbol)); + + // var tickers = await ExchangeData.GetTickerAsync(symbol, ct: ct).ConfigureAwait(false); + // if (!tickers) + // return tickers.As(null); + + // return tickers.As(new Ticker + // { + // SourceObject = tickers.Data, + // Symbol = symbol, + // HighPrice = tickers.Data.Ticker.HighPrice, + // LowPrice = tickers.Data.Ticker.LowPrice, + // LastPrice = tickers.Data.Ticker.LastPrice, + // Price24H = tickers.Data.Ticker.OpenPrice, + // Volume = tickers.Data.Ticker.Volume + // }); + //} + + //async Task>> IBaseRestClient.GetTickersAsync(CancellationToken ct) + //{ + // var tickers = await ExchangeData.GetTickersAsync(ct: ct).ConfigureAwait(false); + // if (!tickers) + // return tickers.As>(null); + + // return tickers.As(tickers.Data.Tickers.Select(t => + // new Ticker + // { + // SourceObject = t, + // Symbol = t.Key, + // HighPrice = t.Value.HighPrice, + // LowPrice = t.Value.LowPrice, + // LastPrice = t.Value.LastPrice, + // Price24H = t.Value.OpenPrice, + // Volume = t.Value.Volume + // })); + //} + + //async Task>> IBaseRestClient.GetKlinesAsync(string symbol, TimeSpan timespan, DateTime? startTime, DateTime? endTime, int? limit, CancellationToken ct) + //{ + // if (startTime != null || endTime != null) + // throw new ArgumentException("CoinEx does not support time based klines requesting", startTime != null ? nameof(startTime) : nameof(endTime)); + + // if (string.IsNullOrEmpty(symbol)) + // throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetKlinesAsync), nameof(symbol)); + + // var klines = await ExchangeData.GetKlinesAsync(symbol, GetKlineIntervalFromTimespan(timespan), limit, ct: ct).ConfigureAwait(false); + // if (!klines) + // return klines.As>(null); + + // return klines.As(klines.Data.Select(t => + // new Kline + // { + // SourceObject = t, + // OpenPrice = t.OpenPrice, + // LowPrice = t.LowPrice, + // OpenTime = t.OpenTime, + // Volume = t.Volume, + // ClosePrice = t.ClosePrice, + // HighPrice = t.HighPrice + // })); + //} + + //async Task> IBaseRestClient.GetOrderBookAsync(string symbol, CancellationToken ct) + //{ + // if (string.IsNullOrEmpty(symbol)) + // throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetOrderBookAsync), nameof(symbol)); + + // var book = await ExchangeData.GetOrderBookAsync(symbol, 0, ct: ct).ConfigureAwait(false); + // if (!book) + // return book.As(null); + + // return book.As(new OrderBook + // { + // SourceObject = book.Data, + // Asks = book.Data.Asks.Select(a => new OrderBookEntry { Price = a.Price, Quantity = a.Quantity }), + // Bids = book.Data.Bids.Select(b => new OrderBookEntry { Price = b.Price, Quantity = b.Quantity }) + // }); + //} + + //async Task>> IBaseRestClient.GetRecentTradesAsync(string symbol, CancellationToken ct) + //{ + // if (string.IsNullOrEmpty(symbol)) + // throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetRecentTradesAsync), nameof(symbol)); + + // var trades = await ExchangeData.GetTradeHistoryAsync(symbol, ct: ct).ConfigureAwait(false); + // if (!trades) + // return trades.As>(null); + + // return trades.As(trades.Data.Select(t => + // new Trade + // { + // SourceObject = t, + // Price = t.Price, + // Quantity = t.Quantity, + // Symbol = symbol, + // Timestamp = t.Timestamp + // })); + //} + + //async Task> ISpotClient.PlaceOrderAsync(string symbol, CommonOrderSide side, CommonOrderType type, decimal quantity, decimal? price, string? accountId, string? clientOrderId, CancellationToken ct) + //{ + // if (price == null && type == CommonOrderType.Limit) + // throw new ArgumentException("Price parameter null while placing a limit order", nameof(price)); + + // if (string.IsNullOrEmpty(symbol)) + // throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.PlaceOrderAsync), nameof(symbol)); + + // var result = await Trading.PlaceOrderAsync( + // symbol, + // side == CommonOrderSide.Sell ? OrderSide.Sell : OrderSide.Buy, + // type == CommonOrderType.Limit ? OrderType.Limit : OrderType.Market, + // quantity, + // price, + // clientOrderId: clientOrderId, + // ct: ct).ConfigureAwait(false); + // if (!result) + // return result.As(null); + + // return result.As(new OrderId { SourceObject = result.Data, Id = result.Data.Id.ToString(CultureInfo.InvariantCulture) }); + //} + + //async Task> IBaseRestClient.GetOrderAsync(string orderId, string? symbol, CancellationToken ct) + //{ + // if (string.IsNullOrEmpty(symbol)) + // throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetOrderAsync), nameof(symbol)); + + // if (!long.TryParse(orderId, out var id)) + // throw new ArgumentException($"Invalid order id for CoinEx {nameof(ISpotClient.GetOrderAsync)}", nameof(orderId)); + + // var order = await Trading.GetOrderAsync(symbol!, id, ct: ct).ConfigureAwait(false); + // if (!order) + // return order.As(null); + + // return order.As(new Order + // { + // SourceObject = order.Data, + // Id = order.Data.Id.ToString(CultureInfo.InvariantCulture), + // Price = order.Data.Price, + // Quantity = order.Data.Quantity, + // QuantityFilled = order.Data.QuantityFilled, + // Timestamp = order.Data.CreateTime, + // Symbol = order.Data.Symbol, + // Side = order.Data.Side == OrderSide.Buy ? CommonOrderSide.Buy: CommonOrderSide.Sell, + // Status = order.Data.Status == OrderStatus.Canceled ? CommonOrderStatus.Canceled: order.Data.Status == OrderStatus.Executed ? CommonOrderStatus.Filled: CommonOrderStatus.Active, + // Type = order.Data.OrderType == OrderType.Market ? CommonOrderType.Market: order.Data.OrderType == OrderType.Limit ? CommonOrderType.Limit: CommonOrderType.Other + // }); + //} + + //async Task>> IBaseRestClient.GetOrderTradesAsync(string orderId, string? symbol, CancellationToken ct) + //{ + // if (!long.TryParse(orderId, out var id)) + // throw new ArgumentException($"Invalid order id for CoinEx {nameof(ISpotClient.GetOrderAsync)}", nameof(orderId)); + + // var result = await Trading.GetOrderTradesAsync(id, 1, 100, ct: ct).ConfigureAwait(false); + // if (!result) + // return result.As>(null); + + // return result.As(result.Data.Data.Select(d => + // new UserTrade + // { + // SourceObject = d, + // Id = d.Id.ToString(CultureInfo.InvariantCulture), + // Price = d.Price, + // Quantity = d.Quantity, + // Fee = d.Fee, + // FeeAsset = d.FeeAsset, + // OrderId = d.OrderId?.ToString(CultureInfo.InvariantCulture), + // Symbol = symbol ?? string.Empty, + // Timestamp = d.Timestamp + // })); + //} + + //async Task>> IBaseRestClient.GetOpenOrdersAsync(string? symbol, CancellationToken ct) + //{ + // if (string.IsNullOrEmpty(symbol)) + // throw new ArgumentException($"CoinEx needs the {nameof(symbol)} parameter for the method {nameof(ISpotClient.GetOpenOrdersAsync)}"); + + // var openOrders = await Trading.GetOpenOrdersAsync(symbol!, 1, 100, ct: ct).ConfigureAwait(false); + // if (!openOrders) + // return openOrders.As>(null); + + // return openOrders.As(openOrders.Data.Data.Select(o => new Order + // { + // SourceObject = o, + // Id = o.Id.ToString(CultureInfo.InvariantCulture), + // Price = o.Price, + // Quantity = o.Quantity, + // QuantityFilled = o.QuantityFilled, + // Timestamp = o.CreateTime, + // Symbol = o.Symbol, + // Side = o.Side == OrderSide.Buy ? CommonOrderSide.Buy : CommonOrderSide.Sell, + // Status = o.Status == OrderStatus.Canceled ? CommonOrderStatus.Canceled : o.Status == OrderStatus.Executed ? CommonOrderStatus.Filled : CommonOrderStatus.Active, + // Type = o.OrderType == OrderType.Market ? CommonOrderType.Market : o.OrderType == OrderType.Limit ? CommonOrderType.Limit : CommonOrderType.Other + // })); + //} + + //async Task>> IBaseRestClient.GetClosedOrdersAsync(string? symbol, CancellationToken ct) + //{ + // if (string.IsNullOrEmpty(symbol)) + // throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetClosedOrdersAsync), nameof(symbol)); + + // if (string.IsNullOrEmpty(symbol)) + // throw new ArgumentException($"CoinEx needs the {nameof(symbol)} parameter for the method {nameof(ISpotClient.GetClosedOrdersAsync)}"); + + // var result = await Trading.GetClosedOrdersAsync(symbol!, 1, 100, ct: ct).ConfigureAwait(false); + // if (!result) + // return result.As>(null); + + // return result.As(result.Data.Data.Select(o => new Order + // { + // SourceObject = o, + // Id = o.Id.ToString(CultureInfo.InvariantCulture), + // Price = o.Price, + // Quantity = o.Quantity, + // QuantityFilled = o.QuantityFilled, + // Timestamp = o.CreateTime, + // Symbol = o.Symbol, + // Side = o.Side == OrderSide.Buy ? CommonOrderSide.Buy : CommonOrderSide.Sell, + // Status = o.Status == OrderStatus.Canceled ? CommonOrderStatus.Canceled : o.Status == OrderStatus.Executed ? CommonOrderStatus.Filled : CommonOrderStatus.Active, + // Type = o.OrderType == OrderType.Market ? CommonOrderType.Market : o.OrderType == OrderType.Limit ? CommonOrderType.Limit : CommonOrderType.Other + // })); + //} + + //async Task> IBaseRestClient.CancelOrderAsync(string orderId, string? symbol, CancellationToken ct) + //{ + // if (string.IsNullOrEmpty(symbol)) + // throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.CancelOrderAsync), nameof(symbol)); + + // if (!long.TryParse(orderId, out var id)) + // throw new ArgumentException($"Invalid order id for CoinEx {nameof(ISpotClient.GetOrderAsync)}", nameof(orderId)); + + // var result = await Trading.CancelOrderAsync(symbol!, id, ct: ct).ConfigureAwait(false); + // if (!result) + // return result.As(null); + + // return result.As(new OrderId + // { + // SourceObject = result.Data, + // Id = result.Data.Id.ToString(CultureInfo.InvariantCulture) + // }); + //} + + //async Task>> IBaseRestClient.GetBalancesAsync(string? accountId, CancellationToken ct) + //{ + // var balances = await Account.GetBalancesAsync(ct: ct).ConfigureAwait(false); + // if (!balances) + // return balances.As>(null); + + // return balances.As(balances.Data.Select(d => new Balance + // { + // SourceObject = d, + // Asset = d.Key, + // Available = d.Value.Available, + // Total = d.Value.Frozen + d.Value.Available + // })); + //} + + //private static KlineInterval GetKlineIntervalFromTimespan(TimeSpan timeSpan) + //{ + // if (timeSpan == TimeSpan.FromMinutes(1)) return KlineInterval.OneMinute; + // if (timeSpan == TimeSpan.FromMinutes(3)) return KlineInterval.ThreeMinutes; + // if (timeSpan == TimeSpan.FromMinutes(5)) return KlineInterval.FiveMinutes; + // if (timeSpan == TimeSpan.FromMinutes(15)) return KlineInterval.FiveMinutes; + // if (timeSpan == TimeSpan.FromMinutes(30)) return KlineInterval.ThirtyMinutes; + // if (timeSpan == TimeSpan.FromHours(1)) return KlineInterval.OneHour; + // if (timeSpan == TimeSpan.FromHours(2)) return KlineInterval.TwoHours; + // if (timeSpan == TimeSpan.FromHours(4)) return KlineInterval.FourHours; + // if (timeSpan == TimeSpan.FromHours(6)) return KlineInterval.SixHours; + // if (timeSpan == TimeSpan.FromHours(12)) return KlineInterval.TwelveHours; + // if (timeSpan == TimeSpan.FromDays(1)) return KlineInterval.OneDay; + // if (timeSpan == TimeSpan.FromDays(3)) return KlineInterval.ThreeDays; + // if (timeSpan == TimeSpan.FromDays(7)) return KlineInterval.OneWeek; + + // throw new ArgumentException("Unsupported timespan for CoinEx Klines, check supported intervals using CoinEx.Net.Objects.KlineInterval"); + //} + //#endregion + + ///// + //protected override Error ParseErrorResponse(int httpStatusCode, IEnumerable>> responseHeaders, IMessageAccessor accessor) + //{ + // if (!accessor.IsJson) + // return new ServerError(accessor.GetOriginalString()); + + // var code = accessor.GetValue(MessagePath.Get().Property("code")); + // var msg = accessor.GetValue(MessagePath.Get().Property("message")); + // if (msg == null) + // return new ServerError(accessor.GetOriginalString()); + + // if (code == null) + // return new ServerError(msg); + + // return new ServerError(code.Value, msg); + //} /// public override TimeSyncInfo? GetTimeSyncInfo() => null; @@ -459,6 +435,6 @@ protected override Error ParseErrorResponse(int httpStatusCode, IEnumerable null; /// - public ISpotClient CommonSpotClient => this; + public ISpotClient CommonSpotClient => null;//this; } } diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiAccount.cs b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiAccount.cs index eda9aa9..a80d385 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiAccount.cs +++ b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiAccount.cs @@ -1,26 +1,21 @@ -using CryptoExchange.Net; +using CoinEx.Net.Converters; +using CoinEx.Net.Enums; +using CryptoExchange.Net; using CryptoExchange.Net.Objects; +using Newtonsoft.Json; using System.Collections.Generic; -using System.Globalization; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using CoinEx.Net.Objects.Models; using CoinEx.Net.Interfaces.Clients.SpotApi; +using CoinEx.Net.ExtensionMethods; namespace CoinEx.Net.Clients.SpotApi { /// public class CoinExRestClientSpotApiAccount : ICoinExClientSpotApiAccount { - private const string AccountInfoEndpoint = "balance/info"; - private const string WithdrawalHistoryEndpoint = "balance/coin/withdraw"; - private const string DepositHistoryEndpoint = "balance/coin/deposit"; - private const string WithdrawEndpoint = "balance/coin/withdraw"; - private const string CancelWithdrawalEndpoint = "balance/coin/withdraw"; - private const string DepositAddressEndpoint = "balance/deposit/address/"; - - private readonly CoinExRestClientSpotApi _baseClient; internal CoinExRestClientSpotApiAccount(CoinExRestClientSpotApi baseClient) @@ -28,80 +23,6 @@ internal CoinExRestClientSpotApiAccount(CoinExRestClientSpotApi baseClient) _baseClient = baseClient; } - /// - public async Task>> GetBalancesAsync(CancellationToken ct = default) - { - var result = await _baseClient.Execute>(_baseClient.GetUrl(AccountInfoEndpoint), HttpMethod.Get, ct, null, true).ConfigureAwait(false); - if (result) - { - foreach (var b in result.Data) - b.Value.Asset = b.Key; - } - - return result; - } - - /// - public async Task>> GetDepositHistoryAsync(string? asset = null, int? page = null, int? limit = null, CancellationToken ct = default) - { - limit?.ValidateIntBetween(nameof(limit), 1, 100); - var parameters = new Dictionary(); - parameters.AddOptionalParameter("coin_type", asset); - parameters.AddOptionalParameter("page", page); - parameters.AddOptionalParameter("limit", limit); - - return await _baseClient.ExecutePaged(_baseClient.GetUrl(DepositHistoryEndpoint), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); - } - - /// - public async Task>> GetWithdrawalHistoryAsync(string? asset = null, long? withdrawId = null, int? page = null, int? limit = null, CancellationToken ct = default) - { - limit?.ValidateIntBetween(nameof(limit), 1, 100); - var parameters = new Dictionary(); - parameters.AddOptionalParameter("coin_type", asset); - parameters.AddOptionalParameter("coin_withdraw_id", withdrawId); - parameters.AddOptionalParameter("page", page); - parameters.AddOptionalParameter("limit", limit); - - return await _baseClient.Execute>(_baseClient.GetUrl(WithdrawalHistoryEndpoint), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); - } - - /// - public async Task> WithdrawAsync(string asset, string address, bool localTransfer, decimal quantity, string? network = null, CancellationToken ct = default) - { - asset.ValidateNotNull(nameof(asset)); - address.ValidateNotNull(nameof(address)); - var parameters = new Dictionary - { - { "coin_type", asset }, - { "coin_address", address }, - { "transfer_method", localTransfer ? "local": "onchain" }, - { "actual_amount", quantity.ToString(CultureInfo.InvariantCulture) } - }; - parameters.AddOptionalParameter("smart_contract_name", network); - - return await _baseClient.Execute(_baseClient.GetUrl(WithdrawEndpoint), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); - } - - /// - public async Task> CancelWithdrawalAsync(long withdrawId, CancellationToken ct = default) - { - var parameters = new Dictionary - { - { "coin_withdraw_id", withdrawId } - }; - - var result = await _baseClient.Execute(_baseClient.GetUrl(CancelWithdrawalEndpoint), HttpMethod.Delete, ct, parameters, true).ConfigureAwait(false); - return result.As(result.Success); - } - /// - public async Task> GetDepositAddressAsync(string asset, string? smartContractName = null, CancellationToken ct = default) - { - var parameters = new Dictionary(); - parameters.AddOptionalParameter("smart_contract_name", smartContractName); - - return await _baseClient.Execute(_baseClient.GetUrl(DepositAddressEndpoint + asset), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); - } } } diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiExchangeData.cs b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiExchangeData.cs index f14b0c1..95bad6f 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiExchangeData.cs +++ b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiExchangeData.cs @@ -1,33 +1,16 @@ -using CoinEx.Net.Converters; -using CoinEx.Net.Enums; -using CryptoExchange.Net; -using CryptoExchange.Net.Objects; -using Newtonsoft.Json; +using CryptoExchange.Net.Objects; using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using CoinEx.Net.Objects.Models; +using CoinEx.Net.Objects.Models.V2; using CoinEx.Net.Interfaces.Clients.SpotApi; -using CoinEx.Net.ExtensionMethods; namespace CoinEx.Net.Clients.SpotApi { /// public class CoinExRestClientSpotApiExchangeData : ICoinExClientSpotApiExchangeData { - private const string AssetConfigEndpoint = "common/asset/config"; - private const string CurrencyRateEndpoint = "common/currency/rate"; - - private const string MarketListEndpoint = "market/list"; - private const string MarketStatisticsEndpoint = "market/ticker"; - private const string MarketStatisticsListEndpoint = "market/ticker/all"; - private const string MarketDepthEndpoint = "market/depth"; - private const string MarketDealsEndpoint = "market/deals"; - private const string MarketKlinesEndpoint = "market/kline"; - private const string MarketInfoEndpoint = "market/info"; - private const string MiningDifficultyEndpoint = "order/mining/difficulty"; - private readonly CoinExRestClientSpotApi _baseClient; internal CoinExRestClientSpotApiExchangeData(CoinExRestClientSpotApi baseClient) @@ -37,118 +20,17 @@ internal CoinExRestClientSpotApiExchangeData(CoinExRestClientSpotApi baseClient) /// - public async Task>> GetSymbolsAsync(CancellationToken ct = default) - { - return await _baseClient.Execute>(_baseClient.GetUrl(MarketListEndpoint), HttpMethod.Get, ct).ConfigureAwait(false); - } - - /// - public async Task>> GetCurrencyRateAsync(CancellationToken ct = default) - { - return await _baseClient.Execute>(_baseClient.GetUrl(CurrencyRateEndpoint), HttpMethod.Get, ct).ConfigureAwait(false); - } - - /// - public async Task>> GetAssetsAsync(string? assetType = null, CancellationToken ct = default) - { - var parameters = new Dictionary(); - parameters.AddOptionalParameter("coin_type", assetType); - - return await _baseClient.Execute>(_baseClient.GetUrl(AssetConfigEndpoint), HttpMethod.Get, ct, parameters).ConfigureAwait(false); - } - - /// - public async Task> GetTickerAsync(string symbol, CancellationToken ct = default) - { - symbol.ValidateCoinExSymbol(); - var parameters = new Dictionary - { - { "market", symbol } - }; - - return await _baseClient.Execute(_baseClient.GetUrl(MarketStatisticsEndpoint), HttpMethod.Get, ct, parameters).ConfigureAwait(false); - } - - /// - public async Task> GetTickersAsync(CancellationToken ct = default) + public async Task>> GetSymbolsAsync(CancellationToken ct = default) { - var data = await _baseClient.Execute(_baseClient.GetUrl(MarketStatisticsListEndpoint), HttpMethod.Get, ct) - .ConfigureAwait(false); - if (!data) - return data; - - foreach (var item in data.Data.Tickers) - item.Value.Symbol = item.Key; - return data; + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/spot/market"), HttpMethod.Get, ct).ConfigureAwait(false); } /// - public async Task> GetOrderBookAsync(string symbol, int mergeDepth, int? limit = null, CancellationToken ct = default) - { - symbol.ValidateCoinExSymbol(); - mergeDepth.ValidateIntBetween(nameof(mergeDepth), 0, 8); - limit?.ValidateIntValues(nameof(limit), 5, 10, 20); - - var parameters = new Dictionary - { - { "market", symbol }, - { "merge", CoinExHelpers.MergeDepthIntToString(mergeDepth) } - }; - parameters.AddOptionalParameter("limit", limit); - - return await _baseClient.Execute(_baseClient.GetUrl(MarketDepthEndpoint), HttpMethod.Get, ct, parameters).ConfigureAwait(false); - } - - /// - public async Task>> GetTradeHistoryAsync(string symbol, long? fromId = null, CancellationToken ct = default) - { - symbol.ValidateCoinExSymbol(); - - var parameters = new Dictionary - { - { "market", symbol } - }; - parameters.AddOptionalParameter("last_id", fromId); - - return await _baseClient.Execute>(_baseClient.GetUrl(MarketDealsEndpoint), HttpMethod.Get, ct, parameters).ConfigureAwait(false); - } - - /// - public async Task>> GetSymbolInfoAsync(string symbol, CancellationToken ct = default) - { - var parameters = new Dictionary - { - { "market", symbol } - }; - return await _baseClient.Execute>(_baseClient.GetUrl(MarketInfoEndpoint), HttpMethod.Get, ct, parameters).ConfigureAwait(false); - } - - /// - public async Task>> GetSymbolInfoAsync(CancellationToken ct = default) - { - return await _baseClient.Execute>(_baseClient.GetUrl(MarketInfoEndpoint), HttpMethod.Get, ct).ConfigureAwait(false); - } - - /// - public async Task>> GetKlinesAsync(string symbol, KlineInterval interval, int? limit = null, CancellationToken ct = default) - { - symbol.ValidateCoinExSymbol(); - limit?.ValidateIntBetween(nameof(limit), 1, 1000); - var parameters = new Dictionary - { - { "market", symbol }, - { "type", JsonConvert.SerializeObject(interval, new KlineIntervalConverter(false)) } - }; - parameters.AddOptionalParameter("limit", limit); - - return await _baseClient.Execute>(_baseClient.GetUrl(MarketKlinesEndpoint), HttpMethod.Get, ct, parameters).ConfigureAwait(false); - } - - - /// - public async Task> GetMiningDifficultyAsync(CancellationToken ct = default) + public async Task>> GetTickersAsync(IEnumerable? symbols = null, CancellationToken ct = default) { - return await _baseClient.Execute(_baseClient.GetUrl(MiningDifficultyEndpoint), HttpMethod.Get, ct, null, true).ConfigureAwait(false); + var parameters = new ParameterCollection(); + parameters.AddOptional("market", symbols == null ? null : string.Join(",", symbols)); + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/spot/ticker"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); } } } diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs index ee1963c..90fe61d 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs +++ b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs @@ -3,15 +3,12 @@ using CryptoExchange.Net; using CryptoExchange.Net.Objects; using Newtonsoft.Json; -using System; using System.Collections.Generic; -using System.Globalization; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using CoinEx.Net.Objects.Models; using CoinEx.Net.Interfaces.Clients.SpotApi; -using CryptoExchange.Net.CommonObjects; using CoinEx.Net.ExtensionMethods; namespace CoinEx.Net.Clients.SpotApi @@ -19,20 +16,6 @@ namespace CoinEx.Net.Clients.SpotApi /// public class CoinExRestClientSpotApiTrading : ICoinExClientSpotApiTrading { - private const string PlaceLimitOrderEndpoint = "order/limit"; - private const string PlaceMarketOrderEndpoint = "order/market"; - private const string PlaceStopLimitOrderEndpoint = "order/stop/limit"; - private const string PlaceStopMarketOrderEndpoint = "order/stop/market"; - private const string PlaceImmediateOrCancelOrderEndpoint = "order/ioc"; - private const string FinishedOrdersEndpoint = "order/finished"; - private const string OpenOrdersEndpoint = "order/pending"; - private const string OpenStopOrdersEndpoint = "order/stop/pending"; - private const string OrderStatusEndpoint = "order/status"; - private const string OrderDetailsEndpoint = "order/deals"; - private const string UserTransactionsEndpoint = "order/user/deals"; - private const string CancelOrderEndpoint = "order/pending"; - private const string CancelStopOrderEndpoint = "order/stop/pending"; - private readonly CoinExRestClientSpotApi _baseClient; internal CoinExRestClientSpotApiTrading(CoinExRestClientSpotApi baseClient) @@ -40,188 +23,11 @@ internal CoinExRestClientSpotApiTrading(CoinExRestClientSpotApi baseClient) _baseClient = baseClient; } - /// - public async Task> PlaceOrderAsync( - string symbol, - OrderSide side, - OrderType type, - decimal quantity, - - decimal? price = null, - decimal? stopPrice = null, - bool? immediateOrCancel = null, - OrderOption? orderOption = null, - string? clientOrderId = null, - string? sourceId = null, - CancellationToken ct = default) - { - symbol.ValidateCoinExSymbol(); - - var endpoint = ""; - if (type == OrderType.Limit) - endpoint = PlaceLimitOrderEndpoint; - else if (type == OrderType.Market) - endpoint = PlaceMarketOrderEndpoint; - else if (type == OrderType.StopLimit) - endpoint = PlaceStopLimitOrderEndpoint; - else if (type == OrderType.StopMarket) - endpoint = PlaceStopMarketOrderEndpoint; - - if (immediateOrCancel == true) - { - if (type != OrderType.Limit) - throw new ArgumentException("ImmediateOrCancel only valid for limit orders"); - - endpoint = PlaceImmediateOrCancelOrderEndpoint; - } - - clientOrderId ??= ExchangeHelpers.AppendRandomString("x-" + _baseClient._brokerId + "-", 32); - - var parameters = new Dictionary - { - { "market", symbol }, - { "type", JsonConvert.SerializeObject(side, new OrderSideConverter(false)) }, - { "amount", quantity.ToString(CultureInfo.InvariantCulture) } - }; - parameters.AddOptionalParameter("price", price?.ToString(CultureInfo.InvariantCulture)); - parameters.AddOptionalParameter("option", orderOption.HasValue ? JsonConvert.SerializeObject(orderOption, new OrderOptionConverter(false)) : null); - parameters.AddOptionalParameter("stop_price", stopPrice?.ToString(CultureInfo.InvariantCulture)); - parameters.AddOptionalParameter("client_id", clientOrderId); - parameters.AddOptionalParameter("source_id", sourceId); - - var result = await _baseClient.Execute(_baseClient.GetUrl(endpoint), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); - if (result) - _baseClient.InvokeOrderPlaced(new OrderId { SourceObject = result.Data, Id = result.Data.Id.ToString(CultureInfo.InvariantCulture) }); - - return result; - } - - /// - public async Task>> GetOpenOrdersAsync(string? symbol = null, int? page = null, int? limit = null, CancellationToken ct = default) - { - symbol?.ValidateCoinExSymbol(); - limit?.ValidateIntBetween(nameof(limit), 1, 100); - var parameters = new Dictionary - { - { "page", page ?? 1 }, - { "limit", limit ?? 100 } - }; - - parameters.AddOptionalParameter("market", symbol); - - return await _baseClient.ExecutePaged(_baseClient.GetUrl(OpenOrdersEndpoint), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); - } - - /// - public async Task>> GetOpenStopOrdersAsync(string symbol, int? page = null, int? limit = null, CancellationToken ct = default) - { - symbol?.ValidateCoinExSymbol(); - limit?.ValidateIntBetween(nameof(limit), 1, 100); - var parameters = new Dictionary - { - { "page", page ?? 1 }, - { "limit", limit ?? 100 } - }; - parameters.AddOptionalParameter("market", symbol); - return await _baseClient.ExecutePaged(_baseClient.GetUrl(OpenStopOrdersEndpoint), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); - } - - /// - public async Task>> GetClosedOrdersAsync(string symbol, int? page = null, int? limit = null, CancellationToken ct = default) - { - symbol.ValidateCoinExSymbol(); - limit?.ValidateIntBetween(nameof(limit), 1, 100); - var parameters = new Dictionary - { - { "market", symbol }, - { "page", page ?? 1 }, - { "limit", limit ?? 100 } - }; - - return await _baseClient.ExecutePaged(_baseClient.GetUrl(FinishedOrdersEndpoint), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); - } - - /// - public async Task> GetOrderAsync(string symbol, long orderId, CancellationToken ct = default) - { - symbol.ValidateCoinExSymbol(); - var parameters = new Dictionary - { - { "market", symbol }, - { "id", orderId } - }; - - return await _baseClient.Execute(_baseClient.GetUrl(OrderStatusEndpoint), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); - } - - /// - public async Task>> GetOrderTradesAsync(long orderId, int? page = null, int? limit = null, CancellationToken ct = default) - { - limit?.ValidateIntBetween(nameof(limit), 1, 100); - var parameters = new Dictionary - { - { "id", orderId }, - { "page", page ?? 1 }, - { "limit", limit ?? 100 } - }; - - return await _baseClient.ExecutePaged(_baseClient.GetUrl(OrderDetailsEndpoint), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); - } /// - public async Task>> GetUserTradesAsync(string symbol, int? page = null, int? limit = null, CancellationToken ct = default) + public async Task>> GetSymbolsAsync(CancellationToken ct = default) { - symbol.ValidateCoinExSymbol(); - limit?.ValidateIntBetween(nameof(limit), 1, 100); - var parameters = new Dictionary - { - { "market", symbol }, - { "page", page ?? 1 }, - { "limit", limit ?? 100 } - }; - - return await _baseClient.ExecutePaged(_baseClient.GetUrl(UserTransactionsEndpoint), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("spot/market"), HttpMethod.Get, ct).ConfigureAwait(false); } - - /// - public async Task> CancelOrderAsync(string symbol, long orderId, CancellationToken ct = default) - { - symbol.ValidateCoinExSymbol(); - var parameters = new Dictionary - { - { "market", symbol }, - { "id", orderId } - }; - - var result = await _baseClient.Execute(_baseClient.GetUrl(CancelOrderEndpoint), HttpMethod.Delete, ct, parameters, true).ConfigureAwait(false); - if (result) - _baseClient.InvokeOrderCanceled(new OrderId { SourceObject = result.Data, Id = result.Data.Id.ToString(CultureInfo.InvariantCulture) }); - return result; - } - - /// - public async Task CancelAllOrdersAsync(string symbol, CancellationToken ct = default) - { - symbol.ValidateCoinExSymbol(); - var parameters = new Dictionary - { - { "market", symbol }, - }; - - return await _baseClient.Execute(_baseClient.GetUrl(CancelOrderEndpoint), HttpMethod.Delete, ct, parameters, true).ConfigureAwait(false); - } - - /// - public async Task CancelAllStopOrdersAsync(string symbol, CancellationToken ct = default) - { - symbol.ValidateCoinExSymbol(); - var parameters = new Dictionary - { - { "market", symbol }, - }; - - return await _baseClient.Execute(_baseClient.GetUrl(CancelStopOrderEndpoint), HttpMethod.Delete, ct, parameters, true).ConfigureAwait(false); - } - } } diff --git a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1.cs new file mode 100644 index 0000000..6f2d93a --- /dev/null +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1.cs @@ -0,0 +1,464 @@ +using CryptoExchange.Net; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Authentication; +using CoinEx.Net.Enums; +using CoinEx.Net.Objects.Internal; +using CoinEx.Net.Objects.Models; +using CoinEx.Net.Interfaces.Clients.SpotApi; +using CryptoExchange.Net.CommonObjects; +using System.Globalization; +using CryptoExchange.Net.Interfaces.CommonClients; +using Newtonsoft.Json.Linq; +using Microsoft.Extensions.Logging; +using CoinEx.Net.Objects.Options; +using CryptoExchange.Net.Interfaces; +using CryptoExchange.Net.Converters.MessageParsing; +using CryptoExchange.Net.Clients; + +namespace CoinEx.Net.Clients.SpotApi +{ + /// + public class CoinExRestClientSpotApiV1 : RestApiClient, ICoinExClientSpotApiV1, ISpotClient + { + #region fields + /// + public new CoinExRestOptions ClientOptions => (CoinExRestOptions)base.ClientOptions; + + /// + /// Event triggered when an order is placed via this client + /// + public event Action? OnOrderPlaced; + /// + /// Event triggered when an order is canceled via this client. Note that this does not trigger when using CancelAllOrdersAsync + /// + public event Action? OnOrderCanceled; + #endregion + + /// + public string ExchangeName => "CoinEx"; + + #region Api clients + /// + public ICoinExClientSpotApiV1Account Account { get; } + /// + public ICoinExClientSpotApiV1ExchangeData ExchangeData { get; } + /// + public ICoinExClientSpotApiV1Trading Trading { get; } + #endregion + + internal readonly string _brokerId; + + #region ctor + internal CoinExRestClientSpotApiV1(ILogger logger, HttpClient? httpClient, CoinExRestOptions options) : + base(logger, httpClient, options.Environment.RestBaseAddress, options, options.SpotOptions) + { + Account = new CoinExRestClientSpotApiV1Account(this); + ExchangeData = new CoinExRestClientSpotApiV1ExchangeData(this); + Trading = new CoinExRestClientSpotApiV1Trading(this); + + ParameterPositions[HttpMethod.Delete] = HttpMethodParameterPosition.InUri; + + _brokerId = !string.IsNullOrEmpty(options.BrokerId) ? options.BrokerId! : "147866029"; + + } + #endregion + + /// + protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials) + => new CoinExAuthenticationProvider(credentials, ClientOptions.NonceProvider ?? new CoinExNonceProvider()); + + #region methods + #region private + internal async Task> Execute(Uri uri, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false) where T : class + { + var result = await SendRequestAsync>(uri, method, ct, parameters, signed).ConfigureAwait(false); + if (!result) + return result.As(default); + + if (result.Data.Code != 0) + return result.AsError(new ServerError(result.Data.Code, result.Data.Message!)); + + return result.As(result.Data.Data); + } + + internal async Task Execute(Uri uri, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false) + { + var result = await SendRequestAsync(uri, method, ct, parameters, signed).ConfigureAwait(false); + if (!result) + return result.AsDataless(); + + if (result.Data.Code != 0) + return result.AsDatalessError(new ServerError(result.Data.Code, result.Data.Message!)); + + return result.AsDataless(); + } + + internal async Task>> ExecutePaged(Uri uri, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false) where T : class + { + var result = await SendRequestAsync>>(uri, method, ct, parameters, signed).ConfigureAwait(false); + if (!result) + return result.As>(default); + + if (result.Data.Code != 0) + return result.AsError>(new ServerError(result.Data.Code, result.Data.Message!)); + + return result.As(result.Data.Data); + } + + internal Uri GetUrl(string endpoint) + { + return new Uri(BaseAddress.AppendPath("v1").AppendPath(endpoint)); + } + + internal void InvokeOrderPlaced(OrderId id) + { + OnOrderPlaced?.Invoke(id); + } + + internal void InvokeOrderCanceled(OrderId id) + { + OnOrderCanceled?.Invoke(id); + } + + #endregion + #endregion + + #region common interface + + /// + /// Get the name of a symbol for CoinEx based on the base and quote asset + /// + /// + /// + /// + public string GetSymbolName(string baseAsset, string quoteAsset) => (baseAsset + quoteAsset).ToUpperInvariant(); + + async Task>> IBaseRestClient.GetSymbolsAsync(CancellationToken ct) + { + var symbols = await ExchangeData.GetSymbolInfoAsync(ct: ct).ConfigureAwait(false); + if (!symbols) + return symbols.As>(null); + + return symbols.As(symbols.Data.Select(d => new Symbol + { + SourceObject = d, + Name = d.Key, + MinTradeQuantity = d.Value.MinQuantity, + PriceDecimals = d.Value.PricingDecimal, + QuantityDecimals = d.Value.TradingDecimal + })); + } + + async Task> IBaseRestClient.GetTickerAsync(string symbol, CancellationToken ct) + { + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetTickerAsync), nameof(symbol)); + + var tickers = await ExchangeData.GetTickerAsync(symbol, ct: ct).ConfigureAwait(false); + if (!tickers) + return tickers.As(null); + + return tickers.As(new Ticker + { + SourceObject = tickers.Data, + Symbol = symbol, + HighPrice = tickers.Data.Ticker.HighPrice, + LowPrice = tickers.Data.Ticker.LowPrice, + LastPrice = tickers.Data.Ticker.LastPrice, + Price24H = tickers.Data.Ticker.OpenPrice, + Volume = tickers.Data.Ticker.Volume + }); + } + + async Task>> IBaseRestClient.GetTickersAsync(CancellationToken ct) + { + var tickers = await ExchangeData.GetTickersAsync(ct: ct).ConfigureAwait(false); + if (!tickers) + return tickers.As>(null); + + return tickers.As(tickers.Data.Tickers.Select(t => + new Ticker + { + SourceObject = t, + Symbol = t.Key, + HighPrice = t.Value.HighPrice, + LowPrice = t.Value.LowPrice, + LastPrice = t.Value.LastPrice, + Price24H = t.Value.OpenPrice, + Volume = t.Value.Volume + })); + } + + async Task>> IBaseRestClient.GetKlinesAsync(string symbol, TimeSpan timespan, DateTime? startTime, DateTime? endTime, int? limit, CancellationToken ct) + { + if (startTime != null || endTime != null) + throw new ArgumentException("CoinEx does not support time based klines requesting", startTime != null ? nameof(startTime) : nameof(endTime)); + + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetKlinesAsync), nameof(symbol)); + + var klines = await ExchangeData.GetKlinesAsync(symbol, GetKlineIntervalFromTimespan(timespan), limit, ct: ct).ConfigureAwait(false); + if (!klines) + return klines.As>(null); + + return klines.As(klines.Data.Select(t => + new Kline + { + SourceObject = t, + OpenPrice = t.OpenPrice, + LowPrice = t.LowPrice, + OpenTime = t.OpenTime, + Volume = t.Volume, + ClosePrice = t.ClosePrice, + HighPrice = t.HighPrice + })); + } + + async Task> IBaseRestClient.GetOrderBookAsync(string symbol, CancellationToken ct) + { + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetOrderBookAsync), nameof(symbol)); + + var book = await ExchangeData.GetOrderBookAsync(symbol, 0, ct: ct).ConfigureAwait(false); + if (!book) + return book.As(null); + + return book.As(new OrderBook + { + SourceObject = book.Data, + Asks = book.Data.Asks.Select(a => new OrderBookEntry { Price = a.Price, Quantity = a.Quantity }), + Bids = book.Data.Bids.Select(b => new OrderBookEntry { Price = b.Price, Quantity = b.Quantity }) + }); + } + + async Task>> IBaseRestClient.GetRecentTradesAsync(string symbol, CancellationToken ct) + { + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetRecentTradesAsync), nameof(symbol)); + + var trades = await ExchangeData.GetTradeHistoryAsync(symbol, ct: ct).ConfigureAwait(false); + if (!trades) + return trades.As>(null); + + return trades.As(trades.Data.Select(t => + new Trade + { + SourceObject = t, + Price = t.Price, + Quantity = t.Quantity, + Symbol = symbol, + Timestamp = t.Timestamp + })); + } + + async Task> ISpotClient.PlaceOrderAsync(string symbol, CommonOrderSide side, CommonOrderType type, decimal quantity, decimal? price, string? accountId, string? clientOrderId, CancellationToken ct) + { + if (price == null && type == CommonOrderType.Limit) + throw new ArgumentException("Price parameter null while placing a limit order", nameof(price)); + + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.PlaceOrderAsync), nameof(symbol)); + + var result = await Trading.PlaceOrderAsync( + symbol, + side == CommonOrderSide.Sell ? OrderSide.Sell : OrderSide.Buy, + type == CommonOrderType.Limit ? OrderType.Limit : OrderType.Market, + quantity, + price, + clientOrderId: clientOrderId, + ct: ct).ConfigureAwait(false); + if (!result) + return result.As(null); + + return result.As(new OrderId { SourceObject = result.Data, Id = result.Data.Id.ToString(CultureInfo.InvariantCulture) }); + } + + async Task> IBaseRestClient.GetOrderAsync(string orderId, string? symbol, CancellationToken ct) + { + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetOrderAsync), nameof(symbol)); + + if (!long.TryParse(orderId, out var id)) + throw new ArgumentException($"Invalid order id for CoinEx {nameof(ISpotClient.GetOrderAsync)}", nameof(orderId)); + + var order = await Trading.GetOrderAsync(symbol!, id, ct: ct).ConfigureAwait(false); + if (!order) + return order.As(null); + + return order.As(new Order + { + SourceObject = order.Data, + Id = order.Data.Id.ToString(CultureInfo.InvariantCulture), + Price = order.Data.Price, + Quantity = order.Data.Quantity, + QuantityFilled = order.Data.QuantityFilled, + Timestamp = order.Data.CreateTime, + Symbol = order.Data.Symbol, + Side = order.Data.Side == OrderSide.Buy ? CommonOrderSide.Buy: CommonOrderSide.Sell, + Status = order.Data.Status == OrderStatus.Canceled ? CommonOrderStatus.Canceled: order.Data.Status == OrderStatus.Executed ? CommonOrderStatus.Filled: CommonOrderStatus.Active, + Type = order.Data.OrderType == OrderType.Market ? CommonOrderType.Market: order.Data.OrderType == OrderType.Limit ? CommonOrderType.Limit: CommonOrderType.Other + }); + } + + async Task>> IBaseRestClient.GetOrderTradesAsync(string orderId, string? symbol, CancellationToken ct) + { + if (!long.TryParse(orderId, out var id)) + throw new ArgumentException($"Invalid order id for CoinEx {nameof(ISpotClient.GetOrderAsync)}", nameof(orderId)); + + var result = await Trading.GetOrderTradesAsync(id, 1, 100, ct: ct).ConfigureAwait(false); + if (!result) + return result.As>(null); + + return result.As(result.Data.Data.Select(d => + new UserTrade + { + SourceObject = d, + Id = d.Id.ToString(CultureInfo.InvariantCulture), + Price = d.Price, + Quantity = d.Quantity, + Fee = d.Fee, + FeeAsset = d.FeeAsset, + OrderId = d.OrderId?.ToString(CultureInfo.InvariantCulture), + Symbol = symbol ?? string.Empty, + Timestamp = d.Timestamp + })); + } + + async Task>> IBaseRestClient.GetOpenOrdersAsync(string? symbol, CancellationToken ct) + { + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException($"CoinEx needs the {nameof(symbol)} parameter for the method {nameof(ISpotClient.GetOpenOrdersAsync)}"); + + var openOrders = await Trading.GetOpenOrdersAsync(symbol!, 1, 100, ct: ct).ConfigureAwait(false); + if (!openOrders) + return openOrders.As>(null); + + return openOrders.As(openOrders.Data.Data.Select(o => new Order + { + SourceObject = o, + Id = o.Id.ToString(CultureInfo.InvariantCulture), + Price = o.Price, + Quantity = o.Quantity, + QuantityFilled = o.QuantityFilled, + Timestamp = o.CreateTime, + Symbol = o.Symbol, + Side = o.Side == OrderSide.Buy ? CommonOrderSide.Buy : CommonOrderSide.Sell, + Status = o.Status == OrderStatus.Canceled ? CommonOrderStatus.Canceled : o.Status == OrderStatus.Executed ? CommonOrderStatus.Filled : CommonOrderStatus.Active, + Type = o.OrderType == OrderType.Market ? CommonOrderType.Market : o.OrderType == OrderType.Limit ? CommonOrderType.Limit : CommonOrderType.Other + })); + } + + async Task>> IBaseRestClient.GetClosedOrdersAsync(string? symbol, CancellationToken ct) + { + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetClosedOrdersAsync), nameof(symbol)); + + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException($"CoinEx needs the {nameof(symbol)} parameter for the method {nameof(ISpotClient.GetClosedOrdersAsync)}"); + + var result = await Trading.GetClosedOrdersAsync(symbol!, 1, 100, ct: ct).ConfigureAwait(false); + if (!result) + return result.As>(null); + + return result.As(result.Data.Data.Select(o => new Order + { + SourceObject = o, + Id = o.Id.ToString(CultureInfo.InvariantCulture), + Price = o.Price, + Quantity = o.Quantity, + QuantityFilled = o.QuantityFilled, + Timestamp = o.CreateTime, + Symbol = o.Symbol, + Side = o.Side == OrderSide.Buy ? CommonOrderSide.Buy : CommonOrderSide.Sell, + Status = o.Status == OrderStatus.Canceled ? CommonOrderStatus.Canceled : o.Status == OrderStatus.Executed ? CommonOrderStatus.Filled : CommonOrderStatus.Active, + Type = o.OrderType == OrderType.Market ? CommonOrderType.Market : o.OrderType == OrderType.Limit ? CommonOrderType.Limit : CommonOrderType.Other + })); + } + + async Task> IBaseRestClient.CancelOrderAsync(string orderId, string? symbol, CancellationToken ct) + { + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.CancelOrderAsync), nameof(symbol)); + + if (!long.TryParse(orderId, out var id)) + throw new ArgumentException($"Invalid order id for CoinEx {nameof(ISpotClient.GetOrderAsync)}", nameof(orderId)); + + var result = await Trading.CancelOrderAsync(symbol!, id, ct: ct).ConfigureAwait(false); + if (!result) + return result.As(null); + + return result.As(new OrderId + { + SourceObject = result.Data, + Id = result.Data.Id.ToString(CultureInfo.InvariantCulture) + }); + } + + async Task>> IBaseRestClient.GetBalancesAsync(string? accountId, CancellationToken ct) + { + var balances = await Account.GetBalancesAsync(ct: ct).ConfigureAwait(false); + if (!balances) + return balances.As>(null); + + return balances.As(balances.Data.Select(d => new Balance + { + SourceObject = d, + Asset = d.Key, + Available = d.Value.Available, + Total = d.Value.Frozen + d.Value.Available + })); + } + + private static KlineInterval GetKlineIntervalFromTimespan(TimeSpan timeSpan) + { + if (timeSpan == TimeSpan.FromMinutes(1)) return KlineInterval.OneMinute; + if (timeSpan == TimeSpan.FromMinutes(3)) return KlineInterval.ThreeMinutes; + if (timeSpan == TimeSpan.FromMinutes(5)) return KlineInterval.FiveMinutes; + if (timeSpan == TimeSpan.FromMinutes(15)) return KlineInterval.FiveMinutes; + if (timeSpan == TimeSpan.FromMinutes(30)) return KlineInterval.ThirtyMinutes; + if (timeSpan == TimeSpan.FromHours(1)) return KlineInterval.OneHour; + if (timeSpan == TimeSpan.FromHours(2)) return KlineInterval.TwoHours; + if (timeSpan == TimeSpan.FromHours(4)) return KlineInterval.FourHours; + if (timeSpan == TimeSpan.FromHours(6)) return KlineInterval.SixHours; + if (timeSpan == TimeSpan.FromHours(12)) return KlineInterval.TwelveHours; + if (timeSpan == TimeSpan.FromDays(1)) return KlineInterval.OneDay; + if (timeSpan == TimeSpan.FromDays(3)) return KlineInterval.ThreeDays; + if (timeSpan == TimeSpan.FromDays(7)) return KlineInterval.OneWeek; + + throw new ArgumentException("Unsupported timespan for CoinEx Klines, check supported intervals using CoinEx.Net.Objects.KlineInterval"); + } + #endregion + + /// + protected override Error ParseErrorResponse(int httpStatusCode, IEnumerable>> responseHeaders, IMessageAccessor accessor) + { + if (!accessor.IsJson) + return new ServerError(accessor.GetOriginalString()); + + var code = accessor.GetValue(MessagePath.Get().Property("code")); + var msg = accessor.GetValue(MessagePath.Get().Property("message")); + if (msg == null) + return new ServerError(accessor.GetOriginalString()); + + if (code == null) + return new ServerError(msg); + + return new ServerError(code.Value, msg); + } + + /// + public override TimeSyncInfo? GetTimeSyncInfo() => null; + + /// + public override TimeSpan? GetTimeOffset() => null; + + /// + public ISpotClient CommonSpotClient => this; + } +} diff --git a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Account.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Account.cs new file mode 100644 index 0000000..1ec8bd2 --- /dev/null +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Account.cs @@ -0,0 +1,107 @@ +using CryptoExchange.Net; +using CryptoExchange.Net.Objects; +using System.Collections.Generic; +using System.Globalization; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Objects.Models; +using CoinEx.Net.Interfaces.Clients.SpotApi; + +namespace CoinEx.Net.Clients.SpotApi +{ + /// + public class CoinExRestClientSpotApiV1Account : ICoinExClientSpotApiV1Account + { + private const string AccountInfoEndpoint = "balance/info"; + private const string WithdrawalHistoryEndpoint = "balance/coin/withdraw"; + private const string DepositHistoryEndpoint = "balance/coin/deposit"; + private const string WithdrawEndpoint = "balance/coin/withdraw"; + private const string CancelWithdrawalEndpoint = "balance/coin/withdraw"; + private const string DepositAddressEndpoint = "balance/deposit/address/"; + + + private readonly CoinExRestClientSpotApiV1 _baseClient; + + internal CoinExRestClientSpotApiV1Account(CoinExRestClientSpotApiV1 baseClient) + { + _baseClient = baseClient; + } + + /// + public async Task>> GetBalancesAsync(CancellationToken ct = default) + { + var result = await _baseClient.Execute>(_baseClient.GetUrl(AccountInfoEndpoint), HttpMethod.Get, ct, null, true).ConfigureAwait(false); + if (result) + { + foreach (var b in result.Data) + b.Value.Asset = b.Key; + } + + return result; + } + + /// + public async Task>> GetDepositHistoryAsync(string? asset = null, int? page = null, int? limit = null, CancellationToken ct = default) + { + limit?.ValidateIntBetween(nameof(limit), 1, 100); + var parameters = new Dictionary(); + parameters.AddOptionalParameter("coin_type", asset); + parameters.AddOptionalParameter("page", page); + parameters.AddOptionalParameter("limit", limit); + + return await _baseClient.ExecutePaged(_baseClient.GetUrl(DepositHistoryEndpoint), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetWithdrawalHistoryAsync(string? asset = null, long? withdrawId = null, int? page = null, int? limit = null, CancellationToken ct = default) + { + limit?.ValidateIntBetween(nameof(limit), 1, 100); + var parameters = new Dictionary(); + parameters.AddOptionalParameter("coin_type", asset); + parameters.AddOptionalParameter("coin_withdraw_id", withdrawId); + parameters.AddOptionalParameter("page", page); + parameters.AddOptionalParameter("limit", limit); + + return await _baseClient.Execute>(_baseClient.GetUrl(WithdrawalHistoryEndpoint), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> WithdrawAsync(string asset, string address, bool localTransfer, decimal quantity, string? network = null, CancellationToken ct = default) + { + asset.ValidateNotNull(nameof(asset)); + address.ValidateNotNull(nameof(address)); + var parameters = new Dictionary + { + { "coin_type", asset }, + { "coin_address", address }, + { "transfer_method", localTransfer ? "local": "onchain" }, + { "actual_amount", quantity.ToString(CultureInfo.InvariantCulture) } + }; + parameters.AddOptionalParameter("smart_contract_name", network); + + return await _baseClient.Execute(_baseClient.GetUrl(WithdrawEndpoint), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> CancelWithdrawalAsync(long withdrawId, CancellationToken ct = default) + { + var parameters = new Dictionary + { + { "coin_withdraw_id", withdrawId } + }; + + var result = await _baseClient.Execute(_baseClient.GetUrl(CancelWithdrawalEndpoint), HttpMethod.Delete, ct, parameters, true).ConfigureAwait(false); + return result.As(result.Success); + } + + /// + public async Task> GetDepositAddressAsync(string asset, string? smartContractName = null, CancellationToken ct = default) + { + var parameters = new Dictionary(); + parameters.AddOptionalParameter("smart_contract_name", smartContractName); + + return await _baseClient.Execute(_baseClient.GetUrl(DepositAddressEndpoint + asset), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + } +} diff --git a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1ExchangeData.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1ExchangeData.cs new file mode 100644 index 0000000..1e48b44 --- /dev/null +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1ExchangeData.cs @@ -0,0 +1,154 @@ +using CoinEx.Net.Converters; +using CoinEx.Net.Enums; +using CryptoExchange.Net; +using CryptoExchange.Net.Objects; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Objects.Models; +using CoinEx.Net.Interfaces.Clients.SpotApi; +using CoinEx.Net.ExtensionMethods; + +namespace CoinEx.Net.Clients.SpotApi +{ + /// + public class CoinExRestClientSpotApiV1ExchangeData : ICoinExClientSpotApiV1ExchangeData + { + private const string AssetConfigEndpoint = "common/asset/config"; + private const string CurrencyRateEndpoint = "common/currency/rate"; + + private const string MarketListEndpoint = "market/list"; + private const string MarketStatisticsEndpoint = "market/ticker"; + private const string MarketStatisticsListEndpoint = "market/ticker/all"; + private const string MarketDepthEndpoint = "market/depth"; + private const string MarketDealsEndpoint = "market/deals"; + private const string MarketKlinesEndpoint = "market/kline"; + private const string MarketInfoEndpoint = "market/info"; + private const string MiningDifficultyEndpoint = "order/mining/difficulty"; + + private readonly CoinExRestClientSpotApiV1 _baseClient; + + internal CoinExRestClientSpotApiV1ExchangeData(CoinExRestClientSpotApiV1 baseClient) + { + _baseClient = baseClient; + } + + + /// + public async Task>> GetSymbolsAsync(CancellationToken ct = default) + { + return await _baseClient.Execute>(_baseClient.GetUrl(MarketListEndpoint), HttpMethod.Get, ct).ConfigureAwait(false); + } + + /// + public async Task>> GetCurrencyRateAsync(CancellationToken ct = default) + { + return await _baseClient.Execute>(_baseClient.GetUrl(CurrencyRateEndpoint), HttpMethod.Get, ct).ConfigureAwait(false); + } + + /// + public async Task>> GetAssetsAsync(string? assetType = null, CancellationToken ct = default) + { + var parameters = new Dictionary(); + parameters.AddOptionalParameter("coin_type", assetType); + + return await _baseClient.Execute>(_baseClient.GetUrl(AssetConfigEndpoint), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task> GetTickerAsync(string symbol, CancellationToken ct = default) + { + symbol.ValidateCoinExSymbol(); + var parameters = new Dictionary + { + { "market", symbol } + }; + + return await _baseClient.Execute(_baseClient.GetUrl(MarketStatisticsEndpoint), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task> GetTickersAsync(CancellationToken ct = default) + { + var data = await _baseClient.Execute(_baseClient.GetUrl(MarketStatisticsListEndpoint), HttpMethod.Get, ct) + .ConfigureAwait(false); + if (!data) + return data; + + foreach (var item in data.Data.Tickers) + item.Value.Symbol = item.Key; + return data; + } + + /// + public async Task> GetOrderBookAsync(string symbol, int mergeDepth, int? limit = null, CancellationToken ct = default) + { + symbol.ValidateCoinExSymbol(); + mergeDepth.ValidateIntBetween(nameof(mergeDepth), 0, 8); + limit?.ValidateIntValues(nameof(limit), 5, 10, 20); + + var parameters = new Dictionary + { + { "market", symbol }, + { "merge", CoinExHelpers.MergeDepthIntToString(mergeDepth) } + }; + parameters.AddOptionalParameter("limit", limit); + + return await _baseClient.Execute(_baseClient.GetUrl(MarketDepthEndpoint), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task>> GetTradeHistoryAsync(string symbol, long? fromId = null, CancellationToken ct = default) + { + symbol.ValidateCoinExSymbol(); + + var parameters = new Dictionary + { + { "market", symbol } + }; + parameters.AddOptionalParameter("last_id", fromId); + + return await _baseClient.Execute>(_baseClient.GetUrl(MarketDealsEndpoint), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task>> GetSymbolInfoAsync(string symbol, CancellationToken ct = default) + { + var parameters = new Dictionary + { + { "market", symbol } + }; + return await _baseClient.Execute>(_baseClient.GetUrl(MarketInfoEndpoint), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task>> GetSymbolInfoAsync(CancellationToken ct = default) + { + return await _baseClient.Execute>(_baseClient.GetUrl(MarketInfoEndpoint), HttpMethod.Get, ct).ConfigureAwait(false); + } + + /// + public async Task>> GetKlinesAsync(string symbol, KlineInterval interval, int? limit = null, CancellationToken ct = default) + { + symbol.ValidateCoinExSymbol(); + limit?.ValidateIntBetween(nameof(limit), 1, 1000); + var parameters = new Dictionary + { + { "market", symbol }, + { "type", JsonConvert.SerializeObject(interval, new KlineIntervalConverter(false)) } + }; + parameters.AddOptionalParameter("limit", limit); + + return await _baseClient.Execute>(_baseClient.GetUrl(MarketKlinesEndpoint), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + + /// + public async Task> GetMiningDifficultyAsync(CancellationToken ct = default) + { + return await _baseClient.Execute(_baseClient.GetUrl(MiningDifficultyEndpoint), HttpMethod.Get, ct, null, true).ConfigureAwait(false); + } + } +} diff --git a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Trading.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Trading.cs new file mode 100644 index 0000000..1530f09 --- /dev/null +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Trading.cs @@ -0,0 +1,227 @@ +using CoinEx.Net.Converters; +using CoinEx.Net.Enums; +using CryptoExchange.Net; +using CryptoExchange.Net.Objects; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Objects.Models; +using CoinEx.Net.Interfaces.Clients.SpotApi; +using CryptoExchange.Net.CommonObjects; +using CoinEx.Net.ExtensionMethods; + +namespace CoinEx.Net.Clients.SpotApi +{ + /// + public class CoinExRestClientSpotApiV1Trading : ICoinExClientSpotApiV1Trading + { + private const string PlaceLimitOrderEndpoint = "order/limit"; + private const string PlaceMarketOrderEndpoint = "order/market"; + private const string PlaceStopLimitOrderEndpoint = "order/stop/limit"; + private const string PlaceStopMarketOrderEndpoint = "order/stop/market"; + private const string PlaceImmediateOrCancelOrderEndpoint = "order/ioc"; + private const string FinishedOrdersEndpoint = "order/finished"; + private const string OpenOrdersEndpoint = "order/pending"; + private const string OpenStopOrdersEndpoint = "order/stop/pending"; + private const string OrderStatusEndpoint = "order/status"; + private const string OrderDetailsEndpoint = "order/deals"; + private const string UserTransactionsEndpoint = "order/user/deals"; + private const string CancelOrderEndpoint = "order/pending"; + private const string CancelStopOrderEndpoint = "order/stop/pending"; + + private readonly CoinExRestClientSpotApiV1 _baseClient; + + internal CoinExRestClientSpotApiV1Trading(CoinExRestClientSpotApiV1 baseClient) + { + _baseClient = baseClient; + } + + /// + public async Task> PlaceOrderAsync( + string symbol, + OrderSide side, + OrderType type, + decimal quantity, + + decimal? price = null, + decimal? stopPrice = null, + bool? immediateOrCancel = null, + OrderOption? orderOption = null, + string? clientOrderId = null, + string? sourceId = null, + CancellationToken ct = default) + { + symbol.ValidateCoinExSymbol(); + + var endpoint = ""; + if (type == OrderType.Limit) + endpoint = PlaceLimitOrderEndpoint; + else if (type == OrderType.Market) + endpoint = PlaceMarketOrderEndpoint; + else if (type == OrderType.StopLimit) + endpoint = PlaceStopLimitOrderEndpoint; + else if (type == OrderType.StopMarket) + endpoint = PlaceStopMarketOrderEndpoint; + + if (immediateOrCancel == true) + { + if (type != OrderType.Limit) + throw new ArgumentException("ImmediateOrCancel only valid for limit orders"); + + endpoint = PlaceImmediateOrCancelOrderEndpoint; + } + + clientOrderId ??= ExchangeHelpers.AppendRandomString("x-" + _baseClient._brokerId + "-", 32); + + var parameters = new Dictionary + { + { "market", symbol }, + { "type", JsonConvert.SerializeObject(side, new OrderSideConverter(false)) }, + { "amount", quantity.ToString(CultureInfo.InvariantCulture) } + }; + parameters.AddOptionalParameter("price", price?.ToString(CultureInfo.InvariantCulture)); + parameters.AddOptionalParameter("option", orderOption.HasValue ? JsonConvert.SerializeObject(orderOption, new OrderOptionConverter(false)) : null); + parameters.AddOptionalParameter("stop_price", stopPrice?.ToString(CultureInfo.InvariantCulture)); + parameters.AddOptionalParameter("client_id", clientOrderId); + parameters.AddOptionalParameter("source_id", sourceId); + + var result = await _baseClient.Execute(_baseClient.GetUrl(endpoint), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + if (result) + _baseClient.InvokeOrderPlaced(new OrderId { SourceObject = result.Data, Id = result.Data.Id.ToString(CultureInfo.InvariantCulture) }); + + return result; + } + + /// + public async Task>> GetOpenOrdersAsync(string? symbol = null, int? page = null, int? limit = null, CancellationToken ct = default) + { + symbol?.ValidateCoinExSymbol(); + limit?.ValidateIntBetween(nameof(limit), 1, 100); + var parameters = new Dictionary + { + { "page", page ?? 1 }, + { "limit", limit ?? 100 } + }; + + parameters.AddOptionalParameter("market", symbol); + + return await _baseClient.ExecutePaged(_baseClient.GetUrl(OpenOrdersEndpoint), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetOpenStopOrdersAsync(string symbol, int? page = null, int? limit = null, CancellationToken ct = default) + { + symbol?.ValidateCoinExSymbol(); + limit?.ValidateIntBetween(nameof(limit), 1, 100); + var parameters = new Dictionary + { + { "page", page ?? 1 }, + { "limit", limit ?? 100 } + }; + parameters.AddOptionalParameter("market", symbol); + return await _baseClient.ExecutePaged(_baseClient.GetUrl(OpenStopOrdersEndpoint), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetClosedOrdersAsync(string symbol, int? page = null, int? limit = null, CancellationToken ct = default) + { + symbol.ValidateCoinExSymbol(); + limit?.ValidateIntBetween(nameof(limit), 1, 100); + var parameters = new Dictionary + { + { "market", symbol }, + { "page", page ?? 1 }, + { "limit", limit ?? 100 } + }; + + return await _baseClient.ExecutePaged(_baseClient.GetUrl(FinishedOrdersEndpoint), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> GetOrderAsync(string symbol, long orderId, CancellationToken ct = default) + { + symbol.ValidateCoinExSymbol(); + var parameters = new Dictionary + { + { "market", symbol }, + { "id", orderId } + }; + + return await _baseClient.Execute(_baseClient.GetUrl(OrderStatusEndpoint), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetOrderTradesAsync(long orderId, int? page = null, int? limit = null, CancellationToken ct = default) + { + limit?.ValidateIntBetween(nameof(limit), 1, 100); + var parameters = new Dictionary + { + { "id", orderId }, + { "page", page ?? 1 }, + { "limit", limit ?? 100 } + }; + + return await _baseClient.ExecutePaged(_baseClient.GetUrl(OrderDetailsEndpoint), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetUserTradesAsync(string symbol, int? page = null, int? limit = null, CancellationToken ct = default) + { + symbol.ValidateCoinExSymbol(); + limit?.ValidateIntBetween(nameof(limit), 1, 100); + var parameters = new Dictionary + { + { "market", symbol }, + { "page", page ?? 1 }, + { "limit", limit ?? 100 } + }; + + return await _baseClient.ExecutePaged(_baseClient.GetUrl(UserTransactionsEndpoint), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> CancelOrderAsync(string symbol, long orderId, CancellationToken ct = default) + { + symbol.ValidateCoinExSymbol(); + var parameters = new Dictionary + { + { "market", symbol }, + { "id", orderId } + }; + + var result = await _baseClient.Execute(_baseClient.GetUrl(CancelOrderEndpoint), HttpMethod.Delete, ct, parameters, true).ConfigureAwait(false); + if (result) + _baseClient.InvokeOrderCanceled(new OrderId { SourceObject = result.Data, Id = result.Data.Id.ToString(CultureInfo.InvariantCulture) }); + return result; + } + + /// + public async Task CancelAllOrdersAsync(string symbol, CancellationToken ct = default) + { + symbol.ValidateCoinExSymbol(); + var parameters = new Dictionary + { + { "market", symbol }, + }; + + return await _baseClient.Execute(_baseClient.GetUrl(CancelOrderEndpoint), HttpMethod.Delete, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task CancelAllStopOrdersAsync(string symbol, CancellationToken ct = default) + { + symbol.ValidateCoinExSymbol(); + var parameters = new Dictionary + { + { "market", symbol }, + }; + + return await _baseClient.Execute(_baseClient.GetUrl(CancelStopOrderEndpoint), HttpMethod.Delete, ct, parameters, true).ConfigureAwait(false); + } + + } +} diff --git a/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApiV1.cs similarity index 97% rename from CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs rename to CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApiV1.cs index 5512830..0d4f189 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApiV1.cs @@ -32,8 +32,8 @@ namespace CoinEx.Net.Clients.SpotApi { - /// - public class CoinExSocketClientSpotApi : SocketApiClient, ICoinExSocketClientSpotApi + /// + public class CoinExSocketClientSpotApiV1 : SocketApiClient, ICoinExSocketClientSpotApiV1 { #region fields /// @@ -49,7 +49,7 @@ public class CoinExSocketClientSpotApi : SocketApiClient, ICoinExSocketClientSpo /// /// Create a new instance of CoinExSocketClient with default options /// - internal CoinExSocketClientSpotApi(ILogger logger, CoinExSocketOptions options) + internal CoinExSocketClientSpotApiV1(ILogger logger, CoinExSocketOptions options) : base(logger, options.Environment.SocketBaseAddress, options, options.SpotOptions) { RegisterPeriodicQuery("Ping", TimeSpan.FromMinutes(1), q => (new CoinExQuery("server.ping", new object[] { })), null); diff --git a/CoinEx.Net/CoinEx.Net.xml b/CoinEx.Net/CoinEx.Net.xml index 7310e2d..973cad0 100644 --- a/CoinEx.Net/CoinEx.Net.xml +++ b/CoinEx.Net/CoinEx.Net.xml @@ -7,6 +7,9 @@ + + + @@ -36,7 +39,7 @@ - + @@ -67,38 +70,38 @@ - - + + - + - + Event triggered when an order is placed via this client - + Event triggered when an order is canceled via this client. Note that this does not trigger when using CancelAllOrdersAsync - + - + - + - + - + - + Get the name of a symbol for CoinEx based on the base and quote asset @@ -106,171 +109,235 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + Create a new instance of CoinExSocketClient with default options - + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + Event triggered when an order is placed via this client + + + + + Event triggered when an order is canceled via this client. Note that this does not trigger when using CancelAllOrdersAsync + + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -582,9 +649,14 @@ Client for accessing the CoinEx API. + + + Spot V1 API endpoints + + - Spot endpoints + Spot V2 API endpoints @@ -598,7 +670,7 @@ Client for accessing the CoinEx websocket API - + Spot streams @@ -609,38 +681,38 @@ The credentials to set - + Spot API - + Endpoints related to account settings, info or actions - + Endpoints related to retrieving market and system data - + Endpoints related to orders and trades - + Get the ISpotClient for this client. This is a common interface which allows for some basic operations without knowing any details of the exchange. - + CoinEx account endpoints. Account endpoints include balance info, withdraw/deposit info and requesting and account settings - + Retrieves a list of balances. Requires API credentials @@ -648,7 +720,7 @@ Cancellation token List of balances - + Retrieves a list of deposits. Requires API credentials and withdrawal permission on the API key @@ -659,7 +731,7 @@ Cancellation token - + Get the deposit address of an asset @@ -669,7 +741,7 @@ Cancellation token - + Retrieves a list of withdrawals. Requires API credentials and withdrawal permission on the API key @@ -681,7 +753,7 @@ Cancellation token - + Withdraw assets from CoinEx to a specific address. Requires API credentials and withdrawal permission on the API key @@ -694,7 +766,7 @@ Cancellation token The withdrawal object - + Cancel a specific withdrawal. Requires API credentials and withdrawal permission on the API key @@ -703,12 +775,12 @@ Cancellation token True if successful, false otherwise - + CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. - + Gets the exchange rates of currencies @@ -716,7 +788,7 @@ Cancellation token - + Gets the asset configs @@ -725,7 +797,7 @@ Cancellation token - + Gets a list of symbols active on CoinEx @@ -733,7 +805,7 @@ Cancellation token List of symbol names - + Gets the state of a specific symbol @@ -742,7 +814,7 @@ Cancellation token The state of the symbol - + Gets the states of all symbols @@ -750,7 +822,7 @@ Cancellation token List of states for all symbols - + Gets the order book for a symbol @@ -761,7 +833,7 @@ Cancellation token Order book for a symbol - + Gets the latest trades for a symbol @@ -771,7 +843,7 @@ Cancellation token List of trades for a symbol - + Retrieves kline data for a specific symbol @@ -782,7 +854,7 @@ Cancellation token List of klines for a symbol - + Retrieves market data for the exchange @@ -791,7 +863,7 @@ Cancellation token List of market data for the exchange - + Retrieves market data for the exchange @@ -799,7 +871,7 @@ Cancellation token List of market data for the exchange - + Retrieve the mining difficulty. Requires API credentials @@ -807,12 +879,12 @@ Cancellation token Mining difficulty - + CoinEx trading endpoints, placing and mananging orders. - + Places an order. This is a single method for multiple place order endpoints. The called endpoint depends on the provided order type. @@ -833,7 +905,7 @@ Cancellation token Details of the order that was placed - + Retrieves a list of open orders for a symbol. Requires API credentials @@ -844,7 +916,7 @@ Cancellation token List of open orders for a symbol - + Retrieves a list of open stop orders for a symbol. Requires API credentials @@ -855,7 +927,7 @@ Cancellation token List of open orders for a symbol - + Retrieves a list of executed orders for a symbol in the last 2 days. Requires API credentials @@ -866,7 +938,7 @@ Cancellation token List of executed orders for a symbol - + Retrieves details of an order. Requires API credentials @@ -876,7 +948,7 @@ Cancellation token Details of the order - + Retrieves execution details of a specific order. Requires API credentials @@ -887,7 +959,7 @@ Cancellation token Details of an executed order - + Gets a list of trades you executed on a specific symbol. Requires API credentials @@ -898,7 +970,7 @@ Cancellation token List of trades for a symbol - + Cancels an order. Requires API credentials @@ -908,7 +980,7 @@ Cancellation token Details of the canceled order - + Cancels all stop orders. Requires API credentials @@ -916,7 +988,7 @@ Cancellation token Execution statut - + Cancels all orders. Requires API credentials @@ -925,26 +997,26 @@ Cancellation token Execution statut - + Spot streams - + Pings the server Success if server responded, error otherwise - + Gets the server time The server time - + Get the symbol ticker @@ -953,7 +1025,7 @@ The period to get data over, specified in seconds. i.e. one minute = 60, one day = 86400 Symbol state - + Subscribe to symbol ticker updates for a specific symbol @@ -963,7 +1035,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to ticker updates for all symbols @@ -972,7 +1044,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Get an order book @@ -982,7 +1054,7 @@ The depth of merging, based on 8 decimals. 1 mergeDepth will merge the last decimals of all order in the book, 7 will merge the last 7 decimals of all orders together Order book of a symbol - + Subscribe to order book updates @@ -994,7 +1066,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Gets the latest trades on a symbol @@ -1004,7 +1076,7 @@ Return trades since this id List of trades - + Subscribe to symbol trade updates for a symbol @@ -1014,7 +1086,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Gets symbol kline data @@ -1023,7 +1095,7 @@ The interval of the candles - + Get balances of assets. Requires API credentials @@ -1031,7 +1103,7 @@ The assets to get the balances for, empty for all Dictionary of assets and their balances - + Subscribe to updates of your balances, Receives updates whenever the balance for an asset changes @@ -1040,7 +1112,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Gets a list of open orders for a symbol @@ -1051,7 +1123,7 @@ The limit of results List of open orders - + Subscribe to updates of active orders. Receives updates whenever an order is placed, updated or finished @@ -1060,7 +1132,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to updates of active orders. Receives updates whenever an order is placed, updated or finished @@ -1070,6 +1142,64 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + Spot API + + + + + Endpoints related to account settings, info or actions + + + + + Endpoints related to retrieving market and system data + + + + + Endpoints related to orders and trades + + + + + Get the ISpotClient for this client. This is a common interface which allows for some basic operations without knowing any details of the exchange. + + + + + + CoinEx account endpoints. Account endpoints include balance info, withdraw/deposit info and requesting and account settings + + + + + CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. + + + + + Get symbol information + + + Cancelation Token + + + + + Get symbol tickers + + + Fitler by symbol names + Cancelation Token + + + + + CoinEx trading endpoints, placing and mananging orders. + + CoinEx order book factory @@ -2118,6 +2248,116 @@ The quantity of the transaction + + + Symbol info + + + + + Symbol name + + + + + Maker fee rate + + + + + Taker fee rate + + + + + Minimal order quantiy + + + + + Base asset + + + + + Quote asset + + + + + Quantity precision + + + + + Price precision + + + + + Is Automated Market Maker available + + + + + Is Margin Trading available + + + + + Ticker (24h price stats) info + + + + + Symbol name + + + + + Last price + + + + + Open price + + + + + Close price + + + + + High price + + + + + Low price + + + + + Volume in base asset + + + + + Volume in quote asset + + + + + Sell volume + + + + + Buy volume + + Options for CoinEx SymbolOrderBook diff --git a/CoinEx.Net/ExtensionMethods/ServiceCollectionExtensions.cs b/CoinEx.Net/ExtensionMethods/ServiceCollectionExtensions.cs index 7c4dbaa..b58a762 100644 --- a/CoinEx.Net/ExtensionMethods/ServiceCollectionExtensions.cs +++ b/CoinEx.Net/ExtensionMethods/ServiceCollectionExtensions.cs @@ -60,7 +60,7 @@ public static IServiceCollection AddCoinEx( services.AddTransient(); services.AddTransient(); services.AddSingleton(); - services.AddTransient(x => x.GetRequiredService().SpotApi.CommonSpotClient); + services.AddTransient(x => x.GetRequiredService().SpotApiV1.CommonSpotClient); if (socketClientLifeTime == null) services.AddSingleton(); else diff --git a/CoinEx.Net/Interfaces/Clients/ICoinExRestClient.cs b/CoinEx.Net/Interfaces/Clients/ICoinExRestClient.cs index ea37a06..96f5322 100644 --- a/CoinEx.Net/Interfaces/Clients/ICoinExRestClient.cs +++ b/CoinEx.Net/Interfaces/Clients/ICoinExRestClient.cs @@ -10,7 +10,11 @@ namespace CoinEx.Net.Interfaces.Clients public interface ICoinExRestClient : IRestClient { /// - /// Spot endpoints + /// Spot V1 API endpoints + /// + ICoinExClientSpotApiV1 SpotApiV1 { get; } + /// + /// Spot V2 API endpoints /// ICoinExClientSpotApi SpotApi { get; } diff --git a/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs b/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs index a780e14..89ffe6e 100644 --- a/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs +++ b/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs @@ -12,7 +12,7 @@ public interface ICoinExSocketClient : ISocketClient /// /// Spot streams /// - public ICoinExSocketClientSpotApi SpotApi { get; } + public ICoinExSocketClientSpotApiV1 SpotApiV1 { get; } /// /// Set the API credentials for this client. All Api clients in this client will use the new credentials, regardless of earlier set options. diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiAccount.cs b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiAccount.cs index fd2cade..63e27de 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiAccount.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiAccount.cs @@ -11,67 +11,6 @@ namespace CoinEx.Net.Interfaces.Clients.SpotApi /// public interface ICoinExClientSpotApiAccount { - /// - /// Retrieves a list of balances. Requires API credentials - /// - /// - /// Cancellation token - /// List of balances - Task>> GetBalancesAsync(CancellationToken ct = default); - - /// - /// Retrieves a list of deposits. Requires API credentials and withdrawal permission on the API key - /// - /// - /// The asset to get history for - /// The page in the results to retrieve - /// The number of results to return per page - /// Cancellation token - /// - Task>> GetDepositHistoryAsync(string? asset = null, int? page = null, int? limit = null, CancellationToken ct = default); - - /// - /// Get the deposit address of an asset - /// - /// - /// The asset to deposit - /// Name of the network to use - /// Cancellation token - /// - Task> GetDepositAddressAsync(string asset, string? smartContractName = null, CancellationToken ct = default); - - /// - /// Retrieves a list of withdrawals. Requires API credentials and withdrawal permission on the API key - /// - /// - /// The asset to get history for - /// Retrieve a withdrawal with a specific id - /// The page in the results to retrieve - /// The number of results to return per page - /// Cancellation token - /// - Task>> GetWithdrawalHistoryAsync(string? asset = null, long? withdrawId = null, int? page = null, int? limit = null, CancellationToken ct = default); - - /// - /// Withdraw assets from CoinEx to a specific address. Requires API credentials and withdrawal permission on the API key - /// - /// - /// The asset to withdraw - /// Is it a local transfer between users or onchain - /// The address to withdraw to - /// The quantity to withdraw. This is the quantity AFTER fees have been deducted. For fee rates see https://www.coinex.com/fees - /// The network to use for the withdrawal - /// Cancellation token - /// The withdrawal object - Task> WithdrawAsync(string asset, string address, bool localTransfer, decimal quantity, string? network = null, CancellationToken ct = default); - - /// - /// Cancel a specific withdrawal. Requires API credentials and withdrawal permission on the API key - /// - /// - /// The id of the withdrawal to cancel - /// Cancellation token - /// True if successful, false otherwise - Task> CancelWithdrawalAsync(long withdrawId, CancellationToken ct = default); + } } diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiExchangeData.cs b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiExchangeData.cs index e8e25cf..d96ced8 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiExchangeData.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiExchangeData.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using CoinEx.Net.Objects.Models; +using CoinEx.Net.Objects.Models.V2; namespace CoinEx.Net.Interfaces.Clients.SpotApi { @@ -13,102 +13,20 @@ namespace CoinEx.Net.Interfaces.Clients.SpotApi public interface ICoinExClientSpotApiExchangeData { /// - /// Gets the exchange rates of currencies - /// + /// Get symbol information + /// /// - /// Cancellation token + /// Cancelation Token /// - Task>> GetCurrencyRateAsync(CancellationToken ct = default); + Task>> GetSymbolsAsync(CancellationToken ct = default); /// - /// Gets the asset configs - /// + /// Get symbol tickers + /// /// - /// Optionally only return a certain type of asset, for example BCH - /// Cancellation token + /// Fitler by symbol names + /// Cancelation Token /// - Task>> GetAssetsAsync(string? assetType = null, CancellationToken ct = default); - - /// - /// Gets a list of symbols active on CoinEx - /// - /// - /// Cancellation token - /// List of symbol names - Task>> GetSymbolsAsync(CancellationToken ct = default); - - /// - /// Gets the state of a specific symbol - /// - /// - /// The symbol to retrieve state for - /// Cancellation token - /// The state of the symbol - Task> GetTickerAsync(string symbol, CancellationToken ct = default); - - /// - /// Gets the states of all symbols - /// - /// - /// Cancellation token - /// List of states for all symbols - Task> GetTickersAsync(CancellationToken ct = default); - - /// - /// Gets the order book for a symbol - /// - /// - /// The symbol to retrieve depth data for - /// The depth of merging, based on 8 decimals. 1 mergeDepth will merge the last decimals of all order in the book, 7 will merge the last 7 decimals of all orders together - /// The limit of results returned - /// Cancellation token - /// Order book for a symbol - Task> GetOrderBookAsync(string symbol, int mergeDepth, int? limit = null, CancellationToken ct = default); - - /// - /// Gets the latest trades for a symbol - /// - /// - /// The symbol to retrieve data for - /// The id from which on to return trades - /// Cancellation token - /// List of trades for a symbol - Task>> GetTradeHistoryAsync(string symbol, long? fromId = null, CancellationToken ct = default); - - /// - /// Retrieves kline data for a specific symbol - /// - /// - /// The symbol to retrieve klines for - /// The interval of the candles - /// Limit of the number of results - /// Cancellation token - /// List of klines for a symbol - Task>> GetKlinesAsync(string symbol, KlineInterval interval, int? limit = null, CancellationToken ct = default); - - /// - /// Retrieves market data for the exchange - /// - /// - /// The symbol to retrieve data for - /// Cancellation token - /// List of market data for the exchange - Task>> GetSymbolInfoAsync(string symbol, CancellationToken ct = default); - - /// - /// Retrieves market data for the exchange - /// - /// - /// Cancellation token - /// List of market data for the exchange - Task>> GetSymbolInfoAsync(CancellationToken ct = default); - - /// - /// Retrieve the mining difficulty. Requires API credentials - /// - /// - /// Cancellation token - /// Mining difficulty - Task> GetMiningDifficultyAsync(CancellationToken ct = default); + Task>> GetTickersAsync(IEnumerable? symbols = null, CancellationToken ct = default); } } diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs index 539a858..a98df83 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs @@ -11,129 +11,6 @@ namespace CoinEx.Net.Interfaces.Clients.SpotApi /// public interface ICoinExClientSpotApiTrading { - /// - /// Places an order. This is a single method for multiple place order endpoints. The called endpoint depends on the provided order type. - /// - /// - /// - /// - /// - /// The symbol to place the order for - /// Order side - /// Order type - /// The quantity of the order - /// The price of a single unit of the order - /// Option for the order - /// The stop-price of a single unit of the order - /// True if the order should be filled immediately up on placing, otherwise it will be canceled - /// Client id which can be used to match the order - /// User defined number - /// Cancellation token - /// Details of the order that was placed - Task> PlaceOrderAsync( - string symbol, - OrderSide side, - OrderType type, - decimal quantity, - - decimal? price = null, - decimal? stopPrice = null, - bool? immediateOrCancel = null, - OrderOption? orderOption = null, - string? clientOrderId = null, - string? sourceId = null, - CancellationToken ct = default); - - /// - /// Retrieves a list of open orders for a symbol. Requires API credentials - /// - /// - /// The symbol to retrieve the open orders for - /// The page of the resulting list - /// The number of results per page - /// Cancellation token - /// List of open orders for a symbol - Task>> GetOpenOrdersAsync(string? symbol = null, int? page = null, int? limit = null, CancellationToken ct = default); - - /// - /// Retrieves a list of open stop orders for a symbol. Requires API credentials - /// - /// - /// The symbol to retrieve the open orders for - /// The page of the resulting list - /// The number of results per page - /// Cancellation token - /// List of open orders for a symbol - Task>> GetOpenStopOrdersAsync(string symbol, int? page = null, int? limit = null, CancellationToken ct = default); - - /// - /// Retrieves a list of executed orders for a symbol in the last 2 days. Requires API credentials - /// - /// - /// The symbol to retrieve the open orders for - /// The page of the resulting list - /// The number of results per page - /// Cancellation token - /// List of executed orders for a symbol - Task>> GetClosedOrdersAsync(string symbol, int? page = null, int? limit = null, CancellationToken ct = default); - - /// - /// Retrieves details of an order. Requires API credentials - /// - /// - /// The id of the order to retrieve - /// The symbol the order is for - /// Cancellation token - /// Details of the order - Task> GetOrderAsync(string symbol, long orderId, CancellationToken ct = default); - - /// - /// Retrieves execution details of a specific order. Requires API credentials - /// - /// - /// The id of the order - /// The page of the resulting list - /// The number of results per page - /// Cancellation token - /// Details of an executed order - Task>> GetOrderTradesAsync(long orderId, int? page = null, int? limit = null, CancellationToken ct = default); - - /// - /// Gets a list of trades you executed on a specific symbol. Requires API credentials - /// - /// - /// The symbol to retrieve trades for - /// The page of the resulting list - /// The number of results per page - /// Cancellation token - /// List of trades for a symbol - Task>> GetUserTradesAsync(string symbol, int? page = null, int? limit = null, CancellationToken ct = default); - - /// - /// Cancels an order. Requires API credentials - /// - /// - /// The symbol the order is on - /// The id of the order to cancel - /// Cancellation token - /// Details of the canceled order - Task> CancelOrderAsync(string symbol, long orderId, CancellationToken ct = default); - - /// - /// Cancels all stop orders. Requires API credentials - /// - /// The symbol the orders are on - /// Cancellation token - /// Execution statut - Task CancelAllStopOrdersAsync(string symbol, CancellationToken ct = default); - - /// - /// Cancels all orders. Requires API credentials - /// - /// - /// The symbol the orders are on - /// Cancellation token - /// Execution statut - Task CancelAllOrdersAsync(string symbol, CancellationToken ct = default); + } } diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1.cs new file mode 100644 index 0000000..e527621 --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1.cs @@ -0,0 +1,33 @@ +using CryptoExchange.Net.Interfaces; +using CryptoExchange.Net.Interfaces.CommonClients; +using System; + +namespace CoinEx.Net.Interfaces.Clients.SpotApi +{ + /// + /// Spot API + /// + public interface ICoinExClientSpotApiV1 : IRestApiClient, IDisposable + { + /// + /// Endpoints related to account settings, info or actions + /// + ICoinExClientSpotApiV1Account Account { get; } + + /// + /// Endpoints related to retrieving market and system data + /// + ICoinExClientSpotApiV1ExchangeData ExchangeData { get; } + + /// + /// Endpoints related to orders and trades + /// + ICoinExClientSpotApiV1Trading Trading { get; } + + /// + /// Get the ISpotClient for this client. This is a common interface which allows for some basic operations without knowing any details of the exchange. + /// + /// + public ISpotClient CommonSpotClient { get; } + } +} \ No newline at end of file diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1Account.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1Account.cs new file mode 100644 index 0000000..0a8a594 --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1Account.cs @@ -0,0 +1,77 @@ +using CryptoExchange.Net.Objects; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Objects.Models; + +namespace CoinEx.Net.Interfaces.Clients.SpotApi +{ + /// + /// CoinEx account endpoints. Account endpoints include balance info, withdraw/deposit info and requesting and account settings + /// + public interface ICoinExClientSpotApiV1Account + { + /// + /// Retrieves a list of balances. Requires API credentials + /// + /// + /// Cancellation token + /// List of balances + Task>> GetBalancesAsync(CancellationToken ct = default); + + /// + /// Retrieves a list of deposits. Requires API credentials and withdrawal permission on the API key + /// + /// + /// The asset to get history for + /// The page in the results to retrieve + /// The number of results to return per page + /// Cancellation token + /// + Task>> GetDepositHistoryAsync(string? asset = null, int? page = null, int? limit = null, CancellationToken ct = default); + + /// + /// Get the deposit address of an asset + /// + /// + /// The asset to deposit + /// Name of the network to use + /// Cancellation token + /// + Task> GetDepositAddressAsync(string asset, string? smartContractName = null, CancellationToken ct = default); + + /// + /// Retrieves a list of withdrawals. Requires API credentials and withdrawal permission on the API key + /// + /// + /// The asset to get history for + /// Retrieve a withdrawal with a specific id + /// The page in the results to retrieve + /// The number of results to return per page + /// Cancellation token + /// + Task>> GetWithdrawalHistoryAsync(string? asset = null, long? withdrawId = null, int? page = null, int? limit = null, CancellationToken ct = default); + + /// + /// Withdraw assets from CoinEx to a specific address. Requires API credentials and withdrawal permission on the API key + /// + /// + /// The asset to withdraw + /// Is it a local transfer between users or onchain + /// The address to withdraw to + /// The quantity to withdraw. This is the quantity AFTER fees have been deducted. For fee rates see https://www.coinex.com/fees + /// The network to use for the withdrawal + /// Cancellation token + /// The withdrawal object + Task> WithdrawAsync(string asset, string address, bool localTransfer, decimal quantity, string? network = null, CancellationToken ct = default); + + /// + /// Cancel a specific withdrawal. Requires API credentials and withdrawal permission on the API key + /// + /// + /// The id of the withdrawal to cancel + /// Cancellation token + /// True if successful, false otherwise + Task> CancelWithdrawalAsync(long withdrawId, CancellationToken ct = default); + } +} diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1ExchangeData.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1ExchangeData.cs new file mode 100644 index 0000000..6394977 --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1ExchangeData.cs @@ -0,0 +1,114 @@ +using CoinEx.Net.Enums; +using CryptoExchange.Net.Objects; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Objects.Models; + +namespace CoinEx.Net.Interfaces.Clients.SpotApi +{ + /// + /// CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. + /// + public interface ICoinExClientSpotApiV1ExchangeData + { + /// + /// Gets the exchange rates of currencies + /// + /// + /// Cancellation token + /// + Task>> GetCurrencyRateAsync(CancellationToken ct = default); + + /// + /// Gets the asset configs + /// + /// + /// Optionally only return a certain type of asset, for example BCH + /// Cancellation token + /// + Task>> GetAssetsAsync(string? assetType = null, CancellationToken ct = default); + + /// + /// Gets a list of symbols active on CoinEx + /// + /// + /// Cancellation token + /// List of symbol names + Task>> GetSymbolsAsync(CancellationToken ct = default); + + /// + /// Gets the state of a specific symbol + /// + /// + /// The symbol to retrieve state for + /// Cancellation token + /// The state of the symbol + Task> GetTickerAsync(string symbol, CancellationToken ct = default); + + /// + /// Gets the states of all symbols + /// + /// + /// Cancellation token + /// List of states for all symbols + Task> GetTickersAsync(CancellationToken ct = default); + + /// + /// Gets the order book for a symbol + /// + /// + /// The symbol to retrieve depth data for + /// The depth of merging, based on 8 decimals. 1 mergeDepth will merge the last decimals of all order in the book, 7 will merge the last 7 decimals of all orders together + /// The limit of results returned + /// Cancellation token + /// Order book for a symbol + Task> GetOrderBookAsync(string symbol, int mergeDepth, int? limit = null, CancellationToken ct = default); + + /// + /// Gets the latest trades for a symbol + /// + /// + /// The symbol to retrieve data for + /// The id from which on to return trades + /// Cancellation token + /// List of trades for a symbol + Task>> GetTradeHistoryAsync(string symbol, long? fromId = null, CancellationToken ct = default); + + /// + /// Retrieves kline data for a specific symbol + /// + /// + /// The symbol to retrieve klines for + /// The interval of the candles + /// Limit of the number of results + /// Cancellation token + /// List of klines for a symbol + Task>> GetKlinesAsync(string symbol, KlineInterval interval, int? limit = null, CancellationToken ct = default); + + /// + /// Retrieves market data for the exchange + /// + /// + /// The symbol to retrieve data for + /// Cancellation token + /// List of market data for the exchange + Task>> GetSymbolInfoAsync(string symbol, CancellationToken ct = default); + + /// + /// Retrieves market data for the exchange + /// + /// + /// Cancellation token + /// List of market data for the exchange + Task>> GetSymbolInfoAsync(CancellationToken ct = default); + + /// + /// Retrieve the mining difficulty. Requires API credentials + /// + /// + /// Cancellation token + /// Mining difficulty + Task> GetMiningDifficultyAsync(CancellationToken ct = default); + } +} diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1Trading.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1Trading.cs new file mode 100644 index 0000000..d2a94c5 --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1Trading.cs @@ -0,0 +1,139 @@ +using CoinEx.Net.Enums; +using CryptoExchange.Net.Objects; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Objects.Models; + +namespace CoinEx.Net.Interfaces.Clients.SpotApi +{ + /// + /// CoinEx trading endpoints, placing and mananging orders. + /// + public interface ICoinExClientSpotApiV1Trading + { + /// + /// Places an order. This is a single method for multiple place order endpoints. The called endpoint depends on the provided order type. + /// + /// + /// + /// + /// + /// The symbol to place the order for + /// Order side + /// Order type + /// The quantity of the order + /// The price of a single unit of the order + /// Option for the order + /// The stop-price of a single unit of the order + /// True if the order should be filled immediately up on placing, otherwise it will be canceled + /// Client id which can be used to match the order + /// User defined number + /// Cancellation token + /// Details of the order that was placed + Task> PlaceOrderAsync( + string symbol, + OrderSide side, + OrderType type, + decimal quantity, + + decimal? price = null, + decimal? stopPrice = null, + bool? immediateOrCancel = null, + OrderOption? orderOption = null, + string? clientOrderId = null, + string? sourceId = null, + CancellationToken ct = default); + + /// + /// Retrieves a list of open orders for a symbol. Requires API credentials + /// + /// + /// The symbol to retrieve the open orders for + /// The page of the resulting list + /// The number of results per page + /// Cancellation token + /// List of open orders for a symbol + Task>> GetOpenOrdersAsync(string? symbol = null, int? page = null, int? limit = null, CancellationToken ct = default); + + /// + /// Retrieves a list of open stop orders for a symbol. Requires API credentials + /// + /// + /// The symbol to retrieve the open orders for + /// The page of the resulting list + /// The number of results per page + /// Cancellation token + /// List of open orders for a symbol + Task>> GetOpenStopOrdersAsync(string symbol, int? page = null, int? limit = null, CancellationToken ct = default); + + /// + /// Retrieves a list of executed orders for a symbol in the last 2 days. Requires API credentials + /// + /// + /// The symbol to retrieve the open orders for + /// The page of the resulting list + /// The number of results per page + /// Cancellation token + /// List of executed orders for a symbol + Task>> GetClosedOrdersAsync(string symbol, int? page = null, int? limit = null, CancellationToken ct = default); + + /// + /// Retrieves details of an order. Requires API credentials + /// + /// + /// The id of the order to retrieve + /// The symbol the order is for + /// Cancellation token + /// Details of the order + Task> GetOrderAsync(string symbol, long orderId, CancellationToken ct = default); + + /// + /// Retrieves execution details of a specific order. Requires API credentials + /// + /// + /// The id of the order + /// The page of the resulting list + /// The number of results per page + /// Cancellation token + /// Details of an executed order + Task>> GetOrderTradesAsync(long orderId, int? page = null, int? limit = null, CancellationToken ct = default); + + /// + /// Gets a list of trades you executed on a specific symbol. Requires API credentials + /// + /// + /// The symbol to retrieve trades for + /// The page of the resulting list + /// The number of results per page + /// Cancellation token + /// List of trades for a symbol + Task>> GetUserTradesAsync(string symbol, int? page = null, int? limit = null, CancellationToken ct = default); + + /// + /// Cancels an order. Requires API credentials + /// + /// + /// The symbol the order is on + /// The id of the order to cancel + /// Cancellation token + /// Details of the canceled order + Task> CancelOrderAsync(string symbol, long orderId, CancellationToken ct = default); + + /// + /// Cancels all stop orders. Requires API credentials + /// + /// The symbol the orders are on + /// Cancellation token + /// Execution statut + Task CancelAllStopOrdersAsync(string symbol, CancellationToken ct = default); + + /// + /// Cancels all orders. Requires API credentials + /// + /// + /// The symbol the orders are on + /// Cancellation token + /// Execution statut + Task CancelAllOrdersAsync(string symbol, CancellationToken ct = default); + } +} diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExSocketClientSpotApiV1.cs similarity index 99% rename from CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs rename to CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExSocketClientSpotApiV1.cs index 63c6e18..de179a9 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExSocketClientSpotApiV1.cs @@ -14,7 +14,7 @@ namespace CoinEx.Net.Interfaces.Clients.SpotApi /// /// Spot streams /// - public interface ICoinExSocketClientSpotApi : ISocketApiClient, IDisposable + public interface ICoinExSocketClientSpotApiV1 : ISocketApiClient, IDisposable { /// /// Pings the server diff --git a/CoinEx.Net/Objects/Internal/CoinExApiResult.cs b/CoinEx.Net/Objects/Internal/CoinExApiResult.cs index 26f91da..1b734b5 100644 --- a/CoinEx.Net/Objects/Internal/CoinExApiResult.cs +++ b/CoinEx.Net/Objects/Internal/CoinExApiResult.cs @@ -1,13 +1,18 @@ -namespace CoinEx.Net.Objects.Internal +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Internal { internal class CoinExApiResult { + [JsonPropertyName("message")] public string? Message { get; set; } + [JsonPropertyName("code")] public int Code { get; set; } } internal class CoinExApiResult : CoinExApiResult { + [JsonPropertyName("data")] public T Data { get; set; } = default!; } } diff --git a/CoinEx.Net/Objects/Models/V2/CoinExSymbol.cs b/CoinEx.Net/Objects/Models/V2/CoinExSymbol.cs new file mode 100644 index 0000000..3c16ad8 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExSymbol.cs @@ -0,0 +1,61 @@ +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Symbol info + /// + public record CoinExSymbol + { + /// + /// Symbol name + /// + [JsonPropertyName("market")] + public string Name { get; set; } = string.Empty; + /// + /// Maker fee rate + /// + [JsonPropertyName("maker_fee_rate")] + public decimal MakerFeeRate { get; set; } + /// + /// Taker fee rate + /// + [JsonPropertyName("taker_fee_rate")] + public decimal TakerFeeRate { get; set; } + /// + /// Minimal order quantiy + /// + [JsonPropertyName("min_amount")] + public decimal MinOrderQuantity { get; set; } + /// + /// Base asset + /// + [JsonPropertyName("base_ccy")] + public string BaseAsset { get; set; } = string.Empty; + /// + /// Quote asset + /// + [JsonPropertyName("quote_ccy")] + public string QuoteAsset { get; set; } = string.Empty; + /// + /// Quantity precision + /// + [JsonPropertyName("base_ccy_precision")] + public int QuantityPrecision { get; set; } + /// + /// Price precision + /// + [JsonPropertyName("quote_ccy_precision")] + public int PricePrecision { get; set; } + /// + /// Is Automated Market Maker available + /// + [JsonPropertyName("is_amm_available")] + public bool AutoMarketMakerAvailable { get; set; } + /// + /// Is Margin Trading available + /// + [JsonPropertyName("is_margin_available")] + public bool MarginTradingAvailable { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExTicker.cs b/CoinEx.Net/Objects/Models/V2/CoinExTicker.cs new file mode 100644 index 0000000..acc0aef --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExTicker.cs @@ -0,0 +1,66 @@ +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Ticker (24h price stats) info + /// + public record CoinExTicker + { + /// + /// Symbol name + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Last price + /// + [JsonPropertyName("last")] + public decimal LastPrice { get; set; } + /// + /// Open price + /// + [JsonPropertyName("open")] + public decimal OpenPrice { get; set; } + /// + /// Close price + /// + [JsonPropertyName("close")] + public decimal ClosePrice { get; set; } + /// + /// High price + /// + [JsonPropertyName("high")] + public decimal HighPrice { get; set; } + /// + /// Low price + /// + [JsonPropertyName("low")] + public decimal LowPrice { get; set; } + /// + /// Volume in base asset + /// + [JsonPropertyName("volume")] + public decimal Volume { get; set; } + /// + /// Volume in quote asset + /// + [JsonPropertyName("value")] + public decimal Value { get; set; } + /// + /// Sell volume + /// + [JsonPropertyName("volume_sell")] + public decimal SellVolume { get; set; } + /// + /// Buy volume + /// + [JsonPropertyName("volume_buy")] + public decimal BuyVolume { get; set; } + ///// + ///// Period + ///// + //[JsonPropertyName("period")] + //public KlineInterval Period { get; set; } + } +} diff --git a/CoinEx.Net/SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs b/CoinEx.Net/SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs index cc6bcac..867a716 100644 --- a/CoinEx.Net/SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs +++ b/CoinEx.Net/SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs @@ -64,7 +64,7 @@ public CoinExSpotSymbolOrderBook(string symbol, /// protected override async Task> DoStartAsync(CancellationToken ct) { - var result = await _socketClient.SpotApi.SubscribeToOrderBookUpdatesAsync(Symbol, Levels!.Value, 0, HandleUpdate).ConfigureAwait(false); + var result = await _socketClient.SpotApiV1.SubscribeToOrderBookUpdatesAsync(Symbol, Levels!.Value, 0, HandleUpdate).ConfigureAwait(false); if (!result) return result; From 4f6080826f84906f8e389cdf515f71c85da8434b Mon Sep 17 00:00:00 2001 From: JKorf Date: Fri, 29 Mar 2024 15:52:43 +0100 Subject: [PATCH 02/17] wip --- CoinEx.Net/Clients/CoinExSocketClient.cs | 3 +- .../SpotApi/CoinExRestClientSpotApi.cs | 29 +- .../CoinExRestClientSpotApiExchangeData.cs | 46 +++ .../SpotApi/CoinExSocketClientSpotApi.cs | 121 +++++++ CoinEx.Net/CoinEx.Net.csproj | 4 +- CoinEx.Net/CoinEx.Net.xml | 306 +++++++++++++++++- CoinEx.Net/Enums/KlineInterval.cs | 17 +- CoinEx.Net/Enums/PriceType.cs | 29 ++ .../Interfaces/Clients/ICoinExSocketClient.cs | 1 + .../ICoinExClientSpotApiExchangeData.cs | 43 +++ .../SpotApi/ICoinExSocketClientSpotApi.cs | 22 ++ .../Objects/Models/V2/CoinExIndexPrice.cs | 62 ++++ CoinEx.Net/Objects/Models/V2/CoinExKline.cs | 54 ++++ .../Objects/Models/V2/CoinExOrderBook.cs | 84 +++++ .../Objects/Models/V2/CoinExTickerUpdate.cs | 14 + CoinEx.Net/Objects/Models/V2/CoinExTrade.cs | 40 +++ .../Objects/Sockets/V2/CoinExSocketRequest.cs | 15 + .../Sockets/V2/CoinExSocketResponse.cs | 15 + .../Objects/Sockets/V2/Queries/CoinExQuery.cs | 33 ++ .../V2/Subscriptions/CoinExSubscription.cs | 43 +++ 20 files changed, 963 insertions(+), 18 deletions(-) create mode 100644 CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs create mode 100644 CoinEx.Net/Enums/PriceType.cs create mode 100644 CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExIndexPrice.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExKline.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExOrderBook.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExTickerUpdate.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExTrade.cs create mode 100644 CoinEx.Net/Objects/Sockets/V2/CoinExSocketRequest.cs create mode 100644 CoinEx.Net/Objects/Sockets/V2/CoinExSocketResponse.cs create mode 100644 CoinEx.Net/Objects/Sockets/V2/Queries/CoinExQuery.cs create mode 100644 CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs diff --git a/CoinEx.Net/Clients/CoinExSocketClient.cs b/CoinEx.Net/Clients/CoinExSocketClient.cs index 9690dc1..b602bbe 100644 --- a/CoinEx.Net/Clients/CoinExSocketClient.cs +++ b/CoinEx.Net/Clients/CoinExSocketClient.cs @@ -15,7 +15,7 @@ public class CoinExSocketClient : BaseSocketClient, ICoinExSocketClient #region Api clients /// - //public ICoinExSocketClientSpotApi SpotApi { get; } + public ICoinExSocketClientSpotApi SpotApi { get; } public ICoinExSocketClientSpotApiV1 SpotApiV1 { get; } #endregion @@ -49,6 +49,7 @@ public CoinExSocketClient(Action optionsDelegate, ILoggerFa optionsDelegate(options); Initialize(options); + SpotApi = AddApiClient(new CoinExSocketClientSpotApi(_logger, options)); SpotApiV1 = AddApiClient(new CoinExSocketClientSpotApiV1(_logger, options)); } #endregion diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs index 9747450..a9b2ee7 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs @@ -411,22 +411,25 @@ internal async Task> ExecuteAsync(Uri uri, HttpMethod method //} //#endregion - ///// - //protected override Error ParseErrorResponse(int httpStatusCode, IEnumerable>> responseHeaders, IMessageAccessor accessor) - //{ - // if (!accessor.IsJson) - // return new ServerError(accessor.GetOriginalString()); + /// + protected override ServerError? TryParseError(IMessageAccessor accessor) + { + if (!accessor.IsJson) + return new ServerError(accessor.GetOriginalString()); - // var code = accessor.GetValue(MessagePath.Get().Property("code")); - // var msg = accessor.GetValue(MessagePath.Get().Property("message")); - // if (msg == null) - // return new ServerError(accessor.GetOriginalString()); + var code = accessor.GetValue(MessagePath.Get().Property("code")); + if (code == 0) + return null; + + var msg = accessor.GetValue(MessagePath.Get().Property("message")); + if (msg == null) + return new ServerError(accessor.GetOriginalString()); - // if (code == null) - // return new ServerError(msg); + if (code == null) + return new ServerError(msg); - // return new ServerError(code.Value, msg); - //} + return new ServerError(code.Value, msg); + } /// public override TimeSyncInfo? GetTimeSyncInfo() => null; diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiExchangeData.cs b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiExchangeData.cs index 95bad6f..49b2f50 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiExchangeData.cs +++ b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiExchangeData.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using CoinEx.Net.Objects.Models.V2; using CoinEx.Net.Interfaces.Clients.SpotApi; +using CoinEx.Net.Enums; namespace CoinEx.Net.Clients.SpotApi { @@ -32,5 +33,50 @@ public async Task>> GetTickersAsync(IEnu parameters.AddOptional("market", symbols == null ? null : string.Join(",", symbols)); return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/spot/ticker"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); } + + /// + public async Task> GetOrderBookAsync(string symbol, int limit, string? mergeLevel = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "limit", limit }, + { "interval", mergeLevel ?? "0" } + }; + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/depth"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task>> GetTradeHistoryAsync(string symbol, int? limit = null, long? lastId = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddOptional("limit", limit); + parameters.AddOptional("last_id", lastId); + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/spot/deals"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task>> GetKlinesAsync(string symbol, KlineInterval interval, int? limit = null, PriceType? priceType = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddEnum("period", interval); + parameters.AddOptionalEnum("price_type", priceType); + parameters.AddOptional("limit", limit); + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/spot/kline"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task>> GetIndexPricesAsync(IEnumerable? symbols = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddOptional("market", symbols == null ? null : string.Join(",", symbols)); + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/spot/index"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } } } diff --git a/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs b/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs new file mode 100644 index 0000000..c5ef680 --- /dev/null +++ b/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs @@ -0,0 +1,121 @@ +using CryptoExchange.Net; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Sockets; +using Microsoft.Extensions.Logging; +using CryptoExchange.Net.Authentication; +using System.Threading; +using CoinEx.Net.Objects.Internal; +using CoinEx.Net.Interfaces.Clients.SpotApi; +using CoinEx.Net.Objects.Options; +using CryptoExchange.Net.Objects.Sockets; +using CoinEx.Net.Objects.Sockets.Queries; +using CoinEx.Net.Objects.Sockets; +using CryptoExchange.Net.Interfaces; +using CryptoExchange.Net.Converters.MessageParsing; +using CryptoExchange.Net.Clients; +using CoinEx.Net.Objects.Models.V2; +using CryptoExchange.Net.Converters.SystemTextJson; +using System.Net.WebSockets; +using CoinEx.Net.Objects.Sockets.V2.Subscriptions; + +namespace CoinEx.Net.Clients.SpotApi +{ + /// + public class CoinExSocketClientSpotApi : SocketApiClient, ICoinExSocketClientSpotApi + { + #region fields + /// + public new CoinExSocketOptions ClientOptions => (CoinExSocketOptions)base.ClientOptions; + + private static readonly MessagePath _idPath = MessagePath.Get().Property("id"); + private static readonly MessagePath _methodPath = MessagePath.Get().Property("method"); + private static readonly MessagePath _symbolPath = MessagePath.Get().Property("data").Property("market"); + private static readonly MessagePath _symbolPathDepth = MessagePath.Get().Property("params").Index(2); + #endregion + + #region ctor + /// + /// Create a new instance of CoinExSocketClient with default options + /// + internal CoinExSocketClientSpotApi(ILogger logger, CoinExSocketOptions options) + : base(logger, options.Environment.SocketBaseAddress, options, options.SpotOptions) + { + RegisterPeriodicQuery("Ping", TimeSpan.FromMinutes(1), q => (new CoinExQuery("server.ping", new object[] { })), null); + } + #endregion + + /// + protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials) + => new CoinExAuthenticationProvider(credentials, ClientOptions.NonceProvider ?? new CoinExNonceProvider()); + + #region methods + + /// + protected override IByteMessageAccessor CreateAccessor() => new SystemTextJsonByteMessageAccessor(); + /// + protected override IMessageSerializer CreateSerializer() => new SystemTextJsonMessageSerializer(); + + /// + public override string? GetListenerIdentifier(IMessageAccessor messageAccessor) + { + var id = messageAccessor.GetValue(_idPath); + if (id != null) + return id.ToString(); + + var method = messageAccessor.GetValue(_methodPath); + if (!string.Equals(method, "state.update", StringComparison.Ordinal)) + { + var symbol = messageAccessor.GetValue(_symbolPath); + return method + symbol; + } + + //if (string.Equals(method, "depth.update", StringComparison.Ordinal)) + //{ + // var symbol = messageAccessor.GetValue(_symbolPathDepth); + // return method + symbol; + //} + + return method; + } + + /// + protected override Query? GetAuthenticationRequest() + { + var authProvider = (CoinExAuthenticationProvider)AuthenticationProvider!; + var authParams = authProvider.GetSocketAuthParameters(); + return new CoinExQuery("server.sign", authParams, false); + } + + public override ReadOnlyMemory PreprocessStreamMessage(WebSocketMessageType type, ReadOnlyMemory data) + => data.DecompressGzip(); + + #region public + + /// + public async Task> SubscribeToTickerUpdatesAsync(Action> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "state", new Dictionary + { + { "market_list", new object[] { } } + }, onMessage); + return await SubscribeV2Async(subscription, ct).ConfigureAwait(false); + } + + /// + public async Task> SubscribeToTickerUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "state", new Dictionary + { + { "market_list", symbols } + }, onMessage); + return await SubscribeV2Async(subscription, ct).ConfigureAwait(false); + } + + #endregion + + #endregion + } +} diff --git a/CoinEx.Net/CoinEx.Net.csproj b/CoinEx.Net/CoinEx.Net.csproj index 6d4dff9..36a5f10 100644 --- a/CoinEx.Net/CoinEx.Net.csproj +++ b/CoinEx.Net/CoinEx.Net.csproj @@ -49,10 +49,12 @@ 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 973cad0..a9b325c 100644 --- a/CoinEx.Net/CoinEx.Net.xml +++ b/CoinEx.Net/CoinEx.Net.xml @@ -39,7 +39,7 @@ - + @@ -313,6 +313,9 @@ + + + @@ -334,12 +337,53 @@ + + + + + + + + + + + + + + + + + + + + + Create a new instance of CoinExSocketClient with default options + + + + + + + + + + + + + + + + + + + + CoinEx environments @@ -553,6 +597,26 @@ Stop market order + + + Price type + + + + + Last price + + + + + Mark price + + + + + Index price + + Role of a transaction @@ -670,7 +734,7 @@ Client for accessing the CoinEx websocket API - + Spot streams @@ -1195,11 +1259,59 @@ Cancelation Token + + + Get the orderbook for a symbol + + + Symbol + Amount of rows, 5, 10, 20 or 50 + The merge level, 0.00000000001 up to 1000, 0 for no merging + Cancelation Token + + + + + Get the trade history for a symbol + + + Symbol + Max amount of results + The starting point of the query, 0 means to acquire from the latest record + Cancelation Token + + + + + Get klines/candlesticks + + + Symbol + Kline interval + Max amount of results + Price type, either LastPrice(default) or IndexPrice + Cancelation Token + + + + + Get index prices + + + Filter by symbols + Cancelation Token + + CoinEx trading endpoints, placing and mananging orders. + + + Spot streams + + CoinEx order book factory @@ -2248,6 +2360,166 @@ The quantity of the transaction + + + Index price + + + + + Symbol + + + + + Timestamp + + + + + Price + + + + + Index sources + + + + + Index price source + + + + + Exchange + + + + + Timestamp + + + + + Weight of the source + + + + + Price + + + + + Kline/candlestick info + + + + + Symbol + + + + + Open time + + + + + Open price + + + + + Close price + + + + + High price + + + + + Low price + + + + + Volume + + + + + Value (Quote asset volume) + + + + + Order book info + + + + + Symbol + + + + + Is full order book + + + + + The book data + + + + + Order book data + + + + + Asks list + + + + + Bids list + + + + + Last price + + + + + Update time + + + + + Checksum for validating the order book is correct + + + + + Order book entry + + + + + Price + + + + + Quantity + + Symbol info @@ -2358,6 +2630,36 @@ Buy volume + + + Trade info + + + + + Trade id + + + + + Timestamp + + + + + Trade side + + + + + Price traded at + + + + + Quantity traded + + Options for CoinEx SymbolOrderBook diff --git a/CoinEx.Net/Enums/KlineInterval.cs b/CoinEx.Net/Enums/KlineInterval.cs index 34f6f72..c43b727 100644 --- a/CoinEx.Net/Enums/KlineInterval.cs +++ b/CoinEx.Net/Enums/KlineInterval.cs @@ -1,4 +1,6 @@ -namespace CoinEx.Net.Enums +using CryptoExchange.Net.Attributes; + +namespace CoinEx.Net.Enums { /// /// Interval for klines, int value represent the time in seconds @@ -8,54 +10,67 @@ public enum KlineInterval /// /// 1m /// + [Map("1min")] OneMinute = 60, /// /// 3m /// + [Map("3min")] ThreeMinutes = 60 * 3, /// /// 5m /// + [Map("5min")] FiveMinutes = 60 * 5, /// /// 15m /// + [Map("15min")] FifteenMinutes = 60 * 15, /// /// 30m /// + [Map("30min")] ThirtyMinutes = 60 * 30, /// /// 1h /// + [Map("1hour")] OneHour = 60 * 60, /// /// 2h /// + [Map("2hour")] TwoHours = 60 * 60 * 2, /// /// 4h /// + [Map("4hour")] FourHours = 60 * 60 * 4, /// /// 6h /// + [Map("6hour")] SixHours = 60 * 60 * 6, /// /// 12h /// + [Map("12hour")] TwelveHours = 60 * 60 * 12, /// /// 1d /// + [Map("1day")] OneDay = 60 * 60 * 24, /// /// 3d /// + [Map("3day")] ThreeDays = 60 * 60 * 24 * 3, /// /// 1w /// + [Map("1week")] OneWeek = 60 * 60 * 24 * 7 } } diff --git a/CoinEx.Net/Enums/PriceType.cs b/CoinEx.Net/Enums/PriceType.cs new file mode 100644 index 0000000..ee6adf1 --- /dev/null +++ b/CoinEx.Net/Enums/PriceType.cs @@ -0,0 +1,29 @@ +using CryptoExchange.Net.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Price type + /// + public enum PriceType + { + /// + /// Last price + /// + [Map("latest_price")] + LastPrice, + /// + /// Mark price + /// + [Map("mark_price")] + MarkPrice, + /// + /// Index price + /// + [Map("index_price")] + IndexPrice + } +} diff --git a/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs b/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs index 89ffe6e..d35650e 100644 --- a/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs +++ b/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs @@ -12,6 +12,7 @@ public interface ICoinExSocketClient : ISocketClient /// /// Spot streams /// + public ICoinExSocketClientSpotApi SpotApi { get; } public ICoinExSocketClientSpotApiV1 SpotApiV1 { get; } /// diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiExchangeData.cs b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiExchangeData.cs index d96ced8..3b31a7b 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiExchangeData.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiExchangeData.cs @@ -28,5 +28,48 @@ public interface ICoinExClientSpotApiExchangeData /// Cancelation Token /// Task>> GetTickersAsync(IEnumerable? symbols = null, CancellationToken ct = default); + + /// + /// Get the orderbook for a symbol + /// + /// + /// Symbol + /// Amount of rows, 5, 10, 20 or 50 + /// The merge level, 0.00000000001 up to 1000, 0 for no merging + /// Cancelation Token + /// + Task> GetOrderBookAsync(string symbol, int limit, string? mergeLevel = null, CancellationToken ct = default); + + /// + /// Get the trade history for a symbol + /// + /// + /// Symbol + /// Max amount of results + /// The starting point of the query, 0 means to acquire from the latest record + /// Cancelation Token + /// + Task>> GetTradeHistoryAsync(string symbol, int? limit = null, long? lastId = null, CancellationToken ct = default); + + /// + /// Get klines/candlesticks + /// + /// + /// Symbol + /// Kline interval + /// Max amount of results + /// Price type, either LastPrice(default) or IndexPrice + /// Cancelation Token + /// + Task>> GetKlinesAsync(string symbol, KlineInterval interval, int? limit = null, PriceType? priceType = null, CancellationToken ct = default); + + /// + /// Get index prices + /// + /// + /// Filter by symbols + /// Cancelation Token + /// + Task>> GetIndexPricesAsync(IEnumerable? symbols = null, CancellationToken ct = default); } } diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs new file mode 100644 index 0000000..4f79200 --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Enums; +using CoinEx.Net.Objects.Models; +using CoinEx.Net.Objects.Models.Socket; +using CoinEx.Net.Objects.Models.V2; +using CryptoExchange.Net.Interfaces; +using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Objects.Sockets; + +namespace CoinEx.Net.Interfaces.Clients.SpotApi +{ + /// + /// Spot streams + /// + public interface ICoinExSocketClientSpotApi : ISocketApiClient, IDisposable + { + Task> SubscribeToTickerUpdatesAsync(Action> onMessage, CancellationToken ct = default); + } +} \ No newline at end of file diff --git a/CoinEx.Net/Objects/Models/V2/CoinExIndexPrice.cs b/CoinEx.Net/Objects/Models/V2/CoinExIndexPrice.cs new file mode 100644 index 0000000..a59eb83 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExIndexPrice.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Index price + /// + public record CoinExIndexPrice + { + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Timestamp + /// + [JsonPropertyName("created_at")] + public DateTime Timestamp { get; set; } + /// + /// Price + /// + [JsonPropertyName("price")] + public decimal Price { get; set; } + + /// + /// Index sources + /// + [JsonPropertyName("sources")] + public IEnumerable Sources { get; set; } = Array.Empty(); + } + + /// + /// Index price source + /// + public record CoinExIndexPriceSource + { + /// + /// Exchange + /// + [JsonPropertyName("exchange")] + public string Exchange { get; set; } = string.Empty; + /// + /// Timestamp + /// + [JsonPropertyName("created_at")] + public DateTime Timestamp { get; set; } + /// + /// Weight of the source + /// + [JsonPropertyName("index_weight")] + public decimal Weight { get; set; } + /// + /// Price + /// + [JsonPropertyName("index_price")] + public decimal Price { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExKline.cs b/CoinEx.Net/Objects/Models/V2/CoinExKline.cs new file mode 100644 index 0000000..45a63af --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExKline.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Kline/candlestick info + /// + public record CoinExKline + { + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Open time + /// + [JsonPropertyName("created_at")] + public DateTime OpenTime { get; set; } + /// + /// Open price + /// + [JsonPropertyName("open")] + public decimal OpenPrice { get; set; } + /// + /// Close price + /// + [JsonPropertyName("close")] + public decimal ClosePrice { get; set; } + /// + /// High price + /// + [JsonPropertyName("high")] + public decimal HighPrice { get; set; } + /// + /// Low price + /// + [JsonPropertyName("low")] + public decimal LowPrice { get; set; } + /// + /// Volume + /// + [JsonPropertyName("volume")] + public decimal Volume { get; set; } + /// + /// Value (Quote asset volume) + /// + [JsonPropertyName("value")] + public decimal Value { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExOrderBook.cs b/CoinEx.Net/Objects/Models/V2/CoinExOrderBook.cs new file mode 100644 index 0000000..db1b7d0 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExOrderBook.cs @@ -0,0 +1,84 @@ +using CryptoExchange.Net.Converters; +using CryptoExchange.Net.Interfaces; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Order book info + /// + public record CoinExOrderBook + { + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + + /// + /// Is full order book + /// + [JsonPropertyName("is_full")] + public bool IsFull { get; set; } + + /// + /// The book data + /// + [JsonPropertyName("depth")] + public CoinExOrderBookData Data { get; set; } = null!; + } + + /// + /// Order book data + /// + public record CoinExOrderBookData + { + /// + /// Asks list + /// + [JsonPropertyName("asks")] + public IEnumerable Asks { get; set; } = Array.Empty(); + /// + /// Bids list + /// + [JsonPropertyName("bids")] + public IEnumerable Bids { get; set; } = Array.Empty(); + + /// + /// Last price + /// + [JsonPropertyName("last")] + public decimal LastPrice { get; set; } + /// + /// Update time + /// + [JsonPropertyName("updated_at")] + public DateTime UpdateTime { get; set; } + /// + /// Checksum for validating the order book is correct + /// + [JsonPropertyName("checksum")] + public long Checksum { get; set; } + } + + /// + /// Order book entry + /// + [JsonConverter(typeof(CryptoExchange.Net.Converters.SystemTextJson.ArrayConverter))] + public record CoinExOrderBookEntry : ISymbolOrderBookEntry + { + /// + /// Price + /// + [ArrayProperty(0)] + public decimal Price { get; set; } + /// + /// Quantity + /// + [ArrayProperty(1)] + public decimal Quantity { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExTickerUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExTickerUpdate.cs new file mode 100644 index 0000000..e5a17f3 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExTickerUpdate.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + public record CoinExTickerUpdateWrapper + { + [JsonPropertyName("state_list")] + public IEnumerable Tickers { get; set; } = Array.Empty(); + + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExTrade.cs b/CoinEx.Net/Objects/Models/V2/CoinExTrade.cs new file mode 100644 index 0000000..ff4d218 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExTrade.cs @@ -0,0 +1,40 @@ +using CoinEx.Net.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Trade info + /// + public record CoinExTrade + { + /// + /// Trade id + /// + [JsonPropertyName("deal_id")] + public long Id { get; set; } + /// + /// Timestamp + /// + [JsonPropertyName("created_at")] + public DateTime Timestamp { get; set; } + /// + /// Trade side + /// + [JsonPropertyName("side")] + public OrderSide Side { get; set; } + /// + /// Price traded at + /// + [JsonPropertyName("price")] + public decimal Price { get; set; } + /// + /// Quantity traded + /// + [JsonPropertyName("amount")] + public decimal Quantity { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Sockets/V2/CoinExSocketRequest.cs b/CoinEx.Net/Objects/Sockets/V2/CoinExSocketRequest.cs new file mode 100644 index 0000000..c75d77a --- /dev/null +++ b/CoinEx.Net/Objects/Sockets/V2/CoinExSocketRequest.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Sockets.V2 +{ + internal class CoinExSocketRequest + { + [JsonPropertyName("method")] + public string Method { get; set; } = string.Empty; + [JsonPropertyName("params")] + public Dictionary Parameters { get; set; } = new Dictionary(); + [JsonPropertyName("id")] + public int Id { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Sockets/V2/CoinExSocketResponse.cs b/CoinEx.Net/Objects/Sockets/V2/CoinExSocketResponse.cs new file mode 100644 index 0000000..96aa906 --- /dev/null +++ b/CoinEx.Net/Objects/Sockets/V2/CoinExSocketResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Sockets.V2 +{ + internal class CoinExSocketResponse + { + [JsonPropertyName("id")] + public int Id { get; set; } + [JsonPropertyName("code")] + public int Code { get; set; } + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; + } +} diff --git a/CoinEx.Net/Objects/Sockets/V2/Queries/CoinExQuery.cs b/CoinEx.Net/Objects/Sockets/V2/Queries/CoinExQuery.cs new file mode 100644 index 0000000..7f4c3e6 --- /dev/null +++ b/CoinEx.Net/Objects/Sockets/V2/Queries/CoinExQuery.cs @@ -0,0 +1,33 @@ +using CoinEx.Net.Objects.Sockets.V2; +using CryptoExchange.Net; +using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Sockets; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace CoinEx.Net.Objects.Sockets.V2.Queries +{ + internal class CoinExQuery : Query + { + public override HashSet ListenerIdentifiers { get; set; } + + public CoinExQuery(string method, Dictionary parameters, bool authenticated = false, int weight = 1) : base(new CoinExSocketRequest + { + Id = ExchangeHelpers.NextId(), + Method = method, + Parameters = parameters + }, authenticated, weight) + { + ListenerIdentifiers = new HashSet() { ((CoinExSocketRequest)Request).Id.ToString() }; + } + + public override CallResult HandleMessage(SocketConnection connection, DataEvent message) + { + if (message.Data.Code != 0) + return new CallResult(new ServerError(message.Data.Code, message.Data.Message)); + + return new CallResult(message.Data, message.OriginalData, null); + } + } +} diff --git a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs new file mode 100644 index 0000000..6a0c786 --- /dev/null +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs @@ -0,0 +1,43 @@ +using CoinEx.Net.Objects.Sockets.V2.Queries; +using CryptoExchange.Net.Interfaces; +using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Sockets; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Objects.Sockets.V2.Subscriptions +{ + internal class CoinExSubscription : Subscription + { + private string _topic; + private Dictionary _parameters; + private Action> _handler; + + public override HashSet ListenerIdentifiers { get; set; } + public CoinExSubscription(ILogger logger, string topic, Dictionary parameters, Action> handler) : base(logger, false) + { + _topic = topic; + _parameters = parameters; + _handler = handler; + ListenerIdentifiers = new HashSet { _topic + ".update" }; + } + + public override CallResult DoHandleMessage(SocketConnection connection, DataEvent message) + { + var data = (T)message.Data; + _handler.Invoke(message.As(data)); + return new CallResult(null); + } + + public override Type? GetMessageType(IMessageAccessor message) => typeof(T); + + public override Query? GetSubQuery(SocketConnection connection) + => new CoinExQuery(_topic + ".subscribe", _parameters, false, 1); + + public override Query? GetUnsubQuery() + => new CoinExQuery(_topic + ".unsubscribe", _parameters, false, 1); + } +} From 1509e585a22636d109904547a848a15ea348829d Mon Sep 17 00:00:00 2001 From: JKorf Date: Fri, 29 Mar 2024 15:55:57 +0100 Subject: [PATCH 03/17] Update CoinExSocketClientSpotApi.cs --- CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs b/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs index c5ef680..d220590 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs @@ -101,7 +101,7 @@ public async Task> SubscribeToTickerUpdatesAsync( { { "market_list", new object[] { } } }, onMessage); - return await SubscribeV2Async(subscription, ct).ConfigureAwait(false); + return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); } /// @@ -111,7 +111,7 @@ public async Task> SubscribeToTickerUpdatesAsync( { { "market_list", symbols } }, onMessage); - return await SubscribeV2Async(subscription, ct).ConfigureAwait(false); + return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); } #endregion From c3f59046f6d0d0a019ac97452438ee38d285c5f9 Mon Sep 17 00:00:00 2001 From: JKorf Date: Mon, 1 Apr 2024 21:07:41 +0200 Subject: [PATCH 04/17] Update CoinExClientTests.cs --- CoinEx.Net.UnitTests/CoinExClientTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CoinEx.Net.UnitTests/CoinExClientTests.cs b/CoinEx.Net.UnitTests/CoinExClientTests.cs index 124b906..6e45c8d 100644 --- a/CoinEx.Net.UnitTests/CoinExClientTests.cs +++ b/CoinEx.Net.UnitTests/CoinExClientTests.cs @@ -41,7 +41,7 @@ public async Task ReceivingError_Should_ReturnErrorAndNotSuccess() TestHelpers.SetResponse((CoinExRestClient)client, JsonConvert.SerializeObject(resultObj)); // act - var result = await client.SpotApi.ExchangeData.GetAssetsAsync(); + var result = await client.SpotApiV1.ExchangeData.GetAssetsAsync(); // assert ClassicAssert.IsFalse(result.Success); @@ -58,7 +58,7 @@ public async Task ReceivingHttpErrorWithNoJson_Should_ReturnErrorAndNotSuccess() TestHelpers.SetResponse((CoinExRestClient)client, "", System.Net.HttpStatusCode.BadRequest); // act - var result = await client.SpotApi.ExchangeData.GetAssetsAsync(); + var result = await client.SpotApiV1.ExchangeData.GetAssetsAsync(); // assert ClassicAssert.IsFalse(result.Success); From bce99680ce71b051df87a58d5f05c65cef356d78 Mon Sep 17 00:00:00 2001 From: JKorf Date: Tue, 2 Apr 2024 17:04:17 +0200 Subject: [PATCH 05/17] wip --- .../SpotApi/CoinExSocketClientSpotApi.cs | 42 ++++++++++---- CoinEx.Net/CoinEx.Net.xml | 57 ++++++++++++++++++- .../SpotApi/ICoinExSocketClientSpotApi.cs | 43 +++++++++++++- .../Objects/Options/CoinExSocketOptions.cs | 3 +- .../Objects/Sockets/V2/CoinExSocketUpdate.cs | 15 +++++ .../V2/Subscriptions/CoinExSubscription.cs | 17 ++++-- .../Subscriptions/CoinExTickerSubscription.cs | 49 ++++++++++++++++ 7 files changed, 208 insertions(+), 18 deletions(-) create mode 100644 CoinEx.Net/Objects/Sockets/V2/CoinExSocketUpdate.cs create mode 100644 CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTickerSubscription.cs diff --git a/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs b/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs index d220590..f914624 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs @@ -11,8 +11,6 @@ using CoinEx.Net.Interfaces.Clients.SpotApi; using CoinEx.Net.Objects.Options; using CryptoExchange.Net.Objects.Sockets; -using CoinEx.Net.Objects.Sockets.Queries; -using CoinEx.Net.Objects.Sockets; using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Converters.MessageParsing; using CryptoExchange.Net.Clients; @@ -20,6 +18,8 @@ using CryptoExchange.Net.Converters.SystemTextJson; using System.Net.WebSockets; using CoinEx.Net.Objects.Sockets.V2.Subscriptions; +using CoinEx.Net.Objects.Sockets.V2.Queries; +using System.Linq; namespace CoinEx.Net.Clients.SpotApi { @@ -43,7 +43,7 @@ public class CoinExSocketClientSpotApi : SocketApiClient, ICoinExSocketClientSpo internal CoinExSocketClientSpotApi(ILogger logger, CoinExSocketOptions options) : base(logger, options.Environment.SocketBaseAddress, options, options.SpotOptions) { - RegisterPeriodicQuery("Ping", TimeSpan.FromMinutes(1), q => (new CoinExQuery("server.ping", new object[] { })), null); + RegisterPeriodicQuery("Ping", TimeSpan.FromMinutes(1), q => (new CoinExQuery("server.ping", new Dictionary())), null); } #endregion @@ -84,20 +84,22 @@ protected override AuthenticationProvider CreateAuthenticationProvider(ApiCreden /// protected override Query? GetAuthenticationRequest() { - var authProvider = (CoinExAuthenticationProvider)AuthenticationProvider!; - var authParams = authProvider.GetSocketAuthParameters(); - return new CoinExQuery("server.sign", authParams, false); + //var authProvider = (CoinExAuthenticationProvider)AuthenticationProvider!; + //var authParams = authProvider.GetSocketAuthParameters(); + //return new CoinExQuery("server.sign", authParams, false); + return null; } + /// public override ReadOnlyMemory PreprocessStreamMessage(WebSocketMessageType type, ReadOnlyMemory data) => data.DecompressGzip(); #region public /// - public async Task> SubscribeToTickerUpdatesAsync(Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToTickerUpdatesAsync(Action>> onMessage, CancellationToken ct = default) { - var subscription = new CoinExSubscription(_logger, "state", new Dictionary + var subscription = new CoinExTickerSubscription(_logger, null, new Dictionary { { "market_list", new object[] { } } }, onMessage); @@ -105,15 +107,35 @@ public async Task> SubscribeToTickerUpdatesAsync( } /// - public async Task> SubscribeToTickerUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToTickerUpdatesAsync(IEnumerable symbols, Action>> onMessage, CancellationToken ct = default) { - var subscription = new CoinExSubscription(_logger, "state", new Dictionary + var subscription = new CoinExTickerSubscription(_logger, symbols, new Dictionary { { "market_list", symbols } }, onMessage); return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); } + /// + public async Task> SubscribeToOrderBookUpdatesAsync(string symbol, int depth, string? mergeLevel, bool fullBookUpdates, Action> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "depth", new[] { symbol }, new Dictionary + { + { "market_list", new object[] { new object[] { symbol, depth, mergeLevel ?? "0", fullBookUpdates } } } + }, onMessage); + return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); + } + + /// + public async Task> SubscribeToOrderBookUpdatesAsync(IEnumerable symbols, int depth, string? mergeLevel, bool fullBookUpdates, Action> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "depth", symbols, new Dictionary + { + { "market_list", symbols.Select(x => new object[] { x, depth, mergeLevel ?? "0", fullBookUpdates }).ToList() } + }, onMessage); + return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); + } + #endregion #endregion diff --git a/CoinEx.Net/CoinEx.Net.xml b/CoinEx.Net/CoinEx.Net.xml index a9b325c..557ff0d 100644 --- a/CoinEx.Net/CoinEx.Net.xml +++ b/CoinEx.Net/CoinEx.Net.xml @@ -381,7 +381,19 @@ - + + + + + + + + + + + + + @@ -1312,6 +1324,49 @@ Spot streams + + + Subscribe to symbol ticker updates for the specified symbols. Note that only one ticker subscription can be active at the same time; new ticker subscription will override the old subscriptions. + + + The symbols to subscribe + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to symbol ticker updates for all symbols. Note that only one ticker subscription can be active at the same time; new ticker subscription will override the old subscriptions. + + + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + + + + + + + + + + + + + + + + + + + + + + CoinEx order book factory diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs index 4f79200..747c6b3 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs @@ -17,6 +17,47 @@ namespace CoinEx.Net.Interfaces.Clients.SpotApi /// public interface ICoinExSocketClientSpotApi : ISocketApiClient, IDisposable { - Task> SubscribeToTickerUpdatesAsync(Action> onMessage, CancellationToken ct = default); + /// + /// Subscribe to symbol ticker updates for the specified symbols. Note that only one ticker subscription can be active at the same time; new ticker subscription will override the old subscriptions. + /// + /// + /// The symbols to subscribe + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToTickerUpdatesAsync(IEnumerable symbols, Action>> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to symbol ticker updates for all symbols. Note that only one ticker subscription can be active at the same time; new ticker subscription will override the old subscriptions. + /// + /// + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToTickerUpdatesAsync(Action>> onMessage, CancellationToken ct = default); + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + Task> SubscribeToOrderBookUpdatesAsync(string symbol, int depth, string? mergeLevel, bool fullBookUpdates, Action> onMessage, CancellationToken ct = default); + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + Task> SubscribeToOrderBookUpdatesAsync(IEnumerable symbols, int depth, string? mergeLevel, bool fullBookUpdates, Action> onMessage, CancellationToken ct = default); } } \ No newline at end of file diff --git a/CoinEx.Net/Objects/Options/CoinExSocketOptions.cs b/CoinEx.Net/Objects/Options/CoinExSocketOptions.cs index 5ddc197..cb46ec1 100644 --- a/CoinEx.Net/Objects/Options/CoinExSocketOptions.cs +++ b/CoinEx.Net/Objects/Options/CoinExSocketOptions.cs @@ -13,7 +13,8 @@ public class CoinExSocketOptions : SocketExchangeOptions /// public static CoinExSocketOptions Default { get; set; } = new CoinExSocketOptions { - Environment = CoinExEnvironment.Live + Environment = CoinExEnvironment.Live, + SocketSubscriptionsCombineTarget = 10 }; /// diff --git a/CoinEx.Net/Objects/Sockets/V2/CoinExSocketUpdate.cs b/CoinEx.Net/Objects/Sockets/V2/CoinExSocketUpdate.cs new file mode 100644 index 0000000..409e06e --- /dev/null +++ b/CoinEx.Net/Objects/Sockets/V2/CoinExSocketUpdate.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Sockets.V2 +{ + internal class CoinExSocketUpdate + { + [JsonPropertyName("method")] + public string Method { get; set; } + [JsonPropertyName("data")] + public T Data { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs index 6a0c786..d7c9aeb 100644 --- a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs @@ -1,4 +1,5 @@ using CoinEx.Net.Objects.Sockets.V2.Queries; +using CryptoExchange.Net.Converters.MessageParsing; using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects.Sockets; @@ -6,6 +7,7 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; +using System.Linq; using System.Text; namespace CoinEx.Net.Objects.Sockets.V2.Subscriptions @@ -13,26 +15,31 @@ namespace CoinEx.Net.Objects.Sockets.V2.Subscriptions internal class CoinExSubscription : Subscription { private string _topic; + private IEnumerable? _symbols; private Dictionary _parameters; private Action> _handler; public override HashSet ListenerIdentifiers { get; set; } - public CoinExSubscription(ILogger logger, string topic, Dictionary parameters, Action> handler) : base(logger, false) + public CoinExSubscription(ILogger logger, string topic, IEnumerable? symbols, Dictionary parameters, Action> handler) : base(logger, false) { _topic = topic; + _symbols = symbols; _parameters = parameters; _handler = handler; - ListenerIdentifiers = new HashSet { _topic + ".update" }; + if (symbols == null) + ListenerIdentifiers = new HashSet { _topic + ".update" }; + else + ListenerIdentifiers = new HashSet(_symbols.Select(x => _topic + ".update" + x)); } public override CallResult DoHandleMessage(SocketConnection connection, DataEvent message) { - var data = (T)message.Data; - _handler.Invoke(message.As(data)); + var data = (CoinExSocketUpdate)message.Data; + _handler.Invoke(message.As(data.Data)); return new CallResult(null); } - public override Type? GetMessageType(IMessageAccessor message) => typeof(T); + public override Type? GetMessageType(IMessageAccessor message) => typeof(CoinExSocketUpdate); public override Query? GetSubQuery(SocketConnection connection) => new CoinExQuery(_topic + ".subscribe", _parameters, false, 1); diff --git a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTickerSubscription.cs b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTickerSubscription.cs new file mode 100644 index 0000000..491d87b --- /dev/null +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTickerSubscription.cs @@ -0,0 +1,49 @@ +using CoinEx.Net.Objects.Models.V2; +using CoinEx.Net.Objects.Sockets.V2.Queries; +using CryptoExchange.Net.Interfaces; +using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Sockets; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace CoinEx.Net.Objects.Sockets.V2.Subscriptions +{ + internal class CoinExTickerSubscription : Subscription + { + private IEnumerable? _symbols; + private Dictionary _parameters; + private Action>> _handler; + + public override HashSet ListenerIdentifiers { get; set; } + public CoinExTickerSubscription(ILogger logger, IEnumerable? symbols, Dictionary parameters, Action>> handler) : base(logger, false) + { + _symbols = symbols; + _parameters = parameters; + _handler = handler; + ListenerIdentifiers = new HashSet { "state.update" }; + } + + public override CallResult DoHandleMessage(SocketConnection connection, DataEvent message) + { + var data = (CoinExSocketUpdate)message.Data; + var relevant = data.Data.Tickers.Where(d => _symbols == null || _symbols.Contains(d.Symbol)).ToList(); + if (!relevant.Any()) + return new CallResult(null); + + _handler.Invoke(message.As>(relevant, null, SocketUpdateType.Update)); + return new CallResult(null); + } + + public override Type? GetMessageType(IMessageAccessor message) => typeof(CoinExSocketUpdate); + + public override Query? GetSubQuery(SocketConnection connection) + => new CoinExQuery("state.subscribe", _parameters, false, 1); + + public override Query? GetUnsubQuery() + => new CoinExQuery("state.unsubscribe", _parameters, false, 1); + } +} From 11c9c833fa611783af016c8f08de3166412b0bf7 Mon Sep 17 00:00:00 2001 From: JKorf Date: Tue, 2 Apr 2024 21:36:56 +0200 Subject: [PATCH 06/17] wip --- .../SpotApi/CoinExRestClientSpotApi.cs | 12 ++ .../SpotApi/CoinExRestClientSpotApiTrading.cs | 89 ++++++++++-- .../SpotApi/CoinExSocketClientSpotApi.cs | 62 ++++++-- CoinEx.Net/CoinEx.Net.xml | 133 ++++++++++++++++++ CoinEx.Net/Enums/AccountType.cs | 29 ++++ CoinEx.Net/Enums/OrderSide.cs | 6 +- CoinEx.Net/Enums/OrderStatusV2.cs | 39 +++++ CoinEx.Net/Enums/OrderTypeV2.cs | 39 +++++ .../SpotApi/ICoinExClientSpotApiTrading.cs | 83 ++++++++++- .../SpotApi/ICoinExSocketClientSpotApi.cs | 105 +++++++++++--- .../Objects/Internal/CoinExApiResult.cs | 14 ++ .../Models/V2/CoinExBookPriceUpdate.cs | 45 ++++++ .../Models/V2/CoinExIndexPriceUpdate.cs | 22 +++ CoinEx.Net/Objects/Models/V2/CoinExOrder.cs | 123 ++++++++++++++++ CoinEx.Net/Objects/Models/V2/CoinExStopId.cs | 19 +++ CoinEx.Net/Objects/Models/V2/CoinExTrade.cs | 8 ++ .../Subscriptions/CoinExTradesSubscription.cs | 49 +++++++ 17 files changed, 836 insertions(+), 41 deletions(-) create mode 100644 CoinEx.Net/Enums/AccountType.cs create mode 100644 CoinEx.Net/Enums/OrderStatusV2.cs create mode 100644 CoinEx.Net/Enums/OrderTypeV2.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExBookPriceUpdate.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExIndexPriceUpdate.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExOrder.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExStopId.cs create mode 100644 CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs index a9b2ee7..e287fd5 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs @@ -92,6 +92,18 @@ internal async Task> ExecuteAsync(Uri uri, HttpMethod method return result.As(result.Data.Data); } + internal async Task> ExecutePaginatedAsync(Uri uri, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false) where T : class + { + var result = await SendRequestAsync>(uri, method, ct, parameters, signed).ConfigureAwait(false); + if (!result) + return result.As(default); + + if (result.Data.Code != 0) + return result.AsError(new ServerError(result.Data.Code, result.Data.Message!)); + + return result.As(result.Data.Data); + } + internal Uri GetUri(string path) => new Uri(BaseAddress.AppendPath(path)); #endregion diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs index 90fe61d..bf1a034 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs +++ b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs @@ -1,15 +1,11 @@ -using CoinEx.Net.Converters; -using CoinEx.Net.Enums; -using CryptoExchange.Net; +using CoinEx.Net.Enums; using CryptoExchange.Net.Objects; -using Newtonsoft.Json; using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using CoinEx.Net.Objects.Models; using CoinEx.Net.Interfaces.Clients.SpotApi; -using CoinEx.Net.ExtensionMethods; +using CoinEx.Net.Objects.Models.V2; namespace CoinEx.Net.Clients.SpotApi { @@ -23,11 +19,88 @@ internal CoinExRestClientSpotApiTrading(CoinExRestClientSpotApi baseClient) _baseClient = baseClient; } + /// + public async Task> PlaceOrderAsync( + string symbol, + AccountType accountType, + OrderSide side, + OrderTypeV2 type, + decimal quantity, + decimal? price = null, + string? quantityAsset = null, + string? clientOrderId = null, + bool? hide = null, + CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddEnum("market_type", accountType); + parameters.AddEnum("side", side); + parameters.AddEnum("type", type); + parameters.AddString("amount", quantity); + parameters.AddOptionalString("price", price); + parameters.AddOptional("ccy", quantityAsset); + parameters.AddOptional("client_id", clientOrderId); + parameters.AddOptional("is_hide", hide); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("spot/order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> PlaceStopOrderAsync( + string symbol, + AccountType accountType, + OrderSide side, + OrderTypeV2 type, + decimal quantity, + decimal triggerPrice, + decimal? price = null, + string? quantityAsset = null, + string? clientOrderId = null, + bool? hide = null, + CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddEnum("market_type", accountType); + parameters.AddEnum("side", side); + parameters.AddEnum("type", type); + parameters.AddString("amount", quantity); + parameters.AddString("trigger_price", triggerPrice); + parameters.AddOptionalString("price", price); + parameters.AddOptional("ccy", quantityAsset); + parameters.AddOptional("client_id", clientOrderId); + parameters.AddOptional("is_hide", hide); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("spot/stop-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> GetOrderAsync(string symbol, string orderId, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "order_id", orderId } + }; + return await _baseClient.ExecuteAsync(_baseClient.GetUri("spot/order-status"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } /// - public async Task>> GetSymbolsAsync(CancellationToken ct = default) + public async Task>> GetOpenOrdersAsync(string symbol, AccountType accountType, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default) { - return await _baseClient.ExecuteAsync>(_baseClient.GetUri("spot/market"), HttpMethod.Get, ct).ConfigureAwait(false); + var parameters = new ParameterCollection() + { + { "market", symbol }, + }; + parameters.AddEnum("market_type", accountType); + parameters.AddOptionalEnum("side", side); + parameters.AddOptional("client_id", clientOrderId); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("spot/pending-order"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); } } } diff --git a/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs b/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs index f914624..5cb5036 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs @@ -66,18 +66,15 @@ protected override AuthenticationProvider CreateAuthenticationProvider(ApiCreden return id.ToString(); var method = messageAccessor.GetValue(_methodPath); + if (string.Equals(method, "deals.update", StringComparison.Ordinal)) + return method; + if (!string.Equals(method, "state.update", StringComparison.Ordinal)) { var symbol = messageAccessor.GetValue(_symbolPath); return method + symbol; } - //if (string.Equals(method, "depth.update", StringComparison.Ordinal)) - //{ - // var symbol = messageAccessor.GetValue(_symbolPathDepth); - // return method + symbol; - //} - return method; } @@ -117,25 +114,64 @@ public async Task> SubscribeToTickerUpdatesAsync( } /// - public async Task> SubscribeToOrderBookUpdatesAsync(string symbol, int depth, string? mergeLevel, bool fullBookUpdates, Action> onMessage, CancellationToken ct = default) + public Task> SubscribeToOrderBookUpdatesAsync(string symbol, int depth, string? mergeLevel, bool fullBookUpdates, Action> onMessage, CancellationToken ct = default) + => SubscribeToOrderBookUpdatesAsync(new [] { symbol }, depth, mergeLevel, fullBookUpdates, onMessage, ct); + + /// + public async Task> SubscribeToOrderBookUpdatesAsync(IEnumerable symbols, int depth, string? mergeLevel, bool fullBookUpdates, Action> onMessage, CancellationToken ct = default) { - var subscription = new CoinExSubscription(_logger, "depth", new[] { symbol }, new Dictionary + var subscription = new CoinExSubscription(_logger, "depth", symbols, new Dictionary { - { "market_list", new object[] { new object[] { symbol, depth, mergeLevel ?? "0", fullBookUpdates } } } - }, onMessage); + { "market_list", symbols.Select(x => new object[] { x, depth, mergeLevel ?? "0", fullBookUpdates }).ToList() } + }, x => onMessage(x.As(x.Data, x.Data.Symbol))); return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); } /// - public async Task> SubscribeToOrderBookUpdatesAsync(IEnumerable symbols, int depth, string? mergeLevel, bool fullBookUpdates, Action> onMessage, CancellationToken ct = default) + public Task> SubscribeToTradeUpdatesAsync(string symbol, Action>> onMessage, CancellationToken ct = default) + => SubscribeToTradeUpdatesAsync(new[] { symbol }, onMessage, ct); + + /// + public Task> SubscribeToTradeUpdatesAsync(Action>> onMessage, CancellationToken ct = default) + => SubscribeToTradeUpdatesAsync(new string[] { }, onMessage, ct); + + /// + public async Task> SubscribeToTradeUpdatesAsync(IEnumerable symbols, Action>> onMessage, CancellationToken ct = default) { - var subscription = new CoinExSubscription(_logger, "depth", symbols, new Dictionary + var subscription = new CoinExTradesSubscription(_logger, symbols, new Dictionary { - { "market_list", symbols.Select(x => new object[] { x, depth, mergeLevel ?? "0", fullBookUpdates }).ToList() } + { "market_list", symbols } }, onMessage); return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); } + /// + public Task> SubscribeToIndexPriceUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default) + => SubscribeToIndexPriceUpdatesAsync(new[] { symbol }, onMessage, ct); + + /// + public async Task> SubscribeToIndexPriceUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "index", symbols, new Dictionary + { + { "market_list", symbols } + }, x => onMessage(x.As(x.Data, x.Data.Symbol))); + return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); + } + + /// + public Task> SubscribeToBookPriceUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default) + => SubscribeToBookPriceUpdatesAsync(new[] { symbol }, onMessage, ct); + + /// + public async Task> SubscribeToBookPriceUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "bbo", symbols, new Dictionary + { + { "market_list", symbols } + }, x => onMessage(x.As(x.Data, x.Data.Symbol))); + return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); + } #endregion #endregion diff --git a/CoinEx.Net/CoinEx.Net.xml b/CoinEx.Net/CoinEx.Net.xml index 557ff0d..d2cfd6e 100644 --- a/CoinEx.Net/CoinEx.Net.xml +++ b/CoinEx.Net/CoinEx.Net.xml @@ -396,6 +396,27 @@ + + + + + + + + + + + + + + + + + + + + + CoinEx environments @@ -1367,6 +1388,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CoinEx order book factory @@ -2415,6 +2498,41 @@ The quantity of the transaction + + + Best book prices update + + + + + Symbol + + + + + Update time + + + + + Current best bid price + + + + + Current best bid quantity + + + + + Current best ask price + + + + + Current best ask quantity + + Index price @@ -2465,6 +2583,21 @@ Price + + + Index price update + + + + + Symbol + + + + + Index price + + Kline/candlestick info diff --git a/CoinEx.Net/Enums/AccountType.cs b/CoinEx.Net/Enums/AccountType.cs new file mode 100644 index 0000000..22751fd --- /dev/null +++ b/CoinEx.Net/Enums/AccountType.cs @@ -0,0 +1,29 @@ +using CryptoExchange.Net.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Account type + /// + public enum AccountType + { + /// + /// Spot account + /// + [Map("SPOT")] + Spot, + /// + /// Margin account + /// + [Map("MARGIN")] + Margin, + /// + /// Futures account + /// + [Map("FUTURES")] + Futures + } +} diff --git a/CoinEx.Net/Enums/OrderSide.cs b/CoinEx.Net/Enums/OrderSide.cs index eedefa2..ed08873 100644 --- a/CoinEx.Net/Enums/OrderSide.cs +++ b/CoinEx.Net/Enums/OrderSide.cs @@ -1,4 +1,6 @@ -namespace CoinEx.Net.Enums +using CryptoExchange.Net.Attributes; + +namespace CoinEx.Net.Enums { /// /// Order side @@ -12,10 +14,12 @@ public enum OrderSide /// /// Buy /// + [Map("buy")] Buy, /// /// Sell /// + [Map("sell")] Sell } } diff --git a/CoinEx.Net/Enums/OrderStatusV2.cs b/CoinEx.Net/Enums/OrderStatusV2.cs new file mode 100644 index 0000000..d8c7d6e --- /dev/null +++ b/CoinEx.Net/Enums/OrderStatusV2.cs @@ -0,0 +1,39 @@ +using CryptoExchange.Net.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Order status + /// + public enum OrderStatusV2 + { + /// + /// Open + /// + [Map("open")] + Open, + /// + /// Partially filled + /// + [Map("part_filled")] + PartiallyFilled, + /// + /// Fully filled + /// + [Map("filled")] + Filled, + /// + /// Partially filled, partially canceled + /// + [Map("part_canceled")] + PartiallyCanceled, + /// + /// Fully canceled + /// + [Map("canceled")] + Canceled + } +} diff --git a/CoinEx.Net/Enums/OrderTypeV2.cs b/CoinEx.Net/Enums/OrderTypeV2.cs new file mode 100644 index 0000000..8eafbf2 --- /dev/null +++ b/CoinEx.Net/Enums/OrderTypeV2.cs @@ -0,0 +1,39 @@ +using CryptoExchange.Net.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Order type + /// + public enum OrderTypeV2 + { + /// + /// Limit order + /// + [Map("limit")] + Limit, + /// + /// Market order + /// + [Map("market")] + Market, + /// + /// Post only + /// + [Map("maker_only")] + PostOnly, + /// + /// Immediate or cancel + /// + [Map("ioc")] + ImmediateOrCancel, + /// + /// Fill or kill + /// + [Map("fok")] + FillOrKill + } +} diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs index a98df83..16e28c3 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs @@ -2,7 +2,8 @@ using CryptoExchange.Net.Objects; using System.Threading; using System.Threading.Tasks; -using CoinEx.Net.Objects.Models; +using CoinEx.Net.Objects.Models.V2; +using System.Collections.Generic; namespace CoinEx.Net.Interfaces.Clients.SpotApi { @@ -11,6 +12,84 @@ namespace CoinEx.Net.Interfaces.Clients.SpotApi /// public interface ICoinExClientSpotApiTrading { - + /// + /// Place a new order + /// + /// + /// The symbol + /// Account type, Spot or Margin + /// Order side + /// Order type + /// Quantity + /// Price of the order + /// The asset the quantity is in, for market orders van be the base or quote asset + /// Client order id + /// Hide the order + /// Cancelation Token + /// + Task> PlaceOrderAsync( + string symbol, + AccountType accountType, + OrderSide side, + OrderTypeV2 type, + decimal quantity, + decimal? price = null, + string? quantityAsset = null, + string? clientOrderId = null, + bool? hide = null, + CancellationToken ct = default); + + /// + /// Place a new stop order + /// + /// + /// The symbol + /// Account type, Spot or Margin + /// Order side + /// Order type + /// Quantity + /// Price of the order + /// The asset the quantity is in, for market orders van be the base or quote asset + /// Client order id + /// Price to trigger on + /// Hide the order + /// Cancelation Token + /// + Task> PlaceStopOrderAsync( + string symbol, + AccountType accountType, + OrderSide side, + OrderTypeV2 type, + decimal quantity, + decimal triggerPrice, + decimal? price = null, + string? quantityAsset = null, + string? clientOrderId = null, + bool? hide = null, + CancellationToken ct = default); + + /// + /// Get an order by id + /// + /// + /// Symbol + /// Order id + /// Cancelation Token + /// + Task> GetOrderAsync(string symbol, string orderId, CancellationToken ct = default); + + /// + /// Get a list of open orders + /// + /// + /// Symbol + /// Account type + /// Filter by side + /// Filter by client order id + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetOpenOrdersAsync(string symbol, AccountType accountType, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default); } } diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs index 747c6b3..e7ed3d4 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs @@ -37,27 +37,98 @@ public interface ICoinExSocketClientSpotApi : ISocketApiClient, IDisposable Task> SubscribeToTickerUpdatesAsync(Action>> onMessage, CancellationToken ct = default); /// - /// - /// - /// - /// - /// - /// - /// - /// - /// + /// Subscribe to order book updates + /// + /// + /// Symbol + /// Order book depth, 5, 10, 20 or 50 + /// The merge level, 0.00000000001 up to 1000, 0 for no merging + /// Whether updates should provide full update or only updates + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected Task> SubscribeToOrderBookUpdatesAsync(string symbol, int depth, string? mergeLevel, bool fullBookUpdates, Action> onMessage, CancellationToken ct = default); /// - /// + /// Subscribe to order book updates + /// /// - /// - /// - /// - /// - /// - /// - /// + /// Symbols + /// Order book depth, 5, 10, 20 or 50 + /// The merge level, 0.00000000001 up to 1000, 0 for no merging + /// Whether updates should provide full update or only updates + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected Task> SubscribeToOrderBookUpdatesAsync(IEnumerable symbols, int depth, string? mergeLevel, bool fullBookUpdates, Action> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to live trade updates + /// + /// + /// Symbol + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToTradeUpdatesAsync(string symbol, Action>> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to live trade updates + /// + /// + /// Symbols + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToTradeUpdatesAsync(IEnumerable symbols, Action>> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to live trade updates + /// + /// + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToTradeUpdatesAsync(Action>> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to index price updates + /// + /// + /// Symbol + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToIndexPriceUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to index price updates + /// + /// + /// Symbols + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToIndexPriceUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to book price updates + /// + /// + /// Symbol + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToBookPriceUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to book price updates + /// + /// + /// Symbols + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToBookPriceUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default); } } \ No newline at end of file diff --git a/CoinEx.Net/Objects/Internal/CoinExApiResult.cs b/CoinEx.Net/Objects/Internal/CoinExApiResult.cs index 1b734b5..47f0c35 100644 --- a/CoinEx.Net/Objects/Internal/CoinExApiResult.cs +++ b/CoinEx.Net/Objects/Internal/CoinExApiResult.cs @@ -15,4 +15,18 @@ internal class CoinExApiResult : CoinExApiResult [JsonPropertyName("data")] public T Data { get; set; } = default!; } + + internal class CoinExPageApiResult : CoinExApiResult + { + [JsonPropertyName("pagination")] + public CoinExPage Pagination { get; set; } + } + + internal class CoinExPage + { + [JsonPropertyName("total")] + public int Total { get; set; } + [JsonPropertyName("has_next")] + public bool HasNext { get; set; } + } } diff --git a/CoinEx.Net/Objects/Models/V2/CoinExBookPriceUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExBookPriceUpdate.cs new file mode 100644 index 0000000..d9bd06d --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExBookPriceUpdate.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Best book prices update + /// + public record CoinExBookPriceUpdate + { + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + + /// + /// Update time + /// + [JsonPropertyName("update_at")] + public DateTime UpdateTime { get; set; } + /// + /// Current best bid price + /// + [JsonPropertyName("best_bid_price")] + public decimal BestBidPrice { get; set; } + /// + /// Current best bid quantity + /// + [JsonPropertyName("best_bid_size")] + public decimal BestBidQuantity { get; set; } + /// + /// Current best ask price + /// + [JsonPropertyName("best_ask_price")] + public decimal BestAskPrice { get; set; } + /// + /// Current best ask quantity + /// + [JsonPropertyName("best_ask_size")] + public decimal BestAskQuantity { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExIndexPriceUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExIndexPriceUpdate.cs new file mode 100644 index 0000000..21af34d --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExIndexPriceUpdate.cs @@ -0,0 +1,22 @@ + +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Index price update + /// + public record CoinExIndexPriceUpdate + { + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Index price + /// + [JsonPropertyName("index_price")] + public decimal Price { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExOrder.cs b/CoinEx.Net/Objects/Models/V2/CoinExOrder.cs new file mode 100644 index 0000000..ab67182 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExOrder.cs @@ -0,0 +1,123 @@ +using CoinEx.Net.Enums; +using System; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Order info + /// + public record CoinExOrder + { + /// + /// Order id + /// + [JsonPropertyName("order_id")] + public string OrderId { get; set; } = string.Empty; + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Account type + /// + [JsonPropertyName("market_type")] + public AccountType AccountType { get; set; } + /// + /// Asset the quantity is in + /// + [JsonPropertyName("ccy")] + public string QuantityAsset { get; set; } = string.Empty; + /// + /// Order side + /// + [JsonPropertyName("side")] + public OrderSide Side { get; set; } + /// + /// Order type + /// + [JsonPropertyName("type")] + public OrderTypeV2 Type { get; set; } + /// + /// Order quantity + /// + [JsonPropertyName("amount")] + public decimal Quantity { get; set; } + /// + /// Order price + /// + [JsonPropertyName("price")] + public decimal? Price { get; set; } + /// + /// Quantity remaining + /// + [JsonPropertyName("unfilled_amount")] + public decimal QuantityRemaining { get; set; } + /// + /// Quantity filled + /// + [JsonPropertyName("filled_amount")] + public decimal QuantityFilled { get; set; } + /// + /// Value of the filled part + /// + [JsonPropertyName("filled_value")] + public decimal ValueFilled { get; set; } + /// + /// Client order id + /// + [JsonPropertyName("client_id")] + public string? ClientOrderId { get; set; } + /// + /// Fee in base asset + /// + [JsonPropertyName("base_fee")] + public decimal FeeBaseAsset { get; set; } + /// + /// Fee in quote asset + /// + [JsonPropertyName("quote_fee")] + public decimal FeeQuoteAsset { get; set; } + /// + /// Fee discount + /// + [JsonPropertyName("discount_fee")] + public decimal FeeDiscount { get; set; } + /// + /// Maker fee rate + /// + [JsonPropertyName("maker_fee_rate")] + public decimal MakerFeeRate { get; set; } + /// + /// Taker fee rate + /// + [JsonPropertyName("taker_fee_rate")] + public decimal TakerFeeRate { get; set; } + /// + /// Filled amount of the last trade + /// + [JsonPropertyName("last_filled_amount")] + public decimal? LastFilledQuantity { get; set; } + /// + /// Price of the last trade + /// + [JsonPropertyName("last_filled_price")] + public decimal? LastFilledPrice { get; set; } + /// + /// Timestamp order was created + /// + [JsonPropertyName("created_at")] + public DateTime CreateTime { get; set; } + /// + /// Timestamp order was last updated + /// + [JsonPropertyName("updated_at")] + public DateTime? UpdateTime { get; set; } + /// + /// Status of the order + /// + [JsonPropertyName("status")] + public OrderStatusV2 Status { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExStopId.cs b/CoinEx.Net/Objects/Models/V2/CoinExStopId.cs new file mode 100644 index 0000000..4711da9 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExStopId.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Stop order id + /// + public record CoinExStopId + { + /// + /// Stop order id + /// + [JsonPropertyName("stop_id")] + public string StopId { get; set; } = string.Empty; + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExTrade.cs b/CoinEx.Net/Objects/Models/V2/CoinExTrade.cs index ff4d218..b24dcfd 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExTrade.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExTrade.cs @@ -6,6 +6,14 @@ namespace CoinEx.Net.Objects.Models.V2 { + internal record CoinExTradeWrapper + { + [JsonPropertyName("market")] + public string Symbol { get; set; } + [JsonPropertyName("deal_list")] + public IEnumerable Trades { get; set; } = Array.Empty(); + } + /// /// Trade info /// diff --git a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs new file mode 100644 index 0000000..c28b02d --- /dev/null +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs @@ -0,0 +1,49 @@ +using CoinEx.Net.Objects.Models.V2; +using CoinEx.Net.Objects.Sockets.V2.Queries; +using CryptoExchange.Net.Interfaces; +using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Sockets; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace CoinEx.Net.Objects.Sockets.V2.Subscriptions +{ + internal class CoinExTradesSubscription : Subscription + { + private IEnumerable? _symbols; + private Dictionary _parameters; + private Action>> _handler; + + public override HashSet ListenerIdentifiers { get; set; } + public CoinExTradesSubscription(ILogger logger, IEnumerable? symbols, Dictionary parameters, Action>> handler) : base(logger, false) + { + _symbols = symbols; + _parameters = parameters; + _handler = handler; + ListenerIdentifiers = new HashSet { "deals.update" }; + } + + public override CallResult DoHandleMessage(SocketConnection connection, DataEvent message) + { + var data = (CoinExSocketUpdate)message.Data; + var relevant = data.Data.Trades.Where(d => (_symbols?.Any() != true) || _symbols.Contains(data.Data.Symbol)).ToList(); + if (!relevant.Any() || !data.Data.Trades.Any()) + return new CallResult(null); + + _handler.Invoke(message.As>(relevant, data.Data.Symbol, SocketUpdateType.Update)); + return new CallResult(null); + } + + public override Type? GetMessageType(IMessageAccessor message) => typeof(CoinExSocketUpdate); + + public override Query? GetSubQuery(SocketConnection connection) + => new CoinExQuery("deals.subscribe", _parameters, false, 1); + + public override Query? GetUnsubQuery() + => new CoinExQuery("deals.unsubscribe", _parameters, false, 1); + } +} From 0d2ce23c6c6874c76b79447ae23c9aa9cd315e89 Mon Sep 17 00:00:00 2001 From: JKorf Date: Wed, 3 Apr 2024 17:06:52 +0200 Subject: [PATCH 07/17] wip --- .../SpotApi/CoinExRestClientSpotApi.cs | 32 +- .../SpotApi/CoinExRestClientSpotApiAccount.cs | 136 +- .../SpotApi/CoinExRestClientSpotApiTrading.cs | 184 +- .../SpotApi/CoinExSocketClientSpotApi.cs | 52 +- CoinEx.Net/CoinEx.Net.xml | 1610 +++++++++++++++-- CoinEx.Net/CoinExV2AuthenticationProvider.cs | 57 + CoinEx.Net/Enums/BorrowStatus.cs | 34 + CoinEx.Net/Enums/DepositStatus.cs | 44 + CoinEx.Net/Enums/OrderUpdateType.cs | 29 + CoinEx.Net/Enums/StopOrderUpdateType.cs | 29 + CoinEx.Net/Enums/TriggerDirection.cs | 24 + .../SpotApi/ICoinExClientSpotApiAccount.cs | 145 +- .../SpotApi/ICoinExClientSpotApiTrading.cs | 157 +- .../SpotApi/ICoinExSocketClientSpotApi.cs | 36 + .../Objects/Internal/CoinExApiResult.cs | 4 +- .../Objects/Models/V2/CoinExAmmBalance.cs | 41 + CoinEx.Net/Objects/Models/V2/CoinExBalance.cs | 33 + .../Objects/Models/V2/CoinExBalanceUpdate.cs | 45 + CoinEx.Net/Objects/Models/V2/CoinExBorrow.cs | 60 + .../Objects/Models/V2/CoinExBorrowLimit.cs | 46 + .../Objects/Models/V2/CoinExCreditBalance.cs | 39 + CoinEx.Net/Objects/Models/V2/CoinExDeposit.cs | 85 + .../Objects/Models/V2/CoinExDepositAddress.cs | 24 + .../Objects/Models/V2/CoinExMarginBalance.cs | 78 + .../Objects/Models/V2/CoinExOrderUpdate.cs | 25 + .../Objects/Models/V2/CoinExPaginated.cs | 25 + .../Objects/Models/V2/CoinExStopOrder.cs | 86 + .../Models/V2/CoinExStopOrderUpdate.cs | 25 + .../Objects/Models/V2/CoinExTradeFee.cs | 29 + .../Objects/Models/V2/CoinExUserTrade.cs | 55 + .../Objects/Sockets/V2/Queries/CoinExQuery.cs | 2 +- .../V2/Subscriptions/CoinExSubscription.cs | 6 +- .../Subscriptions/CoinExTickerSubscription.cs | 4 +- .../Subscriptions/CoinExTradesSubscription.cs | 4 +- docs/README.md | 1 + 35 files changed, 3121 insertions(+), 165 deletions(-) create mode 100644 CoinEx.Net/CoinExV2AuthenticationProvider.cs create mode 100644 CoinEx.Net/Enums/BorrowStatus.cs create mode 100644 CoinEx.Net/Enums/DepositStatus.cs create mode 100644 CoinEx.Net/Enums/OrderUpdateType.cs create mode 100644 CoinEx.Net/Enums/StopOrderUpdateType.cs create mode 100644 CoinEx.Net/Enums/TriggerDirection.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExAmmBalance.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExBalance.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExBalanceUpdate.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExBorrow.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExBorrowLimit.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExCreditBalance.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExDeposit.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExDepositAddress.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExMarginBalance.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExOrderUpdate.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExPaginated.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExStopOrderUpdate.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExTradeFee.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExUserTrade.cs create mode 100644 docs/README.md diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs index e287fd5..d6fd78c 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs @@ -21,6 +21,7 @@ using CryptoExchange.Net.Converters.MessageParsing; using CryptoExchange.Net.Clients; using CryptoExchange.Net.Converters.SystemTextJson; +using CoinEx.Net.Objects.Models.V2; namespace CoinEx.Net.Clients.SpotApi { @@ -77,9 +78,21 @@ internal CoinExRestClientSpotApi(ILogger logger, HttpClient? httpClient, CoinExR /// protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials) - => new CoinExAuthenticationProvider(credentials, ClientOptions.NonceProvider ?? new CoinExNonceProvider()); + => new CoinExV2AuthenticationProvider(credentials); #region methods + internal async Task ExecuteAsync(Uri uri, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false) + { + var result = await SendRequestAsync(uri, method, ct, parameters, signed).ConfigureAwait(false); + if (!result) + return result.AsDataless(); + + if (result.Data.Code != 0) + return result.AsDatalessError(new ServerError(result.Data.Code, result.Data.Message!)); + + return result.AsDataless(); + } + internal async Task> ExecuteAsync(Uri uri, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false) where T : class { var result = await SendRequestAsync>(uri, method, ct, parameters, signed).ConfigureAwait(false); @@ -92,16 +105,23 @@ internal async Task> ExecuteAsync(Uri uri, HttpMethod method return result.As(result.Data.Data); } - internal async Task> ExecutePaginatedAsync(Uri uri, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false) where T : class + internal async Task>> ExecutePaginatedAsync(Uri uri, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false) where T : class { - var result = await SendRequestAsync>(uri, method, ct, parameters, signed).ConfigureAwait(false); + var result = await SendRequestAsync>>(uri, method, ct, parameters, signed).ConfigureAwait(false); if (!result) - return result.As(default); + return result.As>(default); if (result.Data.Code != 0) - return result.AsError(new ServerError(result.Data.Code, result.Data.Message!)); + return result.AsError>(new ServerError(result.Data.Code, result.Data.Message!)); - return result.As(result.Data.Data); + var resultPage = new CoinExPaginated + { + HasNext = result.Data.Pagination.HasNext, + Total = result.Data.Pagination.Total, + Items = result.Data.Data + }; + + return result.As(resultPage); } internal Uri GetUri(string path) => new Uri(BaseAddress.AppendPath(path)); diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiAccount.cs b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiAccount.cs index a80d385..08d849c 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiAccount.cs +++ b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiAccount.cs @@ -7,9 +7,9 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using CoinEx.Net.Objects.Models; using CoinEx.Net.Interfaces.Clients.SpotApi; using CoinEx.Net.ExtensionMethods; +using CoinEx.Net.Objects.Models.V2; namespace CoinEx.Net.Clients.SpotApi { @@ -23,6 +23,140 @@ internal CoinExRestClientSpotApiAccount(CoinExRestClientSpotApi baseClient) _baseClient = baseClient; } + /// + public async Task> GetTradingFeesAsync(string symbol, AccountType accountType, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddEnum("market_type", accountType); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/account/trade-fee-rate"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task SetAccountConfigAsync(bool cetDiscountEnabled, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "cet_discount_enabled", cetDiscountEnabled } + }; + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/account/settings"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetBalancesAsync(CancellationToken ct = default) + { + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/assets/spot/balance"), HttpMethod.Get, ct, null, true).ConfigureAwait(false); + } + + /// + public async Task>> GetMarginBalancesAsync(CancellationToken ct = default) + { + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/assets/margin/balance"), HttpMethod.Get, ct, null, true).ConfigureAwait(false); + } + + /// + public async Task>> GetFinancialBalancesAsync(CancellationToken ct = default) + { + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/assets/financial/balance"), HttpMethod.Get, ct, null, true).ConfigureAwait(false); + } + + /// + public async Task>> GetCreditAccountAsync(CancellationToken ct = default) + { + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/assets/credit/info"), HttpMethod.Get, ct, null, true).ConfigureAwait(false); + } + + /// + public async Task>> GetAutoMarketMakerAccountLiquidityAsync(CancellationToken ct = default) + { + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/assets/amm/liquidity"), HttpMethod.Get, ct, null, true).ConfigureAwait(false); + } + + /// + public async Task> MarginBorrowAsync(string symbol, string asset, decimal quantity, bool autoRenew, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "ccy", asset }, + { "is_auto_renew", autoRenew } + }; + parameters.AddString("borrow_amount", quantity); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/assets/margin/borrow"), HttpMethod.Post, ct, null, true).ConfigureAwait(false); + } + + /// + public async Task MarginRepayAsync(string symbol, string asset, decimal quantity, long? borrowId = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "ccy", asset }, + }; + parameters.AddString("borrow_amount", quantity); + parameters.AddOptional("borrow_id", borrowId); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/assets/margin/repay"), HttpMethod.Post, ct, null, true).ConfigureAwait(false); + } + + /// + public async Task>> GetBorrowHistoryAsync(string? symbol = null, BorrowStatus? status = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddOptional("market", symbol); + parameters.AddOptionalEnum("status", status); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/assets/margin/borrow-history"), HttpMethod.Get, ct, null, true).ConfigureAwait(false); + } + + /// + public async Task> GetBorrowLimitAsync(string symbol, string asset, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "ccy", asset }, + }; + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/assets/margin/interest-limit"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> GetDepositAddressAsync(string asset, string network, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "chain", network }, + { "ccy", asset }, + }; + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/assets/deposit-address"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> RenewDepositAddressAsync(string asset, string network, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "chain", network }, + { "ccy", asset }, + }; + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/assets/renewal-deposit-address"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetDepositHistoryAsync(string asset, string? transactionId = null, DepositStatus? status = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "ccy", asset }, + }; + parameters.AddOptional("tx_id", transactionId); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + parameters.AddOptionalEnum("status", status); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/assets/deposit-history"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } } } diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs index bf1a034..09605e0 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs +++ b/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using CoinEx.Net.Interfaces.Clients.SpotApi; using CoinEx.Net.Objects.Models.V2; +using System; namespace CoinEx.Net.Clients.SpotApi { @@ -44,7 +45,7 @@ public async Task> PlaceOrderAsync( parameters.AddOptional("ccy", quantityAsset); parameters.AddOptional("client_id", clientOrderId); parameters.AddOptional("is_hide", hide); - return await _baseClient.ExecuteAsync(_baseClient.GetUri("spot/order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); } /// @@ -74,7 +75,7 @@ public async Task> PlaceStopOrderAsync( parameters.AddOptional("ccy", quantityAsset); parameters.AddOptional("client_id", clientOrderId); parameters.AddOptional("is_hide", hide); - return await _baseClient.ExecuteAsync(_baseClient.GetUri("spot/stop-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/stop-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); } /// @@ -85,11 +86,105 @@ public async Task> GetOrderAsync(string symbol, strin { "market", symbol }, { "order_id", orderId } }; - return await _baseClient.ExecuteAsync(_baseClient.GetUri("spot/order-status"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/order-status"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); } /// - public async Task>> GetOpenOrdersAsync(string symbol, AccountType accountType, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + public async Task>> GetOpenOrdersAsync(AccountType accountType, string? symbol = null, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddEnum("market_type", accountType); + parameters.AddOptionalEnum("side", side); + parameters.AddOptional("market", symbol); + parameters.AddOptional("client_id", clientOrderId); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/spot/pending-order"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetClosedOrdersAsync(AccountType accountType, string? symbol = null, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddEnum("market_type", accountType); + parameters.AddOptionalEnum("side", side); + parameters.AddOptional("market", symbol); + parameters.AddOptional("client_id", clientOrderId); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/spot/finished-order"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetOpenStopOrdersAsync(AccountType accountType, string? symbol = null, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddEnum("market_type", accountType); + parameters.AddOptionalEnum("side", side); + parameters.AddOptional("market", symbol); + parameters.AddOptional("client_id", clientOrderId); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/spot/pending-stop-order"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetClosedStopOrdersAsync(AccountType accountType, string? symbol = null, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddEnum("market_type", accountType); + parameters.AddOptionalEnum("side", side); + parameters.AddOptional("market", symbol); + parameters.AddOptional("client_id", clientOrderId); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/spot/finished-stop-order"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> EditOrderAsync( + string symbol, + AccountType accountType, + string orderId, + decimal quantity, + decimal? price = null, + CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "order_id", orderId } + }; + parameters.AddEnum("market_type", accountType); + parameters.AddString("amount", quantity); + parameters.AddOptionalString("price", price); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/modify-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> EditStopOrderAsync( + string symbol, + AccountType accountType, + string stopOrderId, + decimal quantity, + decimal triggerPrice, + decimal? price = null, + CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "stop_id", stopOrderId } + }; + parameters.AddEnum("market_type", accountType); + parameters.AddString("amount", quantity); + parameters.AddString("trigger_price", triggerPrice); + parameters.AddOptionalString("price", price); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/modify-stop-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task CancelAllOrdersAsync(string symbol, AccountType accountType, OrderSide? side = null, CancellationToken ct = default) { var parameters = new ParameterCollection() { @@ -97,10 +192,87 @@ public async Task>> GetOpenOrdersAsync(st }; parameters.AddEnum("market_type", accountType); parameters.AddOptionalEnum("side", side); - parameters.AddOptional("client_id", clientOrderId); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/cancel-all-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> CancelOrderAsync(string symbol, AccountType accountType, string orderId, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "order_id", orderId } + }; + parameters.AddEnum("market_type", accountType); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/cancel-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> CancelStopOrderAsync(string symbol, AccountType accountType, string stopOrderId, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "stop_id", stopOrderId } + }; + parameters.AddEnum("market_type", accountType); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/cancel-stop-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> CancelOrderByClientOrderIdAsync(string symbol, AccountType accountType, string clientOrderId, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "client_id", clientOrderId } + }; + parameters.AddEnum("market_type", accountType); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/cancel-order-by-client-id"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> CancelStopOrderByClientOrderIdAsync(string symbol, AccountType accountType, string clientStopOrderId, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "client_id", clientStopOrderId } + }; + parameters.AddEnum("market_type", accountType); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/cancel-stop-order-by-client-id"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetUserTradesAsync(string symbol, AccountType accountType, OrderSide? side = null, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddEnum("market_type", accountType); + parameters.AddOptionalEnum("side", side); + parameters.AddOptionalMilliseconds("start_time", startTime); + parameters.AddOptionalMilliseconds("end_Time", endTime); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/spot/user-deals"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetUserOrderTradesAsync(string symbol, AccountType accountType, long orderId, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "order_id", orderId } + }; + parameters.AddEnum("market_type", accountType); parameters.AddOptional("page", page); parameters.AddOptional("limit", pageSize); - return await _baseClient.ExecuteAsync>(_baseClient.GetUri("spot/pending-order"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/spot/order-deals"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); } + + // TODO Batch endpoints } } diff --git a/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs b/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs index 5cb5036..e0c5007 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs @@ -7,7 +7,6 @@ using Microsoft.Extensions.Logging; using CryptoExchange.Net.Authentication; using System.Threading; -using CoinEx.Net.Objects.Internal; using CoinEx.Net.Interfaces.Clients.SpotApi; using CoinEx.Net.Objects.Options; using CryptoExchange.Net.Objects.Sockets; @@ -43,13 +42,13 @@ public class CoinExSocketClientSpotApi : SocketApiClient, ICoinExSocketClientSpo internal CoinExSocketClientSpotApi(ILogger logger, CoinExSocketOptions options) : base(logger, options.Environment.SocketBaseAddress, options, options.SpotOptions) { - RegisterPeriodicQuery("Ping", TimeSpan.FromMinutes(1), q => (new CoinExQuery("server.ping", new Dictionary())), null); + RegisterPeriodicQuery("Ping", TimeSpan.FromMinutes(1), q => (new CoinExQuery("server.ping", new Dictionary())), null); } #endregion /// protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials) - => new CoinExAuthenticationProvider(credentials, ClientOptions.NonceProvider ?? new CoinExNonceProvider()); + => new CoinExV2AuthenticationProvider(credentials); #region methods @@ -81,10 +80,9 @@ protected override AuthenticationProvider CreateAuthenticationProvider(ApiCreden /// protected override Query? GetAuthenticationRequest() { - //var authProvider = (CoinExAuthenticationProvider)AuthenticationProvider!; - //var authParams = authProvider.GetSocketAuthParameters(); - //return new CoinExQuery("server.sign", authParams, false); - return null; + var authProvider = (CoinExV2AuthenticationProvider)AuthenticationProvider!; + var authParams = authProvider.GetSocketAuthParameters(); + return new CoinExQuery("server.sign", authParams, false, 0); } /// @@ -172,6 +170,46 @@ public async Task> SubscribeToBookPriceUpdatesAsy }, x => onMessage(x.As(x.Data, x.Data.Symbol))); return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); } + + /// + public async Task> SubscribeToOrderUpdatesAsync(Action> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "order", Array.Empty(), new Dictionary + { + { "market_list", Array.Empty() } + }, x => onMessage(x.As(x.Data, x.Data.Order.Symbol, SocketUpdateType.Update)), true); + return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); + } + + /// + public async Task> SubscribeToStopOrderUpdatesAsync(Action> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "stop", Array.Empty(), new Dictionary + { + { "market_list", Array.Empty() } + }, x => onMessage(x.As(x.Data, x.Data.Order.Symbol, SocketUpdateType.Update)), true); + return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); + } + + /// + public async Task> SubscribeToUserTradeUpdatesAsync(Action> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "user_deals", Array.Empty(), new Dictionary + { + { "market_list", Array.Empty() } + }, x => onMessage(x.As(x.Data, x.Data.Symbol, SocketUpdateType.Update)), true); + return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); + } + + /// + public async Task> SubscribeToBalanceUpdatesAsync(Action>> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "balance", Array.Empty(), new Dictionary + { + { "ccy_list", Array.Empty() } + }, x => onMessage(x.As(x.Data.Balances, null, SocketUpdateType.Update)), true); + return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); + } #endregion #endregion diff --git a/CoinEx.Net/CoinEx.Net.xml b/CoinEx.Net/CoinEx.Net.xml index d2cfd6e..ecb7cf0 100644 --- a/CoinEx.Net/CoinEx.Net.xml +++ b/CoinEx.Net/CoinEx.Net.xml @@ -328,6 +328,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -352,7 +394,52 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -417,6 +504,18 @@ + + + + + + + + + + + + CoinEx environments @@ -465,6 +564,86 @@ + + + Account type + + + + + Spot account + + + + + Margin account + + + + + Futures account + + + + + Borrow status + + + + + Borrowing + + + + + In debt + + + + + Forcefully liquidated + + + + + Has been repaid + + + + + Deposit status + + + + + Currently processing + + + + + Awaiting blockchain confirmation + + + + + Canceled + + + + + Finished + + + + + Deposit amount was too small + + + + + Exception + + Interval for klines, int value represent the time in seconds @@ -605,6 +784,36 @@ Canceled + + + Order status + + + + + Open + + + + + Partially filled + + + + + Fully filled + + + + + Partially filled, partially canceled + + + + + Fully canceled + + Type of order @@ -630,6 +839,56 @@ Stop market order + + + Order type + + + + + Limit order + + + + + Market order + + + + + Post only + + + + + Immediate or cancel + + + + + Fill or kill + + + + + Order update type + + + + + Order created + + + + + Order updated + + + + + Order finished + + Price type @@ -650,6 +909,26 @@ Index price + + + Stop order update type + + + + + Order created + + + + + Order active + + + + + Order canceled + + Role of a transaction @@ -665,6 +944,21 @@ Taker of an existing order book entry + + + Trigger direction + + + + + Should trigger when the price is higher than the trigger price + + + + + Should trigger when the price is lower than the trigger price + + Type of update @@ -1270,6 +1564,144 @@ CoinEx account endpoints. Account endpoints include balance info, withdraw/deposit info and requesting and account settings + + + Get trading fees + + + Symbol name + Account type + Cancelation token + + + + + Update account settings + + + Global switch for CET Deduction + Cancelation token + + + + + Get balances + + + Cancelation token + + + + + Get margin balances + + + Cancelation token + + + + + Get balances in the financial account + + + Cancelation token + + + + + Get credit account info + + + Cancelation token + + + + + Get automated market maker account liquidity + + + Cancelation token + + + + + Apply for margin borrowing + + + Symbol + Asset + Quantity to borrow + Whether to renew automatically. Automatic renewal means that after the loan expires, the system will renew the loan based on the latest borrowing interest rate and cycle. + Cancelation token + + + + + Repay a margin loan + + + Symbol + Asset + Quantity to repay + Loan record ID + Cancelation token + + + + + Get borrow history + + + Filter by symbol + Filter by status + Page number + Page size + Cancelation token + + + + + Get borrow limits + + + Symbol + Asset + Cancelation token + + + + + Get the deposit address for an asset + + + The asset to deposit + Network + Cancelation token + + + + + Renew deposit address + + + The asset + Network + Cancelation token + + + + + Get deposit history + + + Asset + Filter by transaction id + Filter by status + Page number + Page size + Cancelation token + + CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. @@ -1340,14 +1772,212 @@ CoinEx trading endpoints, placing and mananging orders. - + - Spot streams + Place a new order + + The symbol + Account type, Spot or Margin + Order side + Order type + Quantity + Price of the order + The asset the quantity is in, for market orders van be the base or quote asset + Client order id + Hide the order + Cancelation Token + - + - Subscribe to symbol ticker updates for the specified symbols. Note that only one ticker subscription can be active at the same time; new ticker subscription will override the old subscriptions. + Place a new stop order + + + The symbol + Account type, Spot or Margin + Order side + Order type + Quantity + Price of the order + The asset the quantity is in, for market orders van be the base or quote asset + Client order id + Price to trigger on + Hide the order + Cancelation Token + + + + + Get an order by id + + + Symbol + Order id + Cancelation Token + + + + + Get a list of open orders + + + Filter by symbol + Account type + Filter by side + Filter by client order id + Page number + Page size + Cancelation Token + + + + + Get a list of closed orders. Note that orders canceled without having any trades will not be returned + + + Filter by symbol + Account type + Filter by side + Filter by client order id + Page number + Page size + Cancelation Token + + + + + Get a list of open stop orders + + + Filter by symbol + Account type + Filter by side + Filter by client order id + Page number + Page size + Cancelation Token + + + + + Get a list of closed stop orders. Note that orders canceled without having any trades will not be returned + + + Filter by symbol + Account type + Filter by side + Filter by client order id + Page number + Page size + Cancelation Token + + + + + Edit an active order + + + Symbol + Account type + Order id + New quantity + New price + Cancelation Token + + + + + Edit an active stop order + + + Symbol + Account type + Order id + New quantity + New price + New trigger price + Cancelation Token + + + + + Cancel all orders for a symbol + + + Symbol + Account type + Only cancel a specific order side + Cancelation Token + + + + + Cancel an active order + + + Symbol + Account type + Id of order to cancel + Cancelation Token + + + + + Cancel an active stop order + + + Symbol + Account type + Id of stop order to cancel + Cancelation Token + + + + + Cancel an active order by its client order id + + + Symbol + Account type + Client order id of order to cancel + Cancelation Token + + + + + Cancel an active stop order by its client order id + + + Symbol + Account type + Client order id of stop order to cancel + Cancelation Token + + + + + Get trade list + + + Symbol + Account type + Filter by side + Filter by start time + Filter by end time + Page number + Page size + Cancelation Token + + + + + Spot streams + + + + + Subscribe to symbol ticker updates for the specified symbols. Note that only one ticker subscription can be active at the same time; new ticker subscription will override the old subscriptions. The symbols to subscribe @@ -1366,89 +1996,134 @@ - + Subscribe to order book updates + - - - - - - - + Symbol + Order book depth, 5, 10, 20 or 50 + The merge level, 0.00000000001 up to 1000, 0 for no merging + Whether updates should provide full update or only updates + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to order book updates + - - - - - - - + Symbols + Order book depth, 5, 10, 20 or 50 + The merge level, 0.00000000001 up to 1000, 0 for no merging + Whether updates should provide full update or only updates + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to live trade updates + - - - - + Symbol + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to live trade updates + - - - - + Symbols + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to live trade updates + - - - + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to index price updates + - - - - + Symbol + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to index price updates + - - - - + Symbols + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to book price updates + - - - - + Symbol + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to book price updates + - - - - + Symbols + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to user order updates + + + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to user stop order updates + + + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to user trade updates + + + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to user balance updates + + + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected @@ -2498,194 +3173,669 @@ The quantity of the transaction - + - Best book prices update + Automated Market Maker liquidity info - + Symbol - + - Update time + Base asset - + - Current best bid price + Quote asset - + - Current best bid quantity + Base asset amount - + - Current best ask price + Quote asset amount - + - Current best ask quantity + Liquidity percentage in AMM account - + - Index price + Balance info - + - Symbol + Asset name - + - Timestamp + Available amount - + - Price + Frozen amount - + - Index sources + Total amount - + - Index price source + Balance update - + - Exchange + Margin symbol - + - Timestamp + Asset name - + - Weight of the source + Available amount - + - Price + Frozen amount - + - Index price update + Update time - + - Symbol + Best book prices update - + - Index price + Symbol - + - Kline/candlestick info + Update time - + - Symbol + Current best bid price - + - Open time + Current best bid quantity - + - Open price + Current best ask price - + - Close price + Current best ask quantity - + - High price + Borrow record - + - Low price + Id - + - Volume + Symbol - + - Value (Quote asset volume) + Asset - + - Order book info + Daily interest rate - + - Symbol + Expire time - + - Is full order book + Borrow amount - + - The book data + Amount to repay - + - Order book data + Borrow status - + - Asks list + Is auto renewing - + - Bids list + Borrow limit info - + - Last price + Symbol - + - Update time + Asset + + + + + Daily interest rate + + + + + Max leverage + + + + + Min amount borrowable + + + + + Max amount borrowable + + + + + Credit account balance + + + + + Account assets + + + + + To be repaid + + + + + Current risk rate + + + + + Withdrawal risk rate + + + + + Market value of available withdrawal + + + + + Deposit info + + + + + Deposit id + + + + + Creation time + + + + + Transaction id + + + + + Asset + + + + + Network + + + + + Quantity deposited + + + + + Actual amount received + + + + + Deposit address + + + + + Amount of confirmations + + + + + Status of the deposit + + + + + Blockchain explorer url for the transaction + + + + + Blockchain explorer url for the deposit address + + + + + Remark + + + + + Deposit method + + + + + Deposit addres + + + + + Memo + + + + + Deposit address + + + + + Index price + + + + + Symbol + + + + + Timestamp + + + + + Price + + + + + Index sources + + + + + Index price source + + + + + Exchange + + + + + Timestamp + + + + + Weight of the source + + + + + Price + + + + + Index price update + + + + + Symbol + + + + + Index price + + + + + Kline/candlestick info + + + + + Symbol + + + + + Open time + + + + + Open price + + + + + Close price + + + + + High price + + + + + Low price + + + + + Volume + + + + + Value (Quote asset volume) + + + + + Margin balance info + + + + + Margin account + + + + + Base asset + + + + + Quote asset + + + + + Current risk rate + + + + + Current liquidation price + + + + + Available + + + + + Frozen + + + + + Repaid + + + + + Interest + + + + + Assets balance info + + + + + Base asset amount + + + + + Quote asset amount + + + + + Order info + + + + + Order id + + + + + Symbol + + + + + Account type + + + + + Asset the quantity is in + + + + + Order side + + + + + Order type + + + + + Order quantity + + + + + Order price + + + + + Quantity remaining + + + + + Quantity filled + + + + + Value of the filled part + + + + + Client order id + + + + + Fee in base asset + + + + + Fee in quote asset + + + + + Fee discount + + + + + Maker fee rate + + + + + Taker fee rate + + + + + Filled amount of the last trade + + + + + Price of the last trade + + + + + Timestamp order was created + + + + + Timestamp order was last updated + + + + + Status of the order + + + + + Order book info + + + + + Symbol + + + + + Is full order book + + + + + The book data + + + + + Order book data + + + + + Asks list + + + + + Bids list + + + + + Last price + + + + + Update time @@ -2708,6 +3858,141 @@ Quantity + + + Order update + + + + + Event that triggered the update + + + + + Order data + + + + + Paginated result + + + + + Total results + + + + + Has next page + + + + + Page items + + + + + Stop order id + + + + + Stop order id + + + + + Stop order info + + + + + Order id + + + + + Symbol + + + + + Account type + + + + + Asset the quantity is in + + + + + Order side + + + + + Order type + + + + + Order quantity + + + + + Order price + + + + + Client order id + + + + + Timestamp order was created + + + + + Timestamp order was last updated + + + + + Trigger price + + + + + Trigger direction + + + + + Trigger price type + + + + + Stop order update + + + + + Event that triggered the update + + + + + Order data + + Symbol info @@ -2848,6 +4133,71 @@ Quantity traded + + + Trading fee info + + + + + Symbol + + + + + Fee for maker trades + + + + + Fee for taker trades + + + + + User trade info + + + + + Trade id + + + + + Trade time + + + + + Symbol + + + + + Trade side + + + + + Order id + + + + + Margin symbol + + + + + Trade price + + + + + Quantity traded + + Options for CoinEx SymbolOrderBook diff --git a/CoinEx.Net/CoinExV2AuthenticationProvider.cs b/CoinEx.Net/CoinExV2AuthenticationProvider.cs new file mode 100644 index 0000000..1f49283 --- /dev/null +++ b/CoinEx.Net/CoinExV2AuthenticationProvider.cs @@ -0,0 +1,57 @@ +using CryptoExchange.Net; +using CryptoExchange.Net.Authentication; +using System; +using System.Collections.Generic; +using System.Net.Http; +using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Interfaces; +using CoinEx.Net.Objects.Internal; +using System.Linq; +using CryptoExchange.Net.Clients; +using CryptoExchange.Net.Converters.SystemTextJson; + +namespace CoinEx.Net +{ + internal class CoinExV2AuthenticationProvider : AuthenticationProvider + { + public string GetApiKey() => _credentials.Key!.GetString(); + + public CoinExV2AuthenticationProvider(ApiCredentials credentials): base(credentials) + { + if (credentials.CredentialType != ApiCredentialsType.Hmac) + throw new Exception("Only Hmac authentication is supported"); + } + + public override void AuthenticateRequest(RestApiClient apiClient, Uri uri, HttpMethod method, Dictionary providedParameters, bool auth, ArrayParametersSerialization arraySerialization, HttpMethodParameterPosition parameterPosition, RequestBodyFormat bodyFormat, out SortedDictionary uriParameters, out SortedDictionary bodyParameters, out Dictionary headers) + { + uriParameters = parameterPosition == HttpMethodParameterPosition.InUri ? new SortedDictionary(providedParameters) : new SortedDictionary(); + bodyParameters = parameterPosition == HttpMethodParameterPosition.InBody ? new SortedDictionary(providedParameters) : new SortedDictionary(); + headers = new Dictionary(); + + if (!auth) + return; + + var parameters = parameterPosition == HttpMethodParameterPosition.InUri ? uriParameters: bodyParameters; + var parameterString = parameterPosition == HttpMethodParameterPosition.InUri ? (parameters.Any() ? "?" + parameters.CreateParamString(false, arraySerialization) : "") : new SystemTextJsonMessageSerializer().Serialize(parameters); + var timestamp = GetMillisecondTimestamp(apiClient); + var signData = method.ToString().ToUpperInvariant() + uri.AbsolutePath + parameterString + timestamp + _credentials.Secret!.GetString(); + var sign = SignSHA256(signData, SignOutputType.Hex); + headers.Add("X-COINEX-KEY", _credentials.Key!.GetString()); + headers.Add("X-COINEX-SIGN", sign); + headers.Add("X-COINEX-TIMESTAMP", timestamp); + } + + public Dictionary GetSocketAuthParameters() + { + var timestamp = CryptoExchange.Net.Converters.SystemTextJson.DateTimeConverter.ConvertToMilliseconds(DateTime.UtcNow); + var signData = timestamp + _credentials.Secret!.GetString(); + var sign = SignSHA256(signData, SignOutputType.Hex); + return new Dictionary + { + { "access_id", _credentials.Key!.GetString() }, + { "signed_str", sign }, + { "timestamp", timestamp } + }; + } + } +} diff --git a/CoinEx.Net/Enums/BorrowStatus.cs b/CoinEx.Net/Enums/BorrowStatus.cs new file mode 100644 index 0000000..8f968ed --- /dev/null +++ b/CoinEx.Net/Enums/BorrowStatus.cs @@ -0,0 +1,34 @@ +using CryptoExchange.Net.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Borrow status + /// + public enum BorrowStatus + { + /// + /// Borrowing + /// + [Map("loan")] + Loan, + /// + /// In debt + /// + [Map("debt")] + Debt, + /// + /// Forcefully liquidated + /// + [Map("liquidated")] + Liquidated, + /// + /// Has been repaid + /// + [Map("finish")] + Finish + } +} diff --git a/CoinEx.Net/Enums/DepositStatus.cs b/CoinEx.Net/Enums/DepositStatus.cs new file mode 100644 index 0000000..c0d3e86 --- /dev/null +++ b/CoinEx.Net/Enums/DepositStatus.cs @@ -0,0 +1,44 @@ +using CryptoExchange.Net.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Deposit status + /// + public enum DepositStatus + { + /// + /// Currently processing + /// + [Map("processing")] + Processing, + /// + /// Awaiting blockchain confirmation + /// + [Map("confirming")] + Confirming, + /// + /// Canceled + /// + [Map("cancelled")] + Canceled, + /// + /// Finished + /// + [Map("finished")] + Finished, + /// + /// Deposit amount was too small + /// + [Map("too_small")] + TooSmall, + /// + /// Exception + /// + [Map("exception")] + Exception + } +} diff --git a/CoinEx.Net/Enums/OrderUpdateType.cs b/CoinEx.Net/Enums/OrderUpdateType.cs new file mode 100644 index 0000000..fbd85a0 --- /dev/null +++ b/CoinEx.Net/Enums/OrderUpdateType.cs @@ -0,0 +1,29 @@ +using CryptoExchange.Net.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Order update type + /// + public enum OrderUpdateType + { + /// + /// Order created + /// + [Map("put")] + Put, + /// + /// Order updated + /// + [Map("update")] + Update, + /// + /// Order finished + /// + [Map("finish")] + Finish + } +} diff --git a/CoinEx.Net/Enums/StopOrderUpdateType.cs b/CoinEx.Net/Enums/StopOrderUpdateType.cs new file mode 100644 index 0000000..f38b3a9 --- /dev/null +++ b/CoinEx.Net/Enums/StopOrderUpdateType.cs @@ -0,0 +1,29 @@ +using CryptoExchange.Net.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Stop order update type + /// + public enum StopOrderUpdateType + { + /// + /// Order created + /// + [Map("put")] + Put, + /// + /// Order active + /// + [Map("active")] + Active, + /// + /// Order canceled + /// + [Map("cancel")] + Cancel + } +} diff --git a/CoinEx.Net/Enums/TriggerDirection.cs b/CoinEx.Net/Enums/TriggerDirection.cs new file mode 100644 index 0000000..cbefa03 --- /dev/null +++ b/CoinEx.Net/Enums/TriggerDirection.cs @@ -0,0 +1,24 @@ +using CryptoExchange.Net.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Trigger direction + /// + public enum TriggerDirection + { + /// + /// Should trigger when the price is higher than the trigger price + /// + [Map("higher")] + Higher, + /// + /// Should trigger when the price is lower than the trigger price + /// + [Map("lower")] + Lower + } +} diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiAccount.cs b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiAccount.cs index 63e27de..5b4083e 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiAccount.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiAccount.cs @@ -1,8 +1,11 @@ using CryptoExchange.Net.Objects; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using CoinEx.Net.Objects.Models; +using CoinEx.Net.Objects.Models.V2; +using CoinEx.Net.Enums; +using System.Collections.Generic; +using System.Diagnostics.Metrics; +using System.Text.RegularExpressions; namespace CoinEx.Net.Interfaces.Clients.SpotApi { @@ -11,6 +14,142 @@ namespace CoinEx.Net.Interfaces.Clients.SpotApi /// public interface ICoinExClientSpotApiAccount { - + /// + /// Get trading fees + /// + /// + /// Symbol name + /// Account type + /// Cancelation token + /// + Task> GetTradingFeesAsync(string symbol, AccountType accountType, CancellationToken ct = default); + + /// + /// Update account settings + /// + /// + /// Global switch for CET Deduction + /// Cancelation token + /// + Task SetAccountConfigAsync(bool cetDiscountEnabled, CancellationToken ct = default); + + /// + /// Get balances + /// + /// + /// Cancelation token + /// + Task>> GetBalancesAsync(CancellationToken ct = default); + + /// + /// Get margin balances + /// + /// + /// Cancelation token + /// + Task>> GetMarginBalancesAsync(CancellationToken ct = default); + + /// + /// Get balances in the financial account + /// + /// + /// Cancelation token + /// + Task>> GetFinancialBalancesAsync(CancellationToken ct = default); + + /// + /// Get credit account info + /// + /// + /// Cancelation token + /// + Task>> GetCreditAccountAsync(CancellationToken ct = default); + + /// + /// Get automated market maker account liquidity + /// + /// + /// Cancelation token + /// + Task>> GetAutoMarketMakerAccountLiquidityAsync(CancellationToken ct = default); + + /// + /// Apply for margin borrowing + /// + /// + /// Symbol + /// Asset + /// Quantity to borrow + /// Whether to renew automatically. Automatic renewal means that after the loan expires, the system will renew the loan based on the latest borrowing interest rate and cycle. + /// Cancelation token + /// + Task> MarginBorrowAsync(string symbol, string asset, decimal quantity, bool autoRenew, CancellationToken ct = default); + + /// + /// Repay a margin loan + /// + /// + /// Symbol + /// Asset + /// Quantity to repay + /// Loan record ID + /// Cancelation token + /// + Task MarginRepayAsync(string symbol, string asset, decimal quantity, long? borrowId = null, CancellationToken ct = default); + + /// + /// Get borrow history + /// + /// + /// Filter by symbol + /// Filter by status + /// Page number + /// Page size + /// Cancelation token + /// + Task>> GetBorrowHistoryAsync(string? symbol = null, BorrowStatus? status = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get borrow limits + /// + /// + /// Symbol + /// Asset + /// Cancelation token + /// + Task> GetBorrowLimitAsync(string symbol, string asset, CancellationToken ct = default); + + /// + /// Get the deposit address for an asset + /// + /// + /// The asset to deposit + /// Network + /// Cancelation token + /// + Task> GetDepositAddressAsync(string asset, string network, CancellationToken ct = default); + + /// + /// Renew deposit address + /// + /// + /// The asset + /// Network + /// Cancelation token + /// + Task> RenewDepositAddressAsync(string asset, string network, CancellationToken ct = default); + + /// + /// Get deposit history + /// + /// + /// Asset + /// Filter by transaction id + /// Filter by status + /// Page number + /// Page size + /// Cancelation token + /// + Task>> GetDepositHistoryAsync(string asset, string? transactionId = null, DepositStatus? status = null, int? page = null, int? pageSize = null, CancellationToken ct = default); } } diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs index 16e28c3..62ffa69 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using CoinEx.Net.Objects.Models.V2; using System.Collections.Generic; +using System; namespace CoinEx.Net.Interfaces.Clients.SpotApi { @@ -82,7 +83,35 @@ Task> PlaceStopOrderAsync( /// Get a list of open orders /// /// - /// Symbol + /// Filter by symbol + /// Account type + /// Filter by side + /// Filter by client order id + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetOpenOrdersAsync(AccountType accountType, string? symbol = null, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get a list of closed orders. Note that orders canceled without having any trades will not be returned + /// + /// + /// Filter by symbol + /// Account type + /// Filter by side + /// Filter by client order id + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetClosedOrdersAsync(AccountType accountType, string? symbol = null, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get a list of open stop orders + /// + /// + /// Filter by symbol /// Account type /// Filter by side /// Filter by client order id @@ -90,6 +119,130 @@ Task> PlaceStopOrderAsync( /// Page size /// Cancelation Token /// - Task>> GetOpenOrdersAsync(string symbol, AccountType accountType, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + Task>> GetOpenStopOrdersAsync(AccountType accountType, string? symbol = null, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get a list of closed stop orders. Note that orders canceled without having any trades will not be returned + /// + /// + /// Filter by symbol + /// Account type + /// Filter by side + /// Filter by client order id + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetClosedStopOrdersAsync(AccountType accountType, string? symbol = null, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Edit an active order + /// + /// + /// Symbol + /// Account type + /// Order id + /// New quantity + /// New price + /// Cancelation Token + /// + Task> EditOrderAsync( + string symbol, + AccountType accountType, + string orderId, + decimal quantity, + decimal? price = null, + CancellationToken ct = default); + + /// + /// Edit an active stop order + /// + /// + /// Symbol + /// Account type + /// Order id + /// New quantity + /// New price + /// New trigger price + /// Cancelation Token + /// + Task> EditStopOrderAsync( + string symbol, + AccountType accountType, + string stopOrderId, + decimal quantity, + decimal triggerPrice, + decimal? price = null, + CancellationToken ct = default); + + /// + /// Cancel all orders for a symbol + /// + /// + /// Symbol + /// Account type + /// Only cancel a specific order side + /// Cancelation Token + /// + Task CancelAllOrdersAsync(string symbol, AccountType accountType, OrderSide? side = null, CancellationToken ct = default); + + /// + /// Cancel an active order + /// + /// + /// Symbol + /// Account type + /// Id of order to cancel + /// Cancelation Token + /// + Task> CancelOrderAsync(string symbol, AccountType accountType, string orderId, CancellationToken ct = default); + + /// + /// Cancel an active stop order + /// + /// + /// Symbol + /// Account type + /// Id of stop order to cancel + /// Cancelation Token + /// + Task> CancelStopOrderAsync(string symbol, AccountType accountType, string stopOrderId, CancellationToken ct = default); + + /// + /// Cancel an active order by its client order id + /// + /// + /// Symbol + /// Account type + /// Client order id of order to cancel + /// Cancelation Token + /// + Task> CancelOrderByClientOrderIdAsync(string symbol, AccountType accountType, string clientOrderId, CancellationToken ct = default); + + /// + /// Cancel an active stop order by its client order id + /// + /// + /// Symbol + /// Account type + /// Client order id of stop order to cancel + /// Cancelation Token + /// + Task> CancelStopOrderByClientOrderIdAsync(string symbol, AccountType accountType, string clientStopOrderId, CancellationToken ct = default); + + /// + /// Get trade list + /// + /// + /// Symbol + /// Account type + /// Filter by side + /// Filter by start time + /// Filter by end time + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetUserTradesAsync(string symbol, AccountType accountType, OrderSide? side = null, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default); } } diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs index e7ed3d4..769559f 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs @@ -130,5 +130,41 @@ public interface ICoinExSocketClientSpotApi : ISocketApiClient, IDisposable /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected Task> SubscribeToBookPriceUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to user order updates + /// + /// + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToOrderUpdatesAsync(Action> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to user stop order updates + /// + /// + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToStopOrderUpdatesAsync(Action> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to user trade updates + /// + /// + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToUserTradeUpdatesAsync(Action> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to user balance updates + /// + /// + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToBalanceUpdatesAsync(Action>> onMessage, CancellationToken ct = default); } } \ No newline at end of file diff --git a/CoinEx.Net/Objects/Internal/CoinExApiResult.cs b/CoinEx.Net/Objects/Internal/CoinExApiResult.cs index 47f0c35..fb522cc 100644 --- a/CoinEx.Net/Objects/Internal/CoinExApiResult.cs +++ b/CoinEx.Net/Objects/Internal/CoinExApiResult.cs @@ -19,13 +19,13 @@ internal class CoinExApiResult : CoinExApiResult internal class CoinExPageApiResult : CoinExApiResult { [JsonPropertyName("pagination")] - public CoinExPage Pagination { get; set; } + public CoinExPage Pagination { get; set; } = null!; } internal class CoinExPage { [JsonPropertyName("total")] - public int Total { get; set; } + public int? Total { get; set; } [JsonPropertyName("has_next")] public bool HasNext { get; set; } } diff --git a/CoinEx.Net/Objects/Models/V2/CoinExAmmBalance.cs b/CoinEx.Net/Objects/Models/V2/CoinExAmmBalance.cs new file mode 100644 index 0000000..f196657 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExAmmBalance.cs @@ -0,0 +1,41 @@ +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Automated Market Maker liquidity info + /// + public record CoinExAmmBalance + { + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Base asset + /// + [JsonPropertyName("base_ccy")] + public string BaseAsset { get; set; } = string.Empty; + /// + /// Quote asset + /// + [JsonPropertyName("quote_ccy")] + public string QuoteAsset { get; set; } = string.Empty; + /// + /// Base asset amount + /// + [JsonPropertyName("base_ccy_amount")] + public decimal BaseAssetQuantity { get; set; } + /// + /// Quote asset amount + /// + [JsonPropertyName("quote_ccy_amount")] + public decimal QuoteAssetQuantity { get; set; } + /// + /// Liquidity percentage in AMM account + /// + [JsonPropertyName("liquidity_proportion")] + public decimal LiquidityProportion { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExBalance.cs b/CoinEx.Net/Objects/Models/V2/CoinExBalance.cs new file mode 100644 index 0000000..0e94d68 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExBalance.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Balance info + /// + public record CoinExBalance + { + /// + /// Asset name + /// + [JsonPropertyName("ccy")] + public string Asset { get; set; } = string.Empty; + /// + /// Available amount + /// + [JsonPropertyName("available")] + public decimal Available { get; set; } + /// + /// Frozen amount + /// + [JsonPropertyName("frozen")] + public decimal Frozen { get; set; } + /// + /// Total amount + /// + public decimal Total => Available + Frozen; + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExBalanceUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExBalanceUpdate.cs new file mode 100644 index 0000000..6f19273 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExBalanceUpdate.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + internal record CoinExBalanceUpdateWrapper + { + [JsonPropertyName("balance_list")] + public IEnumerable Balances { get; set; } = Array.Empty(); + } + + /// + /// Balance update + /// + public record CoinExBalanceUpdate + { + /// + /// Margin symbol + /// + [JsonPropertyName("margin_market")] + public string? MarginSymbol { get; set; } + /// + /// Asset name + /// + [JsonPropertyName("ccy")] + public string Asset { get; set; } = string.Empty; + /// + /// Available amount + /// + [JsonPropertyName("available")] + public decimal Available { get; set; } + /// + /// Frozen amount + /// + [JsonPropertyName("frozen")] + public decimal Frozen { get; set; } + /// + /// Update time + /// + [JsonPropertyName("updated_at")] + public DateTime UpdateTime { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExBorrow.cs b/CoinEx.Net/Objects/Models/V2/CoinExBorrow.cs new file mode 100644 index 0000000..da2ec69 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExBorrow.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; +using CoinEx.Net.Enums; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Borrow record + /// + public record CoinExBorrow + { + /// + /// Id + /// + [JsonPropertyName("borrow_id")] + public long BorrowId { get; set; } + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Asset + /// + [JsonPropertyName("ccy")] + public string Asset { get; set; } = string.Empty; + /// + /// Daily interest rate + /// + [JsonPropertyName("daily_interest_rate")] + public decimal DailyInterestRate { get; set; } + /// + /// Expire time + /// + [JsonPropertyName("expired_at")] + public DateTime ExireTime { get; set; } + /// + /// Borrow amount + /// + [JsonPropertyName("borrow_amount")] + public decimal BorrowQuantity { get; set; } + /// + /// Amount to repay + /// + [JsonPropertyName("to_repaid_amount")] + public decimal ToRepayQuantity { get; set; } + /// + /// Borrow status + /// + [JsonPropertyName("status")] + public BorrowStatus Status { get; set; } + /// + /// Is auto renewing + /// + [JsonPropertyName("is_auto_renew")] + public bool? IsAutoRenew { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExBorrowLimit.cs b/CoinEx.Net/Objects/Models/V2/CoinExBorrowLimit.cs new file mode 100644 index 0000000..5e6bce5 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExBorrowLimit.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Borrow limit info + /// + public record CoinExBorrowLimit + { + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + + /// + /// Asset + /// + [JsonPropertyName("ccy")] + public string Asset { get; set; } = string.Empty; + + /// + /// Daily interest rate + /// + [JsonPropertyName("daily_interest_rate")] + public decimal DailyInterestRate { get; set; } + /// + /// Max leverage + /// + [JsonPropertyName("leverage")] + public decimal Leverage { get; set; } + /// + /// Min amount borrowable + /// + [JsonPropertyName("min_amount")] + public decimal MinBorrowable { get; set; } + /// + /// Max amount borrowable + /// + [JsonPropertyName("max_amount")] + public decimal MaxBorrowable { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExCreditBalance.cs b/CoinEx.Net/Objects/Models/V2/CoinExCreditBalance.cs new file mode 100644 index 0000000..22d548e --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExCreditBalance.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Credit account balance + /// + public record CoinExCreditBalance + { + /// + /// Account assets + /// + [JsonPropertyName("equity")] + public decimal Equity { get; set; } + /// + /// To be repaid + /// + [JsonPropertyName("repaid")] + public decimal ToBeRepaid { get; set; } + /// + /// Current risk rate + /// + [JsonPropertyName("risk_rate")] + public decimal RiskRate { get; set; } + /// + /// Withdrawal risk rate + /// + [JsonPropertyName("withdrawal_risk")] + public decimal WithdrawalRiskRate { get; set; } + /// + /// Market value of available withdrawal + /// + [JsonPropertyName("withdrawal_value")] + public decimal WithdrawalValueAvailable { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExDeposit.cs b/CoinEx.Net/Objects/Models/V2/CoinExDeposit.cs new file mode 100644 index 0000000..7fb2028 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExDeposit.cs @@ -0,0 +1,85 @@ +using CoinEx.Net.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Deposit info + /// + public record CoinExDeposit + { + /// + /// Deposit id + /// + [JsonPropertyName("deposit_id")] + public long Id { get; set; } + /// + /// Creation time + /// + [JsonPropertyName("created_at")] + public DateTime CreateTime { get; set; } + /// + /// Transaction id + /// + [JsonPropertyName("tx_id")] + public string TransactionId { get; set; } = string.Empty; + /// + /// Asset + /// + [JsonPropertyName("ccy")] + public string Asset { get; set; } = string.Empty; + /// + /// Network + /// + [JsonPropertyName("chain")] + public string Network { get; set; } = string.Empty; + /// + /// Quantity deposited + /// + [JsonPropertyName("amount")] + public long Quantity { get; set; } + /// + /// Actual amount received + /// + [JsonPropertyName("actual_amount")] + public long QuantityCredited { get; set; } + /// + /// Deposit address + /// + [JsonPropertyName("to_address")] + public string DepositAddress { get; set; } = string.Empty; + /// + /// Amount of confirmations + /// + [JsonPropertyName("confirmations")] + public int Confirmations { get; set; } + /// + /// Status of the deposit + /// + [JsonPropertyName("status")] + public DepositStatus Status { get; set; } + /// + /// Blockchain explorer url for the transaction + /// + [JsonPropertyName("tx_explorer_url")] + public string TransactionExplorerUrl { get; set; } = string.Empty; + /// + /// Blockchain explorer url for the deposit address + /// + [JsonPropertyName("to_addr_explorer_url")] + public string DepositAddressExplorerUrl { get; set; } = string.Empty; + /// + /// Remark + /// + [JsonPropertyName("remark")] + public string Remark { get; set; } = string.Empty; + /// + /// Deposit method + /// + [JsonPropertyName("deposit_method")] + public string DepositMethod { get; set; } = string.Empty; + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExDepositAddress.cs b/CoinEx.Net/Objects/Models/V2/CoinExDepositAddress.cs new file mode 100644 index 0000000..f16d25f --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExDepositAddress.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Deposit addres + /// + public record CoinExDepositAddress + { + /// + /// Memo + /// + [JsonPropertyName("memo")] + public string? Memo { get; set; } + /// + /// Deposit address + /// + [JsonPropertyName("address")] + public string Address { get; set; } = string.Empty; + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExMarginBalance.cs b/CoinEx.Net/Objects/Models/V2/CoinExMarginBalance.cs new file mode 100644 index 0000000..da69da0 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExMarginBalance.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Margin balance info + /// + public record CoinExMarginBalance + { + /// + /// Margin account + /// + [JsonPropertyName("margin_account")] + public string MarginAccount { get; set; } = string.Empty; + + /// + /// Base asset + /// + [JsonPropertyName("base_ccy")] + public string BaseAsset { get; set; } = string.Empty; + /// + /// Quote asset + /// + [JsonPropertyName("quote_ccy")] + public string QuoteAsset { get; set; } = string.Empty; + + /// + /// Current risk rate + /// + [JsonPropertyName("risk_rate")] + public decimal? RiskRate { get; set; } + /// + /// Current liquidation price + /// + [JsonPropertyName("liq_price")] + public decimal? LiquidationPrice { get; set; } + /// + /// Available + /// + [JsonPropertyName("available")] + public CoinExMarginAssetsBalance Available { get; set; } = null!; + /// + /// Frozen + /// + [JsonPropertyName("frozen")] + public CoinExMarginAssetsBalance Frozen { get; set; } = null!; + /// + /// Repaid + /// + [JsonPropertyName("repaid")] + public CoinExMarginAssetsBalance Repaid { get; set; } = null!; + /// + /// Interest + /// + [JsonPropertyName("interest")] + public CoinExMarginAssetsBalance Interest { get; set; } = null!; + } + + /// + /// Assets balance info + /// + public record CoinExMarginAssetsBalance + { + /// + /// Base asset amount + /// + [JsonPropertyName("base_ccy")] + public decimal BaseAsset { get; set; } + /// + /// Quote asset amount + /// + [JsonPropertyName("quote_ccy")] + public decimal QuoteAsset { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExOrderUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExOrderUpdate.cs new file mode 100644 index 0000000..7648e10 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExOrderUpdate.cs @@ -0,0 +1,25 @@ +using CoinEx.Net.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Order update + /// + public record CoinExOrderUpdate + { + /// + /// Event that triggered the update + /// + [JsonPropertyName("event")] + public OrderUpdateType Event { get; set; } + /// + /// Order data + /// + [JsonPropertyName("order")] + public CoinExOrder Order { get; set; } = null!; // TODO check if model is indeed slightly different + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPaginated.cs b/CoinEx.Net/Objects/Models/V2/CoinExPaginated.cs new file mode 100644 index 0000000..27bc6ee --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExPaginated.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Paginated result + /// + public record CoinExPaginated + { + /// + /// Total results + /// + public int? Total { get; set; } + /// + /// Has next page + /// + public bool HasNext { get; set; } + /// + /// Page items + /// + public IEnumerable Items { get; set; } = Array.Empty(); + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs b/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs new file mode 100644 index 0000000..46cb4e4 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs @@ -0,0 +1,86 @@ +using CoinEx.Net.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Stop order info + /// + public record CoinExStopOrder + { + /// + /// Order id + /// + [JsonPropertyName("stop_id")] + public string StopOrderId { get; set; } = string.Empty; + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Account type + /// + [JsonPropertyName("market_type")] + public AccountType AccountType { get; set; } + /// + /// Asset the quantity is in + /// + [JsonPropertyName("ccy")] + public string QuantityAsset { get; set; } = string.Empty; + /// + /// Order side + /// + [JsonPropertyName("side")] + public OrderSide Side { get; set; } + /// + /// Order type + /// + [JsonPropertyName("type")] + public OrderTypeV2 Type { get; set; } + /// + /// Order quantity + /// + [JsonPropertyName("amount")] + public decimal Quantity { get; set; } + /// + /// Order price + /// + [JsonPropertyName("price")] + public decimal? Price { get; set; } + /// + /// Client order id + /// + [JsonPropertyName("client_id")] + public string? ClientOrderId { get; set; } + /// + /// Timestamp order was created + /// + [JsonPropertyName("created_at")] + public DateTime CreateTime { get; set; } + /// + /// Timestamp order was last updated + /// + [JsonPropertyName("updated_at")] + public DateTime? UpdateTime { get; set; } + + /// + /// Trigger price + /// + [JsonPropertyName("trigger_price")] + public decimal TriggerPrice { get; set; } + /// + /// Trigger direction + /// + [JsonPropertyName("trigger_direction")] + public TriggerDirection TriggerDirection { get; set; } + /// + /// Trigger price type + /// + [JsonPropertyName("trigger_price_type")] + public PriceType TriggerPriceType { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExStopOrderUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExStopOrderUpdate.cs new file mode 100644 index 0000000..fcb5eba --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExStopOrderUpdate.cs @@ -0,0 +1,25 @@ +using CoinEx.Net.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Stop order update + /// + public record CoinExStopOrderUpdate + { + /// + /// Event that triggered the update + /// + [JsonPropertyName("event")] + public StopOrderUpdateType Event { get; set; } // TODO CHeck if int or string return + /// + /// Order data + /// + [JsonPropertyName("order")] + public CoinExStopOrder Order { get; set; } = null!; // TODO check if model is indeed slightly different + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExTradeFee.cs b/CoinEx.Net/Objects/Models/V2/CoinExTradeFee.cs new file mode 100644 index 0000000..d0c7c49 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExTradeFee.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Trading fee info + /// + public record CoinExTradeFee + { + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Fee for maker trades + /// + [JsonPropertyName("maker_rate")] + public decimal MakerFeeRate { get; set; } + /// + /// Fee for taker trades + /// + [JsonPropertyName("taker_rate")] + public decimal TakerFeeRate { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExUserTrade.cs b/CoinEx.Net/Objects/Models/V2/CoinExUserTrade.cs new file mode 100644 index 0000000..fcdd9f4 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExUserTrade.cs @@ -0,0 +1,55 @@ +using CoinEx.Net.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// User trade info + /// + public record CoinExUserTrade + { + /// + /// Trade id + /// + [JsonPropertyName("deal_id")] + public long Id { get; set; } + /// + /// Trade time + /// + [JsonPropertyName("created_at")] + public DateTime CreateTime { get; set; } + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Trade side + /// + [JsonPropertyName("side")] + public OrderSide Side { get; set; } + /// + /// Order id + /// + [JsonPropertyName("order_id")] + public long OrderId { get; set; } + /// + /// Margin symbol + /// + [JsonPropertyName("margin_market")] + public string? MarginSymbol { get; set; } + /// + /// Trade price + /// + [JsonPropertyName("price")] + public decimal Price { get; set; } + /// + /// Quantity traded + /// + [JsonPropertyName("amount")] + public decimal Quantity { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Sockets/V2/Queries/CoinExQuery.cs b/CoinEx.Net/Objects/Sockets/V2/Queries/CoinExQuery.cs index 7f4c3e6..a90447b 100644 --- a/CoinEx.Net/Objects/Sockets/V2/Queries/CoinExQuery.cs +++ b/CoinEx.Net/Objects/Sockets/V2/Queries/CoinExQuery.cs @@ -8,7 +8,7 @@ namespace CoinEx.Net.Objects.Sockets.V2.Queries { - internal class CoinExQuery : Query + internal class CoinExQuery : Query { public override HashSet ListenerIdentifiers { get; set; } diff --git a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs index d7c9aeb..2f3125f 100644 --- a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs @@ -20,7 +20,7 @@ internal class CoinExSubscription : Subscription> _handler; public override HashSet ListenerIdentifiers { get; set; } - public CoinExSubscription(ILogger logger, string topic, IEnumerable? symbols, Dictionary parameters, Action> handler) : base(logger, false) + public CoinExSubscription(ILogger logger, string topic, IEnumerable? symbols, Dictionary parameters, Action> handler, bool authenticated = false) : base(logger, authenticated) { _topic = topic; _symbols = symbols; @@ -42,9 +42,9 @@ public override CallResult DoHandleMessage(SocketConnection connection, DataEven public override Type? GetMessageType(IMessageAccessor message) => typeof(CoinExSocketUpdate); public override Query? GetSubQuery(SocketConnection connection) - => new CoinExQuery(_topic + ".subscribe", _parameters, false, 1); + => new CoinExQuery(_topic + ".subscribe", _parameters, false, 1); public override Query? GetUnsubQuery() - => new CoinExQuery(_topic + ".unsubscribe", _parameters, false, 1); + => new CoinExQuery(_topic + ".unsubscribe", _parameters, false, 1); } } diff --git a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTickerSubscription.cs b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTickerSubscription.cs index 491d87b..49092f5 100644 --- a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTickerSubscription.cs +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTickerSubscription.cs @@ -41,9 +41,9 @@ public override CallResult DoHandleMessage(SocketConnection connection, DataEven public override Type? GetMessageType(IMessageAccessor message) => typeof(CoinExSocketUpdate); public override Query? GetSubQuery(SocketConnection connection) - => new CoinExQuery("state.subscribe", _parameters, false, 1); + => new CoinExQuery("state.subscribe", _parameters, false, 1); public override Query? GetUnsubQuery() - => new CoinExQuery("state.unsubscribe", _parameters, false, 1); + => new CoinExQuery("state.unsubscribe", _parameters, false, 1); } } diff --git a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs index c28b02d..484259c 100644 --- a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs @@ -41,9 +41,9 @@ public override CallResult DoHandleMessage(SocketConnection connection, DataEven public override Type? GetMessageType(IMessageAccessor message) => typeof(CoinExSocketUpdate); public override Query? GetSubQuery(SocketConnection connection) - => new CoinExQuery("deals.subscribe", _parameters, false, 1); + => new CoinExQuery("deals.subscribe", _parameters, false, 1); public override Query? GetUnsubQuery() - => new CoinExQuery("deals.unsubscribe", _parameters, false, 1); + => new CoinExQuery("deals.unsubscribe", _parameters, false, 1); } } diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..12668fb --- /dev/null +++ b/docs/README.md @@ -0,0 +1 @@ +Source for https://jkorf.github.io/CoinEx.Net \ No newline at end of file From bc59485774f7b1b05cf294226310254f01f4f105 Mon Sep 17 00:00:00 2001 From: JKorf Date: Thu, 4 Apr 2024 09:32:10 +0200 Subject: [PATCH 08/17] Merge --- .../CoinExRestClientSpotApiV1ExchangeData.cs | 5 ----- .../SpotApiV1/CoinExRestClientSpotApiV1Trading.cs | 10 ---------- .../SpotApiV1/CoinExSocketClientSpotApiV1.cs | 13 ------------- CoinEx.Net/CoinEx.Net.xml | 6 ------ .../SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs | 2 -- 5 files changed, 36 deletions(-) diff --git a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1ExchangeData.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1ExchangeData.cs index 1e48b44..9167fa6 100644 --- a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1ExchangeData.cs +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1ExchangeData.cs @@ -60,7 +60,6 @@ public async Task>> GetAsset /// public async Task> GetTickerAsync(string symbol, CancellationToken ct = default) { - symbol.ValidateCoinExSymbol(); var parameters = new Dictionary { { "market", symbol } @@ -85,7 +84,6 @@ public async Task> GetTickersAsync(Cancell /// public async Task> GetOrderBookAsync(string symbol, int mergeDepth, int? limit = null, CancellationToken ct = default) { - symbol.ValidateCoinExSymbol(); mergeDepth.ValidateIntBetween(nameof(mergeDepth), 0, 8); limit?.ValidateIntValues(nameof(limit), 5, 10, 20); @@ -102,8 +100,6 @@ public async Task> GetOrderBookAsync(string symbo /// public async Task>> GetTradeHistoryAsync(string symbol, long? fromId = null, CancellationToken ct = default) { - symbol.ValidateCoinExSymbol(); - var parameters = new Dictionary { { "market", symbol } @@ -132,7 +128,6 @@ public async Task>> GetSymbolInfo /// public async Task>> GetKlinesAsync(string symbol, KlineInterval interval, int? limit = null, CancellationToken ct = default) { - symbol.ValidateCoinExSymbol(); limit?.ValidateIntBetween(nameof(limit), 1, 1000); var parameters = new Dictionary { diff --git a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Trading.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Trading.cs index 1530f09..a774fd3 100644 --- a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Trading.cs +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Trading.cs @@ -55,8 +55,6 @@ public async Task> PlaceOrderAsync( string? sourceId = null, CancellationToken ct = default) { - symbol.ValidateCoinExSymbol(); - var endpoint = ""; if (type == OrderType.Limit) endpoint = PlaceLimitOrderEndpoint; @@ -99,7 +97,6 @@ public async Task> PlaceOrderAsync( /// public async Task>> GetOpenOrdersAsync(string? symbol = null, int? page = null, int? limit = null, CancellationToken ct = default) { - symbol?.ValidateCoinExSymbol(); limit?.ValidateIntBetween(nameof(limit), 1, 100); var parameters = new Dictionary { @@ -115,7 +112,6 @@ public async Task>> GetOpenOrdersAs /// public async Task>> GetOpenStopOrdersAsync(string symbol, int? page = null, int? limit = null, CancellationToken ct = default) { - symbol?.ValidateCoinExSymbol(); limit?.ValidateIntBetween(nameof(limit), 1, 100); var parameters = new Dictionary { @@ -129,7 +125,6 @@ public async Task>> GetOpenStopOrde /// public async Task>> GetClosedOrdersAsync(string symbol, int? page = null, int? limit = null, CancellationToken ct = default) { - symbol.ValidateCoinExSymbol(); limit?.ValidateIntBetween(nameof(limit), 1, 100); var parameters = new Dictionary { @@ -144,7 +139,6 @@ public async Task>> GetClosedOrders /// public async Task> GetOrderAsync(string symbol, long orderId, CancellationToken ct = default) { - symbol.ValidateCoinExSymbol(); var parameters = new Dictionary { { "market", symbol }, @@ -171,7 +165,6 @@ public async Task>> GetOrderTr /// public async Task>> GetUserTradesAsync(string symbol, int? page = null, int? limit = null, CancellationToken ct = default) { - symbol.ValidateCoinExSymbol(); limit?.ValidateIntBetween(nameof(limit), 1, 100); var parameters = new Dictionary { @@ -186,7 +179,6 @@ public async Task>> Ge /// public async Task> CancelOrderAsync(string symbol, long orderId, CancellationToken ct = default) { - symbol.ValidateCoinExSymbol(); var parameters = new Dictionary { { "market", symbol }, @@ -202,7 +194,6 @@ public async Task> CancelOrderAsync(string symbol, lo /// public async Task CancelAllOrdersAsync(string symbol, CancellationToken ct = default) { - symbol.ValidateCoinExSymbol(); var parameters = new Dictionary { { "market", symbol }, @@ -214,7 +205,6 @@ public async Task CancelAllOrdersAsync(string symbol, Cancellatio /// public async Task CancelAllStopOrdersAsync(string symbol, CancellationToken ct = default) { - symbol.ValidateCoinExSymbol(); var parameters = new Dictionary { { "market", symbol }, diff --git a/CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApiV1.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApiV1.cs index 0d4f189..6d15454 100644 --- a/CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApiV1.cs +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApiV1.cs @@ -112,7 +112,6 @@ public async Task> GetServerTimeAsync() /// public async Task> GetTickerAsync(string symbol, int cyclePeriod) { - symbol.ValidateCoinExSymbol(); var query = await QueryAsync(new CoinExQuery("state.query", new object[] { symbol, cyclePeriod })).ConfigureAwait(false); return query.As(query.Data?.Result); } @@ -120,7 +119,6 @@ public async Task> GetTickerAsync(string sym /// public async Task> GetOrderBookAsync(string symbol, int limit, int mergeDepth) { - symbol.ValidateCoinExSymbol(); mergeDepth.ValidateIntBetween(nameof(mergeDepth), 0, 8); limit.ValidateIntValues(nameof(limit), 5, 10, 20); @@ -131,8 +129,6 @@ public async Task> GetOrderBookAsync(string sy /// public async Task>> GetTradeHistoryAsync(string symbol, int? limit = null, int? fromId = null) { - symbol.ValidateCoinExSymbol(); - var query = await QueryAsync(new CoinExQuery>("deals.query", new object[] { symbol, limit ?? 10, fromId ?? 0 }, false)).ConfigureAwait(false); return query.As>(query.Data?.Result); } @@ -140,8 +136,6 @@ public async Task>> GetTradeHist /// public async Task>> GetKlinesAsync(string symbol, KlineInterval interval) { - symbol.ValidateCoinExSymbol(); - var startTime = DateTimeConverter.ConvertToSeconds(DateTime.UtcNow.AddDays(-1)); var endTime = DateTimeConverter.ConvertToSeconds(DateTime.UtcNow); var query = await QueryAsync(new CoinExQuery>("kline.query", new object[] { symbol, startTime, endTime, interval.ToSeconds() }, false)).ConfigureAwait(false); @@ -158,8 +152,6 @@ public async Task>> GetBalancesAsyn /// public async Task>> GetOpenOrdersAsync(string symbol, OrderSide? side = null, int? offset = null, int? limit = null) { - symbol.ValidateCoinExSymbol(); - var query = await QueryAsync(new CoinExQuery>("order.query", new object[] { symbol, int.Parse(JsonConvert.SerializeObject(side ?? OrderSide.Either, new OrderSideIntConverter(false))), offset ?? 0, limit ?? 10 }, true)).ConfigureAwait(false); return query.As>(query.Data?.Result); } @@ -167,8 +159,6 @@ public async Task>> GetOpe /// public async Task> SubscribeToTickerUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default) { - symbol.ValidateCoinExSymbol(); - var subscription = new CoinExStateSubscription(_logger, symbol, new object[] { symbol }, x => onMessage(x.As(x.Data.Single()))); return await SubscribeAsync(subscription, ct).ConfigureAwait(false); } @@ -183,7 +173,6 @@ public async Task> SubscribeToAllTickerUpdatesAsy /// public async Task> SubscribeToOrderBookUpdatesAsync(string symbol, int limit, int mergeDepth, Action> onMessage, CancellationToken ct = default) { - symbol.ValidateCoinExSymbol(); mergeDepth.ValidateIntBetween(nameof(mergeDepth), 0, 8); limit.ValidateIntValues(nameof(limit), 5, 10, 20); @@ -194,8 +183,6 @@ public async Task> SubscribeToOrderBookUpdatesAsy /// public async Task> SubscribeToTradeUpdatesAsync(string symbol, Action>> onMessage, CancellationToken ct = default) { - symbol.ValidateCoinExSymbol(); - var subscription = new CoinExDealsSubscription(_logger, symbol, new object[] { symbol }, onMessage); return await SubscribeAsync(subscription, ct).ConfigureAwait(false); } diff --git a/CoinEx.Net/CoinEx.Net.xml b/CoinEx.Net/CoinEx.Net.xml index ecb7cf0..d7e3aa0 100644 --- a/CoinEx.Net/CoinEx.Net.xml +++ b/CoinEx.Net/CoinEx.Net.xml @@ -1029,12 +1029,6 @@ Extension methods specific to using the CoinEx API - - - Validate the string is a valid CoinEx symbol. - - string to validate - Client for accessing the CoinEx API. diff --git a/CoinEx.Net/SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs b/CoinEx.Net/SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs index 867a716..9d090ea 100644 --- a/CoinEx.Net/SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs +++ b/CoinEx.Net/SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs @@ -50,8 +50,6 @@ public CoinExSpotSymbolOrderBook(string symbol, optionsDelegate(options); Initialize(options); - symbol.ValidateCoinExSymbol(); - _strictLevels = false; _sequencesAreConsecutive = false; _initialDataTimeout = options?.InitialDataTimeout ?? TimeSpan.FromSeconds(30); From 537d2167a545d810f7e3a3d1ea8afbf9c99afb3d Mon Sep 17 00:00:00 2001 From: JKorf Date: Thu, 4 Apr 2024 10:08:45 +0200 Subject: [PATCH 09/17] Versioning --- CoinEx.Net.UnitTests/CoinExClientTests.cs | 6 +- CoinEx.Net/Clients/CoinExRestClient.cs | 12 +- CoinEx.Net/Clients/CoinExSocketClient.cs | 14 +- ...potApiV1.cs => CoinExRestClientSpotApi.cs} | 22 +- ...t.cs => CoinExRestClientSpotApiAccount.cs} | 10 +- ...=> CoinExRestClientSpotApiExchangeData.cs} | 11 +- ...g.cs => CoinExRestClientSpotApiTrading.cs} | 11 +- ...tApiV1.cs => CoinExSocketClientSpotApi.cs} | 12 +- .../CoinExRestClientSpotApi.cs | 19 +- .../CoinExRestClientSpotApiAccount.cs | 6 +- .../CoinExRestClientSpotApiExchangeData.cs | 6 +- .../CoinExRestClientSpotApiTrading.cs | 6 +- .../CoinExSocketClientSpotApi.cs | 4 +- CoinEx.Net/CoinEx.Net.xml | 534 +++++++++--------- .../ServiceCollectionExtensions.cs | 2 +- .../Interfaces/Clients/ICoinExRestClient.cs | 9 +- .../Interfaces/Clients/ICoinExSocketClient.cs | 12 +- .../ICoinExRestClientSpotApi.cs} | 10 +- ....cs => ICoinExRestClientSpotApiAccount.cs} | 4 +- ...> ICoinExRestClientSpotApiExchangeData.cs} | 4 +- ....cs => ICoinExRestClientSpotApiTrading.cs} | 4 +- ...ApiV1.cs => ICoinExSocketClientSpotApi.cs} | 4 +- .../ICoinExRestClientSpotApi.cs} | 10 +- .../ICoinExRestClientSpotApiAccount.cs} | 4 +- .../ICoinExRestClientSpotApiExchangeData.cs} | 4 +- .../ICoinExRestClientSpotApiTrading.cs} | 4 +- .../ICoinExSocketClientSpotApi.cs | 2 +- .../Objects/Models/V2/CoinExTickerUpdate.cs | 2 +- CoinEx.Net/Objects/Models/V2/CoinExTrade.cs | 2 +- .../Objects/Sockets/V2/CoinExSocketUpdate.cs | 4 +- .../CoinExSpotSymbolOrderBook.cs | 3 +- 31 files changed, 378 insertions(+), 379 deletions(-) rename CoinEx.Net/Clients/SpotApiV1/{CoinExRestClientSpotApiV1.cs => CoinExRestClientSpotApi.cs} (96%) rename CoinEx.Net/Clients/SpotApiV1/{CoinExRestClientSpotApiV1Account.cs => CoinExRestClientSpotApiAccount.cs} (93%) rename CoinEx.Net/Clients/SpotApiV1/{CoinExRestClientSpotApiV1ExchangeData.cs => CoinExRestClientSpotApiExchangeData.cs} (94%) rename CoinEx.Net/Clients/SpotApiV1/{CoinExRestClientSpotApiV1Trading.cs => CoinExRestClientSpotApiTrading.cs} (96%) rename CoinEx.Net/Clients/SpotApiV1/{CoinExSocketClientSpotApiV1.cs => CoinExSocketClientSpotApi.cs} (96%) rename CoinEx.Net/Clients/{SpotApi => SpotApiV2}/CoinExRestClientSpotApi.cs (97%) rename CoinEx.Net/Clients/{SpotApi => SpotApiV2}/CoinExRestClientSpotApiAccount.cs (97%) rename CoinEx.Net/Clients/{SpotApi => SpotApiV2}/CoinExRestClientSpotApiExchangeData.cs (95%) rename CoinEx.Net/Clients/{SpotApi => SpotApiV2}/CoinExRestClientSpotApiTrading.cs (98%) rename CoinEx.Net/Clients/{SpotApi => SpotApiV2}/CoinExSocketClientSpotApi.cs (99%) rename CoinEx.Net/Interfaces/Clients/{SpotApi/ICoinExClientSpotApi.cs => SpotApiV1/ICoinExRestClientSpotApi.cs} (71%) rename CoinEx.Net/Interfaces/Clients/SpotApiV1/{ICoinExClientSpotApiV1Account.cs => ICoinExRestClientSpotApiAccount.cs} (97%) rename CoinEx.Net/Interfaces/Clients/SpotApiV1/{ICoinExClientSpotApiV1ExchangeData.cs => ICoinExRestClientSpotApiExchangeData.cs} (98%) rename CoinEx.Net/Interfaces/Clients/SpotApiV1/{ICoinExClientSpotApiV1Trading.cs => ICoinExRestClientSpotApiTrading.cs} (98%) rename CoinEx.Net/Interfaces/Clients/SpotApiV1/{ICoinExSocketClientSpotApiV1.cs => ICoinExSocketClientSpotApi.cs} (98%) rename CoinEx.Net/Interfaces/Clients/{SpotApiV1/ICoinExClientSpotApiV1.cs => SpotApiV2/ICoinExRestClientSpotApi.cs} (71%) rename CoinEx.Net/Interfaces/Clients/{SpotApi/ICoinExClientSpotApiAccount.cs => SpotApiV2/ICoinExRestClientSpotApiAccount.cs} (98%) rename CoinEx.Net/Interfaces/Clients/{SpotApi/ICoinExClientSpotApiExchangeData.cs => SpotApiV2/ICoinExRestClientSpotApiExchangeData.cs} (97%) rename CoinEx.Net/Interfaces/Clients/{SpotApi/ICoinExClientSpotApiTrading.cs => SpotApiV2/ICoinExRestClientSpotApiTrading.cs} (99%) rename CoinEx.Net/Interfaces/Clients/{SpotApi => SpotApiV2}/ICoinExSocketClientSpotApi.cs (99%) diff --git a/CoinEx.Net.UnitTests/CoinExClientTests.cs b/CoinEx.Net.UnitTests/CoinExClientTests.cs index 5240fc0..7d0f67d 100644 --- a/CoinEx.Net.UnitTests/CoinExClientTests.cs +++ b/CoinEx.Net.UnitTests/CoinExClientTests.cs @@ -16,10 +16,10 @@ using CryptoExchange.Net.Objects; using CryptoExchange.Net.Sockets; using CoinEx.Net.Clients; -using CoinEx.Net.Clients.SpotApi; using CoinEx.Net.ExtensionMethods; using CryptoExchange.Net.Objects.Sockets; using NUnit.Framework.Legacy; +using CoinEx.Net.Clients.SpotApiV1; namespace CoinEx.Net.UnitTests { @@ -41,7 +41,7 @@ public async Task ReceivingError_Should_ReturnErrorAndNotSuccess() TestHelpers.SetResponse((CoinExRestClient)client, JsonConvert.SerializeObject(resultObj)); // act - var result = await client.SpotApiV1.ExchangeData.GetAssetsAsync(); + var result = await client.SpotApi.ExchangeData.GetAssetsAsync(); // assert ClassicAssert.IsFalse(result.Success); @@ -58,7 +58,7 @@ public async Task ReceivingHttpErrorWithNoJson_Should_ReturnErrorAndNotSuccess() TestHelpers.SetResponse((CoinExRestClient)client, "", System.Net.HttpStatusCode.BadRequest); // act - var result = await client.SpotApiV1.ExchangeData.GetAssetsAsync(); + var result = await client.SpotApi.ExchangeData.GetAssetsAsync(); // assert ClassicAssert.IsFalse(result.Success); diff --git a/CoinEx.Net/Clients/CoinExRestClient.cs b/CoinEx.Net/Clients/CoinExRestClient.cs index 9c7b415..763307c 100644 --- a/CoinEx.Net/Clients/CoinExRestClient.cs +++ b/CoinEx.Net/Clients/CoinExRestClient.cs @@ -1,6 +1,4 @@ using CoinEx.Net.Interfaces.Clients; -using CoinEx.Net.Interfaces.Clients.SpotApi; -using CoinEx.Net.Clients.SpotApi; using CryptoExchange.Net.Authentication; using Microsoft.Extensions.Logging; using System.Net.Http; @@ -15,9 +13,9 @@ public class CoinExRestClient : BaseRestClient, ICoinExRestClient { #region Api clients /// - public ICoinExClientSpotApiV1 SpotApiV1 { get; } + public Interfaces.Clients.SpotApiV1.ICoinExRestClientSpotApi SpotApi { get; } /// - public ICoinExClientSpotApi SpotApi { get; } + public Interfaces.Clients.SpotApiV2.ICoinExRestClientSpotApi SpotApiV2 { get; } #endregion #region ctor @@ -43,8 +41,8 @@ public CoinExRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, A optionsDelegate(options); Initialize(options); - SpotApi = AddApiClient(new CoinExRestClientSpotApi(_logger, httpClient, options)); - SpotApiV1 = AddApiClient(new CoinExRestClientSpotApiV1(_logger, httpClient, options)); + SpotApi = AddApiClient(new SpotApiV1.CoinExRestClientSpotApi(_logger, httpClient, options)); + SpotApiV2 = AddApiClient(new SpotApiV2.CoinExRestClientSpotApi(_logger, httpClient, options)); } #endregion @@ -64,7 +62,7 @@ public static void SetDefaultOptions(Action optionsDelegate) public void SetApiCredentials(ApiCredentials credentials) { SpotApi.SetApiCredentials(credentials); - SpotApiV1.SetApiCredentials(credentials); + SpotApiV2.SetApiCredentials(credentials); } #endregion } diff --git a/CoinEx.Net/Clients/CoinExSocketClient.cs b/CoinEx.Net/Clients/CoinExSocketClient.cs index b602bbe..3c4ece6 100644 --- a/CoinEx.Net/Clients/CoinExSocketClient.cs +++ b/CoinEx.Net/Clients/CoinExSocketClient.cs @@ -1,6 +1,4 @@ using CoinEx.Net.Interfaces.Clients; -using CoinEx.Net.Interfaces.Clients.SpotApi; -using CoinEx.Net.Clients.SpotApi; using CryptoExchange.Net.Authentication; using Microsoft.Extensions.Logging; using System; @@ -15,8 +13,9 @@ public class CoinExSocketClient : BaseSocketClient, ICoinExSocketClient #region Api clients /// - public ICoinExSocketClientSpotApi SpotApi { get; } - public ICoinExSocketClientSpotApiV1 SpotApiV1 { get; } + public Interfaces.Clients.SpotApiV2.ICoinExSocketClientSpotApi SpotApiV2 { get; } + /// + public Interfaces.Clients.SpotApiV1.ICoinExSocketClientSpotApi SpotApi { get; } #endregion @@ -49,8 +48,8 @@ public CoinExSocketClient(Action optionsDelegate, ILoggerFa optionsDelegate(options); Initialize(options); - SpotApi = AddApiClient(new CoinExSocketClientSpotApi(_logger, options)); - SpotApiV1 = AddApiClient(new CoinExSocketClientSpotApiV1(_logger, options)); + SpotApi = AddApiClient(new SpotApiV1.CoinExSocketClientSpotApi(_logger, options)); + SpotApiV2 = AddApiClient(new SpotApiV2.CoinExSocketClientSpotApi(_logger, options)); } #endregion @@ -68,7 +67,8 @@ public static void SetDefaultOptions(Action optionsDelegate /// public void SetApiCredentials(ApiCredentials credentials) { - SpotApiV1.SetApiCredentials(credentials); + SpotApiV2.SetApiCredentials(credentials); + SpotApi.SetApiCredentials(credentials); } } } diff --git a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApi.cs similarity index 96% rename from CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1.cs rename to CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApi.cs index 6f2d93a..029bb0f 100644 --- a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1.cs +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApi.cs @@ -10,7 +10,6 @@ using CoinEx.Net.Enums; using CoinEx.Net.Objects.Internal; using CoinEx.Net.Objects.Models; -using CoinEx.Net.Interfaces.Clients.SpotApi; using CryptoExchange.Net.CommonObjects; using System.Globalization; using CryptoExchange.Net.Interfaces.CommonClients; @@ -20,11 +19,12 @@ using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Converters.MessageParsing; using CryptoExchange.Net.Clients; +using CoinEx.Net.Interfaces.Clients.SpotApiV1; -namespace CoinEx.Net.Clients.SpotApi +namespace CoinEx.Net.Clients.SpotApiV1 { - /// - public class CoinExRestClientSpotApiV1 : RestApiClient, ICoinExClientSpotApiV1, ISpotClient + /// + public class CoinExRestClientSpotApi : RestApiClient, ICoinExRestClientSpotApi, ISpotClient { #region fields /// @@ -45,22 +45,22 @@ public class CoinExRestClientSpotApiV1 : RestApiClient, ICoinExClientSpotApiV1, #region Api clients /// - public ICoinExClientSpotApiV1Account Account { get; } + public ICoinExRestClientSpotApiAccount Account { get; } /// - public ICoinExClientSpotApiV1ExchangeData ExchangeData { get; } + public ICoinExRestClientSpotApiExchangeData ExchangeData { get; } /// - public ICoinExClientSpotApiV1Trading Trading { get; } + public ICoinExRestClientSpotApiTrading Trading { get; } #endregion internal readonly string _brokerId; #region ctor - internal CoinExRestClientSpotApiV1(ILogger logger, HttpClient? httpClient, CoinExRestOptions options) : + internal CoinExRestClientSpotApi(ILogger logger, HttpClient? httpClient, CoinExRestOptions options) : base(logger, httpClient, options.Environment.RestBaseAddress, options, options.SpotOptions) { - Account = new CoinExRestClientSpotApiV1Account(this); - ExchangeData = new CoinExRestClientSpotApiV1ExchangeData(this); - Trading = new CoinExRestClientSpotApiV1Trading(this); + Account = new CoinExRestClientSpotApiAccount(this); + ExchangeData = new CoinExRestClientSpotApiExchangeData(this); + Trading = new CoinExRestClientSpotApiTrading(this); ParameterPositions[HttpMethod.Delete] = HttpMethodParameterPosition.InUri; diff --git a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Account.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiAccount.cs similarity index 93% rename from CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Account.cs rename to CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiAccount.cs index 1ec8bd2..82e84b8 100644 --- a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Account.cs +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiAccount.cs @@ -6,12 +6,12 @@ using System.Threading; using System.Threading.Tasks; using CoinEx.Net.Objects.Models; -using CoinEx.Net.Interfaces.Clients.SpotApi; +using CoinEx.Net.Interfaces.Clients.SpotApiV1; -namespace CoinEx.Net.Clients.SpotApi +namespace CoinEx.Net.Clients.SpotApiV1 { /// - public class CoinExRestClientSpotApiV1Account : ICoinExClientSpotApiV1Account + public class CoinExRestClientSpotApiAccount : ICoinExRestClientSpotApiAccount { private const string AccountInfoEndpoint = "balance/info"; private const string WithdrawalHistoryEndpoint = "balance/coin/withdraw"; @@ -21,9 +21,9 @@ public class CoinExRestClientSpotApiV1Account : ICoinExClientSpotApiV1Account private const string DepositAddressEndpoint = "balance/deposit/address/"; - private readonly CoinExRestClientSpotApiV1 _baseClient; + private readonly CoinExRestClientSpotApi _baseClient; - internal CoinExRestClientSpotApiV1Account(CoinExRestClientSpotApiV1 baseClient) + internal CoinExRestClientSpotApiAccount(CoinExRestClientSpotApi baseClient) { _baseClient = baseClient; } diff --git a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1ExchangeData.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiExchangeData.cs similarity index 94% rename from CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1ExchangeData.cs rename to CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiExchangeData.cs index 9167fa6..d0d422a 100644 --- a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1ExchangeData.cs +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiExchangeData.cs @@ -8,13 +8,12 @@ using System.Threading; using System.Threading.Tasks; using CoinEx.Net.Objects.Models; -using CoinEx.Net.Interfaces.Clients.SpotApi; -using CoinEx.Net.ExtensionMethods; +using CoinEx.Net.Interfaces.Clients.SpotApiV1; -namespace CoinEx.Net.Clients.SpotApi +namespace CoinEx.Net.Clients.SpotApiV1 { /// - public class CoinExRestClientSpotApiV1ExchangeData : ICoinExClientSpotApiV1ExchangeData + public class CoinExRestClientSpotApiExchangeData : ICoinExRestClientSpotApiExchangeData { private const string AssetConfigEndpoint = "common/asset/config"; private const string CurrencyRateEndpoint = "common/currency/rate"; @@ -28,9 +27,9 @@ public class CoinExRestClientSpotApiV1ExchangeData : ICoinExClientSpotApiV1Excha private const string MarketInfoEndpoint = "market/info"; private const string MiningDifficultyEndpoint = "order/mining/difficulty"; - private readonly CoinExRestClientSpotApiV1 _baseClient; + private readonly CoinExRestClientSpotApi _baseClient; - internal CoinExRestClientSpotApiV1ExchangeData(CoinExRestClientSpotApiV1 baseClient) + internal CoinExRestClientSpotApiExchangeData(CoinExRestClientSpotApi baseClient) { _baseClient = baseClient; } diff --git a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Trading.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiTrading.cs similarity index 96% rename from CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Trading.cs rename to CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiTrading.cs index a774fd3..259136e 100644 --- a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiV1Trading.cs +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiTrading.cs @@ -10,14 +10,13 @@ using System.Threading; using System.Threading.Tasks; using CoinEx.Net.Objects.Models; -using CoinEx.Net.Interfaces.Clients.SpotApi; using CryptoExchange.Net.CommonObjects; -using CoinEx.Net.ExtensionMethods; +using CoinEx.Net.Interfaces.Clients.SpotApiV1; -namespace CoinEx.Net.Clients.SpotApi +namespace CoinEx.Net.Clients.SpotApiV1 { /// - public class CoinExRestClientSpotApiV1Trading : ICoinExClientSpotApiV1Trading + public class CoinExRestClientSpotApiTrading : ICoinExRestClientSpotApiTrading { private const string PlaceLimitOrderEndpoint = "order/limit"; private const string PlaceMarketOrderEndpoint = "order/market"; @@ -33,9 +32,9 @@ public class CoinExRestClientSpotApiV1Trading : ICoinExClientSpotApiV1Trading private const string CancelOrderEndpoint = "order/pending"; private const string CancelStopOrderEndpoint = "order/stop/pending"; - private readonly CoinExRestClientSpotApiV1 _baseClient; + private readonly CoinExRestClientSpotApi _baseClient; - internal CoinExRestClientSpotApiV1Trading(CoinExRestClientSpotApiV1 baseClient) + internal CoinExRestClientSpotApiTrading(CoinExRestClientSpotApi baseClient) { _baseClient = baseClient; } diff --git a/CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApiV1.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApi.cs similarity index 96% rename from CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApiV1.cs rename to CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApi.cs index 6d15454..c7b7cd7 100644 --- a/CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApiV1.cs +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApi.cs @@ -14,26 +14,24 @@ using CoinEx.Net.Objects.Internal; using CoinEx.Net.Objects.Models; using CoinEx.Net.Objects.Models.Socket; -using CoinEx.Net.Interfaces.Clients.SpotApi; using CoinEx.Net.Objects.Options; using CryptoExchange.Net.Objects.Sockets; using CoinEx.Net.Objects.Sockets.Queries; -using CryptoExchange.Net.Converters; using CoinEx.Net.Objects.Sockets; using CoinEx.Net.Objects.Sockets.Subscriptions.Deals; using CoinEx.Net.Objects.Sockets.Subscriptions.Balance; using CoinEx.Net.Objects.Sockets.Subscriptions.Depth; using CoinEx.Net.Objects.Sockets.Subscriptions.State; using CoinEx.Net.Objects.Sockets.Subscriptions.Orders; -using CoinEx.Net.ExtensionMethods; using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Converters.MessageParsing; using CryptoExchange.Net.Clients; +using CoinEx.Net.Interfaces.Clients.SpotApiV1; -namespace CoinEx.Net.Clients.SpotApi +namespace CoinEx.Net.Clients.SpotApiV1 { - /// - public class CoinExSocketClientSpotApiV1 : SocketApiClient, ICoinExSocketClientSpotApiV1 + /// + public class CoinExSocketClientSpotApi : SocketApiClient, ICoinExSocketClientSpotApi { #region fields /// @@ -49,7 +47,7 @@ public class CoinExSocketClientSpotApiV1 : SocketApiClient, ICoinExSocketClientS /// /// Create a new instance of CoinExSocketClient with default options /// - internal CoinExSocketClientSpotApiV1(ILogger logger, CoinExSocketOptions options) + internal CoinExSocketClientSpotApi(ILogger logger, CoinExSocketOptions options) : base(logger, options.Environment.SocketBaseAddress, options, options.SpotOptions) { RegisterPeriodicQuery("Ping", TimeSpan.FromMinutes(1), q => (new CoinExQuery("server.ping", new object[] { })), null); diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs similarity index 97% rename from CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs rename to CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs index d6fd78c..6cc576a 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs @@ -1,20 +1,14 @@ using CryptoExchange.Net; using System; using System.Collections.Generic; -using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using CryptoExchange.Net.Objects; using CryptoExchange.Net.Authentication; -using CoinEx.Net.Enums; using CoinEx.Net.Objects.Internal; -using CoinEx.Net.Objects.Models; -using CoinEx.Net.Interfaces.Clients.SpotApi; using CryptoExchange.Net.CommonObjects; -using System.Globalization; using CryptoExchange.Net.Interfaces.CommonClients; -using Newtonsoft.Json.Linq; using Microsoft.Extensions.Logging; using CoinEx.Net.Objects.Options; using CryptoExchange.Net.Interfaces; @@ -22,11 +16,12 @@ using CryptoExchange.Net.Clients; using CryptoExchange.Net.Converters.SystemTextJson; using CoinEx.Net.Objects.Models.V2; +using CoinEx.Net.Interfaces.Clients.SpotApiV2; -namespace CoinEx.Net.Clients.SpotApi +namespace CoinEx.Net.Clients.SpotApiV2 { - /// - public class CoinExRestClientSpotApi : RestApiClient, ICoinExClientSpotApi//, ISpotClient + /// + public class CoinExRestClientSpotApi : RestApiClient, ICoinExRestClientSpotApi//, ISpotClient { #region fields /// @@ -47,11 +42,11 @@ public class CoinExRestClientSpotApi : RestApiClient, ICoinExClientSpotApi//, IS #region Api clients /// - public ICoinExClientSpotApiAccount Account { get; } + public ICoinExRestClientSpotApiAccount Account { get; } /// - public ICoinExClientSpotApiExchangeData ExchangeData { get; } + public ICoinExRestClientSpotApiExchangeData ExchangeData { get; } /// - public ICoinExClientSpotApiTrading Trading { get; } + public ICoinExRestClientSpotApiTrading Trading { get; } #endregion internal readonly string _brokerId; diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiAccount.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiAccount.cs similarity index 97% rename from CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiAccount.cs rename to CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiAccount.cs index 08d849c..1a9af96 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiAccount.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiAccount.cs @@ -7,14 +7,14 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using CoinEx.Net.Interfaces.Clients.SpotApi; using CoinEx.Net.ExtensionMethods; using CoinEx.Net.Objects.Models.V2; +using CoinEx.Net.Interfaces.Clients.SpotApiV2; -namespace CoinEx.Net.Clients.SpotApi +namespace CoinEx.Net.Clients.SpotApiV2 { /// - public class CoinExRestClientSpotApiAccount : ICoinExClientSpotApiAccount + public class CoinExRestClientSpotApiAccount : ICoinExRestClientSpotApiAccount { private readonly CoinExRestClientSpotApi _baseClient; diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiExchangeData.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiExchangeData.cs similarity index 95% rename from CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiExchangeData.cs rename to CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiExchangeData.cs index 49b2f50..0714194 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiExchangeData.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiExchangeData.cs @@ -4,13 +4,13 @@ using System.Threading; using System.Threading.Tasks; using CoinEx.Net.Objects.Models.V2; -using CoinEx.Net.Interfaces.Clients.SpotApi; using CoinEx.Net.Enums; +using CoinEx.Net.Interfaces.Clients.SpotApiV2; -namespace CoinEx.Net.Clients.SpotApi +namespace CoinEx.Net.Clients.SpotApiV2 { /// - public class CoinExRestClientSpotApiExchangeData : ICoinExClientSpotApiExchangeData + public class CoinExRestClientSpotApiExchangeData : ICoinExRestClientSpotApiExchangeData { private readonly CoinExRestClientSpotApi _baseClient; diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs similarity index 98% rename from CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs rename to CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs index 09605e0..1a40144 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs @@ -4,14 +4,14 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using CoinEx.Net.Interfaces.Clients.SpotApi; using CoinEx.Net.Objects.Models.V2; using System; +using CoinEx.Net.Interfaces.Clients.SpotApiV2; -namespace CoinEx.Net.Clients.SpotApi +namespace CoinEx.Net.Clients.SpotApiV2 { /// - public class CoinExRestClientSpotApiTrading : ICoinExClientSpotApiTrading + public class CoinExRestClientSpotApiTrading : ICoinExRestClientSpotApiTrading { private readonly CoinExRestClientSpotApi _baseClient; diff --git a/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs similarity index 99% rename from CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs rename to CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs index e0c5007..7589100 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs @@ -7,7 +7,6 @@ using Microsoft.Extensions.Logging; using CryptoExchange.Net.Authentication; using System.Threading; -using CoinEx.Net.Interfaces.Clients.SpotApi; using CoinEx.Net.Objects.Options; using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Interfaces; @@ -19,8 +18,9 @@ using CoinEx.Net.Objects.Sockets.V2.Subscriptions; using CoinEx.Net.Objects.Sockets.V2.Queries; using System.Linq; +using CoinEx.Net.Interfaces.Clients.SpotApiV2; -namespace CoinEx.Net.Clients.SpotApi +namespace CoinEx.Net.Clients.SpotApiV2 { /// public class CoinExSocketClientSpotApi : SocketApiClient, ICoinExSocketClientSpotApi diff --git a/CoinEx.Net/CoinEx.Net.xml b/CoinEx.Net/CoinEx.Net.xml index d7e3aa0..0353bdd 100644 --- a/CoinEx.Net/CoinEx.Net.xml +++ b/CoinEx.Net/CoinEx.Net.xml @@ -7,10 +7,10 @@ - + - + @@ -39,6 +39,9 @@ + + + @@ -70,38 +73,38 @@ - - + + - + - + Event triggered when an order is placed via this client - + Event triggered when an order is canceled via this client. Note that this does not trigger when using CancelAllOrdersAsync - + - + - + - + - + - + Get the name of a symbol for CoinEx based on the base and quote asset @@ -109,411 +112,411 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + Create a new instance of CoinExSocketClient with default options - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + Event triggered when an order is placed via this client - + Event triggered when an order is canceled via this client. Note that this does not trigger when using CancelAllOrdersAsync - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + Create a new instance of CoinExSocketClient with default options - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1034,12 +1037,12 @@ Client for accessing the CoinEx API. - + - Spot V1 API endpoints + Spot V1 API endpoints. Use V2 API if possible, V1 API will be removed at a later date - + Spot V2 API endpoints @@ -1055,9 +1058,14 @@ Client for accessing the CoinEx websocket API + + + V2 API Spot streams + + - Spot streams + V1 API Spot streams. Use V2 if possible, V1 will be removed at a later date @@ -1066,38 +1074,38 @@ The credentials to set - + Spot API - + Endpoints related to account settings, info or actions - + Endpoints related to retrieving market and system data - + Endpoints related to orders and trades - + Get the ISpotClient for this client. This is a common interface which allows for some basic operations without knowing any details of the exchange. - + CoinEx account endpoints. Account endpoints include balance info, withdraw/deposit info and requesting and account settings - + Retrieves a list of balances. Requires API credentials @@ -1105,7 +1113,7 @@ Cancellation token List of balances - + Retrieves a list of deposits. Requires API credentials and withdrawal permission on the API key @@ -1116,7 +1124,7 @@ Cancellation token - + Get the deposit address of an asset @@ -1126,7 +1134,7 @@ Cancellation token - + Retrieves a list of withdrawals. Requires API credentials and withdrawal permission on the API key @@ -1138,7 +1146,7 @@ Cancellation token - + Withdraw assets from CoinEx to a specific address. Requires API credentials and withdrawal permission on the API key @@ -1151,7 +1159,7 @@ Cancellation token The withdrawal object - + Cancel a specific withdrawal. Requires API credentials and withdrawal permission on the API key @@ -1160,12 +1168,12 @@ Cancellation token True if successful, false otherwise - + CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. - + Gets the exchange rates of currencies @@ -1173,7 +1181,7 @@ Cancellation token - + Gets the asset configs @@ -1182,7 +1190,7 @@ Cancellation token - + Gets a list of symbols active on CoinEx @@ -1190,7 +1198,7 @@ Cancellation token List of symbol names - + Gets the state of a specific symbol @@ -1199,7 +1207,7 @@ Cancellation token The state of the symbol - + Gets the states of all symbols @@ -1207,7 +1215,7 @@ Cancellation token List of states for all symbols - + Gets the order book for a symbol @@ -1218,7 +1226,7 @@ Cancellation token Order book for a symbol - + Gets the latest trades for a symbol @@ -1228,7 +1236,7 @@ Cancellation token List of trades for a symbol - + Retrieves kline data for a specific symbol @@ -1239,7 +1247,7 @@ Cancellation token List of klines for a symbol - + Retrieves market data for the exchange @@ -1248,7 +1256,7 @@ Cancellation token List of market data for the exchange - + Retrieves market data for the exchange @@ -1256,7 +1264,7 @@ Cancellation token List of market data for the exchange - + Retrieve the mining difficulty. Requires API credentials @@ -1264,12 +1272,12 @@ Cancellation token Mining difficulty - + CoinEx trading endpoints, placing and mananging orders. - + Places an order. This is a single method for multiple place order endpoints. The called endpoint depends on the provided order type. @@ -1290,7 +1298,7 @@ Cancellation token Details of the order that was placed - + Retrieves a list of open orders for a symbol. Requires API credentials @@ -1301,7 +1309,7 @@ Cancellation token List of open orders for a symbol - + Retrieves a list of open stop orders for a symbol. Requires API credentials @@ -1312,7 +1320,7 @@ Cancellation token List of open orders for a symbol - + Retrieves a list of executed orders for a symbol in the last 2 days. Requires API credentials @@ -1323,7 +1331,7 @@ Cancellation token List of executed orders for a symbol - + Retrieves details of an order. Requires API credentials @@ -1333,7 +1341,7 @@ Cancellation token Details of the order - + Retrieves execution details of a specific order. Requires API credentials @@ -1344,7 +1352,7 @@ Cancellation token Details of an executed order - + Gets a list of trades you executed on a specific symbol. Requires API credentials @@ -1355,7 +1363,7 @@ Cancellation token List of trades for a symbol - + Cancels an order. Requires API credentials @@ -1365,7 +1373,7 @@ Cancellation token Details of the canceled order - + Cancels all stop orders. Requires API credentials @@ -1373,7 +1381,7 @@ Cancellation token Execution statut - + Cancels all orders. Requires API credentials @@ -1382,26 +1390,26 @@ Cancellation token Execution statut - + Spot streams - + Pings the server Success if server responded, error otherwise - + Gets the server time The server time - + Get the symbol ticker @@ -1410,7 +1418,7 @@ The period to get data over, specified in seconds. i.e. one minute = 60, one day = 86400 Symbol state - + Subscribe to symbol ticker updates for a specific symbol @@ -1420,7 +1428,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to ticker updates for all symbols @@ -1429,7 +1437,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Get an order book @@ -1439,7 +1447,7 @@ The depth of merging, based on 8 decimals. 1 mergeDepth will merge the last decimals of all order in the book, 7 will merge the last 7 decimals of all orders together Order book of a symbol - + Subscribe to order book updates @@ -1451,7 +1459,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Gets the latest trades on a symbol @@ -1461,7 +1469,7 @@ Return trades since this id List of trades - + Subscribe to symbol trade updates for a symbol @@ -1471,7 +1479,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Gets symbol kline data @@ -1480,7 +1488,7 @@ The interval of the candles - + Get balances of assets. Requires API credentials @@ -1488,7 +1496,7 @@ The assets to get the balances for, empty for all Dictionary of assets and their balances - + Subscribe to updates of your balances, Receives updates whenever the balance for an asset changes @@ -1497,7 +1505,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Gets a list of open orders for a symbol @@ -1508,7 +1516,7 @@ The limit of results List of open orders - + Subscribe to updates of active orders. Receives updates whenever an order is placed, updated or finished @@ -1517,7 +1525,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to updates of active orders. Receives updates whenever an order is placed, updated or finished @@ -1527,38 +1535,38 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Spot API - + Endpoints related to account settings, info or actions - + Endpoints related to retrieving market and system data - + Endpoints related to orders and trades - + Get the ISpotClient for this client. This is a common interface which allows for some basic operations without knowing any details of the exchange. - + CoinEx account endpoints. Account endpoints include balance info, withdraw/deposit info and requesting and account settings - + Get trading fees @@ -1568,7 +1576,7 @@ Cancelation token - + Update account settings @@ -1577,7 +1585,7 @@ Cancelation token - + Get balances @@ -1585,7 +1593,7 @@ Cancelation token - + Get margin balances @@ -1593,7 +1601,7 @@ Cancelation token - + Get balances in the financial account @@ -1601,7 +1609,7 @@ Cancelation token - + Get credit account info @@ -1609,7 +1617,7 @@ Cancelation token - + Get automated market maker account liquidity @@ -1617,7 +1625,7 @@ Cancelation token - + Apply for margin borrowing @@ -1629,7 +1637,7 @@ Cancelation token - + Repay a margin loan @@ -1641,7 +1649,7 @@ Cancelation token - + Get borrow history @@ -1653,7 +1661,7 @@ Cancelation token - + Get borrow limits @@ -1663,7 +1671,7 @@ Cancelation token - + Get the deposit address for an asset @@ -1673,7 +1681,7 @@ Cancelation token - + Renew deposit address @@ -1683,7 +1691,7 @@ Cancelation token - + Get deposit history @@ -1696,12 +1704,12 @@ Cancelation token - + CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. - + Get symbol information @@ -1709,7 +1717,7 @@ Cancelation Token - + Get symbol tickers @@ -1718,7 +1726,7 @@ Cancelation Token - + Get the orderbook for a symbol @@ -1729,7 +1737,7 @@ Cancelation Token - + Get the trade history for a symbol @@ -1740,7 +1748,7 @@ Cancelation Token - + Get klines/candlesticks @@ -1752,7 +1760,7 @@ Cancelation Token - + Get index prices @@ -1761,12 +1769,12 @@ Cancelation Token - + CoinEx trading endpoints, placing and mananging orders. - + Place a new order @@ -1783,7 +1791,7 @@ Cancelation Token - + Place a new stop order @@ -1801,7 +1809,7 @@ Cancelation Token - + Get an order by id @@ -1811,7 +1819,7 @@ Cancelation Token - + Get a list of open orders @@ -1825,7 +1833,7 @@ Cancelation Token - + Get a list of closed orders. Note that orders canceled without having any trades will not be returned @@ -1839,7 +1847,7 @@ Cancelation Token - + Get a list of open stop orders @@ -1853,7 +1861,7 @@ Cancelation Token - + Get a list of closed stop orders. Note that orders canceled without having any trades will not be returned @@ -1867,7 +1875,7 @@ Cancelation Token - + Edit an active order @@ -1880,7 +1888,7 @@ Cancelation Token - + Edit an active stop order @@ -1894,7 +1902,7 @@ Cancelation Token - + Cancel all orders for a symbol @@ -1905,7 +1913,7 @@ Cancelation Token - + Cancel an active order @@ -1916,7 +1924,7 @@ Cancelation Token - + Cancel an active stop order @@ -1927,7 +1935,7 @@ Cancelation Token - + Cancel an active order by its client order id @@ -1938,7 +1946,7 @@ Cancelation Token - + Cancel an active stop order by its client order id @@ -1949,7 +1957,7 @@ Cancelation Token - + Get trade list @@ -1964,12 +1972,12 @@ Cancelation Token - + Spot streams - + Subscribe to symbol ticker updates for the specified symbols. Note that only one ticker subscription can be active at the same time; new ticker subscription will override the old subscriptions. @@ -1979,7 +1987,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to symbol ticker updates for all symbols. Note that only one ticker subscription can be active at the same time; new ticker subscription will override the old subscriptions. @@ -1988,7 +1996,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to order book updates @@ -2001,7 +2009,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to order book updates @@ -2014,7 +2022,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to live trade updates @@ -2024,7 +2032,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to live trade updates @@ -2034,7 +2042,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to live trade updates @@ -2043,7 +2051,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to index price updates @@ -2053,7 +2061,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to index price updates @@ -2063,7 +2071,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to book price updates @@ -2073,7 +2081,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to book price updates @@ -2083,7 +2091,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to user order updates @@ -2092,7 +2100,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to user stop order updates @@ -2101,7 +2109,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to user trade updates @@ -2110,7 +2118,7 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to user balance updates diff --git a/CoinEx.Net/ExtensionMethods/ServiceCollectionExtensions.cs b/CoinEx.Net/ExtensionMethods/ServiceCollectionExtensions.cs index b58a762..4636765 100644 --- a/CoinEx.Net/ExtensionMethods/ServiceCollectionExtensions.cs +++ b/CoinEx.Net/ExtensionMethods/ServiceCollectionExtensions.cs @@ -60,7 +60,7 @@ public static IServiceCollection AddCoinEx( services.AddTransient(); services.AddTransient(); services.AddSingleton(); - services.AddTransient(x => x.GetRequiredService().SpotApiV1.CommonSpotClient); + services.AddTransient(x => x.GetRequiredService().SpotApiV2.CommonSpotClient); if (socketClientLifeTime == null) services.AddSingleton(); else diff --git a/CoinEx.Net/Interfaces/Clients/ICoinExRestClient.cs b/CoinEx.Net/Interfaces/Clients/ICoinExRestClient.cs index 96f5322..5952751 100644 --- a/CoinEx.Net/Interfaces/Clients/ICoinExRestClient.cs +++ b/CoinEx.Net/Interfaces/Clients/ICoinExRestClient.cs @@ -1,5 +1,4 @@ -using CoinEx.Net.Interfaces.Clients.SpotApi; -using CryptoExchange.Net.Authentication; +using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Interfaces; namespace CoinEx.Net.Interfaces.Clients @@ -10,13 +9,13 @@ namespace CoinEx.Net.Interfaces.Clients public interface ICoinExRestClient : IRestClient { /// - /// Spot V1 API endpoints + /// Spot V1 API endpoints. Use V2 API if possible, V1 API will be removed at a later date /// - ICoinExClientSpotApiV1 SpotApiV1 { get; } + SpotApiV1.ICoinExRestClientSpotApi SpotApi { get; } /// /// Spot V2 API endpoints /// - ICoinExClientSpotApi SpotApi { get; } + SpotApiV2.ICoinExRestClientSpotApi SpotApiV2 { get; } /// /// Set the API credentials for this client. All Api clients in this client will use the new credentials, regardless of earlier set options. diff --git a/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs b/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs index d35650e..1608d3f 100644 --- a/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs +++ b/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs @@ -1,5 +1,4 @@ -using CoinEx.Net.Interfaces.Clients.SpotApi; -using CryptoExchange.Net.Authentication; +using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Interfaces; namespace CoinEx.Net.Interfaces.Clients @@ -10,10 +9,13 @@ namespace CoinEx.Net.Interfaces.Clients public interface ICoinExSocketClient : ISocketClient { /// - /// Spot streams + /// V2 API Spot streams /// - public ICoinExSocketClientSpotApi SpotApi { get; } - public ICoinExSocketClientSpotApiV1 SpotApiV1 { get; } + public SpotApiV2.ICoinExSocketClientSpotApi SpotApiV2 { get; } + /// + /// V1 API Spot streams. Use V2 if possible, V1 will be removed at a later date + /// + public SpotApiV1.ICoinExSocketClientSpotApi SpotApi { get; } /// /// Set the API credentials for this client. All Api clients in this client will use the new credentials, regardless of earlier set options. diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApi.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApi.cs similarity index 71% rename from CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApi.cs rename to CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApi.cs index b53f51e..5acaf93 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApi.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApi.cs @@ -2,27 +2,27 @@ using CryptoExchange.Net.Interfaces.CommonClients; using System; -namespace CoinEx.Net.Interfaces.Clients.SpotApi +namespace CoinEx.Net.Interfaces.Clients.SpotApiV1 { /// /// Spot API /// - public interface ICoinExClientSpotApi : IRestApiClient, IDisposable + public interface ICoinExRestClientSpotApi : IRestApiClient, IDisposable { /// /// Endpoints related to account settings, info or actions /// - ICoinExClientSpotApiAccount Account { get; } + ICoinExRestClientSpotApiAccount Account { get; } /// /// Endpoints related to retrieving market and system data /// - ICoinExClientSpotApiExchangeData ExchangeData { get; } + ICoinExRestClientSpotApiExchangeData ExchangeData { get; } /// /// Endpoints related to orders and trades /// - ICoinExClientSpotApiTrading Trading { get; } + ICoinExRestClientSpotApiTrading Trading { get; } /// /// Get the ISpotClient for this client. This is a common interface which allows for some basic operations without knowing any details of the exchange. diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1Account.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApiAccount.cs similarity index 97% rename from CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1Account.cs rename to CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApiAccount.cs index 0a8a594..eb13f78 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1Account.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApiAccount.cs @@ -4,12 +4,12 @@ using System.Threading.Tasks; using CoinEx.Net.Objects.Models; -namespace CoinEx.Net.Interfaces.Clients.SpotApi +namespace CoinEx.Net.Interfaces.Clients.SpotApiV1 { /// /// CoinEx account endpoints. Account endpoints include balance info, withdraw/deposit info and requesting and account settings /// - public interface ICoinExClientSpotApiV1Account + public interface ICoinExRestClientSpotApiAccount { /// /// Retrieves a list of balances. Requires API credentials diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1ExchangeData.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApiExchangeData.cs similarity index 98% rename from CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1ExchangeData.cs rename to CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApiExchangeData.cs index 6394977..681f521 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1ExchangeData.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApiExchangeData.cs @@ -5,12 +5,12 @@ using System.Threading.Tasks; using CoinEx.Net.Objects.Models; -namespace CoinEx.Net.Interfaces.Clients.SpotApi +namespace CoinEx.Net.Interfaces.Clients.SpotApiV1 { /// /// CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. /// - public interface ICoinExClientSpotApiV1ExchangeData + public interface ICoinExRestClientSpotApiExchangeData { /// /// Gets the exchange rates of currencies diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1Trading.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApiTrading.cs similarity index 98% rename from CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1Trading.cs rename to CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApiTrading.cs index d2a94c5..67f6094 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1Trading.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApiTrading.cs @@ -4,12 +4,12 @@ using System.Threading.Tasks; using CoinEx.Net.Objects.Models; -namespace CoinEx.Net.Interfaces.Clients.SpotApi +namespace CoinEx.Net.Interfaces.Clients.SpotApiV1 { /// /// CoinEx trading endpoints, placing and mananging orders. /// - public interface ICoinExClientSpotApiV1Trading + public interface ICoinExRestClientSpotApiTrading { /// /// Places an order. This is a single method for multiple place order endpoints. The called endpoint depends on the provided order type. diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExSocketClientSpotApiV1.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExSocketClientSpotApi.cs similarity index 98% rename from CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExSocketClientSpotApiV1.cs rename to CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExSocketClientSpotApi.cs index de179a9..26384c5 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExSocketClientSpotApiV1.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExSocketClientSpotApi.cs @@ -9,12 +9,12 @@ using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects.Sockets; -namespace CoinEx.Net.Interfaces.Clients.SpotApi +namespace CoinEx.Net.Interfaces.Clients.SpotApiV1 { /// /// Spot streams /// - public interface ICoinExSocketClientSpotApiV1 : ISocketApiClient, IDisposable + public interface ICoinExSocketClientSpotApi : ISocketApiClient, IDisposable { /// /// Pings the server diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApi.cs similarity index 71% rename from CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1.cs rename to CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApi.cs index e527621..16d651b 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExClientSpotApiV1.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApi.cs @@ -2,27 +2,27 @@ using CryptoExchange.Net.Interfaces.CommonClients; using System; -namespace CoinEx.Net.Interfaces.Clients.SpotApi +namespace CoinEx.Net.Interfaces.Clients.SpotApiV2 { /// /// Spot API /// - public interface ICoinExClientSpotApiV1 : IRestApiClient, IDisposable + public interface ICoinExRestClientSpotApi : IRestApiClient, IDisposable { /// /// Endpoints related to account settings, info or actions /// - ICoinExClientSpotApiV1Account Account { get; } + ICoinExRestClientSpotApiAccount Account { get; } /// /// Endpoints related to retrieving market and system data /// - ICoinExClientSpotApiV1ExchangeData ExchangeData { get; } + ICoinExRestClientSpotApiExchangeData ExchangeData { get; } /// /// Endpoints related to orders and trades /// - ICoinExClientSpotApiV1Trading Trading { get; } + ICoinExRestClientSpotApiTrading Trading { get; } /// /// Get the ISpotClient for this client. This is a common interface which allows for some basic operations without knowing any details of the exchange. diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiAccount.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiAccount.cs similarity index 98% rename from CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiAccount.cs rename to CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiAccount.cs index 5b4083e..b468759 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiAccount.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiAccount.cs @@ -7,12 +7,12 @@ using System.Diagnostics.Metrics; using System.Text.RegularExpressions; -namespace CoinEx.Net.Interfaces.Clients.SpotApi +namespace CoinEx.Net.Interfaces.Clients.SpotApiV2 { /// /// CoinEx account endpoints. Account endpoints include balance info, withdraw/deposit info and requesting and account settings /// - public interface ICoinExClientSpotApiAccount + public interface ICoinExRestClientSpotApiAccount { /// /// Get trading fees diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiExchangeData.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiExchangeData.cs similarity index 97% rename from CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiExchangeData.cs rename to CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiExchangeData.cs index 3b31a7b..258fbe8 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiExchangeData.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiExchangeData.cs @@ -5,12 +5,12 @@ using System.Threading.Tasks; using CoinEx.Net.Objects.Models.V2; -namespace CoinEx.Net.Interfaces.Clients.SpotApi +namespace CoinEx.Net.Interfaces.Clients.SpotApiV2 { /// /// CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. /// - public interface ICoinExClientSpotApiExchangeData + public interface ICoinExRestClientSpotApiExchangeData { /// /// Get symbol information diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs similarity index 99% rename from CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs rename to CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs index 62ffa69..4ed551e 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs @@ -6,12 +6,12 @@ using System.Collections.Generic; using System; -namespace CoinEx.Net.Interfaces.Clients.SpotApi +namespace CoinEx.Net.Interfaces.Clients.SpotApiV2 { /// /// CoinEx trading endpoints, placing and mananging orders. /// - public interface ICoinExClientSpotApiTrading + public interface ICoinExRestClientSpotApiTrading { /// /// Place a new order diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExSocketClientSpotApi.cs similarity index 99% rename from CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs rename to CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExSocketClientSpotApi.cs index 769559f..8086709 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExSocketClientSpotApi.cs @@ -10,7 +10,7 @@ using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects.Sockets; -namespace CoinEx.Net.Interfaces.Clients.SpotApi +namespace CoinEx.Net.Interfaces.Clients.SpotApiV2 { /// /// Spot streams diff --git a/CoinEx.Net/Objects/Models/V2/CoinExTickerUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExTickerUpdate.cs index e5a17f3..0b870c1 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExTickerUpdate.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExTickerUpdate.cs @@ -5,7 +5,7 @@ namespace CoinEx.Net.Objects.Models.V2 { - public record CoinExTickerUpdateWrapper + internal record CoinExTickerUpdateWrapper { [JsonPropertyName("state_list")] public IEnumerable Tickers { get; set; } = Array.Empty(); diff --git a/CoinEx.Net/Objects/Models/V2/CoinExTrade.cs b/CoinEx.Net/Objects/Models/V2/CoinExTrade.cs index b24dcfd..4b4818a 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExTrade.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExTrade.cs @@ -9,7 +9,7 @@ namespace CoinEx.Net.Objects.Models.V2 internal record CoinExTradeWrapper { [JsonPropertyName("market")] - public string Symbol { get; set; } + public string Symbol { get; set; } = string.Empty; [JsonPropertyName("deal_list")] public IEnumerable Trades { get; set; } = Array.Empty(); } diff --git a/CoinEx.Net/Objects/Sockets/V2/CoinExSocketUpdate.cs b/CoinEx.Net/Objects/Sockets/V2/CoinExSocketUpdate.cs index 409e06e..43ccd12 100644 --- a/CoinEx.Net/Objects/Sockets/V2/CoinExSocketUpdate.cs +++ b/CoinEx.Net/Objects/Sockets/V2/CoinExSocketUpdate.cs @@ -8,8 +8,8 @@ namespace CoinEx.Net.Objects.Sockets.V2 internal class CoinExSocketUpdate { [JsonPropertyName("method")] - public string Method { get; set; } + public string Method { get; set; } = string.Empty; [JsonPropertyName("data")] - public T Data { get; set; } + public T Data { get; set; } = default!; } } diff --git a/CoinEx.Net/SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs b/CoinEx.Net/SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs index 9d090ea..d1f8a4e 100644 --- a/CoinEx.Net/SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs +++ b/CoinEx.Net/SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs @@ -62,7 +62,8 @@ public CoinExSpotSymbolOrderBook(string symbol, /// protected override async Task> DoStartAsync(CancellationToken ct) { - var result = await _socketClient.SpotApiV1.SubscribeToOrderBookUpdatesAsync(Symbol, Levels!.Value, 0, HandleUpdate).ConfigureAwait(false); + // TODO CHECK, MERGE REQUEST FOR ORDER BOOK SYNC DOESNT SEEM TO BE INCLUDED + var result = await _socketClient.SpotApi.SubscribeToOrderBookUpdatesAsync(Symbol, Levels!.Value, 0, HandleUpdate).ConfigureAwait(false); if (!result) return result; From 0d89059b5ee5062bee42eeeb0df941ac05196130 Mon Sep 17 00:00:00 2001 From: JKorf Date: Thu, 4 Apr 2024 14:51:39 +0200 Subject: [PATCH 10/17] wip --- .../SpotApiV2/CoinExRestClientSpotApi.cs | 634 +++++++++--------- .../CoinExRestClientSpotApiAccount.cs | 98 +++ .../CoinExRestClientSpotApiTrading.cs | 46 +- CoinEx.Net/CoinEx.Net.xml | 518 +++++++++++++- CoinEx.Net/Enums/MovementMethod.cs | 24 + CoinEx.Net/Enums/TransferStatus.cs | 34 + CoinEx.Net/Enums/WithdrawStatusV2.cs | 59 ++ .../ICoinExRestClientSpotApiAccount.cs | 96 +++ .../ICoinExRestClientSpotApiTrading.cs | 23 +- .../Objects/Internal/CoinExApiResult.cs | 2 + .../Objects/Models/V2/CoinExAamLiquidity.cs | 34 + CoinEx.Net/Objects/Models/V2/CoinExDeposit.cs | 2 +- .../V2/CoinExDepositWithdrawalConfig.cs | 128 ++++ CoinEx.Net/Objects/Models/V2/CoinExOrder.cs | 4 +- CoinEx.Net/Objects/Models/V2/CoinExStopId.cs | 2 +- .../Objects/Models/V2/CoinExStopOrder.cs | 2 +- .../Objects/Models/V2/CoinExTransfer.cs | 50 ++ .../Objects/Models/V2/CoinExWithdrawal.cs | 91 +++ 18 files changed, 1494 insertions(+), 353 deletions(-) create mode 100644 CoinEx.Net/Enums/MovementMethod.cs create mode 100644 CoinEx.Net/Enums/TransferStatus.cs create mode 100644 CoinEx.Net/Enums/WithdrawStatusV2.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExAamLiquidity.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExDepositWithdrawalConfig.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExTransfer.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExWithdrawal.cs diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs index 6cc576a..a81c764 100644 --- a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs @@ -17,11 +17,14 @@ using CryptoExchange.Net.Converters.SystemTextJson; using CoinEx.Net.Objects.Models.V2; using CoinEx.Net.Interfaces.Clients.SpotApiV2; +using CoinEx.Net.Enums; +using System.Linq; +using System.Globalization; namespace CoinEx.Net.Clients.SpotApiV2 { /// - public class CoinExRestClientSpotApi : RestApiClient, ICoinExRestClientSpotApi//, ISpotClient + public class CoinExRestClientSpotApi : RestApiClient, ICoinExRestClientSpotApi, ISpotClient { #region fields /// @@ -122,321 +125,318 @@ internal async Task>> ExecutePaginatedAsync( internal Uri GetUri(string path) => new Uri(BaseAddress.AppendPath(path)); #endregion - //#region common interface - - //internal void InvokeOrderPlaced(OrderId id) - //{ - // OnOrderPlaced?.Invoke(id); - //} - - //internal void InvokeOrderCanceled(OrderId id) - //{ - // OnOrderCanceled?.Invoke(id); - //} - - ///// - ///// Get the name of a symbol for CoinEx based on the base and quote asset - ///// - ///// - ///// - ///// - //public string GetSymbolName(string baseAsset, string quoteAsset) => (baseAsset + quoteAsset).ToUpperInvariant(); - - //async Task>> IBaseRestClient.GetSymbolsAsync(CancellationToken ct) - //{ - // var symbols = await ExchangeData.GetSymbolInfoAsync(ct: ct).ConfigureAwait(false); - // if (!symbols) - // return symbols.As>(null); - - // return symbols.As(symbols.Data.Select(d => new Symbol - // { - // SourceObject = d, - // Name = d.Key, - // MinTradeQuantity = d.Value.MinQuantity, - // PriceDecimals = d.Value.PricingDecimal, - // QuantityDecimals = d.Value.TradingDecimal - // })); - //} - - //async Task> IBaseRestClient.GetTickerAsync(string symbol, CancellationToken ct) - //{ - // if (string.IsNullOrEmpty(symbol)) - // throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetTickerAsync), nameof(symbol)); - - // var tickers = await ExchangeData.GetTickerAsync(symbol, ct: ct).ConfigureAwait(false); - // if (!tickers) - // return tickers.As(null); - - // return tickers.As(new Ticker - // { - // SourceObject = tickers.Data, - // Symbol = symbol, - // HighPrice = tickers.Data.Ticker.HighPrice, - // LowPrice = tickers.Data.Ticker.LowPrice, - // LastPrice = tickers.Data.Ticker.LastPrice, - // Price24H = tickers.Data.Ticker.OpenPrice, - // Volume = tickers.Data.Ticker.Volume - // }); - //} - - //async Task>> IBaseRestClient.GetTickersAsync(CancellationToken ct) - //{ - // var tickers = await ExchangeData.GetTickersAsync(ct: ct).ConfigureAwait(false); - // if (!tickers) - // return tickers.As>(null); - - // return tickers.As(tickers.Data.Tickers.Select(t => - // new Ticker - // { - // SourceObject = t, - // Symbol = t.Key, - // HighPrice = t.Value.HighPrice, - // LowPrice = t.Value.LowPrice, - // LastPrice = t.Value.LastPrice, - // Price24H = t.Value.OpenPrice, - // Volume = t.Value.Volume - // })); - //} - - //async Task>> IBaseRestClient.GetKlinesAsync(string symbol, TimeSpan timespan, DateTime? startTime, DateTime? endTime, int? limit, CancellationToken ct) - //{ - // if (startTime != null || endTime != null) - // throw new ArgumentException("CoinEx does not support time based klines requesting", startTime != null ? nameof(startTime) : nameof(endTime)); - - // if (string.IsNullOrEmpty(symbol)) - // throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetKlinesAsync), nameof(symbol)); - - // var klines = await ExchangeData.GetKlinesAsync(symbol, GetKlineIntervalFromTimespan(timespan), limit, ct: ct).ConfigureAwait(false); - // if (!klines) - // return klines.As>(null); - - // return klines.As(klines.Data.Select(t => - // new Kline - // { - // SourceObject = t, - // OpenPrice = t.OpenPrice, - // LowPrice = t.LowPrice, - // OpenTime = t.OpenTime, - // Volume = t.Volume, - // ClosePrice = t.ClosePrice, - // HighPrice = t.HighPrice - // })); - //} - - //async Task> IBaseRestClient.GetOrderBookAsync(string symbol, CancellationToken ct) - //{ - // if (string.IsNullOrEmpty(symbol)) - // throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetOrderBookAsync), nameof(symbol)); - - // var book = await ExchangeData.GetOrderBookAsync(symbol, 0, ct: ct).ConfigureAwait(false); - // if (!book) - // return book.As(null); - - // return book.As(new OrderBook - // { - // SourceObject = book.Data, - // Asks = book.Data.Asks.Select(a => new OrderBookEntry { Price = a.Price, Quantity = a.Quantity }), - // Bids = book.Data.Bids.Select(b => new OrderBookEntry { Price = b.Price, Quantity = b.Quantity }) - // }); - //} - - //async Task>> IBaseRestClient.GetRecentTradesAsync(string symbol, CancellationToken ct) - //{ - // if (string.IsNullOrEmpty(symbol)) - // throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetRecentTradesAsync), nameof(symbol)); - - // var trades = await ExchangeData.GetTradeHistoryAsync(symbol, ct: ct).ConfigureAwait(false); - // if (!trades) - // return trades.As>(null); - - // return trades.As(trades.Data.Select(t => - // new Trade - // { - // SourceObject = t, - // Price = t.Price, - // Quantity = t.Quantity, - // Symbol = symbol, - // Timestamp = t.Timestamp - // })); - //} - - //async Task> ISpotClient.PlaceOrderAsync(string symbol, CommonOrderSide side, CommonOrderType type, decimal quantity, decimal? price, string? accountId, string? clientOrderId, CancellationToken ct) - //{ - // if (price == null && type == CommonOrderType.Limit) - // throw new ArgumentException("Price parameter null while placing a limit order", nameof(price)); - - // if (string.IsNullOrEmpty(symbol)) - // throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.PlaceOrderAsync), nameof(symbol)); - - // var result = await Trading.PlaceOrderAsync( - // symbol, - // side == CommonOrderSide.Sell ? OrderSide.Sell : OrderSide.Buy, - // type == CommonOrderType.Limit ? OrderType.Limit : OrderType.Market, - // quantity, - // price, - // clientOrderId: clientOrderId, - // ct: ct).ConfigureAwait(false); - // if (!result) - // return result.As(null); - - // return result.As(new OrderId { SourceObject = result.Data, Id = result.Data.Id.ToString(CultureInfo.InvariantCulture) }); - //} - - //async Task> IBaseRestClient.GetOrderAsync(string orderId, string? symbol, CancellationToken ct) - //{ - // if (string.IsNullOrEmpty(symbol)) - // throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetOrderAsync), nameof(symbol)); - - // if (!long.TryParse(orderId, out var id)) - // throw new ArgumentException($"Invalid order id for CoinEx {nameof(ISpotClient.GetOrderAsync)}", nameof(orderId)); - - // var order = await Trading.GetOrderAsync(symbol!, id, ct: ct).ConfigureAwait(false); - // if (!order) - // return order.As(null); - - // return order.As(new Order - // { - // SourceObject = order.Data, - // Id = order.Data.Id.ToString(CultureInfo.InvariantCulture), - // Price = order.Data.Price, - // Quantity = order.Data.Quantity, - // QuantityFilled = order.Data.QuantityFilled, - // Timestamp = order.Data.CreateTime, - // Symbol = order.Data.Symbol, - // Side = order.Data.Side == OrderSide.Buy ? CommonOrderSide.Buy: CommonOrderSide.Sell, - // Status = order.Data.Status == OrderStatus.Canceled ? CommonOrderStatus.Canceled: order.Data.Status == OrderStatus.Executed ? CommonOrderStatus.Filled: CommonOrderStatus.Active, - // Type = order.Data.OrderType == OrderType.Market ? CommonOrderType.Market: order.Data.OrderType == OrderType.Limit ? CommonOrderType.Limit: CommonOrderType.Other - // }); - //} - - //async Task>> IBaseRestClient.GetOrderTradesAsync(string orderId, string? symbol, CancellationToken ct) - //{ - // if (!long.TryParse(orderId, out var id)) - // throw new ArgumentException($"Invalid order id for CoinEx {nameof(ISpotClient.GetOrderAsync)}", nameof(orderId)); - - // var result = await Trading.GetOrderTradesAsync(id, 1, 100, ct: ct).ConfigureAwait(false); - // if (!result) - // return result.As>(null); - - // return result.As(result.Data.Data.Select(d => - // new UserTrade - // { - // SourceObject = d, - // Id = d.Id.ToString(CultureInfo.InvariantCulture), - // Price = d.Price, - // Quantity = d.Quantity, - // Fee = d.Fee, - // FeeAsset = d.FeeAsset, - // OrderId = d.OrderId?.ToString(CultureInfo.InvariantCulture), - // Symbol = symbol ?? string.Empty, - // Timestamp = d.Timestamp - // })); - //} - - //async Task>> IBaseRestClient.GetOpenOrdersAsync(string? symbol, CancellationToken ct) - //{ - // if (string.IsNullOrEmpty(symbol)) - // throw new ArgumentException($"CoinEx needs the {nameof(symbol)} parameter for the method {nameof(ISpotClient.GetOpenOrdersAsync)}"); - - // var openOrders = await Trading.GetOpenOrdersAsync(symbol!, 1, 100, ct: ct).ConfigureAwait(false); - // if (!openOrders) - // return openOrders.As>(null); - - // return openOrders.As(openOrders.Data.Data.Select(o => new Order - // { - // SourceObject = o, - // Id = o.Id.ToString(CultureInfo.InvariantCulture), - // Price = o.Price, - // Quantity = o.Quantity, - // QuantityFilled = o.QuantityFilled, - // Timestamp = o.CreateTime, - // Symbol = o.Symbol, - // Side = o.Side == OrderSide.Buy ? CommonOrderSide.Buy : CommonOrderSide.Sell, - // Status = o.Status == OrderStatus.Canceled ? CommonOrderStatus.Canceled : o.Status == OrderStatus.Executed ? CommonOrderStatus.Filled : CommonOrderStatus.Active, - // Type = o.OrderType == OrderType.Market ? CommonOrderType.Market : o.OrderType == OrderType.Limit ? CommonOrderType.Limit : CommonOrderType.Other - // })); - //} - - //async Task>> IBaseRestClient.GetClosedOrdersAsync(string? symbol, CancellationToken ct) - //{ - // if (string.IsNullOrEmpty(symbol)) - // throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetClosedOrdersAsync), nameof(symbol)); - - // if (string.IsNullOrEmpty(symbol)) - // throw new ArgumentException($"CoinEx needs the {nameof(symbol)} parameter for the method {nameof(ISpotClient.GetClosedOrdersAsync)}"); - - // var result = await Trading.GetClosedOrdersAsync(symbol!, 1, 100, ct: ct).ConfigureAwait(false); - // if (!result) - // return result.As>(null); - - // return result.As(result.Data.Data.Select(o => new Order - // { - // SourceObject = o, - // Id = o.Id.ToString(CultureInfo.InvariantCulture), - // Price = o.Price, - // Quantity = o.Quantity, - // QuantityFilled = o.QuantityFilled, - // Timestamp = o.CreateTime, - // Symbol = o.Symbol, - // Side = o.Side == OrderSide.Buy ? CommonOrderSide.Buy : CommonOrderSide.Sell, - // Status = o.Status == OrderStatus.Canceled ? CommonOrderStatus.Canceled : o.Status == OrderStatus.Executed ? CommonOrderStatus.Filled : CommonOrderStatus.Active, - // Type = o.OrderType == OrderType.Market ? CommonOrderType.Market : o.OrderType == OrderType.Limit ? CommonOrderType.Limit : CommonOrderType.Other - // })); - //} - - //async Task> IBaseRestClient.CancelOrderAsync(string orderId, string? symbol, CancellationToken ct) - //{ - // if (string.IsNullOrEmpty(symbol)) - // throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.CancelOrderAsync), nameof(symbol)); - - // if (!long.TryParse(orderId, out var id)) - // throw new ArgumentException($"Invalid order id for CoinEx {nameof(ISpotClient.GetOrderAsync)}", nameof(orderId)); - - // var result = await Trading.CancelOrderAsync(symbol!, id, ct: ct).ConfigureAwait(false); - // if (!result) - // return result.As(null); - - // return result.As(new OrderId - // { - // SourceObject = result.Data, - // Id = result.Data.Id.ToString(CultureInfo.InvariantCulture) - // }); - //} - - //async Task>> IBaseRestClient.GetBalancesAsync(string? accountId, CancellationToken ct) - //{ - // var balances = await Account.GetBalancesAsync(ct: ct).ConfigureAwait(false); - // if (!balances) - // return balances.As>(null); - - // return balances.As(balances.Data.Select(d => new Balance - // { - // SourceObject = d, - // Asset = d.Key, - // Available = d.Value.Available, - // Total = d.Value.Frozen + d.Value.Available - // })); - //} - - //private static KlineInterval GetKlineIntervalFromTimespan(TimeSpan timeSpan) - //{ - // if (timeSpan == TimeSpan.FromMinutes(1)) return KlineInterval.OneMinute; - // if (timeSpan == TimeSpan.FromMinutes(3)) return KlineInterval.ThreeMinutes; - // if (timeSpan == TimeSpan.FromMinutes(5)) return KlineInterval.FiveMinutes; - // if (timeSpan == TimeSpan.FromMinutes(15)) return KlineInterval.FiveMinutes; - // if (timeSpan == TimeSpan.FromMinutes(30)) return KlineInterval.ThirtyMinutes; - // if (timeSpan == TimeSpan.FromHours(1)) return KlineInterval.OneHour; - // if (timeSpan == TimeSpan.FromHours(2)) return KlineInterval.TwoHours; - // if (timeSpan == TimeSpan.FromHours(4)) return KlineInterval.FourHours; - // if (timeSpan == TimeSpan.FromHours(6)) return KlineInterval.SixHours; - // if (timeSpan == TimeSpan.FromHours(12)) return KlineInterval.TwelveHours; - // if (timeSpan == TimeSpan.FromDays(1)) return KlineInterval.OneDay; - // if (timeSpan == TimeSpan.FromDays(3)) return KlineInterval.ThreeDays; - // if (timeSpan == TimeSpan.FromDays(7)) return KlineInterval.OneWeek; - - // throw new ArgumentException("Unsupported timespan for CoinEx Klines, check supported intervals using CoinEx.Net.Objects.KlineInterval"); - //} - //#endregion + #region common interface + + internal void InvokeOrderPlaced(OrderId id) + { + OnOrderPlaced?.Invoke(id); + } + + internal void InvokeOrderCanceled(OrderId id) + { + OnOrderCanceled?.Invoke(id); + } + + /// + /// Get the name of a symbol for CoinEx based on the base and quote asset + /// + /// + /// + /// + public string GetSymbolName(string baseAsset, string quoteAsset) => (baseAsset + quoteAsset).ToUpperInvariant(); + + async Task>> IBaseRestClient.GetSymbolsAsync(CancellationToken ct) + { + var symbols = await ExchangeData.GetSymbolsAsync(ct: ct).ConfigureAwait(false); + if (!symbols) + return symbols.As>(null); + + return symbols.As(symbols.Data.Select(d => new Symbol + { + SourceObject = d, + Name = d.Name, + MinTradeQuantity = d.MinOrderQuantity, + PriceDecimals = d.PricePrecision, + QuantityDecimals = d.QuantityPrecision + })); + } + + async Task> IBaseRestClient.GetTickerAsync(string symbol, CancellationToken ct) + { + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetTickerAsync), nameof(symbol)); + + var tickers = await ExchangeData.GetTickersAsync(new[] { symbol }, ct: ct).ConfigureAwait(false); + if (!tickers) + return tickers.As(null); + + var ticker = tickers.Data.Single(); + return tickers.As(new Ticker + { + SourceObject = tickers.Data, + Symbol = symbol, + HighPrice = ticker.HighPrice, + LowPrice = ticker.LowPrice, + LastPrice = ticker.LastPrice, + Price24H = ticker.OpenPrice, + Volume = ticker.Volume + }); + } + + async Task>> IBaseRestClient.GetTickersAsync(CancellationToken ct) + { + var tickers = await ExchangeData.GetTickersAsync(ct: ct).ConfigureAwait(false); + if (!tickers) + return tickers.As>(null); + + return tickers.As(tickers.Data.Select(t => + new Ticker + { + SourceObject = t, + Symbol = t.Symbol, + HighPrice = t.HighPrice, + LowPrice = t.LowPrice, + LastPrice = t.LastPrice, + Price24H = t.OpenPrice, + Volume = t.Volume + })); + } + + async Task>> IBaseRestClient.GetKlinesAsync(string symbol, TimeSpan timespan, DateTime? startTime, DateTime? endTime, int? limit, CancellationToken ct) + { + if (startTime != null || endTime != null) + throw new ArgumentException("CoinEx does not support time based klines requesting", startTime != null ? nameof(startTime) : nameof(endTime)); + + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetKlinesAsync), nameof(symbol)); + + var klines = await ExchangeData.GetKlinesAsync(symbol, GetKlineIntervalFromTimespan(timespan), limit, ct: ct).ConfigureAwait(false); + if (!klines) + return klines.As>(null); + + return klines.As(klines.Data.Select(t => + new Kline + { + SourceObject = t, + OpenPrice = t.OpenPrice, + LowPrice = t.LowPrice, + OpenTime = t.OpenTime, + Volume = t.Volume, + ClosePrice = t.ClosePrice, + HighPrice = t.HighPrice + })); + } + + async Task> IBaseRestClient.GetOrderBookAsync(string symbol, CancellationToken ct) + { + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetOrderBookAsync), nameof(symbol)); + + var book = await ExchangeData.GetOrderBookAsync(symbol, 20, ct: ct).ConfigureAwait(false); + if (!book) + return book.As(null); + + return book.As(new OrderBook + { + SourceObject = book.Data, + Asks = book.Data.Data.Asks.Select(a => new OrderBookEntry { Price = a.Price, Quantity = a.Quantity }), + Bids = book.Data.Data.Bids.Select(b => new OrderBookEntry { Price = b.Price, Quantity = b.Quantity }) + }); + } + + async Task>> IBaseRestClient.GetRecentTradesAsync(string symbol, CancellationToken ct) + { + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetRecentTradesAsync), nameof(symbol)); + + var trades = await ExchangeData.GetTradeHistoryAsync(symbol, ct: ct).ConfigureAwait(false); + if (!trades) + return trades.As>(null); + + return trades.As(trades.Data.Select(t => + new Trade + { + SourceObject = t, + Price = t.Price, + Quantity = t.Quantity, + Symbol = symbol, + Timestamp = t.Timestamp + })); + } + + async Task> ISpotClient.PlaceOrderAsync(string symbol, CommonOrderSide side, CommonOrderType type, decimal quantity, decimal? price, string? accountId, string? clientOrderId, CancellationToken ct) + { + if (price == null && type == CommonOrderType.Limit) + throw new ArgumentException("Price parameter null while placing a limit order", nameof(price)); + + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.PlaceOrderAsync), nameof(symbol)); + + var result = await Trading.PlaceOrderAsync( + symbol, + AccountType.Spot, + side == CommonOrderSide.Sell ? OrderSide.Sell : OrderSide.Buy, + type == CommonOrderType.Limit ? OrderTypeV2.Limit : OrderTypeV2.Market, + quantity, + price, + clientOrderId: clientOrderId, + ct: ct).ConfigureAwait(false); + if (!result) + return result.As(null); + + return result.As(new OrderId { SourceObject = result.Data, Id = result.Data.Id.ToString(CultureInfo.InvariantCulture) }); + } + + async Task> IBaseRestClient.GetOrderAsync(string orderId, string? symbol, CancellationToken ct) + { + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetOrderAsync), nameof(symbol)); + + if (!long.TryParse(orderId, out var id)) + throw new ArgumentException($"Invalid order id for CoinEx {nameof(ISpotClient.GetOrderAsync)}", nameof(orderId)); + + var order = await Trading.GetOrderAsync(symbol!, id, ct: ct).ConfigureAwait(false); + if (!order) + return order.As(null); + + return order.As(new Order + { + SourceObject = order.Data, + Id = order.Data.Id.ToString(CultureInfo.InvariantCulture), + Price = order.Data.Price, + Quantity = order.Data.Quantity, + QuantityFilled = order.Data.QuantityFilled, + Timestamp = order.Data.CreateTime, + Symbol = order.Data.Symbol, + Side = order.Data.Side == OrderSide.Buy ? CommonOrderSide.Buy : CommonOrderSide.Sell, + Status = (order.Data.Status == OrderStatusV2.Canceled || order.Data.Status == OrderStatusV2.PartiallyCanceled) ? CommonOrderStatus.Canceled : order.Data.Status == OrderStatusV2.Filled ? CommonOrderStatus.Filled : CommonOrderStatus.Active, + Type = order.Data.OrderType == OrderTypeV2.Market ? CommonOrderType.Market : order.Data.OrderType == OrderTypeV2.Limit ? CommonOrderType.Limit : CommonOrderType.Other + }); + } + + async Task>> IBaseRestClient.GetOrderTradesAsync(string orderId, string? symbol, CancellationToken ct) + { + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.GetOrderTradesAsync), nameof(symbol)); + + if (!long.TryParse(orderId, out var id)) + throw new ArgumentException($"Invalid order id for CoinEx {nameof(ISpotClient.GetOrderAsync)}", nameof(orderId)); + + var result = await Trading.GetOrderTradesAsync(symbol!, AccountType.Spot, id, ct: ct).ConfigureAwait(false); + if (!result) + return result.As>(null); + + return result.As(result.Data.Items.Select(d => + new UserTrade + { + SourceObject = d, + Id = d.Id.ToString(CultureInfo.InvariantCulture), + Price = d.Price, + Quantity = d.Quantity, + OrderId = d.OrderId.ToString(CultureInfo.InvariantCulture), + Symbol = symbol ?? string.Empty, + Timestamp = d.CreateTime + })); + } + + async Task>> IBaseRestClient.GetOpenOrdersAsync(string? symbol, CancellationToken ct) + { + var openOrders = await Trading.GetOpenOrdersAsync(AccountType.Spot, symbol!, ct: ct).ConfigureAwait(false); + if (!openOrders) + return openOrders.As>(null); + + return openOrders.As(openOrders.Data.Items.Select(o => new Order + { + SourceObject = o, + Id = o.Id.ToString(CultureInfo.InvariantCulture), + Price = o.Price, + Quantity = o.Quantity, + QuantityFilled = o.QuantityFilled, + Timestamp = o.CreateTime, + Symbol = o.Symbol, + Side = o.Side == OrderSide.Buy ? CommonOrderSide.Buy : CommonOrderSide.Sell, + Status = (o.Status == OrderStatusV2.Canceled || o.Status == OrderStatusV2.PartiallyCanceled) ? CommonOrderStatus.Canceled : o.Status == OrderStatusV2.Filled ? CommonOrderStatus.Filled : CommonOrderStatus.Active, + Type = o.OrderType == OrderTypeV2.Market ? CommonOrderType.Market : o.OrderType == OrderTypeV2.Limit ? CommonOrderType.Limit : CommonOrderType.Other + })); + } + + async Task>> IBaseRestClient.GetClosedOrdersAsync(string? symbol, CancellationToken ct) + { + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException($"CoinEx needs the {nameof(symbol)} parameter for the method {nameof(ISpotClient.GetClosedOrdersAsync)}"); + + var result = await Trading.GetClosedOrdersAsync(AccountType.Spot, symbol!, ct: ct).ConfigureAwait(false); + if (!result) + return result.As>(null); + + return result.As(result.Data.Items.Select(o => new Order + { + SourceObject = o, + Id = o.Id.ToString(CultureInfo.InvariantCulture), + Price = o.Price, + Quantity = o.Quantity, + QuantityFilled = o.QuantityFilled, + Timestamp = o.CreateTime, + Symbol = o.Symbol, + Side = o.Side == OrderSide.Buy ? CommonOrderSide.Buy : CommonOrderSide.Sell, + Status = (o.Status == OrderStatusV2.Canceled || o.Status == OrderStatusV2.PartiallyCanceled) ? CommonOrderStatus.Canceled : o.Status == OrderStatusV2.Filled ? CommonOrderStatus.Filled : CommonOrderStatus.Active, + Type = o.OrderType == OrderTypeV2.Market ? CommonOrderType.Market : o.OrderType == OrderTypeV2.Limit ? CommonOrderType.Limit : CommonOrderType.Other + })); + } + + async Task> IBaseRestClient.CancelOrderAsync(string orderId, string? symbol, CancellationToken ct) + { + if (string.IsNullOrEmpty(symbol)) + throw new ArgumentException(nameof(symbol) + " required for CoinEx " + nameof(ISpotClient.CancelOrderAsync), nameof(symbol)); + + if (!long.TryParse(orderId, out var id)) + throw new ArgumentException($"Invalid order id for CoinEx {nameof(ISpotClient.GetOrderAsync)}", nameof(orderId)); + + var result = await Trading.CancelOrderAsync(symbol!, AccountType.Spot, id, ct: ct).ConfigureAwait(false); + if (!result) + return result.As(null); + + return result.As(new OrderId + { + SourceObject = result.Data, + Id = result.Data.Id.ToString(CultureInfo.InvariantCulture) + }); + } + + async Task>> IBaseRestClient.GetBalancesAsync(string? accountId, CancellationToken ct) + { + var balances = await Account.GetBalancesAsync(ct: ct).ConfigureAwait(false); + if (!balances) + return balances.As>(null); + + return balances.As(balances.Data.Select(d => new Balance + { + SourceObject = d, + Asset = d.Asset, + Available = d.Available, + Total = d.Total + })); + } + + private static KlineInterval GetKlineIntervalFromTimespan(TimeSpan timeSpan) + { + if (timeSpan == TimeSpan.FromMinutes(1)) return KlineInterval.OneMinute; + if (timeSpan == TimeSpan.FromMinutes(3)) return KlineInterval.ThreeMinutes; + if (timeSpan == TimeSpan.FromMinutes(5)) return KlineInterval.FiveMinutes; + if (timeSpan == TimeSpan.FromMinutes(15)) return KlineInterval.FiveMinutes; + if (timeSpan == TimeSpan.FromMinutes(30)) return KlineInterval.ThirtyMinutes; + if (timeSpan == TimeSpan.FromHours(1)) return KlineInterval.OneHour; + if (timeSpan == TimeSpan.FromHours(2)) return KlineInterval.TwoHours; + if (timeSpan == TimeSpan.FromHours(4)) return KlineInterval.FourHours; + if (timeSpan == TimeSpan.FromHours(6)) return KlineInterval.SixHours; + if (timeSpan == TimeSpan.FromHours(12)) return KlineInterval.TwelveHours; + if (timeSpan == TimeSpan.FromDays(1)) return KlineInterval.OneDay; + if (timeSpan == TimeSpan.FromDays(3)) return KlineInterval.ThreeDays; + if (timeSpan == TimeSpan.FromDays(7)) return KlineInterval.OneWeek; + + throw new ArgumentException("Unsupported timespan for CoinEx Klines, check supported intervals using CoinEx.Net.Objects.KlineInterval"); + } + #endregion /// protected override ServerError? TryParseError(IMessageAccessor accessor) @@ -465,6 +465,6 @@ internal async Task>> ExecutePaginatedAsync( public override TimeSpan? GetTimeOffset() => null; /// - public ISpotClient CommonSpotClient => null;//this; + public ISpotClient CommonSpotClient => this; } } diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiAccount.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiAccount.cs index 1a9af96..80f14c7 100644 --- a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiAccount.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiAccount.cs @@ -10,6 +10,7 @@ using CoinEx.Net.ExtensionMethods; using CoinEx.Net.Objects.Models.V2; using CoinEx.Net.Interfaces.Clients.SpotApiV2; +using System; namespace CoinEx.Net.Clients.SpotApiV2 { @@ -158,5 +159,102 @@ public async Task>> GetDepositHisto return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/assets/deposit-history"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); } + /// + public async Task> WithdrawAsync(string asset, decimal quanity, string toAddress, MovementMethod? method = null, string? network = null, string? remark = null, Dictionary? extraParameters = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "ccy", asset }, + { "to_address", toAddress }, + }; + parameters.AddOptionalEnum("withdraw_method", method); + parameters.AddString("amount", quanity); + parameters.AddOptional("chain", network); + parameters.AddOptional("remark", remark); + parameters.AddOptional("extra", extraParameters); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/assets/withdraw"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task CancelWithdrawalAsync(long withdrawalId, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "withdraw_id", withdrawalId } + }; + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/assets/cancel-withdraw"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetWithdrawalHistoryAsync(string? asset = null, long? withdrawId = null, WithdrawStatus? status = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddOptional("ccy", asset); + parameters.AddOptional("withdraw_id", withdrawId); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + parameters.AddOptionalEnum("status", status); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/assets/withdraw"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> GetDepositWithdrawalConfigAsync(string asset, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddOptional("ccy", asset); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/assets/deposit-withdraw-config"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task TransferAsync(string asset, AccountType fromAccount, AccountType toAccount, decimal quantity, string? marginSymbol = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "ccy", asset } + }; + parameters.AddEnum("from_account_type", fromAccount); + parameters.AddEnum("to_account_type", toAccount); + parameters.AddString("amount", quantity); + parameters.AddOptional("market", marginSymbol); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/assets/transfer"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetTransfersAsync(string asset, AccountType transferType, string? marginSymbol = null, TransferStatus? status = null, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "ccy", asset } + }; + parameters.AddEnum("transfer_type", transferType); + parameters.AddOptionalMilliseconds("start_time", startTime); + parameters.AddOptionalMilliseconds("end_time", endTime); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + parameters.AddOptionalEnum("status", status); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/assets/transfer-history"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> AddAutoMarketMakerLiquidityAsync(string symbol, decimal baseAssetQuantity, decimal quoteAssetQuantity, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddString("base_ccy_amount", baseAssetQuantity); + parameters.AddString("quote_ccy_amount", quoteAssetQuantity); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/amm/add-liquidity"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> RemoveAutoMarketMakerLiquidityAsync(string symbol, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/amm/remove-liquidity"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } } } diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs index 1a40144..0c95f23 100644 --- a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs @@ -33,6 +33,8 @@ public async Task> PlaceOrderAsync( bool? hide = null, CancellationToken ct = default) { + // TODO BROKERID, SAME WAY AS IN V1? + var parameters = new ParameterCollection() { { "market", symbol } @@ -45,7 +47,10 @@ public async Task> PlaceOrderAsync( parameters.AddOptional("ccy", quantityAsset); parameters.AddOptional("client_id", clientOrderId); parameters.AddOptional("is_hide", hide); - return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + var result = await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + if (result) + _baseClient.InvokeOrderPlaced(new CryptoExchange.Net.CommonObjects.OrderId { Id = result.Data.Id.ToString(), SourceObject = result.Data }); + return result; } /// @@ -62,6 +67,8 @@ public async Task> PlaceStopOrderAsync( bool? hide = null, CancellationToken ct = default) { + // TODO BROKERID, SAME WAY AS IN V1? + var parameters = new ParameterCollection() { { "market", symbol } @@ -75,11 +82,14 @@ public async Task> PlaceStopOrderAsync( parameters.AddOptional("ccy", quantityAsset); parameters.AddOptional("client_id", clientOrderId); parameters.AddOptional("is_hide", hide); - return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/stop-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + var result = await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/stop-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + if (result) + _baseClient.InvokeOrderPlaced(new CryptoExchange.Net.CommonObjects.OrderId { Id = result.Data.StopOrderId.ToString(), SourceObject = result.Data }); + return result; } /// - public async Task> GetOrderAsync(string symbol, string orderId, CancellationToken ct = default) + public async Task> GetOrderAsync(string symbol, long orderId, CancellationToken ct = default) { var parameters = new ParameterCollection() { @@ -145,7 +155,7 @@ public async Task>> GetClosedStop public async Task> EditOrderAsync( string symbol, AccountType accountType, - string orderId, + long orderId, decimal quantity, decimal? price = null, CancellationToken ct = default) @@ -165,7 +175,7 @@ public async Task> EditOrderAsync( public async Task> EditStopOrderAsync( string symbol, AccountType accountType, - string stopOrderId, + long stopOrderId, decimal quantity, decimal triggerPrice, decimal? price = null, @@ -196,7 +206,7 @@ public async Task CancelAllOrdersAsync(string symbol, AccountType } /// - public async Task> CancelOrderAsync(string symbol, AccountType accountType, string orderId, CancellationToken ct = default) + public async Task> CancelOrderAsync(string symbol, AccountType accountType, long orderId, CancellationToken ct = default) { var parameters = new ParameterCollection() { @@ -204,11 +214,14 @@ public async Task> CancelOrderAsync(string symbol, Ac { "order_id", orderId } }; parameters.AddEnum("market_type", accountType); - return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/cancel-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + var result = await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/cancel-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + if (result) + _baseClient.InvokeOrderCanceled(new CryptoExchange.Net.CommonObjects.OrderId { Id = result.Data.Id.ToString(), SourceObject = result.Data }); + return result; } /// - public async Task> CancelStopOrderAsync(string symbol, AccountType accountType, string stopOrderId, CancellationToken ct = default) + public async Task> CancelStopOrderAsync(string symbol, AccountType accountType, long stopOrderId, CancellationToken ct = default) { var parameters = new ParameterCollection() { @@ -216,7 +229,10 @@ public async Task> CancelStopOrderAsync(string sy { "stop_id", stopOrderId } }; parameters.AddEnum("market_type", accountType); - return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/cancel-stop-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + var result = await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/cancel-stop-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + if (result) + _baseClient.InvokeOrderCanceled(new CryptoExchange.Net.CommonObjects.OrderId { Id = result.Data.StopOrderId.ToString(), SourceObject = result.Data }); + return result; } /// @@ -228,7 +244,10 @@ public async Task> CancelOrderByClientOrderIdAsync(st { "client_id", clientOrderId } }; parameters.AddEnum("market_type", accountType); - return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/cancel-order-by-client-id"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + var result = await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/cancel-order-by-client-id"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + if (result) + _baseClient.InvokeOrderCanceled(new CryptoExchange.Net.CommonObjects.OrderId { Id = result.Data.Id.ToString(), SourceObject = result.Data }); + return result; } /// @@ -240,7 +259,10 @@ public async Task> CancelStopOrderByClientOrderId { "client_id", clientStopOrderId } }; parameters.AddEnum("market_type", accountType); - return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/cancel-stop-order-by-client-id"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + var result = await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/cancel-stop-order-by-client-id"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + if (result) + _baseClient.InvokeOrderCanceled(new CryptoExchange.Net.CommonObjects.OrderId { Id = result.Data.StopOrderId.ToString(), SourceObject = result.Data }); + return result; } /// @@ -260,7 +282,7 @@ public async Task>> GetUserTrades } /// - public async Task>> GetUserOrderTradesAsync(string symbol, AccountType accountType, long orderId, int? page = null, int? pageSize = null, CancellationToken ct = default) + public async Task>> GetOrderTradesAsync(string symbol, AccountType accountType, long orderId, int? page = null, int? pageSize = null, CancellationToken ct = default) { var parameters = new ParameterCollection() { diff --git a/CoinEx.Net/CoinEx.Net.xml b/CoinEx.Net/CoinEx.Net.xml index 0353bdd..3f06fa1 100644 --- a/CoinEx.Net/CoinEx.Net.xml +++ b/CoinEx.Net/CoinEx.Net.xml @@ -316,6 +316,14 @@ + + + Get the name of a symbol for CoinEx based on the base and quote asset + + + + + @@ -373,6 +381,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -403,7 +435,7 @@ - + @@ -418,19 +450,19 @@ - + - + - + - + @@ -442,7 +474,7 @@ - + @@ -717,6 +749,21 @@ 1w + + + Deposit/Withdrawal method + + + + + On chain + + + + + Between users + + Options when placing an order @@ -947,6 +994,31 @@ Taker of an existing order book entry + + + Transfer status + + + + + Created + + + + + Asset deducted + + + + + Failed to transfer + + + + + Transfer completed + + Trigger direction @@ -1027,6 +1099,56 @@ Failed + + + Withdrawal status + + + + + Created + + + + + To be audited + + + + + Approved + + + + + Procesing + + + + + Waiting for blockchain confirmation + + + + + Finished + + + + + Withdrawal canceld + + + + + Cancelation failed + + + + + Withdrawal failed + + Extension methods specific to using the CoinEx API @@ -1704,6 +1826,101 @@ Cancelation token + + + Withdraw funds to an external address or another CoinEx user + + + Asset to withdraw + Withdrawal quantity + Withdrawal address.The withdrawal address needs to be added to the IP whitelist via Developer Console + Withdrawal methods (On-chain or inter-user transfer). Default as on-chain withdrawal + Network name. Required for On-chain, not required for inter-user transfer + Withdrawal note + If it is a withdrawal from the KDA chain, you need to append the chain_id field to the extra field + Cancelation token + + + + + Cancel a pending withdrawal + + + The withdrawal id + Cancelation token + + + + + Get withdrawal history + + + Filter by asset + Filter by withdrawal id + Filter by status + Page number + Page size + Cancelation token + + + + + Get withdraw and deposit information for an asset + + + The asset + Cancelation token + + + + + Transfer between accounts + + + Asset to transfer + From account + To account + Quantity to transfer + Margin symbol, only needed when from or to account type is Margin + Cancelation token + + + + + Get transfer history + + + Asset + Transfer type. Must be either Margin or Futures + Filter by margin symbol + Filter by status + Filter by start time + Filter by end time + Page number + Page size + Cancelation token + + + + + Add AAM liquidity + + + Symbol + Base asset quantity to add + Quote asset quantity to add + Cancelation token + + + + + Remove AAM liquidity. Currently only support withdrawing all liquidity + + + Symbol + Cancelation token + + CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. @@ -1809,7 +2026,7 @@ Cancelation Token - + Get an order by id @@ -1875,7 +2092,7 @@ Cancelation Token - + Edit an active order @@ -1888,7 +2105,7 @@ Cancelation Token - + Edit an active stop order @@ -1913,7 +2130,7 @@ Cancelation Token - + Cancel an active order @@ -1924,7 +2141,7 @@ Cancelation Token - + Cancel an active stop order @@ -1972,6 +2189,19 @@ Cancelation Token + + + Get trades for a specific order + + + Symbol + Account type + The order id + Page number + Page size + Cancelation Token + + Spot streams @@ -3175,6 +3405,31 @@ The quantity of the transaction + + + Automated market maker liquidity info + + + + + Symbol + + + + + Base asset amount in AMM account + + + + + Quote asset amount in AMM account + + + + + Liquidity percentage in AMM account + + Automated Market Maker liquidity info @@ -3505,6 +3760,121 @@ Deposit address + + + Deposit and withdrawal configuration and info + + + + + Asset information + + + + + Available networks + + + + + Asset infos + + + + + Asset name + + + + + Is deposit enabled + + + + + Is withdrawal enabled + + + + + Is inter user transfer enabled + + + + + Is st + + + + + Network info + + + + + Network name + + + + + Min deposit quantity + + + + + Min withdraw quantity + + + + + Is deposit enabled + + + + + Is withdrawal enabled + + + + + Number of confirmations needed + + + + + Number of confirmations before transaction is irreversable + + + + + Deflation rate + + + + + Withdrawal fee + + + + + Withdrawal precision + + + + + Memo + + + + + Is memo required for deposits + + + + + Blockchain explorer url + + Index price @@ -3685,7 +4055,7 @@ Order info - + Order id @@ -3710,7 +4080,7 @@ Order side - + Order type @@ -3900,7 +4270,7 @@ Stop order id - + Stop order id @@ -4155,6 +4525,46 @@ Fee for taker trades + + + Transfer info + + + + + Margin symbol if either from account or to account was Margin + + + + + Creation time + + + + + From account type + + + + + To account type + + + + + Asset + + + + + Transfer quantity + + + + + Transfer status + + User trade info @@ -4200,6 +4610,86 @@ Quantity traded + + + Withdrawal info + + + + + Withdrawal id + + + + + Creation time + + + + + Asset + + + + + Network + + + + + Memo + + + + + Quantity + + + + + Actual withdrawal quantity + + + + + Fee + + + + + Transaction id + + + + + Destination address + + + + + Number of confirmations + + + + + Blockchain explorer url for the transaction + + + + + Blockchain explorer url for the deposit address + + + + + Status + + + + + Remark + + Options for CoinEx SymbolOrderBook diff --git a/CoinEx.Net/Enums/MovementMethod.cs b/CoinEx.Net/Enums/MovementMethod.cs new file mode 100644 index 0000000..54cac76 --- /dev/null +++ b/CoinEx.Net/Enums/MovementMethod.cs @@ -0,0 +1,24 @@ +using CryptoExchange.Net.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Deposit/Withdrawal method + /// + public enum MovementMethod + { + /// + /// On chain + /// + [Map("on_chain")] + OnChain, + /// + /// Between users + /// + [Map("inter_user")] + InterUser + } +} diff --git a/CoinEx.Net/Enums/TransferStatus.cs b/CoinEx.Net/Enums/TransferStatus.cs new file mode 100644 index 0000000..8e2672d --- /dev/null +++ b/CoinEx.Net/Enums/TransferStatus.cs @@ -0,0 +1,34 @@ +using CryptoExchange.Net.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Transfer status + /// + public enum TransferStatus + { + /// + /// Created + /// + [Map("created")] + Created, + /// + /// Asset deducted + /// + [Map("deducted")] + Deducted, + /// + /// Failed to transfer + /// + [Map("failed")] + Failed, + /// + /// Transfer completed + /// + [Map("finished")] + Finished + } +} diff --git a/CoinEx.Net/Enums/WithdrawStatusV2.cs b/CoinEx.Net/Enums/WithdrawStatusV2.cs new file mode 100644 index 0000000..70c976d --- /dev/null +++ b/CoinEx.Net/Enums/WithdrawStatusV2.cs @@ -0,0 +1,59 @@ +using CryptoExchange.Net.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Withdrawal status + /// + public enum WithdrawStatusV2 + { + /// + /// Created + /// + [Map("created")] + Created, + /// + /// To be audited + /// + [Map("audit_required")] + AuditRequired, + /// + /// Approved + /// + [Map("audited")] + Audited, + /// + /// Procesing + /// + [Map("processing")] + Processing, + /// + /// Waiting for blockchain confirmation + /// + [Map("confirming")] + Confirming, + /// + /// Finished + /// + [Map("finished")] + Finished, + /// + /// Withdrawal canceld + /// + [Map("cancelled")] + Canceled, + /// + /// Cancelation failed + /// + [Map("cancellation_failed")] + CancelationFailed, + /// + /// Withdrawal failed + /// + [Map("failed")] + Failed + } +} diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiAccount.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiAccount.cs index b468759..c743e67 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiAccount.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiAccount.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics.Metrics; using System.Text.RegularExpressions; +using System; namespace CoinEx.Net.Interfaces.Clients.SpotApiV2 { @@ -151,5 +152,100 @@ public interface ICoinExRestClientSpotApiAccount /// Cancelation token /// Task>> GetDepositHistoryAsync(string asset, string? transactionId = null, DepositStatus? status = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Withdraw funds to an external address or another CoinEx user + /// + /// + /// Asset to withdraw + /// Withdrawal quantity + /// Withdrawal address.The withdrawal address needs to be added to the IP whitelist via Developer Console + /// Withdrawal methods (On-chain or inter-user transfer). Default as on-chain withdrawal + /// Network name. Required for On-chain, not required for inter-user transfer + /// Withdrawal note + /// If it is a withdrawal from the KDA chain, you need to append the chain_id field to the extra field + /// Cancelation token + /// + Task> WithdrawAsync(string asset, decimal quanity, string toAddress, MovementMethod? method = null, string? network = null, string? remark = null, Dictionary? extraParameters = null, CancellationToken ct = default); + + /// + /// Cancel a pending withdrawal + /// + /// + /// The withdrawal id + /// Cancelation token + /// + Task CancelWithdrawalAsync(long withdrawalId, CancellationToken ct = default); + + /// + /// Get withdrawal history + /// + /// + /// Filter by asset + /// Filter by withdrawal id + /// Filter by status + /// Page number + /// Page size + /// Cancelation token + /// + Task>> GetWithdrawalHistoryAsync(string? asset = null, long? withdrawId = null, WithdrawStatus? status = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get withdraw and deposit information for an asset + /// + /// + /// The asset + /// Cancelation token + /// + Task> GetDepositWithdrawalConfigAsync(string asset, CancellationToken ct = default); + + /// + /// Transfer between accounts + /// + /// + /// Asset to transfer + /// From account + /// To account + /// Quantity to transfer + /// Margin symbol, only needed when from or to account type is Margin + /// Cancelation token + /// + Task TransferAsync(string asset, AccountType fromAccount, AccountType toAccount, decimal quantity, string? marginSymbol = null, CancellationToken ct = default); + + /// + /// Get transfer history + /// + /// + /// Asset + /// Transfer type. Must be either Margin or Futures + /// Filter by margin symbol + /// Filter by status + /// Filter by start time + /// Filter by end time + /// Page number + /// Page size + /// Cancelation token + /// + Task>> GetTransfersAsync(string asset, AccountType transferType, string? marginSymbol = null, TransferStatus? status = null, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Add AAM liquidity + /// + /// + /// Symbol + /// Base asset quantity to add + /// Quote asset quantity to add + /// Cancelation token + /// + Task> AddAutoMarketMakerLiquidityAsync(string symbol, decimal baseAssetQuantity, decimal quoteAssetQuantity, CancellationToken ct = default); + + /// + /// Remove AAM liquidity. Currently only support withdrawing all liquidity + /// + /// + /// Symbol + /// Cancelation token + /// + Task> RemoveAutoMarketMakerLiquidityAsync(string symbol, CancellationToken ct = default); } } diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs index 4ed551e..0032640 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs @@ -77,7 +77,7 @@ Task> PlaceStopOrderAsync( /// Order id /// Cancelation Token /// - Task> GetOrderAsync(string symbol, string orderId, CancellationToken ct = default); + Task> GetOrderAsync(string symbol, long orderId, CancellationToken ct = default); /// /// Get a list of open orders @@ -149,7 +149,7 @@ Task> PlaceStopOrderAsync( Task> EditOrderAsync( string symbol, AccountType accountType, - string orderId, + long orderId, decimal quantity, decimal? price = null, CancellationToken ct = default); @@ -169,7 +169,7 @@ Task> EditOrderAsync( Task> EditStopOrderAsync( string symbol, AccountType accountType, - string stopOrderId, + long stopOrderId, decimal quantity, decimal triggerPrice, decimal? price = null, @@ -195,7 +195,7 @@ Task> EditStopOrderAsync( /// Id of order to cancel /// Cancelation Token /// - Task> CancelOrderAsync(string symbol, AccountType accountType, string orderId, CancellationToken ct = default); + Task> CancelOrderAsync(string symbol, AccountType accountType, long orderId, CancellationToken ct = default); /// /// Cancel an active stop order @@ -206,7 +206,7 @@ Task> EditStopOrderAsync( /// Id of stop order to cancel /// Cancelation Token /// - Task> CancelStopOrderAsync(string symbol, AccountType accountType, string stopOrderId, CancellationToken ct = default); + Task> CancelStopOrderAsync(string symbol, AccountType accountType, long stopOrderId, CancellationToken ct = default); /// /// Cancel an active order by its client order id @@ -244,5 +244,18 @@ Task> EditStopOrderAsync( /// Cancelation Token /// Task>> GetUserTradesAsync(string symbol, AccountType accountType, OrderSide? side = null, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get trades for a specific order + /// + /// + /// Symbol + /// Account type + /// The order id + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetOrderTradesAsync(string symbol, AccountType accountType, long orderId, int? page = null, int? pageSize = null, CancellationToken ct = default); } } diff --git a/CoinEx.Net/Objects/Internal/CoinExApiResult.cs b/CoinEx.Net/Objects/Internal/CoinExApiResult.cs index fb522cc..0b65b1f 100644 --- a/CoinEx.Net/Objects/Internal/CoinExApiResult.cs +++ b/CoinEx.Net/Objects/Internal/CoinExApiResult.cs @@ -20,6 +20,8 @@ internal class CoinExPageApiResult : CoinExApiResult { [JsonPropertyName("pagination")] public CoinExPage Pagination { get; set; } = null!; + [JsonPropertyName("paginatation")] + public CoinExPage PaginationTypo { set => Pagination = value; } } internal class CoinExPage diff --git a/CoinEx.Net/Objects/Models/V2/CoinExAamLiquidity.cs b/CoinEx.Net/Objects/Models/V2/CoinExAamLiquidity.cs new file mode 100644 index 0000000..77c1c82 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExAamLiquidity.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Automated market maker liquidity info + /// + public record CoinExAamLiquidity + { + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Base asset amount in AMM account + /// + [JsonPropertyName("base_ccy_amount")] + public decimal BaseAssetQuantity { get; set; } + /// + /// Quote asset amount in AMM account + /// + [JsonPropertyName("quote_ccy_amount")] + public decimal QuoteAssetQuantity { get; set; } + /// + /// Liquidity percentage in AMM account + /// + [JsonPropertyName("liquidity_proportion")] + public decimal LiquidityProportion { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExDeposit.cs b/CoinEx.Net/Objects/Models/V2/CoinExDeposit.cs index 7fb2028..58941f2 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExDeposit.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExDeposit.cs @@ -80,6 +80,6 @@ public record CoinExDeposit /// Deposit method /// [JsonPropertyName("deposit_method")] - public string DepositMethod { get; set; } = string.Empty; + public MovementMethod DepositMethod { get; set; } } } diff --git a/CoinEx.Net/Objects/Models/V2/CoinExDepositWithdrawalConfig.cs b/CoinEx.Net/Objects/Models/V2/CoinExDepositWithdrawalConfig.cs new file mode 100644 index 0000000..e67b2ac --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExDepositWithdrawalConfig.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Deposit and withdrawal configuration and info + /// + public record CoinExDepositWithdrawalConfig + { + /// + /// Asset information + /// + [JsonPropertyName("asset")] + public CoinExDepositWithdrawalAsset Asset { get; set; } = null!; + /// + /// Available networks + /// + [JsonPropertyName("chains")] + public IEnumerable Networks { get; set; } = null!; + } + + /// + /// Asset infos + /// + public record CoinExDepositWithdrawalAsset + { + /// + /// Asset name + /// + [JsonPropertyName("ccy")] + public string Asset { get; set; } = string.Empty; + /// + /// Is deposit enabled + /// + [JsonPropertyName("deposit_enabled")] + public bool DepositEnabled { get; set; } + /// + /// Is withdrawal enabled + /// + [JsonPropertyName("withdraw_enabled")] + public bool WithdrawEnabled { get; set; } + /// + /// Is inter user transfer enabled + /// + [JsonPropertyName("inter_transfer_enabled")] + public bool InterTransferEnabled { get; set; } + /// + /// Is st + /// + [JsonPropertyName("is_st")] + public bool IsSt { get; set; } + } + + /// + /// Network info + /// + public record CoinExNetwork + { + /// + /// Network name + /// + [JsonPropertyName("chain")] + public string Network { get; set; } = string.Empty; + /// + /// Min deposit quantity + /// + [JsonPropertyName("min_deposit_amount")] + public decimal MinDepositQuantity { get; set; } + /// + /// Min withdraw quantity + /// + [JsonPropertyName("min_withdraw_amount")] + public decimal MinWithdrawQuantity { get; set; } + /// + /// Is deposit enabled + /// + [JsonPropertyName("deposit_enabled")] + public bool DepositEnabled { get; set; } + /// + /// Is withdrawal enabled + /// + [JsonPropertyName("withdraw_enabled")] + public bool WithdrawEnabled { get; set; } + /// + /// Number of confirmations needed + /// + [JsonPropertyName("safe_confirmations")] + public int? SafeConfirmations { get; set; } + /// + /// Number of confirmations before transaction is irreversable + /// + [JsonPropertyName("irreversible_confirmations")] + public int? IrreversableConfirmations { get; set; } + /// + /// Deflation rate + /// + [JsonPropertyName("deflation_rate")] + public decimal? DeflationRate { get; set; } + /// + /// Withdrawal fee + /// + [JsonPropertyName("withdrawal_fee")] + public decimal? WithdrawalFee { get; set; } + /// + /// Withdrawal precision + /// + [JsonPropertyName("withdrawal_precision")] + public int? WithdrawalPrecision { get; set; } + /// + /// Memo + /// + [JsonPropertyName("memo")] + public string Memo { get; set; } = string.Empty; + /// + /// Is memo required for deposits + /// + [JsonPropertyName("is_memo_required_for_deposit")] + public bool MemoRequired { get; set; } + /// + /// Blockchain explorer url + /// + [JsonPropertyName("explorer_asset_url")] + public string ExplorerUrl { get; set; } = string.Empty; + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExOrder.cs b/CoinEx.Net/Objects/Models/V2/CoinExOrder.cs index ab67182..6d2c1e0 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExOrder.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExOrder.cs @@ -13,7 +13,7 @@ public record CoinExOrder /// Order id /// [JsonPropertyName("order_id")] - public string OrderId { get; set; } = string.Empty; + public long Id { get; set; } /// /// Symbol /// @@ -38,7 +38,7 @@ public record CoinExOrder /// Order type /// [JsonPropertyName("type")] - public OrderTypeV2 Type { get; set; } + public OrderTypeV2 OrderType { get; set; } /// /// Order quantity /// diff --git a/CoinEx.Net/Objects/Models/V2/CoinExStopId.cs b/CoinEx.Net/Objects/Models/V2/CoinExStopId.cs index 4711da9..cc2bf25 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExStopId.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExStopId.cs @@ -14,6 +14,6 @@ public record CoinExStopId /// Stop order id /// [JsonPropertyName("stop_id")] - public string StopId { get; set; } = string.Empty; + public long StopOrderId { get; set; } } } diff --git a/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs b/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs index 46cb4e4..50be6cb 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs @@ -15,7 +15,7 @@ public record CoinExStopOrder /// Order id /// [JsonPropertyName("stop_id")] - public string StopOrderId { get; set; } = string.Empty; + public long StopOrderId { get; set; } /// /// Symbol /// diff --git a/CoinEx.Net/Objects/Models/V2/CoinExTransfer.cs b/CoinEx.Net/Objects/Models/V2/CoinExTransfer.cs new file mode 100644 index 0000000..43b910a --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExTransfer.cs @@ -0,0 +1,50 @@ +using CoinEx.Net.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Transfer info + /// + public record CoinExTransfer + { + /// + /// Margin symbol if either from account or to account was Margin + /// + [JsonPropertyName("margin_market")] + public string? MarginSymbol { get; set; } + /// + /// Creation time + /// + [JsonPropertyName("created_at")] + public DateTime CreateTime { get; set; } + /// + /// From account type + /// + [JsonPropertyName("from_account_type")] + public AccountType FromAccountType { get; set; } + /// + /// To account type + /// + [JsonPropertyName("to_account_type")] + public AccountType ToAccountType { get; set; } + /// + /// Asset + /// + [JsonPropertyName("ccy")] + public string? Asset { get; set; } = string.Empty; + /// + /// Transfer quantity + /// + [JsonPropertyName("amount")] + public decimal Quantity { get; set; } + /// + /// Transfer status + /// + [JsonPropertyName("status")] + public TransferStatus Status { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExWithdrawal.cs b/CoinEx.Net/Objects/Models/V2/CoinExWithdrawal.cs new file mode 100644 index 0000000..79cb456 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExWithdrawal.cs @@ -0,0 +1,91 @@ +using CoinEx.Net.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Withdrawal info + /// + public record CoinExWithdrawal + { + /// + /// Withdrawal id + /// + [JsonPropertyName("withdraw_id")] + public long Id { get; set; } + /// + /// Creation time + /// + [JsonPropertyName("created_at")] + public DateTime CreateTime { get; set; } + /// + /// Asset + /// + [JsonPropertyName("ccy")] + public string Asset { get; set; } = string.Empty; + /// + /// Network + /// + [JsonPropertyName("chain")] + public string Network { get; set; } = string.Empty; + /// + /// Memo + /// + [JsonPropertyName("memo")] + public string Memo { get; set; } = string.Empty; + /// + /// Quantity + /// + [JsonPropertyName("amount")] + public decimal Quantity { get; set; } + /// + /// Actual withdrawal quantity + /// + [JsonPropertyName("actual_amount")] + public decimal ActualQuantity { get; set; } + /// + /// Fee + /// + [JsonPropertyName("tx_fee")] + public decimal Fee { get; set; } + /// + /// Transaction id + /// + [JsonPropertyName("tx_id")] + public string TransactionId { get; set; } = string.Empty; + /// + /// Destination address + /// + [JsonPropertyName("to_address")] + public string ToAddress { get; set; } = string.Empty; + /// + /// Number of confirmations + /// + [JsonPropertyName("confirmation")] + public int Confirmations { get; set; } + /// + /// Blockchain explorer url for the transaction + /// + [JsonPropertyName("explorer_tx_url")] + public string TransactionExplorerUrl { get; set; } = string.Empty; + /// + /// Blockchain explorer url for the deposit address + /// + [JsonPropertyName("explorer_address_url")] + public string WithdrawalAddressExplorerUrl { get; set; } = string.Empty; + + /// + /// Status + /// + [JsonPropertyName("status")] + public WithdrawStatusV2 Status { get; set; } + /// + /// Remark + /// + [JsonPropertyName("remark")] + public string? Remark { get; set; } = string.Empty; + } +} From f8d89c9dc1592cf74f108ada7d11a377be13e1bb Mon Sep 17 00:00:00 2001 From: JKorf Date: Thu, 4 Apr 2024 16:43:05 +0200 Subject: [PATCH 11/17] wip --- CoinEx.Net/Clients/CoinExRestClient.cs | 6 + CoinEx.Net/Clients/CoinExSocketClient.cs | 6 + .../FuturesApi/CoinExRestClientFuturesApi.cs | 146 +++++ .../CoinExRestClientFuturesApiAccount.cs | 39 ++ .../CoinExRestClientFuturesApiExchangeData.cs | 141 +++++ .../CoinExRestClientFuturesApiTrading.cs | 26 + .../CoinExSocketClientFuturesApi.cs | 108 ++++ .../CoinExRestClientSpotApiTrading.cs | 5 +- CoinEx.Net/CoinEx.Net.xml | 553 ++++++++++++++++++ CoinEx.Net/Enums/ContractType.cs | 24 + CoinEx.Net/Enums/PositionSide.cs | 24 + .../FuturesApi/ICoinExRestClientFuturesApi.cs | 27 + .../ICoinExRestClientFuturesApiAccount.cs | 19 + ...ICoinExRestClientFuturesApiExchangeData.cs | 132 +++++ .../ICoinExRestClientFuturesApiTrading.cs | 17 + .../ICoinExSocketClientFuturesApi.cs | 21 + .../Interfaces/Clients/ICoinExRestClient.cs | 7 +- .../Interfaces/Clients/ICoinExSocketClient.cs | 7 +- CoinEx.Net/Objects/Models/V2/CoinExBasis.cs | 29 + .../Objects/Models/V2/CoinExFundingRate.cs | 44 ++ .../Models/V2/CoinExFundingRateHistory.cs | 34 ++ .../Objects/Models/V2/CoinExFuturesSymbol.cs | 70 +++ .../Objects/Models/V2/CoinExFuturesTicker.cs | 22 + .../Objects/Models/V2/CoinExIndexPrice.cs | 2 +- .../Objects/Models/V2/CoinExLiquidation.cs | 45 ++ .../Objects/Models/V2/CoinExPositionLevel.cs | 51 ++ .../Objects/Options/CoinExRestOptions.cs | 6 + .../Objects/Options/CoinExSocketOptions.cs | 6 + 28 files changed, 1612 insertions(+), 5 deletions(-) create mode 100644 CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs create mode 100644 CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs create mode 100644 CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiExchangeData.cs create mode 100644 CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs create mode 100644 CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs create mode 100644 CoinEx.Net/Enums/ContractType.cs create mode 100644 CoinEx.Net/Enums/PositionSide.cs create mode 100644 CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApi.cs create mode 100644 CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs create mode 100644 CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiExchangeData.cs create mode 100644 CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs create mode 100644 CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExBasis.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExFundingRate.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExFundingRateHistory.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExFuturesSymbol.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExFuturesTicker.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExLiquidation.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExPositionLevel.cs diff --git a/CoinEx.Net/Clients/CoinExRestClient.cs b/CoinEx.Net/Clients/CoinExRestClient.cs index 763307c..85f4c99 100644 --- a/CoinEx.Net/Clients/CoinExRestClient.cs +++ b/CoinEx.Net/Clients/CoinExRestClient.cs @@ -5,6 +5,8 @@ using System; using CoinEx.Net.Objects.Options; using CryptoExchange.Net.Clients; +using CoinEx.Net.Interfaces.Clients.FuturesApi; +using CoinEx.Net.Clients.FuturesApi; namespace CoinEx.Net.Clients { @@ -13,6 +15,8 @@ public class CoinExRestClient : BaseRestClient, ICoinExRestClient { #region Api clients /// + public ICoinExRestClientFuturesApi FuturesApi { get; } + /// public Interfaces.Clients.SpotApiV1.ICoinExRestClientSpotApi SpotApi { get; } /// public Interfaces.Clients.SpotApiV2.ICoinExRestClientSpotApi SpotApiV2 { get; } @@ -41,6 +45,7 @@ public CoinExRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, A optionsDelegate(options); Initialize(options); + FuturesApi = AddApiClient(new CoinExRestClientFuturesApi(_logger, httpClient, options)); SpotApi = AddApiClient(new SpotApiV1.CoinExRestClientSpotApi(_logger, httpClient, options)); SpotApiV2 = AddApiClient(new SpotApiV2.CoinExRestClientSpotApi(_logger, httpClient, options)); } @@ -61,6 +66,7 @@ public static void SetDefaultOptions(Action optionsDelegate) /// public void SetApiCredentials(ApiCredentials credentials) { + FuturesApi.SetApiCredentials(credentials); SpotApi.SetApiCredentials(credentials); SpotApiV2.SetApiCredentials(credentials); } diff --git a/CoinEx.Net/Clients/CoinExSocketClient.cs b/CoinEx.Net/Clients/CoinExSocketClient.cs index 3c4ece6..787dca0 100644 --- a/CoinEx.Net/Clients/CoinExSocketClient.cs +++ b/CoinEx.Net/Clients/CoinExSocketClient.cs @@ -4,6 +4,8 @@ using System; using CoinEx.Net.Objects.Options; using CryptoExchange.Net.Clients; +using CoinEx.Net.Interfaces.Clients.FuturesApi; +using CoinEx.Net.Clients.FuturesApi; namespace CoinEx.Net.Clients { @@ -12,6 +14,8 @@ public class CoinExSocketClient : BaseSocketClient, ICoinExSocketClient { #region Api clients + /// + public ICoinExSocketClientFuturesApi FuturesApi { get; } /// public Interfaces.Clients.SpotApiV2.ICoinExSocketClientSpotApi SpotApiV2 { get; } /// @@ -48,6 +52,7 @@ public CoinExSocketClient(Action optionsDelegate, ILoggerFa optionsDelegate(options); Initialize(options); + FuturesApi = AddApiClient(new CoinExSocketClientFuturesApi(_logger, options)); SpotApi = AddApiClient(new SpotApiV1.CoinExSocketClientSpotApi(_logger, options)); SpotApiV2 = AddApiClient(new SpotApiV2.CoinExSocketClientSpotApi(_logger, options)); } @@ -67,6 +72,7 @@ public static void SetDefaultOptions(Action optionsDelegate /// public void SetApiCredentials(ApiCredentials credentials) { + FuturesApi.SetApiCredentials(credentials); SpotApiV2.SetApiCredentials(credentials); SpotApi.SetApiCredentials(credentials); } diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs new file mode 100644 index 0000000..19a6b86 --- /dev/null +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs @@ -0,0 +1,146 @@ +using CryptoExchange.Net; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Authentication; +using CoinEx.Net.Objects.Internal; +using CryptoExchange.Net.CommonObjects; +using CryptoExchange.Net.Interfaces.CommonClients; +using Microsoft.Extensions.Logging; +using CoinEx.Net.Objects.Options; +using CryptoExchange.Net.Interfaces; +using CryptoExchange.Net.Converters.MessageParsing; +using CryptoExchange.Net.Clients; +using CryptoExchange.Net.Converters.SystemTextJson; +using CoinEx.Net.Objects.Models.V2; +using CoinEx.Net.Interfaces.Clients.SpotApiV2; +using CoinEx.Net.Enums; +using System.Linq; +using System.Globalization; +using CoinEx.Net.Interfaces.Clients.FuturesApi; + +namespace CoinEx.Net.Clients.FuturesApi +{ + /// + public class CoinExRestClientFuturesApi : RestApiClient, ICoinExRestClientFuturesApi + { + #region fields + /// + public new CoinExRestOptions ClientOptions => (CoinExRestOptions)base.ClientOptions; + #endregion + + /// + public string ExchangeName => "CoinEx"; + + #region Api clients + /// + public ICoinExRestClientFuturesApiAccount Account { get; } + /// + public ICoinExRestClientFuturesApiExchangeData ExchangeData { get; } + /// + public ICoinExRestClientFuturesApiTrading Trading { get; } + #endregion + + internal readonly string _brokerId; + + #region ctor + internal CoinExRestClientFuturesApi(ILogger logger, HttpClient? httpClient, CoinExRestOptions options) : + base(logger, httpClient, options.Environment.RestBaseAddress, options, options.FuturesOptions) + { + Account = new CoinExRestClientFuturesApiAccount(this); + ExchangeData = new CoinExRestClientFuturesApiExchangeData(this); + Trading = new CoinExRestClientFuturesApiTrading(this); + + ParameterPositions[HttpMethod.Delete] = HttpMethodParameterPosition.InUri; + + _brokerId = !string.IsNullOrEmpty(options.BrokerId) ? options.BrokerId! : "147866029"; + + } + #endregion + + /// + protected override IStreamMessageAccessor CreateAccessor() => new SystemTextJsonStreamMessageAccessor(); + /// + protected override IMessageSerializer CreateSerializer() => new SystemTextJsonMessageSerializer(); + + /// + protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials) + => new CoinExV2AuthenticationProvider(credentials); + + #region methods + internal async Task ExecuteAsync(Uri uri, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false) + { + var result = await SendRequestAsync(uri, method, ct, parameters, signed).ConfigureAwait(false); + if (!result) + return result.AsDataless(); + + if (result.Data.Code != 0) + return result.AsDatalessError(new ServerError(result.Data.Code, result.Data.Message!)); + + return result.AsDataless(); + } + + internal async Task> ExecuteAsync(Uri uri, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false) where T : class + { + var result = await SendRequestAsync>(uri, method, ct, parameters, signed).ConfigureAwait(false); + if (!result) + return result.As(default); + + if (result.Data.Code != 0) + return result.AsError(new ServerError(result.Data.Code, result.Data.Message!)); + + return result.As(result.Data.Data); + } + + internal async Task>> ExecutePaginatedAsync(Uri uri, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false) where T : class + { + var result = await SendRequestAsync>>(uri, method, ct, parameters, signed).ConfigureAwait(false); + if (!result) + return result.As>(default); + + if (result.Data.Code != 0) + return result.AsError>(new ServerError(result.Data.Code, result.Data.Message!)); + + var resultPage = new CoinExPaginated + { + HasNext = result.Data.Pagination.HasNext, + Total = result.Data.Pagination.Total, + Items = result.Data.Data + }; + + return result.As(resultPage); + } + + internal Uri GetUri(string path) => new Uri(BaseAddress.AppendPath(path)); + #endregion + + /// + protected override ServerError? TryParseError(IMessageAccessor accessor) + { + if (!accessor.IsJson) + return new ServerError(accessor.GetOriginalString()); + + var code = accessor.GetValue(MessagePath.Get().Property("code")); + if (code == 0) + return null; + + var msg = accessor.GetValue(MessagePath.Get().Property("message")); + if (msg == null) + return new ServerError(accessor.GetOriginalString()); + + if (code == null) + return new ServerError(msg); + + return new ServerError(code.Value, msg); + } + + /// + public override TimeSyncInfo? GetTimeSyncInfo() => null; + + /// + public override TimeSpan? GetTimeOffset() => null; + } +} diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs new file mode 100644 index 0000000..c96b8d0 --- /dev/null +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs @@ -0,0 +1,39 @@ +using CoinEx.Net.Converters; +using CoinEx.Net.Enums; +using CryptoExchange.Net; +using CryptoExchange.Net.Objects; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.ExtensionMethods; +using CoinEx.Net.Objects.Models.V2; +using CoinEx.Net.Interfaces.Clients.SpotApiV2; +using System; +using CoinEx.Net.Interfaces.Clients.FuturesApi; + +namespace CoinEx.Net.Clients.FuturesApi +{ + /// + public class CoinExRestClientFuturesApiAccount : ICoinExRestClientFuturesApiAccount + { + private readonly CoinExRestClientFuturesApi _baseClient; + + internal CoinExRestClientFuturesApiAccount(CoinExRestClientFuturesApi baseClient) + { + _baseClient = baseClient; + } + + /// + public async Task> GetTradingFeesAsync(string symbol, AccountType accountType, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddEnum("market_type", accountType); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/account/trade-fee-rate"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + } +} diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiExchangeData.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiExchangeData.cs new file mode 100644 index 0000000..ca7d67c --- /dev/null +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiExchangeData.cs @@ -0,0 +1,141 @@ +using CryptoExchange.Net.Objects; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Objects.Models.V2; +using CoinEx.Net.Enums; +using CoinEx.Net.Interfaces.Clients.SpotApiV2; +using CoinEx.Net.Interfaces.Clients.FuturesApi; +using System; + +namespace CoinEx.Net.Clients.FuturesApi +{ + /// + public class CoinExRestClientFuturesApiExchangeData : ICoinExRestClientFuturesApiExchangeData + { + private readonly CoinExRestClientFuturesApi _baseClient; + + internal CoinExRestClientFuturesApiExchangeData(CoinExRestClientFuturesApi baseClient) + { + _baseClient = baseClient; + } + + /// + public async Task>> GetSymbolsAsync(IEnumerable? symbols = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddOptional("market", symbols == null ? null :string.Join(",", symbols)); + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/futures/market"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task>> GetTickersAsync(IEnumerable? symbols = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddOptional("market", symbols == null ? null : string.Join(",", symbols)); + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/futures/ticker"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task> GetOrderBookAsync(string symbol, int limit, string? mergeLevel = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "limit", limit }, + { "interval", mergeLevel ?? "0" } + }; + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/futures/depth"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task>> GetTradeHistoryAsync(string symbol, int? limit = null, long? lastId = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddOptional("limit", limit); + parameters.AddOptional("last_id", lastId); + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/futures/deals"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task>> GetKlinesAsync(string symbol, KlineInterval interval, int? limit = null, PriceType? priceType = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddEnum("period", interval); + parameters.AddOptionalEnum("price_type", priceType); + parameters.AddOptional("limit", limit); + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/futures/kline"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task>> GetIndexPricesAsync(IEnumerable? symbols = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddOptional("market", symbols == null ? null : string.Join(",", symbols)); + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/futures/index"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task>> GetFundingRatesAsync(IEnumerable? symbols = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddOptional("market", symbols == null ? null : string.Join(",", symbols)); + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/futures/funding-rate"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task>> GetFundingRateHistoryAsync(string symbol, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddOptionalMilliseconds("start_time", startTime); + parameters.AddOptionalMilliseconds("end_time", endTime); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/futures/funding-rate-history"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task>> GetPositionLevelsAsync(IEnumerable? symbols = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddOptional("market", symbols == null ? null : string.Join(",", symbols)); + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/futures/position-level"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task>> GetLiquidationHistoryAsync(string symbol, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddOptionalMilliseconds("start_time", startTime); + parameters.AddOptionalMilliseconds("end_time", endTime); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/futures/liquidation-history"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + + /// + public async Task>> GetBasisHistoryAsync(string symbol, DateTime? startTime = null, DateTime? endTime = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddOptionalMilliseconds("start_time", startTime); + parameters.AddOptionalMilliseconds("end_time", endTime); + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/futures/basis-history"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + } + } +} diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs new file mode 100644 index 0000000..4995238 --- /dev/null +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs @@ -0,0 +1,26 @@ +using CoinEx.Net.Enums; +using CryptoExchange.Net.Objects; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Objects.Models.V2; +using System; +using CoinEx.Net.Interfaces.Clients.SpotApiV2; +using CryptoExchange.Net; +using CoinEx.Net.Interfaces.Clients.FuturesApi; + +namespace CoinEx.Net.Clients.FuturesApi +{ + /// + public class CoinExRestClientFuturesApiTrading : ICoinExRestClientFuturesApiTrading + { + private readonly CoinExRestClientFuturesApi _baseClient; + + internal CoinExRestClientFuturesApiTrading(CoinExRestClientFuturesApi baseClient) + { + _baseClient = baseClient; + } + + } +} diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs b/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs new file mode 100644 index 0000000..eb38c1b --- /dev/null +++ b/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs @@ -0,0 +1,108 @@ +using CryptoExchange.Net; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Sockets; +using Microsoft.Extensions.Logging; +using CryptoExchange.Net.Authentication; +using System.Threading; +using CoinEx.Net.Objects.Options; +using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Interfaces; +using CryptoExchange.Net.Converters.MessageParsing; +using CryptoExchange.Net.Clients; +using CoinEx.Net.Objects.Models.V2; +using CryptoExchange.Net.Converters.SystemTextJson; +using System.Net.WebSockets; +using CoinEx.Net.Objects.Sockets.V2.Subscriptions; +using CoinEx.Net.Objects.Sockets.V2.Queries; +using System.Linq; +using CoinEx.Net.Interfaces.Clients.SpotApiV2; +using CoinEx.Net.Interfaces.Clients.FuturesApi; + +namespace CoinEx.Net.Clients.FuturesApi +{ + /// + public class CoinExSocketClientFuturesApi : SocketApiClient, ICoinExSocketClientFuturesApi + { + #region fields + /// + public new CoinExSocketOptions ClientOptions => (CoinExSocketOptions)base.ClientOptions; + + private static readonly MessagePath _idPath = MessagePath.Get().Property("id"); + private static readonly MessagePath _methodPath = MessagePath.Get().Property("method"); + private static readonly MessagePath _symbolPath = MessagePath.Get().Property("data").Property("market"); + private static readonly MessagePath _symbolPathDepth = MessagePath.Get().Property("params").Index(2); + #endregion + + #region ctor + /// + /// Create a new instance of CoinExSocketClient with default options + /// + internal CoinExSocketClientFuturesApi(ILogger logger, CoinExSocketOptions options) + : base(logger, options.Environment.SocketBaseAddress, options, options.FuturesOptions) + { + RegisterPeriodicQuery("Ping", TimeSpan.FromMinutes(1), q => (new CoinExQuery("server.ping", new Dictionary())), null); + } + #endregion + + /// + protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials) + => new CoinExV2AuthenticationProvider(credentials); + + #region methods + + /// + protected override IByteMessageAccessor CreateAccessor() => new SystemTextJsonByteMessageAccessor(); + /// + protected override IMessageSerializer CreateSerializer() => new SystemTextJsonMessageSerializer(); + + /// + public override string? GetListenerIdentifier(IMessageAccessor messageAccessor) + { + var id = messageAccessor.GetValue(_idPath); + if (id != null) + return id.ToString(); + + var method = messageAccessor.GetValue(_methodPath); + if (string.Equals(method, "deals.update", StringComparison.Ordinal)) + return method; + + if (!string.Equals(method, "state.update", StringComparison.Ordinal)) + { + var symbol = messageAccessor.GetValue(_symbolPath); + return method + symbol; + } + + return method; + } + + /// + protected override Query? GetAuthenticationRequest() + { + var authProvider = (CoinExV2AuthenticationProvider)AuthenticationProvider!; + var authParams = authProvider.GetSocketAuthParameters(); + return new CoinExQuery("server.sign", authParams, false, 0); + } + + /// + public override ReadOnlyMemory PreprocessStreamMessage(WebSocketMessageType type, ReadOnlyMemory data) + => data.DecompressGzip(); + + #region public + + /// + public async Task> SubscribeToTickerUpdatesAsync(Action>> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExTickerSubscription(_logger, null, new Dictionary + { + { "market_list", new object[] { } } + }, onMessage); + return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); + } + #endregion + + #endregion + } +} diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs index 0c95f23..13adade 100644 --- a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs @@ -7,6 +7,7 @@ using CoinEx.Net.Objects.Models.V2; using System; using CoinEx.Net.Interfaces.Clients.SpotApiV2; +using CryptoExchange.Net; namespace CoinEx.Net.Clients.SpotApiV2 { @@ -33,7 +34,7 @@ public async Task> PlaceOrderAsync( bool? hide = null, CancellationToken ct = default) { - // TODO BROKERID, SAME WAY AS IN V1? + clientOrderId ??= ExchangeHelpers.AppendRandomString("x-" + _baseClient._brokerId + "-", 32); var parameters = new ParameterCollection() { @@ -67,7 +68,7 @@ public async Task> PlaceStopOrderAsync( bool? hide = null, CancellationToken ct = default) { - // TODO BROKERID, SAME WAY AS IN V1? + clientOrderId ??= ExchangeHelpers.AppendRandomString("x-" + _baseClient._brokerId + "-", 32); var parameters = new ParameterCollection() { diff --git a/CoinEx.Net/CoinEx.Net.xml b/CoinEx.Net/CoinEx.Net.xml index 3f06fa1..3196493 100644 --- a/CoinEx.Net/CoinEx.Net.xml +++ b/CoinEx.Net/CoinEx.Net.xml @@ -7,6 +7,9 @@ + + + @@ -39,6 +42,9 @@ + + + @@ -73,6 +79,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create a new instance of CoinExSocketClient with default options + + + + + + + + + + + + + + + + + + + + + + + @@ -644,6 +763,21 @@ Has been repaid + + + Type of contract + + + + + Linear contract + + + + + Inverse contract + + Deposit status @@ -939,6 +1073,21 @@ Order finished + + + Position side + + + + + Long position + + + + + Short position + + Price type @@ -1154,6 +1303,162 @@ Extension methods specific to using the CoinEx API + + + Futures API + + + + + Endpoints related to account settings, info or actions + + + + + Endpoints related to retrieving market and system data + + + + + Endpoints related to orders and trades + + + + + CoinEx account endpoints. Account endpoints include balance info, withdraw/deposit info and requesting and account settings + + + + + CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. + + + + + Get list of support symbols + + + Filter by symbol name; max 10 + Cancelation token + + + + + Get symbol tickers + + + Fitler by symbol names; max 10 + Cancelation Token + + + + + Get the orderbook for a symbol + + + Symbol + Amount of rows, 5, 10, 20 or 50 + The merge level, 0.00000000001 up to 1000, 0 for no merging + Cancelation Token + + + + + Get the trade history for a symbol + + + Symbol + Max amount of results + The starting point of the query, 0 means to acquire from the latest record + Cancelation Token + + + + + Get klines/candlesticks + + + Symbol + Kline interval + Max amount of results + Price type, either LastPrice(default) or IndexPrice + Cancelation Token + + + + + Get index prices + + + Filter by symbols + Cancelation Token + + + + + Get funding rates + + + Filter by symbols + Cancelation Token + + + + + Get historical funding rates + + + Symbol + Filter by start time + Filter by end time + Page number + Page size + Cancelation Token + + + + + Get position levels + + + Filter by symbols; max 10 + Cancelation Token + + + + + Get liquidation history + + + Symbol + Filter by start time + Filter by end time + Page number + Page size + Cancelation Token + + + + + Get basis rate history + + + Symbol + Filter by start time + Filter by end time + Cancelation Token + + + + + CoinEx trading endpoints, placing and mananging orders. + + + + + Futures streams + + Client for accessing the CoinEx API. @@ -1169,6 +1474,11 @@ Spot V2 API endpoints + + + Futures V2 API endpoints + + Set the API credentials for this client. All Api clients in this client will use the new credentials, regardless of earlier set options. @@ -1180,6 +1490,11 @@ Client for accessing the CoinEx websocket API + + + V2 API Futures streams + + V2 API Spot streams @@ -3520,6 +3835,26 @@ Update time + + + Basis rate + + + + + Symbol + + + + + Create time + + + + + Basis rate + + Best book prices update @@ -3875,6 +4210,139 @@ Blockchain explorer url + + + Funding rate info + + + + + Symbol + + + + + Mark price + + + + + Last funding rate + + + + + Next funding rate + + + + + Last funding time + + + + + Next funding time + + + + + Historic funding rate info + + + + + Symbol + + + + + Funding time + + + + + Theoretical funding rate. The theoretical funding rate to be collected for the current period after calculation + + + + + Actual funding rate. The actual funding rate charged in the current period + + + + + Futures symbol info + + + + + Symbol name + + + + + Contract type + + + + + Taker fee rate + + + + + Maker fee rate + + + + + Min order quantity + + + + + Base asset + + + + + Quote asset + + + + + Base asset precision + + + + + Quote asset precision + + + + + Leverage + + + + + Open interest volume + + + + + + + + Index price + + + + + Mark price + + Index price @@ -3985,6 +4453,41 @@ Value (Quote asset volume) + + + Liquidation record + + + + + Symbol + + + + + Position side + + + + + Liquidation price + + + + + Liquidation quantity + + + + + Bankruptcy price + + + + + Timestamp + + Margin balance info @@ -4265,6 +4768,46 @@ Page items + + + Position levels info + + + + + Symbol + + + + + Levels + + + + + Position level info + + + + + Upper limit of the current position + + + + + Leverage of current level + + + + + Current maintenance margin rate + + + + + Minimum initial margin rate for the current level + + Stop order id @@ -4730,6 +5273,11 @@ Options for the Spot API + + + Options for the Futures API + + The broker reference id to use @@ -4755,6 +5303,11 @@ Options for the Spot API + + + Options for the Futures API + + diff --git a/CoinEx.Net/Enums/ContractType.cs b/CoinEx.Net/Enums/ContractType.cs new file mode 100644 index 0000000..7a73fa9 --- /dev/null +++ b/CoinEx.Net/Enums/ContractType.cs @@ -0,0 +1,24 @@ +using CryptoExchange.Net.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Type of contract + /// + public enum ContractType + { + /// + /// Linear contract + /// + [Map("linear")] + Linear, + /// + /// Inverse contract + /// + [Map("inverse")] + Inverse + } +} diff --git a/CoinEx.Net/Enums/PositionSide.cs b/CoinEx.Net/Enums/PositionSide.cs new file mode 100644 index 0000000..2b637af --- /dev/null +++ b/CoinEx.Net/Enums/PositionSide.cs @@ -0,0 +1,24 @@ +using CryptoExchange.Net.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Position side + /// + public enum PositionSide + { + /// + /// Long position + /// + [Map("long")] + Long, + /// + /// Short position + /// + [Map("short")] + Short + } +} diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApi.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApi.cs new file mode 100644 index 0000000..b6ce86f --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApi.cs @@ -0,0 +1,27 @@ +using CryptoExchange.Net.Interfaces; +using CryptoExchange.Net.Interfaces.CommonClients; +using System; + +namespace CoinEx.Net.Interfaces.Clients.FuturesApi +{ + /// + /// Futures API + /// + public interface ICoinExRestClientFuturesApi : IRestApiClient, IDisposable + { + /// + /// Endpoints related to account settings, info or actions + /// + ICoinExRestClientFuturesApiAccount Account { get; } + + /// + /// Endpoints related to retrieving market and system data + /// + ICoinExRestClientFuturesApiExchangeData ExchangeData { get; } + + /// + /// Endpoints related to orders and trades + /// + ICoinExRestClientFuturesApiTrading Trading { get; } + } +} \ No newline at end of file diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs new file mode 100644 index 0000000..688e996 --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs @@ -0,0 +1,19 @@ +using CryptoExchange.Net.Objects; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Objects.Models.V2; +using CoinEx.Net.Enums; +using System.Collections.Generic; +using System.Diagnostics.Metrics; +using System.Text.RegularExpressions; +using System; + +namespace CoinEx.Net.Interfaces.Clients.FuturesApi +{ + /// + /// CoinEx account endpoints. Account endpoints include balance info, withdraw/deposit info and requesting and account settings + /// + public interface ICoinExRestClientFuturesApiAccount + { + } +} diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiExchangeData.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiExchangeData.cs new file mode 100644 index 0000000..e5abf7b --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiExchangeData.cs @@ -0,0 +1,132 @@ +using CoinEx.Net.Enums; +using CryptoExchange.Net.Objects; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Objects.Models.V2; +using System; + +namespace CoinEx.Net.Interfaces.Clients.FuturesApi +{ + /// + /// CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. + /// + public interface ICoinExRestClientFuturesApiExchangeData + { + /// + /// Get list of support symbols + /// + /// + /// Filter by symbol name; max 10 + /// Cancelation token + /// + Task>> GetSymbolsAsync(IEnumerable? symbols = null, CancellationToken ct = default); + + /// + /// Get symbol tickers + /// + /// + /// Fitler by symbol names; max 10 + /// Cancelation Token + /// + Task>> GetTickersAsync(IEnumerable? symbols = null, CancellationToken ct = default); + + /// + /// Get the orderbook for a symbol + /// + /// + /// Symbol + /// Amount of rows, 5, 10, 20 or 50 + /// The merge level, 0.00000000001 up to 1000, 0 for no merging + /// Cancelation Token + /// + Task> GetOrderBookAsync(string symbol, int limit, string? mergeLevel = null, CancellationToken ct = default); + + /// + /// Get the trade history for a symbol + /// + /// + /// Symbol + /// Max amount of results + /// The starting point of the query, 0 means to acquire from the latest record + /// Cancelation Token + /// + Task>> GetTradeHistoryAsync(string symbol, int? limit = null, long? lastId = null, CancellationToken ct = default); + + /// + /// Get klines/candlesticks + /// + /// + /// Symbol + /// Kline interval + /// Max amount of results + /// Price type, either LastPrice(default) or IndexPrice + /// Cancelation Token + /// + Task>> GetKlinesAsync(string symbol, KlineInterval interval, int? limit = null, PriceType? priceType = null, CancellationToken ct = default); + + /// + /// Get index prices + /// + /// + /// Filter by symbols + /// Cancelation Token + /// + Task>> GetIndexPricesAsync(IEnumerable? symbols = null, CancellationToken ct = default); + + /// + /// Get funding rates + /// + /// + /// Filter by symbols + /// Cancelation Token + /// + Task>> GetFundingRatesAsync(IEnumerable? symbols = null, CancellationToken ct = default); + + /// + /// Get historical funding rates + /// + /// + /// Symbol + /// Filter by start time + /// Filter by end time + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetFundingRateHistoryAsync(string symbol, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get position levels + /// + /// + /// Filter by symbols; max 10 + /// Cancelation Token + /// + Task>> GetPositionLevelsAsync(IEnumerable? symbols = null, CancellationToken ct = default); + + /// + /// Get liquidation history + /// + /// + /// Symbol + /// Filter by start time + /// Filter by end time + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetLiquidationHistoryAsync(string symbol, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get basis rate history + /// + /// + /// Symbol + /// Filter by start time + /// Filter by end time + /// Cancelation Token + /// + Task>> GetBasisHistoryAsync(string symbol, DateTime? startTime = null, DateTime? endTime = null, CancellationToken ct = default); + } +} diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs new file mode 100644 index 0000000..989180a --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs @@ -0,0 +1,17 @@ +using CoinEx.Net.Enums; +using CryptoExchange.Net.Objects; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Objects.Models.V2; +using System.Collections.Generic; +using System; + +namespace CoinEx.Net.Interfaces.Clients.FuturesApi +{ + /// + /// CoinEx trading endpoints, placing and mananging orders. + /// + public interface ICoinExRestClientFuturesApiTrading + { + } +} diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs new file mode 100644 index 0000000..d702668 --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Enums; +using CoinEx.Net.Objects.Models; +using CoinEx.Net.Objects.Models.Socket; +using CoinEx.Net.Objects.Models.V2; +using CryptoExchange.Net.Interfaces; +using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Objects.Sockets; + +namespace CoinEx.Net.Interfaces.Clients.FuturesApi +{ + /// + /// Futures streams + /// + public interface ICoinExSocketClientFuturesApi : ISocketApiClient, IDisposable + { + } +} \ No newline at end of file diff --git a/CoinEx.Net/Interfaces/Clients/ICoinExRestClient.cs b/CoinEx.Net/Interfaces/Clients/ICoinExRestClient.cs index 5952751..6fca34f 100644 --- a/CoinEx.Net/Interfaces/Clients/ICoinExRestClient.cs +++ b/CoinEx.Net/Interfaces/Clients/ICoinExRestClient.cs @@ -1,4 +1,5 @@ -using CryptoExchange.Net.Authentication; +using CoinEx.Net.Interfaces.Clients.FuturesApi; +using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Interfaces; namespace CoinEx.Net.Interfaces.Clients @@ -16,6 +17,10 @@ public interface ICoinExRestClient : IRestClient /// Spot V2 API endpoints /// SpotApiV2.ICoinExRestClientSpotApi SpotApiV2 { get; } + /// + /// Futures V2 API endpoints + /// + ICoinExRestClientFuturesApi FuturesApi { get; } /// /// Set the API credentials for this client. All Api clients in this client will use the new credentials, regardless of earlier set options. diff --git a/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs b/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs index 1608d3f..0f3db1d 100644 --- a/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs +++ b/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs @@ -1,4 +1,5 @@ -using CryptoExchange.Net.Authentication; +using CoinEx.Net.Interfaces.Clients.FuturesApi; +using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Interfaces; namespace CoinEx.Net.Interfaces.Clients @@ -8,6 +9,10 @@ namespace CoinEx.Net.Interfaces.Clients /// public interface ICoinExSocketClient : ISocketClient { + /// + /// V2 API Futures streams + /// + public ICoinExSocketClientFuturesApi FuturesApi { get; } /// /// V2 API Spot streams /// diff --git a/CoinEx.Net/Objects/Models/V2/CoinExBasis.cs b/CoinEx.Net/Objects/Models/V2/CoinExBasis.cs new file mode 100644 index 0000000..a6eb088 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExBasis.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Basis rate + /// + public record CoinExBasis + { + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Create time + /// + [JsonPropertyName("created_at")] + public DateTime CreateTime { get; set; } + /// + /// Basis rate + /// + [JsonPropertyName("basis_rate")] + public decimal BasisRate { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFundingRate.cs b/CoinEx.Net/Objects/Models/V2/CoinExFundingRate.cs new file mode 100644 index 0000000..8958d5f --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFundingRate.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Funding rate info + /// + public record CoinExFundingRate + { + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Mark price + /// + [JsonPropertyName("mark_price")] + public decimal MarkPrice { get; set; } + /// + /// Last funding rate + /// + [JsonPropertyName("latest_funding_rate")] + public decimal LastFundingRate { get; set; } + /// + /// Next funding rate + /// + [JsonPropertyName("next_funding_rate")] + public decimal NextFundingRate { get; set; } + /// + /// Last funding time + /// + [JsonPropertyName("latest_funding_time")] + public DateTime? LastFundingTime { get; set; } + /// + /// Next funding time + /// + [JsonPropertyName("next_funding_time")] + public DateTime? NextFundingTime { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFundingRateHistory.cs b/CoinEx.Net/Objects/Models/V2/CoinExFundingRateHistory.cs new file mode 100644 index 0000000..38f2f53 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFundingRateHistory.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Historic funding rate info + /// + public record CoinExFundingRateHistory + { + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Funding time + /// + [JsonPropertyName("funding_time")] + public DateTime? FundingTime { get; set; } + /// + /// Theoretical funding rate. The theoretical funding rate to be collected for the current period after calculation + /// + [JsonPropertyName("theoretical_funding_rate")] + public decimal TheoreticalFundingrate { get; set; } + /// + /// Actual funding rate. The actual funding rate charged in the current period + /// + [JsonPropertyName("actual_funding_rate")] + public decimal ActualFundingRate { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFuturesSymbol.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesSymbol.cs new file mode 100644 index 0000000..3cac9a0 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesSymbol.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; +using CoinEx.Net.Enums; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Futures symbol info + /// + public record CoinExFuturesSymbol + { + /// + /// Symbol name + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Contract type + /// + [JsonPropertyName("contract_type")] + public ContractType ContractType { get; set; } + /// + /// Taker fee rate + /// + [JsonPropertyName("taker_fee_rate")] + public decimal TakerFeeRate { get; set; } + /// + /// Maker fee rate + /// + [JsonPropertyName("maker_fee_rate")] + public decimal MakerFeeRate { get; set; } + /// + /// Min order quantity + /// + [JsonPropertyName("min_amount")] + public decimal MinOrderQuantity { get; set; } + /// + /// Base asset + /// + [JsonPropertyName("base_ccy")] + public string BaseAsset { get; set; } = string.Empty; + /// + /// Quote asset + /// + [JsonPropertyName("quote_ccy")] + public string QuoteAsset { get; set; } = string.Empty; + /// + /// Base asset precision + /// + [JsonPropertyName("base_ccy_precision")] + public decimal QuantityPrecision { get; set; } + /// + /// Quote asset precision + /// + [JsonPropertyName("quote_ccy_precision")] + public decimal PricePrecision { get; set; } + /// + /// Leverage + /// + [JsonPropertyName("leverage")] + public int[] Leverage { get; set; } = Array.Empty(); + /// + /// Open interest volume + /// + [JsonPropertyName("open_interest_volume")] + public decimal OpenInterestVolume { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFuturesTicker.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesTicker.cs new file mode 100644 index 0000000..bbee8f2 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesTicker.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + public record CoinExFuturesTicker : CoinExTicker + { + /// + /// Index price + /// + [JsonPropertyName("index_price")] + public decimal IndexPrice { get; set; } + /// + /// Mark price + /// + [JsonPropertyName("mark_price")] + public decimal MarkPrice { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExIndexPrice.cs b/CoinEx.Net/Objects/Models/V2/CoinExIndexPrice.cs index a59eb83..044d4a8 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExIndexPrice.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExIndexPrice.cs @@ -57,6 +57,6 @@ public record CoinExIndexPriceSource /// Price /// [JsonPropertyName("index_price")] - public decimal Price { get; set; } + public decimal? Price { get; set; } } } diff --git a/CoinEx.Net/Objects/Models/V2/CoinExLiquidation.cs b/CoinEx.Net/Objects/Models/V2/CoinExLiquidation.cs new file mode 100644 index 0000000..4445d66 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExLiquidation.cs @@ -0,0 +1,45 @@ +using CoinEx.Net.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Liquidation record + /// + public record CoinExLiquidation + { + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Position side + /// + [JsonPropertyName("side")] + public PositionSide Side { get; set; } + /// + /// Liquidation price + /// + [JsonPropertyName("liq_price")] + public decimal LiquidationPrice { get; set; } + /// + /// Liquidation quantity + /// + [JsonPropertyName("liq_amount")] + public decimal LiquidationQuantity { get; set; } + /// + /// Bankruptcy price + /// + [JsonPropertyName("bkr_price")] + public decimal BankruptcyPrice { get; set; } + /// + /// Timestamp + /// + [JsonPropertyName("created_at")] + public DateTime CreateTime { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPositionLevel.cs b/CoinEx.Net/Objects/Models/V2/CoinExPositionLevel.cs new file mode 100644 index 0000000..425fb33 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionLevel.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Position levels info + /// + public record CoinExPositionLevels + { + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Levels + /// + [JsonPropertyName("level")] + public IEnumerable Levels { get; set; } = Array.Empty(); + } + + /// + /// Position level info + /// + public record CoinExPositionLevel + { + /// + /// Upper limit of the current position + /// + [JsonPropertyName("amount")] + public decimal Amount { get; set; } + /// + /// Leverage of current level + /// + [JsonPropertyName("leverage")] + public decimal Leverage { get; set; } + /// + /// Current maintenance margin rate + /// + [JsonPropertyName("maintenance_margin_rate")] + public decimal MaintenanceMarginRate { get; set; } + /// + /// Minimum initial margin rate for the current level + /// + [JsonPropertyName("min_initial_margin_rate")] + public decimal MinInitialMarginRate { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Options/CoinExRestOptions.cs b/CoinEx.Net/Objects/Options/CoinExRestOptions.cs index 2ce7e46..c74a0f7 100644 --- a/CoinEx.Net/Objects/Options/CoinExRestOptions.cs +++ b/CoinEx.Net/Objects/Options/CoinExRestOptions.cs @@ -26,6 +26,11 @@ public class CoinExRestOptions : RestExchangeOptions /// public RestApiOptions SpotOptions { get; private set; } = new RestApiOptions(); + /// + /// Options for the Futures API + /// + public RestApiOptions FuturesOptions { get; private set; } = new RestApiOptions(); + /// /// The broker reference id to use /// @@ -37,6 +42,7 @@ internal CoinExRestOptions Copy() options.BrokerId = BrokerId; options.NonceProvider = NonceProvider; options.SpotOptions = SpotOptions.Copy(); + options.FuturesOptions = SpotOptions.Copy(); return options; } } diff --git a/CoinEx.Net/Objects/Options/CoinExSocketOptions.cs b/CoinEx.Net/Objects/Options/CoinExSocketOptions.cs index cb46ec1..7046132 100644 --- a/CoinEx.Net/Objects/Options/CoinExSocketOptions.cs +++ b/CoinEx.Net/Objects/Options/CoinExSocketOptions.cs @@ -27,11 +27,17 @@ public class CoinExSocketOptions : SocketExchangeOptions /// public SocketApiOptions SpotOptions { get; private set; } = new SocketApiOptions(); + /// + /// Options for the Futures API + /// + public SocketApiOptions FuturesOptions { get; private set; } = new SocketApiOptions(); + internal CoinExSocketOptions Copy() { var options = Copy(); options.NonceProvider = NonceProvider; options.SpotOptions = SpotOptions.Copy(); + options.FuturesOptions = SpotOptions.Copy(); return options; } } From 5fe49054850a3700a79adfaa97e93b67e86dadaf Mon Sep 17 00:00:00 2001 From: JKorf Date: Thu, 4 Apr 2024 21:54:55 +0200 Subject: [PATCH 12/17] wip --- .../CoinExRestClientFuturesApiAccount.cs | 14 +- .../CoinExRestClientFuturesApiTrading.cs | 255 ++++++++ .../CoinExSocketClientFuturesApi.cs | 78 ++- .../CoinExRestClientSpotApiTrading.cs | 4 +- .../SpotApiV2/CoinExSocketClientSpotApi.cs | 2 + CoinEx.Net/CoinEx.Net.xml | 613 +++++++++++++++++- CoinEx.Net/Enums/TriggerPriceType.cs | 29 + .../ICoinExRestClientFuturesApiAccount.cs | 16 + .../ICoinExRestClientFuturesApiTrading.cs | 225 +++++++ .../ICoinExSocketClientFuturesApi.cs | 114 ++++ .../ICoinExRestClientSpotApiTrading.cs | 2 +- .../Objects/Models/V2/CoinExFuturesBalance.cs | 44 ++ .../Objects/Models/V2/CoinExFuturesOrder.cs | 118 ++++ .../Models/V2/CoinExFuturesTickerUpdate.cs | 47 ++ .../Models/V2/CoinExIndexPriceUpdate.cs | 7 +- CoinEx.Net/Objects/Models/V2/CoinExOrder.cs | 2 +- .../Objects/Models/V2/CoinExStopOrder.cs | 2 +- .../CoinExFuturesTickerSubscription.cs | 49 ++ .../Subscriptions/CoinExTradesSubscription.cs | 2 +- 19 files changed, 1608 insertions(+), 15 deletions(-) create mode 100644 CoinEx.Net/Enums/TriggerPriceType.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExFuturesBalance.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExFuturesOrder.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExFuturesTickerUpdate.cs create mode 100644 CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExFuturesTickerSubscription.cs diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs index c96b8d0..26513ca 100644 --- a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs @@ -26,14 +26,24 @@ internal CoinExRestClientFuturesApiAccount(CoinExRestClientFuturesApi baseClient } /// - public async Task> GetTradingFeesAsync(string symbol, AccountType accountType, CancellationToken ct = default) + public async Task> GetTradingFeesAsync(string symbol, CancellationToken ct = default) { var parameters = new ParameterCollection() { { "market", symbol } }; - parameters.AddEnum("market_type", accountType); + parameters.AddEnum("market_type", AccountType.Futures); return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/account/trade-fee-rate"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); } + + /// + public async Task>> GetBalancesAsync(CancellationToken ct = default) + { + var result = await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/assets/futures/balance"), HttpMethod.Get, ct, null, true).ConfigureAwait(false); + if (result && result.Data == null) + return result.As>(Array.Empty()); + + return result; + } } } diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs index 4995238..a97ccfd 100644 --- a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs @@ -22,5 +22,260 @@ internal CoinExRestClientFuturesApiTrading(CoinExRestClientFuturesApi baseClient _baseClient = baseClient; } + /// + public async Task> PlaceOrderAsync( + string symbol, + OrderSide side, + OrderTypeV2 type, + decimal quantity, + decimal? price = null, + string? clientOrderId = null, + bool? hide = null, + CancellationToken ct = default) + { + clientOrderId ??= ExchangeHelpers.AppendRandomString("x-" + _baseClient._brokerId + "-", 32); + + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddEnum("side", side); + parameters.AddEnum("type", type); + parameters.AddString("amount", quantity); + parameters.AddOptionalString("price", price); + parameters.AddOptional("client_id", clientOrderId); + parameters.AddOptional("is_hide", hide); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/futures/order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> PlaceStopOrderAsync( + string symbol, + OrderSide side, + OrderTypeV2 type, + decimal quantity, + decimal triggerPrice, + TriggerPriceType triggerPriceType, + decimal? price = null, + string? clientOrderId = null, + bool? hide = null, + CancellationToken ct = default) + { + clientOrderId ??= ExchangeHelpers.AppendRandomString("x-" + _baseClient._brokerId + "-", 32); + + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddEnum("side", side); + parameters.AddEnum("type", type); + parameters.AddEnum("trigger_price_type", triggerPriceType); + parameters.AddString("amount", quantity); + parameters.AddString("trigger_price", triggerPrice); + parameters.AddOptionalString("price", price); + parameters.AddOptional("client_id", clientOrderId); + parameters.AddOptional("is_hide", hide); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/futures/stop-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> GetOrderAsync(string symbol, long orderId, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "order_id", orderId } + }; + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/futures/order-status"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + /// + public async Task>> GetOpenOrdersAsync(string? symbol = null, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddOptionalEnum("side", side); + parameters.AddOptional("market", symbol); + parameters.AddOptional("client_id", clientOrderId); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/futures/pending-order"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetClosedOrdersAsync(string? symbol = null, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddOptionalEnum("side", side); + parameters.AddOptional("market", symbol); + parameters.AddOptional("client_id", clientOrderId); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/futures/finished-order"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetOpenStopOrdersAsync(string? symbol = null, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddOptionalEnum("side", side); + parameters.AddOptional("market", symbol); + parameters.AddOptional("client_id", clientOrderId); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/futures/pending-stop-order"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetClosedStopOrdersAsync(string? symbol = null, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddOptionalEnum("side", side); + parameters.AddOptional("market", symbol); + parameters.AddOptional("client_id", clientOrderId); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/futures/finished-stop-order"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> EditOrderAsync( + string symbol, + long orderId, + decimal quantity, + decimal? price = null, + CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "order_id", orderId } + }; + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddString("amount", quantity); + parameters.AddOptionalString("price", price); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/futures/modify-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> EditStopOrderAsync( + string symbol, + long stopOrderId, + decimal quantity, + decimal triggerPrice, + decimal? price = null, + CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "stop_id", stopOrderId } + }; + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddString("amount", quantity); + parameters.AddString("trigger_price", triggerPrice); + parameters.AddOptionalString("price", price); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/futures/modify-stop-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task CancelAllOrdersAsync(string symbol, OrderSide? side = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + }; + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddOptionalEnum("side", side); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/futures/cancel-all-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> CancelOrderAsync(string symbol, long orderId, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "order_id", orderId } + }; + parameters.AddEnum("market_type", AccountType.Futures); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/futures/cancel-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + } + + /// + public async Task> CancelStopOrderAsync(string symbol, long stopOrderId, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "stop_id", stopOrderId } + }; + parameters.AddEnum("market_type", AccountType.Futures); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/cancel-stop-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + } + + /// + public async Task> CancelOrderByClientOrderIdAsync(string symbol, string clientOrderId, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "client_id", clientOrderId } + }; + parameters.AddEnum("market_type", AccountType.Futures); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/futures/cancel-order-by-client-id"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + } + + /// + public async Task> CancelStopOrderByClientOrderIdAsync(string symbol, string clientStopOrderId, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "client_id", clientStopOrderId } + }; + parameters.AddEnum("market_type", AccountType.Futures); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/futures/cancel-stop-order-by-client-id"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + } + + /// + public async Task>> GetUserTradesAsync(string symbol, OrderSide? side = null, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddOptionalEnum("side", side); + parameters.AddOptionalMilliseconds("start_time", startTime); + parameters.AddOptionalMilliseconds("end_Time", endTime); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/futures/user-deals"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetOrderTradesAsync(string symbol, long orderId, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "order_id", orderId } + }; + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/futures/order-deals"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + // TODO Batch endpoints } } diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs b/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs index eb38c1b..543c09f 100644 --- a/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs +++ b/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs @@ -43,6 +43,8 @@ public class CoinExSocketClientFuturesApi : SocketApiClient, ICoinExSocketClient internal CoinExSocketClientFuturesApi(ILogger logger, CoinExSocketOptions options) : base(logger, options.Environment.SocketBaseAddress, options, options.FuturesOptions) { + HandleMessageBeforeConfirmation = true; + RegisterPeriodicQuery("Ping", TimeSpan.FromMinutes(1), q => (new CoinExQuery("server.ping", new Dictionary())), null); } #endregion @@ -93,13 +95,83 @@ public override ReadOnlyMemory PreprocessStreamMessage(WebSocketMessageTyp #region public /// - public async Task> SubscribeToTickerUpdatesAsync(Action>> onMessage, CancellationToken ct = default) + public async Task> SubscribeToTickerUpdatesAsync(IEnumerable symbols, Action>> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExFuturesTickerSubscription(_logger, null, new Dictionary + { + { "market_list", symbols } + }, onMessage); + return await SubscribeAsync(BaseAddress.AppendPath("v2/futures"), subscription, ct).ConfigureAwait(false); + } + + /// + public async Task> SubscribeToTickerUpdatesAsync(Action>> onMessage, CancellationToken ct = default) { - var subscription = new CoinExTickerSubscription(_logger, null, new Dictionary + var subscription = new CoinExFuturesTickerSubscription(_logger, null, new Dictionary { { "market_list", new object[] { } } }, onMessage); - return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); + return await SubscribeAsync(BaseAddress.AppendPath("v2/futures"), subscription, ct).ConfigureAwait(false); + } + + /// + public Task> SubscribeToOrderBookUpdatesAsync(string symbol, int depth, string? mergeLevel, bool fullBookUpdates, Action> onMessage, CancellationToken ct = default) + => SubscribeToOrderBookUpdatesAsync(new[] { symbol }, depth, mergeLevel, fullBookUpdates, onMessage, ct); + + /// + public async Task> SubscribeToOrderBookUpdatesAsync(IEnumerable symbols, int depth, string? mergeLevel, bool fullBookUpdates, Action> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "depth", symbols, new Dictionary + { + { "market_list", symbols.Select(x => new object[] { x, depth, mergeLevel ?? "0", fullBookUpdates }).ToList() } + }, x => onMessage(x.As(x.Data, x.Data.Symbol))); + return await SubscribeAsync(BaseAddress.AppendPath("v2/futures"), subscription, ct).ConfigureAwait(false); + } + + /// + public Task> SubscribeToTradeUpdatesAsync(string symbol, Action>> onMessage, CancellationToken ct = default) + => SubscribeToTradeUpdatesAsync(new[] { symbol }, onMessage, ct); + + /// + public Task> SubscribeToTradeUpdatesAsync(Action>> onMessage, CancellationToken ct = default) + => SubscribeToTradeUpdatesAsync(new string[] { }, onMessage, ct); + + /// + public async Task> SubscribeToTradeUpdatesAsync(IEnumerable symbols, Action>> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExTradesSubscription(_logger, symbols, new Dictionary + { + { "market_list", symbols } + }, onMessage); + return await SubscribeAsync(BaseAddress.AppendPath("v2/futures"), subscription, ct).ConfigureAwait(false); + } + + /// + public Task> SubscribeToIndexPriceUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default) + => SubscribeToIndexPriceUpdatesAsync(new[] { symbol }, onMessage, ct); + + /// + public async Task> SubscribeToIndexPriceUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "index", symbols, new Dictionary + { + { "market_list", symbols } + }, x => onMessage(x.As(x.Data, x.Data.Symbol))); + return await SubscribeAsync(BaseAddress.AppendPath("v2/futures"), subscription, ct).ConfigureAwait(false); + } + + /// + public Task> SubscribeToBookPriceUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default) + => SubscribeToBookPriceUpdatesAsync(new[] { symbol }, onMessage, ct); + + /// + public async Task> SubscribeToBookPriceUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "bbo", symbols, new Dictionary + { + { "market_list", symbols } + }, x => onMessage(x.As(x.Data, x.Data.Symbol))); + return await SubscribeAsync(BaseAddress.AppendPath("v2/futures"), subscription, ct).ConfigureAwait(false); } #endregion diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs index 13adade..85ba8e4 100644 --- a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs @@ -173,7 +173,7 @@ public async Task> EditOrderAsync( } /// - public async Task> EditStopOrderAsync( + public async Task> EditStopOrderAsync( string symbol, AccountType accountType, long stopOrderId, @@ -191,7 +191,7 @@ public async Task> EditStopOrderAsync( parameters.AddString("amount", quantity); parameters.AddString("trigger_price", triggerPrice); parameters.AddOptionalString("price", price); - return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/modify-stop-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/modify-stop-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); } /// diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs index 7589100..102bf9d 100644 --- a/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs @@ -42,6 +42,8 @@ public class CoinExSocketClientSpotApi : SocketApiClient, ICoinExSocketClientSpo internal CoinExSocketClientSpotApi(ILogger logger, CoinExSocketOptions options) : base(logger, options.Environment.SocketBaseAddress, options, options.SpotOptions) { + HandleMessageBeforeConfirmation = true; + RegisterPeriodicQuery("Ping", TimeSpan.FromMinutes(1), q => (new CoinExQuery("server.ping", new Dictionary())), null); } #endregion diff --git a/CoinEx.Net/CoinEx.Net.xml b/CoinEx.Net/CoinEx.Net.xml index 3196493..adb1a75 100644 --- a/CoinEx.Net/CoinEx.Net.xml +++ b/CoinEx.Net/CoinEx.Net.xml @@ -118,7 +118,10 @@ - + + + + @@ -160,6 +163,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -189,7 +240,37 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1183,6 +1264,26 @@ Should trigger when the price is lower than the trigger price + + + Trigger price type + + + + + Last price + + + + + Mark price + + + + + Index price + + Type of update @@ -1328,6 +1429,23 @@ CoinEx account endpoints. Account endpoints include balance info, withdraw/deposit info and requesting and account settings + + + Get trading fees for a symbol + + + Symbol + Cancelation token + + + + + Get balances + + + Cancelation token + + CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. @@ -1454,11 +1572,320 @@ CoinEx trading endpoints, placing and mananging orders. + + + Place a new order + + + The symbol + Order side + Order type + Quantity + Price of the order + Client order id + Hide the order + Cancelation Token + + + + + Place a new stop order + + + The symbol + Order side + Order type + Quantity + Price of the order + Price type for the trigger + Client order id + Price to trigger on + Hide the order + Cancelation Token + + + + + Get an order by id + + + Symbol + Order id + Cancelation Token + + + + + Get a list of open orders + + + Filter by symbol + Filter by side + Filter by client order id + Page number + Page size + Cancelation Token + + + + + Get a list of closed orders. Note that orders canceled without having any trades will not be returned + + + Filter by symbol + Filter by side + Filter by client order id + Page number + Page size + Cancelation Token + + + + + Get a list of open stop orders + + + Filter by symbol + Filter by side + Filter by client order id + Page number + Page size + Cancelation Token + + + + + Get a list of closed stop orders. Note that orders canceled without having any trades will not be returned + + + Filter by symbol + Filter by side + Filter by client order id + Page number + Page size + Cancelation Token + + + + + Edit an active order + + + Symbol + Order id + New quantity + New price + Cancelation Token + + + + + Edit an active stop order + + + Symbol + Order id + New quantity + New price + New trigger price + Cancelation Token + + + + + Cancel all orders for a symbol + + + Symbol + Only cancel a specific order side + Cancelation Token + + + + + Cancel an active order + + + Symbol + Id of order to cancel + Cancelation Token + + + + + Cancel an active stop order + + + Symbol + Id of stop order to cancel + Cancelation Token + + + + + Cancel an active order by its client order id + + + Symbol + Client order id of order to cancel + Cancelation Token + + + + + Cancel an active stop order by its client order id + + + Symbol + Client order id of stop order to cancel + Cancelation Token + + + + + Get trade list + + + Symbol + Filter by side + Filter by start time + Filter by end time + Page number + Page size + Cancelation Token + + + + + Get trades for a specific order + + + Symbol + The order id + Page number + Page size + Cancelation Token + + Futures streams + + + Subscribe to symbol ticker updates for all symbols. Note that only one ticker subscription can be active at the same time; new ticker subscription will override the old subscriptions. + + + The symbols to subscribe + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to symbol ticker updates for all symbols. Note that only one ticker subscription can be active at the same time; new ticker subscription will override the old subscriptions. + + + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to order book updates + + + Symbol + Order book depth, 5, 10, 20 or 50 + The merge level, 0.00000000001 up to 1000, 0 for no merging + Whether updates should provide full update or only updates + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to order book updates + + + Symbols + Order book depth, 5, 10, 20 or 50 + The merge level, 0.00000000001 up to 1000, 0 for no merging + Whether updates should provide full update or only updates + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to live trade updates + + + Symbol + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to live trade updates + + + Symbols + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to live trade updates + + + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to index price updates + + + Symbol + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to index price updates + + + Symbols + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to book price updates + + + Symbol + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to book price updates + + + Symbols + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Client for accessing the CoinEx API. @@ -4270,6 +4697,151 @@ Actual funding rate. The actual funding rate charged in the current period + + + Futures balance info + + + + + Asset + + + + + Available balance + + + + + Frozen balance + + + + + Position margin + + + + + Unrealized profit and loss + + + + + Transferable balance + + + + + Order info + + + + + Order id + + + + + Symbol + + + + + Account type + + + + + Order side + + + + + Order type + + + + + Order quantity + + + + + Order price + + + + + Quantity remaining + + + + + Quantity filled + + + + + Value of the filled part + + + + + Client order id + + + + + Fee + + + + + Fee asset + + + + + Maker fee rate + + + + + Taker fee rate + + + + + Filled amount of the last trade + + + + + Price of the last trade + + + + + Realized profit and loss + + + + + Timestamp order was created + + + + + Timestamp order was last updated + + + + + Status of the order + + Futures symbol info @@ -4343,6 +4915,36 @@ Mark price + + + Futures ticker update + + + + + Open interest size + + + + + Last funding rate + + + + + Next funding rate + + + + + Last funding time + + + + + Next funding time + + Index price @@ -4403,11 +5005,16 @@ Symbol - + Index price + + + Mark price + + Kline/candlestick info diff --git a/CoinEx.Net/Enums/TriggerPriceType.cs b/CoinEx.Net/Enums/TriggerPriceType.cs new file mode 100644 index 0000000..330efd3 --- /dev/null +++ b/CoinEx.Net/Enums/TriggerPriceType.cs @@ -0,0 +1,29 @@ +using CryptoExchange.Net.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Trigger price type + /// + public enum TriggerPriceType + { + /// + /// Last price + /// + [Map("latest_price")] + LastPrice, + /// + /// Mark price + /// + [Map("mark_price")] + MarkPrice, + /// + /// Index price + /// + [Map("index_price")] + IndexPrice + } +} diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs index 688e996..edeeffd 100644 --- a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs @@ -15,5 +15,21 @@ namespace CoinEx.Net.Interfaces.Clients.FuturesApi /// public interface ICoinExRestClientFuturesApiAccount { + /// + /// Get trading fees for a symbol + /// + /// + /// Symbol + /// Cancelation token + /// + Task> GetTradingFeesAsync(string symbol, CancellationToken ct = default); + + /// + /// Get balances + /// + /// + /// Cancelation token + /// + Task>> GetBalancesAsync(CancellationToken ct = default); } } diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs index 989180a..485e3b8 100644 --- a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs @@ -13,5 +13,230 @@ namespace CoinEx.Net.Interfaces.Clients.FuturesApi /// public interface ICoinExRestClientFuturesApiTrading { + /// + /// Place a new order + /// + /// + /// The symbol + /// Order side + /// Order type + /// Quantity + /// Price of the order + /// Client order id + /// Hide the order + /// Cancelation Token + /// + Task> PlaceOrderAsync( + string symbol, + OrderSide side, + OrderTypeV2 type, + decimal quantity, + decimal? price = null, + string? clientOrderId = null, + bool? hide = null, + CancellationToken ct = default); + + /// + /// Place a new stop order + /// + /// + /// The symbol + /// Order side + /// Order type + /// Quantity + /// Price of the order + /// Price type for the trigger + /// Client order id + /// Price to trigger on + /// Hide the order + /// Cancelation Token + /// + Task> PlaceStopOrderAsync( + string symbol, + OrderSide side, + OrderTypeV2 type, + decimal quantity, + decimal triggerPrice, + TriggerPriceType triggerPriceType, + decimal? price = null, + string? clientOrderId = null, + bool? hide = null, + CancellationToken ct = default); + + /// + /// Get an order by id + /// + /// + /// Symbol + /// Order id + /// Cancelation Token + /// + Task> GetOrderAsync(string symbol, long orderId, CancellationToken ct = default); + + + /// + /// Get a list of open orders + /// + /// + /// Filter by symbol + /// Filter by side + /// Filter by client order id + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetOpenOrdersAsync(string? symbol = null, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get a list of closed orders. Note that orders canceled without having any trades will not be returned + /// + /// + /// Filter by symbol + /// Filter by side + /// Filter by client order id + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetClosedOrdersAsync(string? symbol = null, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get a list of open stop orders + /// + /// + /// Filter by symbol + /// Filter by side + /// Filter by client order id + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetOpenStopOrdersAsync(string? symbol = null, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get a list of closed stop orders. Note that orders canceled without having any trades will not be returned + /// + /// + /// Filter by symbol + /// Filter by side + /// Filter by client order id + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetClosedStopOrdersAsync(string? symbol = null, OrderSide? side = null, string? clientOrderId = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Edit an active order + /// + /// + /// Symbol + /// Order id + /// New quantity + /// New price + /// Cancelation Token + /// + Task> EditOrderAsync( + string symbol, + long orderId, + decimal quantity, + decimal? price = null, + CancellationToken ct = default); + + /// + /// Edit an active stop order + /// + /// + /// Symbol + /// Order id + /// New quantity + /// New price + /// New trigger price + /// Cancelation Token + /// + Task> EditStopOrderAsync( + string symbol, + long stopOrderId, + decimal quantity, + decimal triggerPrice, + decimal? price = null, + CancellationToken ct = default); + + /// + /// Cancel all orders for a symbol + /// + /// + /// Symbol + /// Only cancel a specific order side + /// Cancelation Token + /// + Task CancelAllOrdersAsync(string symbol, OrderSide? side = null, CancellationToken ct = default); + + /// + /// Cancel an active order + /// + /// + /// Symbol + /// Id of order to cancel + /// Cancelation Token + /// + Task> CancelOrderAsync(string symbol, long orderId, CancellationToken ct = default); + + /// + /// Cancel an active stop order + /// + /// + /// Symbol + /// Id of stop order to cancel + /// Cancelation Token + /// + Task> CancelStopOrderAsync(string symbol, long stopOrderId, CancellationToken ct = default); + + /// + /// Cancel an active order by its client order id + /// + /// + /// Symbol + /// Client order id of order to cancel + /// Cancelation Token + /// + Task> CancelOrderByClientOrderIdAsync(string symbol, string clientOrderId, CancellationToken ct = default); + + /// + /// Cancel an active stop order by its client order id + /// + /// + /// Symbol + /// Client order id of stop order to cancel + /// Cancelation Token + /// + Task> CancelStopOrderByClientOrderIdAsync(string symbol, string clientStopOrderId, CancellationToken ct = default); + + + /// + /// Get trade list + /// + /// + /// Symbol + /// Filter by side + /// Filter by start time + /// Filter by end time + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetUserTradesAsync(string symbol, OrderSide? side = null, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get trades for a specific order + /// + /// + /// Symbol + /// The order id + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetOrderTradesAsync(string symbol, long orderId, int? page = null, int? pageSize = null, CancellationToken ct = default); } } diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs index d702668..dbf5fe0 100644 --- a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs @@ -17,5 +17,119 @@ namespace CoinEx.Net.Interfaces.Clients.FuturesApi /// public interface ICoinExSocketClientFuturesApi : ISocketApiClient, IDisposable { + /// + /// Subscribe to symbol ticker updates for all symbols. Note that only one ticker subscription can be active at the same time; new ticker subscription will override the old subscriptions. + /// + /// + /// The symbols to subscribe + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToTickerUpdatesAsync(IEnumerable symbols, Action>> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to symbol ticker updates for all symbols. Note that only one ticker subscription can be active at the same time; new ticker subscription will override the old subscriptions. + /// + /// + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToTickerUpdatesAsync(Action>> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to order book updates + /// + /// + /// Symbol + /// Order book depth, 5, 10, 20 or 50 + /// The merge level, 0.00000000001 up to 1000, 0 for no merging + /// Whether updates should provide full update or only updates + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToOrderBookUpdatesAsync(string symbol, int depth, string? mergeLevel, bool fullBookUpdates, Action> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to order book updates + /// + /// + /// Symbols + /// Order book depth, 5, 10, 20 or 50 + /// The merge level, 0.00000000001 up to 1000, 0 for no merging + /// Whether updates should provide full update or only updates + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToOrderBookUpdatesAsync(IEnumerable symbols, int depth, string? mergeLevel, bool fullBookUpdates, Action> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to live trade updates + /// + /// + /// Symbol + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToTradeUpdatesAsync(string symbol, Action>> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to live trade updates + /// + /// + /// Symbols + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToTradeUpdatesAsync(IEnumerable symbols, Action>> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to live trade updates + /// + /// + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToTradeUpdatesAsync(Action>> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to index price updates + /// + /// + /// Symbol + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToIndexPriceUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to index price updates + /// + /// + /// Symbols + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToIndexPriceUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to book price updates + /// + /// + /// Symbol + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToBookPriceUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to book price updates + /// + /// + /// Symbols + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToBookPriceUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default); + } } \ No newline at end of file diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs index 0032640..11a12f7 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs @@ -166,7 +166,7 @@ Task> EditOrderAsync( /// New trigger price /// Cancelation Token /// - Task> EditStopOrderAsync( + Task> EditStopOrderAsync( string symbol, AccountType accountType, long stopOrderId, diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalance.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalance.cs new file mode 100644 index 0000000..62b34fa --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalance.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Futures balance info + /// + public record CoinExFuturesBalance + { + /// + /// Asset + /// + [JsonPropertyName("ccy")] + public string Asset { get; set; } = string.Empty; + /// + /// Available balance + /// + [JsonPropertyName("available")] + public decimal Available { get; set; } + /// + /// Frozen balance + /// + [JsonPropertyName("frozen")] + public decimal Frozen { get; set; } + /// + /// Position margin + /// + [JsonPropertyName("margin")] + public decimal Margin { get; set; } + /// + /// Unrealized profit and loss + /// + [JsonPropertyName("unrealized_pnl")] + public decimal UnrealizedPnl { get; set; } + /// + /// Transferable balance + /// + [JsonPropertyName("transferrable")] + public decimal Transferable { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFuturesOrder.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesOrder.cs new file mode 100644 index 0000000..37db564 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesOrder.cs @@ -0,0 +1,118 @@ +using CoinEx.Net.Enums; +using System; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Order info + /// + public record CoinExFuturesOrder + { + /// + /// Order id + /// + [JsonPropertyName("order_id")] + public long Id { get; set; } + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Account type + /// + [JsonPropertyName("market_type")] + public AccountType AccountType { get; set; } + /// + /// Order side + /// + [JsonPropertyName("side")] + public OrderSide Side { get; set; } + /// + /// Order type + /// + [JsonPropertyName("type")] + public OrderTypeV2 OrderType { get; set; } + /// + /// Order quantity + /// + [JsonPropertyName("amount")] + public decimal Quantity { get; set; } + /// + /// Order price + /// + [JsonPropertyName("price")] + public decimal? Price { get; set; } + /// + /// Quantity remaining + /// + [JsonPropertyName("unfilled_amount")] + public decimal QuantityRemaining { get; set; } + /// + /// Quantity filled + /// + [JsonPropertyName("filled_amount")] + public decimal QuantityFilled { get; set; } + /// + /// Value of the filled part + /// + [JsonPropertyName("filled_value")] + public decimal ValueFilled { get; set; } + /// + /// Client order id + /// + [JsonPropertyName("client_id")] + public string? ClientOrderId { get; set; } + /// + /// Fee + /// + [JsonPropertyName("fee")] + public decimal Fee { get; set; } + /// + /// Fee asset + /// + [JsonPropertyName("fee_ccy")] + public string FeeAsset { get; set; } = string.Empty; + /// + /// Maker fee rate + /// + [JsonPropertyName("maker_fee_rate")] + public decimal MakerFeeRate { get; set; } + /// + /// Taker fee rate + /// + [JsonPropertyName("taker_fee_rate")] + public decimal TakerFeeRate { get; set; } + /// + /// Filled amount of the last trade + /// + [JsonPropertyName("last_filled_amount")] + public decimal? LastFilledQuantity { get; set; } + /// + /// Price of the last trade + /// + [JsonPropertyName("last_filled_price")] + public decimal? LastFilledPrice { get; set; } + /// + /// Realized profit and loss + /// + [JsonPropertyName("realized_pnl")] + public decimal? RealizedPnl { get; set; } + /// + /// Timestamp order was created + /// + [JsonPropertyName("created_at")] + public DateTime CreateTime { get; set; } + /// + /// Timestamp order was last updated + /// + [JsonPropertyName("updated_at")] + public DateTime? UpdateTime { get; set; } + /// + /// Status of the order + /// + [JsonPropertyName("status")] + public OrderStatusV2? Status { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFuturesTickerUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesTickerUpdate.cs new file mode 100644 index 0000000..5b4e144 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesTickerUpdate.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + internal record CoinExFuturesTickerUpdateWrapper + { + [JsonPropertyName("state_list")] + public IEnumerable Tickers { get; set; } = Array.Empty(); + } + + /// + /// Futures ticker update + /// + public record CoinExFuturesTickerUpdate : CoinExFuturesTicker + { + /// + /// Open interest size + /// + [JsonPropertyName("open_interest_size")] + public decimal OpenInterestSize { get; set; } + + /// + /// Last funding rate + /// + [JsonPropertyName("latest_funding_rate")] + public decimal LastFundingRate { get; set; } + + /// + /// Next funding rate + /// + [JsonPropertyName("next_funding_rate")] + public decimal NextFundingRate { get; set; } + /// + /// Last funding time + /// + [JsonPropertyName("latest_funding_time")] + public DateTime? LastFundingTime { get; set; } + /// + /// Next funding time + /// + [JsonPropertyName("next_funding_time")] + public DateTime? NextFundingTime { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExIndexPriceUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExIndexPriceUpdate.cs index 21af34d..b1d2725 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExIndexPriceUpdate.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExIndexPriceUpdate.cs @@ -17,6 +17,11 @@ public record CoinExIndexPriceUpdate /// Index price /// [JsonPropertyName("index_price")] - public decimal Price { get; set; } + public decimal IndexPrice { get; set; } + /// + /// Mark price + /// + [JsonPropertyName("mark_price")] + public decimal? MarkPrice { get; set; } } } diff --git a/CoinEx.Net/Objects/Models/V2/CoinExOrder.cs b/CoinEx.Net/Objects/Models/V2/CoinExOrder.cs index 6d2c1e0..d352cab 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExOrder.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExOrder.cs @@ -118,6 +118,6 @@ public record CoinExOrder /// Status of the order /// [JsonPropertyName("status")] - public OrderStatusV2 Status { get; set; } + public OrderStatusV2? Status { get; set; } } } diff --git a/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs b/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs index 50be6cb..51870e7 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs @@ -30,7 +30,7 @@ public record CoinExStopOrder /// Asset the quantity is in /// [JsonPropertyName("ccy")] - public string QuantityAsset { get; set; } = string.Empty; + public string? QuantityAsset { get; set; } /// /// Order side /// diff --git a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExFuturesTickerSubscription.cs b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExFuturesTickerSubscription.cs new file mode 100644 index 0000000..5be2f94 --- /dev/null +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExFuturesTickerSubscription.cs @@ -0,0 +1,49 @@ +using CoinEx.Net.Objects.Models.V2; +using CoinEx.Net.Objects.Sockets.V2.Queries; +using CryptoExchange.Net.Interfaces; +using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Sockets; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace CoinEx.Net.Objects.Sockets.V2.Subscriptions +{ + internal class CoinExFuturesTickerSubscription : Subscription + { + private IEnumerable? _symbols; + private Dictionary _parameters; + private Action>> _handler; + + public override HashSet ListenerIdentifiers { get; set; } + public CoinExFuturesTickerSubscription(ILogger logger, IEnumerable? symbols, Dictionary parameters, Action>> handler) : base(logger, false) + { + _symbols = symbols; + _parameters = parameters; + _handler = handler; + ListenerIdentifiers = new HashSet { "state.update" }; + } + + public override CallResult DoHandleMessage(SocketConnection connection, DataEvent message) + { + var data = (CoinExSocketUpdate)message.Data; + var relevant = data.Data.Tickers.Where(d => _symbols == null || _symbols.Contains(d.Symbol)).ToList(); + if (!relevant.Any()) + return new CallResult(null); + + _handler.Invoke(message.As>(relevant, null, SocketUpdateType.Update)); + return new CallResult(null); + } + + public override Type? GetMessageType(IMessageAccessor message) => typeof(CoinExSocketUpdate); + + public override Query? GetSubQuery(SocketConnection connection) + => new CoinExQuery("state.subscribe", _parameters, false, 1); + + public override Query? GetUnsubQuery() + => new CoinExQuery("state.unsubscribe", _parameters, false, 1); + } +} diff --git a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs index 484259c..0bfa021 100644 --- a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs @@ -34,7 +34,7 @@ public override CallResult DoHandleMessage(SocketConnection connection, DataEven if (!relevant.Any() || !data.Data.Trades.Any()) return new CallResult(null); - _handler.Invoke(message.As>(relevant, data.Data.Symbol, SocketUpdateType.Update)); + _handler.Invoke(message.As>(relevant, data.Data.Symbol, ConnectionInvocations == 1 ? SocketUpdateType.Snapshot : SocketUpdateType.Update)); return new CallResult(null); } From 9c2bc55295edfb83a5e6fb308a026edc32eaaa99 Mon Sep 17 00:00:00 2001 From: JKorf Date: Fri, 5 Apr 2024 15:25:00 +0200 Subject: [PATCH 13/17] Final implementations --- CoinEx.Net.UnitTests/CoinExClientTests.cs | 4 +- .../CoinExSocketClientTests.cs | 431 -------- .../CoinExRestClientFuturesApiAccount.cs | 13 + .../CoinExRestClientFuturesApiTrading.cs | 143 ++- .../CoinExSocketClientFuturesApi.cs | 57 +- .../SpotApiV1/CoinExSocketClientSpotApi.cs | 4 +- .../SpotApiV2/CoinExSocketClientSpotApi.cs | 9 +- CoinEx.Net/CoinEx.Net.xml | 975 +++++++++++++++++- CoinEx.Net/CoinExEnvironment.cs | 2 +- CoinEx.Net/Enums/MarginMode.cs | 21 + CoinEx.Net/Enums/PositionUpdateType.cs | 44 + .../FuturesApi/ICoinExRestClientFuturesApi.cs | 2 +- .../ICoinExRestClientFuturesApiAccount.cs | 11 + .../ICoinExRestClientFuturesApiTrading.cs | 126 +++ .../ICoinExSocketClientFuturesApi.cs | 44 + .../SpotApiV1/ICoinExSocketClientSpotApi.cs | 3 +- .../Interfaces/ICoinExOrderBookFactory.cs | 8 + CoinEx.Net/Objects/CoinExApiAddresses.cs | 2 +- .../Objects/Models/V2/CoinExFuturesBalance.cs | 5 + .../Models/V2/CoinExFuturesBalanceUpdate.cs | 13 + .../Objects/Models/V2/CoinExFuturesOrder.cs | 6 +- .../Models/V2/CoinExFuturesOrderUpdate.cs | 25 + .../Objects/Models/V2/CoinExLeverage.cs | 25 + .../Objects/Models/V2/CoinExOrderUpdate.cs | 2 +- .../Objects/Models/V2/CoinExPosition.cs | 165 +++ .../Objects/Models/V2/CoinExPositionAdl.cs | 66 ++ .../Models/V2/CoinExPositionFundingRate.cs | 65 ++ .../Objects/Models/V2/CoinExPositionMargin.cs | 75 ++ .../Models/V2/CoinExPositionSettlement.cs | 75 ++ .../Objects/Models/V2/CoinExPositionUpdate.cs | 42 + .../Objects/Models/V2/CoinExStopOrder.cs | 12 +- .../Models/V2/CoinExStopOrderUpdate.cs | 6 +- .../Objects/Models/V2/CoinExStreamOrder.cs | 108 ++ .../V2/Subscriptions/CoinExSubscription.cs | 8 +- .../CoinExFuturesSymbolOrderBook.cs | 135 +++ .../CoinExOrderBookFactory.cs | 7 + .../CoinExSpotSymbolOrderBook.cs | 22 +- 37 files changed, 2247 insertions(+), 514 deletions(-) delete mode 100644 CoinEx.Net.UnitTests/CoinExSocketClientTests.cs create mode 100644 CoinEx.Net/Enums/MarginMode.cs create mode 100644 CoinEx.Net/Enums/PositionUpdateType.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExFuturesBalanceUpdate.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExFuturesOrderUpdate.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExLeverage.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExPosition.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExPositionAdl.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExPositionFundingRate.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExPositionMargin.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExPositionSettlement.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExPositionUpdate.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExStreamOrder.cs create mode 100644 CoinEx.Net/SymbolOrderBooks/CoinExFuturesSymbolOrderBook.cs diff --git a/CoinEx.Net.UnitTests/CoinExClientTests.cs b/CoinEx.Net.UnitTests/CoinExClientTests.cs index 7d0f67d..afbdc7d 100644 --- a/CoinEx.Net.UnitTests/CoinExClientTests.cs +++ b/CoinEx.Net.UnitTests/CoinExClientTests.cs @@ -95,8 +95,8 @@ public void SigningString_Should_GiveCorrectSignResult(string input, string outp public void CheckRestInterfaces() { var assembly = Assembly.GetAssembly(typeof(CoinExRestClientSpotApi)); - var ignore = new string[] { "ICoinExClientSpot" }; - var clientInterfaces = assembly.GetTypes().Where(t => t.Name.StartsWith("ICoinExClient") && !ignore.Contains(t.Name)); + var ignore = new string[] { "ICoinExRestClientSpot" }; + var clientInterfaces = assembly.GetTypes().Where(t => t.Name.StartsWith("ICoinExRestClient") && !ignore.Contains(t.Name)); foreach (var clientInterface in clientInterfaces) { diff --git a/CoinEx.Net.UnitTests/CoinExSocketClientTests.cs b/CoinEx.Net.UnitTests/CoinExSocketClientTests.cs deleted file mode 100644 index 9249b13..0000000 --- a/CoinEx.Net.UnitTests/CoinExSocketClientTests.cs +++ /dev/null @@ -1,431 +0,0 @@ -// Needs rework - -//using CoinEx.Net.Objects; -//using CoinEx.Net.Objects.Websocket; -//using CryptoExchange.Net.Authentication; -//using CryptoExchange.Net.Converters; -//using CryptoExchange.Net.Testing; -//using Microsoft.Extensions.Logging; -//using Newtonsoft.Json; -//using NUnit.Framework; -//using System.Collections.Generic; -//using System.Linq; -//using System.Threading; - -//namespace CoinEx.Net.UnitTests -//{ -// [TestFixture] -// public class CoinExSocketClientTests -// { -// [Test] -// public void SubscribingWithNormalResponse_Should_Succeed() -// { -// // Arrange -// var client = TestHelpers.PrepareSocketClient(() => Construct()); - -// // Act -// var sendWait = TestHelpers.WaitForSend(client); -// var subTask = client.SubscribeToSymbolStateUpdatesAsync(data => { }); -// if(!sendWait.Result) -// Assert.Fail("No sub response"); - -// InvokeSubResponse(client); -// subTask.Wait(5000); - -// // Assert -// Assert.That(subTask.IsCompleted); -// Assert.That(subTask.Result.Success); -// } - -// [Test] -// public void SubscribingWithErrorResponse_Should_Fail() -// { -// // Arrange -// var client = TestHelpers.PrepareSocketClient(() => Construct()); - -// // Act -// var subTask = client.SubscribeToSymbolStateUpdatesAsync(data => { }); -// Thread.Sleep(10); -// TestHelpers.InvokeWebsocket(client, JsonConvert.SerializeObject( -// new CoinExSocketRequestResponse() -// { -// Error = new CoinExSocketError() { Code = 1, Message = "Error" }, -// Id = 1, -// Result = new CoinExSocketRequestResponseMessage() { Status = "error" } -// })); -// subTask.Wait(); - -// // Assert -// ClassicAssert.IsFalse(subTask.Result.Success); -// } - -// [Test] -// public void SubscribingToMarketStateUpdates_Should_InvokeUpdateMethod() -// { -// // Arrange -// var client = TestHelpers.PrepareSocketClient(() => Construct(new CoinExSocketClientOptions() -// { -// LogLevel = LogLevel.Debug -// })); - -// var receive = new Dictionary -// { -// { "BTCUSDT", new CoinExSocketSymbolState() }, -// { "ETHUSDT", new CoinExSocketSymbolState() }, -// }; -// var expected = new List { new CoinExSocketSymbolState() { Symbol = "BTCUSDT" }, -// new CoinExSocketSymbolState() { Symbol = "ETHUSDT" } }; -// List actual = null; - -// var sendWait = TestHelpers.WaitForSend(client); -// var subTask = client.SubscribeToSymbolStateUpdatesAsync(data => -// { -// actual = data.Data.ToList(); -// }); - -// if (!sendWait.Result) -// Assert.Fail(); - -// InvokeSubResponse(client); -// subTask.Wait(); - -// // Act -// InvokeSubUpdate(client, "state.update", receive); - -// // Assert -// Assert.That(subTask.Result.Success); -// Assert.That(actual != null); -// TestHelpers.PublicInstancePropertiesEqual(expected[0], actual[0]); -// TestHelpers.PublicInstancePropertiesEqual(expected[1], actual[1]); -// } - -// [Test] -// public void SubscribingToMarketTransactionUpdates_Should_InvokeUpdateMethod() -// { -// // Arrange -// var client = TestHelpers.PrepareSocketClient(() => Construct(new CoinExSocketClientOptions() -// { -// LogLevel = LogLevel.Debug, -// })); - -// var expected = new CoinExSocketSymbolTrade[] { -// new CoinExSocketSymbolTrade() { Type = TransactionType.Buy }, -// new CoinExSocketSymbolTrade() { Type = TransactionType.Sell } }; -// CoinExSocketSymbolTrade[] actual = null; - -// var sendWait = TestHelpers.WaitForSend(client); -// var subTask = client.SubscribeToSymbolTradeUpdatesAsync("ETHBTC", (data) => -// { -// actual = data.Data.ToArray(); -// }); - -// if (!sendWait.Result) -// Assert.Fail(); - -// InvokeSubResponse(client); -// subTask.Wait(); - -// // Act -// InvokeSubUpdate(client, "deals.update", "ETHBTC", expected ); - -// // Assert -// Assert.That(subTask.Result.Success); -// Assert.That(actual != null); -// TestHelpers.PublicInstancePropertiesEqual(expected, actual); -// } - -// [Test] -// public void SubscribingToMarketTransactionUpdates_Should_InvokeUpdateMethodWithExtraReceivedParamter() -// { -// // Arrange -// var client = TestHelpers.PrepareSocketClient(() => Construct(new CoinExSocketClientOptions() -// { -// LogLevel = LogLevel.Debug, -// })); - -// var expected = new CoinExSocketSymbolTrade[] { -// new CoinExSocketSymbolTrade() { Type = TransactionType.Buy }, -// new CoinExSocketSymbolTrade() { Type = TransactionType.Sell } }; -// CoinExSocketSymbolTrade[] actual = null; -// var sendWait = TestHelpers.WaitForSend(client); -// var subTask = client.SubscribeToSymbolTradeUpdatesAsync("ETHBTC", (data) => -// { -// actual = data.Data.ToArray(); -// }); - -// if (!sendWait.Result) -// Assert.Fail(); - -// InvokeSubResponse(client); -// subTask.Wait(); - -// // Act -// InvokeSubUpdate(client, "deals.update", "ETHBTC", expected); - -// // Assert -// Assert.That(subTask.Result.Success); -// Assert.That(actual != null); -// TestHelpers.PublicInstancePropertiesEqual(expected, actual); -// } - -// [Test] -// public void SubscribingToMarketDepthUpdates_Should_InvokeUpdateMethod() -// { -// // Arrange -// var client = TestHelpers.PrepareSocketClient(() => Construct(new CoinExSocketClientOptions() -// { -// LogLevel = LogLevel.Debug -// })); -// var expected = new CoinExSocketOrderBook() -// { -// Asks = new List { new CoinExDepthEntry() { Quantity = 0.1m, Price = 0.2m } }, -// Bids = new List { new CoinExDepthEntry() { Quantity = 0.1m, Price = 0.2m } } -// }; -// CoinExSocketOrderBook actual = null; - -// var sendWait = TestHelpers.WaitForSend(client); -// var subTask = client.SubscribeToOrderBookUpdatesAsync("ETHBTC", 10, 1, (data) => -// { -// actual = data.Data; -// }); -// if (!sendWait.Result) -// Assert.Fail(); - -// InvokeSubResponse(client); -// subTask.Wait(); - -// // Act -// InvokeSubUpdate(client, "depth.update", true, expected, "ETHBTC"); - - -// // Assert -// Assert.That(subTask.Result.Success); -// Assert.That(actual != null); -// TestHelpers.PublicInstancePropertiesEqual(expected, actual); -// } - -// [Test] -// public void SubscribingToMarketKlineUpdates_Should_InvokeUpdateMethod() -// { -// // Arrange -// var client = TestHelpers.PrepareSocketClient(() => Construct(new CoinExSocketClientOptions() -// { -// LogLevel = LogLevel.Debug, -// })); - -// var expected = new CoinExKline[] -// { -// new CoinExKline() { Symbol = "BTC" }, -// new CoinExKline() { Symbol = "ETH" } -// }; -// CoinExKline[] actual = null; -// var sendWait = TestHelpers.WaitForSend(client); -// var subTask = client.SubscribeToKlineUpdatesAsync("ETHBTC", KlineInterval.FiveMinute, (data) => -// { -// actual = data.Data.ToArray(); -// }); -// if (!sendWait.Result) -// Assert.Fail(); - -// InvokeSubResponse(client); -// subTask.Wait(); - -// // Act -// InvokeSubUpdate(client, "kline.update", expected); - -// // Assert -// Assert.That(subTask.Result.Success); -// Assert.That(actual != null); -// TestHelpers.PublicInstancePropertiesEqual(expected, actual); -// } - -// [Test] -// public void SubscribingToBalanceUpdates_Should_InvokeUpdateMethod() -// { -// // Arrange -// var client = TestHelpers.PrepareSocketClient(() => Construct(new CoinExSocketClientOptions() -// { -// ApiCredentials = new ApiCredentials("TestKey", "test"), -// LogLevel = LogLevel.Debug -// })); - -// var receive = new Dictionary -// { -// { "BTC", new CoinExBalance() }, -// { "ETH", new CoinExBalance() }, -// }; -// var expected = new List -// { -// new CoinExBalance() { Symbol = "BTC" }, -// new CoinExBalance() { Symbol = "ETH" } -// }; - -// List actual = null; -// var sendWait = TestHelpers.WaitForSend(client); -// var subTask = client.SubscribeToBalanceUpdatesAsync((data) => -// { -// actual = data.Data.ToList(); -// }); -// if (!sendWait.Result) -// Assert.Fail(); - -// InvokeSubResponse(client); -// Thread.Sleep(10); -// InvokeSubResponse(client, true); -// subTask.Wait(); - -// // Act -// InvokeSubUpdate(client, "asset.update", receive); - -// // Assert -// Assert.That(subTask.Result.Success); -// Assert.That(actual != null); -// TestHelpers.PublicInstancePropertiesEqual(expected, actual); -// } - -// [Test] -// public void SubscribingToOrderUpdates_Should_InvokeUpdateMethod() -// { -// // Arrange -// var client = TestHelpers.PrepareSocketClient(() => Construct(new CoinExSocketClientOptions() -// { -// ApiCredentials = new ApiCredentials("TestKey", "test"), -// LogLevel = LogLevel.Debug, -// })); - -// var expected = new CoinExSocketOrder(); -// CoinExSocketOrder actual = null; -// var sendWait = TestHelpers.WaitForSend(client); -// var subTask = client.SubscribeToOrderUpdatesAsync(new[] { "ETHBTC", "ETHBCH" }, (data) => -// { -// actual = data.Data.Order; -// }); - -// if(!sendWait.Result) -// Assert.Fail(); - -// InvokeSubResponse(client); -// Thread.Sleep(10); -// InvokeSubResponse(client, true); -// subTask.Wait(); - -// // Act -// InvokeSubUpdate(client, "order.update", 1, expected); - -// // Assert -// Assert.That(subTask.Result.Success); -// Assert.That(actual != null); -// TestHelpers.PublicInstancePropertiesEqual(expected, actual); -// } - -// [Test] -// public void SubscribingToAuthenticatedStream_Should_SendAuthentication() -// { -// // TODO -// } - -// [Test] -// public void LosingConnectionAfterSubscribing_Should_BeReconnected() -// { -// // Arrange -// //var client = TestHelpers.PrepareSocketClient(() => Construct(new CoinExSocketClientOptions() -// //{ -// // ReconnectionInterval = TimeSpan.FromMilliseconds(100), -// // SubscriptionResponseTimeout = TimeSpan.FromMilliseconds(100) -// //})); -// //var sendWait = TestHelpers.WaitForSend(client); -// //var subTask = client.SubscribeToMarketStateUpdatesAsync(data => { }); -// //if (!sendWait.Result) -// // Assert.Fail("No sub request send"); - -// //InvokeSubResponse(client); -// //subTask.Wait(); - -// //// Act -// //var conWait = TestHelpers.WaitForConnect(client); -// //TestHelpers.CloseWebsocket(client); - -// //// Assert -// //Assert.That(conWait.Result); -// } - -// [Test] -// public void LosingConnectionDuringResubscribing_Should_BeReconnected() -// { -// //// Arrange -// //var sb = new StringBuilder(); -// //var testWriter = new StringWriter(sb); -// //var client = TestHelpers.PrepareSocketClient(() => Construct(new CoinExSocketClientOptions() -// //{ -// // ReconnectionInterval = TimeSpan.FromMilliseconds(100), -// // SubscriptionResponseTimeout = TimeSpan.FromMilliseconds(500), -// // LogVerbosity = CryptoExchange.Net.Logging.LogVerbosity.Debug, -// // LogWriters = new List { testWriter } -// //})); -// //var sendWait = TestHelpers.WaitForSend(client); -// //var subTask = client.SubscribeToMarketStateUpdatesAsync(data => { }); -// //if (!sendWait.Result) -// // Assert.Fail(sb.ToString()); - -// //InvokeSubResponse(client); -// //subTask.Wait(); -// //if(!subTask.Result.Success) -// // Assert.Fail(sb.ToString()); - -// //// Act -// //// DC1 -// //var conWait = TestHelpers.WaitForConnect(client); -// //var resubSendWait = TestHelpers.WaitForSend(client); -// //TestHelpers.CloseWebsocket(client); -// //var reconResult = conWait.Result; -// //var resubResult = resubSendWait.Result; -// //if(!reconResult) -// // Assert.Fail(sb.ToString()); -// //if (!resubResult) -// // Assert.Fail(sb.ToString()); - -// //// DC2 -// //conWait = TestHelpers.WaitForConnect(client); -// //resubSendWait = TestHelpers.WaitForSend(client); -// //TestHelpers.CloseWebsocket(client); -// //reconResult = conWait.Result; -// //resubResult = resubSendWait.Result; -// //if (!reconResult) -// // Assert.Fail(sb.ToString()); -// //if (!resubResult) -// // Assert.Fail(sb.ToString()); - -// //// Assert -// //Assert.Pass(); -// } - -// private void InvokeSubResponse(CoinExSocketClient client, bool previousId = false) -// { -// TestHelpers.InvokeWebsocket(client, JsonConvert.SerializeObject( -// new CoinExSocketRequestResponse() -// { -// Error = null, -// Id = previousId ? CoinExSocketClient.LastId - 3: CoinExSocketClient.LastId, -// Result = new CoinExSocketRequestResponseMessage() { Status = "success" } -// })); -// } - -// private void InvokeSubUpdate(CoinExSocketClient client, string method, params object[] parameters) -// { -// TestHelpers.InvokeWebsocket(client, JsonConvert.SerializeObject(new CoinExSocketResponse() -// { -// Id = null, -// Method = method, -// Parameters = parameters -// }, -// new TimestampSecondsConverter())); -// } - -// private CoinExSocketClient Construct(CoinExSocketClientOptions options = null) -// { -// if (options != null) -// return new CoinExSocketClient(options); -// return new CoinExSocketClient(); -// } -// } -//} diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs index 26513ca..7af4b98 100644 --- a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs @@ -45,5 +45,18 @@ public async Task>> GetBalancesA return result; } + + /// + public async Task> SetLeverageAsync(string symbol, MarginMode mode, int leverage, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "leverage", leverage } + }; + parameters.AddEnum("market_Type", AccountType.Futures); + parameters.AddEnum("margin_mode", mode); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/futures/adjust-position-leverage"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } } } diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs index a97ccfd..f73a4c3 100644 --- a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs @@ -216,7 +216,7 @@ public async Task> CancelStopOrderAsync(string sy { "stop_id", stopOrderId } }; parameters.AddEnum("market_type", AccountType.Futures); - return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/cancel-stop-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/futures/cancel-stop-order"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); } @@ -276,6 +276,147 @@ public async Task>> GetOrderTrade return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/futures/order-deals"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); } + /// + public async Task>> GetPositionsAsync(string? symbol = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddOptional("market", symbol); + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/futures/pending-position"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetPositionHistoryAsync(string? symbol = null, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddOptional("market", symbol); + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddOptionalMilliseconds("start_time", startTime); + parameters.AddOptionalMilliseconds("end_time", endTime); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/futures/finished-position"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> ClosePositionAsync(string symbol, OrderTypeV2 orderType, decimal? price = null, decimal? quantity = null, string? clientOrderId = null, bool? hidden = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddEnum("type", orderType); + parameters.AddOptionalString("price", price); + parameters.AddOptionalString("amount", quantity); + parameters.AddOptional("client_id", clientOrderId); + parameters.AddOptional("is_hide", hidden); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/futures/close-position"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> AdjustPositionMarginAsync(string symbol, decimal quantity, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddString("amount", quantity); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/futures/adjust-position-margin"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> SetStopLossAsync(string symbol, PriceType stopLossType, decimal stopLossPrice, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddEnum("stop_loss_type", stopLossType); + parameters.AddString("stop_loss_price", stopLossPrice); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/futures/set-position-stop-loss"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task> SetTakeProfitAsync(string symbol, PriceType takeProfitType, decimal takeProfitPrice, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddEnum("take_profit_type", takeProfitType); + parameters.AddString("take_profit_price", takeProfitPrice); + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/futures/set-position-take-profit"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetMarginHistoryAsync(string symbol, long positionId, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "position_id", positionId }, + }; + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddOptionalMilliseconds("start_time", startTime); + parameters.AddOptionalMilliseconds("end_time", endTime); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/futures/position-margin-history"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetFundingRateHistoryAsync(string symbol, long positionId, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "position_id", positionId }, + }; + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddOptionalMilliseconds("start_time", startTime); + parameters.AddOptionalMilliseconds("end_time", endTime); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/futures/position-funding-history"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetAutoDeleverageHistoryAsync(string symbol, long positionId, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "position_id", positionId }, + }; + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddOptionalMilliseconds("start_time", startTime); + parameters.AddOptionalMilliseconds("end_time", endTime); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/futures/position-adl-history"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + public async Task>> GetAutoSettlementHistoryAsync(string symbol, long positionId, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "position_id", positionId }, + }; + parameters.AddEnum("market_type", AccountType.Futures); + parameters.AddOptionalMilliseconds("start_time", startTime); + parameters.AddOptionalMilliseconds("end_time", endTime); + parameters.AddOptional("page", page); + parameters.AddOptional("limit", pageSize); + return await _baseClient.ExecutePaginatedAsync(_baseClient.GetUri("v2/futures/position-settle-history"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } // TODO Batch endpoints } } diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs b/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs index 543c09f..f68ae18 100644 --- a/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs +++ b/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs @@ -68,10 +68,9 @@ protected override AuthenticationProvider CreateAuthenticationProvider(ApiCreden return id.ToString(); var method = messageAccessor.GetValue(_methodPath); - if (string.Equals(method, "deals.update", StringComparison.Ordinal)) - return method; - - if (!string.Equals(method, "state.update", StringComparison.Ordinal)) + if (!string.Equals(method, "state.update", StringComparison.Ordinal) + && !string.Equals(method, "deals.update", StringComparison.Ordinal) + && !string.Equals(method, "user_deals.update", StringComparison.Ordinal)) { var symbol = messageAccessor.GetValue(_symbolPath); return method + symbol; @@ -173,6 +172,56 @@ public async Task> SubscribeToBookPriceUpdatesAsy }, x => onMessage(x.As(x.Data, x.Data.Symbol))); return await SubscribeAsync(BaseAddress.AppendPath("v2/futures"), subscription, ct).ConfigureAwait(false); } + + /// + public async Task> SubscribeToOrderUpdatesAsync(Action> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "order", Array.Empty(), new Dictionary + { + { "market_list", Array.Empty() } + }, x => onMessage(x.As(x.Data, x.Data.Order.Symbol, SocketUpdateType.Update)), true); + return await SubscribeAsync(BaseAddress.AppendPath("v2/futures"), subscription, ct).ConfigureAwait(false); + } + + /// + public async Task> SubscribeToStopOrderUpdatesAsync(Action> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "stop", Array.Empty(), new Dictionary + { + { "market_list", Array.Empty() } + }, x => onMessage(x.As(x.Data, x.Data.Order.Symbol, SocketUpdateType.Update)), true); + return await SubscribeAsync(BaseAddress.AppendPath("v2/futures"), subscription, ct).ConfigureAwait(false); + } + + /// + public async Task> SubscribeToUserTradeUpdatesAsync(Action> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "user_deals", Array.Empty(), new Dictionary + { + { "market_list", Array.Empty() } + }, x => onMessage(x.As(x.Data, x.Data.Symbol, SocketUpdateType.Update)), true); + return await SubscribeAsync(BaseAddress.AppendPath("v2/futures"), subscription, ct).ConfigureAwait(false); + } + + /// + public async Task> SubscribeToBalanceUpdatesAsync(Action>> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "balance", Array.Empty(), new Dictionary + { + { "ccy_list", Array.Empty() } + }, x => onMessage(x.As(x.Data.Balances, null, SocketUpdateType.Update)), true); + return await SubscribeAsync(BaseAddress.AppendPath("v2/futures"), subscription, ct).ConfigureAwait(false); + } + + /// + public async Task> SubscribeToPositionUpdatesAsync(Action> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription(_logger, "position", Array.Empty(), new Dictionary + { + { "market_list", Array.Empty() } + }, onMessage, true); + return await SubscribeAsync(BaseAddress.AppendPath("v2/futures"), subscription, ct).ConfigureAwait(false); + } #endregion #endregion diff --git a/CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApi.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApi.cs index c7b7cd7..1dd559d 100644 --- a/CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApi.cs @@ -169,12 +169,12 @@ public async Task> SubscribeToAllTickerUpdatesAsy } /// - public async Task> SubscribeToOrderBookUpdatesAsync(string symbol, int limit, int mergeDepth, Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToOrderBookUpdatesAsync(string symbol, int limit, int mergeDepth, Action> onMessage, bool diffUpdates = false, CancellationToken ct = default) { mergeDepth.ValidateIntBetween(nameof(mergeDepth), 0, 8); limit.ValidateIntValues(nameof(limit), 5, 10, 20); - var subscription = new CoinExDepthSubscription(_logger, symbol, new object[] { symbol, limit, CoinExHelpers.MergeDepthIntToString(mergeDepth), false }, onMessage); + var subscription = new CoinExDepthSubscription(_logger, symbol, new object[] { symbol, limit, CoinExHelpers.MergeDepthIntToString(mergeDepth), diffUpdates }, onMessage); return await SubscribeAsync(subscription, ct).ConfigureAwait(false); } diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs index 102bf9d..6a87bd9 100644 --- a/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs @@ -67,10 +67,9 @@ protected override AuthenticationProvider CreateAuthenticationProvider(ApiCreden return id.ToString(); var method = messageAccessor.GetValue(_methodPath); - if (string.Equals(method, "deals.update", StringComparison.Ordinal)) - return method; - - if (!string.Equals(method, "state.update", StringComparison.Ordinal)) + if (!string.Equals(method, "state.update", StringComparison.Ordinal) + && !string.Equals(method, "deals.update", StringComparison.Ordinal) + && !string.Equals(method, "user_deals.update", StringComparison.Ordinal)) { var symbol = messageAccessor.GetValue(_symbolPath); return method + symbol; @@ -123,7 +122,7 @@ public async Task> SubscribeToOrderBookUpdatesAsy var subscription = new CoinExSubscription(_logger, "depth", symbols, new Dictionary { { "market_list", symbols.Select(x => new object[] { x, depth, mergeLevel ?? "0", fullBookUpdates }).ToList() } - }, x => onMessage(x.As(x.Data, x.Data.Symbol))); + }, x => onMessage(x.As(x.Data, x.Data.Symbol)), firstUpdateIsSnapshot: true); return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); } diff --git a/CoinEx.Net/CoinEx.Net.xml b/CoinEx.Net/CoinEx.Net.xml index adb1a75..2c3523d 100644 --- a/CoinEx.Net/CoinEx.Net.xml +++ b/CoinEx.Net/CoinEx.Net.xml @@ -124,6 +124,9 @@ + + + @@ -211,6 +214,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -273,6 +306,21 @@ + + + + + + + + + + + + + + + @@ -464,7 +512,7 @@ - + @@ -763,7 +811,7 @@ - Spot V1 Socket client address + Spot Socket client address @@ -964,6 +1012,21 @@ 1w + + + Margin mode + + + + + Isolated margin mode + + + + + Cross margin mode + + Deposit/Withdrawal method @@ -1169,6 +1232,41 @@ Short position + + + Position update type + + + + + Update + + + + + Close + + + + + System closing + + + + + Auto delveraging + + + + + Liquidation + + + + + Alert + + Price type @@ -1421,7 +1519,7 @@ - Endpoints related to orders and trades + Endpoints related to orders, trades and managing positions @@ -1446,6 +1544,17 @@ Cancelation token + + + Set leverage for a symbol + + + Symbol + Margin mode + Leverage + Cancelation token + + CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. @@ -1767,6 +1876,132 @@ Cancelation Token + + + Get user positions + + + Symbol + Page number + Page size + Cancelation Token + + + + + Get position history + + + Symbol + Filter by start time + Filter by end time + Page number + Page size + Cancelation Token + + + + + (partially) Close an open position + + + Symbol + Order type to use + Price of the order + Quantity to close + Client order id + Is hidden + Cancelation Token + + + + + Adjust the margin for a position. Positive quantity for increasing, negative quantity for decreasing + + + Symbol + Quantity to increase (positive number) or decrease (negative number) + Cancelation Token + + + + + Set stop loss for a position + + + Symbol + Stop loss price type + Stop loss price + Cancelation Token + + + + + Set take profit for a position + + + Symbol + Take profit price type + Take profit price + Cancelation Token + + + + + Get margin adjustment history + + + Symbol + Position id + Filter by start time + Filter by end time + Page number + Page size + Cancelation Token + + + + + Get funding rate history + + + Symbol + Position id + Filter by start time + Filter by end time + Page number + Page size + Cancelation Token + + + + + Get auto deleveraging history + + + Symbol + Position id + Filter by start time + Filter by end time + Page number + Page size + Cancelation Token + + + + + Get position settlement history + + + Symbol + Position id + Filter by start time + Filter by end time + Page number + Page size + Cancelation Token + + Futures streams @@ -1886,6 +2121,51 @@ Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + Subscribe to user order updates + + + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to user stop order updates + + + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to user trade updates + + + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to user balance updates + + + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + + + + Subscribe to user position updates + + + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Client for accessing the CoinEx API. @@ -2311,7 +2591,7 @@ The depth of merging, based on 8 decimals. 1 mergeDepth will merge the last decimals of all order in the book, 7 will merge the last 7 decimals of all orders together Order book of a symbol - + Subscribe to order book updates @@ -2319,6 +2599,7 @@ The symbol to receive updates for The limit of results to receive in a update The depth of merging, based on 8 decimals. 1 mergeDepth will merge the last decimals of all order in the book, 7 will merge the last 7 decimals of all orders together + Set to true to get snapshot first, then diff updates Data handler Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected @@ -3112,6 +3393,14 @@ Order book options + + + Create a SymbolOrderBook for the Futures API + + The symbol + Order book options + + Api addresses usable for the CoinEx clients @@ -3119,7 +3408,7 @@ - The address used by the CoinExClient for the rest API + The address used by the CoinExRestClient for the rest API @@ -4732,6 +5021,11 @@ Transferable balance + + + Equity + + Order info @@ -4842,6 +5136,21 @@ Status of the order + + + Order update + + + + + Event that triggered the update + + + + + Order data + + Futures symbol info @@ -5060,6 +5369,21 @@ Value (Quote asset volume) + + + Leverage info + + + + + Margin mode + + + + + Leverage + + Liquidation record @@ -5375,84 +5699,509 @@ Page items - + - Position levels info + Position info - + - Symbol + Position id - + - Levels + Symbol - + - Position level info + Account type - + - Upper limit of the current position + Position side - + - Leverage of current level + Margin mode - + - Current maintenance margin rate + Open interest - + - Minimum initial margin rate for the current level + position size available for closing - + - Stop order id + All time high position quantity - + - Stop order id + Unrealized profit and loss - + - Stop order info + Realized profit and loss - + - Order id + Average entry price - + - Symbol + Cumulative position value - + - Account type + Max position value - + - Asset the quantity is in + Take profit price - + - Order side + Stop loss price + + + + + Take profit price type + + + + + Stop loss price type + + + + + Leverage + + + + + Margin available + + + + + All time high margin size + + + + + Position margin rate + + + + + Maintenance margin rate + + + + + Maintenance margin value + + + + + Liquidation price + + + + + Bankruptcy price + + + + + Auto deleveraging level + + + + + Settlement price + + + + + Settlement value + + + + + Timestamp created + + + + + Timestamp last updated + + + + + Auto deleveraging info + + + + + Position id + + + + + Symbol + + + + + Account type + + + + + Order id + + + + + Trade id + + + + + Order side + + + + + Order quantity + + + + + Order price + + + + + Role + + + + + Timestamp created + + + + + Position funding rate history + + + + + Position id + + + + + Symbol + + + + + Account type + + + + + Position side + + + + + Margin mode + + + + + Open interest + + + + + Settlement price + + + + + Funding rate + + + + + Funding value + + + + + Timestamp + + + + + Position levels info + + + + + Symbol + + + + + Levels + + + + + Position level info + + + + + Upper limit of the current position + + + + + Leverage of current level + + + + + Current maintenance margin rate + + + + + Minimum initial margin rate for the current level + + + + + Position margin info + + + + + Position id + + + + + Symbol + + + + + Account type + + + + + Margin mode + + + + + Leverage + + + + + Liquidation price + + + + + Bankruptcy price + + + + + Settlement price + + + + + Open interest + + + + + Margin available + + + + + Adjusted margin amount + + + + + Timestamp created + + + + + Position settlement info + + + + + Position id + + + + + Symbol + + + + + Account type + + + + + Margin mode + + + + + Leverage + + + + + Liquidation price + + + + + Bankruptcy price + + + + + Settlement price + + + + + Open interest + + + + + Margin available + + + + + Adjusted margin amount + + + + + Timestamp created + + + + + Position update + + + + + Event that triggered the update + + + + + Position data + + + + + Position info + + + + + First filled price + + + + + Last filled price + + + + + Stop order id + + + + + Stop order id + + + + + Stop order info + + + + + Order id + + + + + Symbol + + + + + Account type + + + + + Asset the quantity is in + + + + + Order side @@ -5500,6 +6249,16 @@ Trigger price type + + + Taker fee rate + + + + + Maker fee rate + + Stop order update @@ -5515,6 +6274,106 @@ Order data + + + Order info + + + + + Order id + + + + + Symbol + + + + + Order side + + + + + Order type + + + + + Order quantity + + + + + Order price + + + + + Quantity remaining + + + + + Quantity filled + + + + + Value of the filled part + + + + + Client order id + + + + + Fee in base asset + + + + + Fee in quote asset + + + + + Fee discount + + + + + Maker fee rate + + + + + Taker fee rate + + + + + Filled amount of the last trade + + + + + Price of the last trade + + + + + Timestamp order was created + + + + + Timestamp order was last updated + + Symbol info @@ -5915,6 +6774,45 @@ Options for the Futures API + + + Symbol order book implementation + + + + + Create a new order book instance + + The symbol the order book is for + Option configuration delegate + + + + Create a new order book instance + + The symbol the order book is for + Option configuration delegate + Logger + Socket client instance + + + + + + + + + + + + + + + + + Dispose + + @@ -5927,6 +6825,9 @@ + + + Symbol order book implementation diff --git a/CoinEx.Net/CoinExEnvironment.cs b/CoinEx.Net/CoinExEnvironment.cs index 923ca0f..9992fcd 100644 --- a/CoinEx.Net/CoinExEnvironment.cs +++ b/CoinEx.Net/CoinExEnvironment.cs @@ -14,7 +14,7 @@ public class CoinExEnvironment : TradeEnvironment public string RestBaseAddress { get; } /// - /// Spot V1 Socket client address + /// Spot Socket client address /// public string SocketBaseAddress { get; } diff --git a/CoinEx.Net/Enums/MarginMode.cs b/CoinEx.Net/Enums/MarginMode.cs new file mode 100644 index 0000000..529b2f9 --- /dev/null +++ b/CoinEx.Net/Enums/MarginMode.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Margin mode + /// + public enum MarginMode + { + /// + /// Isolated margin mode + /// + Isolated, + /// + /// Cross margin mode + /// + Cross + } +} diff --git a/CoinEx.Net/Enums/PositionUpdateType.cs b/CoinEx.Net/Enums/PositionUpdateType.cs new file mode 100644 index 0000000..7e1832c --- /dev/null +++ b/CoinEx.Net/Enums/PositionUpdateType.cs @@ -0,0 +1,44 @@ +using CryptoExchange.Net.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoinEx.Net.Enums +{ + /// + /// Position update type + /// + public enum PositionUpdateType + { + /// + /// Update + /// + [Map("update")] + Update, + /// + /// Close + /// + [Map("close")] + Close, + /// + /// System closing + /// + [Map("sys_close")] + SystemClose, + /// + /// Auto delveraging + /// + [Map("adl")] + AutoDeleverage, + /// + /// Liquidation + /// + [Map("liq")] + Liquidation, + /// + /// Alert + /// + [Map("alert")] + Alert + } +} diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApi.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApi.cs index b6ce86f..1fdf92e 100644 --- a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApi.cs +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApi.cs @@ -20,7 +20,7 @@ public interface ICoinExRestClientFuturesApi : IRestApiClient, IDisposable ICoinExRestClientFuturesApiExchangeData ExchangeData { get; } /// - /// Endpoints related to orders and trades + /// Endpoints related to orders, trades and managing positions /// ICoinExRestClientFuturesApiTrading Trading { get; } } diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs index edeeffd..3e17381 100644 --- a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs @@ -31,5 +31,16 @@ public interface ICoinExRestClientFuturesApiAccount /// Cancelation token /// Task>> GetBalancesAsync(CancellationToken ct = default); + + /// + /// Set leverage for a symbol + /// + /// + /// Symbol + /// Margin mode + /// Leverage + /// Cancelation token + /// + Task> SetLeverageAsync(string symbol, MarginMode mode, int leverage, CancellationToken ct = default); } } diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs index 485e3b8..49b5894 100644 --- a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs @@ -238,5 +238,131 @@ Task> EditStopOrderAsync( /// Cancelation Token /// Task>> GetOrderTradesAsync(string symbol, long orderId, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get user positions + /// + /// + /// Symbol + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetPositionsAsync(string? symbol = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get position history + /// + /// + /// Symbol + /// Filter by start time + /// Filter by end time + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetPositionHistoryAsync(string? symbol = null, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// (partially) Close an open position + /// + /// + /// Symbol + /// Order type to use + /// Price of the order + /// Quantity to close + /// Client order id + /// Is hidden + /// Cancelation Token + /// + Task> ClosePositionAsync(string symbol, OrderTypeV2 orderType, decimal? price = null, decimal? quantity = null, string? clientOrderId = null, bool? hidden = null, CancellationToken ct = default); + + /// + /// Adjust the margin for a position. Positive quantity for increasing, negative quantity for decreasing + /// + /// + /// Symbol + /// Quantity to increase (positive number) or decrease (negative number) + /// Cancelation Token + /// + Task> AdjustPositionMarginAsync(string symbol, decimal quantity, CancellationToken ct = default); + + /// + /// Set stop loss for a position + /// + /// + /// Symbol + /// Stop loss price type + /// Stop loss price + /// Cancelation Token + /// + Task> SetStopLossAsync(string symbol, PriceType stopLossType, decimal stopLossPrice, CancellationToken ct = default); + + /// + /// Set take profit for a position + /// + /// + /// Symbol + /// Take profit price type + /// Take profit price + /// Cancelation Token + /// + Task> SetTakeProfitAsync(string symbol, PriceType takeProfitType, decimal takeProfitPrice, CancellationToken ct = default); + + /// + /// Get margin adjustment history + /// + /// + /// Symbol + /// Position id + /// Filter by start time + /// Filter by end time + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetMarginHistoryAsync(string symbol, long positionId, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get funding rate history + /// + /// + /// Symbol + /// Position id + /// Filter by start time + /// Filter by end time + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetFundingRateHistoryAsync(string symbol, long positionId, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get auto deleveraging history + /// + /// + /// Symbol + /// Position id + /// Filter by start time + /// Filter by end time + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetAutoDeleverageHistoryAsync(string symbol, long positionId, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default); + + /// + /// Get position settlement history + /// + /// + /// Symbol + /// Position id + /// Filter by start time + /// Filter by end time + /// Page number + /// Page size + /// Cancelation Token + /// + Task>> GetAutoSettlementHistoryAsync(string symbol, long positionId, DateTime? startTime = null, DateTime? endTime = null, int? page = null, int? pageSize = null, CancellationToken ct = default); } } diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs index dbf5fe0..11e4db0 100644 --- a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs @@ -131,5 +131,49 @@ public interface ICoinExSocketClientFuturesApi : ISocketApiClient, IDisposable /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected Task> SubscribeToBookPriceUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default); + /// + /// Subscribe to user order updates + /// + /// + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToOrderUpdatesAsync(Action> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to user stop order updates + /// + /// + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToStopOrderUpdatesAsync(Action> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to user trade updates + /// + /// + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToUserTradeUpdatesAsync(Action> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to user balance updates + /// + /// + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToBalanceUpdatesAsync(Action>> onMessage, CancellationToken ct = default); + + /// + /// Subscribe to user position updates + /// + /// + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToPositionUpdatesAsync(Action> onMessage, CancellationToken ct = default); } } \ No newline at end of file diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExSocketClientSpotApi.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExSocketClientSpotApi.cs index 26384c5..fafb0ec 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExSocketClientSpotApi.cs @@ -75,10 +75,11 @@ public interface ICoinExSocketClientSpotApi : ISocketApiClient, IDisposable /// The symbol to receive updates for /// The limit of results to receive in a update /// The depth of merging, based on 8 decimals. 1 mergeDepth will merge the last decimals of all order in the book, 7 will merge the last 7 decimals of all orders together + /// Set to true to get snapshot first, then diff updates /// Data handler /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToOrderBookUpdatesAsync(string symbol, int limit, int mergeDepth, Action> onMessage, CancellationToken ct = default); + Task> SubscribeToOrderBookUpdatesAsync(string symbol, int limit, int mergeDepth, Action> onMessage, bool diffUpdates = false, CancellationToken ct = default); /// /// Gets the latest trades on a symbol diff --git a/CoinEx.Net/Interfaces/ICoinExOrderBookFactory.cs b/CoinEx.Net/Interfaces/ICoinExOrderBookFactory.cs index e4277c8..4bcddc6 100644 --- a/CoinEx.Net/Interfaces/ICoinExOrderBookFactory.cs +++ b/CoinEx.Net/Interfaces/ICoinExOrderBookFactory.cs @@ -16,5 +16,13 @@ public interface ICoinExOrderBookFactory /// Order book options /// ISymbolOrderBook CreateSpot(string symbol, Action? options = null); + + /// + /// Create a SymbolOrderBook for the Futures API + /// + /// The symbol + /// Order book options + /// + ISymbolOrderBook CreateFutures(string symbol, Action? options = null); } } \ No newline at end of file diff --git a/CoinEx.Net/Objects/CoinExApiAddresses.cs b/CoinEx.Net/Objects/CoinExApiAddresses.cs index 38b738c..b3c48e9 100644 --- a/CoinEx.Net/Objects/CoinExApiAddresses.cs +++ b/CoinEx.Net/Objects/CoinExApiAddresses.cs @@ -6,7 +6,7 @@ public class CoinExApiAddresses { /// - /// The address used by the CoinExClient for the rest API + /// The address used by the CoinExRestClient for the rest API /// public string RestClientAddress { get; set; } = ""; /// diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalance.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalance.cs index 62b34fa..821de95 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalance.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalance.cs @@ -40,5 +40,10 @@ public record CoinExFuturesBalance /// [JsonPropertyName("transferrable")] public decimal Transferable { get; set; } + /// + /// Equity + /// + [JsonPropertyName("equity")] + public decimal? Equity { get; set; } } } diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalanceUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalanceUpdate.cs new file mode 100644 index 0000000..b8e1d61 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalanceUpdate.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + internal record CoinExFuturesBalanceUpdate + { + [JsonPropertyName("balance_list")] + public IEnumerable Balances { get; set; } = Array.Empty(); + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFuturesOrder.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesOrder.cs index 37db564..c549ee3 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExFuturesOrder.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesOrder.cs @@ -23,7 +23,7 @@ public record CoinExFuturesOrder /// Account type /// [JsonPropertyName("market_type")] - public AccountType AccountType { get; set; } + public AccountType? AccountType { get; set; } /// /// Order side /// @@ -53,12 +53,12 @@ public record CoinExFuturesOrder /// Quantity filled /// [JsonPropertyName("filled_amount")] - public decimal QuantityFilled { get; set; } + public decimal? QuantityFilled { get; set; } /// /// Value of the filled part /// [JsonPropertyName("filled_value")] - public decimal ValueFilled { get; set; } + public decimal? ValueFilled { get; set; } /// /// Client order id /// diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFuturesOrderUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesOrderUpdate.cs new file mode 100644 index 0000000..9bed0be --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesOrderUpdate.cs @@ -0,0 +1,25 @@ +using CoinEx.Net.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Order update + /// + public record CoinExFuturesOrderUpdate + { + /// + /// Event that triggered the update + /// + [JsonPropertyName("event")] + public OrderUpdateType Event { get; set; } + /// + /// Order data + /// + [JsonPropertyName("order")] + public CoinExFuturesOrder Order { get; set; } = null!; + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExLeverage.cs b/CoinEx.Net/Objects/Models/V2/CoinExLeverage.cs new file mode 100644 index 0000000..83a0e76 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExLeverage.cs @@ -0,0 +1,25 @@ +using CoinEx.Net.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Leverage info + /// + public record CoinExLeverage + { + /// + /// Margin mode + /// + [JsonPropertyName("margin_mode")] + public MarginMode MarginMode { get; set; } + /// + /// Leverage + /// + [JsonPropertyName("leverage")] + public decimal Leverage { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExOrderUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExOrderUpdate.cs index 7648e10..3f294b0 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExOrderUpdate.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExOrderUpdate.cs @@ -20,6 +20,6 @@ public record CoinExOrderUpdate /// Order data /// [JsonPropertyName("order")] - public CoinExOrder Order { get; set; } = null!; // TODO check if model is indeed slightly different + public CoinExStreamOrder Order { get; set; } = null!; } } diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPosition.cs b/CoinEx.Net/Objects/Models/V2/CoinExPosition.cs new file mode 100644 index 0000000..7f9d941 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExPosition.cs @@ -0,0 +1,165 @@ +using CoinEx.Net.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Position info + /// + public record CoinExPosition + { + /// + /// Position id + /// + [JsonPropertyName("position_id")] + public long Id { get; set; } + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Account type + /// + [JsonPropertyName("market_type")] + public AccountType? AccountType { get; set; } + /// + /// Position side + /// + [JsonPropertyName("side")] + public PositionSide Side { get; set; } + /// + /// Margin mode + /// + [JsonPropertyName("margin_mode")] + public MarginMode MarginMode { get; set; } + /// + /// Open interest + /// + [JsonPropertyName("open_interest")] + public decimal OpenInterest { get; set; } + /// + /// position size available for closing + /// + [JsonPropertyName("close_avbl")] + public decimal CloseAvailable { get; set; } + /// + /// All time high position quantity + /// + [JsonPropertyName("ath_position_amount")] + public decimal AthPositionQuantity { get; set; } + /// + /// Unrealized profit and loss + /// + [JsonPropertyName("unrealized_pnl")] + public decimal UnrealizedPnl { get; set; } + /// + /// Realized profit and loss + /// + [JsonPropertyName("realized_pnl")] + public decimal RealizedPnl { get; set; } + /// + /// Average entry price + /// + [JsonPropertyName("avg_entry_price")] + public decimal AverageEntryPrice { get; set; } + /// + /// Cumulative position value + /// + [JsonPropertyName("cml_position_value")] + public decimal PositionValue { get; set; } + /// + /// Max position value + /// + [JsonPropertyName("max_position_value")] + public decimal MaxPositionValue { get; set; } + /// + /// Take profit price + /// + [JsonPropertyName("take_profit_price")] + public decimal? TakeProfitPrice { get; set; } + /// + /// Stop loss price + /// + [JsonPropertyName("stop_loss_price")] + public decimal? StopLossPrice { get; set; } + /// + /// Take profit price type + /// + [JsonPropertyName("take_profit_type")] + public PriceType? TakeProfitType { get; set; } + /// + /// Stop loss price type + /// + [JsonPropertyName("stop_loss_type")] + public PriceType? StopLossType { get; set; } + /// + /// Leverage + /// + [JsonPropertyName("leverage")] + public decimal Leverage { get; set; } + /// + /// Margin available + /// + [JsonPropertyName("margin_avbl")] + public decimal MarginAvailable { get; set; } + /// + /// All time high margin size + /// + [JsonPropertyName("ath_margin_size")] + public decimal AthMarginSize { get; set; } + /// + /// Position margin rate + /// + [JsonPropertyName("position_margin_rate")] + public decimal PositionMarginRate { get; set; } + /// + /// Maintenance margin rate + /// + [JsonPropertyName("maintenance_margin_rate")] + public decimal MaintenanceMarginRate { get; set; } + /// + /// Maintenance margin value + /// + [JsonPropertyName("maintenance_margin_value")] + public decimal MaintenanceMarginValue { get; set; } + /// + /// Liquidation price + /// + [JsonPropertyName("liq_price")] + public decimal LiquidationPrice { get; set; } + /// + /// Bankruptcy price + /// + [JsonPropertyName("bkr_price")] + public decimal BankruptcyPrice { get; set; } + /// + /// Auto deleveraging level + /// + [JsonPropertyName("adl_level")] + public int AdlLevel { get; set; } + /// + /// Settlement price + /// + [JsonPropertyName("settle_price")] + public decimal SettlePrice { get; set; } + /// + /// Settlement value + /// + [JsonPropertyName("settle_value")] + public decimal SettleValue { get; set; } + /// + /// Timestamp created + /// + [JsonPropertyName("created_at")] + public DateTime CreateTime { get; set; } + /// + /// Timestamp last updated + /// + [JsonPropertyName("updated_at")] + public DateTime? UpdateTime { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPositionAdl.cs b/CoinEx.Net/Objects/Models/V2/CoinExPositionAdl.cs new file mode 100644 index 0000000..4ebed42 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionAdl.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; +using CoinEx.Net.Enums; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Auto deleveraging info + /// + public record CoinExPositionAdl + { + /// + /// Position id + /// + [JsonPropertyName("position_id")] + public long Id { get; set; } + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Account type + /// + [JsonPropertyName("market_type")] + public AccountType AccountType { get; set; } + + /// + /// Order id + /// + [JsonPropertyName("order_id")] + public long OrderId { get; set; } + /// + /// Trade id + /// + [JsonPropertyName("deal_id")] + public long TradeId { get; set; } + /// + /// Order side + /// + [JsonPropertyName("side")] + public OrderSide Side { get; set; } + /// + /// Order quantity + /// + [JsonPropertyName("amount")] + public decimal Quantity { get; set; } + /// + /// Order price + /// + [JsonPropertyName("price")] + public decimal Price { get; set; } + /// + /// Role + /// + [JsonPropertyName("role")] + public string Role { get; set; } = string.Empty; + /// + /// Timestamp created + /// + [JsonPropertyName("created_at")] + public DateTime CreateTime { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPositionFundingRate.cs b/CoinEx.Net/Objects/Models/V2/CoinExPositionFundingRate.cs new file mode 100644 index 0000000..f829e96 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionFundingRate.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; +using CoinEx.Net.Enums; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Position funding rate history + /// + public record CoinExPositionFundingRate + { + /// + /// Position id + /// + [JsonPropertyName("position_id")] + public long Id { get; set; } + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Account type + /// + [JsonPropertyName("market_type")] + public AccountType AccountType { get; set; } + /// + /// Position side + /// + [JsonPropertyName("side")] + public PositionSide Side { get; set; } + /// + /// Margin mode + /// + [JsonPropertyName("margin_mode")] + public MarginMode MarginMode { get; set; } + /// + /// Open interest + /// + [JsonPropertyName("open_interest")] + public decimal OpenInterest { get; set; } + /// + /// Settlement price + /// + [JsonPropertyName("settle_price")] + public decimal SettlePrice { get; set; } + /// + /// Funding rate + /// + [JsonPropertyName("funding_rate")] + public decimal FundingRate { get; set; } + /// + /// Funding value + /// + [JsonPropertyName("funding_value")] + public decimal FundingValue { get; set; } + /// + /// Timestamp + /// + [JsonPropertyName("created_at")] + public DateTime CreateTime { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPositionMargin.cs b/CoinEx.Net/Objects/Models/V2/CoinExPositionMargin.cs new file mode 100644 index 0000000..a9e3763 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionMargin.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; +using CoinEx.Net.Enums; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Position margin info + /// + public record CoinExPositionMargin + { + /// + /// Position id + /// + [JsonPropertyName("position_id")] + public long Id { get; set; } + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Account type + /// + [JsonPropertyName("market_type")] + public AccountType AccountType { get; set; } + /// + /// Margin mode + /// + [JsonPropertyName("margin_mode")] + public MarginMode MarginMode { get; set; } + /// + /// Leverage + /// + [JsonPropertyName("leverage")] + public decimal Leverage { get; set; } + /// + /// Liquidation price + /// + [JsonPropertyName("liq_price")] + public decimal LiquidationPrice { get; set; } + /// + /// Bankruptcy price + /// + [JsonPropertyName("bkr_price")] + public decimal BankruptcyPrice { get; set; } + /// + /// Settlement price + /// + [JsonPropertyName("settle_price")] + public decimal SettlePrice { get; set; } + /// + /// Open interest + /// + [JsonPropertyName("open_interest")] + public decimal OpenInterest { get; set; } + /// + /// Margin available + /// + [JsonPropertyName("margin_avbl")] + public decimal MarginAvailable { get; set; } + /// + /// Adjusted margin amount + /// + [JsonPropertyName("margin_change")] + public decimal MarginChange { get; set; } + /// + /// Timestamp created + /// + [JsonPropertyName("created_at")] + public DateTime CreateTime { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPositionSettlement.cs b/CoinEx.Net/Objects/Models/V2/CoinExPositionSettlement.cs new file mode 100644 index 0000000..aeff14c --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionSettlement.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; +using CoinEx.Net.Enums; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Position settlement info + /// + public record CoinExPositionSettlement + { + /// + /// Position id + /// + [JsonPropertyName("position_id")] + public long Id { get; set; } + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Account type + /// + [JsonPropertyName("market_type")] + public AccountType AccountType { get; set; } + /// + /// Margin mode + /// + [JsonPropertyName("margin_mode")] + public MarginMode MarginMode { get; set; } + /// + /// Leverage + /// + [JsonPropertyName("leverage")] + public decimal Leverage { get; set; } + /// + /// Liquidation price + /// + [JsonPropertyName("liq_price")] + public decimal LiquidationPrice { get; set; } + /// + /// Bankruptcy price + /// + [JsonPropertyName("bkr_price")] + public decimal BankruptcyPrice { get; set; } + /// + /// Settlement price + /// + [JsonPropertyName("settle_price")] + public decimal SettlePrice { get; set; } + /// + /// Open interest + /// + [JsonPropertyName("open_interest")] + public decimal OpenInterest { get; set; } + /// + /// Margin available + /// + [JsonPropertyName("margin_avbl")] + public decimal MarginAvailable { get; set; } + /// + /// Adjusted margin amount + /// + [JsonPropertyName("margin_change")] + public decimal MarginChange { get; set; } + /// + /// Timestamp created + /// + [JsonPropertyName("created_at")] + public DateTime CreateTime { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPositionUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExPositionUpdate.cs new file mode 100644 index 0000000..f011e3b --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionUpdate.cs @@ -0,0 +1,42 @@ +using CoinEx.Net.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Position update + /// + public record CoinExPositionUpdate + { + /// + /// Event that triggered the update + /// + [JsonPropertyName("event")] + public PositionUpdateType Event { get; set; } + /// + /// Position data + /// + [JsonPropertyName("position")] + public CoinExStreamPosition Position { get; set; } = null!; + } + + /// + /// Position info + /// + public record CoinExStreamPosition : CoinExPosition + { + /// + /// First filled price + /// + [JsonPropertyName("first_filled_price")] + public decimal FirstFilledPrice { get; set; } + /// + /// Last filled price + /// + [JsonPropertyName("latest_filled_price")] + public decimal LastFilledPrice { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs b/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs index 51870e7..cf87465 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs @@ -25,7 +25,7 @@ public record CoinExStopOrder /// Account type /// [JsonPropertyName("market_type")] - public AccountType AccountType { get; set; } + public AccountType? AccountType { get; set; } /// /// Asset the quantity is in /// @@ -82,5 +82,15 @@ public record CoinExStopOrder /// [JsonPropertyName("trigger_price_type")] public PriceType TriggerPriceType { get; set; } + /// + /// Taker fee rate + /// + [JsonPropertyName("taker_fee_rate")] + public decimal TakerFeeRate { get; set; } + /// + /// Maker fee rate + /// + [JsonPropertyName("maker_fee_rate")] + public decimal MakerFeeRate { get; set; } } } diff --git a/CoinEx.Net/Objects/Models/V2/CoinExStopOrderUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExStopOrderUpdate.cs index fcb5eba..4bbe932 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExStopOrderUpdate.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExStopOrderUpdate.cs @@ -15,11 +15,11 @@ public record CoinExStopOrderUpdate /// Event that triggered the update /// [JsonPropertyName("event")] - public StopOrderUpdateType Event { get; set; } // TODO CHeck if int or string return + public StopOrderUpdateType Event { get; set; } /// /// Order data /// - [JsonPropertyName("order")] - public CoinExStopOrder Order { get; set; } = null!; // TODO check if model is indeed slightly different + [JsonPropertyName("stop")] + public CoinExStopOrder Order { get; set; } = null!; } } diff --git a/CoinEx.Net/Objects/Models/V2/CoinExStreamOrder.cs b/CoinEx.Net/Objects/Models/V2/CoinExStreamOrder.cs new file mode 100644 index 0000000..22ae310 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExStreamOrder.cs @@ -0,0 +1,108 @@ +using CoinEx.Net.Enums; +using System; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Order info + /// + public record CoinExStreamOrder + { + /// + /// Order id + /// + [JsonPropertyName("order_id")] + public long Id { get; set; } + /// + /// Symbol + /// + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + /// + /// Order side + /// + [JsonPropertyName("side")] + public OrderSide Side { get; set; } + /// + /// Order type + /// + [JsonPropertyName("type")] + public OrderTypeV2 OrderType { get; set; } + /// + /// Order quantity + /// + [JsonPropertyName("amount")] + public decimal Quantity { get; set; } + /// + /// Order price + /// + [JsonPropertyName("price")] + public decimal? Price { get; set; } + /// + /// Quantity remaining + /// + [JsonPropertyName("unfilled_amount")] + public decimal QuantityRemaining { get; set; } + /// + /// Quantity filled + /// + [JsonPropertyName("filled_amount")] + public decimal QuantityFilled { get; set; } + /// + /// Value of the filled part + /// + [JsonPropertyName("filled_value")] + public decimal ValueFilled { get; set; } + /// + /// Client order id + /// + [JsonPropertyName("client_id")] + public string? ClientOrderId { get; set; } + /// + /// Fee in base asset + /// + [JsonPropertyName("base_ccy_fee")] + public decimal FeeBaseAsset { get; set; } + /// + /// Fee in quote asset + /// + [JsonPropertyName("quote_ccy_fee")] + public decimal FeeQuoteAsset { get; set; } + /// + /// Fee discount + /// + [JsonPropertyName("discount_ccy_fee")] + public decimal FeeDiscount { get; set; } + /// + /// Maker fee rate + /// + [JsonPropertyName("maker_fee_rate")] + public decimal MakerFeeRate { get; set; } + /// + /// Taker fee rate + /// + [JsonPropertyName("taker_fee_rate")] + public decimal TakerFeeRate { get; set; } + /// + /// Filled amount of the last trade + /// + [JsonPropertyName("last_filled_amount")] + public decimal? LastFilledQuantity { get; set; } + /// + /// Price of the last trade + /// + [JsonPropertyName("last_filled_price")] + public decimal? LastFilledPrice { get; set; } + /// + /// Timestamp order was created + /// + [JsonPropertyName("created_at")] + public DateTime CreateTime { get; set; } + /// + /// Timestamp order was last updated + /// + [JsonPropertyName("updated_at")] + public DateTime? UpdateTime { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs index 2f3125f..04d5398 100644 --- a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs @@ -18,15 +18,17 @@ internal class CoinExSubscription : Subscription? _symbols; private Dictionary _parameters; private Action> _handler; + private bool _firstUpdateIsSnapshot; public override HashSet ListenerIdentifiers { get; set; } - public CoinExSubscription(ILogger logger, string topic, IEnumerable? symbols, Dictionary parameters, Action> handler, bool authenticated = false) : base(logger, authenticated) + public CoinExSubscription(ILogger logger, string topic, IEnumerable? symbols, Dictionary parameters, Action> handler, bool authenticated = false, bool firstUpdateIsSnapshot = false) : base(logger, authenticated) { _topic = topic; _symbols = symbols; _parameters = parameters; _handler = handler; - if (symbols == null) + _firstUpdateIsSnapshot = firstUpdateIsSnapshot; + if (symbols?.Any() != true) ListenerIdentifiers = new HashSet { _topic + ".update" }; else ListenerIdentifiers = new HashSet(_symbols.Select(x => _topic + ".update" + x)); @@ -35,7 +37,7 @@ public CoinExSubscription(ILogger logger, string topic, IEnumerable? sym public override CallResult DoHandleMessage(SocketConnection connection, DataEvent message) { var data = (CoinExSocketUpdate)message.Data; - _handler.Invoke(message.As(data.Data)); + _handler.Invoke(message.As(data.Data, null, _firstUpdateIsSnapshot && ConnectionInvocations == 1 ? SocketUpdateType.Snapshot : SocketUpdateType.Update)); return new CallResult(null); } diff --git a/CoinEx.Net/SymbolOrderBooks/CoinExFuturesSymbolOrderBook.cs b/CoinEx.Net/SymbolOrderBooks/CoinExFuturesSymbolOrderBook.cs new file mode 100644 index 0000000..370c3a3 --- /dev/null +++ b/CoinEx.Net/SymbolOrderBooks/CoinExFuturesSymbolOrderBook.cs @@ -0,0 +1,135 @@ +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Clients; +using CoinEx.Net.Interfaces.Clients; +using CoinEx.Net.Objects.Models.V2; +using CoinEx.Net.Objects.Options; +using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.OrderBook; +using Microsoft.Extensions.Logging; + +namespace CoinEx.Net.SymbolOrderBooks +{ + /// + /// Symbol order book implementation + /// + public class CoinExFuturesSymbolOrderBook : SymbolOrderBook + { + private readonly ICoinExSocketClient _socketClient; + private readonly bool _clientOwner; + private readonly TimeSpan _initialDataTimeout; + + /// + /// Create a new order book instance + /// + /// The symbol the order book is for + /// Option configuration delegate + public CoinExFuturesSymbolOrderBook(string symbol, Action? optionsDelegate = null) + : this(symbol, optionsDelegate, null, null) + { + } + + /// + /// Create a new order book instance + /// + /// The symbol the order book is for + /// Option configuration delegate + /// Logger + /// Socket client instance + public CoinExFuturesSymbolOrderBook(string symbol, + Action? optionsDelegate, + ILoggerFactory? logger, + ICoinExSocketClient? socketClient) : base(logger, "CoinEx", "Futures", symbol) + { + var options = CoinExOrderBookOptions.Default.Copy(); + if (optionsDelegate != null) + optionsDelegate(options); + Initialize(options); + + _strictLevels = false; + _sequencesAreConsecutive = false; + _initialDataTimeout = options?.InitialDataTimeout ?? TimeSpan.FromSeconds(30); + + _socketClient = socketClient ?? new CoinExSocketClient(); + _clientOwner = socketClient == null; + Levels = options?.Limit ?? 20; + } + + /// + protected override async Task> DoStartAsync(CancellationToken ct) + { + var result = await _socketClient.FuturesApi.SubscribeToOrderBookUpdatesAsync(Symbol, Levels!.Value, null, true, HandleUpdate).ConfigureAwait(false); + if (!result) + return result; + + if (ct.IsCancellationRequested) + { + await result.Data.CloseAsync().ConfigureAwait(false); + return result.AsError(new CancellationRequestedError()); + } + + Status = OrderBookStatus.Syncing; + + var setResult = await WaitForSetOrderBookAsync(_initialDataTimeout, ct).ConfigureAwait(false); + return setResult ? result : new CallResult(setResult.Error!); + } + + /// + protected override async Task> DoResyncAsync(CancellationToken ct) + { + return await WaitForSetOrderBookAsync(_initialDataTimeout, ct).ConfigureAwait(false); + } + + /// + protected override void DoReset() + { + } + + private void HandleUpdate(DataEvent data) + { + SetInitialOrderBook(DateTime.UtcNow.Ticks, data.Data.Data.Bids, data.Data.Data.Asks); + AddChecksum((int)data.Data.Data.Checksum); + } + + /// + protected override bool DoChecksum(int checksum) + { + var checkStringBuilder = new StringBuilder(); + foreach (var bid in _bids) + { + checkStringBuilder.Append(bid.Value.Price.ToString(System.Globalization.CultureInfo.InvariantCulture) + ":" + bid.Value.Quantity.ToString(System.Globalization.CultureInfo.InvariantCulture) + ":"); + } + foreach (var ask in _asks) + { + checkStringBuilder.Append(ask.Value.Price.ToString(System.Globalization.CultureInfo.InvariantCulture) + ":" + ask.Value.Quantity.ToString(System.Globalization.CultureInfo.InvariantCulture) + ":"); + } + var checkString = checkStringBuilder.ToString().TrimEnd(':'); + var checkBytes = Encoding.ASCII.GetBytes(checkString); + var checkHexCrc32 = Force.Crc32.Crc32Algorithm.Compute(checkBytes); + var result = checkHexCrc32 == (uint)checksum; + if (!result) + { + _logger.Log(LogLevel.Debug, $"{Api} order book {Symbol} failed checksum. Expected {checkHexCrc32}, received {checksum}"); + } + else + { + _logger.Log(LogLevel.Trace, $"{Api} order book {Symbol} checksum OK."); + } + return result; + } + /// + /// + /// Dispose + /// + protected override void Dispose(bool disposing) + { + if (_clientOwner) + _socketClient?.Dispose(); + + base.Dispose(disposing); + } + } +} diff --git a/CoinEx.Net/SymbolOrderBooks/CoinExOrderBookFactory.cs b/CoinEx.Net/SymbolOrderBooks/CoinExOrderBookFactory.cs index 1fdbd2c..a0436f6 100644 --- a/CoinEx.Net/SymbolOrderBooks/CoinExOrderBookFactory.cs +++ b/CoinEx.Net/SymbolOrderBooks/CoinExOrderBookFactory.cs @@ -28,5 +28,12 @@ public ISymbolOrderBook CreateSpot(string symbol, Action options, _serviceProvider.GetRequiredService(), _serviceProvider.GetRequiredService()); + + /// + public ISymbolOrderBook CreateFutures(string symbol, Action? options = null) + => new CoinExFuturesSymbolOrderBook(symbol, + options, + _serviceProvider.GetRequiredService(), + _serviceProvider.GetRequiredService()); } } diff --git a/CoinEx.Net/SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs b/CoinEx.Net/SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs index d1f8a4e..0b86409 100644 --- a/CoinEx.Net/SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs +++ b/CoinEx.Net/SymbolOrderBooks/CoinExSpotSymbolOrderBook.cs @@ -3,9 +3,8 @@ using System.Threading; using System.Threading.Tasks; using CoinEx.Net.Clients; -using CoinEx.Net.ExtensionMethods; using CoinEx.Net.Interfaces.Clients; -using CoinEx.Net.Objects.Models.Socket; +using CoinEx.Net.Objects.Models.V2; using CoinEx.Net.Objects.Options; using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects.Sockets; @@ -62,8 +61,7 @@ public CoinExSpotSymbolOrderBook(string symbol, /// protected override async Task> DoStartAsync(CancellationToken ct) { - // TODO CHECK, MERGE REQUEST FOR ORDER BOOK SYNC DOESNT SEEM TO BE INCLUDED - var result = await _socketClient.SpotApi.SubscribeToOrderBookUpdatesAsync(Symbol, Levels!.Value, 0, HandleUpdate).ConfigureAwait(false); + var result = await _socketClient.SpotApiV2.SubscribeToOrderBookUpdatesAsync(Symbol, Levels!.Value, null, true, HandleUpdate).ConfigureAwait(false); if (!result) return result; @@ -90,20 +88,10 @@ protected override void DoReset() { } - private void HandleUpdate(DataEvent data) + private void HandleUpdate(DataEvent data) { - if (data.UpdateType == SocketUpdateType.Snapshot) - { - SetInitialOrderBook(DateTime.UtcNow.Ticks, data.Data.Bids, data.Data.Asks); - } - else - { - UpdateOrderBook(DateTime.UtcNow.Ticks, data.Data.Bids, data.Data.Asks); - } - if (data.Data.Checksum != null) - { - AddChecksum((int)data.Data.Checksum.Value); - } + SetInitialOrderBook(DateTime.UtcNow.Ticks, data.Data.Data.Bids, data.Data.Data.Asks); + AddChecksum((int)data.Data.Data.Checksum); } /// From 16383dce3c09789078709ce551943a842a7f8d57 Mon Sep 17 00:00:00 2001 From: JKorf Date: Fri, 5 Apr 2024 15:26:15 +0200 Subject: [PATCH 14/17] Unused usings --- .../Clients/FuturesApi/CoinExRestClientFuturesApi.cs | 6 ------ .../FuturesApi/CoinExRestClientFuturesApiAccount.cs | 7 +------ .../FuturesApi/CoinExRestClientFuturesApiExchangeData.cs | 1 - .../FuturesApi/CoinExRestClientFuturesApiTrading.cs | 2 -- .../Clients/FuturesApi/CoinExSocketClientFuturesApi.cs | 1 - CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApi.cs | 1 - .../Clients/SpotApiV2/CoinExRestClientSpotApiAccount.cs | 6 +----- .../Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs | 1 - CoinEx.Net/CoinExV2AuthenticationProvider.cs | 2 -- CoinEx.Net/Converters/KlineIntervalConverter.cs | 1 - CoinEx.Net/Converters/OrderOptionConverter.cs | 1 - CoinEx.Net/Converters/OrderSideConverter.cs | 1 - CoinEx.Net/Converters/OrderSideIntConverter.cs | 1 - CoinEx.Net/Converters/OrderStatusConverter.cs | 1 - CoinEx.Net/Converters/OrderTypeConverter.cs | 1 - CoinEx.Net/Converters/OrderTypeIntConverter.cs | 1 - CoinEx.Net/Converters/TransactionRoleConverter.cs | 1 - CoinEx.Net/Converters/UpdateTypeConverter.cs | 1 - CoinEx.Net/Converters/WithdrawStatusConverter.cs | 1 - CoinEx.Net/Enums/AccountType.cs | 3 --- CoinEx.Net/Enums/BorrowStatus.cs | 3 --- CoinEx.Net/Enums/ContractType.cs | 3 --- CoinEx.Net/Enums/DepositStatus.cs | 3 --- CoinEx.Net/Enums/MarginMode.cs | 6 +----- CoinEx.Net/Enums/MovementMethod.cs | 3 --- CoinEx.Net/Enums/OrderStatusV2.cs | 3 --- CoinEx.Net/Enums/OrderTypeV2.cs | 3 --- CoinEx.Net/Enums/OrderUpdateType.cs | 3 --- CoinEx.Net/Enums/PositionSide.cs | 3 --- CoinEx.Net/Enums/PositionUpdateType.cs | 3 --- CoinEx.Net/Enums/PriceType.cs | 3 --- CoinEx.Net/Enums/StopOrderUpdateType.cs | 3 --- CoinEx.Net/Enums/TransferStatus.cs | 3 --- CoinEx.Net/Enums/TriggerDirection.cs | 3 --- CoinEx.Net/Enums/TriggerPriceType.cs | 3 --- CoinEx.Net/Enums/WithdrawStatusV2.cs | 3 --- CoinEx.Net/ExtensionMethods/CoinExExtensionMethods.cs | 5 +---- .../Clients/FuturesApi/ICoinExRestClientFuturesApi.cs | 1 - .../FuturesApi/ICoinExRestClientFuturesApiAccount.cs | 3 --- .../FuturesApi/ICoinExRestClientFuturesApiTrading.cs | 1 - .../Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs | 3 --- .../Clients/SpotApiV2/ICoinExRestClientSpotApiAccount.cs | 2 -- .../Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs | 1 - .../Clients/SpotApiV2/ICoinExSocketClientSpotApi.cs | 3 --- CoinEx.Net/Objects/Models/CoinExBalance.cs | 1 - CoinEx.Net/Objects/Models/CoinExDeposit.cs | 1 - CoinEx.Net/Objects/Models/CoinExDepositAddress.cs | 1 - CoinEx.Net/Objects/Models/CoinExMiningDifficulty.cs | 1 - CoinEx.Net/Objects/Models/CoinExOrder.cs | 1 - CoinEx.Net/Objects/Models/CoinExOrderTrade.cs | 1 - CoinEx.Net/Objects/Models/CoinExSymbolState.cs | 1 - CoinEx.Net/Objects/Models/CoinExSymbolTrade.cs | 1 - CoinEx.Net/Objects/Models/CoinExWithdrawal.cs | 1 - CoinEx.Net/Objects/Models/Socket/CoinExSocketOrder.cs | 1 - CoinEx.Net/Objects/Models/Socket/CoinExSocketOrderBook.cs | 1 - .../Objects/Models/Socket/CoinExSocketSymbolTrade.cs | 1 - CoinEx.Net/Objects/Models/V2/CoinExAamLiquidity.cs | 5 +---- CoinEx.Net/Objects/Models/V2/CoinExBalance.cs | 5 +---- CoinEx.Net/Objects/Models/V2/CoinExBalanceUpdate.cs | 1 - CoinEx.Net/Objects/Models/V2/CoinExBasis.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExBookPriceUpdate.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExBorrow.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExBorrowLimit.cs | 5 +---- CoinEx.Net/Objects/Models/V2/CoinExCreditBalance.cs | 5 +---- CoinEx.Net/Objects/Models/V2/CoinExDeposit.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExDepositAddress.cs | 5 +---- .../Objects/Models/V2/CoinExDepositWithdrawalConfig.cs | 4 +--- CoinEx.Net/Objects/Models/V2/CoinExFundingRate.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExFundingRateHistory.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExFuturesBalance.cs | 5 +---- CoinEx.Net/Objects/Models/V2/CoinExFuturesBalanceUpdate.cs | 1 - CoinEx.Net/Objects/Models/V2/CoinExFuturesOrderUpdate.cs | 3 --- CoinEx.Net/Objects/Models/V2/CoinExFuturesSymbol.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExFuturesTicker.cs | 5 +---- CoinEx.Net/Objects/Models/V2/CoinExFuturesTickerUpdate.cs | 1 - CoinEx.Net/Objects/Models/V2/CoinExIndexPrice.cs | 1 - CoinEx.Net/Objects/Models/V2/CoinExKline.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExLeverage.cs | 3 --- CoinEx.Net/Objects/Models/V2/CoinExLiquidation.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExMarginBalance.cs | 5 +---- CoinEx.Net/Objects/Models/V2/CoinExOrderBook.cs | 1 - CoinEx.Net/Objects/Models/V2/CoinExOrderUpdate.cs | 3 --- CoinEx.Net/Objects/Models/V2/CoinExPaginated.cs | 1 - CoinEx.Net/Objects/Models/V2/CoinExPosition.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExPositionAdl.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExPositionFundingRate.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExPositionLevel.cs | 1 - CoinEx.Net/Objects/Models/V2/CoinExPositionMargin.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExPositionSettlement.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExPositionUpdate.cs | 3 --- CoinEx.Net/Objects/Models/V2/CoinExStopId.cs | 5 +---- CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExStopOrderUpdate.cs | 3 --- CoinEx.Net/Objects/Models/V2/CoinExTickerUpdate.cs | 1 - CoinEx.Net/Objects/Models/V2/CoinExTrade.cs | 1 - CoinEx.Net/Objects/Models/V2/CoinExTradeFee.cs | 5 +---- CoinEx.Net/Objects/Models/V2/CoinExTransfer.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExUserTrade.cs | 2 -- CoinEx.Net/Objects/Models/V2/CoinExWithdrawal.cs | 2 -- CoinEx.Net/Objects/Sockets/Queries/CoinExQuery.cs | 1 - .../Subscriptions/Balance/CoinExBalanceSubscription.cs | 1 - .../Sockets/Subscriptions/Deals/CoinExDealsSubscription.cs | 1 - .../Sockets/Subscriptions/Depth/CoinExDepthSubscription.cs | 1 - .../Subscriptions/Orders/CoinExOrderSubscription.cs | 1 - .../Sockets/Subscriptions/Orders/CoinExOrderUpdate.cs | 1 - .../Sockets/Subscriptions/State/CoinExStateSubscription.cs | 1 - CoinEx.Net/Objects/Sockets/V2/CoinExSocketResponse.cs | 3 +-- CoinEx.Net/Objects/Sockets/V2/CoinExSocketUpdate.cs | 5 +---- CoinEx.Net/Objects/Sockets/V2/Queries/CoinExQuery.cs | 4 +--- .../V2/Subscriptions/CoinExFuturesTickerSubscription.cs | 1 - .../Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs | 2 -- .../Sockets/V2/Subscriptions/CoinExTickerSubscription.cs | 1 - .../Sockets/V2/Subscriptions/CoinExTradesSubscription.cs | 1 - 113 files changed, 18 insertions(+), 242 deletions(-) diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs index 19a6b86..19154d1 100644 --- a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs @@ -7,8 +7,6 @@ using CryptoExchange.Net.Objects; using CryptoExchange.Net.Authentication; using CoinEx.Net.Objects.Internal; -using CryptoExchange.Net.CommonObjects; -using CryptoExchange.Net.Interfaces.CommonClients; using Microsoft.Extensions.Logging; using CoinEx.Net.Objects.Options; using CryptoExchange.Net.Interfaces; @@ -16,10 +14,6 @@ using CryptoExchange.Net.Clients; using CryptoExchange.Net.Converters.SystemTextJson; using CoinEx.Net.Objects.Models.V2; -using CoinEx.Net.Interfaces.Clients.SpotApiV2; -using CoinEx.Net.Enums; -using System.Linq; -using System.Globalization; using CoinEx.Net.Interfaces.Clients.FuturesApi; namespace CoinEx.Net.Clients.FuturesApi diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs index 7af4b98..ccc81d4 100644 --- a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs @@ -1,15 +1,10 @@ -using CoinEx.Net.Converters; -using CoinEx.Net.Enums; -using CryptoExchange.Net; +using CoinEx.Net.Enums; using CryptoExchange.Net.Objects; -using Newtonsoft.Json; using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using CoinEx.Net.ExtensionMethods; using CoinEx.Net.Objects.Models.V2; -using CoinEx.Net.Interfaces.Clients.SpotApiV2; using System; using CoinEx.Net.Interfaces.Clients.FuturesApi; diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiExchangeData.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiExchangeData.cs index ca7d67c..2188e80 100644 --- a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiExchangeData.cs +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiExchangeData.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using CoinEx.Net.Objects.Models.V2; using CoinEx.Net.Enums; -using CoinEx.Net.Interfaces.Clients.SpotApiV2; using CoinEx.Net.Interfaces.Clients.FuturesApi; using System; diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs index f73a4c3..46f93ef 100644 --- a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs @@ -1,12 +1,10 @@ using CoinEx.Net.Enums; using CryptoExchange.Net.Objects; -using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using CoinEx.Net.Objects.Models.V2; using System; -using CoinEx.Net.Interfaces.Clients.SpotApiV2; using CryptoExchange.Net; using CoinEx.Net.Interfaces.Clients.FuturesApi; diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs b/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs index f68ae18..86de8e3 100644 --- a/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs +++ b/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs @@ -18,7 +18,6 @@ using CoinEx.Net.Objects.Sockets.V2.Subscriptions; using CoinEx.Net.Objects.Sockets.V2.Queries; using System.Linq; -using CoinEx.Net.Interfaces.Clients.SpotApiV2; using CoinEx.Net.Interfaces.Clients.FuturesApi; namespace CoinEx.Net.Clients.FuturesApi diff --git a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApi.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApi.cs index 029bb0f..b90e3f8 100644 --- a/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApi.cs @@ -13,7 +13,6 @@ using CryptoExchange.Net.CommonObjects; using System.Globalization; using CryptoExchange.Net.Interfaces.CommonClients; -using Newtonsoft.Json.Linq; using Microsoft.Extensions.Logging; using CoinEx.Net.Objects.Options; using CryptoExchange.Net.Interfaces; diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiAccount.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiAccount.cs index 80f14c7..69073ba 100644 --- a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiAccount.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiAccount.cs @@ -1,13 +1,9 @@ -using CoinEx.Net.Converters; -using CoinEx.Net.Enums; -using CryptoExchange.Net; +using CoinEx.Net.Enums; using CryptoExchange.Net.Objects; -using Newtonsoft.Json; using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using CoinEx.Net.ExtensionMethods; using CoinEx.Net.Objects.Models.V2; using CoinEx.Net.Interfaces.Clients.SpotApiV2; using System; diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs index 85ba8e4..9097e32 100644 --- a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs @@ -1,6 +1,5 @@ using CoinEx.Net.Enums; using CryptoExchange.Net.Objects; -using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; diff --git a/CoinEx.Net/CoinExV2AuthenticationProvider.cs b/CoinEx.Net/CoinExV2AuthenticationProvider.cs index 1f49283..8b6e4ec 100644 --- a/CoinEx.Net/CoinExV2AuthenticationProvider.cs +++ b/CoinEx.Net/CoinExV2AuthenticationProvider.cs @@ -4,8 +4,6 @@ using System.Collections.Generic; using System.Net.Http; using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Interfaces; -using CoinEx.Net.Objects.Internal; using System.Linq; using CryptoExchange.Net.Clients; using CryptoExchange.Net.Converters.SystemTextJson; diff --git a/CoinEx.Net/Converters/KlineIntervalConverter.cs b/CoinEx.Net/Converters/KlineIntervalConverter.cs index e72edd0..5be59cd 100644 --- a/CoinEx.Net/Converters/KlineIntervalConverter.cs +++ b/CoinEx.Net/Converters/KlineIntervalConverter.cs @@ -1,5 +1,4 @@ using CoinEx.Net.Enums; -using CryptoExchange.Net.Converters; using System.Collections.Generic; namespace CoinEx.Net.Converters diff --git a/CoinEx.Net/Converters/OrderOptionConverter.cs b/CoinEx.Net/Converters/OrderOptionConverter.cs index ca21116..a7bd0e0 100644 --- a/CoinEx.Net/Converters/OrderOptionConverter.cs +++ b/CoinEx.Net/Converters/OrderOptionConverter.cs @@ -1,5 +1,4 @@ using CoinEx.Net.Enums; -using CryptoExchange.Net.Converters; using System.Collections.Generic; namespace CoinEx.Net.Converters diff --git a/CoinEx.Net/Converters/OrderSideConverter.cs b/CoinEx.Net/Converters/OrderSideConverter.cs index 3af219f..de98942 100644 --- a/CoinEx.Net/Converters/OrderSideConverter.cs +++ b/CoinEx.Net/Converters/OrderSideConverter.cs @@ -1,5 +1,4 @@ using CoinEx.Net.Enums; -using CryptoExchange.Net.Converters; using System.Collections.Generic; namespace CoinEx.Net.Converters diff --git a/CoinEx.Net/Converters/OrderSideIntConverter.cs b/CoinEx.Net/Converters/OrderSideIntConverter.cs index bfafd74..94ea932 100644 --- a/CoinEx.Net/Converters/OrderSideIntConverter.cs +++ b/CoinEx.Net/Converters/OrderSideIntConverter.cs @@ -1,5 +1,4 @@ using CoinEx.Net.Enums; -using CryptoExchange.Net.Converters; using System.Collections.Generic; namespace CoinEx.Net.Converters diff --git a/CoinEx.Net/Converters/OrderStatusConverter.cs b/CoinEx.Net/Converters/OrderStatusConverter.cs index b97d1dc..ecc7ac5 100644 --- a/CoinEx.Net/Converters/OrderStatusConverter.cs +++ b/CoinEx.Net/Converters/OrderStatusConverter.cs @@ -1,5 +1,4 @@ using CoinEx.Net.Enums; -using CryptoExchange.Net.Converters; using System.Collections.Generic; namespace CoinEx.Net.Converters diff --git a/CoinEx.Net/Converters/OrderTypeConverter.cs b/CoinEx.Net/Converters/OrderTypeConverter.cs index e9d799b..0a5efe9 100644 --- a/CoinEx.Net/Converters/OrderTypeConverter.cs +++ b/CoinEx.Net/Converters/OrderTypeConverter.cs @@ -1,5 +1,4 @@ using CoinEx.Net.Enums; -using CryptoExchange.Net.Converters; using System.Collections.Generic; namespace CoinEx.Net.Converters diff --git a/CoinEx.Net/Converters/OrderTypeIntConverter.cs b/CoinEx.Net/Converters/OrderTypeIntConverter.cs index ed02250..a3e15a7 100644 --- a/CoinEx.Net/Converters/OrderTypeIntConverter.cs +++ b/CoinEx.Net/Converters/OrderTypeIntConverter.cs @@ -1,5 +1,4 @@ using CoinEx.Net.Enums; -using CryptoExchange.Net.Converters; using System.Collections.Generic; namespace CoinEx.Net.Converters diff --git a/CoinEx.Net/Converters/TransactionRoleConverter.cs b/CoinEx.Net/Converters/TransactionRoleConverter.cs index da0d9e3..f4df845 100644 --- a/CoinEx.Net/Converters/TransactionRoleConverter.cs +++ b/CoinEx.Net/Converters/TransactionRoleConverter.cs @@ -1,5 +1,4 @@ using CoinEx.Net.Enums; -using CryptoExchange.Net.Converters; using System.Collections.Generic; namespace CoinEx.Net.Converters diff --git a/CoinEx.Net/Converters/UpdateTypeConverter.cs b/CoinEx.Net/Converters/UpdateTypeConverter.cs index a15b1bc..1437c05 100644 --- a/CoinEx.Net/Converters/UpdateTypeConverter.cs +++ b/CoinEx.Net/Converters/UpdateTypeConverter.cs @@ -1,5 +1,4 @@ using CoinEx.Net.Enums; -using CryptoExchange.Net.Converters; using System.Collections.Generic; namespace CoinEx.Net.Converters diff --git a/CoinEx.Net/Converters/WithdrawStatusConverter.cs b/CoinEx.Net/Converters/WithdrawStatusConverter.cs index 9204d56..7b05da3 100644 --- a/CoinEx.Net/Converters/WithdrawStatusConverter.cs +++ b/CoinEx.Net/Converters/WithdrawStatusConverter.cs @@ -1,5 +1,4 @@ using CoinEx.Net.Enums; -using CryptoExchange.Net.Converters; using System.Collections.Generic; namespace CoinEx.Net.Converters diff --git a/CoinEx.Net/Enums/AccountType.cs b/CoinEx.Net/Enums/AccountType.cs index 22751fd..68e0549 100644 --- a/CoinEx.Net/Enums/AccountType.cs +++ b/CoinEx.Net/Enums/AccountType.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Enums { diff --git a/CoinEx.Net/Enums/BorrowStatus.cs b/CoinEx.Net/Enums/BorrowStatus.cs index 8f968ed..5a958c3 100644 --- a/CoinEx.Net/Enums/BorrowStatus.cs +++ b/CoinEx.Net/Enums/BorrowStatus.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Enums { diff --git a/CoinEx.Net/Enums/ContractType.cs b/CoinEx.Net/Enums/ContractType.cs index 7a73fa9..ce7195d 100644 --- a/CoinEx.Net/Enums/ContractType.cs +++ b/CoinEx.Net/Enums/ContractType.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Enums { diff --git a/CoinEx.Net/Enums/DepositStatus.cs b/CoinEx.Net/Enums/DepositStatus.cs index c0d3e86..86b44f4 100644 --- a/CoinEx.Net/Enums/DepositStatus.cs +++ b/CoinEx.Net/Enums/DepositStatus.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Enums { diff --git a/CoinEx.Net/Enums/MarginMode.cs b/CoinEx.Net/Enums/MarginMode.cs index 529b2f9..a2e5f6d 100644 --- a/CoinEx.Net/Enums/MarginMode.cs +++ b/CoinEx.Net/Enums/MarginMode.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace CoinEx.Net.Enums +namespace CoinEx.Net.Enums { /// /// Margin mode diff --git a/CoinEx.Net/Enums/MovementMethod.cs b/CoinEx.Net/Enums/MovementMethod.cs index 54cac76..f88e583 100644 --- a/CoinEx.Net/Enums/MovementMethod.cs +++ b/CoinEx.Net/Enums/MovementMethod.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Enums { diff --git a/CoinEx.Net/Enums/OrderStatusV2.cs b/CoinEx.Net/Enums/OrderStatusV2.cs index d8c7d6e..93066e1 100644 --- a/CoinEx.Net/Enums/OrderStatusV2.cs +++ b/CoinEx.Net/Enums/OrderStatusV2.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Enums { diff --git a/CoinEx.Net/Enums/OrderTypeV2.cs b/CoinEx.Net/Enums/OrderTypeV2.cs index 8eafbf2..1ea516b 100644 --- a/CoinEx.Net/Enums/OrderTypeV2.cs +++ b/CoinEx.Net/Enums/OrderTypeV2.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Enums { diff --git a/CoinEx.Net/Enums/OrderUpdateType.cs b/CoinEx.Net/Enums/OrderUpdateType.cs index fbd85a0..1c4c3dc 100644 --- a/CoinEx.Net/Enums/OrderUpdateType.cs +++ b/CoinEx.Net/Enums/OrderUpdateType.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Enums { diff --git a/CoinEx.Net/Enums/PositionSide.cs b/CoinEx.Net/Enums/PositionSide.cs index 2b637af..e13217c 100644 --- a/CoinEx.Net/Enums/PositionSide.cs +++ b/CoinEx.Net/Enums/PositionSide.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Enums { diff --git a/CoinEx.Net/Enums/PositionUpdateType.cs b/CoinEx.Net/Enums/PositionUpdateType.cs index 7e1832c..b0feed9 100644 --- a/CoinEx.Net/Enums/PositionUpdateType.cs +++ b/CoinEx.Net/Enums/PositionUpdateType.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Enums { diff --git a/CoinEx.Net/Enums/PriceType.cs b/CoinEx.Net/Enums/PriceType.cs index ee6adf1..bb9c56a 100644 --- a/CoinEx.Net/Enums/PriceType.cs +++ b/CoinEx.Net/Enums/PriceType.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Enums { diff --git a/CoinEx.Net/Enums/StopOrderUpdateType.cs b/CoinEx.Net/Enums/StopOrderUpdateType.cs index f38b3a9..22d1a15 100644 --- a/CoinEx.Net/Enums/StopOrderUpdateType.cs +++ b/CoinEx.Net/Enums/StopOrderUpdateType.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Enums { diff --git a/CoinEx.Net/Enums/TransferStatus.cs b/CoinEx.Net/Enums/TransferStatus.cs index 8e2672d..2ff4b1f 100644 --- a/CoinEx.Net/Enums/TransferStatus.cs +++ b/CoinEx.Net/Enums/TransferStatus.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Enums { diff --git a/CoinEx.Net/Enums/TriggerDirection.cs b/CoinEx.Net/Enums/TriggerDirection.cs index cbefa03..fce1afc 100644 --- a/CoinEx.Net/Enums/TriggerDirection.cs +++ b/CoinEx.Net/Enums/TriggerDirection.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Enums { diff --git a/CoinEx.Net/Enums/TriggerPriceType.cs b/CoinEx.Net/Enums/TriggerPriceType.cs index 330efd3..69b55c2 100644 --- a/CoinEx.Net/Enums/TriggerPriceType.cs +++ b/CoinEx.Net/Enums/TriggerPriceType.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Enums { diff --git a/CoinEx.Net/Enums/WithdrawStatusV2.cs b/CoinEx.Net/Enums/WithdrawStatusV2.cs index 70c976d..1c6cd19 100644 --- a/CoinEx.Net/Enums/WithdrawStatusV2.cs +++ b/CoinEx.Net/Enums/WithdrawStatusV2.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Enums { diff --git a/CoinEx.Net/ExtensionMethods/CoinExExtensionMethods.cs b/CoinEx.Net/ExtensionMethods/CoinExExtensionMethods.cs index 14917c0..1a1abe0 100644 --- a/CoinEx.Net/ExtensionMethods/CoinExExtensionMethods.cs +++ b/CoinEx.Net/ExtensionMethods/CoinExExtensionMethods.cs @@ -1,7 +1,4 @@ -using System; -using System.Text.RegularExpressions; - -namespace CoinEx.Net.ExtensionMethods +namespace CoinEx.Net.ExtensionMethods { /// /// Extension methods specific to using the CoinEx API diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApi.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApi.cs index 1fdf92e..8417bf6 100644 --- a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApi.cs +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApi.cs @@ -1,5 +1,4 @@ using CryptoExchange.Net.Interfaces; -using CryptoExchange.Net.Interfaces.CommonClients; using System; namespace CoinEx.Net.Interfaces.Clients.FuturesApi diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs index 3e17381..be6cd4c 100644 --- a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs @@ -4,9 +4,6 @@ using CoinEx.Net.Objects.Models.V2; using CoinEx.Net.Enums; using System.Collections.Generic; -using System.Diagnostics.Metrics; -using System.Text.RegularExpressions; -using System; namespace CoinEx.Net.Interfaces.Clients.FuturesApi { diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs index 49b5894..fde1b79 100644 --- a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs @@ -3,7 +3,6 @@ using System.Threading; using System.Threading.Tasks; using CoinEx.Net.Objects.Models.V2; -using System.Collections.Generic; using System; namespace CoinEx.Net.Interfaces.Clients.FuturesApi diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs index 11e4db0..f61a27d 100644 --- a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs @@ -2,9 +2,6 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using CoinEx.Net.Enums; -using CoinEx.Net.Objects.Models; -using CoinEx.Net.Objects.Models.Socket; using CoinEx.Net.Objects.Models.V2; using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Objects; diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiAccount.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiAccount.cs index c743e67..175be40 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiAccount.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiAccount.cs @@ -4,8 +4,6 @@ using CoinEx.Net.Objects.Models.V2; using CoinEx.Net.Enums; using System.Collections.Generic; -using System.Diagnostics.Metrics; -using System.Text.RegularExpressions; using System; namespace CoinEx.Net.Interfaces.Clients.SpotApiV2 diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs index 11a12f7..2997424 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs @@ -3,7 +3,6 @@ using System.Threading; using System.Threading.Tasks; using CoinEx.Net.Objects.Models.V2; -using System.Collections.Generic; using System; namespace CoinEx.Net.Interfaces.Clients.SpotApiV2 diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExSocketClientSpotApi.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExSocketClientSpotApi.cs index 8086709..261bd6c 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExSocketClientSpotApi.cs @@ -2,9 +2,6 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using CoinEx.Net.Enums; -using CoinEx.Net.Objects.Models; -using CoinEx.Net.Objects.Models.Socket; using CoinEx.Net.Objects.Models.V2; using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Objects; diff --git a/CoinEx.Net/Objects/Models/CoinExBalance.cs b/CoinEx.Net/Objects/Models/CoinExBalance.cs index 9e33082..17521c9 100644 --- a/CoinEx.Net/Objects/Models/CoinExBalance.cs +++ b/CoinEx.Net/Objects/Models/CoinExBalance.cs @@ -1,6 +1,5 @@ using System; using CoinEx.Net.Converters; -using CryptoExchange.Net.Converters; using Newtonsoft.Json; namespace CoinEx.Net.Objects.Models diff --git a/CoinEx.Net/Objects/Models/CoinExDeposit.cs b/CoinEx.Net/Objects/Models/CoinExDeposit.cs index b6ff9cc..454016b 100644 --- a/CoinEx.Net/Objects/Models/CoinExDeposit.cs +++ b/CoinEx.Net/Objects/Models/CoinExDeposit.cs @@ -1,6 +1,5 @@ using System; using CoinEx.Net.Converters; -using CryptoExchange.Net.Converters; using Newtonsoft.Json; namespace CoinEx.Net.Objects.Models diff --git a/CoinEx.Net/Objects/Models/CoinExDepositAddress.cs b/CoinEx.Net/Objects/Models/CoinExDepositAddress.cs index 8eb805c..500331c 100644 --- a/CoinEx.Net/Objects/Models/CoinExDepositAddress.cs +++ b/CoinEx.Net/Objects/Models/CoinExDepositAddress.cs @@ -1,5 +1,4 @@ using System; -using CryptoExchange.Net.Converters; using Newtonsoft.Json; namespace CoinEx.Net.Objects.Models diff --git a/CoinEx.Net/Objects/Models/CoinExMiningDifficulty.cs b/CoinEx.Net/Objects/Models/CoinExMiningDifficulty.cs index f419732..5aac1b3 100644 --- a/CoinEx.Net/Objects/Models/CoinExMiningDifficulty.cs +++ b/CoinEx.Net/Objects/Models/CoinExMiningDifficulty.cs @@ -1,5 +1,4 @@ using System; -using CryptoExchange.Net.Converters; using Newtonsoft.Json; namespace CoinEx.Net.Objects.Models diff --git a/CoinEx.Net/Objects/Models/CoinExOrder.cs b/CoinEx.Net/Objects/Models/CoinExOrder.cs index edddce0..502150d 100644 --- a/CoinEx.Net/Objects/Models/CoinExOrder.cs +++ b/CoinEx.Net/Objects/Models/CoinExOrder.cs @@ -1,7 +1,6 @@ using System; using CoinEx.Net.Converters; using CoinEx.Net.Enums; -using CryptoExchange.Net.Converters; using Newtonsoft.Json; namespace CoinEx.Net.Objects.Models diff --git a/CoinEx.Net/Objects/Models/CoinExOrderTrade.cs b/CoinEx.Net/Objects/Models/CoinExOrderTrade.cs index 4583573..8f12033 100644 --- a/CoinEx.Net/Objects/Models/CoinExOrderTrade.cs +++ b/CoinEx.Net/Objects/Models/CoinExOrderTrade.cs @@ -1,7 +1,6 @@ using System; using CoinEx.Net.Converters; using CoinEx.Net.Enums; -using CryptoExchange.Net.Converters; using Newtonsoft.Json; namespace CoinEx.Net.Objects.Models diff --git a/CoinEx.Net/Objects/Models/CoinExSymbolState.cs b/CoinEx.Net/Objects/Models/CoinExSymbolState.cs index ea27a3a..2633a45 100644 --- a/CoinEx.Net/Objects/Models/CoinExSymbolState.cs +++ b/CoinEx.Net/Objects/Models/CoinExSymbolState.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using CoinEx.Net.Converters; -using CryptoExchange.Net.Converters; using Newtonsoft.Json; namespace CoinEx.Net.Objects.Models diff --git a/CoinEx.Net/Objects/Models/CoinExSymbolTrade.cs b/CoinEx.Net/Objects/Models/CoinExSymbolTrade.cs index f628cf3..ca68d8e 100644 --- a/CoinEx.Net/Objects/Models/CoinExSymbolTrade.cs +++ b/CoinEx.Net/Objects/Models/CoinExSymbolTrade.cs @@ -1,7 +1,6 @@ using System; using CoinEx.Net.Converters; using CoinEx.Net.Enums; -using CryptoExchange.Net.Converters; using Newtonsoft.Json; namespace CoinEx.Net.Objects.Models diff --git a/CoinEx.Net/Objects/Models/CoinExWithdrawal.cs b/CoinEx.Net/Objects/Models/CoinExWithdrawal.cs index fd26975..df1a2a2 100644 --- a/CoinEx.Net/Objects/Models/CoinExWithdrawal.cs +++ b/CoinEx.Net/Objects/Models/CoinExWithdrawal.cs @@ -1,7 +1,6 @@ using System; using CoinEx.Net.Converters; using CoinEx.Net.Enums; -using CryptoExchange.Net.Converters; using Newtonsoft.Json; namespace CoinEx.Net.Objects.Models diff --git a/CoinEx.Net/Objects/Models/Socket/CoinExSocketOrder.cs b/CoinEx.Net/Objects/Models/Socket/CoinExSocketOrder.cs index e111bd7..df4c544 100644 --- a/CoinEx.Net/Objects/Models/Socket/CoinExSocketOrder.cs +++ b/CoinEx.Net/Objects/Models/Socket/CoinExSocketOrder.cs @@ -1,7 +1,6 @@ using System; using CoinEx.Net.Converters; using CoinEx.Net.Enums; -using CryptoExchange.Net.Converters; using Newtonsoft.Json; namespace CoinEx.Net.Objects.Models.Socket diff --git a/CoinEx.Net/Objects/Models/Socket/CoinExSocketOrderBook.cs b/CoinEx.Net/Objects/Models/Socket/CoinExSocketOrderBook.cs index 02dcefb..f0042b9 100644 --- a/CoinEx.Net/Objects/Models/Socket/CoinExSocketOrderBook.cs +++ b/CoinEx.Net/Objects/Models/Socket/CoinExSocketOrderBook.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using CryptoExchange.Net.Converters; using Newtonsoft.Json; namespace CoinEx.Net.Objects.Models.Socket diff --git a/CoinEx.Net/Objects/Models/Socket/CoinExSocketSymbolTrade.cs b/CoinEx.Net/Objects/Models/Socket/CoinExSocketSymbolTrade.cs index 2f4e537..01de32f 100644 --- a/CoinEx.Net/Objects/Models/Socket/CoinExSocketSymbolTrade.cs +++ b/CoinEx.Net/Objects/Models/Socket/CoinExSocketSymbolTrade.cs @@ -1,7 +1,6 @@ using System; using CoinEx.Net.Converters; using CoinEx.Net.Enums; -using CryptoExchange.Net.Converters; using Newtonsoft.Json; namespace CoinEx.Net.Objects.Models.Socket diff --git a/CoinEx.Net/Objects/Models/V2/CoinExAamLiquidity.cs b/CoinEx.Net/Objects/Models/V2/CoinExAamLiquidity.cs index 77c1c82..79d0425 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExAamLiquidity.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExAamLiquidity.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 { diff --git a/CoinEx.Net/Objects/Models/V2/CoinExBalance.cs b/CoinEx.Net/Objects/Models/V2/CoinExBalance.cs index 0e94d68..8ef11f9 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExBalance.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExBalance.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 { diff --git a/CoinEx.Net/Objects/Models/V2/CoinExBalanceUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExBalanceUpdate.cs index 6f19273..e0d5d3c 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExBalanceUpdate.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExBalanceUpdate.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExBasis.cs b/CoinEx.Net/Objects/Models/V2/CoinExBasis.cs index a6eb088..c47c52b 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExBasis.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExBasis.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExBookPriceUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExBookPriceUpdate.cs index d9bd06d..c8b97c8 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExBookPriceUpdate.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExBookPriceUpdate.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExBorrow.cs b/CoinEx.Net/Objects/Models/V2/CoinExBorrow.cs index da2ec69..82035b4 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExBorrow.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExBorrow.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; using CoinEx.Net.Enums; diff --git a/CoinEx.Net/Objects/Models/V2/CoinExBorrowLimit.cs b/CoinEx.Net/Objects/Models/V2/CoinExBorrowLimit.cs index 5e6bce5..0f0f1b9 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExBorrowLimit.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExBorrowLimit.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 { diff --git a/CoinEx.Net/Objects/Models/V2/CoinExCreditBalance.cs b/CoinEx.Net/Objects/Models/V2/CoinExCreditBalance.cs index 22d548e..9dd53bc 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExCreditBalance.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExCreditBalance.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 { diff --git a/CoinEx.Net/Objects/Models/V2/CoinExDeposit.cs b/CoinEx.Net/Objects/Models/V2/CoinExDeposit.cs index 58941f2..79bdb9a 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExDeposit.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExDeposit.cs @@ -1,7 +1,5 @@ using CoinEx.Net.Enums; using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExDepositAddress.cs b/CoinEx.Net/Objects/Models/V2/CoinExDepositAddress.cs index f16d25f..2945784 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExDepositAddress.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExDepositAddress.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 { diff --git a/CoinEx.Net/Objects/Models/V2/CoinExDepositWithdrawalConfig.cs b/CoinEx.Net/Objects/Models/V2/CoinExDepositWithdrawalConfig.cs index e67b2ac..4ce8252 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExDepositWithdrawalConfig.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExDepositWithdrawalConfig.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Collections.Generic; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFundingRate.cs b/CoinEx.Net/Objects/Models/V2/CoinExFundingRate.cs index 8958d5f..b29d3a1 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExFundingRate.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExFundingRate.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFundingRateHistory.cs b/CoinEx.Net/Objects/Models/V2/CoinExFundingRateHistory.cs index 38f2f53..93d1dc5 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExFundingRateHistory.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExFundingRateHistory.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalance.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalance.cs index 821de95..2bd0e30 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalance.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalance.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 { diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalanceUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalanceUpdate.cs index b8e1d61..1789eff 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalanceUpdate.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalanceUpdate.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFuturesOrderUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesOrderUpdate.cs index 9bed0be..1b61524 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExFuturesOrderUpdate.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesOrderUpdate.cs @@ -1,7 +1,4 @@ using CoinEx.Net.Enums; -using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFuturesSymbol.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesSymbol.cs index 3cac9a0..ace2c7a 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExFuturesSymbol.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesSymbol.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; using CoinEx.Net.Enums; diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFuturesTicker.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesTicker.cs index bbee8f2..fc44a64 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExFuturesTicker.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesTicker.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 { diff --git a/CoinEx.Net/Objects/Models/V2/CoinExFuturesTickerUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesTickerUpdate.cs index 5b4e144..de4ce8e 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExFuturesTickerUpdate.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesTickerUpdate.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExIndexPrice.cs b/CoinEx.Net/Objects/Models/V2/CoinExIndexPrice.cs index 044d4a8..323ec4d 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExIndexPrice.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExIndexPrice.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExKline.cs b/CoinEx.Net/Objects/Models/V2/CoinExKline.cs index 45a63af..d963a25 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExKline.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExKline.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExLeverage.cs b/CoinEx.Net/Objects/Models/V2/CoinExLeverage.cs index 83a0e76..7a8bdde 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExLeverage.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExLeverage.cs @@ -1,7 +1,4 @@ using CoinEx.Net.Enums; -using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExLiquidation.cs b/CoinEx.Net/Objects/Models/V2/CoinExLiquidation.cs index 4445d66..6c1f80e 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExLiquidation.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExLiquidation.cs @@ -1,7 +1,5 @@ using CoinEx.Net.Enums; using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExMarginBalance.cs b/CoinEx.Net/Objects/Models/V2/CoinExMarginBalance.cs index da69da0..ddb6e42 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExMarginBalance.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExMarginBalance.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 { diff --git a/CoinEx.Net/Objects/Models/V2/CoinExOrderBook.cs b/CoinEx.Net/Objects/Models/V2/CoinExOrderBook.cs index db1b7d0..d08aa3e 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExOrderBook.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExOrderBook.cs @@ -2,7 +2,6 @@ using CryptoExchange.Net.Interfaces; using System; using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExOrderUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExOrderUpdate.cs index 3f294b0..3cb15cb 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExOrderUpdate.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExOrderUpdate.cs @@ -1,7 +1,4 @@ using CoinEx.Net.Enums; -using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPaginated.cs b/CoinEx.Net/Objects/Models/V2/CoinExPaginated.cs index 27bc6ee..24ff6a4 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExPaginated.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExPaginated.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; namespace CoinEx.Net.Objects.Models.V2 { diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPosition.cs b/CoinEx.Net/Objects/Models/V2/CoinExPosition.cs index 7f9d941..dcbcdb8 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExPosition.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExPosition.cs @@ -1,7 +1,5 @@ using CoinEx.Net.Enums; using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPositionAdl.cs b/CoinEx.Net/Objects/Models/V2/CoinExPositionAdl.cs index 4ebed42..ddff8a0 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExPositionAdl.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionAdl.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; using CoinEx.Net.Enums; diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPositionFundingRate.cs b/CoinEx.Net/Objects/Models/V2/CoinExPositionFundingRate.cs index f829e96..6db9c42 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExPositionFundingRate.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionFundingRate.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; using CoinEx.Net.Enums; diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPositionLevel.cs b/CoinEx.Net/Objects/Models/V2/CoinExPositionLevel.cs index 425fb33..e5e9768 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExPositionLevel.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionLevel.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPositionMargin.cs b/CoinEx.Net/Objects/Models/V2/CoinExPositionMargin.cs index a9e3763..7cd9289 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExPositionMargin.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionMargin.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; using CoinEx.Net.Enums; diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPositionSettlement.cs b/CoinEx.Net/Objects/Models/V2/CoinExPositionSettlement.cs index aeff14c..53f99ef 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExPositionSettlement.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionSettlement.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; using CoinEx.Net.Enums; diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPositionUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExPositionUpdate.cs index f011e3b..cb149ec 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExPositionUpdate.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionUpdate.cs @@ -1,7 +1,4 @@ using CoinEx.Net.Enums; -using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExStopId.cs b/CoinEx.Net/Objects/Models/V2/CoinExStopId.cs index cc2bf25..8a86ccf 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExStopId.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExStopId.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 { diff --git a/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs b/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs index cf87465..9a6b945 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs @@ -1,7 +1,5 @@ using CoinEx.Net.Enums; using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExStopOrderUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExStopOrderUpdate.cs index 4bbe932..250e5d8 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExStopOrderUpdate.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExStopOrderUpdate.cs @@ -1,7 +1,4 @@ using CoinEx.Net.Enums; -using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExTickerUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExTickerUpdate.cs index 0b870c1..11630c3 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExTickerUpdate.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExTickerUpdate.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExTrade.cs b/CoinEx.Net/Objects/Models/V2/CoinExTrade.cs index 4b4818a..b005eec 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExTrade.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExTrade.cs @@ -1,7 +1,6 @@ using CoinEx.Net.Enums; using System; using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExTradeFee.cs b/CoinEx.Net/Objects/Models/V2/CoinExTradeFee.cs index d0c7c49..93478c3 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExTradeFee.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExTradeFee.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 { diff --git a/CoinEx.Net/Objects/Models/V2/CoinExTransfer.cs b/CoinEx.Net/Objects/Models/V2/CoinExTransfer.cs index 43b910a..25d73b9 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExTransfer.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExTransfer.cs @@ -1,7 +1,5 @@ using CoinEx.Net.Enums; using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExUserTrade.cs b/CoinEx.Net/Objects/Models/V2/CoinExUserTrade.cs index fcdd9f4..881000d 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExUserTrade.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExUserTrade.cs @@ -1,7 +1,5 @@ using CoinEx.Net.Enums; using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Models/V2/CoinExWithdrawal.cs b/CoinEx.Net/Objects/Models/V2/CoinExWithdrawal.cs index 79cb456..8b123b6 100644 --- a/CoinEx.Net/Objects/Models/V2/CoinExWithdrawal.cs +++ b/CoinEx.Net/Objects/Models/V2/CoinExWithdrawal.cs @@ -1,7 +1,5 @@ using CoinEx.Net.Enums; using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Models.V2 diff --git a/CoinEx.Net/Objects/Sockets/Queries/CoinExQuery.cs b/CoinEx.Net/Objects/Sockets/Queries/CoinExQuery.cs index 54ccdf9..dc04ecf 100644 --- a/CoinEx.Net/Objects/Sockets/Queries/CoinExQuery.cs +++ b/CoinEx.Net/Objects/Sockets/Queries/CoinExQuery.cs @@ -3,7 +3,6 @@ using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Sockets; using System.Collections.Generic; -using System.Threading.Tasks; namespace CoinEx.Net.Objects.Sockets.Queries { diff --git a/CoinEx.Net/Objects/Sockets/Subscriptions/Balance/CoinExBalanceSubscription.cs b/CoinEx.Net/Objects/Sockets/Subscriptions/Balance/CoinExBalanceSubscription.cs index b193a1d..ac864c2 100644 --- a/CoinEx.Net/Objects/Sockets/Subscriptions/Balance/CoinExBalanceSubscription.cs +++ b/CoinEx.Net/Objects/Sockets/Subscriptions/Balance/CoinExBalanceSubscription.cs @@ -8,7 +8,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace CoinEx.Net.Objects.Sockets.Subscriptions.Balance { diff --git a/CoinEx.Net/Objects/Sockets/Subscriptions/Deals/CoinExDealsSubscription.cs b/CoinEx.Net/Objects/Sockets/Subscriptions/Deals/CoinExDealsSubscription.cs index 4f2de77..f3afe22 100644 --- a/CoinEx.Net/Objects/Sockets/Subscriptions/Deals/CoinExDealsSubscription.cs +++ b/CoinEx.Net/Objects/Sockets/Subscriptions/Deals/CoinExDealsSubscription.cs @@ -8,7 +8,6 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; -using System.Threading.Tasks; namespace CoinEx.Net.Objects.Sockets.Subscriptions.Deals { diff --git a/CoinEx.Net/Objects/Sockets/Subscriptions/Depth/CoinExDepthSubscription.cs b/CoinEx.Net/Objects/Sockets/Subscriptions/Depth/CoinExDepthSubscription.cs index f0130a0..f84f6b8 100644 --- a/CoinEx.Net/Objects/Sockets/Subscriptions/Depth/CoinExDepthSubscription.cs +++ b/CoinEx.Net/Objects/Sockets/Subscriptions/Depth/CoinExDepthSubscription.cs @@ -7,7 +7,6 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; -using System.Threading.Tasks; namespace CoinEx.Net.Objects.Sockets.Subscriptions.Depth { diff --git a/CoinEx.Net/Objects/Sockets/Subscriptions/Orders/CoinExOrderSubscription.cs b/CoinEx.Net/Objects/Sockets/Subscriptions/Orders/CoinExOrderSubscription.cs index d938120..c05061f 100644 --- a/CoinEx.Net/Objects/Sockets/Subscriptions/Orders/CoinExOrderSubscription.cs +++ b/CoinEx.Net/Objects/Sockets/Subscriptions/Orders/CoinExOrderSubscription.cs @@ -8,7 +8,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace CoinEx.Net.Objects.Sockets.Subscriptions.Orders { diff --git a/CoinEx.Net/Objects/Sockets/Subscriptions/Orders/CoinExOrderUpdate.cs b/CoinEx.Net/Objects/Sockets/Subscriptions/Orders/CoinExOrderUpdate.cs index fb08040..c962fd3 100644 --- a/CoinEx.Net/Objects/Sockets/Subscriptions/Orders/CoinExOrderUpdate.cs +++ b/CoinEx.Net/Objects/Sockets/Subscriptions/Orders/CoinExOrderUpdate.cs @@ -3,7 +3,6 @@ using CoinEx.Net.Objects.Models.Socket; using CryptoExchange.Net.Attributes; using CryptoExchange.Net.Converters; -using CryptoExchange.Net.Converters.JsonNet; using Newtonsoft.Json; namespace CoinEx.Net.Objects.Sockets.Subscriptions.Orders diff --git a/CoinEx.Net/Objects/Sockets/Subscriptions/State/CoinExStateSubscription.cs b/CoinEx.Net/Objects/Sockets/Subscriptions/State/CoinExStateSubscription.cs index f06eb27..4f7de6a 100644 --- a/CoinEx.Net/Objects/Sockets/Subscriptions/State/CoinExStateSubscription.cs +++ b/CoinEx.Net/Objects/Sockets/Subscriptions/State/CoinExStateSubscription.cs @@ -8,7 +8,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace CoinEx.Net.Objects.Sockets.Subscriptions.State { diff --git a/CoinEx.Net/Objects/Sockets/V2/CoinExSocketResponse.cs b/CoinEx.Net/Objects/Sockets/V2/CoinExSocketResponse.cs index 96aa906..0cc85f8 100644 --- a/CoinEx.Net/Objects/Sockets/V2/CoinExSocketResponse.cs +++ b/CoinEx.Net/Objects/Sockets/V2/CoinExSocketResponse.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Sockets.V2 { diff --git a/CoinEx.Net/Objects/Sockets/V2/CoinExSocketUpdate.cs b/CoinEx.Net/Objects/Sockets/V2/CoinExSocketUpdate.cs index 43ccd12..34ed003 100644 --- a/CoinEx.Net/Objects/Sockets/V2/CoinExSocketUpdate.cs +++ b/CoinEx.Net/Objects/Sockets/V2/CoinExSocketUpdate.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace CoinEx.Net.Objects.Sockets.V2 { diff --git a/CoinEx.Net/Objects/Sockets/V2/Queries/CoinExQuery.cs b/CoinEx.Net/Objects/Sockets/V2/Queries/CoinExQuery.cs index a90447b..dbe8c6f 100644 --- a/CoinEx.Net/Objects/Sockets/V2/Queries/CoinExQuery.cs +++ b/CoinEx.Net/Objects/Sockets/V2/Queries/CoinExQuery.cs @@ -1,10 +1,8 @@ -using CoinEx.Net.Objects.Sockets.V2; -using CryptoExchange.Net; +using CryptoExchange.Net; using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Sockets; using System.Collections.Generic; -using System.Threading.Tasks; namespace CoinEx.Net.Objects.Sockets.V2.Queries { diff --git a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExFuturesTickerSubscription.cs b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExFuturesTickerSubscription.cs index 5be2f94..b26cd7d 100644 --- a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExFuturesTickerSubscription.cs +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExFuturesTickerSubscription.cs @@ -8,7 +8,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace CoinEx.Net.Objects.Sockets.V2.Subscriptions { diff --git a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs index 04d5398..3fb3bae 100644 --- a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs @@ -1,5 +1,4 @@ using CoinEx.Net.Objects.Sockets.V2.Queries; -using CryptoExchange.Net.Converters.MessageParsing; using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects.Sockets; @@ -8,7 +7,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; namespace CoinEx.Net.Objects.Sockets.V2.Subscriptions { diff --git a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTickerSubscription.cs b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTickerSubscription.cs index 49092f5..d374bbd 100644 --- a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTickerSubscription.cs +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTickerSubscription.cs @@ -8,7 +8,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace CoinEx.Net.Objects.Sockets.V2.Subscriptions { diff --git a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs index 0bfa021..c7cd200 100644 --- a/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs @@ -8,7 +8,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace CoinEx.Net.Objects.Sockets.V2.Subscriptions { From 0eda275b330cc468ec2c47f432a4735473028e1b Mon Sep 17 00:00:00 2001 From: JKorf Date: Fri, 5 Apr 2024 19:54:05 +0200 Subject: [PATCH 15/17] Common endpoints --- .../FuturesApi/CoinExRestClientFuturesApi.cs | 11 ++- .../CoinExRestClientFuturesApiExchangeData.cs | 7 ++ .../SpotApiV2/CoinExRestClientSpotApi.cs | 11 ++- .../CoinExRestClientSpotApiExchangeData.cs | 15 ++++ .../SpotApiV2/CoinExSocketClientSpotApi.cs | 10 +++ CoinEx.Net/CoinEx.Net.csproj | 4 +- CoinEx.Net/CoinEx.Net.xml | 69 +++++++++++++++++++ ...ICoinExRestClientFuturesApiExchangeData.cs | 8 +++ .../ICoinExRestClientSpotApiExchangeData.cs | 18 +++++ .../SpotApiV2/ICoinExSocketClientSpotApi.cs | 8 +++ .../Objects/Models/V2/CoinExMaintenance.cs | 38 ++++++++++ .../Objects/Models/V2/CoinExServerTime.cs | 13 ++++ 12 files changed, 205 insertions(+), 7 deletions(-) create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExMaintenance.cs create mode 100644 CoinEx.Net/Objects/Models/V2/CoinExServerTime.cs diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs index 19154d1..859c95d 100644 --- a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs @@ -22,6 +22,8 @@ namespace CoinEx.Net.Clients.FuturesApi public class CoinExRestClientFuturesApi : RestApiClient, ICoinExRestClientFuturesApi { #region fields + internal TimeSyncState _timeSyncState = new TimeSyncState("CoinEx V2 API"); + /// public new CoinExRestOptions ClientOptions => (CoinExRestOptions)base.ClientOptions; #endregion @@ -132,9 +134,14 @@ internal async Task>> ExecutePaginatedAsync( } /// - public override TimeSyncInfo? GetTimeSyncInfo() => null; + protected override async Task> GetServerTimestampAsync() => await ExchangeData.GetServerTimeAsync().ConfigureAwait(false); + + /// + public override TimeSyncInfo? GetTimeSyncInfo() + => new TimeSyncInfo(_logger, (ApiOptions.AutoTimestamp ?? ClientOptions.AutoTimestamp), (ApiOptions.TimestampRecalculationInterval ?? ClientOptions.TimestampRecalculationInterval), _timeSyncState); /// - public override TimeSpan? GetTimeOffset() => null; + public override TimeSpan? GetTimeOffset() + => _timeSyncState.TimeOffset; } } diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiExchangeData.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiExchangeData.cs index 2188e80..6a823dc 100644 --- a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiExchangeData.cs +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiExchangeData.cs @@ -20,6 +20,13 @@ internal CoinExRestClientFuturesApiExchangeData(CoinExRestClientFuturesApi baseC _baseClient = baseClient; } + /// + public async Task> GetServerTimeAsync(CancellationToken ct = default) + { + var result = await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/time"), HttpMethod.Get, ct).ConfigureAwait(false); + return result.As(result.Data?.ServerTime ?? default); + } + /// public async Task>> GetSymbolsAsync(IEnumerable? symbols = null, CancellationToken ct = default) { diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs index a81c764..9e2947b 100644 --- a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs @@ -27,6 +27,8 @@ namespace CoinEx.Net.Clients.SpotApiV2 public class CoinExRestClientSpotApi : RestApiClient, ICoinExRestClientSpotApi, ISpotClient { #region fields + internal TimeSyncState _timeSyncState = new TimeSyncState("CoinEx V2 API"); + /// public new CoinExRestOptions ClientOptions => (CoinExRestOptions)base.ClientOptions; @@ -459,10 +461,15 @@ private static KlineInterval GetKlineIntervalFromTimespan(TimeSpan timeSpan) } /// - public override TimeSyncInfo? GetTimeSyncInfo() => null; + protected override async Task> GetServerTimestampAsync() => await ExchangeData.GetServerTimeAsync().ConfigureAwait(false); + + /// + public override TimeSyncInfo? GetTimeSyncInfo() + => new TimeSyncInfo(_logger, (ApiOptions.AutoTimestamp ?? ClientOptions.AutoTimestamp), (ApiOptions.TimestampRecalculationInterval ?? ClientOptions.TimestampRecalculationInterval), _timeSyncState); /// - public override TimeSpan? GetTimeOffset() => null; + public override TimeSpan? GetTimeOffset() + => _timeSyncState.TimeOffset; /// public ISpotClient CommonSpotClient => this; diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiExchangeData.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiExchangeData.cs index 0714194..511c616 100644 --- a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiExchangeData.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiExchangeData.cs @@ -6,6 +6,7 @@ using CoinEx.Net.Objects.Models.V2; using CoinEx.Net.Enums; using CoinEx.Net.Interfaces.Clients.SpotApiV2; +using System; namespace CoinEx.Net.Clients.SpotApiV2 { @@ -20,6 +21,20 @@ internal CoinExRestClientSpotApiExchangeData(CoinExRestClientSpotApi baseClient) } + /// + public async Task> GetServerTimeAsync(CancellationToken ct = default) + { + var result = await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/time"), HttpMethod.Get, ct).ConfigureAwait(false); + return result.As(result.Data?.ServerTime ?? default); + } + + // Doesn't seem to exist on the url specified in the docs + ///// + //public async Task>> GetMaintenanceInfoAsync(CancellationToken ct = default) + //{ + // return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/maintain-info"), HttpMethod.Get, ct).ConfigureAwait(false); + //} + /// public async Task>> GetSymbolsAsync(CancellationToken ct = default) { diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs index 6a87bd9..9c369a4 100644 --- a/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs @@ -92,6 +92,16 @@ public override ReadOnlyMemory PreprocessStreamMessage(WebSocketMessageTyp #region public + /// + public async Task> SubscribeToSystemNoticeUpdatesAsync(Action>> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExSubscription>(_logger, "notice", null, new Dictionary + { + { "channels", new object[] { 101 } } + }, onMessage, true); + return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); + } + /// public async Task> SubscribeToTickerUpdatesAsync(Action>> onMessage, CancellationToken ct = default) { diff --git a/CoinEx.Net/CoinEx.Net.csproj b/CoinEx.Net/CoinEx.Net.csproj index 91e1ffa..484a954 100644 --- a/CoinEx.Net/CoinEx.Net.csproj +++ b/CoinEx.Net/CoinEx.Net.csproj @@ -49,12 +49,10 @@ 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 2c3523d..80983aa 100644 --- a/CoinEx.Net/CoinEx.Net.xml +++ b/CoinEx.Net/CoinEx.Net.xml @@ -109,6 +109,9 @@ + + + @@ -130,6 +133,9 @@ + + + @@ -575,6 +581,9 @@ + + + @@ -656,6 +665,9 @@ + + + @@ -754,6 +766,9 @@ + + + @@ -1560,6 +1575,14 @@ CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. + + + Get server time + + + Cancelation token + + Get list of support symbols @@ -2949,6 +2972,14 @@ CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. + + + Get server time + + + Cancelation token + + Get symbol information @@ -3230,6 +3261,14 @@ Spot streams + + + Subscribe to system notification updates + + Data handler + Cancellation token for closing this subscription + A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Subscribe to symbol ticker updates for the specified symbols. Note that only one ticker subscription can be active at the same time; new ticker subscription will override the old subscriptions. @@ -5419,6 +5458,36 @@ Timestamp + + + Maintenance info + + + + + Start time of the maintenance + + + + + End time of the maintenance + + + + + Scope that's impacted + + + + + Protection period start time. The protection period refers to a continuous period following the system maintenance (It's an optional configuration, and may or may not be set). During the protection period, you can cancel orders, place orders (limited to Maker Only Limit Orders), and adjust (add or reduce) margins. + + + + + Protection period end time. The protection period refers to a continuous period following the system maintenance (It's an optional configuration, and may or may not be set). During the protection period, you can cancel orders, place orders (limited to Maker Only Limit Orders), and adjust (add or reduce) margins. + + Margin balance info diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiExchangeData.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiExchangeData.cs index e5abf7b..fca4b64 100644 --- a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiExchangeData.cs +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiExchangeData.cs @@ -13,6 +13,14 @@ namespace CoinEx.Net.Interfaces.Clients.FuturesApi /// public interface ICoinExRestClientFuturesApiExchangeData { + /// + /// Get server time + /// + /// + /// Cancelation token + /// + Task> GetServerTimeAsync(CancellationToken ct = default); + /// /// Get list of support symbols /// diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiExchangeData.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiExchangeData.cs index 258fbe8..22f6b39 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiExchangeData.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiExchangeData.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using CoinEx.Net.Objects.Models.V2; +using System; namespace CoinEx.Net.Interfaces.Clients.SpotApiV2 { @@ -12,6 +13,23 @@ namespace CoinEx.Net.Interfaces.Clients.SpotApiV2 /// public interface ICoinExRestClientSpotApiExchangeData { + /// + /// Get server time + /// + /// + /// Cancelation token + /// + Task> GetServerTimeAsync(CancellationToken ct = default); + + // Doesn't seem to exist on the url specified in the docs + ///// + ///// Get maintenance info + ///// + ///// + ///// Cancelation token + ///// + //Task>> GetMaintenanceInfoAsync(CancellationToken ct = default); + /// /// Get symbol information /// diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExSocketClientSpotApi.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExSocketClientSpotApi.cs index 261bd6c..e28d65c 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExSocketClientSpotApi.cs @@ -14,6 +14,14 @@ namespace CoinEx.Net.Interfaces.Clients.SpotApiV2 /// public interface ICoinExSocketClientSpotApi : ISocketApiClient, IDisposable { + /// + /// Subscribe to system notification updates + /// + /// Data handler + /// Cancellation token for closing this subscription + /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected + Task> SubscribeToSystemNoticeUpdatesAsync(Action>> onMessage, CancellationToken ct = default); + /// /// Subscribe to symbol ticker updates for the specified symbols. Note that only one ticker subscription can be active at the same time; new ticker subscription will override the old subscriptions. /// diff --git a/CoinEx.Net/Objects/Models/V2/CoinExMaintenance.cs b/CoinEx.Net/Objects/Models/V2/CoinExMaintenance.cs new file mode 100644 index 0000000..b699d4f --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExMaintenance.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Maintenance info + /// + public record CoinExMaintenance + { + /// + /// Start time of the maintenance + /// + [JsonPropertyName("started_at")] + public DateTime? StartTime { get; set; } + /// + /// End time of the maintenance + /// + [JsonPropertyName("ended_at")] + public DateTime? EndTime { get; set; } + /// + /// Scope that's impacted + /// + [JsonPropertyName("scope")] + public IEnumerable Scope { get; set; } = Array.Empty(); + /// + /// Protection period start time. The protection period refers to a continuous period following the system maintenance (It's an optional configuration, and may or may not be set). During the protection period, you can cancel orders, place orders (limited to Maker Only Limit Orders), and adjust (add or reduce) margins. + /// + [JsonPropertyName("protect_duration_start")] + public DateTime? ProtectDurationStart { get; set; } + /// + /// Protection period end time. The protection period refers to a continuous period following the system maintenance (It's an optional configuration, and may or may not be set). During the protection period, you can cancel orders, place orders (limited to Maker Only Limit Orders), and adjust (add or reduce) margins. + /// + [JsonPropertyName("protect_duration_end")] + public DateTime? ProtectDurationEnd { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExServerTime.cs b/CoinEx.Net/Objects/Models/V2/CoinExServerTime.cs new file mode 100644 index 0000000..394a038 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExServerTime.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + internal record CoinExServerTime + { + [JsonPropertyName("timestamp")] + public DateTime ServerTime { get; set; } + } +} From b815b427640fca4a8e355f3e34ea7858fefaca41 Mon Sep 17 00:00:00 2001 From: JKorf Date: Fri, 5 Apr 2024 20:53:40 +0200 Subject: [PATCH 16/17] Updated CryptoExchange.Net version --- CoinEx.Net/CoinEx.Net.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CoinEx.Net/CoinEx.Net.csproj b/CoinEx.Net/CoinEx.Net.csproj index 484a954..d219fda 100644 --- a/CoinEx.Net/CoinEx.Net.csproj +++ b/CoinEx.Net/CoinEx.Net.csproj @@ -49,7 +49,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From e002afda4a4efe1734180e96845651290a2fb0e2 Mon Sep 17 00:00:00 2001 From: JKorf Date: Sat, 6 Apr 2024 11:24:29 +0200 Subject: [PATCH 17/17] Fix for v1 ticker subscription --- .../Sockets/Subscriptions/State/CoinExStateSubscription.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CoinEx.Net/Objects/Sockets/Subscriptions/State/CoinExStateSubscription.cs b/CoinEx.Net/Objects/Sockets/Subscriptions/State/CoinExStateSubscription.cs index 4f7de6a..000c097 100644 --- a/CoinEx.Net/Objects/Sockets/Subscriptions/State/CoinExStateSubscription.cs +++ b/CoinEx.Net/Objects/Sockets/Subscriptions/State/CoinExStateSubscription.cs @@ -34,6 +34,8 @@ public override CallResult DoHandleMessage(SocketConnection connection, DataEven item.Value.Symbol = item.Key; var relevant = data.Data.First().Where(d => _symbol == null || d.Key == _symbol).Select(d => d.Value); + if (!relevant.Any()) + return new CallResult(null); _handler.Invoke(message.As(relevant, _symbol, SocketUpdateType.Update)); return new CallResult(null);