diff --git a/CoinEx.Net.UnitTests/CoinExClientTests.cs b/CoinEx.Net.UnitTests/CoinExClientTests.cs index ad6a50a..afbdc7d 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 { @@ -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/CoinExRestClient.cs b/CoinEx.Net/Clients/CoinExRestClient.cs index aa45c98..85f4c99 100644 --- a/CoinEx.Net/Clients/CoinExRestClient.cs +++ b/CoinEx.Net/Clients/CoinExRestClient.cs @@ -1,12 +1,12 @@ 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; 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 { @@ -15,7 +15,11 @@ public class CoinExRestClient : BaseRestClient, ICoinExRestClient { #region Api clients /// - public ICoinExClientSpotApi SpotApi { get; } + public ICoinExRestClientFuturesApi FuturesApi { get; } + /// + public Interfaces.Clients.SpotApiV1.ICoinExRestClientSpotApi SpotApi { get; } + /// + public Interfaces.Clients.SpotApiV2.ICoinExRestClientSpotApi SpotApiV2 { get; } #endregion #region ctor @@ -41,7 +45,9 @@ public CoinExRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, A optionsDelegate(options); Initialize(options); - SpotApi = AddApiClient(new CoinExRestClientSpotApi(_logger, httpClient, options)); + FuturesApi = AddApiClient(new CoinExRestClientFuturesApi(_logger, httpClient, options)); + SpotApi = AddApiClient(new SpotApiV1.CoinExRestClientSpotApi(_logger, httpClient, options)); + SpotApiV2 = AddApiClient(new SpotApiV2.CoinExRestClientSpotApi(_logger, httpClient, options)); } #endregion @@ -60,7 +66,9 @@ public static void SetDefaultOptions(Action optionsDelegate) /// public void SetApiCredentials(ApiCredentials credentials) { + FuturesApi.SetApiCredentials(credentials); SpotApi.SetApiCredentials(credentials); + SpotApiV2.SetApiCredentials(credentials); } #endregion } diff --git a/CoinEx.Net/Clients/CoinExSocketClient.cs b/CoinEx.Net/Clients/CoinExSocketClient.cs index da2cdde..787dca0 100644 --- a/CoinEx.Net/Clients/CoinExSocketClient.cs +++ b/CoinEx.Net/Clients/CoinExSocketClient.cs @@ -1,11 +1,11 @@ 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; using CoinEx.Net.Objects.Options; using CryptoExchange.Net.Clients; +using CoinEx.Net.Interfaces.Clients.FuturesApi; +using CoinEx.Net.Clients.FuturesApi; namespace CoinEx.Net.Clients { @@ -15,7 +15,11 @@ public class CoinExSocketClient : BaseSocketClient, ICoinExSocketClient #region Api clients /// - public ICoinExSocketClientSpotApi SpotApi { get; } + public ICoinExSocketClientFuturesApi FuturesApi { get; } + /// + public Interfaces.Clients.SpotApiV2.ICoinExSocketClientSpotApi SpotApiV2 { get; } + /// + public Interfaces.Clients.SpotApiV1.ICoinExSocketClientSpotApi SpotApi { get; } #endregion @@ -48,7 +52,9 @@ public CoinExSocketClient(Action optionsDelegate, ILoggerFa optionsDelegate(options); Initialize(options); - SpotApi = AddApiClient(new CoinExSocketClientSpotApi(_logger, options)); + FuturesApi = AddApiClient(new CoinExSocketClientFuturesApi(_logger, options)); + SpotApi = AddApiClient(new SpotApiV1.CoinExSocketClientSpotApi(_logger, options)); + SpotApiV2 = AddApiClient(new SpotApiV2.CoinExSocketClientSpotApi(_logger, options)); } #endregion @@ -66,6 +72,8 @@ 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..859c95d --- /dev/null +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApi.cs @@ -0,0 +1,147 @@ +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 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.FuturesApi; + +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 + + /// + 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); + } + + /// + 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() + => _timeSyncState.TimeOffset; + } +} diff --git a/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs new file mode 100644 index 0000000..ccc81d4 --- /dev/null +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiAccount.cs @@ -0,0 +1,57 @@ +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.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, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol } + }; + 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; + } + + /// + 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/CoinExRestClientFuturesApiExchangeData.cs b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiExchangeData.cs new file mode 100644 index 0000000..6a823dc --- /dev/null +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiExchangeData.cs @@ -0,0 +1,147 @@ +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.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> 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) + { + 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..46f93ef --- /dev/null +++ b/CoinEx.Net/Clients/FuturesApi/CoinExRestClientFuturesApiTrading.cs @@ -0,0 +1,420 @@ +using CoinEx.Net.Enums; +using CryptoExchange.Net.Objects; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Objects.Models.V2; +using System; +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; + } + + /// + 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/futures/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); + } + + /// + 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 new file mode 100644 index 0000000..86de8e3 --- /dev/null +++ b/CoinEx.Net/Clients/FuturesApi/CoinExSocketClientFuturesApi.cs @@ -0,0 +1,228 @@ +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.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) + { + HandleMessageBeforeConfirmation = true; + + 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, "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; + } + + 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(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 CoinExFuturesTickerSubscription(_logger, null, new Dictionary + { + { "market_list", new object[] { } } + }, onMessage); + 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); + } + + /// + 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/SpotApi/CoinExRestClientSpotApi.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApi.cs similarity index 98% rename from CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs rename to CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApi.cs index 3c792db..b90e3f8 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApi.cs @@ -10,21 +10,20 @@ 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; +using CoinEx.Net.Interfaces.Clients.SpotApiV1; -namespace CoinEx.Net.Clients.SpotApi +namespace CoinEx.Net.Clients.SpotApiV1 { - /// - public class CoinExRestClientSpotApi : RestApiClient, ICoinExClientSpotApi, ISpotClient + /// + public class CoinExRestClientSpotApi : RestApiClient, ICoinExRestClientSpotApi, ISpotClient { #region fields /// @@ -45,11 +44,11 @@ public class CoinExRestClientSpotApi : RestApiClient, ICoinExClientSpotApi, ISpo #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/SpotApiV1/CoinExRestClientSpotApiAccount.cs similarity index 96% rename from CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiAccount.cs rename to CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiAccount.cs index eda9aa9..82e84b8 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiAccount.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 CoinExRestClientSpotApiAccount : ICoinExClientSpotApiAccount + public class CoinExRestClientSpotApiAccount : ICoinExRestClientSpotApiAccount { private const string AccountInfoEndpoint = "balance/info"; private const string WithdrawalHistoryEndpoint = "balance/coin/withdraw"; diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiExchangeData.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiExchangeData.cs similarity index 97% rename from CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiExchangeData.cs rename to CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiExchangeData.cs index 8bb6ed4..d0d422a 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiExchangeData.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 CoinExRestClientSpotApiExchangeData : ICoinExClientSpotApiExchangeData + public class CoinExRestClientSpotApiExchangeData : ICoinExRestClientSpotApiExchangeData { private const string AssetConfigEndpoint = "common/asset/config"; private const string CurrencyRateEndpoint = "common/currency/rate"; diff --git a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiTrading.cs similarity index 98% rename from CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.cs rename to CoinEx.Net/Clients/SpotApiV1/CoinExRestClientSpotApiTrading.cs index 671e740..259136e 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExRestClientSpotApiTrading.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 CoinExRestClientSpotApiTrading : ICoinExClientSpotApiTrading + public class CoinExRestClientSpotApiTrading : ICoinExRestClientSpotApiTrading { private const string PlaceLimitOrderEndpoint = "order/limit"; private const string PlaceMarketOrderEndpoint = "order/market"; diff --git a/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs b/CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApi.cs similarity index 98% rename from CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs rename to CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApi.cs index c7222dd..1dd559d 100644 --- a/CoinEx.Net/Clients/SpotApi/CoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Clients/SpotApiV1/CoinExSocketClientSpotApi.cs @@ -14,23 +14,21 @@ 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 CoinExSocketClientSpotApi : SocketApiClient, ICoinExSocketClientSpotApi @@ -171,7 +169,7 @@ public async Task> SubscribeToAllTickerUpdatesAsy } /// - public async Task> SubscribeToOrderBookUpdatesAsync(string symbol, int limit, int mergeDepth, Action> onMessage, bool diffUpdates, 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); diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs new file mode 100644 index 0000000..9e2947b --- /dev/null +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApi.cs @@ -0,0 +1,477 @@ +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; + +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; + + /// + /// 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 ICoinExRestClientSpotApiAccount Account { get; } + /// + public ICoinExRestClientSpotApiExchangeData ExchangeData { get; } + /// + public ICoinExRestClientSpotApiTrading Trading { get; } + #endregion + + internal readonly string _brokerId; + + #region ctor + internal CoinExRestClientSpotApi(ILogger logger, HttpClient? httpClient, CoinExRestOptions options) : + base(logger, httpClient, options.Environment.RestBaseAddress, options, options.SpotOptions) + { + Account = new CoinExRestClientSpotApiAccount(this); + ExchangeData = new CoinExRestClientSpotApiExchangeData(this); + Trading = new CoinExRestClientSpotApiTrading(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 + + #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) + { + 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); + } + + /// + 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() + => _timeSyncState.TimeOffset; + + /// + public ISpotClient CommonSpotClient => this; + } +} diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiAccount.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiAccount.cs new file mode 100644 index 0000000..69073ba --- /dev/null +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiAccount.cs @@ -0,0 +1,256 @@ +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 CoinEx.Net.Interfaces.Clients.SpotApiV2; +using System; + +namespace CoinEx.Net.Clients.SpotApiV2 +{ + /// + public class CoinExRestClientSpotApiAccount : ICoinExRestClientSpotApiAccount + { + private readonly CoinExRestClientSpotApi _baseClient; + + 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); + } + + /// + 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/CoinExRestClientSpotApiExchangeData.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiExchangeData.cs new file mode 100644 index 0000000..511c616 --- /dev/null +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiExchangeData.cs @@ -0,0 +1,97 @@ +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 System; + +namespace CoinEx.Net.Clients.SpotApiV2 +{ + /// + public class CoinExRestClientSpotApiExchangeData : ICoinExRestClientSpotApiExchangeData + { + private readonly CoinExRestClientSpotApi _baseClient; + + internal CoinExRestClientSpotApiExchangeData(CoinExRestClientSpotApi baseClient) + { + _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); + } + + // 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) + { + return await _baseClient.ExecuteAsync>(_baseClient.GetUri("v2/spot/market"), HttpMethod.Get, ct).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/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/SpotApiV2/CoinExRestClientSpotApiTrading.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs new file mode 100644 index 0000000..9097e32 --- /dev/null +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExRestClientSpotApiTrading.cs @@ -0,0 +1,300 @@ +using CoinEx.Net.Enums; +using CryptoExchange.Net.Objects; +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; + +namespace CoinEx.Net.Clients.SpotApiV2 +{ + /// + public class CoinExRestClientSpotApiTrading : ICoinExRestClientSpotApiTrading + { + private readonly CoinExRestClientSpotApi _baseClient; + + 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) + { + clientOrderId ??= ExchangeHelpers.AppendRandomString("x-" + _baseClient._brokerId + "-", 32); + + 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); + 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; + } + + /// + 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) + { + clientOrderId ??= ExchangeHelpers.AppendRandomString("x-" + _baseClient._brokerId + "-", 32); + + 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); + 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, long orderId, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "order_id", orderId } + }; + return await _baseClient.ExecuteAsync(_baseClient.GetUri("v2/spot/order-status"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + /// + 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, + long 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, + 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); + 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() + { + { "market", symbol }, + }; + parameters.AddEnum("market_type", accountType); + parameters.AddOptionalEnum("side", side); + 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, long orderId, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "order_id", orderId } + }; + parameters.AddEnum("market_type", accountType); + 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, long stopOrderId, CancellationToken ct = default) + { + var parameters = new ParameterCollection() + { + { "market", symbol }, + { "stop_id", stopOrderId } + }; + parameters.AddEnum("market_type", accountType); + 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; + } + + /// + 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); + 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; + } + + /// + 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); + 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; + } + + /// + 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>> GetOrderTradesAsync(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.ExecutePaginatedAsync(_baseClient.GetUri("v2/spot/order-deals"), HttpMethod.Get, ct, parameters, true).ConfigureAwait(false); + } + + // TODO Batch endpoints + } +} diff --git a/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs b/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs new file mode 100644 index 0000000..9c369a4 --- /dev/null +++ b/CoinEx.Net/Clients/SpotApiV2/CoinExSocketClientSpotApi.cs @@ -0,0 +1,228 @@ +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; + +namespace CoinEx.Net.Clients.SpotApiV2 +{ + /// + 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) + { + HandleMessageBeforeConfirmation = true; + + 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, "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; + } + + 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> 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) + { + var subscription = new CoinExTickerSubscription(_logger, null, new Dictionary + { + { "market_list", new object[] { } } + }, onMessage); + return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), subscription, ct).ConfigureAwait(false); + } + + /// + public async Task> SubscribeToTickerUpdatesAsync(IEnumerable symbols, Action>> onMessage, CancellationToken ct = default) + { + var subscription = new CoinExTickerSubscription(_logger, symbols, new Dictionary + { + { "market_list", symbols } + }, onMessage); + return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), 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)), firstUpdateIsSnapshot: true); + return await SubscribeAsync(BaseAddress.AppendPath("v2/spot"), 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/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); + } + + /// + 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.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 diff --git a/CoinEx.Net/CoinEx.Net.xml b/CoinEx.Net/CoinEx.Net.xml index 36d3837..80983aa 100644 --- a/CoinEx.Net/CoinEx.Net.xml +++ b/CoinEx.Net/CoinEx.Net.xml @@ -7,9 +7,15 @@ + + + + + + Create a new instance of the CoinExRestClient using provided options @@ -36,6 +42,12 @@ + + + + + + @@ -67,2050 +79,6693 @@ - - + + - + - - - 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 - - - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - - - Create a new instance of CoinExSocketClient with default options - + + - + - + - + - + - + - + - + - + - + - + - + + + + - + + + Create a new instance of CoinExSocketClient with default options + + + - + - + - + - + - + - + - - - CoinEx environments - + + - - - Spot Rest client address - + + - - - Spot V1 Socket client address - + + - - - Live environment - + + - - - Create a custom environment - - - - - + + - - - CoinEx helpers - + + - - - Kline interval to seconds - - - + + - - - Merge depth to string parameter - - - + + - - - Interval for klines, int value represent the time in seconds - + + - - - 1m - + + - - - 3m - + + - - - 5m - + + - - - 15m - + + - - - 30m - + + - - - 1h - + + - - - 2h - + + - - - 4h - + + - + - 6h + Event triggered when an order is placed via this client - + - 12h + Event triggered when an order is canceled via this client. Note that this does not trigger when using CancelAllOrdersAsync - - - 1d - + + - - - 3d - + + - - - 1w - + + - - - Options when placing an order - + + - - - Normal order - + + - + - Immediate or cancel order + Get the name of a symbol for CoinEx based on the base and quote asset + + + - - - Fill or kill order - + + - - - Maker only order - + + - - - Order side - + + - - - Either (only usable for filtering) - + + - - - Buy - + + - - - Sell - + + - - - Status of an order - + + - - - Executed - + + - - - Partially executed - + + - - - New - + + - - - Canceled - + + - - - Type of order - + + - - - Limit order - + + - - - Market order - + + - - - Stop limit order - + + - - - Stop market order - + + - - - Role of a transaction - + + - - - Maker of a new order book entry - + + - - - Taker of an existing order book entry - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + Get the name of a symbol for CoinEx based on the base and quote asset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create a new instance of CoinExSocketClient with default options + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CoinEx environments + + + + + Spot Rest client address + + + + + Spot Socket client address + + + + + Live environment + + + + + Create a custom environment + + + + + + + + + CoinEx helpers + + + + + Kline interval to seconds + + + + + + + Merge depth to string parameter + + + + + + + Account type + + + + + Spot account + + + + + Margin account + + + + + Futures account + + + + + Borrow status + + + + + Borrowing + + + + + In debt + + + + + Forcefully liquidated + + + + + Has been repaid + + + + + Type of contract + + + + + Linear contract + + + + + Inverse contract + + + + + 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 + + + + + 1m + + + + + 3m + + + + + 5m + + + + + 15m + + + + + 30m + + + + + 1h + + + + + 2h + + + + + 4h + + + + + 6h + + + + + 12h + + + + + 1d + + + + + 3d + + + + + 1w + + + + + Margin mode + + + + + Isolated margin mode + + + + + Cross margin mode + + + + + Deposit/Withdrawal method + + + + + On chain + + + + + Between users + + + + + Options when placing an order + + + + + Normal order + + + + + Immediate or cancel order + + + + + Fill or kill order + + + + + Maker only order + + + + + Order side + + + + + Either (only usable for filtering) + + + + + Buy + + + + + Sell + + + + + Status of an order + + + + + Executed + + + + + Partially executed + + + + + New + + + + + Canceled + + + + + Order status + + + + + Open + + + + + Partially filled + + + + + Fully filled + + + + + Partially filled, partially canceled + + + + + Fully canceled + + + + + Type of order + + + + + Limit order + + + + + Market order + + + + + Stop limit order + + + + + 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 + + + + + Position side + + + + + Long position + + + + + Short position + + + + + Position update type + + + + + Update + + + + + Close + + + + + System closing + + + + + Auto delveraging + + + + + Liquidation + + + + + Alert + + + + + Price type + + + + + Last price + + + + + Mark price + + + + + Index price + + + + + Stop order update type + + + + + Order created + + + + + Order active + + + + + Order canceled + + + + + Role of a transaction + + + + + Maker of a new order book entry + + + + + Taker of an existing order book entry + + + + + Transfer status + + + + + Created + + + + + Asset deducted + + + + + Failed to transfer + + + + + Transfer completed + + + + + Trigger direction + + + + + Should trigger when the price is higher than the trigger price + + + + + Should trigger when the price is lower than the trigger price + + + + + Trigger price type + + + + + Last price + + + + + Mark price + + + + + Index price + - Type of update + Type of update + + + + + New + + + + + Update + + + + + Done + + + + + Status of a withdrawal + + + + + Under audit + + + + + Passed audit + + + + + Processing + + + + + Confirming + + + + + Not passed audit + + + + + Canceled + + + + + Finished + + + + + 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 + + + + + Futures API + + + + + Endpoints related to account settings, info or actions + + + + + Endpoints related to retrieving market and system data + + + + + Endpoints related to orders, trades and managing positions + + + + + 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 + + + + + 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. + + + + + Get server time + + + Cancelation token + + + + + 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. + + + + + 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 + + + + + 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 + + + + + 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 + + + + 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. + + + + + Spot V1 API endpoints. Use V2 API if possible, V1 API will be removed at a later date + + + + + 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. + + The credentials to set + + + + Client for accessing the CoinEx websocket API + + + + + V2 API Futures streams + + + + + V2 API Spot streams + + + + + V1 API Spot streams. Use V2 if possible, V1 will be removed at a later date + + + + + Set the API credentials for this client. All Api clients in this client will use the new credentials, regardless of earlier set options. + + 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 + + + Cancellation token + List of balances + + + + 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 + + + + + Get the deposit address of an asset + + + The asset to deposit + Name of the network to use + Cancellation token + + + + + 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 + + + + + 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 + + + + 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 + + + + CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. + + + + + Gets the exchange rates of currencies + + + Cancellation token + + + + + Gets the asset configs + + + Optionally only return a certain type of asset, for example BCH + Cancellation token + + + + + Gets a list of symbols active on CoinEx + + + Cancellation token + List of symbol names + + + + Gets the state of a specific symbol + + + The symbol to retrieve state for + Cancellation token + The state of the symbol + + + + Gets the states of all symbols + + + Cancellation token + List of states for all symbols + + + + 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 + + + + 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 + + + + 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 + + + + Retrieves market data for the exchange + + + The symbol to retrieve data for + Cancellation token + List of market data for the exchange + + + + Retrieves market data for the exchange + + + Cancellation token + List of market data for the exchange + + + + Retrieve the mining difficulty. Requires API credentials + + + 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. + + + + + + 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 + + + + 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 + + + + 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 + + + + 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 + + + + 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 + + + + 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 + + + + 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 + + + + 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 + + + + Cancels all stop orders. Requires API credentials + + The symbol the orders are on + Cancellation token + Execution statut + + + + Cancels all orders. Requires API credentials + + + The symbol the orders are on + 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 + + + The symbol to get the state for + 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 + + + Symbol to receive updates for + 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 ticker updates for all 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 + + + + Get an order book + + + The symbol to get the order book for + The limit of results returned, 5, 10 or 20 + 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 + + + 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 + + + + Gets the latest trades on a symbol + + + The symbol to get the trades for + The limit of trades + Return trades since this id + List of trades + + + + Subscribe to symbol trade updates for a symbol + + + The symbol to receive updates from + 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 + + + + Gets symbol kline data + + + The symbol to get the data for + The interval of the candles + + + + + Get balances of assets. Requires API credentials + + + 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 + + + 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 + + + + Gets a list of open orders for a symbol + + + Symbol to get open orders for + Order side + The offset in the list + The limit of results + List of open orders + + + + Subscribe to updates of active orders. Receives updates whenever an order is placed, updated or finished + + + 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 updates of active orders. Receives updates whenever an order is placed, updated or finished + + + The symbols to receive order updates from + 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 + + + + 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 + + + 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 + + + + + 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. + + + + + Get server time + + + Cancelation token + + + + + Get symbol information + + + Cancelation Token + + + + + Get symbol tickers + + + Fitler by symbol names + 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. + + + + + 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 + + + + + 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 + + + + + Get trades for a specific order + + + Symbol + Account type + The order id + Page number + Page size + Cancelation Token + + + + + 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. + + + 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 + + + + 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 + + + + CoinEx order book factory + + + + + Create a SymbolOrderBook for the Spot API + + The symbol + Order book options + + + + + Create a SymbolOrderBook for the Futures API + + The symbol + Order book options + + + + + Api addresses usable for the CoinEx clients + + + + + The address used by the CoinExRestClient for the rest API + + + + + The address used by the CoinExSocketClient for the socket API + + + + + The default addresses to connect to the CoinEx.com API + + + + + + + + Asset config + + + + + Asset + + + + + Network + + + + + Deposit is enabled + + + + + Withdraw is enabled + + + + + Minimal deposit quantity + + + + + Minimal withdrawal quantity + + + + + Withdraw fee + + + + + Balance info + + + + + The asset + + + + + The quantity of the asset that is available + + + + + The quantity of the asset not currently available + + + + + Data timestamp + + + + + Deposit info + + + + + The actual quantity of the deposit + + + + + The display for the deposit + + + + + Depositor + + + + + The total quantity of the deposit + + + + + The display for the quantity + + + + + Deposit add displayed + + + + + Deposit add displayed + + + + + Deposit ID + + + + + Deposit ID + + + + + Deposit ID + + + + + Deposit ID + + + + + Explorer + + + + + Remarks + + + + + Status + + + + + Status Displayed + + + + + transfer method + + + + + The transaction id of the withdrawal + + + + + The transaction id of the withdrawal + + + + + Deposit address + + + + + The address + + + + + Time the address was created + + + + + Deposit address id + + + + + Is bitcoin cash + + + + + Status + + + + + Kline data + + + + + The open time of this kline + + + + + The price of the symbol when this kline started + + + + + The price of the symbol when this kline ended + + + + + The highest price of the symbol during this kline + + + + + The lowest price of the symbol during this kline + + + + + The volume of the quote asset. i.e. for symbol ETHBTC this is the volume in ETH + + + + + The volume of the base asset. i.e. for symbol ETHBTC this is the volume in BTC + + + + + The symbol for this kline + + + + + Mining difficulty info + + + + + The difficulty in CET/Hour + + + + + Estimated hourly mining yield to distribute + + + + + The update time of the Prediction field + + + + + Order info + + + + + The quantity of the order + + + + + The fee of the order + + + + + The fee of the order in quote + + + + + The asset of the fee + + + + + The fee discount + + + + + Average price of the executed order for market orders + + + + + The time the order was created + + + + + The time the order was finished + + + + + The executed quantity + + + + + The fee of the executed quantity + + + + + The value of the executed quantity + + + + + The id of the order + + + + + The quantity still left to execute + + + + + The maker fee rate + + + + + The symbol of the order + + + + + The type of the order + + + + + The price per unit of the order + + + + + The source id optionally specified by the client + + + + + The client id optionally specified by the client + + + + + The status of the order + + + + + The taker fee rate + + + + + The stop price + + + + + The transaction type of the order + + + + + Order book + + + + + The price of the last transaction + + + + + Timestamp + + + + + The asks on this symbol + + + + + The bids on this symbol + + + + + Depth info + + + + + The price per unit of the entry + + + + + The quantity of the entry + + + + + Order transaction info + + + + + The quantity of the transaction + + + + + The time the transaction was created + + + + + The value of the transaction + + + + + The fee of the transactions + + + + + The asset of the fee + + + + + The id of the transaction + + + + + The id of the order + + + + + The price per unit of the transaction + + + + + The role of the transaction, maker or taker + + + + + Order side + + + + + Order transaction info + + + + + The symbol of the transaction + + + + + Paged result + + Type of data + + + + The total number of results + + + + + The page currently returned + + + + + The results + + + + + Whether there is a next page + + + + + Total items + + + + + Total pages + + + + + Symbol Info + + + + + The name of the symbol + + + + + The minimum quantity that can be traded + + + + + The fee for the maker + + + + + The fee for the taker + + + + + The asset being that is being traded against + + + + + The number of decimals for the price + + + + + The asset being traded + + + + + The number of decimals for the price + + + + + Symbol state info + + + + + The timestamp of the data + + + + + The symbol state data + + + + + Symbol state list + + + + + The timestamp of the data + + + + + The data specified as symbol -> symbol state data + + + + + Symbol state data + + + + + Symbol + + + + + The best buy price available on the symbol + + + + + The quantity of the best buy price + + + + + The best sell price available on the symbol + + + + + The quantity of the best sell price + + + + + The open price based on a 24H ticker + + + + + The high price based on a 24H ticker + + + + + The low price based on a 24H ticker + + + + + The price of the last trade + + + + + The volume of the quote asset. i.e. for symbol ETHBTC this is the volume in ETH + + + + + Symbol trade info + + + + + The quantity of the transaction + + + + + The timestamp of the transaction + + + + + The id of the transaction + + + + + The price per unit of the transaction + + + + + Order side + + + + + Withdrawal info + + + + + The actual quantity of the withdrawal, i.e. the quantity which will be transferred to the destination address + + + + + The total quantity of the withdrawal + + + + + The destination address of the withdrawal + + + + + The name of the asset of the withdrawal + + + + + The id of this withdrawal + + + + + The current number of confirmations + + + + + The time the withdrawal was created + + + + + The status of the withdrawal + + + + + The fee for the withdrawal + + + + + The transaction id of the withdrawal + + + + + Order info + + + + + The total quantity of the oder + + + + + The time the order was created + + + + + The fee of the order + + + + + The fee discount + + + + + The asset the fee is on + + + + + The executed quantity transaction fee + + + + + The executed value in this update + + + + + The executed quantity in this update + + + + + The order id + + + + + Quantity of order left to execute + + + + + Maker fee + + + + + The last update time + + + + + The price per unit of the order + + + + + The order side + + + + + The source of the order + + + + + Taker fee + + + + + The type of the order + + + + + The id of the user that placed the order + + + + + The symbol of the order + + + + + The client id + + + + + Quantity of the last trade filled for this order + + + + + Price of the last trade filled for this order + + + + + Timestamp of the last trade filled for this order + + + + + Id of the last trade filled for this order + + + + + Role of the last trade filled for this order + + + + + Order book + + + + + The price of the last trade. Only filled on a full update. + + + + + The timestamp of the data. Only filled on a full update. + + + + + The asks on the symbol + + + + + The bids on the symbol + + + + + Signed integer (32 bit) of full depth data checksum + + + + + Order update + + + + + The type of update + + + + + The order info + + + + + Paged result + + Type of data + + + + The number of results + + + + + The offset in the list + + + + + The total number of results + + + + + The data + + + + + Symbol state info + + + + + The close price of the period + + + + + The volume of the quote asset. i.e. for symbol ETHBTC this is the volume in BTC + + + + + The highest symbol price in the period + + + + + The last symbol trade in the period + + + + + The lowest symbol price in the period + + + + + The open price of the period + + + + + The period the data is over in seconds + + + + + The volume of the base asset. i.e. for symbol ETHBTC this is the volume in ETH + + + + + Symbol + + + + + Transaction data + + + + + The orde side + + + + + The timestamp of the transaction + + + + + The price per unit of the transaction + + + + + The order id of the transaction + + + + + 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 + + + + + Symbol + + + + + Base asset + + + + + Quote asset + + + + + Base asset amount + + + + + Quote asset amount + + + + + Liquidity percentage in AMM account + + + + + Balance info + + + + + Asset name + + + + + Available amount + + + + + Frozen amount + + + + + Total amount + + + + + Balance update + + + + + Margin symbol + + + + + Asset name + + + + + Available amount + + + + + Frozen amount + + + + + Update time + + + + + Basis rate + + + + + Symbol + + + + + Create time + + + + + Basis rate + + + + + Best book prices update + + + + + Symbol + + + + + Update time + + + + + Current best bid price + + + + + Current best bid quantity + + + + + Current best ask price + + + + + Current best ask quantity + + + + + Borrow record + + + + + Id + + + + + Symbol + + + + + Asset + + + + + Daily interest rate + + + + + Expire time + + + + + Borrow amount + + + + + Amount to repay + + + + + Borrow status + + + + + Is auto renewing + + + + + Borrow limit info + + + + + Symbol + + + + + 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 + + + + + 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 + + + + + Funding rate info + + + + + Symbol + + + + + Mark price + + + + + Last funding rate + + + + + Next funding rate + + + + + Last funding time + + + + + Next funding time - + - New + Historic funding rate info - + - Update + Symbol - + - Done + Funding time - + - Status of a withdrawal + Theoretical funding rate. The theoretical funding rate to be collected for the current period after calculation - + - Under audit + Actual funding rate. The actual funding rate charged in the current period - + - Passed audit + Futures balance info - + - Processing + Asset - + - Confirming + Available balance - + - Not passed audit + Frozen balance - + - Canceled + Position margin - + + + Unrealized profit and loss + + + + + Transferable balance + + + + + Equity + + + + + 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 + + + + + Order update + + + + + Event that triggered the update + + + + + Order data + + + + + 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 + + + + + Futures ticker update + + + + + Open interest size + + + + + Last funding rate + + + + + Next funding rate + + + + + Last funding time + + + + + Next funding time + + + + + Index price + + + + + Symbol + + + + + Timestamp + + + + + Price + + + + + Index sources + + + + + Index price source + + + + + Exchange + + + + + Timestamp + + + + + Weight of the source + + + + + Price + + + + + Index price update + + + + + Symbol + + + + + Index price + + + + + Mark price + + + + + Kline/candlestick info + + + + + Symbol + + + + + Open time + + + + + Open price + + + + + Close price + + + + + High price + + + + + Low price + + + + + Volume + + + + + Value (Quote asset volume) + + + + + Leverage info + + + + + Margin mode + + + - Finished + Leverage - + - Failed + Liquidation record - + - Extension methods specific to using the CoinEx API + Symbol - + - Client for accessing the CoinEx API. + Position side - + - Spot endpoints + Liquidation price - + - Set the API credentials for this client. All Api clients in this client will use the new credentials, regardless of earlier set options. + Liquidation quantity - The credentials to set - + - Client for accessing the CoinEx websocket API + Bankruptcy price - + - Spot streams + Timestamp - + - Set the API credentials for this client. All Api clients in this client will use the new credentials, regardless of earlier set options. + Maintenance info - The credentials to set - + - Spot API + Start time of the maintenance - + - Endpoints related to account settings, info or actions + End time of the maintenance - + - Endpoints related to retrieving market and system data + Scope that's impacted - + - Endpoints related to orders and trades + 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. - + - Get the ISpotClient for this client. This is a common interface which allows for some basic operations without knowing any details of the exchange. + 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. - - + - CoinEx account endpoints. Account endpoints include balance info, withdraw/deposit info and requesting and account settings + Margin balance info - + - Retrieves a list of balances. Requires API credentials - + Margin account - Cancellation token - List of balances - + - Retrieves a list of deposits. Requires API credentials and withdrawal permission on the API key - + Base asset - The asset to get history for - The page in the results to retrieve - The number of results to return per page - Cancellation token - - + - Get the deposit address of an asset - + Quote asset - The asset to deposit - Name of the network to use - Cancellation token - - + - Retrieves a list of withdrawals. Requires API credentials and withdrawal permission on the API key - + Current risk rate - 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 - - + - Withdraw assets from CoinEx to a specific address. Requires API credentials and withdrawal permission on the API key - + Current liquidation price - 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 - + - Cancel a specific withdrawal. Requires API credentials and withdrawal permission on the API key - + Available - The id of the withdrawal to cancel - Cancellation token - True if successful, false otherwise - + - CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. + Frozen - + - Gets the exchange rates of currencies - + Repaid - Cancellation token - - + - Gets the asset configs - + Interest - Optionally only return a certain type of asset, for example BCH - Cancellation token - - + - Gets a list of symbols active on CoinEx - + Assets balance info - Cancellation token - List of symbol names - + - Gets the state of a specific symbol - + Base asset amount - The symbol to retrieve state for - Cancellation token - The state of the symbol - + - Gets the states of all symbols - + Quote asset amount - Cancellation token - List of states for all symbols - + - Gets the order book for a symbol - + Order info - 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 - + - Gets the latest trades for a symbol - + Order id - The symbol to retrieve data for - The id from which on to return trades - Cancellation token - List of trades for a symbol - + - Retrieves kline data for a specific symbol - + 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 - + - Retrieves market data for the exchange - + Account type - The symbol to retrieve data for - Cancellation token - List of market data for the exchange - + - Retrieves market data for the exchange - + Asset the quantity is in - Cancellation token - List of market data for the exchange - + - Retrieve the mining difficulty. Requires API credentials - + Order side - Cancellation token - Mining difficulty - + - CoinEx trading endpoints, placing and mananging orders. + Order type - + - Places an order. This is a single method for multiple place order endpoints. The called endpoint depends on the provided order type. - - - - + Order quantity - 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 - + - Retrieves a list of open orders for a symbol. Requires API credentials - + Order price - 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 - + - Retrieves a list of open stop orders for a symbol. Requires API credentials - + Quantity remaining - 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 - + - Retrieves a list of executed orders for a symbol in the last 2 days. Requires API credentials - + Quantity filled - 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 - + - Retrieves details of an order. Requires API credentials - + Value of the filled part - The id of the order to retrieve - The symbol the order is for - Cancellation token - Details of the order - + - Retrieves execution details of a specific order. Requires API credentials - + Client order id - The id of the order - The page of the resulting list - The number of results per page - Cancellation token - Details of an executed order - + - Gets a list of trades you executed on a specific symbol. Requires API credentials - + Fee in base asset - 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 - + - Cancels an order. Requires API credentials - + Fee in quote asset - The symbol the order is on - The id of the order to cancel - Cancellation token - Details of the canceled order - + - Cancels all stop orders. Requires API credentials + Fee discount - The symbol the orders are on - Cancellation token - Execution statut - + - Cancels all orders. Requires API credentials - + Maker fee rate - The symbol the orders are on - Cancellation token - Execution statut - + - Spot streams + Taker fee rate - + - Pings the server - + Filled amount of the last trade - Success if server responded, error otherwise - + - Gets the server time - + Price of the last trade - The server time - + - Get the symbol ticker - + Timestamp order was created - The symbol to get the state for - 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 - + Timestamp order was last updated - Symbol to receive updates for - 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 ticker updates for all symbols - + Status of the order - 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 - + - Get an order book - + Order book info - The symbol to get the order book for - The limit of results returned, 5, 10 or 20 - 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 - + Symbol - 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 - Data handler - Set to true to get snapshot first, then diff updates - 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 - + Is full order book - The symbol to get the trades for - The limit of trades - Return trades since this id - List of trades - + - Subscribe to symbol trade updates for a symbol - + The book data - The symbol to receive updates from - 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 - + - Gets symbol kline data - + Order book data + + + + + Asks list - The symbol to get the data for - The interval of the candles - - + - Get balances of assets. Requires API credentials - + Bids list - 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 - + Last price - 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 - + - Gets a list of open orders for a symbol - + Update time - Symbol to get open orders for - Order side - The offset in the list - The limit of results - List of open orders - + - Subscribe to updates of active orders. Receives updates whenever an order is placed, updated or finished - + Checksum for validating the order book is correct - 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 updates of active orders. Receives updates whenever an order is placed, updated or finished - + Order book entry - The symbols to receive order updates from - 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 + Price - + - Create a SymbolOrderBook for the Spot API + Quantity - The symbol - Order book options - - + - Api addresses usable for the CoinEx clients + Order update - + - The address used by the CoinExClient for the rest API + Event that triggered the update - + - The address used by the CoinExSocketClient for the socket API + Order data - + - The default addresses to connect to the CoinEx.com API + Paginated result - - + + + Total results + - + - Asset config + Has next page - + - Asset + Page items - + - Network + Position info - + - Deposit is enabled + Position id - + - Withdraw is enabled + Symbol - + - Minimal deposit quantity + Account type - + - Minimal withdrawal quantity + Position side - + - Withdraw fee + Margin mode - + - Balance info + Open interest - + - The asset + position size available for closing - + - The quantity of the asset that is available + All time high position quantity - + - The quantity of the asset not currently available + Unrealized profit and loss - + - Data timestamp + Realized profit and loss - + - Deposit info + Average entry price - + - The actual quantity of the deposit + Cumulative position value - + - The display for the deposit + Max position value - + - Depositor + Take profit price - + - The total quantity of the deposit + Stop loss price - + - The display for the quantity + Take profit price type - + - Deposit add displayed + Stop loss price type - + - Deposit add displayed + Leverage - + - Deposit ID + Margin available - + - Deposit ID + All time high margin size - + - Deposit ID + Position margin rate - + - Deposit ID + Maintenance margin rate - + - Explorer + Maintenance margin value - + - Remarks + Liquidation price - + - Status + Bankruptcy price - + - Status Displayed + Auto deleveraging level - + - transfer method + Settlement price - + - The transaction id of the withdrawal + Settlement value - + - The transaction id of the withdrawal + Timestamp created - + - Deposit address + Timestamp last updated - + - The address + Auto deleveraging info - + - Time the address was created + Position id - + - Deposit address id + Symbol - + - Is bitcoin cash + Account type - + - Status + Order id - + - Kline data + Trade id - + - The open time of this kline + Order side - + - The price of the symbol when this kline started + Order quantity - + - The price of the symbol when this kline ended + Order price - + - The highest price of the symbol during this kline + Role - + - The lowest price of the symbol during this kline + Timestamp created - + - The volume of the quote asset. i.e. for symbol ETHBTC this is the volume in ETH + Position funding rate history - + - The volume of the base asset. i.e. for symbol ETHBTC this is the volume in BTC + Position id - + - The symbol for this kline + Symbol - + - Mining difficulty info + Account type - + - The difficulty in CET/Hour + Position side - + - Estimated hourly mining yield to distribute + Margin mode - + - The update time of the Prediction field + Open interest - + - Order info + Settlement price - + - The quantity of the order + Funding rate - + - The fee of the order + Funding value - + - The fee of the order in quote + Timestamp - + - The asset of the fee + Position levels info - + - The fee discount + Symbol - + - Average price of the executed order for market orders + Levels - + - The time the order was created + Position level info - + - The time the order was finished + Upper limit of the current position - + - The executed quantity + Leverage of current level - + - The fee of the executed quantity + Current maintenance margin rate - + - The value of the executed quantity + Minimum initial margin rate for the current level - + - The id of the order + Position margin info - + - The quantity still left to execute + Position id - + - The maker fee rate + Symbol - + - The symbol of the order + Account type - + - The type of the order + Margin mode - + - The price per unit of the order + Leverage - + - The source id optionally specified by the client + Liquidation price - + - The client id optionally specified by the client + Bankruptcy price - + - The status of the order + Settlement price - + - The taker fee rate + Open interest - + - The stop price + Margin available - + - The transaction type of the order + Adjusted margin amount - + - Order book + Timestamp created - + - The price of the last transaction + Position settlement info - + - Timestamp + Position id - + - The asks on this symbol + Symbol - + - The bids on this symbol + Account type - + - Depth info + Margin mode - + - The price per unit of the entry + Leverage - + - The quantity of the entry + Liquidation price - + - Order transaction info + Bankruptcy price - + - The quantity of the transaction + Settlement price - + - The time the transaction was created + Open interest - + - The value of the transaction + Margin available - + - The fee of the transactions + Adjusted margin amount - + - The asset of the fee + Timestamp created - + - The id of the transaction + Position update - + - The id of the order + Event that triggered the update - + - The price per unit of the transaction + Position data - + - The role of the transaction, maker or taker + Position info - + - Order side + First filled price - + - Order transaction info + Last filled price - + - The symbol of the transaction + Stop order id - + - Paged result + Stop order id - Type of data - + - The total number of results + Stop order info - + - The page currently returned + Order id - + - The results + Symbol - + - Whether there is a next page + Account type - + - Total items + Asset the quantity is in - + - Total pages + Order side - + - Symbol Info + Order type - + - The name of the symbol + Order quantity - + - The minimum quantity that can be traded + Order price - + - The fee for the maker + Client order id - + - The fee for the taker + Timestamp order was created - + - The asset being that is being traded against + Timestamp order was last updated - + - The number of decimals for the price + Trigger price - + - The asset being traded + Trigger direction - + - The number of decimals for the price + Trigger price type - + - Symbol state info + Taker fee rate - + - The timestamp of the data + Maker fee rate - + - The symbol state data + Stop order update - + - Symbol state list + Event that triggered the update - + - The timestamp of the data + Order data - + - The data specified as symbol -> symbol state data + Order info - + - Symbol state data + Order id - + Symbol - + - The best buy price available on the symbol + Order side - + - The quantity of the best buy price + Order type - + - The best sell price available on the symbol + Order quantity - + - The quantity of the best sell price + Order price - + - The open price based on a 24H ticker + Quantity remaining - + - The high price based on a 24H ticker + Quantity filled - + - The low price based on a 24H ticker + Value of the filled part - + - The price of the last trade + Client order id - + - The volume of the quote asset. i.e. for symbol ETHBTC this is the volume in ETH + Fee in base asset - + - Symbol trade info + Fee in quote asset - + - The quantity of the transaction + Fee discount - + - The timestamp of the transaction + Maker fee rate - + - The id of the transaction + Taker fee rate - + - The price per unit of the transaction + Filled amount of the last trade - + - Order side + Price of the last trade - + - Withdrawal info + Timestamp order was created - + - The actual quantity of the withdrawal, i.e. the quantity which will be transferred to the destination address + Timestamp order was last updated - + - The total quantity of the withdrawal + Symbol info - + - The destination address of the withdrawal + Symbol name - + - The name of the asset of the withdrawal + Maker fee rate - + - The id of this withdrawal + Taker fee rate - + - The current number of confirmations + Minimal order quantiy - + - The time the withdrawal was created + Base asset - + - The status of the withdrawal + Quote asset - + - The fee for the withdrawal + Quantity precision - + - The transaction id of the withdrawal + Price precision - + - Order info + Is Automated Market Maker available - + - The total quantity of the oder + Is Margin Trading available - + - The time the order was created + Ticker (24h price stats) info - + - The fee of the order + Symbol name - + - The fee discount + Last price - + - The asset the fee is on + Open price - + - The executed quantity transaction fee + Close price - + - The executed value in this update + High price - + - The executed quantity in this update + Low price - + - The order id + Volume in base asset - + - Quantity of order left to execute + Volume in quote asset - + - Maker fee + Sell volume - + - The last update time + Buy volume - + - The price per unit of the order + Trade info - + - The order side + Trade id - + - The source of the order + Timestamp - + - Taker fee + Trade side - + - The type of the order + Price traded at - + - The id of the user that placed the order + Quantity traded - + - The symbol of the order + Trading fee info - + - The client id + Symbol - + - Quantity of the last trade filled for this order + Fee for maker trades - + - Price of the last trade filled for this order + Fee for taker trades - + - Timestamp of the last trade filled for this order + Transfer info - + - Id of the last trade filled for this order + Margin symbol if either from account or to account was Margin - + - Role of the last trade filled for this order + Creation time - + - Order book + From account type - + - The price of the last trade. Only filled on a full update. + To account type - + - The timestamp of the data. Only filled on a full update. + Asset - + - The asks on the symbol + Transfer quantity - + - The bids on the symbol + Transfer status - + - Signed integer (32 bit) of full depth data checksum + User trade info - + - Order update + Trade id - + - The type of update + Trade time - + - The order info + Symbol - + - Paged result + Trade side - Type of data - + - The number of results + Order id - + - The offset in the list + Margin symbol - + - The total number of results + Trade price - + - The data + Quantity traded - + - Symbol state info + Withdrawal info - + - The close price of the period + Withdrawal id - + - The volume of the quote asset. i.e. for symbol ETHBTC this is the volume in BTC + Creation time - + - The highest symbol price in the period + Asset - + - The last symbol trade in the period + Network - + - The lowest symbol price in the period + Memo - + - The open price of the period + Quantity - + - The period the data is over in seconds + Actual withdrawal quantity - + - The volume of the base asset. i.e. for symbol ETHBTC this is the volume in ETH + Fee - + - Symbol + Transaction id - + - Transaction data + Destination address - + - The orde side + Number of confirmations - + - The timestamp of the transaction + Blockchain explorer url for the transaction - + - The price per unit of the transaction + Blockchain explorer url for the deposit address - + - The order id of the transaction + Status - + - The quantity of the transaction + Remark @@ -2153,6 +6808,11 @@ Options for the Spot API + + + Options for the Futures API + + The broker reference id to use @@ -2178,6 +6838,50 @@ Options for the Spot API + + + 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 + + @@ -2190,6 +6894,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/CoinExV2AuthenticationProvider.cs b/CoinEx.Net/CoinExV2AuthenticationProvider.cs new file mode 100644 index 0000000..8b6e4ec --- /dev/null +++ b/CoinEx.Net/CoinExV2AuthenticationProvider.cs @@ -0,0 +1,55 @@ +using CryptoExchange.Net; +using CryptoExchange.Net.Authentication; +using System; +using System.Collections.Generic; +using System.Net.Http; +using CryptoExchange.Net.Objects; +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/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 new file mode 100644 index 0000000..68e0549 --- /dev/null +++ b/CoinEx.Net/Enums/AccountType.cs @@ -0,0 +1,26 @@ +using CryptoExchange.Net.Attributes; + +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/BorrowStatus.cs b/CoinEx.Net/Enums/BorrowStatus.cs new file mode 100644 index 0000000..5a958c3 --- /dev/null +++ b/CoinEx.Net/Enums/BorrowStatus.cs @@ -0,0 +1,31 @@ +using CryptoExchange.Net.Attributes; + +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/ContractType.cs b/CoinEx.Net/Enums/ContractType.cs new file mode 100644 index 0000000..ce7195d --- /dev/null +++ b/CoinEx.Net/Enums/ContractType.cs @@ -0,0 +1,21 @@ +using CryptoExchange.Net.Attributes; + +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/DepositStatus.cs b/CoinEx.Net/Enums/DepositStatus.cs new file mode 100644 index 0000000..86b44f4 --- /dev/null +++ b/CoinEx.Net/Enums/DepositStatus.cs @@ -0,0 +1,41 @@ +using CryptoExchange.Net.Attributes; + +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/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/MarginMode.cs b/CoinEx.Net/Enums/MarginMode.cs new file mode 100644 index 0000000..a2e5f6d --- /dev/null +++ b/CoinEx.Net/Enums/MarginMode.cs @@ -0,0 +1,17 @@ +namespace CoinEx.Net.Enums +{ + /// + /// Margin mode + /// + public enum MarginMode + { + /// + /// Isolated margin mode + /// + Isolated, + /// + /// Cross margin mode + /// + Cross + } +} diff --git a/CoinEx.Net/Enums/MovementMethod.cs b/CoinEx.Net/Enums/MovementMethod.cs new file mode 100644 index 0000000..f88e583 --- /dev/null +++ b/CoinEx.Net/Enums/MovementMethod.cs @@ -0,0 +1,21 @@ +using CryptoExchange.Net.Attributes; + +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/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..93066e1 --- /dev/null +++ b/CoinEx.Net/Enums/OrderStatusV2.cs @@ -0,0 +1,36 @@ +using CryptoExchange.Net.Attributes; + +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..1ea516b --- /dev/null +++ b/CoinEx.Net/Enums/OrderTypeV2.cs @@ -0,0 +1,36 @@ +using CryptoExchange.Net.Attributes; + +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/Enums/OrderUpdateType.cs b/CoinEx.Net/Enums/OrderUpdateType.cs new file mode 100644 index 0000000..1c4c3dc --- /dev/null +++ b/CoinEx.Net/Enums/OrderUpdateType.cs @@ -0,0 +1,26 @@ +using CryptoExchange.Net.Attributes; + +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/PositionSide.cs b/CoinEx.Net/Enums/PositionSide.cs new file mode 100644 index 0000000..e13217c --- /dev/null +++ b/CoinEx.Net/Enums/PositionSide.cs @@ -0,0 +1,21 @@ +using CryptoExchange.Net.Attributes; + +namespace CoinEx.Net.Enums +{ + /// + /// Position side + /// + public enum PositionSide + { + /// + /// Long position + /// + [Map("long")] + Long, + /// + /// Short position + /// + [Map("short")] + Short + } +} diff --git a/CoinEx.Net/Enums/PositionUpdateType.cs b/CoinEx.Net/Enums/PositionUpdateType.cs new file mode 100644 index 0000000..b0feed9 --- /dev/null +++ b/CoinEx.Net/Enums/PositionUpdateType.cs @@ -0,0 +1,41 @@ +using CryptoExchange.Net.Attributes; + +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/Enums/PriceType.cs b/CoinEx.Net/Enums/PriceType.cs new file mode 100644 index 0000000..bb9c56a --- /dev/null +++ b/CoinEx.Net/Enums/PriceType.cs @@ -0,0 +1,26 @@ +using CryptoExchange.Net.Attributes; + +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/Enums/StopOrderUpdateType.cs b/CoinEx.Net/Enums/StopOrderUpdateType.cs new file mode 100644 index 0000000..22d1a15 --- /dev/null +++ b/CoinEx.Net/Enums/StopOrderUpdateType.cs @@ -0,0 +1,26 @@ +using CryptoExchange.Net.Attributes; + +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/TransferStatus.cs b/CoinEx.Net/Enums/TransferStatus.cs new file mode 100644 index 0000000..2ff4b1f --- /dev/null +++ b/CoinEx.Net/Enums/TransferStatus.cs @@ -0,0 +1,31 @@ +using CryptoExchange.Net.Attributes; + +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/TriggerDirection.cs b/CoinEx.Net/Enums/TriggerDirection.cs new file mode 100644 index 0000000..fce1afc --- /dev/null +++ b/CoinEx.Net/Enums/TriggerDirection.cs @@ -0,0 +1,21 @@ +using CryptoExchange.Net.Attributes; + +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/Enums/TriggerPriceType.cs b/CoinEx.Net/Enums/TriggerPriceType.cs new file mode 100644 index 0000000..69b55c2 --- /dev/null +++ b/CoinEx.Net/Enums/TriggerPriceType.cs @@ -0,0 +1,26 @@ +using CryptoExchange.Net.Attributes; + +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/Enums/WithdrawStatusV2.cs b/CoinEx.Net/Enums/WithdrawStatusV2.cs new file mode 100644 index 0000000..1c6cd19 --- /dev/null +++ b/CoinEx.Net/Enums/WithdrawStatusV2.cs @@ -0,0 +1,56 @@ +using CryptoExchange.Net.Attributes; + +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/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/ExtensionMethods/ServiceCollectionExtensions.cs b/CoinEx.Net/ExtensionMethods/ServiceCollectionExtensions.cs index 7c4dbaa..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().SpotApi.CommonSpotClient); + services.AddTransient(x => x.GetRequiredService().SpotApiV2.CommonSpotClient); if (socketClientLifeTime == null) services.AddSingleton(); else diff --git a/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApi.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApi.cs new file mode 100644 index 0000000..8417bf6 --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApi.cs @@ -0,0 +1,26 @@ +using CryptoExchange.Net.Interfaces; +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, trades and managing positions + /// + 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..be6cd4c --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiAccount.cs @@ -0,0 +1,43 @@ +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; + +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 + { + /// + /// 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); + + /// + /// 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/ICoinExRestClientFuturesApiExchangeData.cs b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiExchangeData.cs new file mode 100644 index 0000000..fca4b64 --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiExchangeData.cs @@ -0,0 +1,140 @@ +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 server time + /// + /// + /// Cancelation token + /// + Task> GetServerTimeAsync(CancellationToken ct = default); + + /// + /// 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..fde1b79 --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExRestClientFuturesApiTrading.cs @@ -0,0 +1,367 @@ +using CoinEx.Net.Enums; +using CryptoExchange.Net.Objects; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Objects.Models.V2; +using System; + +namespace CoinEx.Net.Interfaces.Clients.FuturesApi +{ + /// + /// CoinEx trading endpoints, placing and mananging orders. + /// + 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); + + /// + /// 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 new file mode 100644 index 0000000..f61a27d --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/FuturesApi/ICoinExSocketClientFuturesApi.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +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 + { + /// + /// 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); + + /// + /// 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/ICoinExRestClient.cs b/CoinEx.Net/Interfaces/Clients/ICoinExRestClient.cs index ea37a06..6fca34f 100644 --- a/CoinEx.Net/Interfaces/Clients/ICoinExRestClient.cs +++ b/CoinEx.Net/Interfaces/Clients/ICoinExRestClient.cs @@ -1,4 +1,4 @@ -using CoinEx.Net.Interfaces.Clients.SpotApi; +using CoinEx.Net.Interfaces.Clients.FuturesApi; using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Interfaces; @@ -10,9 +10,17 @@ namespace CoinEx.Net.Interfaces.Clients public interface ICoinExRestClient : IRestClient { /// - /// Spot endpoints + /// Spot V1 API endpoints. Use V2 API if possible, V1 API will be removed at a later date /// - ICoinExClientSpotApi SpotApi { get; } + SpotApiV1.ICoinExRestClientSpotApi SpotApi { get; } + /// + /// 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 a780e14..0f3db1d 100644 --- a/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs +++ b/CoinEx.Net/Interfaces/Clients/ICoinExSocketClient.cs @@ -1,4 +1,4 @@ -using CoinEx.Net.Interfaces.Clients.SpotApi; +using CoinEx.Net.Interfaces.Clients.FuturesApi; using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Interfaces; @@ -10,9 +10,17 @@ namespace CoinEx.Net.Interfaces.Clients public interface ICoinExSocketClient : ISocketClient { /// - /// Spot streams + /// V2 API Futures streams /// - public ICoinExSocketClientSpotApi SpotApi { get; } + public ICoinExSocketClientFuturesApi FuturesApi { get; } + /// + /// V2 API Spot streams + /// + 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/SpotApi/ICoinExClientSpotApiAccount.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApiAccount.cs similarity index 97% rename from CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiAccount.cs rename to CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApiAccount.cs index fd2cade..eb13f78 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiAccount.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 ICoinExClientSpotApiAccount + public interface ICoinExRestClientSpotApiAccount { /// /// Retrieves a list of balances. Requires API credentials diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiExchangeData.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApiExchangeData.cs similarity index 98% rename from CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiExchangeData.cs rename to CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApiExchangeData.cs index e8e25cf..681f521 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiExchangeData.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 ICoinExClientSpotApiExchangeData + public interface ICoinExRestClientSpotApiExchangeData { /// /// Gets the exchange rates of currencies diff --git a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApiTrading.cs similarity index 98% rename from CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.cs rename to CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExRestClientSpotApiTrading.cs index 539a858..67f6094 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExClientSpotApiTrading.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 ICoinExClientSpotApiTrading + 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/SpotApi/ICoinExSocketClientSpotApi.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExSocketClientSpotApi.cs similarity index 99% rename from CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs rename to CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExSocketClientSpotApi.cs index 60db0a3..fafb0ec 100644 --- a/CoinEx.Net/Interfaces/Clients/SpotApi/ICoinExSocketClientSpotApi.cs +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV1/ICoinExSocketClientSpotApi.cs @@ -9,7 +9,7 @@ using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects.Sockets; -namespace CoinEx.Net.Interfaces.Clients.SpotApi +namespace CoinEx.Net.Interfaces.Clients.SpotApiV1 { /// /// Spot streams @@ -75,8 +75,8 @@ 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 - /// Data handler /// 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, bool diffUpdates = false, CancellationToken ct = default); diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApi.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApi.cs new file mode 100644 index 0000000..16d651b --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApi.cs @@ -0,0 +1,33 @@ +using CryptoExchange.Net.Interfaces; +using CryptoExchange.Net.Interfaces.CommonClients; +using System; + +namespace CoinEx.Net.Interfaces.Clients.SpotApiV2 +{ + /// + /// Spot API + /// + public interface ICoinExRestClientSpotApi : IRestApiClient, IDisposable + { + /// + /// Endpoints related to account settings, info or actions + /// + ICoinExRestClientSpotApiAccount Account { get; } + + /// + /// Endpoints related to retrieving market and system data + /// + ICoinExRestClientSpotApiExchangeData ExchangeData { get; } + + /// + /// Endpoints related to orders and trades + /// + 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. + /// + /// + public ISpotClient CommonSpotClient { get; } + } +} \ No newline at end of file diff --git a/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiAccount.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiAccount.cs new file mode 100644 index 0000000..175be40 --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiAccount.cs @@ -0,0 +1,249 @@ +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; + +namespace CoinEx.Net.Interfaces.Clients.SpotApiV2 +{ + /// + /// CoinEx account endpoints. Account endpoints include balance info, withdraw/deposit info and requesting and account settings + /// + public interface ICoinExRestClientSpotApiAccount + { + /// + /// 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); + + /// + /// 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/ICoinExRestClientSpotApiExchangeData.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiExchangeData.cs new file mode 100644 index 0000000..22f6b39 --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiExchangeData.cs @@ -0,0 +1,93 @@ +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.SpotApiV2 +{ + /// + /// CoinEx exchange data endpoints. Exchange data includes market data (tickers, order books, etc) and system status. + /// + 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 + /// + /// + /// Cancelation Token + /// + Task>> GetSymbolsAsync(CancellationToken ct = default); + + /// + /// Get symbol tickers + /// + /// + /// Fitler by symbol names + /// 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/SpotApiV2/ICoinExRestClientSpotApiTrading.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs new file mode 100644 index 0000000..2997424 --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExRestClientSpotApiTrading.cs @@ -0,0 +1,260 @@ +using CoinEx.Net.Enums; +using CryptoExchange.Net.Objects; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Objects.Models.V2; +using System; + +namespace CoinEx.Net.Interfaces.Clients.SpotApiV2 +{ + /// + /// CoinEx trading endpoints, placing and mananging orders. + /// + public interface ICoinExRestClientSpotApiTrading + { + /// + /// 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, long orderId, CancellationToken ct = default); + + /// + /// 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 + /// + 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 + /// Page number + /// Page size + /// Cancelation Token + /// + 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, + long 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, + long 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, long 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, long 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); + + /// + /// 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/Interfaces/Clients/SpotApiV2/ICoinExSocketClientSpotApi.cs b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExSocketClientSpotApi.cs new file mode 100644 index 0000000..e28d65c --- /dev/null +++ b/CoinEx.Net/Interfaces/Clients/SpotApiV2/ICoinExSocketClientSpotApi.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using CoinEx.Net.Objects.Models.V2; +using CryptoExchange.Net.Interfaces; +using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Objects.Sockets; + +namespace CoinEx.Net.Interfaces.Clients.SpotApiV2 +{ + /// + /// Spot streams + /// + 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. + /// + /// + /// 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); + + /// + /// 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/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/Internal/CoinExApiResult.cs b/CoinEx.Net/Objects/Internal/CoinExApiResult.cs index 26f91da..0b65b1f 100644 --- a/CoinEx.Net/Objects/Internal/CoinExApiResult.cs +++ b/CoinEx.Net/Objects/Internal/CoinExApiResult.cs @@ -1,13 +1,34 @@ -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!; } + + internal class CoinExPageApiResult : CoinExApiResult + { + [JsonPropertyName("pagination")] + public CoinExPage Pagination { get; set; } = null!; + [JsonPropertyName("paginatation")] + public CoinExPage PaginationTypo { set => Pagination = value; } + } + + 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/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 new file mode 100644 index 0000000..79d0425 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExAamLiquidity.cs @@ -0,0 +1,31 @@ +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/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..8ef11f9 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExBalance.cs @@ -0,0 +1,30 @@ +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..e0d5d3c --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExBalanceUpdate.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +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/CoinExBasis.cs b/CoinEx.Net/Objects/Models/V2/CoinExBasis.cs new file mode 100644 index 0000000..c47c52b --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExBasis.cs @@ -0,0 +1,27 @@ +using System; +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/CoinExBookPriceUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExBookPriceUpdate.cs new file mode 100644 index 0000000..c8b97c8 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExBookPriceUpdate.cs @@ -0,0 +1,43 @@ +using System; +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/CoinExBorrow.cs b/CoinEx.Net/Objects/Models/V2/CoinExBorrow.cs new file mode 100644 index 0000000..82035b4 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExBorrow.cs @@ -0,0 +1,58 @@ +using System; +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..0f0f1b9 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExBorrowLimit.cs @@ -0,0 +1,43 @@ +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..9dd53bc --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExCreditBalance.cs @@ -0,0 +1,36 @@ +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..79bdb9a --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExDeposit.cs @@ -0,0 +1,83 @@ +using CoinEx.Net.Enums; +using System; +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 MovementMethod DepositMethod { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExDepositAddress.cs b/CoinEx.Net/Objects/Models/V2/CoinExDepositAddress.cs new file mode 100644 index 0000000..2945784 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExDepositAddress.cs @@ -0,0 +1,21 @@ +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/CoinExDepositWithdrawalConfig.cs b/CoinEx.Net/Objects/Models/V2/CoinExDepositWithdrawalConfig.cs new file mode 100644 index 0000000..4ce8252 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExDepositWithdrawalConfig.cs @@ -0,0 +1,126 @@ +using System.Collections.Generic; +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/CoinExFundingRate.cs b/CoinEx.Net/Objects/Models/V2/CoinExFundingRate.cs new file mode 100644 index 0000000..b29d3a1 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFundingRate.cs @@ -0,0 +1,42 @@ +using System; +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..93d1dc5 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFundingRateHistory.cs @@ -0,0 +1,32 @@ +using System; +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/CoinExFuturesBalance.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalance.cs new file mode 100644 index 0000000..2bd0e30 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalance.cs @@ -0,0 +1,46 @@ +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; } + /// + /// 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..1789eff --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesBalanceUpdate.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +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 new file mode 100644 index 0000000..c549ee3 --- /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/CoinExFuturesOrderUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesOrderUpdate.cs new file mode 100644 index 0000000..1b61524 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesOrderUpdate.cs @@ -0,0 +1,22 @@ +using CoinEx.Net.Enums; +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/CoinExFuturesSymbol.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesSymbol.cs new file mode 100644 index 0000000..ace2c7a --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesSymbol.cs @@ -0,0 +1,68 @@ +using System; +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..fc44a64 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesTicker.cs @@ -0,0 +1,19 @@ +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/CoinExFuturesTickerUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExFuturesTickerUpdate.cs new file mode 100644 index 0000000..de4ce8e --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExFuturesTickerUpdate.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +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/CoinExIndexPrice.cs b/CoinEx.Net/Objects/Models/V2/CoinExIndexPrice.cs new file mode 100644 index 0000000..323ec4d --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExIndexPrice.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +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/CoinExIndexPriceUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExIndexPriceUpdate.cs new file mode 100644 index 0000000..b1d2725 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExIndexPriceUpdate.cs @@ -0,0 +1,27 @@ + +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 IndexPrice { get; set; } + /// + /// Mark price + /// + [JsonPropertyName("mark_price")] + public decimal? MarkPrice { 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..d963a25 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExKline.cs @@ -0,0 +1,52 @@ +using System; +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/CoinExLeverage.cs b/CoinEx.Net/Objects/Models/V2/CoinExLeverage.cs new file mode 100644 index 0000000..7a8bdde --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExLeverage.cs @@ -0,0 +1,22 @@ +using CoinEx.Net.Enums; +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/CoinExLiquidation.cs b/CoinEx.Net/Objects/Models/V2/CoinExLiquidation.cs new file mode 100644 index 0000000..6c1f80e --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExLiquidation.cs @@ -0,0 +1,43 @@ +using CoinEx.Net.Enums; +using System; +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/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/CoinExMarginBalance.cs b/CoinEx.Net/Objects/Models/V2/CoinExMarginBalance.cs new file mode 100644 index 0000000..ddb6e42 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExMarginBalance.cs @@ -0,0 +1,75 @@ +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/CoinExOrder.cs b/CoinEx.Net/Objects/Models/V2/CoinExOrder.cs new file mode 100644 index 0000000..d352cab --- /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 long Id { get; set; } + /// + /// 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 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_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/CoinExOrderBook.cs b/CoinEx.Net/Objects/Models/V2/CoinExOrderBook.cs new file mode 100644 index 0000000..d08aa3e --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExOrderBook.cs @@ -0,0 +1,83 @@ +using CryptoExchange.Net.Converters; +using CryptoExchange.Net.Interfaces; +using System; +using System.Collections.Generic; +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/CoinExOrderUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExOrderUpdate.cs new file mode 100644 index 0000000..3cb15cb --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExOrderUpdate.cs @@ -0,0 +1,22 @@ +using CoinEx.Net.Enums; +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 CoinExStreamOrder Order { get; set; } = null!; + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExPaginated.cs b/CoinEx.Net/Objects/Models/V2/CoinExPaginated.cs new file mode 100644 index 0000000..24ff6a4 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExPaginated.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; + +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/CoinExPosition.cs b/CoinEx.Net/Objects/Models/V2/CoinExPosition.cs new file mode 100644 index 0000000..dcbcdb8 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExPosition.cs @@ -0,0 +1,163 @@ +using CoinEx.Net.Enums; +using System; +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..ddff8a0 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionAdl.cs @@ -0,0 +1,64 @@ +using System; +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..6db9c42 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionFundingRate.cs @@ -0,0 +1,63 @@ +using System; +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/CoinExPositionLevel.cs b/CoinEx.Net/Objects/Models/V2/CoinExPositionLevel.cs new file mode 100644 index 0000000..e5e9768 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionLevel.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +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/Models/V2/CoinExPositionMargin.cs b/CoinEx.Net/Objects/Models/V2/CoinExPositionMargin.cs new file mode 100644 index 0000000..7cd9289 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionMargin.cs @@ -0,0 +1,73 @@ +using System; +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..53f99ef --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionSettlement.cs @@ -0,0 +1,73 @@ +using System; +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..cb149ec --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExPositionUpdate.cs @@ -0,0 +1,39 @@ +using CoinEx.Net.Enums; +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/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; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExStopId.cs b/CoinEx.Net/Objects/Models/V2/CoinExStopId.cs new file mode 100644 index 0000000..8a86ccf --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExStopId.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Stop order id + /// + public record CoinExStopId + { + /// + /// Stop order id + /// + [JsonPropertyName("stop_id")] + public long StopOrderId { get; set; } + } +} diff --git a/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs b/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs new file mode 100644 index 0000000..9a6b945 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExStopOrder.cs @@ -0,0 +1,94 @@ +using CoinEx.Net.Enums; +using System; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + /// + /// Stop order info + /// + public record CoinExStopOrder + { + /// + /// Order id + /// + [JsonPropertyName("stop_id")] + public long StopOrderId { get; set; } + /// + /// 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; } + /// + /// 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; } + /// + /// 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 new file mode 100644 index 0000000..250e5d8 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExStopOrderUpdate.cs @@ -0,0 +1,22 @@ +using CoinEx.Net.Enums; +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; } + /// + /// Order data + /// + [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/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/Objects/Models/V2/CoinExTickerUpdate.cs b/CoinEx.Net/Objects/Models/V2/CoinExTickerUpdate.cs new file mode 100644 index 0000000..11630c3 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExTickerUpdate.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + 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 new file mode 100644 index 0000000..b005eec --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExTrade.cs @@ -0,0 +1,47 @@ +using CoinEx.Net.Enums; +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Models.V2 +{ + internal record CoinExTradeWrapper + { + [JsonPropertyName("market")] + public string Symbol { get; set; } = string.Empty; + [JsonPropertyName("deal_list")] + public IEnumerable Trades { get; set; } = Array.Empty(); + } + + /// + /// 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/Models/V2/CoinExTradeFee.cs b/CoinEx.Net/Objects/Models/V2/CoinExTradeFee.cs new file mode 100644 index 0000000..93478c3 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExTradeFee.cs @@ -0,0 +1,26 @@ +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/CoinExTransfer.cs b/CoinEx.Net/Objects/Models/V2/CoinExTransfer.cs new file mode 100644 index 0000000..25d73b9 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExTransfer.cs @@ -0,0 +1,48 @@ +using CoinEx.Net.Enums; +using System; +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/CoinExUserTrade.cs b/CoinEx.Net/Objects/Models/V2/CoinExUserTrade.cs new file mode 100644 index 0000000..881000d --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExUserTrade.cs @@ -0,0 +1,53 @@ +using CoinEx.Net.Enums; +using System; +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/Models/V2/CoinExWithdrawal.cs b/CoinEx.Net/Objects/Models/V2/CoinExWithdrawal.cs new file mode 100644 index 0000000..8b123b6 --- /dev/null +++ b/CoinEx.Net/Objects/Models/V2/CoinExWithdrawal.cs @@ -0,0 +1,89 @@ +using CoinEx.Net.Enums; +using System; +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; + } +} 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 5ddc197..7046132 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 }; /// @@ -26,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; } } 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..000c097 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 { @@ -35,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); 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..0cc85f8 --- /dev/null +++ b/CoinEx.Net/Objects/Sockets/V2/CoinExSocketResponse.cs @@ -0,0 +1,14 @@ +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/CoinExSocketUpdate.cs b/CoinEx.Net/Objects/Sockets/V2/CoinExSocketUpdate.cs new file mode 100644 index 0000000..34ed003 --- /dev/null +++ b/CoinEx.Net/Objects/Sockets/V2/CoinExSocketUpdate.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace CoinEx.Net.Objects.Sockets.V2 +{ + internal class CoinExSocketUpdate + { + [JsonPropertyName("method")] + public string Method { get; set; } = string.Empty; + [JsonPropertyName("data")] + public T Data { get; set; } = default!; + } +} 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..dbe8c6f --- /dev/null +++ b/CoinEx.Net/Objects/Sockets/V2/Queries/CoinExQuery.cs @@ -0,0 +1,31 @@ +using CryptoExchange.Net; +using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Sockets; +using System.Collections.Generic; + +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/CoinExFuturesTickerSubscription.cs b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExFuturesTickerSubscription.cs new file mode 100644 index 0000000..b26cd7d --- /dev/null +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExFuturesTickerSubscription.cs @@ -0,0 +1,48 @@ +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; + +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/CoinExSubscription.cs b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs new file mode 100644 index 0000000..3fb3bae --- /dev/null +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExSubscription.cs @@ -0,0 +1,50 @@ +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; + +namespace CoinEx.Net.Objects.Sockets.V2.Subscriptions +{ + internal class CoinExSubscription : Subscription + { + private string _topic; + private IEnumerable? _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, bool firstUpdateIsSnapshot = false) : base(logger, authenticated) + { + _topic = topic; + _symbols = symbols; + _parameters = parameters; + _handler = handler; + _firstUpdateIsSnapshot = firstUpdateIsSnapshot; + if (symbols?.Any() != true) + 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 = (CoinExSocketUpdate)message.Data; + _handler.Invoke(message.As(data.Data, null, _firstUpdateIsSnapshot && ConnectionInvocations == 1 ? SocketUpdateType.Snapshot : SocketUpdateType.Update)); + return new CallResult(null); + } + + public override Type? GetMessageType(IMessageAccessor message) => typeof(CoinExSocketUpdate); + + public override Query? GetSubQuery(SocketConnection connection) + => new CoinExQuery(_topic + ".subscribe", _parameters, false, 1); + + public override Query? GetUnsubQuery() + => 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 new file mode 100644 index 0000000..d374bbd --- /dev/null +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTickerSubscription.cs @@ -0,0 +1,48 @@ +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; + +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); + } +} 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..c7cd200 --- /dev/null +++ b/CoinEx.Net/Objects/Sockets/V2/Subscriptions/CoinExTradesSubscription.cs @@ -0,0 +1,48 @@ +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; + +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, ConnectionInvocations == 1 ? SocketUpdateType.Snapshot : 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); + } +} 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 a7eeed8..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,7 +61,7 @@ public CoinExSpotSymbolOrderBook(string symbol, /// protected override async Task> DoStartAsync(CancellationToken ct) { - var result = await _socketClient.SpotApi.SubscribeToOrderBookUpdatesAsync(Symbol, Levels!.Value, 0, HandleUpdate, diffUpdates: true).ConfigureAwait(false); + var result = await _socketClient.SpotApiV2.SubscribeToOrderBookUpdatesAsync(Symbol, Levels!.Value, null, true, HandleUpdate).ConfigureAwait(false); if (!result) return result; @@ -74,10 +73,6 @@ protected override async Task> DoStartAsync(Cance Status = OrderBookStatus.Syncing; - // Query the initial order book - var initialBook = await _socketClient.SpotApi.GetOrderBookAsync(Symbol, Levels!.Value, 0).ConfigureAwait(false); - SetInitialOrderBook(DateTime.UtcNow.Ticks, initialBook.Data.Bids, initialBook.Data.Asks); - var setResult = await WaitForSetOrderBookAsync(_initialDataTimeout, ct).ConfigureAwait(false); return setResult ? result : new CallResult(setResult.Error!); } @@ -85,10 +80,6 @@ protected override async Task> DoStartAsync(Cance /// protected override async Task> DoResyncAsync(CancellationToken ct) { - // Query the initial order book - var initialBook = await _socketClient.SpotApi.GetOrderBookAsync(Symbol, Levels!.Value, 0).ConfigureAwait(false); - SetInitialOrderBook(DateTime.UtcNow.Ticks, initialBook.Data.Bids, initialBook.Data.Asks); - return await WaitForSetOrderBookAsync(_initialDataTimeout, ct).ConfigureAwait(false); } @@ -97,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); } /// 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