diff --git a/Kraken.Net.UnitTests/ConvertersTests/StreamOrderbookConvertersTest.cs b/Kraken.Net.UnitTests/ConvertersTests/StreamOrderbookConvertersTest.cs deleted file mode 100644 index 2217caf..0000000 --- a/Kraken.Net.UnitTests/ConvertersTests/StreamOrderbookConvertersTest.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Linq; -using Kraken.Net.Converters; -using Newtonsoft.Json.Linq; -using NUnit.Framework; -using NUnit.Framework.Legacy; - -namespace Kraken.Net.UnitTests.ConvertersTests.StreamOrderbookConvertersTests -{ - [TestFixture] - public class StreamOrderbookConvertersTests - { - private JArray _fiveElements; - - [OneTimeSetUp] - public void SetupData() - { - var fiveElementsString = @" - [ - 1234, - { - ""a"": [ - [ - ""5541.30000"", - ""2.50700000"", - ""1534614248.456738"" - ], - [ - ""5542.50000"", - ""0.40100000"", - ""1534614248.456738"" - ] - ] - }, - { - ""b"": [ - [ - ""5541.30000"", - ""0.00000000"", - ""1534614335.345903"" - ] - ], - ""c"": ""974942666"" - }, - ""book-10"", - ""XBT/USD"" - ] - "; - - this._fiveElements = JArray.Parse(fiveElementsString); - } - - [Test] - public void Event_Should_ParseCountOfFour() - { - var testObj = StreamOrderBookConverter.Convert(this._fiveElements!); - - Assert.That(2 == testObj!.Data.Asks.Count()); - Assert.That(1 == testObj.Data.Bids.Count()); - Assert.That(1234 == testObj.ChannelId); - Assert.That("book-10" == testObj.ChannelName); - Assert.That("XBT/USD" == testObj.Symbol); - - Assert.That("0.40100000" == testObj.Data.Asks.ElementAt(1).RawQuantity); - Assert.That("5542.50000" == testObj.Data.Asks.ElementAt(1).RawPrice); - } - } -} \ No newline at end of file diff --git a/Kraken.Net.UnitTests/Endpoints/Futures/Account/GetInitialMarginRequirements.txt b/Kraken.Net.UnitTests/Endpoints/Futures/Account/GetInitialMarginRequirements.txt index 479f0ba..81ab0a5 100644 --- a/Kraken.Net.UnitTests/Endpoints/Futures/Account/GetInitialMarginRequirements.txt +++ b/Kraken.Net.UnitTests/Endpoints/Futures/Account/GetInitialMarginRequirements.txt @@ -3,5 +3,5 @@ GET true { "result": "success", - "serverTime": "2016-02-25T09:45:53.818Z", + "serverTime": "2016-02-25T09:45:53.818Z" } \ No newline at end of file diff --git a/Kraken.Net.UnitTests/Endpoints/Futures/Account/GetMaxOrderQuantity.txt b/Kraken.Net.UnitTests/Endpoints/Futures/Account/GetMaxOrderQuantity.txt index a7acaba..c9ecade 100644 --- a/Kraken.Net.UnitTests/Endpoints/Futures/Account/GetMaxOrderQuantity.txt +++ b/Kraken.Net.UnitTests/Endpoints/Futures/Account/GetMaxOrderQuantity.txt @@ -3,5 +3,5 @@ GET true { "result": "success", - "serverTime": "2016-02-25T09:45:53.818Z", + "serverTime": "2016-02-25T09:45:53.818Z" } \ No newline at end of file diff --git a/Kraken.Net.UnitTests/Endpoints/Spot/Trading/CancelAllOrdersAfter.txt b/Kraken.Net.UnitTests/Endpoints/Spot/Trading/CancelAllOrdersAfter.txt new file mode 100644 index 0000000..7eb6bcb --- /dev/null +++ b/Kraken.Net.UnitTests/Endpoints/Spot/Trading/CancelAllOrdersAfter.txt @@ -0,0 +1,10 @@ +POST +/0/private/CancelAllOrdersAfter +true +{ + "error": [], + "result": { + "currentTime": "2023-03-24T17:41:56Z", + "triggerTime": "2023-03-24T17:42:56Z" + } +} \ No newline at end of file diff --git a/Kraken.Net.UnitTests/JsonSocketTests.cs b/Kraken.Net.UnitTests/JsonSocketTests.cs deleted file mode 100644 index 0f600b4..0000000 --- a/Kraken.Net.UnitTests/JsonSocketTests.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Kraken.Net.Interfaces.Clients; -using Kraken.Net.Objects.Internal; -using Kraken.Net.Objects.Models; -using Kraken.Net.Objects.Models.Socket; -using Kraken.Net.Objects.Sockets; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Kraken.Net.UnitTests -{ - internal class JsonSocketTests - { - [Test] - public async Task ValidateSystemStatusUpdateStreamJson() - { - await TestFileToObject(@"JsonResponses/Socket/SystemStatusUpdate.txt"); - } - - [Test] - public async Task ValidateTickerUpdateStreamJson() - { - await TestFileToObject>(@"JsonResponses/Socket/TickerUpdate.txt"); - } - - [Test] - public async Task ValidateKlineUpdateStreamJson() - { - await TestFileToObject>(@"JsonResponses/Socket/KlineUpdate.txt"); - } - - [Test] - public async Task ValidateTradeUpdateStreamJson() - { - await TestFileToObject>>(@"JsonResponses/Socket/TradeUpdate.txt"); - } - - [Test] - public async Task ValidateSpreadUpdateStreamJson() - { - await TestFileToObject>(@"JsonResponses/Socket/SpreadUpdate.txt"); - } - - [Test] - public async Task ValidateOpenOrderUpdateStreamJson() - { - await TestFileToObject>>(@"JsonResponses/Socket/OpenOrdersUpdate.txt", new List { "avg_price", "expiretm", "starttm" }); - } - - [Test] - public async Task ValidateUserTradeUpdateStreamJson() - { - await TestFileToObject>>(@"JsonResponses/Socket/OwnTradeUpdate.txt", new List { "avg_price" }); - } - - private static async Task TestFileToObject(string filePath, List ignoreProperties = null) - { - var listener = new EnumValueTraceListener(); - Trace.Listeners.Add(listener); - var path = Directory.GetParent(Environment.CurrentDirectory).Parent.Parent.FullName; - string json; - try - { - var file = File.OpenRead(Path.Combine(path, filePath)); - using var reader = new StreamReader(file); - json = await reader.ReadToEndAsync(); - } - catch (FileNotFoundException) - { - throw; - } - - var result = JsonConvert.DeserializeObject(json); - JsonToObjectComparer.ProcessData("", result, json, ignoreProperties: new Dictionary> - { - { "", ignoreProperties ?? new List() } - }); - Trace.Listeners.Remove(listener); - } - } - - internal class EnumValueTraceListener : TraceListener - { - public override void Write(string message) - { - if (message.Contains("Cannot map")) - throw new Exception("Enum value error: " + message); - } - - public override void WriteLine(string message) - { - if (message.Contains("Cannot map")) - throw new Exception("Enum value error: " + message); - } - } -} diff --git a/Kraken.Net.UnitTests/JsonTests.cs b/Kraken.Net.UnitTests/JsonTests.cs deleted file mode 100644 index 8aed430..0000000 --- a/Kraken.Net.UnitTests/JsonTests.cs +++ /dev/null @@ -1,115 +0,0 @@ -using Kraken.Net.Interfaces; -using Kraken.Net.UnitTests.TestImplementations; -using NUnit.Framework; -using System.Collections.Generic; -using System.Threading.Tasks; -using CryptoExchange.Net.Interfaces; -using Kraken.Net.Objects; -using Kraken.Net.Interfaces.Clients; - -namespace Kraken.Net.UnitTests -{ - [TestFixture] - public class JsonTests - { - private JsonToObjectComparer _comparer = new JsonToObjectComparer((json) => TestHelpers.CreateResponseClient(json, x => - { - x.ApiCredentials = new CryptoExchange.Net.Authentication.ApiCredentials("1234", "1234"); - x.OutputOriginalData = true; - x.RateLimiterEnabled = false; - })); - - [Test] - public async Task ValidateSpotAccountCalls() - { - await _comparer.ProcessSubject("SpotAccount", c => c.SpotApi.Account, - useNestedJsonPropertyForAllCompare: new List { "result" }, - useNestedJsonPropertyForCompare: new Dictionary { - { "GetOrderBookAsync", "XXBTZUSD" } , - } - ); - } - - [Test] - public async Task ValidateSpotExchangeDataCalls() - { - await _comparer.ProcessSubject("SpotExchangeData", c => c.SpotApi.ExchangeData, - useNestedJsonPropertyForAllCompare: new List { "result" }, - useNestedJsonPropertyForCompare: new Dictionary { - { "GetOrderBookAsync", "XXBTZUSD" } , - } - ); - } - - [Test] - public async Task ValidateSpotTradingCalls() - { - await _comparer.ProcessSubject("SpotTrading", c => c.SpotApi.Trading, - useNestedJsonPropertyForAllCompare: new List { "result" }, - useNestedJsonPropertyForCompare: new Dictionary { - { "GetOrderBookAsync", "XXBTZUSD" } , - } - ); - } - - [Test] - public async Task ValidateSpotEarnCalls() - { - await _comparer.ProcessSubject("SpotEarn", c => c.SpotApi.Earn, - useNestedJsonPropertyForAllCompare: new List { "result" } - ); - } - - [Test] - public async Task ValidateFuturesAccountCalls() - { - await _comparer.ProcessSubject("FuturesAccount", c => c.FuturesApi.Account, - useNestedJsonPropertyForCompare: new Dictionary - { - { "GetBalancesAsync", "accounts" }, - { "GetFeeScheduleVolumeAsync", "volumesByFeeSchedule" }, - { "GetPnlCurrencyAsync", "preferences" } - }); - } - - [Test] - public async Task ValidateFuturesExchangeDataCalls() - { - await _comparer.ProcessSubject("FuturesExchangeData", c => c.FuturesApi.ExchangeData, - useNestedJsonPropertyForCompare: new Dictionary - { - { "GetFeeSchedulesAsync", "feeSchedules" }, - { "GetHistoricalFundingRatesAsync", "rates" }, - { "GetOrderBookAsync", "orderBook" }, - { "GetSymbolsAsync", "instruments" }, - { "GetSymbolStatusAsync", "instrumentStatus" }, - { "GetTickersAsync", "tickers" }, - { "GetTradesAsync", "history" } - }); - } - - [Test] - public async Task ValidateFuturesTradingDataCalls() - { - await _comparer.ProcessSubject("FuturesTrading", c => c.FuturesApi.Trading, - useNestedJsonPropertyForCompare: new Dictionary - { - { "CancelAllOrderAfterAsync", "status" }, - { "CancelAllOrdersAsync", "cancelStatus" }, - { "CancelOrderAsync", "cancelStatus" }, - { "EditOrderAsync", "editStatus" }, - { "GetLeverageAsync", "leveragePreferences" }, - { "GetOpenOrdersAsync", "openOrders" }, - { "GetOpenPositionsAsync", "openPositions" }, - { "GetOrdersAsync", "orders" }, - { "GetSelfTradeStrategyAsync", "strategy" }, - { "GetUserTradesAsync", "fills" }, - { "PlaceOrderAsync", "sendStatus" }, - }, - ignoreProperties: new Dictionary> - { - { "PlaceOrderAsync", new List{ "takerReducedQuantity" } } - }); - } - } -} diff --git a/Kraken.Net.UnitTests/JsonToObjectComparer.cs b/Kraken.Net.UnitTests/JsonToObjectComparer.cs deleted file mode 100644 index 84f47d6..0000000 --- a/Kraken.Net.UnitTests/JsonToObjectComparer.cs +++ /dev/null @@ -1,444 +0,0 @@ -using CryptoExchange.Net.Converters; -using CryptoExchange.Net.Converters.JsonNet; -using CryptoExchange.Net.Objects; -using Kraken.Net.UnitTests.TestImplementations; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using NUnit.Framework; -using NUnit.Framework.Legacy; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -namespace Kraken.Net.UnitTests -{ - public class JsonToObjectComparer - { - private Func _clientFunc; - - public JsonToObjectComparer(Func getClient) - { - _clientFunc = getClient; - } - - public async Task ProcessSubject(Func getSubject, - string[] parametersToSetNull = null, - Dictionary useNestedJsonPropertyForCompare = null, - Dictionary useNestedObjectPropertyForCompare = null, - List useNestedJsonPropertyForAllCompare = null, - Dictionary> ignoreProperties = null, - List takeFirstItemForCompare = null) - => await ProcessSubject("", getSubject, parametersToSetNull, useNestedJsonPropertyForCompare, useNestedObjectPropertyForCompare, useNestedJsonPropertyForAllCompare, ignoreProperties, takeFirstItemForCompare); - - public async Task ProcessSubject( - string folderPrefix, - Func getSubject, - string[] parametersToSetNull = null, - Dictionary useNestedJsonPropertyForCompare = null, - Dictionary useNestedObjectPropertyForCompare = null, - List useNestedJsonPropertyForAllCompare = null, - Dictionary> ignoreProperties = null, - List takeFirstItemForCompare = null) - { - var listener = new EnumValueTraceListener(); - Trace.Listeners.Add(listener); - - var methods = typeof(K).GetMethods(); - var callResultMethods = methods.Where(m => m.Name.EndsWith("Async")).ToList(); - var skippedMethods = new List(); - - foreach (var method in callResultMethods) - { - var path = Directory.GetParent(Environment.CurrentDirectory).Parent.Parent.FullName; - FileStream file = null; - try - { - file = File.OpenRead(Path.Combine(path, $"JsonResponses", folderPrefix, $"{method.Name}.txt")); - } - catch (FileNotFoundException) - { - skippedMethods.Add(method.Name); - continue; - } - - var buffer = new byte[file.Length]; - await file.ReadAsync(buffer, 0, buffer.Length); - file.Close(); - - var json = Encoding.UTF8.GetString(buffer); - var client = _clientFunc(json); - - var parameters = method.GetParameters(); - var input = new List(); - foreach (var parameter in parameters) - { - if (parametersToSetNull?.Contains(parameter.Name) == true && parameter.ParameterType != typeof(int)) - input.Add(null); - else - input.Add(TestHelpers.GetTestValue(parameter.ParameterType, 1)); - } - - // act - var result = (CallResult)await TestHelpers.InvokeAsync(method, getSubject(client), input.ToArray()); - - // asset - ClassicAssert.Null(result.Error, method.Name); - - var resultProp = result.GetType().GetProperty("Data", BindingFlags.Public | BindingFlags.Instance); - if (resultProp == null) - // No result - continue; - - var resultData = resultProp.GetValue(result); - ProcessData(method.Name, resultData, json, useNestedJsonPropertyForCompare, useNestedObjectPropertyForCompare, useNestedJsonPropertyForAllCompare, ignoreProperties, takeFirstItemForCompare); - - } - - if (skippedMethods.Any()) - Debug.WriteLine("Skipped methods:"); - foreach (var method in skippedMethods) - Debug.WriteLine(method); - - Trace.Listeners.Remove(listener); - } - - internal static void ProcessData(string method, - object resultData, - string json, - Dictionary useNestedJsonPropertyForCompare = null, - Dictionary useNestedObjectPropertyForCompare = null, - List useNestedJsonPropertyForAllCompare = null, - Dictionary> ignoreProperties = null, - List takeFirstItemForCompare = null) - { - var resultProperties = resultData.GetType().GetProperties().Select(p => (p, (JsonPropertyAttribute)p.GetCustomAttributes(typeof(JsonPropertyAttribute), true).SingleOrDefault())); - var jsonObject = JToken.Parse(json); - if (useNestedJsonPropertyForAllCompare?.Any() == true) - { - foreach (var c in useNestedJsonPropertyForAllCompare) - jsonObject = jsonObject[c]; - } - - if (useNestedJsonPropertyForCompare?.ContainsKey(method) == true) - { - jsonObject = jsonObject[useNestedJsonPropertyForCompare[method]]; - } - - if (useNestedObjectPropertyForCompare?.ContainsKey(method) == true) - resultData = resultData.GetType().GetProperty(useNestedObjectPropertyForCompare[method]).GetValue(resultData); - - if (resultData.GetType().GetInterfaces().Contains(typeof(IDictionary))) - { - ProcessDictionary(method, (IDictionary)resultData, (JObject)jsonObject, ignoreProperties); - } - else if (jsonObject.Type == JTokenType.Array) - { - var jObjs = (JArray)jsonObject; - if (resultData.GetType().GetCustomAttribute()?.ConverterType == typeof(ArrayConverter)) - { - var resultProps = resultData.GetType().GetProperties().Select(p => (p, p.GetCustomAttributes(typeof(ArrayPropertyAttribute), true).Cast().SingleOrDefault())); - var arrayConverterProperty = resultData.GetType().GetCustomAttributes(typeof(JsonConverterAttribute), true).FirstOrDefault(); - var jsonConverter = (arrayConverterProperty as JsonConverterAttribute).ConverterType; - if (jsonConverter != typeof(ArrayConverter)) - // Not array converter? - return; - - int i = 0; - foreach (var item in jObjs.Children()) - { - var arrayProp = resultProps.SingleOrDefault(p => p.Item2?.Index == i).p; - if (arrayProp != null) - CheckPropertyValue(method, item, arrayProp.GetValue(resultData), arrayProp.Name, "Array index " + i, arrayProp, ignoreProperties); - - i++; - } - } - else - { - if (takeFirstItemForCompare?.Contains(method) == true) - { - resultData = new object[] { resultData }; - } - - var list = (IEnumerable)resultData; - var enumerator = list.GetEnumerator(); - foreach (var jObj in jObjs) - { - enumerator.MoveNext(); - if (jObj.Type == JTokenType.Object) - { - if (enumerator.Current is IDictionary dictionary) - { - ProcessDictionary(method, dictionary, (JObject)jObj, ignoreProperties); - } - else - { - foreach (var subProp in ((JObject)jObj).Properties()) - { - if (ignoreProperties?.ContainsKey(method) == true && ignoreProperties[method].Contains(subProp.Name)) - continue; - - CheckObject(method, subProp, enumerator.Current, ignoreProperties); - } - } - } - else if (jObj.Type == JTokenType.Array) - { - var resultObj = enumerator.Current; - ProcessArray(method, resultObj, jObj, ignoreProperties); - } - else - { - var value = enumerator.Current; - if (value == default && ((JValue)jObj).Type != JTokenType.Null) - { - throw new Exception($"{method}: Array has no value while input json array has value {jObj}"); - } - } - } - } - } - else - { - foreach (var item in jsonObject) - { - if (item is JProperty prop) - { - if (ignoreProperties?.ContainsKey(method) == true && ignoreProperties[method].Contains(prop.Name)) - continue; - - CheckObject(method, prop, resultData, ignoreProperties); - } - } - } - - Debug.WriteLine($"Successfully validated {method}"); - } - - private static void ProcessDictionary(string method, IDictionary resultData, JObject jsonObject, Dictionary> ignoreProperties = null) - { - var properties = jsonObject.Properties(); - foreach (var dictProp in properties) - { - if (!resultData.Contains(dictProp.Name)) - throw new Exception($"{method}: Dictionary has no value for {dictProp.Name} while input json `{dictProp.Name}` has value {dictProp.Value}"); - - if (dictProp.Value.Type == JTokenType.Object) - { - // TODO Some additional checking for objects - foreach (var prop in ((JObject)dictProp.Value).Properties()) - { - if (ignoreProperties?.ContainsKey(method) == true && ignoreProperties[method].Contains(prop.Name)) - continue; - - CheckObject(method, prop, resultData[dictProp.Name], ignoreProperties); - } - } - else - { - if (resultData[dictProp.Name] == default && dictProp.Value.Type != JTokenType.Null) - { - // Property value not correct - throw new Exception($"{method}: Dictionary entry `{dictProp.Name}` has no value while input json has value {dictProp.Value}"); - } - } - } - } - - private static void CheckObject(string method, JProperty prop, object obj, Dictionary> ignoreProperties) - { - var resultProperties = obj.GetType().GetProperties().Select(p => (p, (JsonPropertyAttribute)p.GetCustomAttributes(typeof(JsonPropertyAttribute), true).SingleOrDefault())); - - // Property has a value - var property = resultProperties.SingleOrDefault(p => p.Item2?.PropertyName == prop.Name).p; - property ??= resultProperties.SingleOrDefault(p => p.p.Name == prop.Name).p; - property ??= resultProperties.SingleOrDefault(p => p.p.Name.ToUpperInvariant() == prop.Name.ToUpperInvariant()).p; - - if (property is null) - { - // Property not found - throw new Exception($"{method}: Missing property `{prop.Name}` on `{obj.GetType().Name}`"); - } - - var propertyValue = property.GetValue(obj); - if(property.GetCustomAttribute(true)?.ItemConverterType == null) - CheckPropertyValue(method, prop.Value, propertyValue, property.Name, prop.Name, property, ignoreProperties); - } - - private static void CheckPropertyValue(string method, JToken propValue, object propertyValue, string propertyName = null, string propName = null, PropertyInfo info = null, Dictionary> ignoreProperties = null) - { - if (propertyValue == default && propValue.Type != JTokenType.Null && !string.IsNullOrEmpty(propValue.ToString())) - { - if ((propValue.Type == JTokenType.Integer || propValue.Type == JTokenType.String) - && propValue.ToString() == "0" && (info.PropertyType == typeof(DateTime) || info.PropertyType == typeof(DateTime?))) - { - return; - } - - // Property value not correct - throw new Exception($"{method}: Property `{propertyName}` has no value while input json `{propName}` has value {propValue}"); - } - - if (propertyValue == default && (propValue.Type == JTokenType.Null || string.IsNullOrEmpty(propValue.ToString()))) - return; - - if (propertyValue.GetType().GetInterfaces().Contains(typeof(IDictionary))) - { - var dict = (IDictionary)propertyValue; - var jObj = (JObject)propValue; - var properties = jObj.Properties(); - foreach (var dictProp in properties) - { - if (!dict.Contains(dictProp.Name)) - throw new Exception($"{method}: Property `{propertyName}` has no value while input json `{propName}` has value {propValue}"); - - if (dictProp.Value.Type == JTokenType.Object) - { - // TODO Some additional checking for objects - } - else - { - if (dict[dictProp.Name] == default && dictProp.Value.Type != JTokenType.Null) - { - // Property value not correct - throw new Exception($"{method}: Dictionary entry `{dictProp.Name}` has no value while input json has value {propValue}"); - } - } - } - } - else if (propertyValue.GetType().GetInterfaces().Contains(typeof(IEnumerable)) - && propertyValue.GetType() != typeof(string)) - { - var jObjs = (JArray)propValue; - var list = (IEnumerable)propertyValue; - var enumerator = list.GetEnumerator(); - foreach (JToken jtoken in jObjs) - { - enumerator.MoveNext(); - var typeConverter = enumerator.Current.GetType().GetCustomAttributes(typeof(JsonConverterAttribute), true); - if (typeConverter.Any() && ((JsonConverterAttribute)typeConverter.First()).ConverterType != typeof(ArrayConverter)) - // Custom converter for the type, skip - continue; - - if (jtoken.Type == JTokenType.Object) - { - foreach (var subProp in ((JObject)jtoken).Properties()) - { - if (ignoreProperties?.ContainsKey(method) == true && ignoreProperties[method].Contains(subProp.Name)) - continue; - - CheckObject(method, subProp, enumerator.Current, ignoreProperties); - } - } - else if (jtoken.Type == JTokenType.Array) - { - var resultObj = enumerator.Current; - ProcessArray(method, resultObj, jtoken, ignoreProperties); - } - else - { - var value = enumerator.Current; - if (value == default && ((JValue)jtoken).Type != JTokenType.Null) - { - throw new Exception($"{method}: Property `{propertyName}` has no value while input json `{propName}` has value {jtoken}"); - } - - CheckValues(method, propertyName, (JValue)jtoken, value); - } - } - } - else - { - if (propValue.Type == JTokenType.Object) - { - foreach (var item in propValue) - { - if (item is JProperty prop) - { - if (ignoreProperties?.ContainsKey(method) == true && ignoreProperties[method].Contains(prop.Name)) - continue; - - CheckObject(method, prop, propertyValue, ignoreProperties); - } - } - } - else if(propValue.Type == JTokenType.Array) - { - ProcessArray(method, propertyValue, propValue, ignoreProperties); - } - else - { - if (info.GetCustomAttribute(true) == null - && info.GetCustomAttribute(true)?.ItemConverterType == null) - { - CheckValues(method, propertyName, (JValue)propValue, propertyValue); - } - } - } - } - - private static void ProcessArray(string method, object resultObj, JToken jtoken, Dictionary> ignoreProperties = null) - { - var resultProps = resultObj.GetType().GetProperties().Select(p => (p, p.GetCustomAttributes(typeof(ArrayPropertyAttribute), true).Cast().SingleOrDefault())); - var arrayConverterProperty = resultObj.GetType().GetCustomAttributes(typeof(JsonConverterAttribute), true).FirstOrDefault(); - var jsonConverter = (arrayConverterProperty as JsonConverterAttribute)?.ConverterType; - if (jsonConverter != typeof(ArrayConverter)) - // Not array converter? - return; - - int i = 0; - foreach (var item in jtoken.Children()) - { - var arrayProp = resultProps.FirstOrDefault(p => p.Item2?.Index == i).p; - if (arrayProp != null) - CheckPropertyValue(method, item, arrayProp.GetValue(resultObj), arrayProp.Name, "Array index " + i, arrayProp, ignoreProperties); - - i++; - } - } - - private static void CheckValues(string method, string property, JValue jsonValue, object objectValue) - { - if (jsonValue.Type == JTokenType.String) - { - if (objectValue is decimal dec) - { - if (jsonValue.Value() != dec) - throw new Exception($"{method}: {property} not equal: {jsonValue.Value()} vs {dec}"); - } - else if (objectValue is DateTime time) - { - // timestamp, hard to check.. - } - else if (jsonValue.Value.ToString().ToLowerInvariant() != objectValue.ToString().ToLowerInvariant()) - { - throw new Exception($"{method}: {property} not equal: {jsonValue.Value()} vs {objectValue.ToString()}"); - } - } - else if (jsonValue.Type == JTokenType.Integer) - { - if (objectValue is string str) - { - if(jsonValue.Value.ToString().ToLowerInvariant() != objectValue.ToString().ToLowerInvariant()) - throw new Exception($"{method}: {property} not equal: {jsonValue.Value().ToLowerInvariant()} vs {objectValue.ToString().ToLowerInvariant()}"); - } - else - { - if (jsonValue.Value() != Convert.ToInt64(objectValue)) - throw new Exception($"{method}: {property} not equal: {jsonValue.Value()} vs {Convert.ToInt64(objectValue)}"); - } - } - else if (jsonValue.Type == JTokenType.Boolean) - { - if (jsonValue.Value() != (bool)objectValue) - throw new Exception($"{method}: {property} not equal: {jsonValue.Value()} vs {(bool)objectValue}"); - } - } - } -} diff --git a/Kraken.Net.UnitTests/Kraken.Net.UnitTests.csproj b/Kraken.Net.UnitTests/Kraken.Net.UnitTests.csproj index 5880c29..5cfc2b0 100644 --- a/Kraken.Net.UnitTests/Kraken.Net.UnitTests.csproj +++ b/Kraken.Net.UnitTests/Kraken.Net.UnitTests.csproj @@ -1,4 +1,4 @@ - + Exe diff --git a/Kraken.Net.UnitTests/KrakenClientTests.cs b/Kraken.Net.UnitTests/KrakenClientTests.cs index 59692e4..fae2991 100644 --- a/Kraken.Net.UnitTests/KrakenClientTests.cs +++ b/Kraken.Net.UnitTests/KrakenClientTests.cs @@ -1,19 +1,8 @@ -using Newtonsoft.Json; -using NUnit.Framework; -using System; -using Kraken.Net.Objects; +using NUnit.Framework; using Kraken.Net.UnitTests.TestImplementations; using System.Threading.Tasks; -using System.Reflection; -using System.Linq; -using System.Diagnostics; -using CryptoExchange.Net.Sockets; -using CryptoExchange.Net.Objects; using Kraken.Net.Objects.Internal; using Kraken.Net.Clients; -using Kraken.Net.Clients.SpotApi; -using Kraken.Net.ExtensionMethods; -using CryptoExchange.Net.Objects.Sockets; using System.Collections.Generic; using Kraken.Net.Objects.Models.Futures; using NUnit.Framework.Legacy; @@ -21,6 +10,7 @@ using CryptoExchange.Net.Authentication; using System.Net.Http; using CryptoExchange.Net.Testing.Implementations; +using System.Text.Json; namespace Kraken.Net.UnitTests { @@ -65,7 +55,7 @@ public string SerializeExpected(T data) Error = new string[] {} }; - return JsonConvert.SerializeObject(result); + return JsonSerializer.Serialize(result); } [TestCase()] @@ -97,7 +87,7 @@ public async Task ReceivingErrorOnSpotApi_Should_ReturnErrorAndNotSuccess() } }; - TestHelpers.SetResponse((KrakenRestClient)client, JsonConvert.SerializeObject(resultObj)); + TestHelpers.SetResponse((KrakenRestClient)client, JsonSerializer.Serialize(resultObj)); // act var result = await client.SpotApi.ExchangeData.GetAssetsAsync(); @@ -134,7 +124,7 @@ public async Task ReceivingErrorOnFuturesApi_Should_ReturnErrorAndNotSuccess() Error = "Error occured" }; - TestHelpers.SetResponse((KrakenRestClient)client, JsonConvert.SerializeObject(resultObj), System.Net.HttpStatusCode.BadRequest); + TestHelpers.SetResponse((KrakenRestClient)client, JsonSerializer.Serialize(resultObj), System.Net.HttpStatusCode.BadRequest); // act var result = await client.FuturesApi.ExchangeData.GetTickersAsync(); diff --git a/Kraken.Net.UnitTests/KrakenRestIntegrationTests.cs b/Kraken.Net.UnitTests/KrakenRestIntegrationTests.cs index 8154909..63134b5 100644 --- a/Kraken.Net.UnitTests/KrakenRestIntegrationTests.cs +++ b/Kraken.Net.UnitTests/KrakenRestIntegrationTests.cs @@ -1,13 +1,9 @@ using Kraken.Net.Clients; -using Kraken.Net.Objects; using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Testing; using Microsoft.Extensions.Logging; using NUnit.Framework; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; namespace Kraken.Net.UnitTests diff --git a/Kraken.Net.UnitTests/KrakenSocketClientTests.cs b/Kraken.Net.UnitTests/KrakenSocketClientTests.cs index 1816c65..38ef3a1 100644 --- a/Kraken.Net.UnitTests/KrakenSocketClientTests.cs +++ b/Kraken.Net.UnitTests/KrakenSocketClientTests.cs @@ -1,15 +1,7 @@ -using CryptoExchange.Net; -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; -using Kraken.Net.Clients; -using Kraken.Net.UnitTests.TestImplementations; -using Kucoin.Net.UnitTests.TestImplementations; +using Kraken.Net.UnitTests.TestImplementations; using Newtonsoft.Json.Linq; using NUnit.Framework; using NUnit.Framework.Legacy; -using System.Diagnostics; -using System.Linq; -using System.Reflection; using System.Threading.Tasks; namespace Kraken.Net.UnitTests @@ -28,8 +20,8 @@ public async Task Subscribe_Should_SucceedIfAckResponse() // act var subTask = client.SpotApi.SubscribeToTickerUpdatesAsync("XBT/EUR", test => { }); await Task.Delay(10); - var id = JToken.Parse(socket.LastSendMessage!)["reqid"]; - socket.InvokeMessage($"{{\"channelID\": 1, \"status\": \"subscribed\", \"reqid\":{id}}}"); + var id = JToken.Parse(socket.LastSendMessage!)["req_id"]; + socket.InvokeMessage("{\"method\": \"subscribe\", \"result\": {\"channel\": \"ticker\", \"snapshot\": true, \"symbol\": \"XBT/EUR\" }, \"success\": true, \"time_in\": \"2023-09-25T09:04:31.742599Z\", \"time_out\": \"2023-09-25T09:04:31.742648Z\", \"req_id\": " + id + "}"); var subResult = subTask.Result; // assert @@ -47,13 +39,13 @@ public async Task Subscribe_Should_FailIfNotAckResponse() // act var subTask = client.SpotApi.SubscribeToTickerUpdatesAsync("XBT/EUR", test => { }); await Task.Delay(10); - var id = JToken.Parse(socket.LastSendMessage!)["reqid"]; - socket.InvokeMessage($"{{\"channelID\": 1, \"status\": \"error\", \"errormessage\": \"Failed to sub\", \"reqid\":{id}}}"); + var id = JToken.Parse(socket.LastSendMessage!)["req_id"]; + socket.InvokeMessage($"{{\"error\":\"Currency pair not in ISO 4217-A3 format DSF\",\"method\":\"subscribe\",\"req_id\":5,\"success\":false,\"symbol\":\"DSF\",\"time_in\":\"2024-10-11T09:49:47.814408Z\",\"time_out\":\"2024-10-11T09:49:47.814465Z\", \"req_id\": {id} }}"); var subResult = subTask.Result; // assert ClassicAssert.IsFalse(subResult.Success); - Assert.That(subResult.Error!.Message.Contains("Failed to sub")); + Assert.That(subResult.Error!.Message.Contains("Currency pair not in ISO 4217-A3 format")); } } } diff --git a/Kraken.Net.UnitTests/RestRequestTests.cs b/Kraken.Net.UnitTests/RestRequestTests.cs index 4f44b42..838cc79 100644 --- a/Kraken.Net.UnitTests/RestRequestTests.cs +++ b/Kraken.Net.UnitTests/RestRequestTests.cs @@ -23,7 +23,7 @@ public async Task ValidateSpotAccountCalls() opts.ApiCredentials = new ApiCredentials("MTIz", "MTIz"); opts.RateLimiterEnabled = false; }); - var tester = new RestRequestValidator(client, "Endpoints/Spot/Account", "https://api.kraken.com", IsAuthenticatedSpot, "result", stjCompare: false); + var tester = new RestRequestValidator(client, "Endpoints/Spot/Account", "https://api.kraken.com", IsAuthenticatedSpot, "result", stjCompare: true); await tester.ValidateAsync(client => client.SpotApi.Account.GetBalancesAsync(), "GetBalances"); await tester.ValidateAsync(client => client.SpotApi.Account.GetAvailableBalancesAsync(), "GetAvailableBalances"); await tester.ValidateAsync(client => client.SpotApi.Account.GetTradeBalanceAsync(), "GetTradeBalance"); @@ -31,7 +31,7 @@ public async Task ValidateSpotAccountCalls() await tester.ValidateAsync(client => client.SpotApi.Account.GetLedgerInfoAsync(), "GetLedgerInfo", skipResponseValidation: true); await tester.ValidateAsync(client => client.SpotApi.Account.GetLedgersEntryAsync(), "GetLedgersEntry"); await tester.ValidateAsync(client => client.SpotApi.Account.GetTradeVolumeAsync(), "GetTradeVolume", skipResponseValidation: true); - await tester.ValidateAsync(client => client.SpotApi.Account.GetDepositMethodsAsync("ETH"), "GetDepositMethods"); + await tester.ValidateAsync(client => client.SpotApi.Account.GetDepositMethodsAsync("ETH"), "GetDepositMethods", ignoreProperties: new List { "limit" }); await tester.ValidateAsync(client => client.SpotApi.Account.GetDepositAddressesAsync("ETH", "123"), "GetDepositAddresses"); await tester.ValidateAsync(client => client.SpotApi.Account.GetDepositStatusAsync(), "GetDepositStatus"); await tester.ValidateAsync(client => client.SpotApi.Account.GetWithdrawInfoAsync("ETH", "123", 1), "GetWithdrawInfo"); @@ -50,7 +50,7 @@ public async Task ValidateSpotExchangeDataCalls() { opts.AutoTimestamp = false; }); - var tester = new RestRequestValidator(client, "Endpoints/Spot/ExchangeData", "https://api.kraken.com", IsAuthenticatedSpot, "result", stjCompare: false); + var tester = new RestRequestValidator(client, "Endpoints/Spot/ExchangeData", "https://api.kraken.com", IsAuthenticatedSpot, "result", stjCompare: true); await tester.ValidateAsync(client => client.SpotApi.ExchangeData.GetSystemStatusAsync(), "GetSystemStatus"); await tester.ValidateAsync(client => client.SpotApi.ExchangeData.GetAssetsAsync(), "GetAssets"); await tester.ValidateAsync(client => client.SpotApi.ExchangeData.GetSymbolsAsync(), "GetSymbols"); @@ -70,7 +70,7 @@ public async Task ValidateSpotTradingCalls() opts.ApiCredentials = new ApiCredentials("MTIz", "MTIz"); opts.RateLimiterEnabled = false; }); - var tester = new RestRequestValidator(client, "Endpoints/Spot/Trading", "https://api.kraken.com", IsAuthenticatedSpot, "result", stjCompare: false); + var tester = new RestRequestValidator(client, "Endpoints/Spot/Trading", "https://api.kraken.com", IsAuthenticatedSpot, "result", stjCompare: true); await tester.ValidateAsync(client => client.SpotApi.Trading.GetOpenOrdersAsync(), "GetOpenOrders", skipResponseValidation: true); await tester.ValidateAsync(client => client.SpotApi.Trading.GetClosedOrdersAsync(), "GetClosedOrders", skipResponseValidation: true); await tester.ValidateAsync(client => client.SpotApi.Trading.GetOrdersAsync(), "GetOrders", skipResponseValidation: true); @@ -81,6 +81,7 @@ public async Task ValidateSpotTradingCalls() await tester.ValidateAsync(client => client.SpotApi.Trading.EditOrderAsync("ETHUSDT", "123", 1), "EditOrder"); await tester.ValidateAsync(client => client.SpotApi.Trading.CancelOrderAsync("ETHUSDT", "123"), "CancelOrder"); await tester.ValidateAsync(client => client.SpotApi.Trading.CancelAllOrdersAsync("ETHUSDT"), "CancelAllOrders"); + await tester.ValidateAsync(client => client.SpotApi.Trading.CancelAllOrdersAfterAsync(TimeSpan.FromSeconds(5)), "CancelAllOrdersAfter"); } [Test] @@ -92,7 +93,7 @@ public async Task ValidateSpotEarnCalls() opts.ApiCredentials = new ApiCredentials("MTIz", "MTIz"); opts.RateLimiterEnabled = false; }); - var tester = new RestRequestValidator(client, "Endpoints/Spot/Earn", "https://api.kraken.com", IsAuthenticatedSpot, "result", stjCompare: false); + var tester = new RestRequestValidator(client, "Endpoints/Spot/Earn", "https://api.kraken.com", IsAuthenticatedSpot, "result", stjCompare: true); await tester.ValidateAsync(client => client.SpotApi.Earn.GetStrategiesAsync(), "GetStrategies"); await tester.ValidateAsync(client => client.SpotApi.Earn.GetAllocationsAsync(), "GetAllocations"); await tester.ValidateAsync(client => client.SpotApi.Earn.GetAllocationStatusAsync("123"), "GetAllocationStatus"); @@ -107,9 +108,10 @@ public async Task ValidateFuturesAccountCalls() var client = new KrakenRestClient(opts => { opts.AutoTimestamp = false; + opts.RateLimiterEnabled = false; opts.ApiCredentials = new ApiCredentials("MTIz", "MTIz"); }); - var tester = new RestRequestValidator(client, "Endpoints/Futures/Account", "https://futures.kraken.com", IsAuthenticatedFutures, stjCompare: false); + var tester = new RestRequestValidator(client, "Endpoints/Futures/Account", "https://futures.kraken.com", IsAuthenticatedFutures, stjCompare: true); await tester.ValidateAsync(client => client.FuturesApi.Account.GetBalancesAsync(), "GetBalances", "result", skipResponseValidation: true); await tester.ValidateAsync(client => client.FuturesApi.Account.GetPnlCurrencyAsync(), "GetPnlCurrency", "result"); await tester.ValidateAsync(client => client.FuturesApi.Account.SetPnlCurrencyAsync("ETHUSDT", "ETH"), "SetPnlCurrency"); @@ -126,9 +128,10 @@ public async Task ValidateFuturesTradingCalls() var client = new KrakenRestClient(opts => { opts.AutoTimestamp = false; + opts.RateLimiterEnabled = false; opts.ApiCredentials = new ApiCredentials("MTIz", "MTIz"); }); - var tester = new RestRequestValidator(client, "Endpoints/Futures/Trading", "https://futures.kraken.com", IsAuthenticatedFutures, stjCompare: false); + var tester = new RestRequestValidator(client, "Endpoints/Futures/Trading", "https://futures.kraken.com", IsAuthenticatedFutures, stjCompare: true); await tester.ValidateAsync(client => client.FuturesApi.Trading.GetUserTradesAsync(), "GetUserTrades", "result"); await tester.ValidateAsync(client => client.FuturesApi.Trading.GetSelfTradeStrategyAsync(), "GetSelfTradeStrategy", skipResponseValidation: true); await tester.ValidateAsync(client => client.FuturesApi.Trading.SetSelfTradeStrategyAsync(Enums.SelfTradeStrategy.CancelMakerSelf), "SetSelfTradeStrategy", skipResponseValidation: true); @@ -138,7 +141,7 @@ public async Task ValidateFuturesTradingCalls() await tester.ValidateAsync(client => client.FuturesApi.Trading.PlaceOrderAsync("ETHUSDT", Enums.OrderSide.Buy, Enums.FuturesOrderType.Market, 1), "PlaceOrder", "sendStatus"); await tester.ValidateAsync(client => client.FuturesApi.Trading.GetOpenOrdersAsync(), "GetOpenOrders", "openOrders"); await tester.ValidateAsync(client => client.FuturesApi.Trading.GetOrdersAsync(), "GetOrders", "openOrders", skipResponseValidation: true); - await tester.ValidateAsync(client => client.FuturesApi.Trading.EditOrderAsync(), "EditOrder", "editStatus"); + await tester.ValidateAsync(client => client.FuturesApi.Trading.EditOrderAsync(), "EditOrder", "editStatus", ignoreProperties: new List { "orderId" }); await tester.ValidateAsync(client => client.FuturesApi.Trading.CancelOrderAsync(), "CancelOrder", "cancelStatus"); await tester.ValidateAsync(client => client.FuturesApi.Trading.CancelAllOrdersAsync(), "CancelAllOrders", "cancelStatus"); await tester.ValidateAsync(client => client.FuturesApi.Trading.CancelAllOrderAfterAsync(TimeSpan.Zero), "CancelAllOrderAfter", "status"); @@ -150,10 +153,11 @@ public async Task ValidateFuturesExchangeDataCalls() { var client = new KrakenRestClient(opts => { + opts.RateLimiterEnabled = false; opts.AutoTimestamp = false; opts.ApiCredentials = new ApiCredentials("MTIz", "MTIz"); }); - var tester = new RestRequestValidator(client, "Endpoints/Futures/ExchangeData", "https://futures.kraken.com", IsAuthenticatedFutures, stjCompare: false); + var tester = new RestRequestValidator(client, "Endpoints/Futures/ExchangeData", "https://futures.kraken.com", IsAuthenticatedFutures, stjCompare: true); await tester.ValidateAsync(client => client.FuturesApi.ExchangeData.GetPlatformNotificationsAsync(), "GetPlatformNotifications", "result"); await tester.ValidateAsync(client => client.FuturesApi.ExchangeData.GetHistoricalFundingRatesAsync("ETHUSDT"), "GetHistoricalFundingRates", "result"); await tester.ValidateAsync(client => client.FuturesApi.ExchangeData.GetFeeSchedulesAsync(), "GetFeeSchedules", "result"); diff --git a/Kraken.Net.UnitTests/SocketSubscriptionTests.cs b/Kraken.Net.UnitTests/SocketSubscriptionTests.cs new file mode 100644 index 0000000..811c3ec --- /dev/null +++ b/Kraken.Net.UnitTests/SocketSubscriptionTests.cs @@ -0,0 +1,28 @@ +using CryptoExchange.Net.Authentication; +using CryptoExchange.Net.Testing; +using Kraken.Net.Clients; +using Kraken.Net.Objects.Models.Socket; +using NUnit.Framework; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Kraken.Net.UnitTests +{ + [TestFixture] + public class SocketSubscriptionTests + { + [Test] + public async Task ValidateSpotSubscriptions() + { + var client = new KrakenSocketClient(opts => + { + opts.ApiCredentials = new ApiCredentials("MTIz", "MTIz"); + }); + var tester = new SocketSubscriptionValidator(client, "Subscriptions/Spot", "wss://ws-auth.kraken.com", "data", stjCompare: true); + await tester.ValidateAsync((client, handler) => client.SpotApi.SubscribeToTickerUpdatesAsync("ETH/USDT", handler), "Ticker"); + await tester.ValidateAsync>((client, handler) => client.SpotApi.SubscribeToKlineUpdatesAsync("ETH/USDT", Enums.KlineInterval.FiveMinutes, handler), "Kline", ignoreProperties: new List { "timestamp" }); + await tester.ValidateAsync>((client, handler) => client.SpotApi.SubscribeToTradeUpdatesAsync("ETH/USDT", handler), "Trades"); + await tester.ValidateAsync((client, handler) => client.SpotApi.SubscribeToAggregatedOrderBookUpdatesAsync("ETH/USDT", 10, handler), "AggBook"); + } + } +} diff --git a/Kraken.Net.UnitTests/Subscriptions/Spot/AggBook.txt b/Kraken.Net.UnitTests/Subscriptions/Spot/AggBook.txt new file mode 100644 index 0000000..62a300a --- /dev/null +++ b/Kraken.Net.UnitTests/Subscriptions/Spot/AggBook.txt @@ -0,0 +1,97 @@ +> { "method": "subscribe", "params": { "channel": "book", "symbol": ["ETH/USDT"] }, "req_id": "|1|" } +< { "method": "subscribe", "result": { "channel": "book", "snapshot": true, "symbol": "ETH/USDT" }, "success": true,"time_in": "2023-09-25T09:04:31.742599Z", "time_out": "2023-09-25T09:04:31.742648Z", "req_id": |1| } += +{ + "channel": "book", + "type": "snapshot", + "data": [ + { + "symbol": "ETH/USDT", + "bids": [ + { + "price": 0.5666, + "qty": 4831.75496356 + }, + { + "price": 0.5665, + "qty": 6658.22734739 + }, + { + "price": 0.5664, + "qty": 18724.91513344 + }, + { + "price": 0.5663, + "qty": 11563.92544914 + }, + { + "price": 0.5662, + "qty": 14006.65365711 + }, + { + "price": 0.5661, + "qty": 17454.85679807 + }, + { + "price": 0.566, + "qty": 18097.1547 + }, + { + "price": 0.5659, + "qty": 33644.89175666 + }, + { + "price": 0.5658, + "qty": 148.3464 + }, + { + "price": 0.5657, + "qty": 606.70854372 + } + ], + "asks": [ + { + "price": 0.5668, + "qty": 4410.79769741 + }, + { + "price": 0.5669, + "qty": 4655.40412487 + }, + { + "price": 0.567, + "qty": 49844.89424998 + }, + { + "price": 0.5671, + "qty": 24306.41678 + }, + { + "price": 0.5672, + "qty": 29783.25223475 + }, + { + "price": 0.5673, + "qty": 57234.71239278 + }, + { + "price": 0.5674, + "qty": 45065.04744 + }, + { + "price": 0.5675, + "qty": 5912.76380354 + }, + { + "price": 0.5676, + "qty": 42514.92434778 + }, + { + "price": 0.5677, + "qty": 36304.0847022 + } + ], + "checksum": 2439117997 + } + ] +} \ No newline at end of file diff --git a/Kraken.Net.UnitTests/Subscriptions/Spot/IndividualBook.txt b/Kraken.Net.UnitTests/Subscriptions/Spot/IndividualBook.txt new file mode 100644 index 0000000..297d301 --- /dev/null +++ b/Kraken.Net.UnitTests/Subscriptions/Spot/IndividualBook.txt @@ -0,0 +1,53 @@ +> { "method": "subscribe", "params": { "channel": "level3", "symbol": ["ETH/USDT"] }, "req_id": "|1|" } +< { "method": "subscribe", "result": { "channel": "level3", "snapshot": true, "symbol": "ETH/USDT" }, "success": true,"time_in": "2023-09-25T09:04:31.742599Z", "time_out": "2023-09-25T09:04:31.742648Z", "req_id": |1| } += +{ + "channel": "level3", + "type": "snapshot", + "data": [ + { + "symbol": "ETH/USDT", + "checksum": 281817320, + "bids": [ + { + "order_id": "O6ZQNQ-BXL4E-5WGINO", + "limit_price": 0.5629, + "order_qty": 111.56125344, + "timestamp": "2023-10-06T17:35:00.279389650Z" + }, + { + "order_id": "OEP26Y-YAFEF-OFR62B", + "limit_price": 0.5625, + "order_qty": 6390.19338, + "timestamp": "2023-10-06T18:19:55.056070105Z" + }, + { + "order_id": "OKNAY7-67JRK-AIZ4JO", + "limit_price": 0.5625, + "order_qty": 14084.5, + "timestamp": "2023-10-06T18:20:55.357467423Z" + } + ], + "asks": [ + { + "order_id": "OLLSXO-HDMT3-BUOKEI", + "limit_price": 0.563, + "order_qty": 4422.9978357, + "timestamp": "2023-10-06T18:18:20.734897896Z" + }, + { + "order_id": "O5SR5W-L7OLY-BLDEJV", + "limit_price": 0.563, + "order_qty": 420.0, + "timestamp": "2023-10-06T18:18:20.738706230Z" + }, + { + "order_id": "OXV6QS-2GG4Q-F4EECM", + "limit_price": 0.563, + "order_qty": 490.0, + "timestamp": "2023-10-06T18:18:21.064657206Z" + } + ] + } + ] +} \ No newline at end of file diff --git a/Kraken.Net.UnitTests/Subscriptions/Spot/Kline.txt b/Kraken.Net.UnitTests/Subscriptions/Spot/Kline.txt new file mode 100644 index 0000000..7d64f1f --- /dev/null +++ b/Kraken.Net.UnitTests/Subscriptions/Spot/Kline.txt @@ -0,0 +1,23 @@ +> { "method": "subscribe", "params": { "channel": "ohlc", "symbol": ["ETH/USDT"], "interval": 5 }, "req_id": "|1|" } +< { "method": "subscribe", "result": { "channel": "ohlc", "snapshot": true, "symbol": "ETH/USDT", "interval": 5 }, "success": true,"time_in": "2023-09-25T09:04:31.742599Z", "time_out": "2023-09-25T09:04:31.742648Z", "req_id": |1| } += +{ + "channel": "ohlc", + "type": "update", + "timestamp": "2023-10-04T16:26:30.524394914Z", + "data": [ + { + "symbol": "ETH/USDT", + "open": 0.5624, + "high": 0.5628, + "low": 0.5622, + "close": 0.5627, + "trades": 12, + "volume": 30927.68066226, + "vwap": 0.5626, + "interval_begin": "2023-10-04T16:25:00.000000000Z", + "interval": 5, + "timestamp": "2023-10-04T16:30:00.000000Z" + } + ] +} \ No newline at end of file diff --git a/Kraken.Net.UnitTests/Subscriptions/Spot/Ticker.txt b/Kraken.Net.UnitTests/Subscriptions/Spot/Ticker.txt new file mode 100644 index 0000000..b9413bb --- /dev/null +++ b/Kraken.Net.UnitTests/Subscriptions/Spot/Ticker.txt @@ -0,0 +1,23 @@ +> { "method": "subscribe", "params": { "channel": "ticker", "symbol": ["ETH/USDT"] }, "req_id": "|1|" } +< { "method": "subscribe", "result": { "channel": "ticker", "snapshot": true, "symbol": "ETH/USDT" }, "success": true,"time_in": "2023-09-25T09:04:31.742599Z", "time_out": "2023-09-25T09:04:31.742648Z", "req_id": |1| } += +{ + "channel": "ticker", + "type": "update", + "data": [ + { + "symbol": "ETH/USDT", + "bid": 0.10025, + "bid_qty": 740.0, + "ask": 0.10035, + "ask_qty": 740.0, + "last": 0.10035, + "volume": 997038.98383185, + "vwap": 0.10148, + "low": 0.09979, + "high": 0.10285, + "change": -0.00017, + "change_pct": -0.17 + } + ] +} \ No newline at end of file diff --git a/Kraken.Net.UnitTests/Subscriptions/Spot/Trades.txt b/Kraken.Net.UnitTests/Subscriptions/Spot/Trades.txt new file mode 100644 index 0000000..bc277ed --- /dev/null +++ b/Kraken.Net.UnitTests/Subscriptions/Spot/Trades.txt @@ -0,0 +1,18 @@ +> { "method": "subscribe", "params": { "channel": "trade", "symbol": ["ETH/USDT"] }, "req_id": "|1|" } +< { "method": "subscribe", "result": { "channel": "trade", "snapshot": true, "symbol": "ETH/USDT" }, "success": true,"time_in": "2023-09-25T09:04:31.742599Z", "time_out": "2023-09-25T09:04:31.742648Z", "req_id": |1| } += +{ + "channel": "trade", + "type": "update", + "data": [ + { + "symbol": "ETH/USDT", + "side": "sell", + "price": 0.5117, + "qty": 40.0, + "ord_type": "market", + "trade_id": 4665906, + "timestamp": "2023-09-25T07:49:37.708706Z" + } + ] +} \ No newline at end of file diff --git a/Kraken.Net.UnitTests/TestImplementations/TestSocket.cs b/Kraken.Net.UnitTests/TestImplementations/TestSocket.cs index 578de79..ee251f0 100644 --- a/Kraken.Net.UnitTests/TestImplementations/TestSocket.cs +++ b/Kraken.Net.UnitTests/TestImplementations/TestSocket.cs @@ -8,7 +8,7 @@ using CryptoExchange.Net.Objects; using Newtonsoft.Json; -namespace Kucoin.Net.UnitTests.TestImplementations +namespace Kraken.Net.UnitTests.TestImplementations { public class TestSocket: IWebsocket { diff --git a/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApi.cs b/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApi.cs index 2f6deb6..0f26ea9 100644 --- a/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApi.cs +++ b/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApi.cs @@ -47,6 +47,10 @@ internal KrakenRestClientFuturesApi(ILogger logger, HttpClient? httpClient, Krak } #endregion + protected override IStreamMessageAccessor CreateAccessor() => new SystemTextJsonStreamMessageAccessor(); + + protected override IMessageSerializer CreateSerializer() => new SystemTextJsonMessageSerializer(); + public IKrakenRestClientFuturesApiShared SharedClient => this; /// @@ -55,10 +59,9 @@ public override string FormatSymbol(string baseAsset, string quoteAsset, Trading return $"{(tradingMode == TradingMode.PerpetualLinear ? "PF" : tradingMode == TradingMode.PerpetualInverse ? "PI" : tradingMode == TradingMode.DeliveryLinear ? "FF": "FI")}_{baseAsset.ToUpperInvariant()}{quoteAsset.ToUpperInvariant()}" + (!deliverTime.HasValue ? string.Empty : ("_" + deliverTime.Value.ToString("yyMMdd"))); } - internal async Task> Execute(Uri url, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false, int weight = 0) - where T : KrakenFuturesResult + internal async Task> SendToAddressAsync(string baseAddress, RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null) where T : KrakenFuturesResult { - var result = await SendRequestAsync(url, method, ct, parameters, signed, requestWeight: weight, gate: KrakenExchange.RateLimiter.FuturesApi).ConfigureAwait(false); + var result = await base.SendAsync(baseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false); if (!result) return result.AsError(result.Error!); @@ -76,53 +79,36 @@ internal async Task> Execute(Uri url, HttpMethod method, return result.As(result.Data.Data); } - internal async Task> Execute(Uri url, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false, int weight = 0) - where T : KrakenFuturesResult + internal async Task SendAsync(RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null) { - var result = await SendRequestAsync(url, method, ct, parameters, signed, requestWeight: weight, gate: KrakenExchange.RateLimiter.FuturesApi).ConfigureAwait(false); + var result = await base.SendAsync(BaseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false); if (!result) - return result.AsError(result.Error!); - - if (result.Data.Errors?.Any() == true) - { - if (result.Data.Errors.Count() > 1) - return result.AsError(new ServerError(string.Join(", ", result.Data.Errors.Select(e => e.Code + ":" + e.Message)))); - else - return result.AsError(new ServerError(result.Data.Errors.First().Code, result.Data.Errors.First().Message)); - } + return result.AsDatalessError(result.Error!); - if (!string.IsNullOrEmpty(result.Data.Error)) - return result.AsError(new ServerError(result.Data.Error!)); + if (result.Data.Error?.Any() == true) + return result.AsDatalessError(new ServerError(string.Join(", ", result.Data.Error))); - return result.As(result.Data); + return result.AsDataless(); } - internal async Task Execute(Uri url, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false, int requestWeight = 0) + internal async Task> SendAsync(RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null) where T : class { - var result = await SendRequestAsync(url, method, ct, parameters, signed, requestWeight: requestWeight, gate: KrakenExchange.RateLimiter.FuturesApi).ConfigureAwait(false); + var result = await base.SendAsync>(BaseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false); if (!result) - return result.AsDatalessError(result.Error!); - - if (result.Data.Errors?.Any() == true) - { - if (result.Data.Errors.Count() > 1) - return result.AsDatalessError(new ServerError(string.Join(", ", result.Data.Errors.Select(e => e.Code + ":" + e.Message)))); - else - return result.AsDatalessError(new ServerError(result.Data.Errors.First().Code, result.Data.Errors.First().Message)); - } + return result.AsError(result.Error!); - if (!string.IsNullOrEmpty(result.Data.Error)) - return result.AsDatalessError(new ServerError(result.Data.Error!)); + if (result.Data.Error?.Any() == true) + return result.AsError(new ServerError(string.Join(", ", result.Data.Error))); - return result.AsDataless(); + return result.As(result.Data.Data); } - internal async Task> ExecuteBase(Uri url, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false, int requestWeight = 0) - where T : class - { - return await SendRequestAsync(url, method, ct, parameters, signed, requestWeight: requestWeight, gate: KrakenExchange.RateLimiter.FuturesApi).ConfigureAwait(false); - } + internal Task> SendAsync(RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null) where T : KrakenFuturesResult + => SendToAddressAsync(BaseAddress, definition, parameters, cancellationToken, weight); + internal async Task> SendRawAsync(RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null) where T: class + => await base.SendAsync(BaseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false); + /// protected override Error ParseErrorResponse(int httpStatusCode, IEnumerable>> responseHeaders, IMessageAccessor accessor) { diff --git a/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiAccount.cs b/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiAccount.cs index 018b84d..341c2eb 100644 --- a/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiAccount.cs +++ b/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiAccount.cs @@ -1,13 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading.Tasks; -using System.Threading; -using CryptoExchange.Net.Objects; -using Kraken.Net.Objects.Models.Futures; -using CryptoExchange.Net; -using System.Globalization; -using CryptoExchange.Net.Converters; +using Kraken.Net.Objects.Models.Futures; using Kraken.Net.Interfaces.Clients.FuturesApi; using Kraken.Net.Enums; @@ -16,6 +7,7 @@ namespace Kraken.Net.Clients.FuturesApi /// internal class KrakenRestClientFuturesApiAccount : IKrakenRestClientFuturesApiAccount { + private static readonly RequestDefinitionCache _definitions = new RequestDefinitionCache(); private readonly KrakenRestClientFuturesApi _baseClient; internal KrakenRestClientFuturesApiAccount(KrakenRestClientFuturesApi baseClient) @@ -23,47 +15,71 @@ internal KrakenRestClientFuturesApiAccount(KrakenRestClientFuturesApi baseClient _baseClient = baseClient; } + #region Get Balances + /// - public async Task>> GetBalancesAsync(CancellationToken ct = default) + public async Task> GetBalancesAsync(CancellationToken ct = default) { - return await _baseClient.Execute>(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/accounts")), HttpMethod.Get, ct, signed: true, weight: 2).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/accounts", KrakenExchange.RateLimiter.FuturesApi, 2, true); + return await _baseClient.SendAsync(request, null, ct).ConfigureAwait(false); } + #endregion + + #region Get Pnl Currency + /// public async Task>> GetPnlCurrencyAsync(CancellationToken ct = default) { - return await _baseClient.Execute>(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/pnlpreferences")), HttpMethod.Get, ct, signed: true, weight: 2).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/pnlpreferences", KrakenExchange.RateLimiter.FuturesApi, 2, true); + return await _baseClient.SendAsync>(request, null, ct).ConfigureAwait(false); } + #endregion + + #region Set Pnl Currency + /// public async Task SetPnlCurrencyAsync(string symbol, string pnlCurrency, CancellationToken ct = default) { - var parameters = new Dictionary() + var parameters = new ParameterCollection() { { "pnlPreference", pnlCurrency }, { "symbol", symbol } }; - return await _baseClient.Execute(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/pnlpreferences")), HttpMethod.Put, ct, parameters, signed: true, requestWeight: 10).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Put, "derivatives/api/v3/pnlpreferences", KrakenExchange.RateLimiter.FuturesApi, 10, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Transfer + /// public async Task TransferAsync( string asset, decimal quantity, string fromAccount, string toAccount, CancellationToken ct = default) { - var parameters = new Dictionary() + var parameters = new ParameterCollection() { { "amount", quantity.ToString(CultureInfo.InvariantCulture) }, { "fromAccount", fromAccount }, { "toAccount", toAccount }, { "unit", asset } }; - return await _baseClient.Execute(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/transfer")), HttpMethod.Post, ct, parameters, signed: true, requestWeight: 10).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "derivatives/api/v3/transfer", KrakenExchange.RateLimiter.FuturesApi, 10, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Account Log + /// public async Task> GetAccountLogAsync(DateTime? startTime = null, DateTime? endTime = null, int? fromId = null, int? toId = null, string? sort = null, string? type = null, int? limit = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("before", DateTimeConverter.ConvertToMilliseconds(endTime)); parameters.AddOptionalParameter("count", limit); parameters.AddOptionalParameter("from", fromId); @@ -73,15 +89,25 @@ public async Task> GetAccountLogAsync(Date parameters.AddOptionalParameter("to", toId); var weight = limit == null ? 3 : limit <= 25 ? 1 : limit <= 50 ? 2 : limit <= 1000 ? 3 : limit <= 5000 ? 6 : 10; - return await _baseClient.ExecuteBase(new Uri(_baseClient.BaseAddress.AppendPath("api/history/v3/account-log")), HttpMethod.Get, ct, parameters, signed: true, requestWeight: weight).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "api/history/v3/account-log", KrakenExchange.RateLimiter.FuturesApi, weight, true); + return await _baseClient.SendRawAsync(request, null, ct).ConfigureAwait(false); } + #endregion + + #region Get Fee Schedule Volume + /// public async Task>> GetFeeScheduleVolumeAsync(CancellationToken ct = default) { - return await _baseClient.Execute>(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/feeschedules/volumes")), HttpMethod.Get, ct, signed: true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/feeschedules/volumes", KrakenExchange.RateLimiter.FuturesApi, 1, true); + return await _baseClient.SendAsync>(request, null, ct).ConfigureAwait(false); } + #endregion + + #region Get Initial Margin Requirements + /// public async Task> GetInitialMarginRequirementsAsync(string symbol, FuturesOrderType orderType, OrderSide side, decimal quantity, decimal? price = null, CancellationToken ct = default) { @@ -92,7 +118,8 @@ public async Task> GetInitialMarg parameters.AddString("size", quantity); parameters.AddOptionalString("limitPrice", price); - var result = await _baseClient.ExecuteBase(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/initialmargin")), HttpMethod.Get, ct, parameters, signed: true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/initialmargin", KrakenExchange.RateLimiter.FuturesApi, 1, true); + var result = await _baseClient.SendRawAsync (request, null, ct).ConfigureAwait(false); if (!result) return result.As(null); @@ -106,6 +133,10 @@ public async Task> GetInitialMarg }); } + #endregion + + #region Get Max Order Quantity + /// public async Task> GetMaxOrderQuantityAsync(string symbol, FuturesOrderType orderType, decimal? price = null, CancellationToken ct = default) { @@ -114,7 +145,8 @@ public async Task> GetMaxOrderQuantityA parameters.AddEnum("orderType", orderType); parameters.AddOptionalString("limitPrice", price); - var result = await _baseClient.ExecuteBase(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/initialmargin/maxordersize")), HttpMethod.Get, ct, parameters, signed: true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/initialmargin/maxordersize", KrakenExchange.RateLimiter.FuturesApi, 1, true); + var result = await _baseClient.SendRawAsync(request, null, ct).ConfigureAwait(false); if (!result) return result.As(null); @@ -129,5 +161,7 @@ public async Task> GetMaxOrderQuantityA MaxSellQuantity = result.Data.MaxSellQuantity }); } + + #endregion } } diff --git a/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiExchangeData.cs b/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiExchangeData.cs index aae9888..43e66b5 100644 --- a/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiExchangeData.cs +++ b/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiExchangeData.cs @@ -1,13 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading.Tasks; -using System.Threading; -using CryptoExchange.Net.Objects; -using CryptoExchange.Net; -using Kraken.Net.Objects.Models.Futures; +using Kraken.Net.Objects.Models.Futures; using Kraken.Net.Enums; -using CryptoExchange.Net.Converters; using Kraken.Net.Interfaces.Clients.FuturesApi; namespace Kraken.Net.Clients.FuturesApi @@ -15,6 +7,7 @@ namespace Kraken.Net.Clients.FuturesApi /// internal class KrakenRestClientFuturesApiExchangeData : IKrakenRestClientFuturesApiExchangeData { + private static readonly RequestDefinitionCache _definitions = new RequestDefinitionCache(); private readonly KrakenRestClientFuturesApi _baseClient; internal KrakenRestClientFuturesApiExchangeData(KrakenRestClientFuturesApi baseClient) @@ -22,10 +15,13 @@ internal KrakenRestClientFuturesApiExchangeData(KrakenRestClientFuturesApi baseC _baseClient = baseClient; } + #region Get Platform Notificiations + /// public async Task> GetPlatformNotificationsAsync(CancellationToken ct = default) { - var result = await _baseClient.Execute(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/notifications")), HttpMethod.Get, ct, signed: true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/notifications", KrakenExchange.RateLimiter.FuturesApi, 1, true); + var result = await _baseClient.SendRawAsync(request, null, ct).ConfigureAwait(false); if (!result) return result.AsError(result.Error!); @@ -36,68 +32,122 @@ public async Task> GetPla }); } + #endregion + + #region Get Historical Funding Rates + /// public async Task>> GetHistoricalFundingRatesAsync(string symbol, CancellationToken ct = default) { - var parameters = new Dictionary + var parameters = new ParameterCollection { { "symbol", symbol } }; - return await _baseClient.Execute>(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v4/historicalfundingrates")), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v4/historicalfundingrates", KrakenExchange.RateLimiter.FuturesApi, 1, false); + return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Fee Schedules + /// public async Task>> GetFeeSchedulesAsync(CancellationToken ct = default) { - return await _baseClient.Execute>(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/feeschedules")), HttpMethod.Get, ct).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/feeschedules", KrakenExchange.RateLimiter.FuturesApi, 1, false); + return await _baseClient.SendAsync>(request, null, ct).ConfigureAwait(false); } + #endregion + + #region Get Symbols + /// public async Task>> GetSymbolsAsync(CancellationToken ct = default) { - return await _baseClient.Execute>(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/instruments")), HttpMethod.Get, ct).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/instruments", KrakenExchange.RateLimiter.FuturesApi, 1, false); + return await _baseClient.SendAsync>(request, null, ct).ConfigureAwait(false); } + #endregion + + #region Get Symbol Status + /// public async Task>> GetSymbolStatusAsync(CancellationToken ct = default) { - return await _baseClient.Execute>(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/instruments/status")), HttpMethod.Get, ct).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/instruments/status", KrakenExchange.RateLimiter.FuturesApi, 1, false); + return await _baseClient.SendAsync>(request, null, ct).ConfigureAwait(false); } + #endregion + + #region Get Trades + /// public async Task>> GetTradesAsync(string symbol, DateTime? startTime = null, CancellationToken ct = default) { - var parameters = new Dictionary() + var parameters = new ParameterCollection() { { "symbol", symbol } }; parameters.AddOptionalParameter("lastTime", startTime?.ToString("u").Replace(" ", "T")); - return await _baseClient.Execute>(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/history")), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/history", KrakenExchange.RateLimiter.FuturesApi, 1, false); + return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Order Book + /// public async Task> GetOrderBookAsync(string symbol, CancellationToken ct = default) { - var parameters = new Dictionary() + var parameters = new ParameterCollection() { { "symbol", symbol } }; - return await _baseClient.Execute(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/orderbook")), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/orderbook", KrakenExchange.RateLimiter.FuturesApi, 1, false); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Tickers + + /// + public async Task> GetTickerAsync(string symbol, CancellationToken ct = default) + { + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/tickers/" + symbol, KrakenExchange.RateLimiter.FuturesApi, 1, false); + return await _baseClient.SendAsync(request, null, ct).ConfigureAwait(false); + } + + #endregion + + #region Get Tickers + /// public async Task>> GetTickersAsync(CancellationToken ct = default) { - return await _baseClient.Execute>(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/tickers")), HttpMethod.Get, ct).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/tickers", KrakenExchange.RateLimiter.FuturesApi, 1, false); + return await _baseClient.SendAsync>(request, null, ct).ConfigureAwait(false); } + #endregion + + #region Get Klines + /// public async Task> GetKlinesAsync(TickType tickType, string symbol, FuturesKlineInterval interval, DateTime? startTime = null, DateTime? endTime = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("from", DateTimeConverter.ConvertToSeconds(startTime)); parameters.AddOptionalParameter("to", DateTimeConverter.ConvertToSeconds(endTime)); - return await _baseClient.ExecuteBase(new Uri(_baseClient.BaseAddress.AppendPath($"api/charts/v1/{EnumConverter.GetString(tickType)}/{symbol}/{EnumConverter.GetString(interval)}")), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Get, $"api/charts/v1/{EnumConverter.GetString(tickType)}/{symbol}/{EnumConverter.GetString(interval)}", KrakenExchange.RateLimiter.FuturesApi, 1, false); + return await _baseClient.SendRawAsync(request, parameters, ct).ConfigureAwait(false); } + + #endregion } } diff --git a/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiShared.cs b/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiShared.cs index 48f2f8d..e74b029 100644 --- a/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiShared.cs +++ b/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiShared.cs @@ -1,17 +1,657 @@ using CryptoExchange.Net.SharedApis; -using Kraken.Net.Interfaces.Clients.SpotApi; +using Kraken.Net.Enums; +using Kraken.Net.Interfaces.Clients.FuturesApi; +using System.Linq; namespace Kraken.Net.Clients.FuturesApi { internal partial class KrakenRestClientFuturesApi : IKrakenRestClientFuturesApiShared { public string Exchange => KrakenExchange.ExchangeName; - public TradingMode[] SupportedTradingModes { get; } = new TradingMode[] { }; + public TradingMode[] SupportedTradingModes { get; } = new[] { TradingMode.PerpetualLinear, TradingMode.DeliveryLinear, TradingMode.PerpetualInverse, TradingMode.DeliveryInverse }; public void SetDefaultExchangeParameter(string key, object value) => ExchangeParameters.SetStaticParameter(Exchange, key, value); public void ResetDefaultExchangeParameters() => ExchangeParameters.ResetStaticParameters(); - // TODO implement after library update + #region Balance Client + EndpointOptions IBalanceRestClient.GetBalancesOptions { get; } = new EndpointOptions(true) + { + RequestNotes = "Returns the flex/MultiAssetCollateral balances, for individual margin accounts pass " + }; + async Task>> IBalanceRestClient.GetBalancesAsync(GetBalancesRequest request, CancellationToken ct) + { + var validationError = ((IBalanceRestClient)this).GetBalancesOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult>(Exchange, validationError); + + var result = await Account.GetBalancesAsync(ct: ct).ConfigureAwait(false); + if (!result) + return result.AsExchangeResult>(Exchange, null, default); + + var setting = ExchangeParameters.GetValue(request.ExchangeParameters, ExchangeName, "MultiAssetCollateral"); + if (setting == false) + { + var balances = new List(); + foreach(var balance in result.Data.MarginAccounts) + { + foreach(var currency in balance.Balances) + balances.Add(new SharedBalance(currency.Key, currency.Value, currency.Value) { IsolatedMarginSymbol = balance.Symbol }); + } + + return result.AsExchangeResult>(Exchange, SupportedTradingModes, balances); + } + else + return result.AsExchangeResult>(Exchange, SupportedTradingModes, result.Data.MultiCollateralMarginAccount.Currencies.Select(x => new SharedBalance(x.Key, x.Value.Available, x.Value.Quantity)).ToArray()); + } + + #endregion + + #region Klines client + + GetKlinesOptions IKlineRestClient.GetKlinesOptions { get; } = new GetKlinesOptions(SharedPaginationSupport.Ascending, false) + { + MaxRequestDataPoints = 5000 + }; + + async Task>> IKlineRestClient.GetKlinesAsync(GetKlinesRequest request, INextPageToken? pageToken, CancellationToken ct) + { + var interval = (Enums.FuturesKlineInterval)request.Interval; + if (!Enum.IsDefined(typeof(Enums.FuturesKlineInterval), interval)) + return new ExchangeWebResult>(Exchange, new ArgumentError("Interval not supported")); + + var validationError = ((IKlineRestClient)this).GetKlinesOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult>(Exchange, validationError); + + // Determine pagination + // Data is normally returned oldest first, so to do newest first pagination we have to do some calc + DateTime endTime = request.EndTime ?? DateTime.UtcNow; + DateTime? startTime = request.StartTime; + if (pageToken is DateTimeToken dateTimeToken) + startTime = dateTimeToken.LastTime; + + var limit = request.Limit ?? 5000; + if (startTime != null) { + var klineCount = Math.Ceiling((endTime - startTime.Value).TotalSeconds / (int)interval); + if (klineCount > limit) + endTime = startTime.Value.AddSeconds((limit * (int)interval) - 1); + } + + var result = await ExchangeData.GetKlinesAsync( + Enums.TickType.Trade, + request.Symbol.GetSymbol(FormatSymbol), + interval, + startTime, + endTime, + ct: ct + ).ConfigureAwait(false); + if (!result) + return result.AsExchangeResult>(Exchange, null, default); + + // Get next token + DateTimeToken? nextToken = null; + if (result.Data.MoreKlines) + { + var maxOpenTime = result.Data.Klines.Max(x => x.Timestamp); + if (request.EndTime == null || maxOpenTime < request.EndTime.Value) + nextToken = new DateTimeToken(maxOpenTime.AddSeconds(1)); + } + + return result.AsExchangeResult>(Exchange, request.Symbol.TradingMode, result.Data.Klines.Select(x => new SharedKline(x.Timestamp, x.ClosePrice, x.HighPrice, x.LowPrice, x.OpenPrice, x.Volume)).ToArray(), nextToken); + } + + #endregion + + #region Order Book client + GetOrderBookOptions IOrderBookRestClient.GetOrderBookOptions { get; } = new GetOrderBookOptions(0, 1000, false); + async Task> IOrderBookRestClient.GetOrderBookAsync(GetOrderBookRequest request, CancellationToken ct) + { + var validationError = ((IOrderBookRestClient)this).GetOrderBookOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult(Exchange, validationError); + + var result = await ExchangeData.GetOrderBookAsync( + request.Symbol.GetSymbol(FormatSymbol), + ct: ct).ConfigureAwait(false); + if (!result) + return result.AsExchangeResult(Exchange, null, default); + + var asks = result.Data.Asks; + var bids = result.Data.Bids; + if (request.Limit != null) + { + asks = asks.Take(request.Limit.Value).ToArray(); + bids = bids.Take(request.Limit.Value).ToArray(); + } + + return result.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedOrderBook(asks, bids)); + } + + #endregion + + #region Recent Trade client + + GetRecentTradesOptions IRecentTradeRestClient.GetRecentTradesOptions { get; } = new GetRecentTradesOptions(100, false); + async Task>> IRecentTradeRestClient.GetRecentTradesAsync(GetRecentTradesRequest request, CancellationToken ct) + { + var validationError = ((IRecentTradeRestClient)this).GetRecentTradesOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult>(Exchange, validationError); + + var result = await ExchangeData.GetTradesAsync( + request.Symbol.GetSymbol(FormatSymbol), + ct: ct).ConfigureAwait(false); + if (!result) + return result.AsExchangeResult>(Exchange, null, default); + + var data = result.Data; + if (request.Limit != null) + data = data.Take(request.Limit.Value).ToArray(); + + return result.AsExchangeResult>(Exchange, request.Symbol.TradingMode, data.Select(x => new SharedTrade(x.Quantity!.Value, x.Price, x.Timestamp!.Value)).ToArray()); + } + + #endregion + + #region Funding Rate client + GetFundingRateHistoryOptions IFundingRateRestClient.GetFundingRateHistoryOptions { get; } = new GetFundingRateHistoryOptions(SharedPaginationSupport.NotSupported, false); + + async Task>> IFundingRateRestClient.GetFundingRateHistoryAsync(GetFundingRateHistoryRequest request, INextPageToken? pageToken, CancellationToken ct) + { + var validationError = ((IFundingRateRestClient)this).GetFundingRateHistoryOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult>(Exchange, validationError); + + // Get data + var result = await ExchangeData.GetHistoricalFundingRatesAsync( + request.Symbol.GetSymbol(FormatSymbol), + ct: ct).ConfigureAwait(false); + if (!result) + return result.AsExchangeResult>(Exchange, null, default); + + var data = result.Data; + if (request.StartTime != null) + data = data.Where(x => x.Timestamp >= request.StartTime.Value); + if (request.EndTime != null) + data = data.Where(x => x.Timestamp <= request.EndTime.Value); + + // Return + return result.AsExchangeResult>(Exchange, request.Symbol.TradingMode, data.Select(x => new SharedFundingRate(x.FundingRate, x.Timestamp)).ToArray()); + } + #endregion + + #region Futures Symbol client + + EndpointOptions IFuturesSymbolRestClient.GetFuturesSymbolsOptions { get; } = new EndpointOptions(false); + async Task>> IFuturesSymbolRestClient.GetFuturesSymbolsAsync(GetSymbolsRequest request, CancellationToken ct) + { + var validationError = ((IFuturesSymbolRestClient)this).GetFuturesSymbolsOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult>(Exchange, validationError); + + var result = await ExchangeData.GetSymbolsAsync(ct).ConfigureAwait(false); + if (!result) + return result.AsExchangeResult>(Exchange, null, default); + + var data = result.Data.Where(x => x.Type != Enums.SymbolType.SpotIndex && !x.Symbol.StartsWith("rr")); + var resultData = data.Select(s => + { + var split = s.Symbol.Split('_'); + var assets = split[1]; + + return new SharedFuturesSymbol( + s.Type == Enums.SymbolType.FlexibleFutures && split.Count() == 2 ? SharedSymbolType.PerpetualLinear : + s.Type == Enums.SymbolType.FlexibleFutures && split.Count() > 2 ? SharedSymbolType.DeliveryLinear : + s.Type == Enums.SymbolType.InverseFutures && split.Count() == 2 ? SharedSymbolType.PerpetualInverse : + SharedSymbolType.DeliveryInverse, + assets.Substring(0, assets.Length - 3), + assets.Substring(assets.Length - 3), + s.Symbol, + s.Tradeable) + { + QuantityDecimals = (int?)s.ContractValueTradePrecision, + PriceStep = s.TickSize, + ContractSize = s.ContractSize, + DeliveryTime = split.Count() > 2 ? DateTime.ParseExact(split[2], "yyMMdd", CultureInfo.InvariantCulture) : null + }; + }); + + if (request.TradingMode != null) + resultData = resultData.Where(x => request.TradingMode == TradingMode.PerpetualLinear ? x.SymbolType == SharedSymbolType.PerpetualLinear : + request.TradingMode == TradingMode.DeliveryLinear ? x.SymbolType == SharedSymbolType.DeliveryLinear : + request.TradingMode == TradingMode.PerpetualInverse ? x.SymbolType == SharedSymbolType.PerpetualInverse : + x.SymbolType == SharedSymbolType.DeliveryInverse); + + return result.AsExchangeResult>(Exchange, request.TradingMode == null ? SupportedTradingModes : new[] { request.TradingMode.Value }, resultData.ToArray()); + } + + #endregion + + #region Ticker client + + EndpointOptions IFuturesTickerRestClient.GetFuturesTickerOptions { get; } = new EndpointOptions(false); + async Task> IFuturesTickerRestClient.GetFuturesTickerAsync(GetTickerRequest request, CancellationToken ct) + { + var validationError = ((IFuturesTickerRestClient)this).GetFuturesTickerOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult(Exchange, validationError); + + var resultTicker = await ExchangeData.GetTickerAsync(request.Symbol.GetSymbol(FormatSymbol), ct).ConfigureAwait(false); + if (!resultTicker) + return resultTicker.AsExchangeResult(Exchange, null, default); + + var time = DateTime.UtcNow; + + return resultTicker.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedFuturesTicker(resultTicker.Data.Symbol, resultTicker.Data.LastPrice ?? 0, 0, 0, resultTicker.Data.Volume24h, (resultTicker.Data.OpenPrice24h > 0 && resultTicker.Data.LastPrice > 0) ? Math.Round(resultTicker.Data.LastPrice.Value / resultTicker.Data.OpenPrice24h.Value * 100 - 100, 2) : null) + { + MarkPrice = resultTicker.Data.MarkPrice, + IndexPrice = resultTicker.Data.IndexPrice, + FundingRate = resultTicker.Data.FundingRate, + NextFundingTime = resultTicker.Data.FundingRate == null ? null : new DateTime(time.Year, time.Month, time.Day, time.Hour, 0, 0, DateTimeKind.Utc).AddHours(1) + }); + } + + EndpointOptions IFuturesTickerRestClient.GetFuturesTickersOptions { get; } = new EndpointOptions(false); + async Task>> IFuturesTickerRestClient.GetFuturesTickersAsync(GetTickersRequest request, CancellationToken ct) + { + var validationError = ((IFuturesTickerRestClient)this).GetFuturesTickersOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult>(Exchange, validationError); + + var resultTicker = await ExchangeData.GetTickersAsync(ct).ConfigureAwait(false); + if (!resultTicker) + return resultTicker.AsExchangeResult>(Exchange, null, default); + + var data = resultTicker.Data.Where(x => !x.Symbol.StartsWith("rr_") && !x.Symbol.StartsWith("in_")); + if (request.TradingMode != null) + { + data = data.Where(x => request.TradingMode == TradingMode.PerpetualLinear ? x.Symbol.StartsWith("PF_") : + request.TradingMode == TradingMode.DeliveryLinear ? x.Symbol.StartsWith("FI_") : + request.TradingMode == TradingMode.PerpetualInverse ? x.Symbol.StartsWith("PI_") : + x.Symbol.StartsWith("FF")); + } + + var time = DateTime.UtcNow; + return resultTicker.AsExchangeResult>(Exchange, request.TradingMode == null ? SupportedTradingModes : new[] { request.TradingMode.Value }, data.Select(x => new SharedFuturesTicker(x.Symbol, x.LastPrice ?? 0, 0, 0, x.Volume24h, (x.OpenPrice24h > 0 && x.LastPrice > 0) ? Math.Round(x.LastPrice.Value / x.OpenPrice24h.Value * 100 - 100, 2) : null) + { + MarkPrice = x.MarkPrice, + IndexPrice = x.IndexPrice, + FundingRate = x.FundingRate, + NextFundingTime = x.FundingRate == null ? null : new DateTime(time.Year, time.Month, time.Day, time.Hour, 0, 0, DateTimeKind.Utc).AddHours(1) + }).ToArray()); + } + + #endregion + + #region Mark Price Klines client + + GetKlinesOptions IMarkPriceKlineRestClient.GetMarkPriceKlinesOptions { get; } = new GetKlinesOptions(SharedPaginationSupport.Ascending, false) + { + MaxRequestDataPoints = 5000 + }; + + async Task>> IMarkPriceKlineRestClient.GetMarkPriceKlinesAsync(GetKlinesRequest request, INextPageToken? pageToken, CancellationToken ct) + { + var interval = (Enums.FuturesKlineInterval)request.Interval; + if (!Enum.IsDefined(typeof(Enums.FuturesKlineInterval), interval)) + return new ExchangeWebResult>(Exchange, new ArgumentError("Interval not supported")); + + var validationError = ((IMarkPriceKlineRestClient)this).GetMarkPriceKlinesOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult>(Exchange, validationError); + + // Determine pagination + // Data is normally returned oldest first, so to do newest first pagination we have to do some calc + DateTime endTime = request.EndTime ?? DateTime.UtcNow; + DateTime? startTime = request.StartTime; + if (pageToken is DateTimeToken dateTimeToken) + startTime = dateTimeToken.LastTime; + + var limit = request.Limit ?? 5000; + if (startTime != null) + { + var klineCount = Math.Ceiling((endTime - startTime.Value).TotalSeconds / (int)interval); + if (klineCount > limit) + endTime = startTime.Value.AddSeconds((limit * (int)interval) - 1); + } + + var result = await ExchangeData.GetKlinesAsync( + Enums.TickType.Mark, + request.Symbol.GetSymbol(FormatSymbol), + interval, + startTime, + endTime, + ct: ct + ).ConfigureAwait(false); + if (!result) + return result.AsExchangeResult>(Exchange, null, default); + + // Get next token + DateTimeToken? nextToken = null; + if (result.Data.MoreKlines) + { + var maxOpenTime = result.Data.Klines.Max(x => x.Timestamp); + if (request.EndTime == null || maxOpenTime < request.EndTime.Value) + nextToken = new DateTimeToken(maxOpenTime.AddSeconds(1)); + } + + return result.AsExchangeResult>(Exchange, request.Symbol.TradingMode, result.Data.Klines.Select(x => new SharedFuturesKline(x.Timestamp, x.ClosePrice, x.HighPrice, x.LowPrice, x.OpenPrice)).ToArray(), nextToken); + } + + #endregion + + #region Open Interest client + + EndpointOptions IOpenInterestRestClient.GetOpenInterestOptions { get; } = new EndpointOptions(true); + async Task> IOpenInterestRestClient.GetOpenInterestAsync(GetOpenInterestRequest request, CancellationToken ct) + { + var validationError = ((IOpenInterestRestClient)this).GetOpenInterestOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult(Exchange, validationError); + + var result = await ExchangeData.GetTickerAsync(request.Symbol.GetSymbol(FormatSymbol), ct: ct).ConfigureAwait(false); + if (!result) + return result.AsExchangeResult(Exchange, null, default); + + return result.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedOpenInterest(result.Data.OpenInterest)); + } + + #endregion + + #region Leverage client + SharedLeverageSettingMode ILeverageRestClient.LeverageSettingType => SharedLeverageSettingMode.PerSymbol; + + EndpointOptions ILeverageRestClient.GetLeverageOptions { get; } = new EndpointOptions(true); + async Task> ILeverageRestClient.GetLeverageAsync(GetLeverageRequest request, CancellationToken ct) + { + var validationError = ((ILeverageRestClient)this).GetLeverageOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult(Exchange, validationError); + + var result = await Trading.GetLeverageAsync(ct: ct).ConfigureAwait(false); + if (!result) + return result.AsExchangeResult(Exchange, null, default); + + var symbolLeverage = result.Data.SingleOrDefault(x => x.Symbol == request.Symbol.GetSymbol(FormatSymbol)); + if (symbolLeverage == null) + return result.AsExchangeError(Exchange, new ServerError("Not found")); + + return result.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedLeverage(symbolLeverage.MaxLeverage) + { + Side = request.PositionSide + }); + } + + SetLeverageOptions ILeverageRestClient.SetLeverageOptions { get; } = new SetLeverageOptions(); + async Task> ILeverageRestClient.SetLeverageAsync(SetLeverageRequest request, CancellationToken ct) + { + var validationError = ((ILeverageRestClient)this).SetLeverageOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult(Exchange, validationError); + + var result = await Trading.SetLeverageAsync(symbol: request.Symbol.GetSymbol(FormatSymbol), request.Leverage, ct: ct).ConfigureAwait(false); + if (!result) + return result.AsExchangeResult(Exchange, null, default); + + return result.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedLeverage(request.Leverage)); + } + #endregion + + #region Futures Order Client + + SharedFeeDeductionType IFuturesOrderRestClient.FuturesFeeDeductionType => SharedFeeDeductionType.AddToCost; + SharedFeeAssetType IFuturesOrderRestClient.FuturesFeeAssetType => SharedFeeAssetType.QuoteAsset; + + IEnumerable IFuturesOrderRestClient.FuturesSupportedOrderTypes { get; } = new[] { SharedOrderType.Limit, SharedOrderType.Market }; + IEnumerable IFuturesOrderRestClient.FuturesSupportedTimeInForce { get; } = new[] { SharedTimeInForce.GoodTillCanceled, SharedTimeInForce.ImmediateOrCancel }; + SharedQuantitySupport IFuturesOrderRestClient.FuturesSupportedOrderQuantity { get; } = new SharedQuantitySupport( + SharedQuantityType.Contracts, + SharedQuantityType.Contracts, + SharedQuantityType.Contracts, + SharedQuantityType.Contracts); + + PlaceFuturesOrderOptions IFuturesOrderRestClient.PlaceFuturesOrderOptions { get; } = new PlaceFuturesOrderOptions(); + async Task> IFuturesOrderRestClient.PlaceFuturesOrderAsync(PlaceFuturesOrderRequest request, CancellationToken ct) + { + var validationError = ((IFuturesOrderRestClient)this).PlaceFuturesOrderOptions.ValidateRequest( + Exchange, + request, + request.Symbol.TradingMode, + SupportedTradingModes, + ((IFuturesOrderRestClient)this).FuturesSupportedOrderTypes, + ((IFuturesOrderRestClient)this).FuturesSupportedTimeInForce, + ((IFuturesOrderRestClient)this).FuturesSupportedOrderQuantity); + if (validationError != null) + return new ExchangeWebResult(Exchange, validationError); + + var result = await Trading.PlaceOrderAsync( + request.Symbol.GetSymbol(FormatSymbol), + request.Side == SharedOrderSide.Buy ? Enums.OrderSide.Buy : Enums.OrderSide.Sell, + GetOrderType(request.OrderType, request.TimeInForce), + quantity: request.Quantity ?? 0, + price: request.Price, + reduceOnly: request.ReduceOnly, + clientOrderId: request.ClientOrderId, + ct: ct).ConfigureAwait(false); + + if (!result) + return result.AsExchangeResult(Exchange, null, default); + + return result.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedId(result.Data.OrderId.ToString())); + } + + EndpointOptions IFuturesOrderRestClient.GetFuturesOrderOptions { get; } = new EndpointOptions(true); + async Task> IFuturesOrderRestClient.GetFuturesOrderAsync(GetOrderRequest request, CancellationToken ct) + { + var validationError = ((IFuturesOrderRestClient)this).GetFuturesOrderOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult(Exchange, validationError); + + var order = await Trading.GetOrderAsync(request.OrderId, ct: ct).ConfigureAwait(false); + if (!order) + return order.AsExchangeResult(Exchange, null, default); + + return order.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedFuturesOrder( + order.Data.Order.Symbol, + order.Data.Order.OrderId, + SharedOrderType.Other, + order.Data.Order.Side == OrderSide.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell, + ParseOrderStatus(order.Data.Status), + order.Data.Order.Timestamp) + { + ClientOrderId = order.Data.Order.ClientOrderId, + OrderPrice = order.Data.Order.Price == 0 ? null : order.Data.Order.Price, + Quantity = order.Data.Order.Quantity == 0 ? null : order.Data.Order.Quantity, + QuantityFilled = order.Data.Order.QuantityFilled, + UpdateTime = order.Data.Order.LastUpdateTime, + ReduceOnly = order.Data.Order.ReduceOnly + }); + } + + EndpointOptions IFuturesOrderRestClient.GetOpenFuturesOrdersOptions { get; } = new EndpointOptions(true); + async Task>> IFuturesOrderRestClient.GetOpenFuturesOrdersAsync(GetOpenOrdersRequest request, CancellationToken ct) + { + var validationError = ((IFuturesOrderRestClient)this).GetOpenFuturesOrdersOptions.ValidateRequest(Exchange, request, request.Symbol?.TradingMode ?? request.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult>(Exchange, validationError); + + var symbol = request.Symbol?.GetSymbol(FormatSymbol); + var orders = await Trading.GetOpenOrdersAsync(ct: ct).ConfigureAwait(false); + if (!orders) + return orders.AsExchangeResult>(Exchange, null, default); + + var data = orders.Data; + if (symbol != null) + data = data.Where(x => x.Symbol == symbol); + + return orders.AsExchangeResult>(Exchange, request.Symbol == null ? SupportedTradingModes : new[] { request.Symbol.TradingMode }, data.Select(x => new SharedFuturesOrder( + x.Symbol, + x.OrderId.ToString(), + x.Type == FuturesOrderType.Limit ? SharedOrderType.Limit : SharedOrderType.Other, + x.Side == OrderSide.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell, + SharedOrderStatus.Open, + x.Timestamp) + { + ClientOrderId = x.ClientOrderId, + OrderPrice = x.Price == 0 ? null : x.Price, + Quantity = x.Quantity, + QuantityFilled = x.QuantityFilled, + UpdateTime = x.LastUpdateTime, + ReduceOnly = x.ReduceOnly + }).ToArray()); + } + + PaginatedEndpointOptions IFuturesOrderRestClient.GetClosedFuturesOrdersOptions { get; } = new PaginatedEndpointOptions(SharedPaginationSupport.NotSupported, true) + { + RequestNotes = "There is no way to retrieve closed orders without specifying order ids, so this method can never return success" + }; + Task>> IFuturesOrderRestClient.GetClosedFuturesOrdersAsync(GetClosedOrdersRequest request, INextPageToken? pageToken, CancellationToken ct) + { + return Task.FromResult(new ExchangeWebResult>(Exchange, new InvalidOperationError($"Method not available for {Exchange}"))); + } + + EndpointOptions IFuturesOrderRestClient.GetFuturesOrderTradesOptions { get; } = new EndpointOptions(true) + { + RequestNotes = "There is no way to retrieve trades for a specific order, so this method can never return success" + }; + Task>> IFuturesOrderRestClient.GetFuturesOrderTradesAsync(GetOrderTradesRequest request, CancellationToken ct) + { + return Task.FromResult(new ExchangeWebResult>(Exchange, new InvalidOperationError($"Method not available for {Exchange}"))); + } + + PaginatedEndpointOptions IFuturesOrderRestClient.GetFuturesUserTradesOptions { get; } = new PaginatedEndpointOptions(SharedPaginationSupport.Ascending, true); + async Task>> IFuturesOrderRestClient.GetFuturesUserTradesAsync(GetUserTradesRequest request, INextPageToken? pageToken, CancellationToken ct) + { + var validationError = ((IFuturesOrderRestClient)this).GetFuturesUserTradesOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult>(Exchange, validationError); + + // Determine page token + DateTime? fromTime = null; + if (pageToken is DateTimeToken fromToken) + fromTime = fromToken.LastTime; + + // Get data + var orders = await Trading.GetUserTradesAsync( + startTime: request.StartTime, + ct: ct + ).ConfigureAwait(false); + if (!orders) + return orders.AsExchangeResult>(Exchange, null, default); + + // Get next token + DateTimeToken? nextToken = null; + if (orders.Data.Any()) + nextToken = new DateTimeToken(orders.Data.Max(o => o.FillTime).AddMilliseconds(1)); + + return orders.AsExchangeResult>(Exchange, request.Symbol.TradingMode, orders.Data.Select(x => new SharedUserTrade( + x.Symbol, + x.OrderId.ToString(), + x.Id.ToString(), + x.Side == OrderSide.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell, + x.Quantity, + x.Price, + x.FillTime) + { + Price = x.Price, + Quantity = x.Quantity, + Role = x.Type == TradeType.Maker ? SharedRole.Maker : SharedRole.Taker + }).ToArray(), nextToken); + } + + EndpointOptions IFuturesOrderRestClient.CancelFuturesOrderOptions { get; } = new EndpointOptions(true); + async Task> IFuturesOrderRestClient.CancelFuturesOrderAsync(CancelOrderRequest request, CancellationToken ct) + { + var validationError = ((IFuturesOrderRestClient)this).CancelFuturesOrderOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult(Exchange, validationError); + + var order = await Trading.CancelOrderAsync(request.OrderId, ct: ct).ConfigureAwait(false); + if (!order) + return order.AsExchangeResult(Exchange, null, default); + + return order.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedId(order.Data.OrderId.ToString())); + } + + EndpointOptions IFuturesOrderRestClient.GetPositionsOptions { get; } = new EndpointOptions(true); + async Task>> IFuturesOrderRestClient.GetPositionsAsync(GetPositionsRequest request, CancellationToken ct) + { + var validationError = ((IFuturesOrderRestClient)this).GetPositionsOptions.ValidateRequest(Exchange, request, request.Symbol?.TradingMode ?? request.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult>(Exchange, validationError); + + var result = await Trading.GetOpenPositionsAsync(ct: ct).ConfigureAwait(false); + if (!result) + return result.AsExchangeResult>(Exchange, null, default); + + var data = result.Data; + if (request.Symbol != null) + data = data.Where(x => x.Symbol == request.Symbol.GetSymbol(FormatSymbol)); + + if (request.TradingMode.HasValue) + data = data.Where(x => request.TradingMode == TradingMode.PerpetualLinear ? x.Symbol.StartsWith("PF_") : + request.TradingMode == TradingMode.DeliveryLinear ? x.Symbol.StartsWith("FF_") : + request.TradingMode == TradingMode.PerpetualInverse ? x.Symbol.StartsWith("PI_") : + x.Symbol.StartsWith("FI_")); + + var resultTypes = request.Symbol == null && request.TradingMode == null ? SupportedTradingModes : request.Symbol != null ? new[] { request.Symbol.TradingMode } : new[] { request.TradingMode!.Value }; + return result.AsExchangeResult>(Exchange, resultTypes, data.Select(x => new SharedPosition(x.Symbol, Math.Abs(x.Quantity), x.FillTime) + { + Leverage = x.MaxFixedLeverage, + AverageOpenPrice = x.Price, + PositionSide = x.Side == PositionSide.Short ? SharedPositionSide.Short : SharedPositionSide.Long + }).ToArray()); + } + + EndpointOptions IFuturesOrderRestClient.ClosePositionOptions { get; } = new EndpointOptions(true) + { + RequiredOptionalParameters = new List + { + new ParameterDescription(nameof(ClosePositionRequest.PositionSide), typeof(SharedPositionSide), "The position side to close", SharedPositionSide.Long), + new ParameterDescription(nameof(ClosePositionRequest.Quantity), typeof(decimal), "Quantity of the position is required", 0.1m) + } + }; + async Task> IFuturesOrderRestClient.ClosePositionAsync(ClosePositionRequest request, CancellationToken ct) + { + var validationError = ((IFuturesOrderRestClient)this).ClosePositionOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeWebResult(Exchange, validationError); + + var symbol = request.Symbol.GetSymbol(FormatSymbol); + + var result = await Trading.PlaceOrderAsync( + symbol, + request.PositionSide == SharedPositionSide.Long ? OrderSide.Sell : OrderSide.Buy, + FuturesOrderType.Market, + quantity: request.Quantity!.Value, + reduceOnly: true, + ct: ct).ConfigureAwait(false); + if (!result) + return result.AsExchangeResult(Exchange, null, default); + + return result.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedId(result.Data.OrderId.ToString())); + } + + + private SharedOrderStatus ParseOrderStatus(KrakenFuturesOrderActiveStatus status) + { + if (status == KrakenFuturesOrderActiveStatus.EnteredBook) return SharedOrderStatus.Open; + if (status == KrakenFuturesOrderActiveStatus.FullyExecuted) return SharedOrderStatus.Filled; + return SharedOrderStatus.Canceled; + } + + private FuturesOrderType GetOrderType(SharedOrderType orderType, SharedTimeInForce? timeInForce) + { + if (orderType == SharedOrderType.Market) + return FuturesOrderType.Market; + + if (timeInForce == SharedTimeInForce.ImmediateOrCancel) + return FuturesOrderType.ImmediateOrCancel; + + return FuturesOrderType.Limit; + } + + #endregion } } diff --git a/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiTrading.cs b/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiTrading.cs index 72d5e57..85ed92f 100644 --- a/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiTrading.cs +++ b/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiTrading.cs @@ -1,15 +1,5 @@ using Kraken.Net.Objects.Models.Futures; -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading.Tasks; -using System.Threading; -using CryptoExchange.Net.Objects; -using CryptoExchange.Net; -using CryptoExchange.Net.Converters; using Kraken.Net.Enums; -using System.Globalization; -using System.Linq; using Kraken.Net.Interfaces.Clients.FuturesApi; namespace Kraken.Net.Clients.FuturesApi @@ -17,6 +7,7 @@ namespace Kraken.Net.Clients.FuturesApi /// internal class KrakenRestClientFuturesApiTrading : IKrakenRestClientFuturesApiTrading { + private static readonly RequestDefinitionCache _definitions = new RequestDefinitionCache(); private readonly KrakenRestClientFuturesApi _baseClient; internal KrakenRestClientFuturesApiTrading(KrakenRestClientFuturesApi baseClient) @@ -24,55 +15,87 @@ internal KrakenRestClientFuturesApiTrading(KrakenRestClientFuturesApi baseClient _baseClient = baseClient; } + #region Get User Trades + /// public async Task>> GetUserTradesAsync(DateTime? startTime = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("lastFillTime", startTime?.ToString("o")); var weight = startTime == null ? 2 : 25; - return await _baseClient.Execute>(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/fills")), HttpMethod.Get, ct, parameters, signed: true, weight: weight).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/fills", KrakenExchange.RateLimiter.FuturesApi, 1, true); + return await _baseClient.SendAsync>(request, null, ct).ConfigureAwait(false); } + #endregion + + #region Get Self Trade Strategy + /// public async Task> GetSelfTradeStrategyAsync(CancellationToken ct = default) { - return await _baseClient.Execute(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/self-trade-strategy")), HttpMethod.Get, ct, signed: true, weight: 2).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/self-trade-strategy", KrakenExchange.RateLimiter.FuturesApi, 2, true); + return await _baseClient.SendAsync(request, null, ct).ConfigureAwait(false); } + #endregion + + #region Set Self Trade Strategy + /// public async Task> SetSelfTradeStrategyAsync(SelfTradeStrategy strategy, CancellationToken ct = default) { - var parameters = new Dictionary() + var parameters = new ParameterCollection() { { "strategy", EnumConverter.GetString(strategy) } }; - return await _baseClient.Execute(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/self-trade-strategy")), HttpMethod.Put, ct, parameters, true, weight: 2).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Put, "derivatives/api/v3/self-trade-strategy", KrakenExchange.RateLimiter.FuturesApi, 2, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Open Positions + /// public async Task>> GetOpenPositionsAsync(CancellationToken ct = default) { - return await _baseClient.Execute>(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/openpositions")), HttpMethod.Get, ct, signed: true, weight: 2).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/openpositions", KrakenExchange.RateLimiter.FuturesApi, 2, true); + return await _baseClient.SendAsync>(request, null, ct).ConfigureAwait(false); } + #endregion + + #region Get Leverage + /// public async Task>> GetLeverageAsync(CancellationToken ct = default) { - return await _baseClient.Execute>(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/leveragepreferences")), HttpMethod.Get, ct, signed: true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/leveragepreferences", KrakenExchange.RateLimiter.FuturesApi, 1, true); + return await _baseClient.SendAsync>(request, null, ct).ConfigureAwait(false); } + #endregion + + #region Set Leverage + /// public async Task SetLeverageAsync(string symbol, decimal maxLeverage, CancellationToken ct = default) { - var parameters = new Dictionary() + var parameters = new ParameterCollection() { { "symbol", symbol }, { "maxLeverage", maxLeverage.ToString(CultureInfo.InvariantCulture) } }; - return await _baseClient.Execute(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/leveragepreferences")), HttpMethod.Put, ct, parameters, signed: true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Put, "derivatives/api/v3/leveragepreferences", KrakenExchange.RateLimiter.FuturesApi, 1, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Place Order + /// public async Task> PlaceOrderAsync( string symbol, @@ -87,7 +110,7 @@ public async Task> PlaceOrderAsync( TriggerSignal? triggerSignal = null, string? clientOrderId = null, CancellationToken ct = default) { - var parameters = new Dictionary() + var parameters = new ParameterCollection() { { "symbol", symbol }, { "orderType", EnumConverter.GetString(type) }, @@ -103,24 +126,60 @@ public async Task> PlaceOrderAsync( parameters.AddOptionalParameter("trailingStopMaxDeviation", trailingStopMaxDeviation?.ToString(CultureInfo.InvariantCulture)); parameters.AddOptionalParameter("triggerSignal", EnumConverter.GetString(triggerSignal)); - return await _baseClient.Execute(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/sendorder")), HttpMethod.Post, ct, parameters, signed: true, weight: 10).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "derivatives/api/v3/sendorder", KrakenExchange.RateLimiter.FuturesApi, 10, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Open Orders + /// public async Task>> GetOpenOrdersAsync(CancellationToken ct = default) { - return await _baseClient.Execute>(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/openorders")), HttpMethod.Get, ct, signed: true, weight: 2).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "derivatives/api/v3/openorders", KrakenExchange.RateLimiter.FuturesApi, 2, true); + return await _baseClient.SendAsync>(request, null, ct).ConfigureAwait(false); + } + + #endregion + + #region Get Order + + /// + public async Task> GetOrderAsync(string? orderId = null, string? clientOrderId = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddOptional("orderIds", orderId == null ? null : new object[] { orderId }); + parameters.AddOptional("cliOrdIds", clientOrderId == null ? null : new object[] { clientOrderId }); + var request = _definitions.GetOrCreate(HttpMethod.Post, "derivatives/api/v3/orders/status", KrakenExchange.RateLimiter.FuturesApi, 1, true); + var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); + if (!result) + return result.As(default); + + if (result.Data == null) + return result.AsError(new ServerError("Order not found")); + + return result.As(result.Data.Single()); } + #endregion + + #region Get Orders + /// public async Task>> GetOrdersAsync(IEnumerable? orderIds = null, IEnumerable? clientOrderIds = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("orderIds", orderIds?.ToArray()); parameters.AddOptionalParameter("cliOrdIds", clientOrderIds?.ToArray()); - return await _baseClient.Execute>(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/orders/status")), HttpMethod.Post, ct, parameters, signed: true, weight: 1).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "derivatives/api/v3/orders/status", KrakenExchange.RateLimiter.FuturesApi, 1, true); + return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Edit Order + /// public async Task> EditOrderAsync( string? orderId = null, @@ -132,7 +191,7 @@ public async Task> EditOrderAsync( decimal? trailingStopMaxDeviation = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("orderId", orderId); parameters.AddOptionalParameter("cliOrdId", clientOrderId); parameters.AddOptionalParameter("size", quantity?.ToString(CultureInfo.InvariantCulture)); @@ -141,45 +200,68 @@ public async Task> EditOrderAsync( parameters.AddOptionalParameter("trailingStopDeviationUnit", EnumConverter.GetString(trailingStopDeviationUnit)); parameters.AddOptionalParameter("trailingStopMaxDeviation", trailingStopMaxDeviation?.ToString(CultureInfo.InvariantCulture)); - return await _baseClient.Execute(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/editorder")), HttpMethod.Post, ct, parameters, signed: true, weight: 10).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "derivatives/api/v3/editorder", KrakenExchange.RateLimiter.FuturesApi, 10, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Cancel Order + /// public async Task> CancelOrderAsync(string? orderId = null, string? clientOrderId = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("order_id", orderId); parameters.AddOptionalParameter("cliOrdId", clientOrderId); - return await _baseClient.Execute(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/cancelorder")), HttpMethod.Post, ct, parameters, signed: true, weight: 10).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "derivatives/api/v3/cancelorder", KrakenExchange.RateLimiter.FuturesApi, 10, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Cancel All Orders + /// public async Task> CancelAllOrdersAsync(string? symbol = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("symbol", symbol); - return await _baseClient.Execute(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/cancelallorders")), HttpMethod.Post, ct, parameters, signed: true, weight: 25).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "derivatives/api/v3/cancelallorders", KrakenExchange.RateLimiter.FuturesApi, 25, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Cancel All Orders After + /// public async Task> CancelAllOrderAfterAsync(TimeSpan cancelAfter, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("timeout", (int)Math.Floor(cancelAfter.TotalSeconds)); - return await _baseClient.Execute(new Uri(_baseClient.BaseAddress.AppendPath("derivatives/api/v3/cancelallordersafter")), HttpMethod.Post, ct, parameters, signed: true, weight: 25).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "derivatives/api/v3/cancelallordersafter", KrakenExchange.RateLimiter.FuturesApi, 25, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Execution Events + /// public async Task> GetExecutionEventsAsync(DateTime? startTime = null, DateTime? endTime = null, string? sort = null, string? tradeable = null, string? continuationToken = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("before", DateTimeConverter.ConvertToMilliseconds(endTime)); parameters.AddOptionalParameter("since", DateTimeConverter.ConvertToMilliseconds(startTime)); parameters.AddOptionalParameter("sort", sort); parameters.AddOptionalParameter("tradeable", tradeable); parameters.AddOptionalParameter("continuationToken", continuationToken); - return await _baseClient.ExecuteBase(new Uri(_baseClient.BaseAddress.AppendPath("api/history/v3/executions")), HttpMethod.Get, ct, parameters, signed: true, requestWeight: 1).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "api/history/v3/executions", KrakenExchange.RateLimiter.FuturesApi, 1, true); + return await _baseClient.SendRawAsync(request, parameters, ct).ConfigureAwait(false); } + + #endregion } } diff --git a/Kraken.Net/Clients/FuturesApi/KrakenSocketClientFuturesApi.cs b/Kraken.Net/Clients/FuturesApi/KrakenSocketClientFuturesApi.cs index 5ca14b0..2d4f792 100644 --- a/Kraken.Net/Clients/FuturesApi/KrakenSocketClientFuturesApi.cs +++ b/Kraken.Net/Clients/FuturesApi/KrakenSocketClientFuturesApi.cs @@ -1,12 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using CryptoExchange.Net.Authentication; -using CryptoExchange.Net.Clients; +using CryptoExchange.Net.Clients; using CryptoExchange.Net.Converters.MessageParsing; -using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.Sockets; @@ -17,7 +10,6 @@ using Kraken.Net.Objects.Sockets.Queries; using Kraken.Net.Objects.Sockets.Subscriptions.Futures; using Kraken.Net.Objects.Sockets.Subscriptions.Spot; -using Microsoft.Extensions.Logging; namespace Kraken.Net.Clients.FuturesApi { @@ -46,6 +38,10 @@ internal KrakenSocketClientFuturesApi(ILogger logger, KrakenSocketOptions option } #endregion + protected override IByteMessageAccessor CreateAccessor() => new SystemTextJsonByteMessageAccessor(); + + protected override IMessageSerializer CreateSerializer() => new SystemTextJsonMessageSerializer(); + /// public IKrakenSocketClientFuturesApiShared SharedClient => this; @@ -86,6 +82,8 @@ protected override AuthenticationProvider CreateAuthenticationProvider(ApiCreden /// protected override Query? GetAuthenticationRequest(SocketConnection connection) => new KrakenFuturesAuthQuery(((KrakenFuturesAuthenticationProvider)AuthenticationProvider!).GetApiKey()); + #region Heartbeat + /// public async Task> SubscribeToHeartbeatUpdatesAsync(Action> handler, CancellationToken ct = default) { @@ -93,6 +91,10 @@ public async Task> SubscribeToHeartbeatUpdatesAsy return await SubscribeAsync(BaseAddress.AppendPath("ws/v1"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region Ticker + /// public Task> SubscribeToTickerUpdatesAsync(string symbol, Action> handler, CancellationToken ct = default) => SubscribeToTickerUpdatesAsync(new List { symbol }, handler, ct); @@ -104,6 +106,10 @@ public async Task> SubscribeToTickerUpdatesAsync( return await SubscribeAsync(BaseAddress.AppendPath("ws/v1"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region Trade + /// public Task> SubscribeToTradeUpdatesAsync(string symbol, Action>> handler, CancellationToken ct = default) => SubscribeToTradeUpdatesAsync(new List { symbol }, handler, ct); @@ -118,6 +124,10 @@ public async Task> SubscribeToTradeUpdatesAsync( return await SubscribeAsync(BaseAddress.AppendPath("ws/v1"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region Mini Ticker + /// public Task> SubscribeToMiniTickerUpdatesAsync(string symbol, Action> handler, CancellationToken ct = default) => SubscribeToMiniTickerUpdatesAsync(new List { symbol }, handler, ct); @@ -129,6 +139,10 @@ public async Task> SubscribeToMiniTickerUpdatesAs return await SubscribeAsync(BaseAddress.AppendPath("ws/v1"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region Order Book + /// public Task> SubscribeToOrderBookUpdatesAsync( string symbol, @@ -148,6 +162,10 @@ public async Task> SubscribeToOrderBookUpdatesAsy return await SubscribeAsync(BaseAddress.AppendPath("ws/v1"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region Open Orders + /// public async Task> SubscribeToOpenOrdersUpdatesAsync( bool verbose, @@ -159,6 +177,10 @@ public async Task> SubscribeToOpenOrdersUpdatesAs return await SubscribeAsync(BaseAddress.AppendPath("ws/v1"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region Account Log + /// public async Task> SubscribeToAccountLogUpdatesAsync( Action> snapshotHandler, @@ -169,6 +191,10 @@ public async Task> SubscribeToAccountLogUpdatesAs return await SubscribeAsync(BaseAddress.AppendPath("ws/v1"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region Open Position + /// public async Task> SubscribeToOpenPositionUpdatesAsync( Action> handler, @@ -178,6 +204,10 @@ public async Task> SubscribeToOpenPositionUpdates return await SubscribeAsync(BaseAddress.AppendPath("ws/v1"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region Balances + /// public async Task> SubscribeToBalanceUpdatesAsync( Action> handler, @@ -187,6 +217,10 @@ public async Task> SubscribeToBalanceUpdatesAsync return await SubscribeAsync(BaseAddress.AppendPath("ws/v1"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region User Trade + /// public async Task> SubscribeToUserTradeUpdatesAsync( Action> handler, @@ -196,6 +230,10 @@ public async Task> SubscribeToUserTradeUpdatesAsy return await SubscribeAsync(BaseAddress.AppendPath("ws/v1"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region Notification + /// public async Task> SubscribeToNotificationUpdatesAsync( Action> handler, @@ -204,5 +242,7 @@ public async Task> SubscribeToNotificationUpdates var subscription = new KrakenFuturesNotificationSubscription(_logger, handler); return await SubscribeAsync(BaseAddress.AppendPath("ws/v1"), subscription, ct).ConfigureAwait(false); } + + #endregion } } diff --git a/Kraken.Net/Clients/FuturesApi/KrakenSocketClientFuturesApiShared.cs b/Kraken.Net/Clients/FuturesApi/KrakenSocketClientFuturesApiShared.cs index e1a3a02..5c928a2 100644 --- a/Kraken.Net/Clients/FuturesApi/KrakenSocketClientFuturesApiShared.cs +++ b/Kraken.Net/Clients/FuturesApi/KrakenSocketClientFuturesApiShared.cs @@ -1,19 +1,189 @@ -using CryptoExchange.Net.SharedApis; +using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.SharedApis; +using Kraken.Net.Enums; using Kraken.Net.Interfaces.Clients.FuturesApi; -using System; -using System.Collections.Generic; -using System.Text; namespace Kraken.Net.Clients.FuturesApi { internal partial class KrakenSocketClientFuturesApi : IKrakenSocketClientFuturesApiShared { public string Exchange => KrakenExchange.ExchangeName; - public TradingMode[] SupportedTradingModes { get; } = new[] { TradingMode.Spot }; + public TradingMode[] SupportedTradingModes { get; } = new[] { TradingMode.PerpetualLinear, TradingMode.DeliveryLinear, TradingMode.PerpetualInverse, TradingMode.DeliveryInverse }; public void SetDefaultExchangeParameter(string key, object value) => ExchangeParameters.SetStaticParameter(Exchange, key, value); public void ResetDefaultExchangeParameters() => ExchangeParameters.ResetStaticParameters(); - // Can be implemented with V2 websockets + #region Ticker client + EndpointOptions ITickerSocketClient.SubscribeTickerOptions { get; } = new EndpointOptions(false); + async Task> ITickerSocketClient.SubscribeToTickerUpdatesAsync(SubscribeTickerRequest request, Action> handler, CancellationToken ct) + { + var validationError = ((ITickerSocketClient)this).SubscribeTickerOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeResult(Exchange, validationError); + + var symbol = request.Symbol.GetSymbol(FormatSymbol); + var result = await SubscribeToTickerUpdatesAsync(symbol, update => handler(update.AsExchangeEvent(Exchange, new SharedSpotTicker(symbol, update.Data.LastPrice, null, null, update.Data.Volume, update.Data.ChangePercentage24h))), ct).ConfigureAwait(false); + + return new ExchangeResult(Exchange, result); + } + #endregion + + #region Trade client + + EndpointOptions ITradeSocketClient.SubscribeTradeOptions { get; } = new EndpointOptions(false); + async Task> ITradeSocketClient.SubscribeToTradeUpdatesAsync(SubscribeTradeRequest request, Action>> handler, CancellationToken ct) + { + var validationError = ((ITradeSocketClient)this).SubscribeTradeOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeResult(Exchange, validationError); + + var symbol = request.Symbol.GetSymbol(FormatSymbol); + var result = await SubscribeToTradeUpdatesAsync(symbol, update => handler(update.AsExchangeEvent(Exchange, update.Data.Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)))), ct).ConfigureAwait(false); + + return new ExchangeResult(Exchange, result); + } + #endregion + + #region Book Ticker client + + EndpointOptions IBookTickerSocketClient.SubscribeBookTickerOptions { get; } = new EndpointOptions(false); + async Task> IBookTickerSocketClient.SubscribeToBookTickerUpdatesAsync(SubscribeBookTickerRequest request, Action> handler, CancellationToken ct) + { + var validationError = ((IBookTickerSocketClient)this).SubscribeBookTickerOptions.ValidateRequest(Exchange, request, request.Symbol.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeResult(Exchange, validationError); + + var symbol = request.Symbol.GetSymbol(FormatSymbol); + var result = await SubscribeToTickerUpdatesAsync(symbol, update => handler(update.AsExchangeEvent(Exchange, new SharedBookTicker(update.Data.BestAskPrice, update.Data.BestAskQuantity, update.Data.BestBidPrice, update.Data.BestBidQuantity))), ct).ConfigureAwait(false); + + return new ExchangeResult(Exchange, result); + } + #endregion + + #region Balance client + EndpointOptions IBalanceSocketClient.SubscribeBalanceOptions { get; } = new EndpointOptions(false); + async Task> IBalanceSocketClient.SubscribeToBalanceUpdatesAsync(SubscribeBalancesRequest request, Action>> handler, CancellationToken ct) + { + var validationError = ((IBalanceSocketClient)this).SubscribeBalanceOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeResult(Exchange, validationError); + + var result = await SubscribeToBalanceUpdatesAsync( + update => { + if (update.UpdateType == SocketUpdateType.Snapshot || update.Data.FlexFutures == null) + return; + + handler(update.AsExchangeEvent>(Exchange, update.Data.FlexFutures.Currencies.Select(x => + new SharedBalance(x.Key, x.Value.Available, x.Value.Quantity)).ToArray())); + }, + ct: ct).ConfigureAwait(false); + + return new ExchangeResult(Exchange, result); + } + #endregion + + #region Futures Order client + EndpointOptions IFuturesOrderSocketClient.SubscribeFuturesOrderOptions { get; } = new EndpointOptions(true); + async Task> IFuturesOrderSocketClient.SubscribeToFuturesOrderUpdatesAsync(SubscribeFuturesOrderRequest request, Action>> handler, CancellationToken ct) + { + var validationError = ((IFuturesOrderSocketClient)this).SubscribeFuturesOrderOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeResult(Exchange, validationError); + + var result = await SubscribeToOpenOrdersUpdatesAsync( + true, + update => { }, + update => + { + handler(update.AsExchangeEvent>(Exchange, new[] { + new SharedFuturesOrder( + update.Data.Order?.Symbol ?? string.Empty, + update.Data.OrderId.ToString(), + update.Data.Order == null ? default : update.Data.Order.Type == FuturesOrderType.Limit ? SharedOrderType.Limit : update.Data.Order.Type == FuturesOrderType.Market ? SharedOrderType.Market : SharedOrderType.Other, + update.Data.Order?.Side == OrderSide.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell, + update.Data.IsCancel ? SharedOrderStatus.Canceled : update.Data.Order!.QuantityFilled == update.Data.Order.Quantity ? SharedOrderStatus.Filled : SharedOrderStatus.Open, + update.Data.Order?.Timestamp) + { + ClientOrderId = update.Data.Order?.ClientOrderId, + Quantity = update.Data.Order?.Quantity, + QuantityFilled = update.Data.Order?.QuantityFilled, + UpdateTime = update.Data.Order?.LastUpdateTime, + OrderPrice = update.Data.Order?.Price, + ReduceOnly = update.Data.Order?.ReduceOnly, + } + })); + }, + ct: ct).ConfigureAwait(false); + + return new ExchangeResult(Exchange, result); + } + #endregion + + #region User Trade client + + EndpointOptions IUserTradeSocketClient.SubscribeUserTradeOptions { get; } = new EndpointOptions(true); + async Task> IUserTradeSocketClient.SubscribeToUserTradeUpdatesAsync(SubscribeUserTradeRequest request, Action>> handler, CancellationToken ct) + { + var validationError = ((IUserTradeSocketClient)this).SubscribeUserTradeOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeResult(Exchange, validationError); + + var result = await SubscribeToUserTradeUpdatesAsync( + update => + { + if (update.UpdateType == SocketUpdateType.Snapshot) + return; + + handler(update.AsExchangeEvent>(Exchange, update.Data.Trades.Select(x => + new SharedUserTrade( + x.Symbol, + x.OrderId.ToString(), + x.TradeId.ToString(), + x.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell, + x.Quantity, + x.Price, + x.Timestamp) + { + Fee = Math.Abs(x.FeePaid), + FeeAsset = x.FeeCurrency, + Role = x.TradeType == Enums.TradeType.Taker ? SharedRole.Taker : SharedRole.Maker + } + ).ToArray())); + }, + ct: ct).ConfigureAwait(false); + + return new ExchangeResult(Exchange, result); + } + #endregion + + #region Position client + EndpointOptions IPositionSocketClient.SubscribePositionOptions { get; } = new EndpointOptions(true); + async Task> IPositionSocketClient.SubscribeToPositionUpdatesAsync(SubscribePositionRequest request, Action>> handler, CancellationToken ct) + { + var validationError = ((IPositionSocketClient)this).SubscribePositionOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeResult(Exchange, validationError); + + var result = await SubscribeToOpenPositionUpdatesAsync( + update => { + if (update.UpdateType == SocketUpdateType.Snapshot) + return; + + handler(update.AsExchangeEvent>(Exchange, update.Data.Positions.Select( + x => new SharedPosition(x.Symbol, Math.Abs(x.Balance), update.Timestamp) + { + AverageOpenPrice = x.EntryPrice, + PositionSide = x.Balance > 0 ? SharedPositionSide.Long : SharedPositionSide.Short, + UnrealizedPnl = x.ProfitAndLoss, + Leverage = x.EffectiveLeverage, + LiquidationPrice = x.LiquidationThreshold + }).ToArray())); + }, + ct: ct).ConfigureAwait(false); + + return new ExchangeResult(Exchange, result); + } + + #endregion } } diff --git a/Kraken.Net/Clients/KrakenRestClient.cs b/Kraken.Net/Clients/KrakenRestClient.cs index 570dde7..e91265a 100644 --- a/Kraken.Net/Clients/KrakenRestClient.cs +++ b/Kraken.Net/Clients/KrakenRestClient.cs @@ -1,14 +1,10 @@ -using System; -using System.Net.Http; -using CryptoExchange.Net.Authentication; -using CryptoExchange.Net.Clients; +using CryptoExchange.Net.Clients; using Kraken.Net.Clients.FuturesApi; using Kraken.Net.Clients.SpotApi; using Kraken.Net.Interfaces.Clients; using Kraken.Net.Interfaces.Clients.FuturesApi; using Kraken.Net.Interfaces.Clients.SpotApi; using Kraken.Net.Objects.Options; -using Microsoft.Extensions.Logging; namespace Kraken.Net.Clients { diff --git a/Kraken.Net/Clients/KrakenSocketClient.cs b/Kraken.Net/Clients/KrakenSocketClient.cs index a914759..26a24e0 100644 --- a/Kraken.Net/Clients/KrakenSocketClient.cs +++ b/Kraken.Net/Clients/KrakenSocketClient.cs @@ -1,13 +1,10 @@ -using CryptoExchange.Net.Authentication; -using CryptoExchange.Net.Clients; +using CryptoExchange.Net.Clients; using Kraken.Net.Clients.FuturesApi; using Kraken.Net.Clients.SpotApi; using Kraken.Net.Interfaces.Clients; using Kraken.Net.Interfaces.Clients.FuturesApi; using Kraken.Net.Interfaces.Clients.SpotApi; using Kraken.Net.Objects.Options; -using Microsoft.Extensions.Logging; -using System; namespace Kraken.Net.Clients { diff --git a/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApi.cs b/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApi.cs index a64e06f..b3cdfb5 100644 --- a/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApi.cs +++ b/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApi.cs @@ -67,6 +67,10 @@ internal KrakenRestClientSpotApi(ILogger logger, HttpClient? httpClient, KrakenR protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials) => new KrakenAuthenticationProvider(credentials, ClientOptions.NonceProvider ?? new KrakenNonceProvider()); + protected override IStreamMessageAccessor CreateAccessor() => new SystemTextJsonStreamMessageAccessor(); + + protected override IMessageSerializer CreateSerializer() => new SystemTextJsonMessageSerializer(); + #region common interface async Task>> IBaseRestClient.GetSymbolsAsync(CancellationToken ct) @@ -194,7 +198,7 @@ async Task> ISpotClient.PlaceOrderAsync(string symbol, Co clientId = id; } - var result = await Trading.PlaceOrderAsync(symbol, GetOrderSide(side), GetOrderType(type), quantity, price: price, clientOrderId: clientId, ct: ct).ConfigureAwait(false); + var result = await Trading.PlaceOrderAsync(symbol, GetOrderSide(side), GetOrderType(type), quantity, price: price, userReference: clientId, ct: ct).ConfigureAwait(false); if (!result) return result.As(null); @@ -361,9 +365,9 @@ protected override async Task ShouldRetryRequestAsync(IRateLimitGate? g return false; } - internal async Task Execute(Uri url, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false, int weight = 1, RequestBodyFormat? bodyFormat = null) + internal async Task SendAsync(RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null) { - var result = await SendRequestAsync(url, method, ct, parameters, signed, requestWeight: weight, gate: KrakenExchange.RateLimiter.SpotRest, requestBodyFormat: bodyFormat).ConfigureAwait(false); + var result = await base.SendAsync(BaseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false); if (!result) return result.AsDatalessError(result.Error!); @@ -373,9 +377,12 @@ internal async Task Execute(Uri url, HttpMethod method, Cancellat return result.AsDataless(); } - internal async Task> Execute(Uri url, HttpMethod method, CancellationToken ct, Dictionary? parameters = null, bool signed = false, int weight = 1, RequestBodyFormat? bodyFormat = null, IRateLimitGate? gate = null) + internal Task> SendAsync(RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null) + => SendToAddressAsync(BaseAddress, definition, parameters, cancellationToken, weight); + + internal async Task> SendToAddressAsync(string baseAddress, RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null) { - var result = await SendRequestAsync>(url, method, ct, parameters, signed, requestWeight: weight, gate: gate ?? KrakenExchange.RateLimiter.SpotRest, requestBodyFormat: bodyFormat).ConfigureAwait(false); + var result = await base.SendAsync>(baseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false); if (!result) return result.AsError(result.Error!); diff --git a/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiAccount.cs b/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiAccount.cs index 82eb9d5..de106cb 100644 --- a/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiAccount.cs +++ b/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiAccount.cs @@ -1,16 +1,4 @@ -using CryptoExchange.Net; -using CryptoExchange.Net.Converters; -using CryptoExchange.Net.Objects; -using Kraken.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; +using Kraken.Net.Enums; using Kraken.Net.Objects.Models; using Kraken.Net.Objects.Models.Socket; using Kraken.Net.Interfaces.Clients.SpotApi; @@ -20,6 +8,7 @@ namespace Kraken.Net.Clients.SpotApi /// internal class KrakenRestClientSpotApiAccount : IKrakenRestClientSpotApiAccount { + private static readonly RequestDefinitionCache _definitions = new RequestDefinitionCache(); private readonly KrakenRestClientSpotApi _baseClient; internal KrakenRestClientSpotApiAccount(KrakenRestClientSpotApi baseClient) @@ -27,21 +16,30 @@ internal KrakenRestClientSpotApiAccount(KrakenRestClientSpotApi baseClient) _baseClient = baseClient; } + #region Get Balances /// public async Task>> GetBalancesAsync(string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - return await _baseClient.Execute>(_baseClient.GetUri("0/private/Balance"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/Balance", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Available Balances + /// public async Task>> GetAvailableBalancesAsync(string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - var result = await _baseClient.Execute>(_baseClient.GetUri("0/private/BalanceEx"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/BalanceEx", KrakenExchange.RateLimiter.SpotRest, 1, true); + var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); if (result) { foreach (var item in result.Data) @@ -51,24 +49,36 @@ public async Task>> Get return result; } + #endregion + + #region Get Trade Balances + /// public async Task> GetTradeBalanceAsync(string? baseAsset = null, string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("aclass", "currency"); parameters.AddOptionalParameter("asset", baseAsset); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - return await _baseClient.Execute(_baseClient.GetUri("0/private/TradeBalance"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/TradeBalance", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Open Positions + /// public async Task>> GetOpenPositionsAsync(IEnumerable? transactionIds = null, string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("docalcs", true); parameters.AddOptionalParameter("txid", transactionIds?.Any() == true ? string.Join(",", transactionIds) : null); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - var result = await _baseClient.Execute>(_baseClient.GetUri("0/private/OpenPositions"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/OpenPositions", KrakenExchange.RateLimiter.SpotRest, 1, true); + var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); if (result) { foreach (var item in result.Data) @@ -77,17 +87,22 @@ public async Task>> GetOpenPosi return result; } + #endregion + + #region Get Ledger Info + /// public async Task> GetLedgerInfoAsync(IEnumerable? assets = null, IEnumerable? entryTypes = null, DateTime? startTime = null, DateTime? endTime = null, int? resultOffset = null, string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("asset", assets != null ? string.Join(",", assets) : null); - parameters.AddOptionalParameter("type", entryTypes != null ? string.Join(",", entryTypes.Select(e => JsonConvert.SerializeObject(e, new LedgerEntryTypeConverter(false)))) : null); + parameters.AddOptionalParameter("type", entryTypes != null ? string.Join(",", entryTypes.Select(EnumConverter.GetString)) : null); parameters.AddOptionalParameter("start", DateTimeConverter.ConvertToSeconds(startTime)); parameters.AddOptionalParameter("end", DateTimeConverter.ConvertToSeconds(endTime)); parameters.AddOptionalParameter("ofs", resultOffset); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - var result = await _baseClient.Execute(_baseClient.GetUri("0/private/Ledgers"), HttpMethod.Post, ct, parameters, true, weight: 2).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/Ledgers", KrakenExchange.RateLimiter.SpotRest, 1, true); + var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); if (result) { foreach (var item in result.Data.Ledger) @@ -96,13 +111,19 @@ public async Task> GetLedgerInfoAsync(IEnumerabl return result; } + #endregion + + #region Get Ledgers Entry + /// public async Task>> GetLedgersEntryAsync(IEnumerable? ledgerIds = null, string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("id", ledgerIds?.Any() == true ? string.Join(",", ledgerIds) : null); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - var result = await _baseClient.Execute>(_baseClient.GetUri("0/private/QueryLedgers"), HttpMethod.Post, ct, parameters, true, weight: 2).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/QueryLedgers", KrakenExchange.RateLimiter.SpotRest, 2, true); + var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); + if (result) { foreach (var item in result.Data) @@ -111,34 +132,49 @@ public async Task>> GetLedge return result; } + #endregion + + #region Get Trade Volume + /// public async Task> GetTradeVolumeAsync(IEnumerable? symbols = null, string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("fee-info", true); parameters.AddOptionalParameter("pair", symbols?.Any() == true ? string.Join(",", symbols) : null); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - return await _baseClient.Execute(_baseClient.GetUri("0/private/TradeVolume"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/TradeVolume", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Deposit Methods + /// public async Task>> GetDepositMethodsAsync(string asset, string? twoFactorPassword = null, CancellationToken ct = default) { asset.ValidateNotNull(nameof(asset)); - var parameters = new Dictionary() + var parameters = new ParameterCollection() { {"asset", asset} }; parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - return await _baseClient.Execute>(_baseClient.GetUri("0/private/DepositMethods"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/DepositMethods", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Deposit Addresses + /// public async Task>> GetDepositAddressesAsync(string asset, string depositMethod, bool generateNew = false, decimal? quantity = null, string? twoFactorPassword = null, CancellationToken ct = default) { asset.ValidateNotNull(nameof(asset)); depositMethod.ValidateNotNull(nameof(depositMethod)); - var parameters = new Dictionary() + var parameters = new ParameterCollection() { {"asset", asset}, {"method", depositMethod } @@ -151,20 +187,30 @@ public async Task>> GetDepositAd parameters.AddOptionalParameter("amount", quantity); parameters.AddOptionalParameter("otp", twoFactorPassword); - return await _baseClient.Execute>(_baseClient.GetUri("0/private/DepositAddresses"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/DepositAddresses", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Deposit Status + /// public async Task>> GetDepositStatusAsync(string? asset = null, string? depositMethod = null, string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("asset", asset); parameters.AddOptionalParameter("method", depositMethod); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - return await _baseClient.Execute>(_baseClient.GetUri("0/private/DepositStatus"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/DepositStatus", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Deposit History + /// public async Task>> GetDepositHistoryAsync( string? asset = null, @@ -176,7 +222,7 @@ public async Task>> GetDepo string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("asset", asset); parameters.AddOptionalParameter("method", depositMethod); parameters.AddOptionalParameter("start", startTime); @@ -185,16 +231,21 @@ public async Task>> GetDepo parameters.AddOptionalParameter("cursor", cursor ?? "true"); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - return await _baseClient.Execute>(_baseClient.GetUri("0/private/DepositStatus"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/DepositStatus", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Withdraw Info + /// public async Task> GetWithdrawInfoAsync(string asset, string key, decimal quantity, string? twoFactorPassword = null, CancellationToken ct = default) { asset.ValidateNotNull(nameof(asset)); key.ValidateNotNull(nameof(key)); - var parameters = new Dictionary + var parameters = new ParameterCollection { { "asset", asset }, { "key", key }, @@ -202,16 +253,22 @@ public async Task> GetWithdrawInfoAsync(string }; parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - return await _baseClient.Execute(_baseClient.GetUri("0/private/WithdrawInfo"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/WithdrawInfo", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Withdraw + /// public async Task> WithdrawAsync(string asset, string key, decimal quantity, string? address = null, string? twoFactorPassword = null, CancellationToken ct = default) { asset.ValidateNotNull(nameof(asset)); key.ValidateNotNull(nameof(key)); - var parameters = new Dictionary + var parameters = new ParameterCollection { {"asset", asset}, {"key", key}, @@ -220,42 +277,64 @@ public async Task> WithdrawAsync(string asset, str parameters.AddOptionalParameter("address", address); parameters.AddOptionalParameter("otp", twoFactorPassword); - return await _baseClient.Execute(_baseClient.GetUri("0/private/Withdraw"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/Withdraw", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Withdraw Addresses + /// public async Task>> GetWithdrawAddressesAsync(string? asset = null, string? aclass = null, string? method = null, string? key = null, bool? verified = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("asset", asset); parameters.AddOptionalParameter("aclass", aclass); parameters.AddOptionalParameter("method", method); parameters.AddOptionalParameter("key", key); parameters.AddOptionalParameter("verified", verified); - return await _baseClient.Execute>(_baseClient.GetUri("0/private/WithdrawAddresses"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/WithdrawAddresses", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Withdraw Methods + /// public async Task>> GetWithdrawMethodsAsync(string? asset = null, string? aclass = null, string? network = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("asset", asset); parameters.AddOptionalParameter("aclass", aclass); parameters.AddOptionalParameter("network", network); - return await _baseClient.Execute>(_baseClient.GetUri("0/private/WithdrawMethods"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/WithdrawMethods", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Withdrawal Status + /// public async Task>> GetWithdrawalStatusAsync(string? asset = null, string? withdrawalMethod = null, string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("asset", asset); parameters.AddOptionalParameter("method", withdrawalMethod); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - return await _baseClient.Execute>(_baseClient.GetUri("0/private/WithdrawStatus"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/WithdrawStatus", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Withdrawal History + /// public async Task>> GetWithdrawalHistoryAsync( string? asset = null, @@ -267,7 +346,7 @@ public async Task>> GetWith string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("asset", asset); parameters.AddOptionalParameter("method", withdrawalMethod); parameters.AddOptionalParameter("start", startTime); @@ -276,25 +355,35 @@ public async Task>> GetWith parameters.AddOptionalParameter("cursor", cursor ?? "true"); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - return await _baseClient.Execute>(_baseClient.GetUri("0/private/WithdrawStatus"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/WithdrawStatus", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Cancel Withdrawal + /// public async Task> CancelWithdrawalAsync(string asset, string referenceId, string? twoFactorPassword = null, CancellationToken ct = default) { asset.ValidateNotNull(nameof(asset)); referenceId.ValidateNotNull(nameof(referenceId)); - var parameters = new Dictionary + var parameters = new ParameterCollection { {"asset", asset}, {"refid", referenceId} }; parameters.AddOptionalParameter("otp", twoFactorPassword); - return await _baseClient.Execute(_baseClient.GetUri("0/private/WithdrawCancel"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/WithdrawCancel", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Transfer + /// public async Task> TransferAsync(string asset, decimal quantity, string fromWallet, string toWallet, string? twoFactorPassword = null, CancellationToken ct = default) { @@ -302,7 +391,7 @@ public async Task> TransferAsync(string asset, fromWallet.ValidateNotNull(nameof(fromWallet)); toWallet.ValidateNotNull(nameof(toWallet)); - var parameters = new Dictionary + var parameters = new ParameterCollection { {"asset", asset}, {"from", fromWallet}, @@ -311,14 +400,21 @@ public async Task> TransferAsync(string asset, }; parameters.AddOptionalParameter("otp", twoFactorPassword); - return await _baseClient.Execute(_baseClient.GetUri("0/private/WalletTransfer"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/WalletTransfer", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Websocket Token + /// public async Task> GetWebsocketTokenAsync(CancellationToken ct = default) { - return await _baseClient.Execute(_baseClient.GetUri("0/private/GetWebSocketsToken"), HttpMethod.Post, ct, null, true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/GetWebSocketsToken", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync(request, null, ct).ConfigureAwait(false); } + #endregion } } diff --git a/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiEarn.cs b/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiEarn.cs index 815cb11..dce454c 100644 --- a/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiEarn.cs +++ b/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiEarn.cs @@ -1,15 +1,13 @@ using Kraken.Net.Enums; using Kraken.Net.Interfaces.Clients.SpotApi; using Kraken.Net.Objects.Models; -using System; -using System.Collections.Generic; -using System.Text; namespace Kraken.Net.Clients.SpotApi { /// internal class KrakenRestClientSpotApiEarn : IKrakenRestClientSpotApiEarn { + private static readonly RequestDefinitionCache _definitions = new RequestDefinitionCache(); private readonly KrakenRestClientSpotApi _baseClient; internal KrakenRestClientSpotApiEarn(KrakenRestClientSpotApi baseClient) @@ -17,6 +15,8 @@ internal KrakenRestClientSpotApiEarn(KrakenRestClientSpotApi baseClient) _baseClient = baseClient; } + #region Get Strategies + /// public async Task>> GetStrategiesAsync(string? asset = null, LockType? lockType = null, string? cursor = null, int? limit = null, bool? asc = null, string? twoFactorPassword = null, CancellationToken ct = default) { @@ -27,9 +27,15 @@ public async Task>> GetStrate parameters.AddOptional("limit", limit); parameters.AddOptional("asc", asc.HasValue ? asc == true : null); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - return await _baseClient.Execute>(_baseClient.GetUri("0/private/Earn/Strategies"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/Earn/Strategies", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Allocations + /// public async Task> GetAllocationsAsync(string? convertAsset = null, bool? hideZeroAllocations = null, bool? asc = null, string? twoFactorPassword = null, CancellationToken ct = default) { @@ -38,9 +44,15 @@ public async Task> GetAllocationsAsyn parameters.AddOptional("hide_zero_allocations", hideZeroAllocations); parameters.AddOptional("asc", asc.HasValue ? asc == true : null); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - return await _baseClient.Execute(_baseClient.GetUri("0/private/Earn/Allocations"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/Earn/Allocations", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Allocation Status + /// public async Task> GetAllocationStatusAsync(string strategyId, string? twoFactorPassword = null, CancellationToken ct = default) { @@ -49,9 +61,15 @@ public async Task> GetAllocationStatusAsync(stri { "strategy_id", strategyId } }; parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - return await _baseClient.Execute(_baseClient.GetUri("0/private/Earn/AllocateStatus"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/Earn/AllocateStatus", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Deallocation Status + /// public async Task> GetDeallocationStatusAsync(string strategyId, string? twoFactorPassword = null, CancellationToken ct = default) { @@ -60,9 +78,15 @@ public async Task> GetDeallocationStatusAsync(st { "strategy_id", strategyId } }; parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - return await _baseClient.Execute(_baseClient.GetUri("0/private/Earn/DeallocateStatus"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/Earn/DeallocateStatus", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Allocate Earn Funds + /// public async Task AllocateEarnFundsAsync(string strategyId, decimal quantity, string? twoFactorPassword = null, CancellationToken ct = default) { @@ -72,9 +96,15 @@ public async Task AllocateEarnFundsAsync(string strategyId, decim { "amount", quantity.ToString(CultureInfo.InvariantCulture) } }; parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - return await _baseClient.Execute(_baseClient.GetUri("0/private/Earn/Allocate"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/Earn/Allocate", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Deallocate Earn Funds + /// public async Task DeallocateEarnFundsAsync(string strategyId, decimal quantity, string? twoFactorPassword = null, CancellationToken ct = default) { @@ -84,7 +114,11 @@ public async Task DeallocateEarnFundsAsync(string strategyId, dec { "amount", quantity.ToString(CultureInfo.InvariantCulture) } }; parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - return await _baseClient.Execute(_baseClient.GetUri("0/private/Earn/Deallocate"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/Earn/Deallocate", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + + #endregion } } diff --git a/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiExchangeData.cs b/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiExchangeData.cs index 3b2e747..6058729 100644 --- a/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiExchangeData.cs +++ b/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiExchangeData.cs @@ -1,24 +1,14 @@ -using CryptoExchange.Net; -using CryptoExchange.Net.Converters; -using CryptoExchange.Net.Objects; -using Kraken.Net.Converters; +using Kraken.Net.Converters; using Kraken.Net.Enums; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; using Kraken.Net.Objects.Models; using Kraken.Net.Interfaces.Clients.SpotApi; -using Kraken.Net.ExtensionMethods; namespace Kraken.Net.Clients.SpotApi { /// internal class KrakenRestClientSpotApiExchangeData : IKrakenRestClientSpotApiExchangeData { + private static readonly RequestDefinitionCache _definitions = new RequestDefinitionCache(); private readonly KrakenRestClientSpotApi _baseClient; internal KrakenRestClientSpotApiExchangeData(KrakenRestClientSpotApi baseClient) @@ -26,54 +16,81 @@ internal KrakenRestClientSpotApiExchangeData(KrakenRestClientSpotApi baseClient) _baseClient = baseClient; } + #region Get Server Time + /// public async Task> GetServerTimeAsync(CancellationToken ct = default) { - var result = await _baseClient.Execute(_baseClient.GetUri("0/public/Time"), HttpMethod.Get, ct).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "0/public/Time", KrakenExchange.RateLimiter.SpotRest, 1, false); + var result = await _baseClient.SendAsync(request, null, ct).ConfigureAwait(false); if (!result) return result.AsError(result.Error!); - return result.As(result.Data.UnixTime); + return result.As(result.Data.UnixTime); } + #endregion + + #region Get System Status + /// public async Task> GetSystemStatusAsync(CancellationToken ct = default) { - return await _baseClient.Execute(_baseClient.GetUri("0/public/SystemStatus"), HttpMethod.Get, ct).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "0/public/SystemStatus", KrakenExchange.RateLimiter.SpotRest, 1, false); + return await _baseClient.SendAsync(request, null, ct).ConfigureAwait(false); } + #endregion + + #region Get Assets + /// public async Task>> GetAssetsAsync(IEnumerable? assets = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); if (assets?.Any() == true) parameters.AddOptionalParameter("asset", string.Join(",", assets)); - return await _baseClient.Execute>(_baseClient.GetUri("0/public/Assets"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "0/public/Assets", KrakenExchange.RateLimiter.SpotRest, 1, false); + return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Symbols + /// public async Task>> GetSymbolsAsync(IEnumerable? symbols = null, string? countryCode = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("country_code", countryCode); if (symbols?.Any() == true) parameters.AddOptionalParameter("pair", string.Join(",", symbols)); - return await _baseClient.Execute>(_baseClient.GetUri("0/public/AssetPairs"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "0/public/AssetPairs", KrakenExchange.RateLimiter.SpotRest, 1, false); + return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Ticker + /// public Task>> GetTickerAsync(string symbol, CancellationToken ct = default) => GetTickersAsync(new[] { symbol }, ct); + #endregion + + #region Get Tickers + /// public async Task>> GetTickersAsync(IEnumerable? symbols = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); if (symbols?.Any() == true) parameters.AddParameter("pair", string.Join(",", symbols)); - var result = await _baseClient.Execute>(_baseClient.GetUri("0/public/Ticker"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Get, "0/public/Ticker", KrakenExchange.RateLimiter.SpotRest, 1, false); + var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); if (!result) return result; @@ -83,53 +100,79 @@ public async Task>> GetTickersA return result; } + #endregion + + #region Get Klines + /// public async Task> GetKlinesAsync(string symbol, KlineInterval interval, DateTime? since = null, CancellationToken ct = default) { - var parameters = new Dictionary() + var parameters = new ParameterCollection() { {"pair", symbol}, - {"interval", JsonConvert.SerializeObject(interval, new KlineIntervalConverter(false))} }; + parameters.AddEnum("interval", interval); parameters.AddOptionalParameter("since", DateTimeConverter.ConvertToSeconds(since)); - return await _baseClient.Execute(_baseClient.GetUri("0/public/OHLC"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Get, "0/public/OHLC", KrakenExchange.RateLimiter.SpotRest, 1, false); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Order Book + /// public async Task> GetOrderBookAsync(string symbol, int? limit = null, CancellationToken ct = default) { - var parameters = new Dictionary() + var parameters = new ParameterCollection() { {"pair", symbol}, }; parameters.AddOptionalParameter("count", limit); - var result = await _baseClient.Execute>(_baseClient.GetUri("0/public/Depth"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Get, "0/public/Depth", KrakenExchange.RateLimiter.SpotRest, 1, false); + var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); if (!result) return result.AsError(result.Error!); return result.As(result.Data.First().Value); } + #endregion + + #region Get Trade History + /// public async Task> GetTradeHistoryAsync(string symbol, DateTime? since = null, int? limit = null, CancellationToken ct = default) { - var parameters = new Dictionary() + var parameters = new ParameterCollection() { {"pair", symbol}, }; parameters.AddOptionalParameter("since", DateTimeConverter.ConvertToNanoseconds(since)); parameters.AddOptionalParameter("count", limit); - return await _baseClient.Execute(_baseClient.GetUri("0/public/Trades"), HttpMethod.Get, ct, parameters: parameters).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Get, "0/public/Trades", KrakenExchange.RateLimiter.SpotRest, 1, false); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Get Recent Spread + /// public async Task> GetRecentSpreadAsync(string symbol, DateTime? since = null, CancellationToken ct = default) { - var parameters = new Dictionary() + var parameters = new ParameterCollection() { {"pair", symbol}, }; parameters.AddOptionalParameter("since", DateTimeConverter.ConvertToSeconds(since)); - return await _baseClient.Execute(_baseClient.GetUri("0/public/Spread"), HttpMethod.Get, ct, parameters).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Get, "0/public/Spread", KrakenExchange.RateLimiter.SpotRest, 1, false); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + + #endregion } } diff --git a/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiShared.cs b/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiShared.cs index fc40ec4..6569c7e 100644 --- a/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiShared.cs +++ b/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiShared.cs @@ -206,17 +206,13 @@ async Task> ISpotOrderRestClient.PlaceSpotOrderAsync if (validationError != null) return new ExchangeWebResult(Exchange, validationError); - uint cid = 0; - if (request.ClientOrderId != null && !uint.TryParse(request.ClientOrderId, out cid)) - return new ExchangeWebResult(Exchange, new ArgumentError("Client order id needs to be a positive integer if specified")); - var result = await Trading.PlaceOrderAsync( request.Symbol.GetSymbol(FormatSymbol), request.Side == SharedOrderSide.Buy ? Enums.OrderSide.Buy : Enums.OrderSide.Sell, GetPlaceOrderType(request.OrderType), request.Quantity ?? request.QuoteQuantity ?? 0, request.Price, - clientOrderId: request.ClientOrderId != null ? cid : null, + clientOrderId: request.ClientOrderId, orderFlags: GetOrderFlags(request.OrderType, request.QuoteQuantity), timeInForce: GetTimeInForce(request.TimeInForce), ct: ct).ConfigureAwait(false); diff --git a/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiTrading.cs b/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiTrading.cs index b3793c6..bbf9f92 100644 --- a/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiTrading.cs +++ b/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiTrading.cs @@ -1,26 +1,14 @@ -using CryptoExchange.Net; -using CryptoExchange.Net.Converters; -using CryptoExchange.Net.Objects; -using Kraken.Net.Converters; using Kraken.Net.Enums; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; using Kraken.Net.Objects.Models; using Kraken.Net.Interfaces.Clients.SpotApi; using CryptoExchange.Net.CommonObjects; -using Kraken.Net.ExtensionMethods; namespace Kraken.Net.Clients.SpotApi { /// internal class KrakenRestClientSpotApiTrading : IKrakenRestClientSpotApiTrading { + private static readonly RequestDefinitionCache _definitions = new RequestDefinitionCache(); private readonly KrakenRestClientSpotApi _baseClient; internal KrakenRestClientSpotApiTrading(KrakenRestClientSpotApi baseClient) @@ -28,15 +16,18 @@ internal KrakenRestClientSpotApiTrading(KrakenRestClientSpotApi baseClient) _baseClient = baseClient; } + #region Get Open Orders /// public async Task> GetOpenOrdersAsync(uint? clientOrderId = null, string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("trades", true); parameters.AddOptionalParameter("userref", clientOrderId); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - var result = await _baseClient.Execute(_baseClient.GetUri("0/private/OpenOrders"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/OpenOrders", KrakenExchange.RateLimiter.SpotRest, 1, true); + var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); if (result) { foreach (var item in result.Data.Open) @@ -45,17 +36,23 @@ public async Task> GetOpenOrdersAsync(uint? client return result; } + #endregion + + #region Get Closed Orders + /// public async Task> GetClosedOrdersAsync(uint? clientOrderId = null, DateTime? startTime = null, DateTime? endTime = null, int? resultOffset = null, string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("trades", true); parameters.AddOptionalParameter("userref", clientOrderId); parameters.AddOptionalParameter("start", DateTimeConverter.ConvertToSeconds(startTime)); parameters.AddOptionalParameter("end", DateTimeConverter.ConvertToSeconds(endTime)); parameters.AddOptionalParameter("ofs", resultOffset); parameters.AddOptionalParameter("otp", twoFactorPassword); - var result = await _baseClient.Execute(_baseClient.GetUri("0/private/ClosedOrders"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/ClosedOrders", KrakenExchange.RateLimiter.SpotRest, 1, true); + var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); if (result) { foreach (var item in result.Data.Closed) @@ -64,20 +61,31 @@ public async Task> GetClosedOrdersAsync(ui return result; } + #endregion + + #region Get Order + /// public Task>> GetOrderAsync(string? orderId = null, uint? clientOrderId = null, bool? consolidateTaker = null, bool? trades = null, string? twoFactorPassword = null, CancellationToken ct = default) => GetOrdersAsync(orderId == null ? null : new[] { orderId }, clientOrderId, consolidateTaker, trades, twoFactorPassword, ct); + #endregion + + #region Get Orders + /// public async Task>> GetOrdersAsync(IEnumerable? orderIds = null, uint? clientOrderId = null, bool? consolidateTaker = null, bool? trades = null, string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("userref", clientOrderId); parameters.AddOptionalParameter("txid", orderIds?.Any() == true ? string.Join(",", orderIds) : null); parameters.AddOptionalParameter("consolidate_taker", consolidateTaker); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); parameters.AddOptionalParameter("trades", trades); - var result = await _baseClient.Execute>(_baseClient.GetUri("0/private/QueryOrders"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/QueryOrders", KrakenExchange.RateLimiter.SpotRest, 1, true); + var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); + if (result) { foreach (var item in result.Data) @@ -86,17 +94,23 @@ public async Task>> GetOrdersAsync return result; } + #endregion + + #region Get User Trades + /// public async Task> GetUserTradesAsync(DateTime? startTime = null, DateTime? endTime = null, int? resultOffset = null, bool? consolidateTaker = null, string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("trades", true); parameters.AddOptionalParameter("start", DateTimeConverter.ConvertToSeconds(startTime)); parameters.AddOptionalParameter("end", DateTimeConverter.ConvertToSeconds(endTime)); parameters.AddOptionalParameter("ofs", resultOffset); parameters.AddOptionalParameter("consolidate_taker", consolidateTaker); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - var result = await _baseClient.Execute(_baseClient.GetUri("0/private/TradesHistory"), HttpMethod.Post, ct, parameters, true, weight: 2).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/TradesHistory", KrakenExchange.RateLimiter.SpotRest, 1, true); + var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); if (result) { foreach (var item in result.Data.Trades) @@ -106,19 +120,29 @@ public async Task> GetUserTradesAsync(DateTi return result; } + #endregion + + #region Get User Trade Details + /// public Task>> GetUserTradeDetailsAsync(string tradeId, string? twoFactorPassword = null, CancellationToken ct = default) => GetUserTradeDetailsAsync(new[] { tradeId }, twoFactorPassword, ct); + #endregion + + #region Get User Trade Details + /// public async Task>> GetUserTradeDetailsAsync(IEnumerable tradeIds, string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("trades", true); parameters.AddOptionalParameter("txid", tradeIds?.Any() == true ? string.Join(",", tradeIds) : null); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - var result = await _baseClient.Execute>(_baseClient.GetUri("0/private/QueryTrades"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/QueryTrades", KrakenExchange.RateLimiter.SpotRest, 1, true); + var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); if (result) { foreach (var item in result.Data) @@ -127,6 +151,10 @@ public async Task>> GetUserTra return result; } + #endregion + + #region Place Multiple Orders + /// public async Task> PlaceMultipleOrdersAsync(string symbol, IEnumerable orders, DateTime? deadline = null, bool? validateOnly = null, CancellationToken ct = default) { @@ -140,9 +168,14 @@ public async Task> PlaceMultipleOrdersAsyn if (validateOnly == true) parameters.AddOptionalParameter("validate", true); - return await _baseClient.Execute(_baseClient.GetUri("0/private/AddOrderBatch"), HttpMethod.Post, ct, parameters, true, bodyFormat: RequestBodyFormat.Json, weight: 0).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/AddOrderBatch", KrakenExchange.RateLimiter.SpotRest, 0, true, requestBodyFormat: RequestBodyFormat.Json); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Place Order + /// public async Task> PlaceOrderAsync( string symbol, @@ -155,7 +188,8 @@ public async Task> PlaceOrderAsync( DateTime? startTime = null, DateTime? expireTime = null, bool? validateOnly = null, - uint? clientOrderId = null, + uint? userReference = null, + string? clientOrderId = null, IEnumerable? flags = null, string? twoFactorPassword = null, TimeInForce? timeInForce = null, @@ -172,20 +206,21 @@ public async Task> PlaceOrderAsync( string? secondaryPriceSuffixOperator = null, CancellationToken ct = default) { - var parameters = new Dictionary() + var parameters = new ParameterCollection() { { "pair", symbol }, - { "type", JsonConvert.SerializeObject(side, new OrderSideConverter(false)) }, - { "ordertype", JsonConvert.SerializeObject(type, new OrderTypeConverter(false)) }, { "volume", quantity.ToString(CultureInfo.InvariantCulture) }, { "trading_agreement", "agree" } }; - parameters.AddOptionalParameter("oflags", flags == null ? null: string.Join(",", flags.Select(f => JsonConvert.SerializeObject(f, new OrderFlagsConverter(false))))); + parameters.AddEnum("type", side); + parameters.AddEnum("ordertype", type); + parameters.AddOptionalParameter("oflags", flags == null ? null: string.Join(",", flags.Select(EnumConverter.GetString))); if (price != null) parameters.AddOptionalParameter("price", $"{pricePrefixOperator}{price.Value.ToString(CultureInfo.InvariantCulture)}{priceSuffixOperator}"); if (secondaryPrice != null) parameters.AddOptionalParameter("price2", $"{secondaryPricePrefixOperator}{secondaryPrice.Value.ToString(CultureInfo.InvariantCulture)}{secondaryPriceSuffixOperator}"); parameters.AddOptionalParameter("userref", clientOrderId); + parameters.AddOptional("cl_ord_id", clientOrderId); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); parameters.AddOptionalParameter("leverage", leverage?.ToString(CultureInfo.InvariantCulture)); parameters.AddOptionalParameter("starttm", DateTimeConverter.ConvertToSeconds(startTime)); @@ -195,17 +230,23 @@ public async Task> PlaceOrderAsync( parameters.AddOptionalParameter("trigger", EnumConverter.GetString(trigger)); parameters.AddOptionalParameter("displayvol", icebergQuanty?.ToString(CultureInfo.InvariantCulture)); parameters.AddOptionalParameter("stptype", EnumConverter.GetString(selfTradePreventionType)); - parameters.AddOptionalParameter("close[ordertype]", closeOrderType == null? null: JsonConvert.SerializeObject(closeOrderType, new OrderTypeConverter(false))); + parameters.AddOptionalEnum("close[ordertype]", closeOrderType); parameters.AddOptionalParameter("close[price]", closePrice?.ToString(CultureInfo.InvariantCulture)); parameters.AddOptionalParameter("close[price2]", secondaryClosePrice?.ToString(CultureInfo.InvariantCulture)); if (validateOnly == true) parameters.AddOptionalParameter("validate", true); - var result = await _baseClient.Execute(_baseClient.GetUri("0/private/AddOrder"), HttpMethod.Post, ct, parameters, true, weight: 0).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/AddOrder", KrakenExchange.RateLimiter.SpotRest, 0, true); + var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); if (result) _baseClient.InvokeOrderPlaced(new OrderId { SourceObject = result.Data, Id = result.Data.OrderIds.FirstOrDefault() }); return result; } + #endregion + + #region Edit Order + /// public async Task> EditOrderAsync( string symbol, @@ -226,12 +267,12 @@ public async Task> EditOrderAsync( string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary() + var parameters = new ParameterCollection() { { "pair", symbol }, { "txid", orderId }, }; - parameters.AddOptionalParameter("oflags", flags == null ? null : string.Join(",", flags.Select(f => JsonConvert.SerializeObject(f, new OrderFlagsConverter(false))))); + parameters.AddOptionalParameter("oflags", flags == null ? null : string.Join(",", flags.Select(EnumConverter.GetString))); parameters.AddOptionalParameter("volume", quantity?.ToString(CultureInfo.InvariantCulture)); if (price != null) parameters.AddOptionalParameter("price", $"{pricePrefixOperator}{price.Value.ToString(CultureInfo.InvariantCulture)}{priceSuffixOperator}"); @@ -245,33 +286,63 @@ public async Task> EditOrderAsync( if (validateOnly == true) parameters.AddOptionalParameter("validate", true); - return await _baseClient.Execute(_baseClient.GetUri("0/private/EditOrder"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/EditOrder", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } + #endregion + + #region Cancel Order + /// public async Task> CancelOrderAsync(string orderId, string? twoFactorPassword = null, CancellationToken ct = default) { orderId.ValidateNotNull(nameof(orderId)); - var parameters = new Dictionary() + var parameters = new ParameterCollection() { {"txid", orderId} }; parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - var result = await _baseClient.Execute(_baseClient.GetUri("0/private/CancelOrder"), HttpMethod.Post, ct, parameters, true, weight: 0).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/CancelOrder", KrakenExchange.RateLimiter.SpotRest, 1, true); + var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); if (result) _baseClient.InvokeOrderCanceled(new OrderId { SourceObject = result.Data, Id = orderId }); return result; } + #endregion + + #region Cancel All Orders + /// public async Task> CancelAllOrdersAsync(string? twoFactorPassword = null, CancellationToken ct = default) { - var parameters = new Dictionary(); + var parameters = new ParameterCollection(); parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); - var result = await _baseClient.Execute(_baseClient.GetUri("0/private/CancelAll"), HttpMethod.Post, ct, parameters, true).ConfigureAwait(false); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/CancelAll", KrakenExchange.RateLimiter.SpotRest, 1, true); + var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); if (result) _baseClient.InvokeOrderCanceled(new OrderId { SourceObject = result.Data }); return result; } + + #endregion + + #region Cancel All Orders After + + /// + public async Task> CancelAllOrdersAfterAsync(TimeSpan cancelAfter, string? twoFactorPassword = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.Add("timeout", (int)cancelAfter.TotalSeconds); + parameters.AddOptionalParameter("otp", twoFactorPassword ?? _baseClient.ClientOptions.StaticTwoFactorAuthenticationPassword); + + var request = _definitions.GetOrCreate(HttpMethod.Post, "0/private/CancelAllOrdersAfter", KrakenExchange.RateLimiter.SpotRest, 1, true); + return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); + } + + #endregion } } diff --git a/Kraken.Net/Clients/SpotApi/KrakenSocketClientSpotApi.cs b/Kraken.Net/Clients/SpotApi/KrakenSocketClientSpotApi.cs index 6eef2cd..ef9c622 100644 --- a/Kraken.Net/Clients/SpotApi/KrakenSocketClientSpotApi.cs +++ b/Kraken.Net/Clients/SpotApi/KrakenSocketClientSpotApi.cs @@ -1,46 +1,42 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using CryptoExchange.Net.Authentication; -using CryptoExchange.Net.Clients; -using CryptoExchange.Net.Converters; +using CryptoExchange.Net.Clients; using CryptoExchange.Net.Converters.MessageParsing; -using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.Sockets; -using Kraken.Net.Converters; using Kraken.Net.Enums; -using Kraken.Net.ExtensionMethods; using Kraken.Net.Interfaces.Clients.SpotApi; using Kraken.Net.Objects; using Kraken.Net.Objects.Internal; using Kraken.Net.Objects.Models; using Kraken.Net.Objects.Models.Socket; +using Kraken.Net.Objects.Models.Socket.Futures; using Kraken.Net.Objects.Options; -using Kraken.Net.Objects.Sockets; using Kraken.Net.Objects.Sockets.Queries; using Kraken.Net.Objects.Sockets.Subscriptions.Spot; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; +using System.Collections.Concurrent; namespace Kraken.Net.Clients.SpotApi { /// internal partial class KrakenSocketClientSpotApi : SocketApiClient, IKrakenSocketClientSpotApi { - private static readonly MessagePath _idPath = MessagePath.Get().Property("reqid"); - private static readonly MessagePath _eventPath = MessagePath.Get().Property("event"); - private static readonly MessagePath _item1Path = MessagePath.Get().Index(1); - private static readonly MessagePath _item2Path = MessagePath.Get().Index(2); - private static readonly MessagePath _item3Path = MessagePath.Get().Index(3); - private static readonly MessagePath _item4Path = MessagePath.Get().Index(4); - - #region fields - private readonly Dictionary _symbolSynonyms; + private static readonly MessagePath _idPath = MessagePath.Get().Property("req_id"); + private static readonly MessagePath _methodPath = MessagePath.Get().Property("method"); + private static readonly MessagePath _channelPath = MessagePath.Get().Property("channel"); + private static readonly MessagePath _symbolPath = MessagePath.Get().Property("data").Index(0).Property("symbol"); + + private static readonly ConcurrentDictionary _tokenCache = new(); + + private static readonly HashSet _channelsWithoutSymbol = + [ + "heartbeat", + "status", + "instrument", + "executions", + "balances" + ]; + + #region fields private readonly string _privateBaseAddress; /// @@ -54,11 +50,6 @@ internal KrakenSocketClientSpotApi(ILogger logger, KrakenSocketOptions options) base(logger, options.Environment.SpotSocketPublicAddress, options, options.SpotOptions) { _privateBaseAddress = options.Environment.SpotSocketPrivateAddress; - _symbolSynonyms = new Dictionary - { - { "BTC", "XBT"}, - { "DOGE", "XDG" } - }; AddSystemSubscription(new HeartbeatSubscription(_logger)); AddSystemSubscription(new SystemStatusSubscription(_logger)); @@ -69,6 +60,10 @@ internal KrakenSocketClientSpotApi(ILogger logger, KrakenSocketOptions options) } #endregion + protected override IByteMessageAccessor CreateAccessor() => new SystemTextJsonByteMessageAccessor(); + + protected override IMessageSerializer CreateSerializer() => new SystemTextJsonMessageSerializer(); + /// public override string FormatSymbol(string baseAsset, string quoteAsset, TradingMode tradingMode, DateTime? deliverTime = null) => $"{baseAsset.ToUpperInvariant()}/{quoteAsset.ToUpperInvariant()}"; @@ -82,292 +77,580 @@ internal KrakenSocketClientSpotApi(ILogger logger, KrakenSocketOptions options) if (id != null) return id; - var evnt = message.GetValue(_eventPath); - if (evnt != null) - return evnt; - - var arr3 = message.GetValue(_item3Path); - var arr4 = message.GetValue(_item4Path); - if (arr4 != null) - return arr3!.ToLowerInvariant() + "-" + arr4.ToLowerInvariant(); - - var arr2 = message.GetValue(_item2Path); - if (arr2 != null) - return arr2.ToLowerInvariant() + "-" + arr3!.ToLowerInvariant(); + var channel = message.GetValue(_channelPath); + var method = message.GetValue(_methodPath); - var arr1 = message.GetValue(_item1Path); + if (!_channelsWithoutSymbol.Contains(channel!)) + { + var symbol = message.GetValue(_symbolPath); + return channel + method + "-" + symbol; + } - return arr1; + return channel + method; } /// protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials) => new KrakenAuthenticationProvider(credentials, ClientOptions.NonceProvider ?? new KrakenNonceProvider()); - #region methods + #region Streams + + #region System Status /// public async Task> SubscribeToSystemStatusUpdatesAsync(Action> handler, CancellationToken ct = default) { var subscription = new KrakenSystemStatusSubscription(_logger, handler); - return await SubscribeAsync(subscription, ct).ConfigureAwait(false); + return await SubscribeAsync(BaseAddress.AppendPath("v2"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region Ticker + /// - public async Task> SubscribeToTickerUpdatesAsync(string symbol, Action> handler, CancellationToken ct = default) + public async Task> SubscribeToTickerUpdatesAsync(string symbol, Action> handler, CancellationToken ct = default) { - var subSymbol = SymbolToServer(symbol); - var subscription = new KrakenSubscription(_logger, "ticker", new[] { subSymbol }, null, null, handler); - return await SubscribeAsync(subscription, ct).ConfigureAwait(false); + var subscription = new KrakenSubscriptionV2>(_logger, "ticker", [symbol], null, null, null, + x => handler(x.As(x.Data.First()).WithSymbol(x.Data.First().Symbol)) + ); + return await SubscribeAsync(BaseAddress.AppendPath("v2"), subscription, ct).ConfigureAwait(false); } /// - public async Task> SubscribeToTickerUpdatesAsync(IEnumerable symbols, Action> handler, CancellationToken ct = default) + public async Task> SubscribeToTickerUpdatesAsync(IEnumerable symbols, Action> handler, CancellationToken ct = default) { - var symbolArray = symbols.ToArray(); - for (var i = 0; i < symbolArray.Length; i++) - { - symbolArray[i] = SymbolToServer(symbolArray[i]); - } - - var subscription = new KrakenSubscription(_logger, "ticker", symbolArray, null, null, handler); - return await SubscribeAsync(subscription, ct).ConfigureAwait(false); + var subscription = new KrakenSubscriptionV2>(_logger, "ticker", symbols, null, null, null, + x => handler(x.As(x.Data.First()).WithSymbol(x.Data.First().Symbol)) + ); + return await SubscribeAsync(BaseAddress.AppendPath("v2"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region Kline + /// - public Task> SubscribeToKlineUpdatesAsync(string symbol, KlineInterval interval, Action> handler, CancellationToken ct = default) - => SubscribeToKlineUpdatesAsync(new[] { symbol }, interval, handler, ct); + public Task> SubscribeToKlineUpdatesAsync(string symbol, KlineInterval interval, Action>> handler, CancellationToken ct = default) + => SubscribeToKlineUpdatesAsync([symbol], interval, handler, ct); /// - public async Task> SubscribeToKlineUpdatesAsync(IEnumerable symbols, KlineInterval interval, Action> handler, CancellationToken ct = default) + public async Task> SubscribeToKlineUpdatesAsync(IEnumerable symbols, KlineInterval interval, Action>> handler, CancellationToken ct = default) { - var subSymbols = symbols.Select(SymbolToServer); - - var subscription = new KrakenSubscription(_logger, "ohlc", subSymbols.ToArray(), int.Parse(JsonConvert.SerializeObject(interval, new KlineIntervalConverter(false))), null, handler); - return await SubscribeAsync(subscription, ct).ConfigureAwait(false); + var subscription = new KrakenSubscriptionV2>(_logger, "ohlc", symbols.ToArray(), int.Parse(EnumConverter.GetString(interval)), null, null, + x => handler(x.WithSymbol(x.Data.First().Symbol)) + ); + return await SubscribeAsync(BaseAddress.AppendPath("v2"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region Trade + /// - public Task> SubscribeToTradeUpdatesAsync(string symbol, Action>> handler, CancellationToken ct = default) - => SubscribeToTradeUpdatesAsync(new[] { symbol }, handler, ct); + public Task> SubscribeToTradeUpdatesAsync(string symbol, Action>> handler, bool? snapshot = null, CancellationToken ct = default) + => SubscribeToTradeUpdatesAsync([symbol], handler, snapshot, ct); /// - public async Task> SubscribeToTradeUpdatesAsync(IEnumerable symbols, Action>> handler, CancellationToken ct = default) + public async Task> SubscribeToTradeUpdatesAsync(IEnumerable symbols, Action>> handler, bool? snapshot = null, CancellationToken ct = default) { - var subSymbols = symbols.Select(SymbolToServer); - var subscription = new KrakenSubscription>(_logger, "trade", subSymbols.ToArray(), null, null, handler); - return await SubscribeAsync(subscription, ct).ConfigureAwait(false); + var subscription = new KrakenSubscriptionV2>(_logger, "trade", symbols.ToArray(), null, snapshot, null, + x => handler(x.WithSymbol(x.Data.First().Symbol)) + ); + return await SubscribeAsync(BaseAddress.AppendPath("v2"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region Aggregated Order Book + /// - public Task> SubscribeToSpreadUpdatesAsync(string symbol, Action> handler, CancellationToken ct = default) - => SubscribeToSpreadUpdatesAsync(new[] { symbol }, handler, ct); + public Task> SubscribeToAggregatedOrderBookUpdatesAsync(string symbol, int depth, Action> handler, bool? snapshot = null, CancellationToken ct = default) + => SubscribeToAggregatedOrderBookUpdatesAsync([symbol], depth, handler, snapshot, ct); /// - public async Task> SubscribeToSpreadUpdatesAsync(IEnumerable symbols, Action> handler, CancellationToken ct = default) + public async Task> SubscribeToAggregatedOrderBookUpdatesAsync(IEnumerable symbols, int depth, Action> handler, bool? snapshot = null, CancellationToken ct = default) { - var subscription = new KrakenSubscription(_logger, "spread", symbols.ToArray(), null, null, handler); - return await SubscribeAsync(subscription, ct).ConfigureAwait(false); + depth.ValidateIntValues(nameof(depth), 10, 25, 100, 500, 1000); + + var subscription = new KrakenSubscriptionV2>(_logger, "book", symbols.ToArray(), null, snapshot, null, + x => handler(x.As(x.Data.First()).WithSymbol(x.Data.First().Symbol)) + ); + return await SubscribeAsync(BaseAddress.AppendPath("v2"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region Order Book + /// - public Task> SubscribeToOrderBookUpdatesAsync(string symbol, int depth, Action> handler, CancellationToken ct = default) - => SubscribeToOrderBookUpdatesAsync(new[] { symbol }, depth, handler, ct); + public Task> SubscribeToInvidualOrderBookUpdatesAsync(string symbol, int depth, Action> handler, bool? snapshot = null, CancellationToken ct = default) + => SubscribeToInvidualOrderBookUpdatesAsync([symbol], depth, handler, snapshot, ct); /// - public async Task> SubscribeToOrderBookUpdatesAsync(IEnumerable symbols, int depth, Action> handler, CancellationToken ct = default) + public async Task> SubscribeToInvidualOrderBookUpdatesAsync(IEnumerable symbols, int depth, Action> handler, bool? snapshot = null, CancellationToken ct = default) { - depth.ValidateIntValues(nameof(depth), 10, 25, 100, 500, 1000); - var subSymbols = symbols.Select(SymbolToServer); + depth.ValidateIntValues(nameof(depth), 10, 100, 1000); + + var token = await GetTokenAsync().ConfigureAwait(false); + if (!token) + return new CallResult(token.Error!); - var subscription = new KrakenBookSubscription(_logger, subSymbols.ToArray(), depth, handler); - return await SubscribeAsync(subscription, ct).ConfigureAwait(false); + var subscription = new KrakenSubscriptionV2>(_logger, "level3", symbols.ToArray(), null, snapshot, token.Data, + x => handler(x.As(x.Data.First()).WithSymbol(x.Data.First().Symbol)) + ); + return await SubscribeAsync(_privateBaseAddress.AppendPath("v2"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region Instrument + /// - public async Task> SubscribeToOrderUpdatesAsync(string socketToken, Action>> handler, CancellationToken ct = default) + public async Task> SubscribeToInstrumentUpdatesAsync(Action> handler, bool? snapshot = null, CancellationToken ct = default) { - var innerHandler = new Action>>>>(data => - { - foreach (var item in data.Data.Data) - { - foreach (var value in item) - { - value.Value.Id = value.Key; - value.Value.SequenceNumber = data.Data.Sequence.Sequence; - } - } - - handler.Invoke(data.As>(data.Data.Data.SelectMany(d => d.Values).ToList())); - }); - - var subscription = new KrakenAuthSubscription>>(_logger, "openOrders", socketToken, innerHandler); - return await SubscribeAsync(_privateBaseAddress, subscription, ct).ConfigureAwait(false); + var subscription = new KrakenSubscriptionV2(_logger, "instrument", null, null, snapshot, null, handler); + return await SubscribeAsync(BaseAddress.AppendPath("v2"), subscription, ct).ConfigureAwait(false); } + #endregion + + #region Balance + /// - public Task> SubscribeToUserTradeUpdatesAsync(string socketToken, Action>> handler, CancellationToken ct = default) - => SubscribeToUserTradeUpdatesAsync(socketToken, true, handler, ct); + public async Task> SubscribeToBalanceUpdatesAsync(Action>>? snapshotHandler, Action>> updateHandler, bool? snapshot = null, CancellationToken ct = default) + { + var token = await GetTokenAsync().ConfigureAwait(false); + if (!token) + return new CallResult(token.Error!); + + var subscription = new KrakenBalanceSubscription(_logger, snapshot, token.Data, snapshotHandler, updateHandler); + return await SubscribeAsync(_privateBaseAddress.AppendPath("v2"), subscription, ct).ConfigureAwait(false); + } + + #endregion + + #region Order /// - public async Task> SubscribeToUserTradeUpdatesAsync(string socketToken, bool snapshot, Action>> handler, CancellationToken ct = default) + public async Task> SubscribeToOrderUpdatesAsync( + Action>> updateHandler, + bool? snapshotOrder = null, + bool? snapshotTrades = null, + CancellationToken ct = default) { - var innerHandler = new Action>>>>(data => - { - foreach (var item in data.Data.Data) - { - foreach (var value in item) - { - value.Value.Id = value.Key; - value.Value.SequenceNumber = data.Data.Sequence.Sequence; - } - } - handler.Invoke(data.As(data.Data.Data.SelectMany(d => d.Values))); - }); + var token = await GetTokenAsync().ConfigureAwait(false); + if (!token) + return new CallResult(token.Error!); - var subscription = new KrakenAuthSubscription>>(_logger, "ownTrades", socketToken, innerHandler); - return await SubscribeAsync(_privateBaseAddress, subscription, ct).ConfigureAwait(false); + var subscription = new KrakenOrderSubscription(_logger, snapshotOrder, snapshotTrades, token.Data, updateHandler); + return await SubscribeAsync(_privateBaseAddress.AppendPath("v2"), subscription, ct).ConfigureAwait(false); } + #endregion + + #endregion + + #region Queries + + #region Place Order + /// - public async Task> PlaceOrderAsync( - string websocketToken, + public async Task> PlaceOrderAsync( string symbol, - OrderType type, OrderSide side, + OrderType type, decimal quantity, - uint? clientOrderId = null, - decimal? price = null, - decimal? secondaryPrice = null, - decimal? leverage = null, + string? clientOrderId = null, + uint? userReference = null, + decimal? limitPrice = null, + PriceType? limitPriceType = null, + TimeInForce? timeInForce = null, + bool? reduceOnly = null, + bool? margin = null, + bool? postOnly = null, DateTime? startTime = null, DateTime? expireTime = null, + DateTime? deadline = null, + decimal? icebergQuantity = null, + FeePreference? feePreference = null, + bool? noMarketPriceProtection = null, + SelfTradePreventionType? selfTradePreventionType = null, + decimal? quoteQuantity = null, bool? validateOnly = null, - OrderType? closeOrderType = null, - decimal? closePrice = null, - decimal? secondaryClosePrice = null, - IEnumerable? flags = null, - bool? reduceOnly = null, - bool? margin = null, + + Trigger? triggerPriceReference = null, + decimal? triggerPrice = null, + PriceType? triggerPriceType = null, + + OrderType? conditionalOrderType = null, + decimal? conditionalLimitPrice = null, + PriceType? conditionalLimitPriceType = null, + decimal? conditionalTriggerPrice = null, + PriceType? conditionalTriggerPriceType = null, + CancellationToken ct = default) { - var request = new KrakenSocketPlaceOrderRequest + + var token = await GetTokenAsync().ConfigureAwait(false); + if (!token) + return new CallResult(token.Error!); + + var request = new KrakenSocketPlaceOrderRequestV2 { - Event = "addOrder", - Token = websocketToken, + Token = token.Data, Symbol = symbol, - Type = side, + Side = side, OrderType = type, - Volume = quantity.ToString(CultureInfo.InvariantCulture), - ClientOrderId = clientOrderId?.ToString(), - Price = price?.ToString(CultureInfo.InvariantCulture), - SecondaryPrice = secondaryPrice?.ToString(CultureInfo.InvariantCulture), - Leverage = leverage?.ToString(CultureInfo.InvariantCulture), - StartTime = (startTime.HasValue && startTime > DateTime.UtcNow) ? DateTimeConverter.ConvertToSeconds(startTime).ToString() : null, - ExpireTime = expireTime.HasValue ? DateTimeConverter.ConvertToSeconds(expireTime).ToString() : null, - ValidateOnly = validateOnly, - CloseOrderType = closeOrderType, - ClosePrice = closePrice?.ToString(CultureInfo.InvariantCulture), - SecondaryClosePrice = secondaryClosePrice?.ToString(CultureInfo.InvariantCulture), + Quantity = quantity, + ClientOrderId = clientOrderId, + UserReference = userReference, + Price = limitPrice, + LimitPriceType = limitPriceType, + TimeInForce = timeInForce, ReduceOnly = reduceOnly, - RequestId = ExchangeHelpers.NextId(), Margin = margin, - Flags = flags == null ? null : string.Join(",", flags.Select(f => JsonConvert.SerializeObject(f, new OrderFlagsConverter(false)))) + PostOnly = postOnly, + EffectiveTime = startTime?.ToRfc3339String(), + ExpireTime = expireTime?.ToRfc3339String(), + Deadline = deadline?.ToRfc3339String(), + IcebergQuantity = icebergQuantity, + FeePreference = feePreference, + NoMarketPriceProtection = noMarketPriceProtection, + SelfTradePreventionType = selfTradePreventionType, + QuoteQuantity = quoteQuantity, + ValidateOnly = validateOnly, }; - var query = new KrakenSpotQuery(request, false); - return await QueryAsync(_privateBaseAddress, query, ct).ConfigureAwait(false); + if (triggerPrice != null) + { + request.Trigger = new KrakenSocketPlaceOrderRequestV2Trigger + { + LimitPriceType = triggerPriceType, + Price = triggerPrice, + Reference = triggerPriceReference + }; + } + + if (conditionalOrderType != null) + { + request.Conditional = new KrakenSocketPlaceOrderRequestV2Condition + { + LimitPriceType = conditionalLimitPriceType, + OrderType = conditionalOrderType.Value, + Price = conditionalLimitPrice, + TriggerPrice = conditionalTriggerPrice, + TriggerPriceType = conditionalTriggerPriceType + }; + } + + var requestMessage = new KrakenSocketRequestV2 + { + Method = "add_order", + RequestId = ExchangeHelpers.NextId(), + Parameters = request + }; + + var query = new KrakenSpotQueryV2(requestMessage, false); + var result = await QueryAsync(_privateBaseAddress.AppendPath("v2"), query, ct).ConfigureAwait(false); + return result.As(result.Data?.Result); } + #endregion + + #region Place Multiple Orders + /// - public Task> CancelOrderAsync(string websocketToken, string orderId, CancellationToken ct = default) - => CancelOrdersAsync(websocketToken, new[] { orderId }, ct); + public async Task>> PlaceMultipleOrdersAsync( + string symbol, + IEnumerable orders, + DateTime? deadline = null, + bool? validateOnly = null, + CancellationToken ct = default) + { + var token = await GetTokenAsync().ConfigureAwait(false); + if (!token) + return new CallResult>(token.Error!); + + var request = new KrakenSocketPlaceMultipleOrderRequestV2 + { + Token = token.Data, + Symbol = symbol, + ValidateOnly = validateOnly, + Deadline = deadline?.ToRfc3339String(), + Orders = orders + }; + + var requestMessage = new KrakenSocketRequestV2 + { + Method = "batch_add", + RequestId = ExchangeHelpers.NextId(), + Parameters = request + }; + + var query = new KrakenSpotQueryV2, KrakenSocketPlaceMultipleOrderRequestV2>(requestMessage, false); + var result = await QueryAsync(_privateBaseAddress.AppendPath("v2"), query, ct).ConfigureAwait(false); + if (!result) + return result.As>(default); + + if (!result.Data.Success) + return new CallResult>(result.Data.Result, result.OriginalData, new ServerError("Order placement failed")); + + return result.As>(result.Data?.Result); + } + + #endregion + + #region Edit Order /// - public async Task> CancelOrdersAsync(string websocketToken, IEnumerable orderIds, CancellationToken ct = default) + public async Task> EditOrderAsync( + string? orderId = null, + string? clientOrderId = null, + decimal? limitPrice = null, + PriceType? limitPriceType = null, + decimal? quantity = null, + decimal? icebergQuantity = null, + bool? postOnly = null, + decimal? triggerPrice = null, + PriceType? triggerPriceType = null, + DateTime? deadline = null, + CancellationToken ct = default) { - var request = new KrakenSocketCancelOrdersRequest + var token = await GetTokenAsync().ConfigureAwait(false); + if (!token) + return new CallResult(token.Error!); + + var request = new KrakenSocketEditOrderRequest { - Event = "cancelOrder", - OrderIds = orderIds, - Token = websocketToken, - RequestId = ExchangeHelpers.NextId() + Token = token.Data, + ClientOrderId = clientOrderId, + Deadline = deadline?.ToRfc3339String(), + IcebergQuantity = icebergQuantity, + LimitPriceType = limitPriceType, + OrderId = orderId, + PostOnly = postOnly, + Price = limitPrice, + Quantity = quantity, + TriggerPrice = triggerPrice, + TriggerPriceType = triggerPriceType + }; + var requestMessage = new KrakenSocketRequestV2 + { + Method = "amend_order", + RequestId = ExchangeHelpers.NextId(), + Parameters = request }; - var query = new KrakenSpotQuery(request, false); - var result = await QueryAsync(_privateBaseAddress, query, ct).ConfigureAwait(false); - return result.As(result.Success); + var query = new KrakenSpotQueryV2(requestMessage, false); + var result = await QueryAsync(_privateBaseAddress.AppendPath("v2"), query, ct).ConfigureAwait(false); + return result.As(result.Data?.Result); } + #endregion + + #region Replace Order + /// - public async Task> CancelAllOrdersAsync(string websocketToken, CancellationToken ct = default) + public async Task> ReplaceOrderAsync( + string symbol, + string? orderId = null, + string? clientOrderId = null, + decimal? quantity = null, + uint? userReference = null, + decimal? limitPrice = null, + bool? reduceOnly = null, + bool? postOnly = null, + DateTime? deadline = null, + decimal? icebergQuantity = null, + FeePreference? feePreference = null, + bool? noMarketPriceProtection = null, + bool? validateOnly = null, + + Trigger? triggerPriceReference = null, + decimal? triggerPrice = null, + PriceType? triggerPriceType = null, + CancellationToken ct = default) { - var request = new KrakenSocketAuthRequest + var token = await GetTokenAsync().ConfigureAwait(false); + if (!token) + return new CallResult(token.Error!); + + var request = new KrakenSocketReplaceOrderRequest { - Event = "cancelAll", - Token = websocketToken, - RequestId = ExchangeHelpers.NextId() + Token = token.Data, + Deadline = deadline?.ToRfc3339String(), + IcebergQuantity = icebergQuantity, + OrderId = orderId, + PostOnly = postOnly, + Price = limitPrice, + Quantity = quantity, + FeePreference = feePreference, + NoMarketPriceProtection = noMarketPriceProtection, + ReduceOnly = reduceOnly, + Symbol = symbol, + UserReference = userReference, + ValidateOnly = validateOnly, }; - var query = new KrakenSpotQuery(request, false); - return await QueryAsync(_privateBaseAddress, query, ct).ConfigureAwait(false); + if (triggerPrice != null) + { + request.Trigger = new KrakenSocketPlaceOrderRequestV2Trigger + { + LimitPriceType = triggerPriceType, + Price = triggerPrice, + Reference = triggerPriceReference + }; + } + + var requestMessage = new KrakenSocketRequestV2 + { + Method = "edit_order", + RequestId = ExchangeHelpers.NextId(), + Parameters = request + }; + + var query = new KrakenSpotQueryV2(requestMessage, false); + var result = await QueryAsync(_privateBaseAddress.AppendPath("v2"), query, ct).ConfigureAwait(false); + return result.As(result.Data?.Result); } + #endregion + + #region Cancel Order + /// - public async Task> CancelAllOrdersAfterAsync(string websocketToken, TimeSpan timeout, CancellationToken ct = default) + public Task> CancelOrderAsync(string orderId, CancellationToken ct = default) + => CancelOrdersAsync(new[] { orderId }, ct); + + /// + public async Task> CancelOrdersAsync(IEnumerable orderIds, CancellationToken ct = default) { - var request = new KrakenSocketCancelAfterRequest + var token = await GetTokenAsync().ConfigureAwait(false); + if (!token) + return new CallResult(token.Error!); + + var request = new KrakenSocketCancelOrdersRequestV2 + { + OrderIds = orderIds.ToArray(), + Token = token.Data + }; + var requestMessage = new KrakenSocketRequestV2 { - Event = "cancelAllOrdersAfter", - Token = websocketToken, - Timeout = (int)Math.Round(timeout.TotalSeconds), - RequestId = ExchangeHelpers.NextId() + Method = "cancel_order", + RequestId = ExchangeHelpers.NextId(), + Parameters = request }; - var query = new KrakenSpotQuery(request, false); - return await QueryAsync(_privateBaseAddress, query, ct).ConfigureAwait(false); + var query = new KrakenSpotQueryV2(requestMessage, false); + var result = await QueryAsync(_privateBaseAddress.AppendPath("v2"), query, ct).ConfigureAwait(false); + return result.As(result.Data?.Result); } + #endregion - /// - /// Maps an input symbol to a server symbol - /// - /// - /// - protected string SymbolToServer(string input) + #region Cancel All Orders + + /// + public async Task> CancelAllOrdersAsync(CancellationToken ct = default) { - var split = input.Split('/'); - if (split.Length != 2) - return input; - - var baseAsset = split[0]; - var quoteAsset = split[1]; - if (_symbolSynonyms.TryGetValue(baseAsset.ToUpperInvariant(), out var baseOutput)) - baseAsset = baseOutput; - if (_symbolSynonyms.TryGetValue(baseAsset.ToUpperInvariant(), out var quoteOutput)) - quoteAsset = quoteOutput; - return baseAsset + "/" + quoteAsset; + var token = await GetTokenAsync().ConfigureAwait(false); + if (!token) + return new CallResult(token.Error!); + + var request = new KrakenSocketAuthRequestV2 + { + Token = token.Data + }; + var requestMessage = new KrakenSocketRequestV2 + { + Method = "cancel_all", + RequestId = ExchangeHelpers.NextId(), + Parameters = request + }; + + var query = new KrakenSpotQueryV2(requestMessage, false); + var result = await QueryAsync(_privateBaseAddress.AppendPath("v2"), query, ct).ConfigureAwait(false); + return result.As(result.Data?.Result); } + #endregion + + #region Cancel All Orders After + + /// + public async Task> CancelAllOrdersAfterAsync(TimeSpan timeout, CancellationToken ct = default) + { + var token = await GetTokenAsync().ConfigureAwait(false); + if (!token) + return new CallResult(token.Error!); + + var request = new KrakenSocketCancelAfterRequest + { + Token = token.Data, + Timeout = (int)timeout.TotalSeconds + }; + var requestMessage = new KrakenSocketRequestV2 + { + Method = "cancel_all_orders_after", + RequestId = ExchangeHelpers.NextId(), + Parameters = request + }; + + var query = new KrakenSpotQueryV2(requestMessage, false); + var result = await QueryAsync(_privateBaseAddress.AppendPath("v2"), query, ct).ConfigureAwait(false); + return result.As(result.Data?.Result); + } + + #endregion + + #endregion + /// protected override async Task RevitalizeRequestAsync(Subscription subscription) { - if (subscription is not KrakenAuthSubscription authSubscription) + var krakenSubscription = (KrakenSubscription)subscription; + + if (!krakenSubscription.TokenRequired) return new CallResult(null); + var token = await GetTokenAsync().ConfigureAwait(false); + if (!token) + return token.AsDataless(); + + krakenSubscription.Token = token.Data; + return new CallResult(null); + } + + private async Task> GetTokenAsync() + { var apiCredentials = ApiOptions.ApiCredentials ?? ClientOptions.ApiCredentials; + if (apiCredentials == null) + return new CallResult(new NoApiCredentialsError()); + + if (_tokenCache.TryGetValue(apiCredentials.Key, out var token) && token.Expire > DateTime.UtcNow) + return new CallResult(token.Token); + + _logger.LogDebug("Requesting websocket token"); var restClient = new KrakenRestClient(x => { x.ApiCredentials = apiCredentials; x.Environment = ClientOptions.Environment; }); - var newToken = await restClient.SpotApi.Account.GetWebsocketTokenAsync().ConfigureAwait(false); - if (!newToken.Success) - return newToken.AsDataless(); + var result = await restClient.SpotApi.Account.GetWebsocketTokenAsync().ConfigureAwait(false); + if (result) + _tokenCache[apiCredentials.Key] = new CachedToken { Token = result.Data.Token, Expire = DateTime.UtcNow.AddSeconds(result.Data.Expires - 5) }; + else + _logger.LogWarning("Failed to retrieve websocket token: {Error}", result.Error); - authSubscription.UpdateToken(newToken.Data.Token); - return new CallResult(null); + return result.As(result.Data?.Token); + } + + private class CachedToken + { + public string Token { get; set; } = string.Empty; + public DateTime Expire { get; set; } } } } \ No newline at end of file diff --git a/Kraken.Net/Clients/SpotApi/KrakenSocketClientSpotApiShared.cs b/Kraken.Net/Clients/SpotApi/KrakenSocketClientSpotApiShared.cs index 9f6378e..6540092 100644 --- a/Kraken.Net/Clients/SpotApi/KrakenSocketClientSpotApiShared.cs +++ b/Kraken.Net/Clients/SpotApi/KrakenSocketClientSpotApiShared.cs @@ -1,6 +1,7 @@ using Kraken.Net.Interfaces.Clients.SpotApi; using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.Objects.Sockets; +using Kraken.Net.Enums; namespace Kraken.Net.Clients.SpotApi { @@ -21,7 +22,7 @@ async Task> ITickerSocketClient.SubscribeToTi return new ExchangeResult(Exchange, validationError); var symbol = request.Symbol.GetSymbol(FormatSymbol); - var result = await SubscribeToTickerUpdatesAsync(symbol, update => handler(update.AsExchangeEvent(Exchange, new SharedSpotTicker(symbol, update.Data.LastTrade.Price, update.Data.High.Value24H, update.Data.Low.Value24H, update.Data.Volume.Value24H, update.Data.Open.Value24H == 0 ? null : Math.Round(update.Data.LastTrade.Price / update.Data.Open.Value24H * 100 - 100, 2))))).ConfigureAwait(false); + var result = await SubscribeToTickerUpdatesAsync(symbol, update => handler(update.AsExchangeEvent(Exchange, new SharedSpotTicker(symbol, update.Data.LastPrice, update.Data.HighPrice, update.Data.LowPrice, update.Data.Volume, update.Data.PriceChangePercentage))), ct).ConfigureAwait(false); return new ExchangeResult(Exchange, result); } @@ -37,7 +38,7 @@ async Task> ITradeSocketClient.SubscribeToTra return new ExchangeResult(Exchange, validationError); var symbol = request.Symbol.GetSymbol(FormatSymbol); - var result = await SubscribeToTradeUpdatesAsync(symbol, update => handler(update.AsExchangeEvent(Exchange, update.Data.Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)))), ct).ConfigureAwait(false); + var result = await SubscribeToTradeUpdatesAsync(symbol, update => handler(update.AsExchangeEvent(Exchange, update.Data.Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)))), false, ct).ConfigureAwait(false); return new ExchangeResult(Exchange, result); } @@ -53,7 +54,7 @@ async Task> IBookTickerSocketClient.Subscribe return new ExchangeResult(Exchange, validationError); var symbol = request.Symbol.GetSymbol(FormatSymbol); - var result = await SubscribeToSpreadUpdatesAsync(symbol, update => handler(update.AsExchangeEvent(Exchange, new SharedBookTicker(update.Data.BestAskPrice, update.Data.BestAskQuantity, update.Data.BestBidPrice, update.Data.BestBidQuantity))), ct).ConfigureAwait(false); + var result = await SubscribeToTickerUpdatesAsync(symbol, update => handler(update.AsExchangeEvent(Exchange, new SharedBookTicker(update.Data.BestAskPrice, update.Data.BestAskQuantity, update.Data.BestBidPrice, update.Data.BestBidQuantity))), ct).ConfigureAwait(false); return new ExchangeResult(Exchange, result); } @@ -72,25 +73,81 @@ async Task> IKlineSocketClient.SubscribeToKli return new ExchangeResult(Exchange, validationError); var symbol = request.Symbol.GetSymbol(FormatSymbol); - var result = await SubscribeToKlineUpdatesAsync(symbol, interval, update => handler(update.AsExchangeEvent(Exchange, new SharedKline(update.Data.OpenTime, update.Data.ClosePrice, update.Data.HighPrice, update.Data.LowPrice, update.Data.OpenPrice, update.Data.Volume))), ct).ConfigureAwait(false); + var result = await SubscribeToKlineUpdatesAsync(symbol, interval, update => + { + if (update.UpdateType == SocketUpdateType.Snapshot) + return; + + foreach(var item in update.Data) + handler(update.AsExchangeEvent(Exchange, new SharedKline(item.OpenTime, item.ClosePrice, item.HighPrice, item.LowPrice, item.OpenPrice, item.Volume))); + }, ct).ConfigureAwait(false); return new ExchangeResult(Exchange, result); } #endregion - // Can be implemented with V2 websockets - //async Task> IBalanceSocketClient.SubscribeToBalanceUpdatesAsync(SharedRequest request, Action>> handler, CancellationToken ct) - //{ - // var result = await SubscribeTo( - // update => handler(update.As(update.Data.Select(x => new SharedBalance(x.Asset, x.Available, x.Total)))), - // ct: ct).ConfigureAwait(false); + #region Balance client + EndpointOptions IBalanceSocketClient.SubscribeBalanceOptions { get; } = new EndpointOptions(false); + async Task> IBalanceSocketClient.SubscribeToBalanceUpdatesAsync(SubscribeBalancesRequest request, Action>> handler, CancellationToken ct) + { + var validationError = ((IBalanceSocketClient)this).SubscribeBalanceOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + if (validationError != null) + return new ExchangeResult(Exchange, validationError); + var result = await SubscribeToBalanceUpdatesAsync( + null, + update => handler(update.AsExchangeEvent>(Exchange, update.Data.Select(x => + new SharedBalance(x.Asset, x.Balance, x.Balance)).ToArray())), + false, + ct: ct).ConfigureAwait(false); + + return new ExchangeResult(Exchange, result); + } + #endregion - // return result; - //} + #region Spot Order client - // Can be implemented with V2 websockets - //async Task> ISpotOrderSocketClient.SubscribeToOrderUpdatesAsync(SharedRequest request, Action>> handler, CancellationToken ct) - //{ - //} + EndpointOptions ISpotOrderSocketClient.SubscribeSpotOrderOptions { get; } = new EndpointOptions(false); + async Task> ISpotOrderSocketClient.SubscribeToSpotOrderUpdatesAsync(SubscribeSpotOrderRequest request, Action>> handler, CancellationToken ct) + { + var validationError = ((ISpotOrderSocketClient)this).SubscribeSpotOrderOptions.ValidateRequest(Exchange, request, TradingMode.Spot, SupportedTradingModes); + if (validationError != null) + return new ExchangeResult(Exchange, validationError); + + var result = await SubscribeToOrderUpdatesAsync( + update => handler(update.AsExchangeEvent>(Exchange, update.Data.Select( + x => new SharedSpotOrder( + x.Symbol ?? string.Empty, + x.OrderId, + x.OrderType == Enums.OrderType.Limit ? SharedOrderType.Limit : x.OrderType == Enums.OrderType.Market ? SharedOrderType.Market : SharedOrderType.Other, + x.OrderSide == Enums.OrderSide.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell, + ParseStatus(x.OrderStatus), + x.Timestamp) + { + AveragePrice = x.AveragePrice, + ClientOrderId = x.ClientOrderId, + OrderId = x.OrderId, + OrderPrice = x.LimitPrice, + Quantity = x.OrderQuantity, + QuantityFilled = x.QuantityFilled, + QuoteQuantity = x.QuoteOrderQuantity, + QuoteQuantityFilled = x.ValueFilled, + TimeInForce = x.TimeInForce == TimeInForce.IOC ? SharedTimeInForce.ImmediateOrCancel : x.TimeInForce == TimeInForce.GTC ? SharedTimeInForce.GoodTillCanceled : null, + LastTrade = x.LastTradeId == null ? null : new SharedUserTrade(x.Symbol ?? string.Empty, x.OrderId, x.LastTradeId.ToString(), x.OrderSide == OrderSide.Sell ? SharedOrderSide.Sell : SharedOrderSide.Buy, x.LastTradeQuantity ?? 0, x.LastTradePrice ?? 0, x.Timestamp) + } + ).ToArray())), + false, + false, + ct: ct).ConfigureAwait(false); + + return new ExchangeResult(Exchange, result); + } + + private SharedOrderStatus ParseStatus(OrderStatusUpdate orderStatus) + { + if (orderStatus == OrderStatusUpdate.New || orderStatus == OrderStatusUpdate.Pending || orderStatus == OrderStatusUpdate.PartiallyFilled) return SharedOrderStatus.Open; + if (orderStatus == OrderStatusUpdate.Canceled || orderStatus == OrderStatusUpdate.Expired) return SharedOrderStatus.Canceled; + return SharedOrderStatus.Filled; + } + #endregion } } diff --git a/Kraken.Net/Converters/ExtendedDictionaryConverter.cs b/Kraken.Net/Converters/ExtendedDictionaryConverter.cs index 7385634..ca10e08 100644 --- a/Kraken.Net/Converters/ExtendedDictionaryConverter.cs +++ b/Kraken.Net/Converters/ExtendedDictionaryConverter.cs @@ -1,46 +1,43 @@ -using System; -using System.Collections.Generic; -using CryptoExchange.Net.Converters; -using Kraken.Net.Objects.Models; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using Kraken.Net.Objects.Models; namespace Kraken.Net.Converters { - internal class ExtendedDictionaryConverter: JsonConverter + internal class ExtendedDictionaryConverter: JsonConverter where T : KrakenDictionaryResult { - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - var data = (KrakenDictionaryResult) value!; - writer.WriteStartObject(); - writer.WritePropertyName("data"); - writer.WriteRawValue(JsonConvert.SerializeObject(data.Data)); - writer.WritePropertyName("last"); - writer.WriteValue(DateTimeConverter.ConvertToSeconds(data.LastUpdateTime)); - writer.WriteEndObject(); - } - - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - var obj = JObject.Load(reader); - var inner = obj.First; - if (inner?.First == null) - return null; + var doc = JsonDocument.ParseValue(ref reader); + var inner = doc.RootElement.EnumerateObject().First().Value; - var data = inner.First.ToObject(); - var result = (KrakenDictionaryResult)Activator.CreateInstance(objectType); + var data = inner.Deserialize(); + var result = (T)Activator.CreateInstance(typeToConvert); result.Data = data!; - var lastValue = obj["last"]; - if (lastValue != null) + if (doc.RootElement.TryGetProperty("last", out var lastElement)) { - result.LastUpdateTime = lastValue.ToObject(new JsonSerializer() { Converters = { new DateTimeConverter() } }); + DateTime last = default; + if (lastElement.ValueKind == JsonValueKind.Number) + { + var intVal = lastElement.GetInt32(); + last = DateTimeConverter.ConvertFromSeconds(intVal); + } + else + { + var strVal = lastElement.GetString(); + last = DateTimeConverter.ParseFromString(strVal!); + } + result.LastUpdateTime = last; } - return Convert.ChangeType(result, objectType); + + return (T)Convert.ChangeType(result, typeToConvert); } - public override bool CanConvert(Type objectType) + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { - return true; + writer.WriteStartObject(); + writer.WritePropertyName("data"); + writer.WriteRawValue(JsonSerializer.Serialize(value.Data)); + writer.WriteNumber("last", (long)DateTimeConverter.ConvertToSeconds(value.LastUpdateTime)); + writer.WriteEndObject(); } } @@ -53,19 +50,20 @@ public class KrakenDictionaryResult /// /// The data /// + [JsonPropertyName("data")] public T Data { get; set; } = default!; /// /// The timestamp of the data /// [JsonConverter(typeof(DateTimeConverter))] - [JsonProperty("last")] + [JsonPropertyName("last")] public DateTime LastUpdateTime { get; set; } } /// /// Kline result /// - [JsonConverter(typeof(ExtendedDictionaryConverter>))] + [JsonConverter(typeof(ExtendedDictionaryConverter>))] public class KrakenKlinesResult : KrakenDictionaryResult> { } @@ -73,7 +71,7 @@ public class KrakenKlinesResult : KrakenDictionaryResult /// Trade result /// - [JsonConverter(typeof(ExtendedDictionaryConverter>))] + [JsonConverter(typeof(ExtendedDictionaryConverter>))] public class KrakenTradesResult : KrakenDictionaryResult> { } @@ -81,7 +79,7 @@ public class KrakenTradesResult : KrakenDictionaryResult /// Spread result /// - [JsonConverter(typeof(ExtendedDictionaryConverter>))] + [JsonConverter(typeof(ExtendedDictionaryConverter>))] public class KrakenSpreadsResult : KrakenDictionaryResult> { } diff --git a/Kraken.Net/Converters/KlineIntervalConverter.cs b/Kraken.Net/Converters/KlineIntervalConverter.cs deleted file mode 100644 index b8179bb..0000000 --- a/Kraken.Net/Converters/KlineIntervalConverter.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; - -namespace Kraken.Net.Converters -{ - internal class KlineIntervalConverter: BaseConverter - { - public KlineIntervalConverter() : this(true) { } - public KlineIntervalConverter(bool quotes) : base(quotes) { } - - protected override List> Mapping => new List> - { - new KeyValuePair(KlineInterval.OneMinute, "1"), - new KeyValuePair(KlineInterval.FiveMinutes, "5"), - new KeyValuePair(KlineInterval.FifteenMinutes, "15"), - new KeyValuePair(KlineInterval.ThirtyMinutes, "30"), - new KeyValuePair(KlineInterval.OneHour, "60"), - new KeyValuePair(KlineInterval.FourHour, "240"), - new KeyValuePair(KlineInterval.OneDay, "1440"), - new KeyValuePair(KlineInterval.OneWeek, "10080"), - new KeyValuePair(KlineInterval.FifteenDays, "21600"), - }; - } -} diff --git a/Kraken.Net/Converters/KrakenFuturesBalancesConverter.cs b/Kraken.Net/Converters/KrakenFuturesBalancesConverter.cs index 0f8b413..1093225 100644 --- a/Kraken.Net/Converters/KrakenFuturesBalancesConverter.cs +++ b/Kraken.Net/Converters/KrakenFuturesBalancesConverter.cs @@ -1,50 +1,41 @@ using Kraken.Net.Objects.Models.Futures; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; namespace Kraken.Net.Converters { - internal class KrakenFuturesBalancesConverter : JsonConverter + internal class KrakenFuturesBalancesConverter : JsonConverter { - public override bool CanConvert(Type objectType) + public override KrakenFuturesBalances? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - return typeof(KrakenBalances).IsAssignableFrom(objectType); - } - - public override object ReadJson(JsonReader reader, - Type objectType, object? existingValue, JsonSerializer serializer) - { - JObject jo = JObject.Load(reader); - - KrakenBalances balances = null!; - var type = (string?)jo["type"]; - switch (type) + var doc = JsonDocument.ParseValue(ref reader); + var result = new KrakenFuturesBalances(); + var marginAccounts = new List(); + foreach (var element in doc.RootElement.EnumerateObject()) { - case "marginAccount": - balances = new KrakenMarginAccountBalances(); - break; - case "cashAccount": - balances = new KrakenCashBalances(); - break; - case "multiCollateralMarginAccount": - balances = new KrakenMultiCollateralMarginBalances(); - break; - } + var type = element.Value.GetProperty("type").GetString(); - serializer.Populate(jo.CreateReader(), balances); - return balances; - } + switch (type) + { + case "marginAccount": + var balance = element.Value.Deserialize()!; + balance.Symbol = element.Name; + marginAccounts.Add(balance); + break; + case "cashAccount": + result.CashAccount = element.Value.Deserialize()!; + break; + case "multiCollateralMarginAccount": + result.MultiCollateralMarginAccount = element.Value.Deserialize()!; + break; + } + } - public override bool CanWrite - { - get { return false; } + result.MarginAccounts = marginAccounts; + return result; } - public override void WriteJson(JsonWriter writer, - object? value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, KrakenFuturesBalances value, JsonSerializerOptions options) { - throw new NotImplementedException(); + writer.WriteNullValue(); } } } diff --git a/Kraken.Net/Converters/LedgerEntryTypeConverter.cs b/Kraken.Net/Converters/LedgerEntryTypeConverter.cs deleted file mode 100644 index f99e1d4..0000000 --- a/Kraken.Net/Converters/LedgerEntryTypeConverter.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; - -namespace Kraken.Net.Converters -{ - internal class LedgerEntryTypeConverter: BaseConverter - { - public LedgerEntryTypeConverter() : this(true) { } - public LedgerEntryTypeConverter(bool quotes) : base(quotes) { } - - protected override List> Mapping => new List> - { - new KeyValuePair(LedgerEntryType.None, "none"), - new KeyValuePair(LedgerEntryType.Trade, "trade"), - new KeyValuePair(LedgerEntryType.Deposit, "deposit"), - new KeyValuePair(LedgerEntryType.Withdrawal, "withdrawal"), - new KeyValuePair(LedgerEntryType.Margin, "margin"), - new KeyValuePair(LedgerEntryType.Adjustment, "adjustment"), - new KeyValuePair(LedgerEntryType.Transfer, "transfer"), - new KeyValuePair(LedgerEntryType.Rollover, "rollover"), - new KeyValuePair(LedgerEntryType.Spend, "spend"), - new KeyValuePair(LedgerEntryType.Receive, "receive"), - new KeyValuePair(LedgerEntryType.Settled, "settled"), - new KeyValuePair(LedgerEntryType.Staking, "staking"), - new KeyValuePair(LedgerEntryType.Credit, "credit"), - new KeyValuePair(LedgerEntryType.Reward, "reward"), - new KeyValuePair(LedgerEntryType.Dividend, "dividend"), - new KeyValuePair(LedgerEntryType.Sale, "sale"), - new KeyValuePair(LedgerEntryType.Conversion, "conversion"), - new KeyValuePair(LedgerEntryType.NftTrade, "nfttrade"), - new KeyValuePair(LedgerEntryType.NftCreatorFee, "nftcreatorfee"), - new KeyValuePair(LedgerEntryType.NftRebate, "nftrebate"), - new KeyValuePair(LedgerEntryType.CustodyTransfer, "custodytransfer"), - }; - } -} diff --git a/Kraken.Net/Converters/OrderFlagsConverter.cs b/Kraken.Net/Converters/OrderFlagsConverter.cs deleted file mode 100644 index 1edc0e1..0000000 --- a/Kraken.Net/Converters/OrderFlagsConverter.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; - -namespace Kraken.Net.Converters -{ - internal class OrderFlagsConverter : BaseConverter - { - public OrderFlagsConverter() : this(true) { } - public OrderFlagsConverter(bool quotes) : base(quotes) { } - - protected override List> Mapping => new List> - { - new KeyValuePair(OrderFlags.PostOnly, "post"), - new KeyValuePair(OrderFlags.FeeCalculationInBaseAsset, "fcib"), - new KeyValuePair(OrderFlags.FeeCalculationInQuoteAsset, "fciq"), - new KeyValuePair(OrderFlags.NoMarketPriceProtection, "nompp"), - new KeyValuePair(OrderFlags.OrderVolumeExpressedInQuoteAsset, "viqc") - }; - } -} \ No newline at end of file diff --git a/Kraken.Net/Converters/OrderSideConverter.cs b/Kraken.Net/Converters/OrderSideConverter.cs deleted file mode 100644 index 0f0e566..0000000 --- a/Kraken.Net/Converters/OrderSideConverter.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; - -namespace Kraken.Net.Converters -{ - internal class OrderSideConverter: BaseConverter - { - public OrderSideConverter() : this(true) { } - public OrderSideConverter(bool quotes) : base(quotes) { } - - protected override List> Mapping => new List> - { - new KeyValuePair(OrderSide.Buy, "buy"), - new KeyValuePair(OrderSide.Sell, "sell"), - new KeyValuePair(OrderSide.Buy, "b"), - new KeyValuePair(OrderSide.Sell, "s"), - }; - } -} diff --git a/Kraken.Net/Converters/OrderStatusConverter.cs b/Kraken.Net/Converters/OrderStatusConverter.cs deleted file mode 100644 index 328ca6d..0000000 --- a/Kraken.Net/Converters/OrderStatusConverter.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; - -namespace Kraken.Net.Converters -{ - internal class OrderStatusConverter: BaseConverter - { - public OrderStatusConverter() : this(true) { } - public OrderStatusConverter(bool quotes) : base(quotes) { } - - protected override List> Mapping => new List> - { - new KeyValuePair(OrderStatus.Open, "open"), - new KeyValuePair(OrderStatus.Pending, "pending"), - new KeyValuePair(OrderStatus.Closed, "closed"), - new KeyValuePair(OrderStatus.Canceled, "canceled"), - new KeyValuePair(OrderStatus.Expired, "expired"), - }; - } -} diff --git a/Kraken.Net/Converters/OrderTypeConverter.cs b/Kraken.Net/Converters/OrderTypeConverter.cs deleted file mode 100644 index c977d50..0000000 --- a/Kraken.Net/Converters/OrderTypeConverter.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; - -namespace Kraken.Net.Converters -{ - internal class OrderTypeConverter: BaseConverter - { - public OrderTypeConverter() : this(true) { } - public OrderTypeConverter(bool quotes) : base(quotes) { } - - protected override List> Mapping => new List> - { - new KeyValuePair(OrderType.Limit, "limit"), - new KeyValuePair(OrderType.Market, "market"), - new KeyValuePair(OrderType.StopMarket, "stop market"), - new KeyValuePair(OrderType.StopLimit, "stop limit"), - new KeyValuePair(OrderType.StopLoss, "stop-loss"), - new KeyValuePair(OrderType.TakeProfit, "take-profit"), - new KeyValuePair(OrderType.StopLossProfit, "stop-loss-profit"), - new KeyValuePair(OrderType.StopLossProfitLimit, "stop-loss-profit-limit"), - new KeyValuePair(OrderType.StopLossLimit, "stop-loss-limit"), - new KeyValuePair(OrderType.TakeProfitLimit, "take-profit-limit"), - new KeyValuePair(OrderType.TrailingStop, "trailing-stop"), - new KeyValuePair(OrderType.TrailingStopLimit, "trailing-stop-limit"), - new KeyValuePair(OrderType.StopLossAndLimit, "stop-loss-and-limit"), - new KeyValuePair(OrderType.SettlePosition, "settle-position"), - }; - } -} diff --git a/Kraken.Net/Converters/OrderTypeMinimalConverter.cs b/Kraken.Net/Converters/OrderTypeMinimalConverter.cs deleted file mode 100644 index 7af4fb8..0000000 --- a/Kraken.Net/Converters/OrderTypeMinimalConverter.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; - -namespace Kraken.Net.Converters -{ - internal class OrderTypeMinimalConverter: BaseConverter - { - public OrderTypeMinimalConverter() : this(true) { } - public OrderTypeMinimalConverter(bool quotes) : base(quotes) { } - - protected override List> Mapping => new List> - { - new KeyValuePair(OrderTypeMinimal.Limit, "l"), - new KeyValuePair(OrderTypeMinimal.Market, "m"), - }; - } -} diff --git a/Kraken.Net/Converters/StakingRewardTypeConverter.cs b/Kraken.Net/Converters/StakingRewardTypeConverter.cs deleted file mode 100644 index a60d7f4..0000000 --- a/Kraken.Net/Converters/StakingRewardTypeConverter.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; - -namespace Kraken.Net.Converters -{ - internal class StakingRewardTypeConverter : BaseConverter - { - public StakingRewardTypeConverter() : this(true) { } - public StakingRewardTypeConverter(bool quotes) : base(quotes) { } - - protected override List> Mapping => new List> - { - new KeyValuePair(StakingRewardType.Percentage, "percentage") - }; - } -} diff --git a/Kraken.Net/Converters/StakingTransactionStatusConverter.cs b/Kraken.Net/Converters/StakingTransactionStatusConverter.cs deleted file mode 100644 index f11227c..0000000 --- a/Kraken.Net/Converters/StakingTransactionStatusConverter.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; - -namespace Kraken.Net.Converters -{ - internal class StakingTransactionStatusConverter : BaseConverter - { - public StakingTransactionStatusConverter() : this(true) { } - public StakingTransactionStatusConverter(bool quotes) : base(quotes) { } - - protected override List> Mapping => new List> - { - new KeyValuePair(StakingTransactionStatus.Failure, "failure"), - new KeyValuePair(StakingTransactionStatus.Initial, "initial"), - new KeyValuePair(StakingTransactionStatus.Pending, "pending"), - new KeyValuePair(StakingTransactionStatus.Settled, "settled"), - new KeyValuePair(StakingTransactionStatus.Success, "success"), - }; - } -} diff --git a/Kraken.Net/Converters/StakingTypeConverter.cs b/Kraken.Net/Converters/StakingTypeConverter.cs deleted file mode 100644 index d58dbbe..0000000 --- a/Kraken.Net/Converters/StakingTypeConverter.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; - -namespace Kraken.Net.Converters -{ - internal class StakingTypeConverter : BaseConverter - { - public StakingTypeConverter() : this(true) { } - public StakingTypeConverter(bool quotes) : base(quotes) { } - - protected override List> Mapping => new List> - { - new KeyValuePair(StakingType.Bonding, "bonding"), - new KeyValuePair(StakingType.Reward, "reward"), - new KeyValuePair(StakingType.Unbonding, "unbonding"), - }; - } -} diff --git a/Kraken.Net/Converters/StreamOrderBookConverter.cs b/Kraken.Net/Converters/StreamOrderBookConverter.cs deleted file mode 100644 index 01730cf..0000000 --- a/Kraken.Net/Converters/StreamOrderBookConverter.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Collections.Generic; -using Kraken.Net.Objects.Models; -using Kraken.Net.Objects.Sockets; -using Newtonsoft.Json.Linq; - -namespace Kraken.Net.Converters -{ - internal static class StreamOrderBookConverter - { - public static KrakenSocketUpdate? Convert(JArray arr) - { - var result = new KrakenSocketUpdate {ChannelId = (int) arr[0]}; - - var orderBook = new KrakenStreamOrderBook(); - if (arr.Count == 4) - { - var innerObject = arr[1]; - if (innerObject == null) - return null; - - if (innerObject["as"] != null) - { - // snapshot - orderBook.Asks = innerObject["as"]!.ToObject>()!; - orderBook.Bids = innerObject["bs"]!.ToObject>()!; - orderBook.Snapshot = true; - } - else if (innerObject["a"] != null) - { - // Only asks - orderBook.Asks = innerObject["a"]!.ToObject>()!; - } - else - { - // Only bids - orderBook.Bids = innerObject["b"]!.ToObject>()!; - } - - if (innerObject["c"] != null) - orderBook.Checksum = innerObject["c"]!.Value(); - - result.ChannelName = arr[2].ToString(); - result.Symbol = arr[3].ToString(); - } - else - { - orderBook.Asks = arr[1]["a"]!.ToObject>()!; - orderBook.Bids = arr[2]["b"]!.ToObject>()!; - orderBook.Checksum = arr[2]["c"]!.Value(); - result.ChannelName = arr[3].ToString(); - result.Symbol = arr[4].ToString(); - } - - result.Data = orderBook; - return result; - } - } -} diff --git a/Kraken.Net/Converters/SystemStatusConverter.cs b/Kraken.Net/Converters/SystemStatusConverter.cs deleted file mode 100644 index e3c166d..0000000 --- a/Kraken.Net/Converters/SystemStatusConverter.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; - -namespace Kraken.Net.Converters -{ - internal class SystemStatusConverter : BaseConverter - { - public SystemStatusConverter() : this(true) { } - public SystemStatusConverter(bool quotes) : base(quotes) { } - - protected override List> Mapping => new List> - { - new KeyValuePair(SystemStatus.Online, "online"), - new KeyValuePair(SystemStatus.Maintenance, "maintenance"), - new KeyValuePair(SystemStatus.CancelOnly, "cancel_only"), - new KeyValuePair(SystemStatus.PostOnly, "post_only"), - }; - } -} diff --git a/Kraken.Net/Enums/AssetStatus.cs b/Kraken.Net/Enums/AssetStatus.cs index 424e6a0..e68941c 100644 --- a/Kraken.Net/Enums/AssetStatus.cs +++ b/Kraken.Net/Enums/AssetStatus.cs @@ -15,17 +15,27 @@ public enum AssetStatus /// /// Only deposits available /// - [Map("deposit_only")] + [Map("deposit_only", "depositonly")] DepositOnly, /// /// Only withdrawals available /// - [Map("withdrawal_only")] + [Map("withdrawal_only", "withdrawalonly")] WithdrawalOnly, /// /// Funding temp disabled /// - [Map("funding_temporarily_disabled")] - FundingTemporarilyDisabled + [Map("funding_temporarily_disabled", "fundingtemporarilydisabled")] + FundingTemporarilyDisabled, + /// + /// Disabled + /// + [Map("disabled")] + Disabled, + /// + /// Work in process + /// + [Map("work_in_progress")] + WorkInProcess } } diff --git a/Kraken.Net/Enums/AutoCompound.cs b/Kraken.Net/Enums/AutoCompound.cs index 423d30b..8e58998 100644 --- a/Kraken.Net/Enums/AutoCompound.cs +++ b/Kraken.Net/Enums/AutoCompound.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace Kraken.Net.Enums { diff --git a/Kraken.Net/Enums/BalanceUpdateCategory.cs b/Kraken.Net/Enums/BalanceUpdateCategory.cs new file mode 100644 index 0000000..97bcfef --- /dev/null +++ b/Kraken.Net/Enums/BalanceUpdateCategory.cs @@ -0,0 +1,97 @@ +using CryptoExchange.Net.Attributes; + +namespace Kraken.Net.Enums +{ + /// + /// Category + /// + public enum BalanceUpdateCategory + { + /// + /// Deposit + /// + [Map("deposit")] + Deposit, + /// + /// Withdrawal + /// + [Map("withdrawal")] + Withdrawal, + /// + /// Trade + /// + [Map("trade")] + Trade, + /// + /// Margin trade + /// + [Map("margin-trade")] + MarginTrade, + /// + /// Margin settle + /// + [Map("margin-settle")] + MarginSettle, + /// + /// Margin conversion + /// + [Map("margin-conversion")] + MarginConversion, + /// + /// Conversion + /// + [Map("conversion")] + Conversion, + /// + /// Credit + /// + [Map("credit")] + Credit, + /// + /// Marginrollover + /// + [Map("marginrollover")] + Marginrollover, + /// + /// Staking rewards + /// + [Map("staking-rewards")] + StakingRewards, + /// + /// Instant + /// + [Map("instant")] + Instant, + /// + /// Equity trade + /// + [Map("equity-trade")] + EquityTrade, + /// + /// Airdrop + /// + [Map("airdrop")] + Airdrop, + /// + /// Equity dividend + /// + [Map("equity-dividend")] + EquityDividend, + /// + /// Reward bonus + /// + [Map("reward-bonus")] + RewardBonus, + /// + /// Nft + /// + [Map("nft")] + Nft, + /// + /// Block trade + /// + [Map("block-trade")] + BlockTrade, + } + +} diff --git a/Kraken.Net/Enums/BalanceUpdateType.cs b/Kraken.Net/Enums/BalanceUpdateType.cs new file mode 100644 index 0000000..6cfab84 --- /dev/null +++ b/Kraken.Net/Enums/BalanceUpdateType.cs @@ -0,0 +1,92 @@ +using CryptoExchange.Net.Attributes; + +namespace Kraken.Net.Enums +{ + /// + /// Balance update type + /// + public enum BalanceUpdateType + { + /// + /// Deposit + /// + [Map("deposit")] + Deposit, + /// + /// Withdrawal + /// + [Map("withdrawal")] + Withdrawal, + /// + /// Trade + /// + [Map("trade")] + Trade, + /// + /// Margin + /// + [Map("margin")] + Margin, + /// + /// Adjustment + /// + [Map("adjustment")] + Adjustment, + /// + /// Rollover + /// + [Map("rollover")] + Rollover, + /// + /// Credit + /// + [Map("credit")] + Credit, + /// + /// Transfer + /// + [Map("transfer")] + Transfer, + /// + /// Settled + /// + [Map("settled")] + Settled, + /// + /// Staking + /// + [Map("staking")] + Staking, + /// + /// Sale + /// + [Map("sale")] + Sale, + /// + /// Reserve + /// + [Map("reserve")] + Reserve, + /// + /// Conversion + /// + [Map("conversion")] + Conversion, + /// + /// Dividend + /// + [Map("dividend")] + Dividend, + /// + /// Reward + /// + [Map("reward")] + Reward, + /// + /// Creator fee + /// + [Map("creator_fee")] + CreatorFee, + } + +} diff --git a/Kraken.Net/Enums/FeePreference.cs b/Kraken.Net/Enums/FeePreference.cs new file mode 100644 index 0000000..b67ede4 --- /dev/null +++ b/Kraken.Net/Enums/FeePreference.cs @@ -0,0 +1,21 @@ +using CryptoExchange.Net.Attributes; + +namespace Kraken.Net.Enums +{ + /// + /// Fee preference + /// + public enum FeePreference + { + /// + /// In base asset, default for buy orders + /// + [Map("base")] + Static, + /// + /// In quote asset, default for sell orders + /// + [Map("quote")] + Quote + } +} diff --git a/Kraken.Net/Enums/FuturesOrderType.cs b/Kraken.Net/Enums/FuturesOrderType.cs index f114f2e..7f5222a 100644 --- a/Kraken.Net/Enums/FuturesOrderType.cs +++ b/Kraken.Net/Enums/FuturesOrderType.cs @@ -11,7 +11,7 @@ public enum FuturesOrderType /// /// Limit order /// - [Map("lmt")] + [Map("lmt", "limit")] Limit, /// /// Post only limit order @@ -31,7 +31,7 @@ public enum FuturesOrderType /// /// Stop order /// - [Map("stp")] + [Map("stp", "stop")] Stop, /// /// Take profit order diff --git a/Kraken.Net/Enums/InstrumentType.cs b/Kraken.Net/Enums/InstrumentType.cs index 4d4157b..85c10d5 100644 --- a/Kraken.Net/Enums/InstrumentType.cs +++ b/Kraken.Net/Enums/InstrumentType.cs @@ -21,7 +21,7 @@ public enum SymbolType /// Vanilla futures /// [Map("futures_vanilla")] - ValillaFutures, + VanillaFutures, /// /// Spot index /// diff --git a/Kraken.Net/Enums/KlineInterval.cs b/Kraken.Net/Enums/KlineInterval.cs index a400e3f..09766ce 100644 --- a/Kraken.Net/Enums/KlineInterval.cs +++ b/Kraken.Net/Enums/KlineInterval.cs @@ -1,4 +1,6 @@ -namespace Kraken.Net.Enums +using CryptoExchange.Net.Attributes; + +namespace Kraken.Net.Enums { /// /// The time interval of kline data, the int value respresents the time in seconds @@ -8,38 +10,47 @@ public enum KlineInterval /// /// 1m /// + [Map("1")] OneMinute = 60, /// /// 5m /// + [Map("5")] FiveMinutes = 60 * 5, /// /// 15m /// + [Map("15")] FifteenMinutes = 60 * 15, /// /// 30m /// + [Map("30")] ThirtyMinutes = 60 * 30, /// /// 1h /// + [Map("60")] OneHour = 60 * 60, /// /// 4h /// + [Map("240")] FourHour = 60 * 60 * 4, /// /// 1d /// + [Map("1440")] OneDay = 60 * 60 * 24, /// /// 1w /// + [Map("10080")] OneWeek = 60 * 60 * 7, /// /// 15d /// + [Map("21600")] FifteenDays = 60 * 60 * 15 } } diff --git a/Kraken.Net/Enums/LedgerEntryType.cs b/Kraken.Net/Enums/LedgerEntryType.cs index d7a5433..65b561f 100644 --- a/Kraken.Net/Enums/LedgerEntryType.cs +++ b/Kraken.Net/Enums/LedgerEntryType.cs @@ -1,4 +1,6 @@ -namespace Kraken.Net.Enums +using CryptoExchange.Net.Attributes; + +namespace Kraken.Net.Enums { /// /// The type of a ledger entry @@ -8,86 +10,107 @@ public enum LedgerEntryType /// /// Deposit /// + [Map("deposit")] Deposit, /// /// Withdrawal /// + [Map("withdrawal")] Withdrawal, /// /// Trade change /// + [Map("trade")] Trade, /// /// Margin /// + [Map("margin")] Margin, /// /// Adjustment /// + [Map("adjustment")] Adjustment, /// /// Transfer /// + [Map("transfer")] Transfer, /// /// Rollover /// + [Map("rollover")] Rollover, /// /// Spend /// + [Map("spend")] Spend, /// /// Receive /// + [Map("receive")] Receive, /// /// Settled /// + [Map("settled")] Settled, /// /// Staking /// + [Map("staking")] Staking, /// /// None /// + [Map("none")] None, /// /// Credit /// + [Map("credit")] Credit, /// /// Dividend /// + [Map("dividend")] Dividend, /// /// Sale /// + [Map("sale")] Sale, /// /// Reward /// + [Map("reward")] Reward, /// /// Conversion /// + [Map("conversion")] Conversion, /// /// NFT Trade /// + [Map("nfttrade")] NftTrade, /// /// NFT Creator fee /// + [Map("nftcreatorfee")] NftCreatorFee, /// /// NFT rebate /// + [Map("nftrebate")] NftRebate, /// /// Custody transfer /// + [Map("custodytransfer")] CustodyTransfer } } diff --git a/Kraken.Net/Enums/LockType.cs b/Kraken.Net/Enums/LockType.cs index 4231b16..f4bd43a 100644 --- a/Kraken.Net/Enums/LockType.cs +++ b/Kraken.Net/Enums/LockType.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace Kraken.Net.Enums { diff --git a/Kraken.Net/Enums/OrderBookChange.cs b/Kraken.Net/Enums/OrderBookChange.cs new file mode 100644 index 0000000..30dd438 --- /dev/null +++ b/Kraken.Net/Enums/OrderBookChange.cs @@ -0,0 +1,26 @@ +using CryptoExchange.Net.Attributes; + +namespace Kraken.Net.Enums +{ + /// + /// Order book event + /// + public enum OrderBookChange + { + /// + /// Add + /// + [Map("add")] + Add, + /// + /// Modify + /// + [Map("modify")] + Modify, + /// + /// Delete + /// + [Map("delete")] + Delete + } +} diff --git a/Kraken.Net/Enums/OrderEventType.cs b/Kraken.Net/Enums/OrderEventType.cs new file mode 100644 index 0000000..594c5e0 --- /dev/null +++ b/Kraken.Net/Enums/OrderEventType.cs @@ -0,0 +1,57 @@ +using CryptoExchange.Net.Attributes; + +namespace Kraken.Net.Enums +{ + /// + /// Order event type + /// + public enum OrderEventType + { + /// + /// Order request has been received and validated but the order is not live yet. + /// + [Map("pending_new")] + PendingNew, + /// + /// Order has been created and is live in the engine. + /// + [Map("new")] + New, + /// + /// The order has received a fill. + /// + [Map("trade")] + Trade, + /// + /// The order has been fully filled. + /// + [Map("filled")] + Filled, + /// + /// The order has been cancelled. + /// + [Map("canceled")] + Canceled, + /// + /// The order has expired. + /// + [Map("expired")] + Expired, + /// + /// There is a user initiated amend on the order, i.e. limit price change. + /// + [Map("amended")] + Amended, + /// + /// There is a engine initiated amend on the order for maintenance of position or book, see reason field, i.e. reduce non-tradable liquidity. + /// + [Map("restated")] + Restated, + /// + /// The order has a status update, i.e. trigger price has been updated. + /// + [Map("status")] + Status, + } + +} diff --git a/Kraken.Net/Enums/OrderFlags.cs b/Kraken.Net/Enums/OrderFlags.cs index ef1a3ac..57891da 100644 --- a/Kraken.Net/Enums/OrderFlags.cs +++ b/Kraken.Net/Enums/OrderFlags.cs @@ -1,4 +1,6 @@ -namespace Kraken.Net.Enums +using CryptoExchange.Net.Attributes; + +namespace Kraken.Net.Enums { /// /// Flags for an order @@ -8,22 +10,27 @@ public enum OrderFlags /// /// Post only order (only availalbe for limit orders) /// + [Map("post")] PostOnly, /// /// Prefer fee in base asset (fcib) /// + [Map("fcib")] FeeCalculationInBaseAsset, /// /// Prefer fee in quote asset (fciq) /// + [Map("fciq")] FeeCalculationInQuoteAsset, /// /// Disable market price protection (nompp) /// + [Map("nompp")] NoMarketPriceProtection, /// /// Order volume expressed in quote asset. This is supported only for market orders (viqc) /// + [Map("viqc")] OrderVolumeExpressedInQuoteAsset } } \ No newline at end of file diff --git a/Kraken.Net/Enums/OrderSide.cs b/Kraken.Net/Enums/OrderSide.cs index 3b0e9a7..8def0af 100644 --- a/Kraken.Net/Enums/OrderSide.cs +++ b/Kraken.Net/Enums/OrderSide.cs @@ -10,12 +10,12 @@ public enum OrderSide /// /// Buy /// - [Map("buy", "0")] + [Map("buy", "0", "b")] Buy, /// /// Sell /// - [Map("sell", "1")] + [Map("sell", "1", "s")] Sell } } diff --git a/Kraken.Net/Enums/OrderStatus.cs b/Kraken.Net/Enums/OrderStatus.cs index 55b2282..26baf30 100644 --- a/Kraken.Net/Enums/OrderStatus.cs +++ b/Kraken.Net/Enums/OrderStatus.cs @@ -1,4 +1,6 @@ -namespace Kraken.Net.Enums +using CryptoExchange.Net.Attributes; + +namespace Kraken.Net.Enums { /// /// Status of an order @@ -8,22 +10,27 @@ public enum OrderStatus /// /// Pending /// + [Map("pending")] Pending, /// /// Active, not (fully) filled /// + [Map("open")] Open, /// /// Fully filled /// + [Map("closed")] Closed, /// /// Canceled /// + [Map("canceled")] Canceled, /// /// Expired /// + [Map("expired")] Expired } } diff --git a/Kraken.Net/Enums/OrderStatusUpdate.cs b/Kraken.Net/Enums/OrderStatusUpdate.cs new file mode 100644 index 0000000..54d6477 --- /dev/null +++ b/Kraken.Net/Enums/OrderStatusUpdate.cs @@ -0,0 +1,41 @@ +using CryptoExchange.Net.Attributes; + +namespace Kraken.Net.Enums +{ + /// + /// Status of an order + /// + public enum OrderStatusUpdate + { + /// + /// Pending + /// + [Map("pending_new")] + Pending, + /// + /// Active, not filled + /// + [Map("new")] + New, + /// + /// Active, partially filled + /// + [Map("partially_filled")] + PartiallyFilled, + /// + /// Fully filled + /// + [Map("filled")] + Filled, + /// + /// Canceled + /// + [Map("canceled")] + Canceled, + /// + /// Expired + /// + [Map("expired")] + Expired + } +} diff --git a/Kraken.Net/Enums/OrderType.cs b/Kraken.Net/Enums/OrderType.cs index 4110ee4..4fb7650 100644 --- a/Kraken.Net/Enums/OrderType.cs +++ b/Kraken.Net/Enums/OrderType.cs @@ -1,4 +1,6 @@ -namespace Kraken.Net.Enums +using CryptoExchange.Net.Attributes; + +namespace Kraken.Net.Enums { /// /// Order type @@ -8,58 +10,72 @@ public enum OrderType /// /// Limit order /// + [Map("limit")] Limit, /// /// Symbol order /// + [Map("market")] Market, /// /// Stop market order /// + [Map("stop market")] StopMarket, /// /// Stop limit order /// + [Map("stop limit")] StopLimit, /// /// Stop loss order /// + [Map("stop-loss")] StopLoss, /// /// Take profit order /// + [Map("take-profit")] TakeProfit, /// /// Stop loss profit order /// + [Map("stop-loss-profit")] StopLossProfit, /// /// Stop loss profit limit order /// + [Map("stop-loss-profit-limit")] StopLossProfitLimit, /// /// Stop loss limit order /// + [Map("stop-loss-limit")] StopLossLimit, /// /// Take profit limit order /// + [Map("take-profit-limit")] TakeProfitLimit, /// /// Trailing stop order /// + [Map("trailing-stop")] TrailingStop, /// /// Trailing stop limit order /// + [Map("trailing-stop-limit")] TrailingStopLimit, /// /// Stop loss and limit order /// + [Map("stop-loss-and-limit")] StopLossAndLimit, /// /// Settle position /// + [Map("settle-position")] SettlePosition } } diff --git a/Kraken.Net/Enums/OrderTypeMinimal.cs b/Kraken.Net/Enums/OrderTypeMinimal.cs index 8d5a0f7..f64224f 100644 --- a/Kraken.Net/Enums/OrderTypeMinimal.cs +++ b/Kraken.Net/Enums/OrderTypeMinimal.cs @@ -1,4 +1,6 @@ -namespace Kraken.Net.Enums +using CryptoExchange.Net.Attributes; + +namespace Kraken.Net.Enums { /// /// Order type, limited to market or limit @@ -8,10 +10,12 @@ public enum OrderTypeMinimal /// /// Limit order /// + [Map("l")] Limit, /// /// Symbol order /// + [Map("m")] Market } } diff --git a/Kraken.Net/Enums/PriceType.cs b/Kraken.Net/Enums/PriceType.cs new file mode 100644 index 0000000..5f01fd4 --- /dev/null +++ b/Kraken.Net/Enums/PriceType.cs @@ -0,0 +1,26 @@ +using CryptoExchange.Net.Attributes; + +namespace Kraken.Net.Enums +{ + /// + /// Price type + /// + public enum PriceType + { + /// + /// Static market price for the asset, i.e. 30000 for BTC/USD. + /// + [Map("static")] + Static, + /// + /// Percentage offset from the reference price, i.e. -10% from index price. + /// + [Map("pct")] + Percentage, + /// + /// Notional offset from the reference price in the quote currency, i.e, 150 BTC/USD from last price + /// + [Map("quote")] + Quote + } +} diff --git a/Kraken.Net/Enums/RateLimitTier.cs b/Kraken.Net/Enums/RateLimitTier.cs index 04b108d..206c651 100644 --- a/Kraken.Net/Enums/RateLimitTier.cs +++ b/Kraken.Net/Enums/RateLimitTier.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Kraken.Net.Enums +namespace Kraken.Net.Enums { /// /// Tier to use for ratelimiting diff --git a/Kraken.Net/Enums/StakingRewardType.cs b/Kraken.Net/Enums/StakingRewardType.cs deleted file mode 100644 index 670c0c3..0000000 --- a/Kraken.Net/Enums/StakingRewardType.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Kraken.Net.Enums -{ - /// - /// Reward type - /// - public enum StakingRewardType - { - /// - /// Percentage - /// - Percentage - } -} diff --git a/Kraken.Net/Enums/StakingTransactionStatus.cs b/Kraken.Net/Enums/StakingTransactionStatus.cs deleted file mode 100644 index 0a63f77..0000000 --- a/Kraken.Net/Enums/StakingTransactionStatus.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Kraken.Net.Enums -{ - /// - /// The current status of the staking transaction. - /// - public enum StakingTransactionStatus - { -#pragma warning disable CS1591 - Initial, - Pending, - Settled, - Success, - Failure - } -} \ No newline at end of file diff --git a/Kraken.Net/Enums/StakingType.cs b/Kraken.Net/Enums/StakingType.cs deleted file mode 100644 index ff74e2d..0000000 --- a/Kraken.Net/Enums/StakingType.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Kraken.Net.Enums -{ - /// - /// The type of transaction. - /// - public enum StakingType - { -#pragma warning disable CS1591 - Bonding, - Reward, - Unbonding - } -} diff --git a/Kraken.Net/Enums/SymbolStatus.cs b/Kraken.Net/Enums/SymbolStatus.cs index 78d4251..ee08e87 100644 --- a/Kraken.Net/Enums/SymbolStatus.cs +++ b/Kraken.Net/Enums/SymbolStatus.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace Kraken.Net.Enums { @@ -34,6 +31,21 @@ public enum SymbolStatus /// Only allowed to reduce position /// [Map("reduce_only")] - ReduceOnly + ReduceOnly, + /// + /// Delisted + /// + [Map("delisted")] + Delisted, + /// + /// Maintenance + /// + [Map("maintenance")] + Maintenance, + /// + /// Work in process + /// + [Map("work_in_progress")] + WorkInProcess } } diff --git a/Kraken.Net/Enums/SystemStatus.cs b/Kraken.Net/Enums/SystemStatus.cs index bc6b41a..336bd3e 100644 --- a/Kraken.Net/Enums/SystemStatus.cs +++ b/Kraken.Net/Enums/SystemStatus.cs @@ -1,4 +1,6 @@ -namespace Kraken.Net.Enums +using CryptoExchange.Net.Attributes; + +namespace Kraken.Net.Enums { /// /// System status info @@ -8,18 +10,22 @@ public enum SystemStatus /// /// Kraken is operating normally. All order types may be submitted and trades can occur. /// + [Map("online")] Online, /// /// The exchange is offline. No new orders or cancelations may be submitted. /// + [Map("maintenance")] Maintenance, /// /// Resting (open) orders can be canceled but no new orders may be submitted. No trades will occur. /// + [Map("cancel_only")] CancelOnly, /// /// Only post-only limit orders can be submitted. Existing orders may still be canceled. No trades will occur. /// + [Map("post_only")] PostOnly } } diff --git a/Kraken.Net/Enums/TradeType.cs b/Kraken.Net/Enums/TradeType.cs index 2df532a..b3a98b8 100644 --- a/Kraken.Net/Enums/TradeType.cs +++ b/Kraken.Net/Enums/TradeType.cs @@ -10,12 +10,12 @@ public enum TradeType /// /// Maker /// - [Map("maker")] + [Map("maker", "m")] Maker, /// /// Taker /// - [Map("taker")] + [Map("taker", "t")] Taker, /// /// Liquidation diff --git a/Kraken.Net/Enums/WalletType.cs b/Kraken.Net/Enums/WalletType.cs new file mode 100644 index 0000000..77d9d08 --- /dev/null +++ b/Kraken.Net/Enums/WalletType.cs @@ -0,0 +1,21 @@ +using CryptoExchange.Net.Attributes; + +namespace Kraken.Net.Enums +{ + /// + /// Wallet type + /// + public enum WalletType + { + /// + /// Spot + /// + [Map("spot")] + Spot, + /// + /// Earn + /// + [Map("earn")] + Earn + } +} diff --git a/Kraken.Net/Enums/YieldSource.cs b/Kraken.Net/Enums/YieldSource.cs index cda46cd..15efd14 100644 --- a/Kraken.Net/Enums/YieldSource.cs +++ b/Kraken.Net/Enums/YieldSource.cs @@ -1,7 +1,4 @@ using CryptoExchange.Net.Attributes; -using System; -using System.Collections.Generic; -using System.Text; namespace Kraken.Net.Enums { diff --git a/Kraken.Net/ExtensionMethods/KrakenExtensionMethods.cs b/Kraken.Net/ExtensionMethods/KrakenExtensionMethods.cs index da0314e..2570a6b 100644 --- a/Kraken.Net/ExtensionMethods/KrakenExtensionMethods.cs +++ b/Kraken.Net/ExtensionMethods/KrakenExtensionMethods.cs @@ -1,7 +1,4 @@ -using System; -using System.Text.RegularExpressions; - -namespace Kraken.Net.ExtensionMethods +namespace Kraken.Net.ExtensionMethods { /// /// Extension methods specific to using the Kraken API diff --git a/Kraken.Net/ExtensionMethods/ServiceCollectionExtensions.cs b/Kraken.Net/ExtensionMethods/ServiceCollectionExtensions.cs index 9df3174..be6a278 100644 --- a/Kraken.Net/ExtensionMethods/ServiceCollectionExtensions.cs +++ b/Kraken.Net/ExtensionMethods/ServiceCollectionExtensions.cs @@ -1,13 +1,10 @@ using CryptoExchange.Net.Clients; -using CryptoExchange.Net.Interfaces; using Kraken.Net.Clients; using Kraken.Net.Interfaces; using Kraken.Net.Interfaces.Clients; using Kraken.Net.Objects.Options; using Kraken.Net.SymbolOrderBooks; -using System; using System.Net; -using System.Net.Http; namespace Microsoft.Extensions.DependencyInjection { diff --git a/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApi.cs b/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApi.cs index b4000ff..03931f5 100644 --- a/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApi.cs +++ b/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApi.cs @@ -1,6 +1,4 @@ -using CryptoExchange.Net.Interfaces; -using Kraken.Net.Interfaces.Clients.SpotApi; -using System; +using Kraken.Net.Interfaces.Clients.SpotApi; namespace Kraken.Net.Interfaces.Clients.FuturesApi { diff --git a/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApiAccount.cs b/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApiAccount.cs index 082e439..dbaa06e 100644 --- a/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApiAccount.cs +++ b/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApiAccount.cs @@ -1,10 +1,5 @@ -using CryptoExchange.Net.Objects; -using Kraken.Net.Enums; +using Kraken.Net.Enums; using Kraken.Net.Objects.Models.Futures; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; namespace Kraken.Net.Interfaces.Clients.FuturesApi { @@ -15,7 +10,7 @@ public interface IKrakenRestClientFuturesApiAccount { /// /// Get account log entries - /// + /// /// /// Return results after this time /// Return results before this time @@ -30,15 +25,15 @@ public interface IKrakenRestClientFuturesApiAccount /// /// Get asset balances and margin info - /// + /// /// /// Cancellation token /// - Task>> GetBalancesAsync(CancellationToken ct = default); + Task> GetBalancesAsync(CancellationToken ct = default); /// /// Get the PNL currency preference is used to determine which currency to pay out when realizing PNL gains. - /// + /// /// /// Cancellation token /// @@ -46,7 +41,7 @@ public interface IKrakenRestClientFuturesApiAccount /// /// Set the PNL currency preference is used to determine which currency to pay out when realizing PNL gains. - /// + /// /// /// Symbol to update, for example `PF_ETHUSD` /// Currency to use @@ -56,7 +51,7 @@ public interface IKrakenRestClientFuturesApiAccount /// /// Transfer between 2 margin accounts or between margin and cash account - /// + /// /// /// The asset to transfer, for example `USDT` /// The amount to transfer @@ -68,7 +63,7 @@ public interface IKrakenRestClientFuturesApiAccount /// /// Get fee schedule volume - /// + /// /// /// Cancellation token /// @@ -76,7 +71,7 @@ public interface IKrakenRestClientFuturesApiAccount /// /// Get the initial margin requirements for the provided parameters - /// + /// /// /// The symbol, for example `PF_ETHUSD` /// Order type @@ -89,7 +84,7 @@ public interface IKrakenRestClientFuturesApiAccount /// /// Get the max order quantity - /// + /// /// /// The symbol, for example `PF_ETHUSD` /// Order type diff --git a/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApiExchangeData.cs b/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApiExchangeData.cs index 0156641..c6e3304 100644 --- a/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApiExchangeData.cs +++ b/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApiExchangeData.cs @@ -1,10 +1,5 @@ -using CryptoExchange.Net.Objects; -using Kraken.Net.Enums; +using Kraken.Net.Enums; using Kraken.Net.Objects.Models.Futures; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; namespace Kraken.Net.Interfaces.Clients.FuturesApi { @@ -15,7 +10,7 @@ public interface IKrakenRestClientFuturesApiExchangeData { /// /// Get fee schedules - /// + /// /// /// Cancellation token /// @@ -23,7 +18,7 @@ public interface IKrakenRestClientFuturesApiExchangeData /// /// Get historical funding rates - /// + /// /// /// The symbol, for example `PF_ETHUSD` /// Cancellation token @@ -32,7 +27,7 @@ public interface IKrakenRestClientFuturesApiExchangeData /// /// Get klines/candle data - /// + /// /// /// Type of price tick /// The symbol, for example `PF_ETHUSD` @@ -45,7 +40,7 @@ public interface IKrakenRestClientFuturesApiExchangeData /// /// Get the orderbook - /// + /// /// /// The symbol, for example `PF_ETHUSD` /// Cancellation token @@ -54,7 +49,7 @@ public interface IKrakenRestClientFuturesApiExchangeData /// /// Get platform notifications - /// + /// /// /// Cancellation token /// @@ -62,7 +57,7 @@ public interface IKrakenRestClientFuturesApiExchangeData /// /// Get a list of symbols - /// + /// /// /// Cancellation token /// @@ -70,15 +65,24 @@ public interface IKrakenRestClientFuturesApiExchangeData /// /// Get a list of symbols statuses - /// + /// /// /// Cancellation token /// Task>> GetSymbolStatusAsync(CancellationToken ct = default); + /// + /// Get ticker + /// + /// + /// Symbol to get ticker for + /// Cancellation token + /// + Task> GetTickerAsync(string symbol, CancellationToken ct = default); + /// /// Get tickers - /// + /// /// /// Cancellation token /// @@ -86,7 +90,7 @@ public interface IKrakenRestClientFuturesApiExchangeData /// /// Get list of recent trades - /// + /// /// /// The symbol, for example `PF_ETHUSD` /// Filter by start time diff --git a/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApiShared.cs b/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApiShared.cs index d8bd0ae..6e9d1e5 100644 --- a/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApiShared.cs +++ b/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApiShared.cs @@ -1,13 +1,22 @@ using CryptoExchange.Net.SharedApis; -namespace Kraken.Net.Interfaces.Clients.SpotApi +namespace Kraken.Net.Interfaces.Clients.FuturesApi { /// /// Shared interface for Futures rest API usage /// - public interface IKrakenRestClientFuturesApiShared : ISharedClient + public interface IKrakenRestClientFuturesApiShared : + IBalanceRestClient, + IKlineRestClient, + IOrderBookRestClient, + IRecentTradeRestClient, + IFundingRateRestClient, + IFuturesSymbolRestClient, + IFuturesTickerRestClient, + IMarkPriceKlineRestClient, + IOpenInterestRestClient, + ILeverageRestClient, + IFuturesOrderRestClient { - // TODO implement after library update - } } diff --git a/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApiTrading.cs b/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApiTrading.cs index b2a07a4..a53cb37 100644 --- a/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApiTrading.cs +++ b/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenRestClientFuturesApiTrading.cs @@ -1,10 +1,5 @@ -using CryptoExchange.Net.Objects; -using Kraken.Net.Enums; +using Kraken.Net.Enums; using Kraken.Net.Objects.Models.Futures; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; namespace Kraken.Net.Interfaces.Clients.FuturesApi { @@ -94,6 +89,16 @@ public interface IKrakenRestClientFuturesApiTrading /// Task>> GetOpenPositionsAsync(CancellationToken ct = default); + /// + /// Get order by id + /// + /// + /// Order id, either this or clientOrderId should be provided + /// Client order id, either this or orderId should be provided + /// Cancellation token + /// + Task> GetOrderAsync(string? orderId = null, string? clientOrderId = null, CancellationToken ct = default); + /// /// Get orders by ids /// diff --git a/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenSocketClientFuturesApi.cs b/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenSocketClientFuturesApi.cs index 6e1e056..4512354 100644 --- a/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenSocketClientFuturesApi.cs +++ b/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenSocketClientFuturesApi.cs @@ -1,11 +1,5 @@ -using CryptoExchange.Net.Interfaces; -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Objects.Sockets; using Kraken.Net.Objects.Models.Socket.Futures; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; namespace Kraken.Net.Interfaces.Clients.FuturesApi { @@ -21,7 +15,7 @@ public interface IKrakenSocketClientFuturesApi : ISocketApiClient, IDisposable /// /// Subscribe to account updates - /// + /// /// /// Handler for the initial snapshot data received when (re)connecting the stream /// Update handler @@ -31,7 +25,7 @@ public interface IKrakenSocketClientFuturesApi : ISocketApiClient, IDisposable /// /// Subscribe to balance updates - /// + /// /// /// Update handler /// Cancellation token for closing this subscription @@ -40,7 +34,7 @@ public interface IKrakenSocketClientFuturesApi : ISocketApiClient, IDisposable /// /// Subscribe to heartbeat updates - /// + /// /// /// Update handler /// Cancellation token for closing this subscription @@ -49,7 +43,7 @@ public interface IKrakenSocketClientFuturesApi : ISocketApiClient, IDisposable /// /// Subscribe to mini ticker updates - /// + /// /// /// The symbol to subscribe, for example `PF_ETHUSD` /// Update handler @@ -59,7 +53,7 @@ public interface IKrakenSocketClientFuturesApi : ISocketApiClient, IDisposable /// /// Subscribe to mini ticker updates - /// + /// /// /// The symbols to subscribe, for example `PF_ETHUSD` /// Update handler @@ -69,7 +63,7 @@ public interface IKrakenSocketClientFuturesApi : ISocketApiClient, IDisposable /// /// Subscribe to notification updates - /// + /// /// /// Update handler /// Cancellation token for closing this subscription @@ -78,8 +72,8 @@ public interface IKrakenSocketClientFuturesApi : ISocketApiClient, IDisposable /// /// Subscribe to open order updates - /// - /// + /// + /// /// /// Whether to connect to the verbose stream or not. The verbose feed adds extra information about all the post-only orders that failed to cross the book. /// Handler for the initial snapshot data received when (re)connecting the stream @@ -90,7 +84,7 @@ public interface IKrakenSocketClientFuturesApi : ISocketApiClient, IDisposable /// /// Subscribe to open position updates - /// + /// /// /// Update handler /// Cancellation token for closing this subscription @@ -99,7 +93,7 @@ public interface IKrakenSocketClientFuturesApi : ISocketApiClient, IDisposable /// /// Subscribe to order book updates - /// + /// /// /// The symbols to subscribe, for example `PF_ETHUSD` /// Handler for the initial snapshot data received when (re)connecting the stream @@ -110,7 +104,7 @@ public interface IKrakenSocketClientFuturesApi : ISocketApiClient, IDisposable /// /// Subscribe to order book updates - /// + /// /// /// The symbol to subscribe, for example `PF_ETHUSD` /// Handler for the initial snapshot data received when (re)connecting the stream @@ -121,7 +115,7 @@ public interface IKrakenSocketClientFuturesApi : ISocketApiClient, IDisposable /// /// Subscribe to ticker updates - /// + /// /// /// The symbol to subscribe, for example `PF_ETHUSD` /// Update handler @@ -131,7 +125,7 @@ public interface IKrakenSocketClientFuturesApi : ISocketApiClient, IDisposable /// /// Subscribe to ticker updates - /// + /// /// /// The symbols to subscribe, for example `PF_ETHUSD` /// Update handler @@ -141,7 +135,7 @@ public interface IKrakenSocketClientFuturesApi : ISocketApiClient, IDisposable /// /// Subscribe to public trade updates - /// + /// /// /// The symbol to subscribe, for example `PF_ETHUSD` /// Update handler @@ -151,7 +145,7 @@ public interface IKrakenSocketClientFuturesApi : ISocketApiClient, IDisposable /// /// Subscribe to public trade updates - /// + /// /// /// The symbols to subscribe, for example `PF_ETHUSD` /// Update handler @@ -161,7 +155,7 @@ public interface IKrakenSocketClientFuturesApi : ISocketApiClient, IDisposable /// /// Subscribe to user trades updates - /// + /// /// /// Handler for the initial snapshot data received when (re)connecting the stream /// Cancellation token for closing this subscription diff --git a/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenSocketClientFuturesApiShared.cs b/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenSocketClientFuturesApiShared.cs index b4f94d3..890f096 100644 --- a/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenSocketClientFuturesApiShared.cs +++ b/Kraken.Net/Interfaces/Clients/FuturesApi/IKrakenSocketClientFuturesApiShared.cs @@ -6,8 +6,13 @@ namespace Kraken.Net.Interfaces.Clients.FuturesApi /// Shared interface for Futures socket API usage /// public interface IKrakenSocketClientFuturesApiShared - : ISharedClient - //Can be implemented with V2 websockets + : ITickerSocketClient, + ITradeSocketClient, + IBookTickerSocketClient, + IBalanceSocketClient, + IFuturesOrderSocketClient, + IUserTradeSocketClient, + IPositionSocketClient { } } diff --git a/Kraken.Net/Interfaces/Clients/IKrakenRestClient.cs b/Kraken.Net/Interfaces/Clients/IKrakenRestClient.cs index faf3624..8df4e28 100644 --- a/Kraken.Net/Interfaces/Clients/IKrakenRestClient.cs +++ b/Kraken.Net/Interfaces/Clients/IKrakenRestClient.cs @@ -1,6 +1,4 @@ -using CryptoExchange.Net.Authentication; -using CryptoExchange.Net.Interfaces; -using Kraken.Net.Interfaces.Clients.FuturesApi; +using Kraken.Net.Interfaces.Clients.FuturesApi; using Kraken.Net.Interfaces.Clients.SpotApi; namespace Kraken.Net.Interfaces.Clients diff --git a/Kraken.Net/Interfaces/Clients/IKrakenSocketClient.cs b/Kraken.Net/Interfaces/Clients/IKrakenSocketClient.cs index 5de1dcc..a02551b 100644 --- a/Kraken.Net/Interfaces/Clients/IKrakenSocketClient.cs +++ b/Kraken.Net/Interfaces/Clients/IKrakenSocketClient.cs @@ -1,6 +1,4 @@ -using CryptoExchange.Net.Authentication; -using CryptoExchange.Net.Interfaces; -using Kraken.Net.Interfaces.Clients.FuturesApi; +using Kraken.Net.Interfaces.Clients.FuturesApi; using Kraken.Net.Interfaces.Clients.SpotApi; namespace Kraken.Net.Interfaces.Clients diff --git a/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenRestClientSpotApiAccount.cs b/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenRestClientSpotApiAccount.cs index a167f02..8b7b4c6 100644 --- a/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenRestClientSpotApiAccount.cs +++ b/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenRestClientSpotApiAccount.cs @@ -1,9 +1,4 @@ -using CryptoExchange.Net.Objects; -using Kraken.Net.Enums; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; +using Kraken.Net.Enums; using Kraken.Net.Objects.Models; using Kraken.Net.Objects.Models.Socket; @@ -16,7 +11,7 @@ public interface IKrakenRestClientSpotApiAccount { /// /// Get balances - /// + /// /// /// Password or authentication app code if enabled /// Cancellation token @@ -25,7 +20,7 @@ public interface IKrakenRestClientSpotApiAccount /// /// Get balances including quantity in holding - /// + /// /// /// Cancellation token /// Password or authentication app code if enabled @@ -34,7 +29,7 @@ public interface IKrakenRestClientSpotApiAccount /// /// Get trade balance - /// + /// /// /// Base asset to get trade balance for, for example `USDT` /// Password or authentication app code if enabled @@ -45,7 +40,7 @@ public interface IKrakenRestClientSpotApiAccount /// /// Get a list of open positions - /// + /// /// /// Filter by transaction ids /// Password or authentication app code if enabled @@ -55,7 +50,7 @@ public interface IKrakenRestClientSpotApiAccount /// /// Get ledger entries info - /// + /// /// /// Filter list by asset names, for example `USDT` /// Filter list by entry types @@ -69,7 +64,7 @@ public interface IKrakenRestClientSpotApiAccount /// /// Get info on specific ledger entries - /// + /// /// /// The ids to get info for /// Password or authentication app code if enabled @@ -79,7 +74,7 @@ public interface IKrakenRestClientSpotApiAccount /// /// Get trade volume - /// + /// /// /// Symbols to get data for, for example `ETHUSDT` /// Password or authentication app code if enabled @@ -89,7 +84,7 @@ public interface IKrakenRestClientSpotApiAccount /// /// Get deposit methods - /// + /// /// /// Asset to get methods for, for example `ETH` /// Password or authentication app code if enabled @@ -99,7 +94,7 @@ public interface IKrakenRestClientSpotApiAccount /// /// Get deposit addresses for an asset - /// + /// /// /// The asset to get the deposit address for, for example `ETH` /// The method of deposit @@ -112,7 +107,7 @@ public interface IKrakenRestClientSpotApiAccount /// /// Get status of deposits - /// + /// /// /// Asset to get deposit info for, for example `ETH` /// The deposit method @@ -123,7 +118,7 @@ public interface IKrakenRestClientSpotApiAccount /// /// Get deposit history - /// + /// /// /// Asset filter, for example `ETH` /// Deposit method @@ -146,7 +141,7 @@ Task>> GetDepositHistoryAsy /// /// Retrieve fee information about potential withdrawals for a particular asset, key and amount. - /// + /// /// /// The asset, for example `ETH` /// The withdrawal key name @@ -159,7 +154,7 @@ Task> GetWithdrawInfoAsync(string asset, strin /// /// Withdraw funds - /// + /// /// /// The asset being withdrawn, for example `ETH` /// The withdrawal key name, as set up on your account @@ -172,7 +167,7 @@ Task> GetWithdrawInfoAsync(string asset, strin /// /// Get withdraw addresses - /// + /// /// /// The asset to get the deposit address for, for example `ETH` /// Filter addresses for specific asset class @@ -185,7 +180,7 @@ Task> GetWithdrawInfoAsync(string asset, strin /// /// Retrieve a list of withdrawal methods available for the user. - /// + /// /// /// The asset to get the deposit address for, for example `ETH` /// Filter addresses for specific asset class @@ -195,8 +190,8 @@ Task> GetWithdrawInfoAsync(string asset, strin Task>> GetWithdrawMethodsAsync(string? asset = null, string? aclass = null, string? network = null, CancellationToken ct = default); /// - /// Get the token to connect to the private websocket streams - /// + /// Get the token to connect to the private websocket streams. Note that this endpoint is used internally and there is normally no need to call this. + /// /// /// Cancellation token /// @@ -204,7 +199,7 @@ Task> GetWithdrawInfoAsync(string asset, strin /// /// Get status of withdrawals - /// + /// /// /// Filter by asset, for example `ETH` /// Filter by method @@ -215,7 +210,7 @@ Task> GetWithdrawInfoAsync(string asset, strin /// /// Get withdrawal history - /// + /// /// /// Asset filter, for example `ETH` /// Withdrawal method @@ -238,7 +233,7 @@ Task>> GetWithdrawalHistory /// /// Cancel an active withdrawal - /// + /// /// /// Asset, for example `ETH` /// Reference id @@ -249,7 +244,7 @@ Task>> GetWithdrawalHistory /// /// Transfer funds between wallets - /// + /// /// /// Asset, for example `ETH` /// Quantity diff --git a/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenRestClientSpotApiEarn.cs b/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenRestClientSpotApiEarn.cs index dd5c7d9..4322029 100644 --- a/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenRestClientSpotApiEarn.cs +++ b/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenRestClientSpotApiEarn.cs @@ -10,7 +10,7 @@ public interface IKrakenRestClientSpotApiEarn { /// /// List earn strategies along with their parameters. - /// + /// /// /// Filter by asset, for example `USDT` /// Filter by lock type @@ -24,7 +24,7 @@ public interface IKrakenRestClientSpotApiEarn /// /// Get earn allocations - /// + /// /// /// Convert asset, defaults to USD /// Hide zero allocations @@ -36,7 +36,7 @@ public interface IKrakenRestClientSpotApiEarn /// /// Get status of the last allocation request - /// + /// /// /// Strategy id /// Password or authentication app code if enabled @@ -46,7 +46,7 @@ public interface IKrakenRestClientSpotApiEarn /// /// Get status of the last deallocation request - /// + /// /// /// Strategy id /// Password or authentication app code if enabled @@ -56,7 +56,7 @@ public interface IKrakenRestClientSpotApiEarn /// /// Allocate earn funds to a strategy - /// + /// /// /// Strategy id /// Amount to allocate @@ -67,7 +67,7 @@ public interface IKrakenRestClientSpotApiEarn /// /// Deallocate previously allocated funds - /// + /// /// /// Strategy id /// Amount to deallocate diff --git a/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenRestClientSpotApiExchangeData.cs b/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenRestClientSpotApiExchangeData.cs index 824620f..ff0cdfe 100644 --- a/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenRestClientSpotApiExchangeData.cs +++ b/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenRestClientSpotApiExchangeData.cs @@ -1,10 +1,5 @@ -using CryptoExchange.Net.Objects; -using Kraken.Net.Converters; +using Kraken.Net.Converters; using Kraken.Net.Enums; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Kraken.Net.Objects.Models; namespace Kraken.Net.Interfaces.Clients.SpotApi @@ -16,7 +11,7 @@ public interface IKrakenRestClientSpotApiExchangeData { /// /// Get the server time - /// + /// /// /// Cancellation token /// Server time @@ -24,7 +19,7 @@ public interface IKrakenRestClientSpotApiExchangeData /// /// Get the system status - /// + /// /// /// Cancellation token /// System status @@ -32,7 +27,7 @@ public interface IKrakenRestClientSpotApiExchangeData /// /// Get a list of assets and info about them - /// + /// /// /// Filter list for specific assets, for example `ETH` /// Cancellation token @@ -41,7 +36,7 @@ public interface IKrakenRestClientSpotApiExchangeData /// /// Get a list of symbols and info about them - /// + /// /// /// Filter whats available for a specific country/region /// Filter list for specific symbols, for example `ETHUSDT` @@ -51,7 +46,7 @@ public interface IKrakenRestClientSpotApiExchangeData /// /// Get tickers for symbol - /// + /// /// /// Symbol to get tickers for, for example `ETHUSDT` /// Cancellation token @@ -60,7 +55,7 @@ public interface IKrakenRestClientSpotApiExchangeData /// /// Get tickers for symbols - /// + /// /// /// Symbols to get tickers for, for example `ETHUSDT` /// Cancellation token @@ -69,7 +64,7 @@ public interface IKrakenRestClientSpotApiExchangeData /// /// Gets kline data for a symbol - /// + /// /// /// The symbol to get data for, for example `ETHUSDT` /// The interval of the klines @@ -80,7 +75,7 @@ public interface IKrakenRestClientSpotApiExchangeData /// /// Get the order book for a symbol - /// + /// /// /// Symbol to get the book for, for example `ETHUSDT` /// Limit to book to the best x bids/asks @@ -90,7 +85,7 @@ public interface IKrakenRestClientSpotApiExchangeData /// /// Get a list of recent trades for a symbol - /// + /// /// /// Symbol to get trades for, for example `ETHUSDT` /// Return trades since a specific time @@ -101,7 +96,7 @@ public interface IKrakenRestClientSpotApiExchangeData /// /// Get spread data for a symbol - /// + /// /// /// Symbol to get spread data for, for example `ETHUSDT` /// Return spread data since a specific time diff --git a/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenRestClientSpotApiTrading.cs b/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenRestClientSpotApiTrading.cs index 14d14d3..6dd4014 100644 --- a/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenRestClientSpotApiTrading.cs +++ b/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenRestClientSpotApiTrading.cs @@ -1,9 +1,4 @@ -using CryptoExchange.Net.Objects; using Kraken.Net.Enums; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Kraken.Net.Objects.Models; namespace Kraken.Net.Interfaces.Clients.SpotApi @@ -15,7 +10,7 @@ public interface IKrakenRestClientSpotApiTrading { /// /// Get a list of open orders - /// + /// /// /// Filter by client order id /// Password or authentication app code if enabled @@ -25,7 +20,7 @@ public interface IKrakenRestClientSpotApiTrading /// /// Get a list of closed orders - /// + /// /// /// Filter by client order id /// Return data after this time @@ -38,7 +33,7 @@ public interface IKrakenRestClientSpotApiTrading /// /// Get info on specific order - /// + /// /// /// Get orders by clientOrderId /// Get order by its order id @@ -51,7 +46,7 @@ public interface IKrakenRestClientSpotApiTrading /// /// Get info on specific orders - /// + /// /// /// Get orders by clientOrderId /// Get orders by their order ids @@ -64,7 +59,7 @@ public interface IKrakenRestClientSpotApiTrading /// /// Get trade history - /// + /// /// /// Return data after this time /// Return data before this time @@ -77,7 +72,7 @@ public interface IKrakenRestClientSpotApiTrading /// /// Get info on specific trades - /// + /// /// /// The trade to get info on /// Password or authentication app code if enabled @@ -87,7 +82,7 @@ public interface IKrakenRestClientSpotApiTrading /// /// Get info on specific trades - /// + /// /// /// The trades to get info on /// Password or authentication app code if enabled @@ -97,6 +92,7 @@ public interface IKrakenRestClientSpotApiTrading /// /// Place multiple new orders + /// /// /// The symbol the order is on, for example `ETHUSDT` /// The orders to place @@ -108,12 +104,13 @@ public interface IKrakenRestClientSpotApiTrading /// /// Place a new order - /// + /// /// /// The symbol the order is on, for example `ETHUSDT` /// The side of the order /// The type of the order /// The quantity of the order + /// A numeric id to reference the order by /// A client id to reference the order by /// Price of the order:
/// Limit=limit price
@@ -163,7 +160,8 @@ Task> PlaceOrderAsync( DateTime? startTime = null, DateTime? expireTime = null, bool? validateOnly = null, - uint? clientOrderId = null, + uint? userReference = null, + string? clientOrderId = null, IEnumerable? orderFlags = null, string? twoFactorPassword = null, TimeInForce? timeInForce = null, @@ -182,7 +180,7 @@ Task> PlaceOrderAsync( ///
/// Edit an order - /// + /// /// /// Symbol, for example `ETHUSDT` /// Order id or client order id of the order to edit @@ -223,7 +221,7 @@ Task> EditOrderAsync( /// /// Cancel an order - /// + /// /// /// The id of the order to cancel /// Password or authentication app code if enabled @@ -233,11 +231,21 @@ Task> EditOrderAsync( /// /// Cancel all orders - /// + /// /// /// Password or authentication app code if enabled /// Cancellation token /// Cancel result Task> CancelAllOrdersAsync(string? twoFactorPassword = null, CancellationToken ct = default); + + /// + /// Cancel all orders after the timeout expires. Can be called at an interval to keep extending the timeout and word as a dead-man switch. Timeout can be disabled by setting timeout to 0. + /// + /// + /// + /// + /// + /// + Task> CancelAllOrdersAfterAsync(TimeSpan cancelAfter, string? twoFactorPassword = null, CancellationToken ct = default); } } diff --git a/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenSocketClientSpotApi.cs b/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenSocketClientSpotApi.cs index 299ba89..7c4a86a 100644 --- a/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenSocketClientSpotApi.cs +++ b/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenSocketClientSpotApi.cs @@ -1,18 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using CryptoExchange.Net.Interfaces; -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Objects.Sockets; using Kraken.Net.Enums; using Kraken.Net.Objects.Models; using Kraken.Net.Objects.Models.Socket; +using Kraken.Net.Objects.Models.Socket.Futures; namespace Kraken.Net.Interfaces.Clients.SpotApi { /// - /// Spot API + /// Spot V2 websocket API /// public interface IKrakenSocketClientSpotApi : ISocketApiClient, IDisposable { @@ -23,7 +18,7 @@ public interface IKrakenSocketClientSpotApi : ISocketApiClient, IDisposable /// /// Subscribe to system status updates - /// + /// /// /// Data handler /// Cancellation token for closing this subscription @@ -32,239 +27,344 @@ public interface IKrakenSocketClientSpotApi : ISocketApiClient, IDisposable /// /// Subscribe to ticker updates - /// + /// /// /// Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property /// 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(string symbol, Action> handler, CancellationToken ct = default); + Task> SubscribeToTickerUpdatesAsync(string symbol, Action> handler, CancellationToken ct = default); /// /// Subscribe to ticker updates - /// + /// /// /// Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property /// 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> handler, CancellationToken ct = default); + Task> SubscribeToTickerUpdatesAsync(IEnumerable symbols, Action> handler, CancellationToken ct = default); /// /// Subscribe to kline updates - /// + /// /// /// Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property /// Kline interval /// Data handler /// Cancellation token for closing this subscription /// A stream subscription. This streamv subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToKlineUpdatesAsync(string symbol, KlineInterval interval, Action> handler, CancellationToken ct = default); + Task> SubscribeToKlineUpdatesAsync(string symbol, KlineInterval interval, Action>> handler, CancellationToken ct = default); /// /// Subscribe to kline updates - /// + /// /// /// Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property /// Kline interval /// Data handler /// Cancellation token for closing this subscription /// A stream subscription. This streamv subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToKlineUpdatesAsync(IEnumerable symbols, KlineInterval interval, Action> handler, CancellationToken ct = default); + Task> SubscribeToKlineUpdatesAsync(IEnumerable symbols, KlineInterval interval, Action>> handler, CancellationToken ct = default); /// /// Subscribe to trade updates - /// + /// /// /// Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property /// Data handler + /// Whether or not a snapshot of the last trades should be send after subscribing /// 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>> handler, CancellationToken ct = default); + Task> SubscribeToTradeUpdatesAsync(string symbol, Action>> handler, bool? snapshot = null, CancellationToken ct = default); /// /// Subscribe to trade updates - /// + /// /// /// Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property /// Data handler + /// Whether or not a snapshot of the last trades should be send after subscribing /// 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>> handler, CancellationToken ct = default); + Task> SubscribeToTradeUpdatesAsync(IEnumerable symbols, Action>> handler, bool? snapshot = null, CancellationToken ct = default); /// - /// Subscribe to spread updates - /// + /// Subscribe to order book updates. Order book entries are aggregated per price level + /// /// - /// Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + /// Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + /// Depth of the initial order book snapshot. 10, 25, 100, 500 or 1000 /// Data handler + /// Whether or not a snapshot of the last book state should be send after subscribing /// 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> SubscribeToSpreadUpdatesAsync(string symbol, Action> handler, CancellationToken ct = default); + Task> SubscribeToAggregatedOrderBookUpdatesAsync(IEnumerable symbols, int depth, Action> handler, bool? snapshot = null, CancellationToken ct = default); /// - /// Subscribe to spread updates - /// + /// Subscribe to order book updates. Order book entries are aggregated per price level + /// /// - /// Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + /// Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + /// Depth of the initial order book snapshot. 10, 25, 100, 500 or 1000 /// Data handler + /// Whether or not a snapshot of the last book state should be send after subscribing /// 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> SubscribeToSpreadUpdatesAsync(IEnumerable symbols, Action> handler, CancellationToken ct = default); + Task> SubscribeToAggregatedOrderBookUpdatesAsync(string symbol, int depth, Action> handler, bool? snapshot = null, CancellationToken ct = default); + /// - /// Subscribe to depth updates - /// + /// Subscribe to the full order book with individual orders. Requires authentication. + /// /// /// Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property - /// Depth of the initial order book snapshot. 10, 25, 100, 500 or 1000 + /// Depth of the initial order book snapshot. 10, 100 or 1000 /// Data handler + /// Whether or not a snapshot of the last book state should be send after subscribing /// 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, Action> handler, CancellationToken ct = default); + Task> SubscribeToInvidualOrderBookUpdatesAsync(IEnumerable symbols, int depth, Action> handler, bool? snapshot = null, CancellationToken ct = default); /// - /// Subscribe to depth updates - /// + /// Subscribe to the full order book with individual orders. Requires authentication. + /// /// /// Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property - /// Depth of the initial order book snapshot. 10, 25, 100, 500 or 1000 + /// Depth of the initial order book snapshot. 10, 100 or 1000 /// Data handler + /// Whether or not a snapshot of the last book state should be send after subscribing /// 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, Action> handler, CancellationToken ct = default); + Task> SubscribeToInvidualOrderBookUpdatesAsync(string symbol, int depth, Action> handler, bool? snapshot = null, CancellationToken ct = default); /// - /// Subscribe to open order updates - /// + /// Subscribe to instrument (asset and symbol) updates + /// /// - /// The socket token as retrieved by the restClient.SpotApi.Account.GetWebsocketTokenAsync method /// Data handler + /// Whether or not a snapshot of the current instruments state should be send after subscribing /// 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(string socketToken, - Action>> handler, CancellationToken ct = default); + Task> SubscribeToInstrumentUpdatesAsync(Action> handler, bool? snapshot = null, CancellationToken ct = default); /// - /// Subscribe to own trade updates - /// + /// Subscribe to user balances updates + /// /// - /// The socket token as retrieved by the restClient.SpotApi.Account.GetWebsocketTokenAsync method - /// Data handler + /// Handler for the initial snapshot data send after subscribing + /// Handler for update data when changes occur + /// Whether or not a snapshot of the current balances should be send after subscribing /// 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(string socketToken, - Action>> handler, CancellationToken ct = default); + Task> SubscribeToBalanceUpdatesAsync(Action>>? snapshotHandler, Action>> updateHandler, bool? snapshot = null, CancellationToken ct = default); /// - /// Subscribe to own trade updates - /// + /// Subscribe to user order updates + /// /// - /// The socket token as retrieved by the restClient.SpotApi.Account.GetWebsocketTokenAsync method - /// Whether or not to receive a snapshot of the data upon subscribing - /// Data handler + /// Data handler. Depending on the event not all fields on the update event are filled + /// Whether or not a snapshot of the current open orders should be send after subscribing + /// Whether or not a snapshot of the last 50 user trades should be send after subscribing /// 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(string socketToken, bool snapshot, - Action>> handler, CancellationToken ct = default); + Task> SubscribeToOrderUpdatesAsync( + Action>> updateHandler, + bool? snapshotOrder = null, + bool? snapshotTrades = null, + CancellationToken ct = default); /// /// Place a new order - /// + /// /// - /// The socket token as retrieved by the restClient.SpotApi.Account.GetWebsocketTokenAsync method /// The symbol the order is on, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the /// The side of the order /// The type of the order /// The quantity of the order - /// A client id to reference the order by - /// Price of the order:
- /// Limit=limit price
- /// StopLoss=stop loss price
- /// TakeProfit=take profit price
- /// StopLossProfit=stop loss price
- /// StopLossProfitLimit=stop loss price
- /// StopLossLimit=stop loss trigger price
- /// TakeProfitLimit=take profit trigger price
- /// TrailingStop=trailing stop offset
- /// TrailingStopLimit=trailing stop offset
- /// StopLossAndLimit=stop loss price - /// - /// Secondary price of an order:
- /// StopLossProfit/StopLossProfitLimit=take profit price
- /// StopLossLimit/TakeProfitLimit=triggered limit price
- /// TrailingStopLimit=triggered limit offset
- /// StopLossAndLimit=limit price - /// Desired leverage + /// Client order id + /// User reference id + /// Order limit price + /// Limit price type + /// Time in force + /// Reduce only order + /// Funds the order on margin using the maximum leverage for the pair. Note, absolute max leverage is 5. + /// Post only order flag /// Scheduled start time /// Expiration time + /// Deadline time, the engine will prevent this order from matching after this time, it provides protection against latency on time sensitive orders. + /// Display size for iceberg orders + /// Fee preference + /// Execute without market price protection + /// Self trade prevention type + /// Quantity in quote asset (buy market orders only) /// Only validate inputs, don't actually place the order - /// Close order type - /// Close order price - /// Close order secondary price - /// Order flags - /// Reduce only order - /// Funds the order on margin using the maximum leverage for the pair. Note, absolute max leverage is 5. + /// Order trigger price reference + /// Order trigger price + /// Order trigger price type + /// Conditional order type + /// Conditional order limit price + /// Conditional order limit price type + /// Conditional order trigger price + /// Conditional order trigger price type /// Cancellation token /// - Task> PlaceOrderAsync( - string websocketToken, + Task> PlaceOrderAsync( string symbol, - OrderType type, OrderSide side, + OrderType type, decimal quantity, - uint? clientOrderId = null, - decimal? price = null, - decimal? secondaryPrice = null, - decimal? leverage = null, + string? clientOrderId = null, + uint? userReference = null, + decimal? limitPrice = null, + PriceType? limitPriceType = null, + TimeInForce? timeInForce = null, + bool? reduceOnly = null, + bool? margin = null, + bool? postOnly = null, DateTime? startTime = null, DateTime? expireTime = null, + DateTime? deadline = null, + decimal? icebergQuantity = null, + FeePreference? feePreference = null, + bool? noMarketPriceProtection = null, + SelfTradePreventionType? selfTradePreventionType = null, + decimal? quoteQuantity = null, bool? validateOnly = null, - OrderType? closeOrderType = null, - decimal? closePrice = null, - decimal? secondaryClosePrice = null, - IEnumerable? flags = null, + + Trigger? triggerPriceReference = null, + decimal? triggerPrice = null, + PriceType? triggerPriceType = null, + + OrderType? conditionalOrderType = null, + decimal? conditionalLimitPrice = null, + PriceType? conditionalLimitPriceType = null, + decimal? conditionalTriggerPrice = null, + PriceType? conditionalTriggerPriceType = null, + + CancellationToken ct = default); + + ///
+ /// Edit an existing order + /// + /// + /// Order id, either this or clientOrderId should be provided + /// Client order id, either this or orderId should be provided + /// New limit price + /// New limit price type + /// New quantity + /// New iceberg quantity + /// New post only flag + /// New trigger price + /// New trigger price type + /// Deadline + /// Cancellation token + /// + Task> EditOrderAsync( + string? orderId = null, + string? clientOrderId = null, + decimal? limitPrice = null, + PriceType? limitPriceType = null, + decimal? quantity = null, + decimal? icebergQuantity = null, + bool? postOnly = null, + decimal? triggerPrice = null, + PriceType? triggerPriceType = null, + DateTime? deadline = null, + CancellationToken ct = default); + + /// + /// Replace an existing order + /// + /// + /// The symbol the order is on, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the + /// Order id, either this or clientOrderId should be provided + /// Client order id, either this or orderId should be provided + /// The quantity of the order + /// User reference id + /// Order limit price + /// Reduce only order + /// Post only order flag + /// Deadline time, the engine will prevent this order from matching after this time, it provides protection against latency on time sensitive orders. + /// Display size for iceberg orders + /// Fee preference + /// Execute without market price protection + /// Only validate inputs, don't actually place the order + /// Order trigger price reference + /// Order trigger price + /// Order trigger price type + /// Cancellation token + /// + Task> ReplaceOrderAsync( + string symbol, + string? orderId = null, + string? clientOrderId = null, + decimal? quantity = null, + uint? userReference = null, + decimal? limitPrice = null, bool? reduceOnly = null, - bool? margin = null, + bool? postOnly = null, + DateTime? deadline = null, + decimal? icebergQuantity = null, + FeePreference? feePreference = null, + bool? noMarketPriceProtection = null, + bool? validateOnly = null, + + Trigger? triggerPriceReference = null, + decimal? triggerPrice = null, + PriceType? triggerPriceType = null, + CancellationToken ct = default); + + /// + /// Place multiple orders in a single request + /// + /// + /// Symbol to place the orders on + /// Order info + /// Deadline time, the engine will prevent this order from matching after this time, it provides protection against latency on time sensitive orders. + /// Only validate inputs, don't actually place the order + /// Cancellation token + /// + Task>> PlaceMultipleOrdersAsync( + string symbol, + IEnumerable orders, + DateTime? deadline = null, + bool? validateOnly = null, CancellationToken ct = default); /// /// Cancel an order - /// + /// /// - /// The socket token as retrieved by the restClient.SpotApi.Account.GetWebsocketTokenAsync method /// Id of the order to cancel /// Cancellation token /// - Task> CancelOrderAsync(string websocketToken, string orderId, CancellationToken ct = default); + Task> CancelOrderAsync(string orderId, CancellationToken ct = default); /// /// Cancel multiple orders - /// + /// /// - /// The socket token as retrieved by the restClient.SpotApi.Account.GetWebsocketTokenAsync method /// Id of the orders to cancel /// Cancellation token /// - Task> CancelOrdersAsync(string websocketToken, IEnumerable orderIds, CancellationToken ct = default); + Task> CancelOrdersAsync(IEnumerable orderIds, CancellationToken ct = default); /// /// Cancel all open orders - /// + /// /// - /// The socket token as retrieved by the restClient.SpotApi.Account.GetWebsocketTokenAsync method /// Cancellation token /// - Task> CancelAllOrdersAsync(string websocketToken, CancellationToken ct = default); + Task> CancelAllOrdersAsync(CancellationToken ct = default); /// /// Cancel all open orders after the timeout - /// + /// /// /// - /// The socket token as retrieved by the restClient.SpotApi.Account.GetWebsocketTokenAsync method /// Cancellation token /// - Task> CancelAllOrdersAfterAsync(string websocketToken, TimeSpan timeout, CancellationToken ct = default); + Task> CancelAllOrdersAfterAsync(TimeSpan timeout, CancellationToken ct = default); } } \ No newline at end of file diff --git a/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenSocketClientSpotApiShared.cs b/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenSocketClientSpotApiShared.cs index 2f4faa9..087e075 100644 --- a/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenSocketClientSpotApiShared.cs +++ b/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenSocketClientSpotApiShared.cs @@ -9,10 +9,9 @@ public interface IKrakenSocketClientSpotApiShared : ITickerSocketClient, ITradeSocketClient, IBookTickerSocketClient, - IKlineSocketClient - //Can be implemented with V2 websockets - //IBalanceSocketClient, - //ISpotOrderSocketClient + IKlineSocketClient, + IBalanceSocketClient, + ISpotOrderSocketClient { } } diff --git a/Kraken.Net/Interfaces/IKrakenFuturesOrder.cs b/Kraken.Net/Interfaces/IKrakenFuturesOrder.cs index 86bc862..a19c12a 100644 --- a/Kraken.Net/Interfaces/IKrakenFuturesOrder.cs +++ b/Kraken.Net/Interfaces/IKrakenFuturesOrder.cs @@ -1,5 +1,4 @@ using Kraken.Net.Enums; -using System; namespace Kraken.Net.Interfaces { diff --git a/Kraken.Net/Interfaces/IKrakenOrderBookFactory.cs b/Kraken.Net/Interfaces/IKrakenOrderBookFactory.cs index c61ba49..0b7db55 100644 --- a/Kraken.Net/Interfaces/IKrakenOrderBookFactory.cs +++ b/Kraken.Net/Interfaces/IKrakenOrderBookFactory.cs @@ -1,6 +1,4 @@ -using CryptoExchange.Net.Interfaces; -using Kraken.Net.Objects.Options; -using System; +using Kraken.Net.Objects.Options; namespace Kraken.Net.Interfaces { diff --git a/Kraken.Net/Kraken.Net.csproj b/Kraken.Net/Kraken.Net.csproj index b60620e..0e7dd88 100644 --- a/Kraken.Net/Kraken.Net.csproj +++ b/Kraken.Net/Kraken.Net.csproj @@ -2,7 +2,7 @@ netstandard2.0;netstandard2.1 enable - 10.0 + 12.0 KrakenExchange.Net @@ -51,10 +51,10 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive - \ No newline at end of file diff --git a/Kraken.Net/Kraken.Net.xml b/Kraken.Net/Kraken.Net.xml index b2560f4..71293b4 100644 --- a/Kraken.Net/Kraken.Net.xml +++ b/Kraken.Net/Kraken.Net.xml @@ -91,6 +91,9 @@ + + + @@ -124,6 +127,9 @@ + + + @@ -481,7 +487,7 @@ - + @@ -493,6 +499,9 @@ + + + @@ -514,66 +523,68 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - Maps an input symbol to a server symbol - - - + + + + + + + + @@ -634,6 +645,16 @@ Funding temp disabled
+ + + Disabled + + + + + Work in process + + Auto compound @@ -654,6 +675,196 @@ Optional + + + Category + + + + + Deposit + + + + + Withdrawal + + + + + Trade + + + + + Margin trade + + + + + Margin settle + + + + + Margin conversion + + + + + Conversion + + + + + Credit + + + + + Marginrollover + + + + + Staking rewards + + + + + Instant + + + + + Equity trade + + + + + Airdrop + + + + + Equity dividend + + + + + Reward bonus + + + + + Nft + + + + + Block trade + + + + + Balance update type + + + + + Deposit + + + + + Withdrawal + + + + + Trade + + + + + Margin + + + + + Adjustment + + + + + Rollover + + + + + Credit + + + + + Transfer + + + + + Settled + + + + + Staking + + + + + Sale + + + + + Reserve + + + + + Conversion + + + + + Dividend + + + + + Reward + + + + + Creator fee + + + + + Fee preference + + + + + In base asset, default for buy orders + + + + + In quote asset, default for sell orders + + Futures kline interval @@ -759,7 +970,7 @@ Inverse futures - + Vanilla futures @@ -1159,6 +1370,76 @@ The size of the order is partially but not entirely filled
+ + + Order book event + + + + + Add + + + + + Modify + + + + + Delete + + + + + Order event type + + + + + Order request has been received and validated but the order is not live yet. + + + + + Order has been created and is live in the engine. + + + + + The order has received a fill. + + + + + The order has been fully filled. + + + + + The order has been cancelled. + + + + + The order has expired. + + + + + There is a user initiated amend on the order, i.e. limit price change. + + + + + There is a engine initiated amend on the order for maintenance of position or book, see reason field, i.e. reduce non-tradable liquidity. + + + + + The order has a status update, i.e. trigger price has been updated. + + Flags for an order @@ -1234,6 +1515,41 @@ Expired + + + Status of an order + + + + + Pending + + + + + Active, not filled + + + + + Active, partially filled + + + + + Fully filled + + + + + Canceled + + + + + Expired + + Order type @@ -1339,6 +1655,26 @@ Short position + + + Price type + + + + + Static market price for the asset, i.e. 30000 for BTC/USD. + + + + + Percentage offset from the reference price, i.e. -10% from index price. + + + + + Notional offset from the reference price in the quote currency, i.e, 150 BTC/USD from last price + + Tier to use for ratelimiting @@ -1404,26 +1740,6 @@ Allows both master accounts and their subaccounts to cancel maker orders - - - Reward type - - - - - Percentage - - - - - The current status of the staking transaction. - - - - - The type of transaction. - - Symbol status @@ -1454,6 +1770,21 @@ Only allowed to reduce position + + + Delisted + + + + + Maintenance + + + + + Work in process + + System status info @@ -1649,7 +1980,22 @@ Last trade price - + + + Wallet type + + + + + Spot + + + + + Earn + + + Yield source @@ -1707,7 +2053,7 @@ Get account log entries - + Return results after this time Return results before this time @@ -1722,7 +2068,7 @@ Get asset balances and margin info - + Cancellation token @@ -1730,7 +2076,7 @@ Get the PNL currency preference is used to determine which currency to pay out when realizing PNL gains. - + Cancellation token @@ -1738,7 +2084,7 @@ Set the PNL currency preference is used to determine which currency to pay out when realizing PNL gains. - + Symbol to update, for example `PF_ETHUSD` Currency to use @@ -1748,7 +2094,7 @@ Transfer between 2 margin accounts or between margin and cash account - + The asset to transfer, for example `USDT` The amount to transfer @@ -1760,7 +2106,7 @@ Get fee schedule volume - + Cancellation token @@ -1768,7 +2114,7 @@ Get the initial margin requirements for the provided parameters - + The symbol, for example `PF_ETHUSD` Order type @@ -1781,7 +2127,7 @@ Get the max order quantity - + The symbol, for example `PF_ETHUSD` Order type @@ -1797,7 +2143,7 @@ Get fee schedules - + Cancellation token @@ -1805,7 +2151,7 @@ Get historical funding rates - + The symbol, for example `PF_ETHUSD` Cancellation token @@ -1814,7 +2160,7 @@ Get klines/candle data - + Type of price tick The symbol, for example `PF_ETHUSD` @@ -1827,7 +2173,7 @@ Get the orderbook - + The symbol, for example `PF_ETHUSD` Cancellation token @@ -1836,7 +2182,7 @@ Get platform notifications - + Cancellation token @@ -1844,7 +2190,7 @@ Get a list of symbols - + Cancellation token @@ -1852,15 +2198,24 @@ Get a list of symbols statuses - + + + Cancellation token + + + + + Get ticker + + Symbol to get ticker for Cancellation token Get tickers - + Cancellation token @@ -1868,13 +2223,18 @@ Get list of recent trades - + The symbol, for example `PF_ETHUSD` Filter by start time Cancellation token + + + Shared interface for Futures rest API usage + + Kraken futures trading endpoints, placing and mananging orders. @@ -1961,6 +2321,16 @@ Cancellation token + + + Get order by id + + + Order id, either this or clientOrderId should be provided + Client order id, either this or orderId should be provided + Cancellation token + + Get orders by ids @@ -2039,7 +2409,7 @@ Subscribe to account updates - + Handler for the initial snapshot data received when (re)connecting the stream Update handler @@ -2049,7 +2419,7 @@ Subscribe to balance updates - + Update handler Cancellation token for closing this subscription @@ -2058,7 +2428,7 @@ Subscribe to heartbeat updates - + Update handler Cancellation token for closing this subscription @@ -2067,7 +2437,7 @@ Subscribe to mini ticker updates - + The symbol to subscribe, for example `PF_ETHUSD` Update handler @@ -2077,7 +2447,7 @@ Subscribe to mini ticker updates - + The symbols to subscribe, for example `PF_ETHUSD` Update handler @@ -2087,7 +2457,7 @@ Subscribe to notification updates - + Update handler Cancellation token for closing this subscription @@ -2096,8 +2466,8 @@ Subscribe to open order updates - - + + Whether to connect to the verbose stream or not. The verbose feed adds extra information about all the post-only orders that failed to cross the book. Handler for the initial snapshot data received when (re)connecting the stream @@ -2108,7 +2478,7 @@ Subscribe to open position updates - + Update handler Cancellation token for closing this subscription @@ -2117,7 +2487,7 @@ Subscribe to order book updates - + The symbols to subscribe, for example `PF_ETHUSD` Handler for the initial snapshot data received when (re)connecting the stream @@ -2128,7 +2498,7 @@ Subscribe to order book updates - + The symbol to subscribe, for example `PF_ETHUSD` Handler for the initial snapshot data received when (re)connecting the stream @@ -2139,7 +2509,7 @@ Subscribe to ticker updates - + The symbol to subscribe, for example `PF_ETHUSD` Update handler @@ -2149,7 +2519,7 @@ Subscribe to ticker updates - + The symbols to subscribe, for example `PF_ETHUSD` Update handler @@ -2159,7 +2529,7 @@ Subscribe to public trade updates - + The symbol to subscribe, for example `PF_ETHUSD` Update handler @@ -2169,7 +2539,7 @@ Subscribe to public trade updates - + The symbols to subscribe, for example `PF_ETHUSD` Update handler @@ -2179,7 +2549,7 @@ Subscribe to user trades updates - + Handler for the initial snapshot data received when (re)connecting the stream Cancellation token for closing this subscription @@ -2190,10 +2560,47 @@ Shared interface for Futures socket API usage - + - Shared interface for Futures rest API usage + Client for accessing the Kraken API. + + + + + Spot API endpoints + + + + + Futures 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 Kraken websocket API. + + + + + Spot Api + + + + + Futures Api + + + + + 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 @@ -2238,7 +2645,7 @@ Get balances - + Password or authentication app code if enabled Cancellation token @@ -2247,7 +2654,7 @@ Get balances including quantity in holding - + Cancellation token Password or authentication app code if enabled @@ -2256,7 +2663,7 @@ Get trade balance - + Base asset to get trade balance for, for example `USDT` Password or authentication app code if enabled @@ -2266,7 +2673,7 @@ Get a list of open positions - + Filter by transaction ids Password or authentication app code if enabled @@ -2276,7 +2683,7 @@ Get ledger entries info - + Filter list by asset names, for example `USDT` Filter list by entry types @@ -2290,7 +2697,7 @@ Get info on specific ledger entries - + The ids to get info for Password or authentication app code if enabled @@ -2300,7 +2707,7 @@ Get trade volume - + Symbols to get data for, for example `ETHUSDT` Password or authentication app code if enabled @@ -2310,7 +2717,7 @@ Get deposit methods - + Asset to get methods for, for example `ETH` Password or authentication app code if enabled @@ -2320,7 +2727,7 @@ Get deposit addresses for an asset - + The asset to get the deposit address for, for example `ETH` The method of deposit @@ -2333,7 +2740,7 @@ Get status of deposits - + Asset to get deposit info for, for example `ETH` The deposit method @@ -2344,7 +2751,7 @@ Get deposit history - + Asset filter, for example `ETH` Deposit method @@ -2359,7 +2766,7 @@ Retrieve fee information about potential withdrawals for a particular asset, key and amount. - + The asset, for example `ETH` The withdrawal key name @@ -2371,7 +2778,7 @@ Withdraw funds - + The asset being withdrawn, for example `ETH` The withdrawal key name, as set up on your account @@ -2384,7 +2791,7 @@ Get withdraw addresses - + The asset to get the deposit address for, for example `ETH` Filter addresses for specific asset class @@ -2397,7 +2804,7 @@ Retrieve a list of withdrawal methods available for the user. - + The asset to get the deposit address for, for example `ETH` Filter addresses for specific asset class @@ -2407,8 +2814,8 @@ - Get the token to connect to the private websocket streams - + Get the token to connect to the private websocket streams. Note that this endpoint is used internally and there is normally no need to call this. + Cancellation token @@ -2416,7 +2823,7 @@ Get status of withdrawals - + Filter by asset, for example `ETH` Filter by method @@ -2427,7 +2834,7 @@ Get withdrawal history - + Asset filter, for example `ETH` Withdrawal method @@ -2442,7 +2849,7 @@ Cancel an active withdrawal - + Asset, for example `ETH` Reference id @@ -2453,7 +2860,7 @@ Transfer funds between wallets - + Asset, for example `ETH` Quantity @@ -2471,7 +2878,7 @@ List earn strategies along with their parameters. - + Filter by asset, for example `USDT` Filter by lock type @@ -2485,7 +2892,7 @@ Get earn allocations - + Convert asset, defaults to USD Hide zero allocations @@ -2497,7 +2904,7 @@ Get status of the last allocation request - + Strategy id Password or authentication app code if enabled @@ -2507,7 +2914,7 @@ Get status of the last deallocation request - + Strategy id Password or authentication app code if enabled @@ -2517,7 +2924,7 @@ Allocate earn funds to a strategy - + Strategy id Amount to allocate @@ -2528,7 +2935,7 @@ Deallocate previously allocated funds - + Strategy id Amount to deallocate @@ -2544,7 +2951,7 @@ Get the server time - + Cancellation token Server time @@ -2552,7 +2959,7 @@ Get the system status - + Cancellation token System status @@ -2560,7 +2967,7 @@ Get a list of assets and info about them - + Filter list for specific assets, for example `ETH` Cancellation token @@ -2569,7 +2976,7 @@ Get a list of symbols and info about them - + Filter whats available for a specific country/region Filter list for specific symbols, for example `ETHUSDT` @@ -2579,7 +2986,7 @@ Get tickers for symbol - + Symbol to get tickers for, for example `ETHUSDT` Cancellation token @@ -2588,7 +2995,7 @@ Get tickers for symbols - + Symbols to get tickers for, for example `ETHUSDT` Cancellation token @@ -2597,7 +3004,7 @@ Gets kline data for a symbol - + The symbol to get data for, for example `ETHUSDT` The interval of the klines @@ -2608,7 +3015,7 @@ Get the order book for a symbol - + Symbol to get the book for, for example `ETHUSDT` Limit to book to the best x bids/asks @@ -2618,7 +3025,7 @@ Get a list of recent trades for a symbol - + Symbol to get trades for, for example `ETHUSDT` Return trades since a specific time @@ -2629,7 +3036,7 @@ Get spread data for a symbol - + Symbol to get spread data for, for example `ETHUSDT` Return spread data since a specific time @@ -2649,7 +3056,7 @@ Get a list of open orders - + Filter by client order id Password or authentication app code if enabled @@ -2659,7 +3066,7 @@ Get a list of closed orders - + Filter by client order id Return data after this time @@ -2672,7 +3079,7 @@ Get info on specific order - + Get orders by clientOrderId Get order by its order id @@ -2685,7 +3092,7 @@ Get info on specific orders - + Get orders by clientOrderId Get orders by their order ids @@ -2698,7 +3105,7 @@ Get trade history - + Return data after this time Return data before this time @@ -2711,7 +3118,7 @@ Get info on specific trades - + The trade to get info on Password or authentication app code if enabled @@ -2721,7 +3128,7 @@ Get info on specific trades - + The trades to get info on Password or authentication app code if enabled @@ -2731,6 +3138,7 @@ Place multiple new orders + The symbol the order is on, for example `ETHUSDT` The orders to place @@ -2739,15 +3147,16 @@ Cancellation token - + Place a new order - + The symbol the order is on, for example `ETHUSDT` The side of the order The type of the order The quantity of the order + A numeric id to reference the order by A client id to reference the order by Price of the order:
Limit=limit price
@@ -2790,7 +3199,7 @@
Edit an order - + Symbol, for example `ETHUSDT` Order id or client order id of the order to edit @@ -2814,7 +3223,7 @@ Cancel an order - + The id of the order to cancel Password or authentication app code if enabled @@ -2824,15 +3233,25 @@ Cancel all orders - + Password or authentication app code if enabled Cancellation token Cancel result + + + Cancel all orders after the timeout expires. Can be called at an interval to keep extending the timeout and word as a dead-man switch. Timeout can be disabled by setting timeout to 0. + + + + + + + - Spot API + Spot V2 websocket API @@ -2843,36 +3262,36 @@ Subscribe to system status 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 ticker updates - + Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property 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 - + Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property 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 kline updates - + Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property Kline interval @@ -2880,10 +3299,10 @@ Cancellation token for closing this subscription A stream subscription. This streamv subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to kline updates - + Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property Kline interval @@ -2891,176 +3310,231 @@ Cancellation token for closing this subscription A stream subscription. This streamv subscription can be used to be notified when the socket is disconnected/reconnected - + Subscribe to trade updates - + Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property Data handler + Whether or not a snapshot of the last trades should be send after subscribing 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 trade updates - + Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property Data handler + Whether or not a snapshot of the last trades should be send after subscribing 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 spread updates - + Subscribe to order book updates. Order book entries are aggregated per price level + - Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + Depth of the initial order book snapshot. 10, 25, 100, 500 or 1000 Data handler + Whether or not a snapshot of the last book state should be send after subscribing 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 spread updates - + Subscribe to order book updates. Order book entries are aggregated per price level + - Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + Depth of the initial order book snapshot. 10, 25, 100, 500 or 1000 Data handler + Whether or not a snapshot of the last book state should be send after subscribing 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 depth updates - + Subscribe to the full order book with individual orders. Requires authentication. + Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property - Depth of the initial order book snapshot. 10, 25, 100, 500 or 1000 + Depth of the initial order book snapshot. 10, 100 or 1000 Data handler + Whether or not a snapshot of the last book state should be send after subscribing 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 depth updates - + Subscribe to the full order book with individual orders. Requires authentication. + Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property - Depth of the initial order book snapshot. 10, 25, 100, 500 or 1000 + Depth of the initial order book snapshot. 10, 100 or 1000 Data handler + Whether or not a snapshot of the last book state should be send after subscribing 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 open order updates - + Subscribe to instrument (asset and symbol) updates + - The socket token as retrieved by the restClient.SpotApi.Account.GetWebsocketTokenAsync method Data handler + Whether or not a snapshot of the current instruments state should be send after subscribing 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 own trade updates - + Subscribe to user balances updates + - The socket token as retrieved by the restClient.SpotApi.Account.GetWebsocketTokenAsync method - Data handler + Handler for the initial snapshot data send after subscribing + Handler for update data when changes occur + Whether or not a snapshot of the current balances should be send after subscribing 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 own trade updates - + Subscribe to user order updates + - The socket token as retrieved by the restClient.SpotApi.Account.GetWebsocketTokenAsync method - Whether or not to receive a snapshot of the data upon subscribing - Data handler + Data handler. Depending on the event not all fields on the update event are filled + Whether or not a snapshot of the current open orders should be send after subscribing + Whether or not a snapshot of the last 50 user trades should be send after subscribing Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - + Place a new order - + - The socket token as retrieved by the restClient.SpotApi.Account.GetWebsocketTokenAsync method The symbol the order is on, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the The side of the order The type of the order The quantity of the order - A client id to reference the order by - Price of the order:
- Limit=limit price
- StopLoss=stop loss price
- TakeProfit=take profit price
- StopLossProfit=stop loss price
- StopLossProfitLimit=stop loss price
- StopLossLimit=stop loss trigger price
- TakeProfitLimit=take profit trigger price
- TrailingStop=trailing stop offset
- TrailingStopLimit=trailing stop offset
- StopLossAndLimit=stop loss price - - Secondary price of an order:
- StopLossProfit/StopLossProfitLimit=take profit price
- StopLossLimit/TakeProfitLimit=triggered limit price
- TrailingStopLimit=triggered limit offset
- StopLossAndLimit=limit price - Desired leverage + Client order id + User reference id + Order limit price + Limit price type + Time in force + Reduce only order + Funds the order on margin using the maximum leverage for the pair. Note, absolute max leverage is 5. + Post only order flag Scheduled start time Expiration time + Deadline time, the engine will prevent this order from matching after this time, it provides protection against latency on time sensitive orders. + Display size for iceberg orders + Fee preference + Execute without market price protection + Self trade prevention type + Quantity in quote asset (buy market orders only) Only validate inputs, don't actually place the order - Close order type - Close order price - Close order secondary price - Order flags + Order trigger price reference + Order trigger price + Order trigger price type + Conditional order type + Conditional order limit price + Conditional order limit price type + Conditional order trigger price + Conditional order trigger price type + Cancellation token + + + +
+ Edit an existing order + + + Order id, either this or clientOrderId should be provided + Client order id, either this or orderId should be provided + New limit price + New limit price type + New quantity + New iceberg quantity + New post only flag + New trigger price + New trigger price type + Deadline + Cancellation token + + + + + Replace an existing order + + + The symbol the order is on, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the + Order id, either this or clientOrderId should be provided + Client order id, either this or orderId should be provided + The quantity of the order + User reference id + Order limit price Reduce only order - Funds the order on margin using the maximum leverage for the pair. Note, absolute max leverage is 5. + Post only order flag + Deadline time, the engine will prevent this order from matching after this time, it provides protection against latency on time sensitive orders. + Display size for iceberg orders + Fee preference + Execute without market price protection + Only validate inputs, don't actually place the order + Order trigger price reference + Order trigger price + Order trigger price type + Cancellation token + + + + + Place multiple orders in a single request + + + Symbol to place the orders on + Order info + Deadline time, the engine will prevent this order from matching after this time, it provides protection against latency on time sensitive orders. + Only validate inputs, don't actually place the order Cancellation token - + Cancel an order - + - The socket token as retrieved by the restClient.SpotApi.Account.GetWebsocketTokenAsync method Id of the order to cancel Cancellation token - + Cancel multiple orders - + - The socket token as retrieved by the restClient.SpotApi.Account.GetWebsocketTokenAsync method Id of the orders to cancel Cancellation token - + Cancel all open orders - + - The socket token as retrieved by the restClient.SpotApi.Account.GetWebsocketTokenAsync method Cancellation token - + Cancel all open orders after the timeout - + - The socket token as retrieved by the restClient.SpotApi.Account.GetWebsocketTokenAsync method Cancellation token @@ -3069,48 +3543,6 @@ Shared interface for Spot socket API usage
- - - Client for accessing the Kraken API. - - - - - Spot API endpoints - - - - - Futures 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 Kraken websocket API. - - - - - Spot Api - - - - - Futures Api - - - - - 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 - Order info @@ -3297,7 +3729,7 @@ - Place order request + Cancel orders after request @@ -3305,34 +3737,54 @@ Timeout - + + + Order trigger + + + - Cancel orders request + Trigger price reference - + - Place order request + Trigger price - + - Socket request base + Trigger price type - + - Request id + Order condition - + - Event + Order type + + + + + Limit price + + + + + Limit price type - + - Token + Trigger rpice + + + + + Trigger price type @@ -3508,6 +3960,26 @@ Type of the balance info
+ + + Balance info + + + + + Cash account + + + + + Multi collateral margin account + + + + + Margin accounts + + Cash balances @@ -4203,11 +4675,6 @@ The symbol - - - The profit and loss currency - - Unrealised funding on the position. @@ -4848,6 +5315,11 @@ Margin account balances + + + Symbol + + Balances @@ -5218,44 +5690,44 @@ Status - + Balance info - + Asset - + Balance - + - Balance info + The quantity currently locked into a trade - + - Asset + The quantity available - + - Balance + Cancel after result - + - The quantity currently locked into a trade + Current time - + - The quantity available + Trigger time @@ -5788,6 +6260,11 @@ Reference id
+ + + Client reference id + + Client reference id @@ -5910,7 +6387,7 @@ - Secondary price of the order ( for details) + Secondary price of the order ( for details) @@ -6053,6 +6530,11 @@ Order to place + + + User reference + + Client order id @@ -6393,198 +6875,57 @@ Best ask quantity - + - Kraken's response to a staking request. + Symbol info - + - Reference id which can be tracked back to a ledger entry corresponding to the - staked asset (e.g. DOT.S). + Alternative name - + - Represents an asset that can be staked by the user. + Name to use for the socket client subscriptions - + - Unique ID of the staking option (used in Stake/Unstake operations). + Class of the base asset - + - Asset code/name e.g. DOT. + Name of the base asset - + - Staking asset code/name e.g. DOT.S + Class of the quote asset - + - Describes the rewards earned while staking. + Name of the quote asset - + - Whether the staking operation is on-chain or not. + Lot size - + - Whether the user will be able to stake this asset. + Decimals of the symbol - + - Whether the user will be able to unstake this asset. + Lot decimals - - - Minimium amounts for staking/unstaking. - - - - - Minimum amounts for staking/unstaking. - - - - - The minimum amount of value that can be staked. - - - - - The minimum amount of value that can be unstaked. - - - - - Describes the rewards earned while staking. - - - - - Reward earned while staking. - - - - - The type of the reward e.g. "percentage". - - - - - Staking Transaction Info - - - - - Staking method as described by . - - - - - Asset code/name e.g. DOT - - - - - The reference ID of the transaction. - - - - - The transaction amount. - - - - - Transaction fee. - - - - - Unix timestamp when the transaction was initiated. - - - - - Transaction status. - - - - - Transaction type. - - - - - Unix timestamp from the start of bond period (applicable only to bonding transactions). - - - - - Unix timestamp from the start of bond period (applicable only to bonding transactions). - - - - - Symbol info - - - - - Alternative name - - - - - Name to use for the socket client subscriptions - - - - - Class of the base asset - - - - - Name of the base asset - - - - - Class of the quote asset - - - - - Name of the quote asset - - - - - Lot size - - - - - Decimals of the symbol - - - - - Lot decimals - - - + Cost decimals @@ -6940,17 +7281,6 @@ Tier volume
- - - Kraken's response to an unstaking request. - - - - - Reference id which can be tracked back to a ledger entry corresponding to the - unstaked asset (e.g. DOT.S). - - User trade info @@ -7166,6 +7496,71 @@ Minimum amount + + + Balance update + + + + + Ledger id + + + + + Reference id + + + + + Timestamp + + + + + Type + + + + + Asset + + + + + Asset class + + + + + Category + + + + + Wallet type + + + + + Wallet id + + + + + Quantity + + + + + Fee + + + + + Balance + + Account log snapshot update @@ -8141,244 +8536,1014 @@ Order type - + - Cancel after result + Symbol and asset updates - + - Current time + Assets - + - Trigger time + Symbols - + - Cancel all result + Asset info - + - Number of orders canceled + Asset - + - Placed order result + Status - + - Order description + Precision - + - Placed order id + Recommended display precision - + - System status + Borrowable - + - Connection id + Collateral value - + - Name of the event + Margin rate - + - Status + Symbol info - + - Version + Symbol - + - Socket token + Base asset - + - Token to use for connecting to private websockets + Quote asset - + - Expires after x seconds + Status - + - Options for the Kraken SymbolOrderBook + Quantity precision - + - Default options for the Kraken SymbolOrderBook + Quantity increment step - + - The limit of entries in the order book + Price precision - + - After how much time we should consider the connection dropped if no data is received for this time after the initial subscriptions + Cost precision - + - Options for the KrakenRestClient + Marginable - + - Default options for new KrakenRestClients + Has index - + - The static password configured as two-factor authentication for the API key. Will be send as otp parameter on private requests. + Minimal notional value of an order - + - Optional nonce provider for signing requests. Careful providing a custom provider; once a nonce is sent to the server, every request after that needs a higher nonce than that + Initial margin requirement - + - Options for the Spot API + Position limit long - + - Options for the Futures API + Position limit short - + - Options for the KrakenSocketClient + Price increment step - + - Default options for new KrakenRestClients + Min order quantity - + - Optional nonce provider for signing requests. Careful providing a custom provider; once a nonce is sent to the server, every request after that needs a higher nonce than that + Snapshot data - + - Options for the Spot API + Asset - + - Options for the Futures API + Asset class - + - Kraken message event + Balance - + - The message event + Wallets - + - Socket event + Wallet info - + - The event type + Type - + - The feed + Id - + - The symbols + Balance - + - The event type + Order book update - + - The feed + The symbol - + - The symbols + Asks in the book - + - The event type + Bids in the book - + - The feed + Checksum - + - Message + Order book entry - + - The symbols + The price - + - Kraken response to a query + The quantity - + - Response status + Book update - + - Optional error message + The symbol - + - Live order book implementation + Asks in the book + + + + + Bids in the book + + + + + Book order entry + + + + + The order id + + + + + Price + + + + + Quantity + + + + + Timestamp + + + + + Event + + + + + Kline/candlestick info + + + + + Symbol + + + + + Open price + + + + + High price + + + + + Low price + + + + + Close price + + + + + Number of trades + + + + + Volume + + + + + Volume weighted average price + + + + + Open timestamp + + + + + Interval + + + + + Order result + + + + + Order id + + + + + Order id + + + + + User reference id + + + + + Error + + + + + Status + + + + + Kraken order update + + + + + Order id + + + + + Symbol + + + + + Client order id + + + + + Order quantity in quote asset + + + + + Order quantity + + + + + Filled quantity value + + + + + Filled quantity + + + + + Display quantity for iceberg orders + + + + + Time in force + + + + + Order event + + + + + Side + + + + + Order type + + + + + Order user reference + + + + + Limit price + + + + + Stop price + + + + + Order status + + + + + Fee paid expressed in USD + + + + + Fee asset preference + + + + + Scheduled start time of the order + + + + + Scheduled expiration time of the order + + + + + Timestamp + + + + + Average order trade price + + + + + Fees paid + + + + + Whether the order has been amended + + + + + Indicates if the order has been liquidated by the engine + + + + + Indicates if the order can be funded on margin + + + + + Indicates if an execution is on margin, i.e. if the trade increased or reduced size of margin borrowing. On trade events only + + + + + Indicates if the order has market price protection + + + + + Post only flag + + + + + Reduce only flag + + + + + Indicates status of the position on a margin order + + + + + The reason associated with an event, if applicable + + + + + Id of the execution this update is for + + + + + Id of the trade this update is for + + + + + Quantity of the trade this update is for + + + + + Price of the trade this update is for + + + + + Value of the trade this update is for + + + + + Trade role of the trade this update is for, maker or taker + + + + + Fee info + + + + + Fee asset + + + + + Quantity + + + + + Amend order result + + + + + Amend id + + + + + Order id + + + + + Client order id + + + + + Order request + + + + + Order type + + + + + Order side + + + + + Quote quantity + + + + + Conditional order + + + + + Iceberg quantity + + + + + Start time + + + + + Expire time + + + + + Fee preference setting + + + + + Limit price + + + + + Limit price type + + + + + Funds the order on margin using the maximum leverage for the pair (maximum is leverage of 5). + + + + + Disable market price protection + + + + + Client order id + + + + + User reference + + + + + Order quantity + + + + + Post only flag + + + + + Reduce only flag + + + + + Self trade prevention type + + + + + Time in force + + + + + Trigger info + + + + + Replace order result + + + + + Order id + + + + + The original order id + + + + + Cancel all result + + + + + Number of orders canceled + + + + + System status + + + + + Connection id + + + + + Status + + + + + Version + + + + + API Version + + + + + Ticker info + + + + + Symbol + + + + + Price of best bid + + + + + Quantity of the best bid + + + + + Price of best ask + + + + + Quantity of the best ask + + + + + Last trade price + + + + + Volume + + + + + Volume weighted average price of last 24 hours + + + + + Low price + + + + + High price + + + + + Price change in the last 24 hours + + + + + Price change percentage in last 24 hours + + + + + Trade info + + + + + Symbol + + + + + Side + + + + + Price + + + + + Quantity + + + + + Order type + + + + + Trade id + + + + + Timestamp + + + + + Socket token + + + + + Token to use for connecting to private websockets + + + + + Expires after x seconds + + + + + Options for the Kraken SymbolOrderBook + + + + + Default options for the Kraken SymbolOrderBook + + + + + The limit of entries in the order book + + + + + After how much time we should consider the connection dropped if no data is received for this time after the initial subscriptions + + + + + Options for the KrakenRestClient + + + + + Default options for new KrakenRestClients + + + + + The static password configured as two-factor authentication for the API key. Will be send as otp parameter on private requests. + + + + + Optional nonce provider for signing requests. Careful providing a custom provider; once a nonce is sent to the server, every request after that needs a higher nonce than that + + + + + Options for the Spot API + + + + + Options for the Futures API + + + + + Options for the KrakenSocketClient + + + + + Default options for new KrakenRestClients + + + + + Optional nonce provider for signing requests. Careful providing a custom provider; once a nonce is sent to the server, every request after that needs a higher nonce than that + + + + + Options for the Spot API + + + + + Options for the Futures API + + + + + Kraken message event + + + + + The channel + + + + + Socket event + + + + + The event type + + + + + The feed + + + + + The symbols + + + + + The event type + + + + + The feed + + + + + The symbols + + + + + The event type + + + + + The feed + + + + + Message + + + + + The symbols + + + + + Live order book implementation @@ -8453,9 +9618,6 @@ - - - diff --git a/Kraken.Net/KrakenAuthenticationProvider.cs b/Kraken.Net/KrakenAuthenticationProvider.cs index 33d7704..adccf4e 100644 --- a/Kraken.Net/KrakenAuthenticationProvider.cs +++ b/Kraken.Net/KrakenAuthenticationProvider.cs @@ -1,20 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Security.Cryptography; +using System.Security.Cryptography; using System.Text; -using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Clients; -using CryptoExchange.Net.Interfaces; -using CryptoExchange.Net.Objects; using Kraken.Net.Objects; -using Newtonsoft.Json; namespace Kraken.Net { internal class KrakenAuthenticationProvider: AuthenticationProvider { + private static readonly IMessageSerializer _serializer = new SystemTextJsonMessageSerializer(); private readonly INonceProvider _nonceProvider; private readonly byte[] _hmacSecret; @@ -45,12 +38,12 @@ public override void AuthenticateRequest( IDictionary parameters; if (parameterPosition == HttpMethodParameterPosition.InUri) { - uriParameters ??= new Dictionary(); + uriParameters ??= new ParameterCollection(); parameters = uriParameters; } else { - bodyParameters ??= new Dictionary(); + bodyParameters ??= new ParameterCollection(); parameters = bodyParameters; } @@ -62,7 +55,8 @@ public override void AuthenticateRequest( if (uri.PathAndQuery == "/0/private/AddOrderBatch") { // Only endpoint using json body data atm - np = nonce + JsonConvert.SerializeObject(parameters); + np = nonce + GetSerializedBody(_serializer, parameters); + } else { diff --git a/Kraken.Net/KrakenEnvironment.cs b/Kraken.Net/KrakenEnvironment.cs index 11eb10c..5b6b6ff 100644 --- a/Kraken.Net/KrakenEnvironment.cs +++ b/Kraken.Net/KrakenEnvironment.cs @@ -1,5 +1,4 @@ -using CryptoExchange.Net.Objects; -using Kraken.Net.Objects; +using Kraken.Net.Objects; namespace Kraken.Net { diff --git a/Kraken.Net/KrakenFuturesAuthenticationProvider.cs b/Kraken.Net/KrakenFuturesAuthenticationProvider.cs index 419936c..7b88a63 100644 --- a/Kraken.Net/KrakenFuturesAuthenticationProvider.cs +++ b/Kraken.Net/KrakenFuturesAuthenticationProvider.cs @@ -1,11 +1,5 @@ -using CryptoExchange.Net.Authentication; -using CryptoExchange.Net.Clients; -using CryptoExchange.Net.Interfaces; -using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Clients; using Kraken.Net.Objects; -using System; -using System.Collections.Generic; -using System.Net.Http; using System.Security.Cryptography; using System.Text; @@ -48,12 +42,12 @@ public override void AuthenticateRequest( IDictionary parameters; if (parameterPosition == HttpMethodParameterPosition.InUri) { - uriParameters ??= new Dictionary(); + uriParameters ??= new ParameterCollection(); parameters = uriParameters; } else { - bodyParameters ??= new Dictionary(); + bodyParameters ??= new ParameterCollection(); parameters = bodyParameters; } diff --git a/Kraken.Net/Objects/Internal/KrakenInfoEvent.cs b/Kraken.Net/Objects/Internal/KrakenInfoEvent.cs index e46572a..1de532e 100644 --- a/Kraken.Net/Objects/Internal/KrakenInfoEvent.cs +++ b/Kraken.Net/Objects/Internal/KrakenInfoEvent.cs @@ -1,14 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Kraken.Net.Objects.Internal +namespace Kraken.Net.Objects.Internal { internal record KrakenInfoEvent { - [JsonProperty("event")] + [JsonPropertyName("event")] public string Event { get; set; } = string.Empty; - [JsonProperty("version")] + [JsonPropertyName("version")] public int Version { get; set; } } } diff --git a/Kraken.Net/Objects/Internal/KrakenResult.cs b/Kraken.Net/Objects/Internal/KrakenResult.cs index a9d777b..b6d66bd 100644 --- a/Kraken.Net/Objects/Internal/KrakenResult.cs +++ b/Kraken.Net/Objects/Internal/KrakenResult.cs @@ -1,15 +1,14 @@ -using System; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Internal +namespace Kraken.Net.Objects.Internal { internal class KrakenResult { + [JsonPropertyName("error")] public IEnumerable Error { get; set; } = Array.Empty(); } - internal class KrakenResult: KrakenResult + internal class KrakenResult : KrakenResult { + [JsonPropertyName("result")] public T Result { get; set; } = default!; } } diff --git a/Kraken.Net/Objects/Internal/KrakenSocketAuthRequestV2.cs b/Kraken.Net/Objects/Internal/KrakenSocketAuthRequestV2.cs new file mode 100644 index 0000000..69e4a5f --- /dev/null +++ b/Kraken.Net/Objects/Internal/KrakenSocketAuthRequestV2.cs @@ -0,0 +1,8 @@ +namespace Kraken.Net.Objects.Internal +{ + internal class KrakenSocketAuthRequestV2 + { + [JsonPropertyName("token")] + public string Token { get; set; } = string.Empty; + } +} diff --git a/Kraken.Net/Objects/Internal/KrakenSocketCancelAfterRequest.cs b/Kraken.Net/Objects/Internal/KrakenSocketCancelAfterRequest.cs index df08353..422262c 100644 --- a/Kraken.Net/Objects/Internal/KrakenSocketCancelAfterRequest.cs +++ b/Kraken.Net/Objects/Internal/KrakenSocketCancelAfterRequest.cs @@ -1,16 +1,14 @@ -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Internal +namespace Kraken.Net.Objects.Internal { /// - /// Place order request + /// Cancel orders after request /// - internal class KrakenSocketCancelAfterRequest : KrakenSocketAuthRequest + internal class KrakenSocketCancelAfterRequest : KrakenSocketAuthRequestV2 { /// /// Timeout /// - [JsonProperty("timeout")] + [JsonPropertyName("timeout")] public int Timeout { get; set; } } } diff --git a/Kraken.Net/Objects/Internal/KrakenSocketCancelOrdersRequest.cs b/Kraken.Net/Objects/Internal/KrakenSocketCancelOrdersRequest.cs deleted file mode 100644 index 247d7ca..0000000 --- a/Kraken.Net/Objects/Internal/KrakenSocketCancelOrdersRequest.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Internal -{ - /// - /// Cancel orders request - /// - internal class KrakenSocketCancelOrdersRequest : KrakenSocketAuthRequest - { - [JsonProperty("txid")] - public IEnumerable OrderIds { get; set; } = Array.Empty(); - - } -} diff --git a/Kraken.Net/Objects/Internal/KrakenSocketCancelOrdersRequestV2.cs b/Kraken.Net/Objects/Internal/KrakenSocketCancelOrdersRequestV2.cs new file mode 100644 index 0000000..fa3b092 --- /dev/null +++ b/Kraken.Net/Objects/Internal/KrakenSocketCancelOrdersRequestV2.cs @@ -0,0 +1,8 @@ +namespace Kraken.Net.Objects.Internal +{ + internal class KrakenSocketCancelOrdersRequestV2 : KrakenSocketAuthRequestV2 + { + [JsonPropertyName("order_id")] + public string[] OrderIds { get; set; } = Array.Empty(); + } +} diff --git a/Kraken.Net/Objects/Internal/KrakenSocketEditOrderRequest.cs b/Kraken.Net/Objects/Internal/KrakenSocketEditOrderRequest.cs new file mode 100644 index 0000000..d10ba20 --- /dev/null +++ b/Kraken.Net/Objects/Internal/KrakenSocketEditOrderRequest.cs @@ -0,0 +1,28 @@ +using Kraken.Net.Enums; + +namespace Kraken.Net.Objects.Internal +{ + internal class KrakenSocketEditOrderRequest : KrakenSocketAuthRequestV2 + { + [JsonPropertyName("order_id")] + public string? OrderId { get; set; } + [JsonPropertyName("cl_ord_id")] + public string? ClientOrderId { get; set; } + [JsonPropertyName("order_qty"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? Quantity { get; set; } + [JsonPropertyName("limit_price"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? Price { get; set; } + [JsonPropertyName("limit_price_type"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public PriceType? LimitPriceType { get; set; } + [JsonPropertyName("display_qty"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? IcebergQuantity { get; set; } + [JsonPropertyName("post_only"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? PostOnly { get; set; } + [JsonPropertyName("trigger_price"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? TriggerPrice { get; set; } + [JsonPropertyName("trigger_price_type"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public PriceType? TriggerPriceType { get; set; } + [JsonPropertyName("deadline"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? Deadline { get; set; } + } +} diff --git a/Kraken.Net/Objects/Internal/KrakenSocketPlaceMultipleOrderRequestV2.cs b/Kraken.Net/Objects/Internal/KrakenSocketPlaceMultipleOrderRequestV2.cs new file mode 100644 index 0000000..b739a0b --- /dev/null +++ b/Kraken.Net/Objects/Internal/KrakenSocketPlaceMultipleOrderRequestV2.cs @@ -0,0 +1,17 @@ +using Kraken.Net.Objects.Models.Socket; + +namespace Kraken.Net.Objects.Internal +{ + internal class KrakenSocketPlaceMultipleOrderRequestV2 : KrakenSocketAuthRequestV2 + { + [JsonPropertyName("symbol")] + public string Symbol { get; set; } = string.Empty; + [JsonPropertyName("validate"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? ValidateOnly { get; set; } + [JsonPropertyName("deadline"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? Deadline { get; set; } + [JsonPropertyName("orders")] + public IEnumerable Orders { get; set; } = Array.Empty(); + } + +} diff --git a/Kraken.Net/Objects/Internal/KrakenSocketPlaceOrderRequest.cs b/Kraken.Net/Objects/Internal/KrakenSocketPlaceOrderRequest.cs deleted file mode 100644 index 2169133..0000000 --- a/Kraken.Net/Objects/Internal/KrakenSocketPlaceOrderRequest.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Kraken.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Internal -{ - /// - /// Place order request - /// - internal class KrakenSocketPlaceOrderRequest: KrakenSocketAuthRequest - { - [JsonProperty("pair")] - public string Symbol { get; set; } = string.Empty; - [JsonProperty("ordertype")] - [JsonConverter(typeof(OrderTypeConverter))] - public OrderType OrderType { get; set; } - [JsonProperty("type")] - [JsonConverter(typeof(OrderSideConverter))] - public OrderSide Type { get; set; } - [JsonProperty("leverage", DefaultValueHandling = DefaultValueHandling.Ignore)] - public string? Leverage { get; set; } - [JsonProperty("volume", DefaultValueHandling = DefaultValueHandling.Ignore)] - public string? Volume { get; set; } - [JsonProperty("userref", DefaultValueHandling = DefaultValueHandling.Ignore)] - public string? ClientOrderId { get; set; } - [JsonProperty("price", DefaultValueHandling = DefaultValueHandling.Ignore)] - public string? Price { get; set; } - [JsonProperty("price2", DefaultValueHandling = DefaultValueHandling.Ignore)] - public string? SecondaryPrice { get; set; } - [JsonProperty("starttm", DefaultValueHandling = DefaultValueHandling.Ignore)] - public string? StartTime { get; set; } - [JsonProperty("expiretm", DefaultValueHandling = DefaultValueHandling.Ignore)] - public string? ExpireTime { get; set; } - [JsonProperty("validate", DefaultValueHandling = DefaultValueHandling.Ignore)] - public bool? ValidateOnly { get; set; } - [JsonProperty("close[ordertype]", DefaultValueHandling = DefaultValueHandling.Ignore)] - [JsonConverter(typeof(OrderTypeConverter))] - public OrderType? CloseOrderType { get; set; } - [JsonProperty("close[price]", DefaultValueHandling = DefaultValueHandling.Ignore)] - public string? ClosePrice { get; set; } - [JsonProperty("close[price2]", DefaultValueHandling = DefaultValueHandling.Ignore)] - public string? SecondaryClosePrice { get; set; } - [JsonProperty("oflags", DefaultValueHandling = DefaultValueHandling.Ignore)] - public string? Flags { get; set; } - [JsonProperty("reduce_only", DefaultValueHandling = DefaultValueHandling.Ignore)] - public bool? ReduceOnly { get; set; } - [JsonProperty("margin", DefaultValueHandling = DefaultValueHandling.Ignore)] - public bool? Margin { get; set; } - } -} diff --git a/Kraken.Net/Objects/Internal/KrakenSocketPlaceOrderRequestV2.cs b/Kraken.Net/Objects/Internal/KrakenSocketPlaceOrderRequestV2.cs new file mode 100644 index 0000000..39a0b50 --- /dev/null +++ b/Kraken.Net/Objects/Internal/KrakenSocketPlaceOrderRequestV2.cs @@ -0,0 +1,112 @@ +using Kraken.Net.Enums; + +namespace Kraken.Net.Objects.Internal +{ + internal class KrakenSocketPlaceOrderRequestV2 : KrakenSocketAuthRequestV2 + { + [JsonPropertyName("symbol")] + public string Symbol { get; set; } = string.Empty; + [JsonPropertyName("order_type")] + [JsonConverter(typeof(EnumConverter))] + public OrderType OrderType { get; set; } + [JsonPropertyName("side")] + [JsonConverter(typeof(EnumConverter))] + public OrderSide Side { get; set; } + [JsonPropertyName("order_qty"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? Quantity { get; set; } + [JsonPropertyName("limit_price"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? Price { get; set; } + [JsonPropertyName("limit_price_type"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public PriceType? LimitPriceType { get; set; } + [JsonPropertyName("time_in_force"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [JsonConverter(typeof(EnumConverter))] + public TimeInForce? TimeInForce { get; set; } + [JsonPropertyName("margin"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? Margin { get; set; } + [JsonPropertyName("post_only"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? PostOnly { get; set; } + [JsonPropertyName("reduce_only"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? ReduceOnly { get; set; } + [JsonPropertyName("effective_time"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? EffectiveTime { get; set; } + [JsonPropertyName("expire_time"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? ExpireTime { get; set; } + [JsonPropertyName("deadline"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? Deadline { get; set; } + [JsonPropertyName("cl_ord_id"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? ClientOrderId { get; set; } + [JsonPropertyName("order_userref"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public uint? UserReference { get; set; } + [JsonPropertyName("display_qty"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? IcebergQuantity { get; set; } + [JsonPropertyName("fee_preference"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public FeePreference? FeePreference { get; set; } + [JsonPropertyName("no_mpp"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? NoMarketPriceProtection { get; set; } + [JsonPropertyName("stp_type"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public SelfTradePreventionType? SelfTradePreventionType { get; set; } + [JsonPropertyName("cash_order_qty"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? QuoteQuantity { get; set; } + [JsonPropertyName("validate"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? ValidateOnly { get; set; } + + [JsonPropertyName("triggers"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public KrakenSocketPlaceOrderRequestV2Trigger? Trigger { get; set; } + [JsonPropertyName("conditional"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public KrakenSocketPlaceOrderRequestV2Condition? Conditional { get; set; } + } + + /// + /// Order trigger + /// + public record KrakenSocketPlaceOrderRequestV2Trigger + { + /// + /// Trigger price reference + /// + [JsonPropertyName("reference"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public Trigger? Reference { get; set; } + /// + /// Trigger price + /// + [JsonPropertyName("price"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? Price { get; set; } + /// + /// Trigger price type + /// + [JsonPropertyName("price_type"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public PriceType? LimitPriceType { get; set; } + } + + /// + /// Order condition + /// + public record KrakenSocketPlaceOrderRequestV2Condition + { + /// + /// Order type + /// + [JsonPropertyName("order_type"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public OrderType OrderType { get; set; } + /// + /// Limit price + /// + [JsonPropertyName("limit_price"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? Price { get; set; } + /// + /// Limit price type + /// + [JsonPropertyName("limit_price_type"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public PriceType? LimitPriceType { get; set; } + /// + /// Trigger rpice + /// + [JsonPropertyName("trigger_price"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? TriggerPrice { get; set; } + /// + /// Trigger price type + /// + [JsonPropertyName("trigger_price_type"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public PriceType? TriggerPriceType { get; set; } + } +} diff --git a/Kraken.Net/Objects/Internal/KrakenSocketReplaceOrderRequest.cs b/Kraken.Net/Objects/Internal/KrakenSocketReplaceOrderRequest.cs new file mode 100644 index 0000000..394b06e --- /dev/null +++ b/Kraken.Net/Objects/Internal/KrakenSocketReplaceOrderRequest.cs @@ -0,0 +1,34 @@ +using Kraken.Net.Enums; + +namespace Kraken.Net.Objects.Internal +{ + internal class KrakenSocketReplaceOrderRequest : KrakenSocketAuthRequestV2 + { + [JsonPropertyName("deadline"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? Deadline { get; set; } + [JsonPropertyName("display_qty"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? IcebergQuantity { get; set; } + [JsonPropertyName("fee_preference"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public FeePreference? FeePreference { get; set; } + [JsonPropertyName("limit_price"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? Price { get; set; } + [JsonPropertyName("no_mpp"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? NoMarketPriceProtection { get; set; } + [JsonPropertyName("order_id")] + public string? OrderId { get; set; } + [JsonPropertyName("order_qty"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? Quantity { get; set; } + [JsonPropertyName("order_userref"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public uint? UserReference { get; set; } + [JsonPropertyName("post_only"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? PostOnly { get; set; } + [JsonPropertyName("reduce_only"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? ReduceOnly { get; set; } + [JsonPropertyName("symbol")] + public string Symbol { get; set; } = string.Empty; + [JsonPropertyName("validate"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? ValidateOnly { get; set; } + [JsonPropertyName("triggers"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public KrakenSocketPlaceOrderRequestV2Trigger? Trigger { get; set; } + } +} diff --git a/Kraken.Net/Objects/Internal/KrakenSocketRequestBase.cs b/Kraken.Net/Objects/Internal/KrakenSocketRequestBase.cs deleted file mode 100644 index b598a62..0000000 --- a/Kraken.Net/Objects/Internal/KrakenSocketRequestBase.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Internal -{ - /// - /// Socket request base - /// - internal class KrakenSocketRequest - { - /// - /// Request id - /// - [JsonProperty("reqid")] - public int RequestId { get; set; } - /// - /// Event - /// - [JsonProperty("event")] - public string Event { get; set; } = string.Empty; - } - - internal class KrakenSocketAuthRequest : KrakenSocketRequest - { - /// - /// Token - /// - [JsonProperty("token")] - public string Token { get; set; } = string.Empty; - } -} diff --git a/Kraken.Net/Objects/Internal/KrakenSocketRequestV2.cs b/Kraken.Net/Objects/Internal/KrakenSocketRequestV2.cs new file mode 100644 index 0000000..1347b65 --- /dev/null +++ b/Kraken.Net/Objects/Internal/KrakenSocketRequestV2.cs @@ -0,0 +1,12 @@ +namespace Kraken.Net.Objects.Internal +{ + internal class KrakenSocketRequestV2 + { + [JsonPropertyName("method")] + public string Method { get; set; } = string.Empty; + [JsonPropertyName("params")] + public T Parameters { get; set; } = default!; + [JsonPropertyName("req_id")] + public long RequestId { get; set; } + } +} diff --git a/Kraken.Net/Objects/Internal/KrakenSocketResponseV2.cs b/Kraken.Net/Objects/Internal/KrakenSocketResponseV2.cs new file mode 100644 index 0000000..e263d18 --- /dev/null +++ b/Kraken.Net/Objects/Internal/KrakenSocketResponseV2.cs @@ -0,0 +1,20 @@ +namespace Kraken.Net.Objects.Internal +{ + internal class KrakenSocketResponseV2 + { + [JsonPropertyName("method")] + public string Method { get; set; } = string.Empty; + [JsonPropertyName("result")] + public T Result { get; set; } = default!; + [JsonPropertyName("success")] + public bool Success { get; set; } + [JsonPropertyName("time_in")] + public DateTime TimestampIn { get; set; } + [JsonPropertyName("time_out")] + public DateTime TimestampOut { get; set; } + [JsonPropertyName("req_id")] + public long RequestId { get; set; } + [JsonPropertyName("error")] + public string? Error { get; set; } + } +} diff --git a/Kraken.Net/Objects/Internal/KrakenSocketSubRequest.cs b/Kraken.Net/Objects/Internal/KrakenSocketSubRequest.cs new file mode 100644 index 0000000..c340cc9 --- /dev/null +++ b/Kraken.Net/Objects/Internal/KrakenSocketSubRequest.cs @@ -0,0 +1,25 @@ +namespace Kraken.Net.Objects.Internal +{ + internal class KrakenSocketSubRequest + { + [JsonPropertyName("channel")] + public string Channel { get; set; } = string.Empty; + [JsonPropertyName("symbol"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string[]? Symbol { get; set; } + + [JsonPropertyName("interval"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public int? Interval { get; set; } + + [JsonPropertyName("snapshot"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? Snapshot { get; set; } + + [JsonPropertyName("snap_orders"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? SnapshotOrders { get; set; } + + [JsonPropertyName("snap_trades"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? SnapshotTrades { get; set; } + + [JsonPropertyName("token"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? Token { get; set; } + } +} diff --git a/Kraken.Net/Objects/Internal/KrakenSocketSubResponse.cs b/Kraken.Net/Objects/Internal/KrakenSocketSubResponse.cs new file mode 100644 index 0000000..266f205 --- /dev/null +++ b/Kraken.Net/Objects/Internal/KrakenSocketSubResponse.cs @@ -0,0 +1,12 @@ +namespace Kraken.Net.Objects.Internal +{ + internal class KrakenSocketSubResponse + { + [JsonPropertyName("channel")] + public string Channel { get; set; } = string.Empty; + [JsonPropertyName("snapshot")] + public bool Snapshot { get; set; } + [JsonPropertyName("symbol")] + public string Symbol { get; set; } = string.Empty; + } +} diff --git a/Kraken.Net/Objects/Internal/KrakenSocketUpdateV2.cs b/Kraken.Net/Objects/Internal/KrakenSocketUpdateV2.cs new file mode 100644 index 0000000..fbd2f29 --- /dev/null +++ b/Kraken.Net/Objects/Internal/KrakenSocketUpdateV2.cs @@ -0,0 +1,12 @@ +namespace Kraken.Net.Objects.Internal +{ + internal class KrakenSocketUpdateV2 + { + [JsonPropertyName("channel")] + public string Channel { get; set; } = string.Empty; + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; + [JsonPropertyName("data")] + public T Data { get; set; } = default!; + } +} diff --git a/Kraken.Net/Objects/Internal/KrakenSubscribeRequest.cs b/Kraken.Net/Objects/Internal/KrakenSubscribeRequest.cs deleted file mode 100644 index 0482686..0000000 --- a/Kraken.Net/Objects/Internal/KrakenSubscribeRequest.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Linq; -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Internal -{ - internal class KrakenSubscribeRequest : KrakenSocketRequest - { - [JsonProperty("pair", DefaultValueHandling = DefaultValueHandling.Ignore)] - public string[]? Symbols { get; set; } - [JsonProperty("subscription")] - public KrakenSubscriptionDetails Details { get; set; } - - public KrakenSubscribeRequest(string topic, string? token, int? interval, bool? snapshot, int? depth, int requestId, params string[]? symbols) - { - RequestId = requestId; - Details = new KrakenSubscriptionDetails(topic, token, interval, snapshot, depth); - if(symbols?.Any() == true) - Symbols = symbols; - } - } - - - internal class KrakenSubscriptionDetails - { - [JsonProperty("name")] - public string Topic { get; set; } - - [JsonProperty("token", DefaultValueHandling = DefaultValueHandling.Ignore)] - public string? Token { get; set; } - - [JsonProperty("interval", DefaultValueHandling = DefaultValueHandling.Ignore)] - public int? Interval { get; set; } - - [JsonProperty("snapshot", DefaultValueHandling = DefaultValueHandling.Ignore)] - public bool? Snapshot { get; set; } - - [JsonProperty("depth", DefaultValueHandling = DefaultValueHandling.Ignore)] - public int? Depth { get; set; } - - public KrakenSubscriptionDetails(string topic, string? token, int? interval, bool? snapshot, int? depth) - { - Topic = topic; - Token = token; - Interval = interval; - Snapshot = snapshot; - Depth = depth; - } - } -} diff --git a/Kraken.Net/Objects/KrakenNonceProvider.cs b/Kraken.Net/Objects/KrakenNonceProvider.cs index 20af949..72da5de 100644 --- a/Kraken.Net/Objects/KrakenNonceProvider.cs +++ b/Kraken.Net/Objects/KrakenNonceProvider.cs @@ -1,7 +1,4 @@ -using CryptoExchange.Net.Interfaces; -using System; - -namespace Kraken.Net.Objects +namespace Kraken.Net.Objects { internal class KrakenNonceProvider : INonceProvider { diff --git a/Kraken.Net/Objects/KrakenOptions.cs b/Kraken.Net/Objects/KrakenOptions.cs deleted file mode 100644 index 3508810..0000000 --- a/Kraken.Net/Objects/KrakenOptions.cs +++ /dev/null @@ -1,181 +0,0 @@ -//using System; -//using System.Collections.Generic; -//using CryptoExchange.Net.Interfaces; -//using CryptoExchange.Net.Objects; -//using Kraken.Net.Interfaces.Clients; - -//namespace Kraken.Net.Objects -//{ -// /// -// /// Options for the Kraken client -// /// -// public class KrakenClientOptions : ClientOptions -// { -// /// -// /// Default options for the spot client -// /// -// public static KrakenClientOptions Default { get; set; } = new KrakenClientOptions(); - -// /// -// /// The static password configured as two-factor authentication for the API key. Will be send as otp parameter on private requests. -// /// -// public string? StaticTwoFactorAuthenticationPassword { get; set; } - -// /// -// /// Optional nonce provider for signing requests. Careful providing a custom provider; once a nonce is sent to the server, every request after that needs a higher nonce than that -// /// -// public INonceProvider? NonceProvider { get; set; } - -// private RestApiClientOptions _spotApiOptions = new RestApiClientOptions(KrakenApiAddresses.Default.SpotRestClientAddress) -// { -// RateLimiters = new List -// { -// new RateLimiter() -// .AddApiKeyLimit(15, TimeSpan.FromSeconds(45), false, false) -// .AddEndpointLimit(new [] { "/private/AddOrder", "/private/CancelOrder", "/private/CancelAll", "/private/CancelAllOrdersAfter" }, 60, TimeSpan.FromSeconds(60), null, true), -// } -// }; - -// /// -// /// Spot API options -// /// -// public RestApiClientOptions SpotApiOptions -// { -// get => _spotApiOptions; -// set => _spotApiOptions = new RestApiClientOptions(_spotApiOptions, value); -// } - -// /// -// /// ctor -// /// -// public KrakenClientOptions() : this(Default) -// { -// } - -// /// -// /// ctor -// /// -// /// Base the new options on other options -// internal KrakenClientOptions(KrakenClientOptions baseOn) : base(baseOn) -// { -// if (baseOn == null) -// return; - -// NonceProvider = baseOn.NonceProvider; -// StaticTwoFactorAuthenticationPassword = baseOn.StaticTwoFactorAuthenticationPassword; -// _spotApiOptions = new RestApiClientOptions(baseOn.SpotApiOptions, null); -// } -// } - -// /// -// /// Options for the Kraken socket client -// /// -// public class KrakenSocketClientOptions : ClientOptions -// { -// /// -// /// Default options for the spot client -// /// -// public static KrakenSocketClientOptions Default { get; set; } = new KrakenSocketClientOptions(); - -// /// -// /// Optional nonce provider for signing requests. Careful providing a custom provider; once a nonce is sent to the server, every request after that needs a higher nonce than that -// /// -// public INonceProvider? NonceProvider { get; set; } - -// private KrakenSocketApiClientOptions _spotStreamsOptions = new KrakenSocketApiClientOptions(KrakenApiAddresses.Default.SpotSocketPublicAddress, KrakenApiAddresses.Default.SpotSocketPrivateAddress) -// { -// SocketSubscriptionsCombineTarget = 10 -// }; - -// /// -// /// Spot streams options -// /// -// public KrakenSocketApiClientOptions SpotStreamsOptions -// { -// get => _spotStreamsOptions; -// set => _spotStreamsOptions = new KrakenSocketApiClientOptions(_spotStreamsOptions, value); -// } - -// /// -// /// ctor -// /// -// public KrakenSocketClientOptions() : this(Default) -// { -// } - -// /// -// /// ctor -// /// -// /// Base the new options on other options -// internal KrakenSocketClientOptions(KrakenSocketClientOptions baseOn) : base(baseOn) -// { -// if (baseOn == null) -// return; - -// NonceProvider = baseOn.NonceProvider; -// _spotStreamsOptions = new KrakenSocketApiClientOptions(baseOn.SpotStreamsOptions, null); -// } -// } - -// /// -// /// Socket API options -// /// -// public class KrakenSocketApiClientOptions : SocketApiClientOptions -// { -// /// -// /// The base address for the authenticated websocket -// /// -// public string BaseAddressAuthenticated { get; set; } - -// /// -// /// ctor -// /// -//#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -// public KrakenSocketApiClientOptions() -//#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -// { -// } - -// /// -// /// ctor -// /// -// /// -// /// -// internal KrakenSocketApiClientOptions(string baseAddress, string baseAddressAuthenticated) : base(baseAddress) -// { -// BaseAddressAuthenticated = baseAddressAuthenticated; -// } - -// /// -// /// ctor -// /// -// /// -// /// -// internal KrakenSocketApiClientOptions(KrakenSocketApiClientOptions baseOn, KrakenSocketApiClientOptions? newValues) : base(baseOn, newValues) -// { -// BaseAddressAuthenticated = newValues?.BaseAddressAuthenticated ?? baseOn.BaseAddressAuthenticated; -// } -// } - -// /// -// /// Options for the Kraken symbol order book -// /// -// public class KrakenOrderBookOptions : OrderBookOptions -// { -// /// -// /// The client to use for the socket connection. When using the same client for multiple order books the connection can be shared. -// /// -// public IKrakenSocketClient? SocketClient { get; set; } - -// /// -// /// The limit of entries in the order book -// /// -// public int? Limit { get; set; } - -// /// -// /// After how much time we should consider the connection dropped if no data is received for this time after the initial subscriptions -// /// -// public TimeSpan? InitialDataTimeout { get; set; } -// } -//} - diff --git a/Kraken.Net/Objects/Models/Futures/KrakenAccountLogResult.cs b/Kraken.Net/Objects/Models/Futures/KrakenAccountLogResult.cs index fe705a8..4161195 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenAccountLogResult.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenAccountLogResult.cs @@ -1,9 +1,4 @@ -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { /// /// Account log @@ -13,11 +8,13 @@ public record KrakenAccountLogResult /// /// Account id /// + [JsonPropertyName("accountUid")] public string AccountUid { get; set; } = string.Empty; /// /// Log entries /// + [JsonPropertyName("logs")] public IEnumerable Logs { get; set; } = Array.Empty(); } @@ -29,102 +26,108 @@ public record KrakenAccountLog /// /// The asset /// - [JsonProperty("asset")] + [JsonPropertyName("asset")] public string? Asset { get; set; } /// /// Booking id /// - [JsonProperty("booking_uid")] + [JsonPropertyName("booking_uid")] public string? BookingUid { get; set; } = string.Empty; /// /// Collateral /// + [JsonPropertyName("collateral")] public string Collateral { get; set; } = string.Empty; /// /// Contract /// + [JsonPropertyName("contract")] public string Contract { get; set; } = string.Empty; /// /// Conversion speed percentage /// - [JsonProperty("conversion_spread_percentage")] + [JsonPropertyName("conversion_spread_percentage")] public decimal? ConversionSpeedPercentage { get; set; } /// /// Event time /// - [JsonProperty("date")] + [JsonPropertyName("date")] [JsonConverter(typeof(DateTimeConverter))] public DateTime Timestamp { get; set; } /// /// Execution /// + [JsonPropertyName("execution")] public string Execution { get; set; } = string.Empty; /// /// Fee paid /// + [JsonPropertyName("fee")] public decimal? Fee { get; set; } /// /// Funding rate /// - [JsonProperty("funding_rate")] + [JsonPropertyName("funding_rate")] public decimal? FundingRate { get; set; } /// /// Log id /// + [JsonPropertyName("id")] public int Id { get; set; } /// /// Info /// + [JsonPropertyName("info")] public string Info { get; set; } = string.Empty; /// /// Liquidation fee /// - [JsonProperty("liquidation_fee")] + [JsonPropertyName("liquidation_fee")] public decimal? LiquidationFee { get; set; } /// /// Margin account /// - [JsonProperty("margin_account")] + [JsonPropertyName("margin_account")] public string? MarginAccount { get; set; } /// /// Mark price /// - [JsonProperty("mark_price")] + [JsonPropertyName("mark_price")] public decimal? MarkPrice { get; set; } /// /// New average entry price /// - [JsonProperty("new_average_entry_price")] + [JsonPropertyName("new_average_entry_price")] public decimal? NewAverageEntryPrice { get; set; } /// /// New balance /// - [JsonProperty("new_balance")] + [JsonPropertyName("new_balance")] public decimal? NewBalance { get; set; } /// /// Previous average entry price /// - [JsonProperty("old_average_entry_price")] + [JsonPropertyName("old_average_entry_price")] public decimal? OldAverageEntryPrice { get; set; } /// /// Previous balance /// - [JsonProperty("old_balance")] + [JsonPropertyName("old_balance")] public decimal? OldBalance { get; set; } /// /// Realized funding /// - [JsonProperty("realized_funding")] + [JsonPropertyName("realized_funding")] public decimal? RealizedFunding { get; set; } /// /// Realized profit and loss /// - [JsonProperty("realized_pnl")] + [JsonPropertyName("realized_pnl")] public decimal? RealizedProfitAndLoss { get; set; } /// /// Trade price /// - [JsonProperty("trade_price")] + [JsonPropertyName("trade_price")] public decimal? TradePrice { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenBalances.cs b/Kraken.Net/Objects/Models/Futures/KrakenBalances.cs index 67de122..35d65b6 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenBalances.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenBalances.cs @@ -1,24 +1,42 @@ using Kraken.Net.Converters; -using Newtonsoft.Json; -using System.Collections.Generic; namespace Kraken.Net.Objects.Models.Futures { - internal record KrakenBalancesResult : KrakenFuturesResult> + internal record KrakenBalancesResult : KrakenFuturesResult { - [JsonProperty("accounts")] - public override Dictionary Data { get; set; } = new Dictionary(); + [JsonPropertyName("accounts")] + [JsonConverter(typeof(KrakenFuturesBalancesConverter))] + public override KrakenFuturesBalances Data { get; set; } = null!; } /// /// Kraken balances info /// - [JsonConverter(typeof(KrakenFuturesBalancesConverter))] public record KrakenBalances { /// /// Type of the balance info /// + [JsonPropertyName("type")] public string Type { get; set; } = string.Empty; } + + /// + /// Balance info + /// + public record KrakenFuturesBalances + { + /// + /// Cash account + /// + public KrakenCashBalances CashAccount { get; set; } + /// + /// Multi collateral margin account + /// + public KrakenMultiCollateralMarginBalances MultiCollateralMarginAccount { get; set; } + /// + /// Margin accounts + /// + public IEnumerable MarginAccounts { get; set; } + } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenCashBalances.cs b/Kraken.Net/Objects/Models/Futures/KrakenCashBalances.cs index 2f1a256..d5f8a4d 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenCashBalances.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenCashBalances.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { /// /// Cash balances @@ -10,6 +8,7 @@ public record KrakenCashBalances : KrakenBalances /// /// Balances /// + [JsonPropertyName("balances")] public Dictionary Balances { get; set; } = new Dictionary(); } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFeeSchedule.cs b/Kraken.Net/Objects/Models/Futures/KrakenFeeSchedule.cs index 444412b..b932218 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFeeSchedule.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFeeSchedule.cs @@ -1,11 +1,14 @@ -using Newtonsoft.Json; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { + internal record KrakenFeeScheduleVolumeResult : KrakenFuturesResult> + { + [JsonPropertyName("feeSchedules")] + public override Dictionary Data { get; set; } = new Dictionary(); + } + internal record KrakenFeeSchedulesResult : KrakenFuturesResult> { - [JsonProperty("feeSchedules")] + [JsonPropertyName("feeSchedules")] public override IEnumerable Data { get; set; } = new List(); } @@ -17,14 +20,17 @@ public record KrakenFeeSchedule /// /// Name /// + [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; /// /// Id /// + [JsonPropertyName("uid")] public string Uid { get; set; } = string.Empty; /// /// Fee tiers /// + [JsonPropertyName("tiers")] public IEnumerable Tiers { get; set; } = new List(); } @@ -36,14 +42,17 @@ public record KrakenFee /// /// Fee for maker orders /// + [JsonPropertyName("makerFee")] public decimal MakerFee { get; set; } /// /// Fee for taker orders /// + [JsonPropertyName("takerFee")] public decimal TakerFee { get; set; } /// /// Usd trade volume threshold /// + [JsonPropertyName("usdVolume")] public decimal UsdVolume { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFeeScheduleVolume.cs b/Kraken.Net/Objects/Models/Futures/KrakenFeeScheduleVolume.cs deleted file mode 100644 index 95c334a..0000000 --- a/Kraken.Net/Objects/Models/Futures/KrakenFeeScheduleVolume.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Newtonsoft.Json; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Futures -{ - internal record KrakenFeeScheduleVolumeResult : KrakenFuturesResult> - { - [JsonProperty("volumesByFeeSchedule")] - public override Dictionary Data { get; set; } = new Dictionary(); - } -} diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFundingRate.cs b/Kraken.Net/Objects/Models/Futures/KrakenFundingRate.cs index a4b30b0..2dcc250 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFundingRate.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFundingRate.cs @@ -1,13 +1,8 @@ -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFundingRatesResult : KrakenFuturesResult> { - [JsonProperty("rates")] + [JsonPropertyName("rates")] public override IEnumerable Data { get; set; } = Array.Empty(); } @@ -19,15 +14,18 @@ public record KrakenFundingRate /// /// Funding rate /// + [JsonPropertyName("fundingRate")] public decimal FundingRate { get; set; } /// /// Relative funding rate /// + [JsonPropertyName("relativeFundingRate")] public decimal RelativeFundingRate { get; set; } /// /// Timestamp /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("timestamp")] public DateTime Timestamp { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesCachedOrder.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesCachedOrder.cs index 9a7092c..01838f2 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesCachedOrder.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesCachedOrder.cs @@ -1,12 +1,8 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; +using Kraken.Net.Enums; using Kraken.Net.Interfaces; -using Newtonsoft.Json; -using System; namespace Kraken.Net.Objects.Models.Futures { - /// /// Order info /// @@ -15,32 +11,33 @@ public record KrakenFuturesCachedOrder : IKrakenFuturesOrder /// /// Client order id /// - [JsonProperty("cliOrdId")] + [JsonPropertyName("cliOrdId")] public string? ClientOrderId { get; set; } /// /// Quantity filled /// - [JsonProperty("filled")] + [JsonPropertyName("filled")] public decimal QuantityFilled { get; set; } /// /// Last update time /// [JsonConverter(typeof(DateTimeConverter))] - [JsonProperty("lastUpdateTimestamp")] + [JsonPropertyName("lastUpdateTimestamp")] public DateTime? LastUpdateTime { get; set; } /// /// Price /// - [JsonProperty("limitPrice")] + [JsonPropertyName("limitPrice")] public decimal? Price { get; set; } /// /// Order id /// - [JsonProperty("orderId")] + [JsonPropertyName("orderId")] public string OrderId { get; set; } = string.Empty; /// /// Quantity /// + [JsonPropertyName("quantity")] public decimal Quantity { get; set; } /// /// Quantity remaining @@ -53,36 +50,42 @@ public decimal QuantityRemaining /// /// Reduce only /// + [JsonPropertyName("reduceOnly")] public bool ReduceOnly { get; set; } /// /// Order side /// [JsonConverter(typeof(EnumConverter))] - [JsonProperty("side")] + [JsonPropertyName("side")] public OrderSide Side { get; set; } /// /// Symbol /// + [JsonPropertyName("symbol")] public string Symbol { get; set; } = string.Empty; /// /// Timestamp /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("timestamp")] public DateTime Timestamp { get; set; } /// /// Trigger order type /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("type")] public TriggerOrderType Type { get; set; } /// /// Trigger options /// + [JsonPropertyName("priceTriggerOptions")] public KrakenTriggerOptions? PriceTriggerOptions { get; set; } /// /// Trigger timestamp /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("triggerTime")] public DateTime? TriggerTime { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesCancelAfter.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesCancelAfter.cs index c9dc02e..eda02ba 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesCancelAfter.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesCancelAfter.cs @@ -1,12 +1,8 @@ -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; -using System; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesCancelAfterResult : KrakenFuturesResult { - [JsonProperty("status")] + [JsonPropertyName("status")] public override KrakenFuturesCancelAfter Data { get; set; } = null!; } @@ -19,11 +15,13 @@ public record KrakenFuturesCancelAfter /// Current timestamp /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("currentTime")] public DateTime CurrentTime { get; set; } /// /// Trigger time /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("triggerTime")] public DateTime? TriggerTime { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesCancelledOrders.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesCancelledOrders.cs index 6837f21..d5dda1b 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesCancelledOrders.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesCancelledOrders.cs @@ -1,13 +1,8 @@ -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesCancelledOrdersResult : KrakenFuturesResult { - [JsonProperty("cancelStatus")] + [JsonPropertyName("cancelStatus")] public override KrakenFuturesCancelledOrders Data { get; set; } = null!; } @@ -19,23 +14,28 @@ public record KrakenFuturesCancelledOrders /// /// Cancelled all or a specific symbol /// + [JsonPropertyName("cancelOnly")] public string CancelOnly { get; set; } = string.Empty; /// /// Cancelled order ids /// + [JsonPropertyName("cancelledOrders")] public IEnumerable CancelledOrders { get; set; } = Array.Empty(); /// /// Order events /// + [JsonPropertyName("orderEvents")] public IEnumerable OrderEvents { get; set; } = Array.Empty(); /// /// Received timestamp /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("receivedTime")] public DateTime ReceivedTime { get; set; } /// /// Status /// + [JsonPropertyName("status")] public string Status { get; set; } = string.Empty; } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesKlines.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesKlines.cs index 2000d68..62dc936 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesKlines.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesKlines.cs @@ -1,9 +1,4 @@ -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { /// /// Kline info @@ -13,12 +8,12 @@ public record KrakenFuturesKlines /// /// True if there are more candles in the time range /// - [JsonProperty("more_candles")] + [JsonPropertyName("more_candles")] public bool MoreKlines { get; set; } /// /// Candles /// - [JsonProperty("candles")] + [JsonPropertyName("candles")] public IEnumerable Klines { get; set; } = Array.Empty(); } @@ -30,31 +25,32 @@ public record KrakenFuturesKline /// /// High price /// - [JsonProperty("high")] + [JsonPropertyName("high")] public decimal HighPrice { get; set; } /// /// Low price /// - [JsonProperty("low")] + [JsonPropertyName("low")] public decimal LowPrice { get; set; } /// /// Close price /// - [JsonProperty("close")] + [JsonPropertyName("close")] public decimal ClosePrice { get; set; } /// /// Open price /// - [JsonProperty("open")] + [JsonPropertyName("open")] public decimal OpenPrice { get; set; } /// /// Volume /// + [JsonPropertyName("volume")] public decimal Volume { get; set; } /// /// Timestamp /// - [JsonProperty("time")] + [JsonPropertyName("time")] [JsonConverter(typeof(DateTimeConverter))] public DateTime Timestamp { get; set; } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesLeverage.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesLeverage.cs index 3af5627..3106403 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesLeverage.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesLeverage.cs @@ -1,12 +1,8 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesLeverageResult : KrakenFuturesResult> { - [JsonProperty("leveragePreferences")] + [JsonPropertyName("leveragePreferences")] public override IEnumerable Data { get; set; } = Array.Empty(); } @@ -18,10 +14,12 @@ public record KrakenFuturesLeverage /// /// Symbol /// + [JsonPropertyName("symbol")] public string Symbol { get; set; } = string.Empty; /// /// Max leverage /// + [JsonPropertyName("maxLeverage")] public decimal MaxLeverage { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesMarginRequirements.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesMarginRequirements.cs index 45314d7..cdd6776 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesMarginRequirements.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesMarginRequirements.cs @@ -1,12 +1,10 @@ -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesMarginRequirementsInternal : KrakenFuturesResult { - [JsonProperty("initialMargin")] + [JsonPropertyName("initialMargin")] public decimal? InitialMargin { get; set; } - [JsonProperty("price")] + [JsonPropertyName("price")] public decimal? Price { get; set; } } @@ -18,12 +16,12 @@ public record KrakenFuturesMarginRequirements /// /// Initial margin /// - [JsonProperty("initialMargin")] + [JsonPropertyName("initialMargin")] public decimal? InitialMargin { get; set; } /// /// Price /// - [JsonProperty("price")] + [JsonPropertyName("price")] public decimal? Price { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesMaxOrderSize.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesMaxOrderSize.cs index 53ad0aa..918e9c9 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesMaxOrderSize.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesMaxOrderSize.cs @@ -1,19 +1,14 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesMaxOrderSizeInternal : KrakenFuturesResult { - [JsonProperty("buyPrice")] + [JsonPropertyName("buyPrice")] public decimal? BuyPrice { get; set; } - [JsonProperty("maxBuySize")] + [JsonPropertyName("maxBuySize")] public decimal? MaxBuyQuantity { get; set; } - [JsonProperty("maxSellSize")] + [JsonPropertyName("maxSellSize")] public decimal? MaxSellQuantity { get; set; } - [JsonProperty("sellPrice")] + [JsonPropertyName("sellPrice")] public decimal? SellPrice { get; set; } } @@ -25,22 +20,22 @@ public record KrakenFuturesMaxOrderSize /// /// Buy price /// - [JsonProperty("buyPrice")] + [JsonPropertyName("buyPrice")] public decimal? BuyPrice { get; set; } /// /// Max buy quantity /// - [JsonProperty("maxBuySize")] + [JsonPropertyName("maxBuySize")] public decimal? MaxBuyQuantity { get; set; } /// /// Max sell quantity /// - [JsonProperty("maxSellSize")] + [JsonPropertyName("maxSellSize")] public decimal? MaxSellQuantity { get; set; } /// /// Sell price /// - [JsonProperty("sellPrice")] + [JsonPropertyName("sellPrice")] public decimal? SellPrice { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesOpenOrder.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesOpenOrder.cs index d799f33..fe382d0 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesOpenOrder.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesOpenOrder.cs @@ -1,15 +1,11 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; +using Kraken.Net.Enums; using Kraken.Net.Interfaces; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesOpenOrderResult : KrakenFuturesResult> { - [JsonProperty("openOrders")] + [JsonPropertyName("openOrders")] public override IEnumerable Data { get; set; } = Array.Empty(); } @@ -21,28 +17,28 @@ public record KrakenFuturesOpenOrder : IKrakenFuturesOrder /// /// Client order id /// - [JsonProperty("cliOrdId")] + [JsonPropertyName("cliOrdId")] public string? ClientOrderId { get; set; } /// /// Quantity filled /// - [JsonProperty("filledSize")] + [JsonPropertyName("filledSize")] public decimal QuantityFilled { get; set; } /// /// Last update time /// [JsonConverter(typeof(DateTimeConverter))] - [JsonProperty("lastUpdateTime")] + [JsonPropertyName("lastUpdateTime")] public DateTime? LastUpdateTime { get; set; } /// /// Price /// - [JsonProperty("limitPrice")] + [JsonPropertyName("limitPrice")] public decimal? Price { get; set; } /// /// Order id /// - [JsonProperty("order_id")] + [JsonPropertyName("order_id")] public string OrderId { get; set; } = string.Empty; /// /// Quantity @@ -55,46 +51,52 @@ public decimal Quantity /// /// Quantity /// - [JsonProperty("unfilledSize")] + [JsonPropertyName("unfilledSize")] public decimal QuantityRemaining { get; set; } /// /// Reduce only /// + [JsonPropertyName("reduceOnly")] public bool ReduceOnly { get; set; } /// /// Order side /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("side")] public OrderSide Side { get; set; } /// /// Symbol /// + [JsonPropertyName("symbol")] public string Symbol { get; set; } = string.Empty; /// /// Timestamp /// [JsonConverter(typeof(DateTimeConverter))] - [JsonProperty("receivedTime")] + [JsonPropertyName("receivedTime")] public DateTime Timestamp { get; set; } /// /// Order type /// - [JsonProperty("orderType")] + [JsonPropertyName("orderType")] [JsonConverter(typeof(EnumConverter))] public FuturesOrderType Type { get; set; } /// /// Order status /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("status")] public OpenOrderStatus Status { get; set; } /// /// Trigger signal /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("triggerSignal")] public TriggerSignal? TriggerSignal { get; set; } /// /// Stop price /// + [JsonPropertyName("stopPrice")] public decimal? StopPrice { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrder.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrder.cs index fbae4b9..1e19b82 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrder.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrder.cs @@ -1,8 +1,5 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; +using Kraken.Net.Enums; using Kraken.Net.Interfaces; -using Newtonsoft.Json; -using System; namespace Kraken.Net.Objects.Models.Futures { @@ -14,37 +11,38 @@ public record KrakenFuturesOrder : IKrakenFuturesOrder /// /// Client order id /// - [JsonProperty("cliOrdId")] + [JsonPropertyName("cliOrdId")] public string? ClientOrderId { get; set; } /// /// Quantity filled /// - [JsonProperty("filled")] + [JsonPropertyName("filled")] public decimal QuantityFilled { get; set; } /// /// Last update time /// [JsonConverter(typeof(DateTimeConverter))] - [JsonProperty("lastUpdateTimestamp")] + [JsonPropertyName("lastUpdateTimestamp")] public DateTime? LastUpdateTime { get; set; } /// /// Price /// - [JsonProperty("limitPrice")] + [JsonPropertyName("limitPrice")] public decimal? Price { get; set; } /// /// Stop price /// - [JsonProperty("stopPrice")] + [JsonPropertyName("stopPrice")] public decimal? StopPrice { get; set; } /// /// Order id /// - [JsonProperty("orderId")] + [JsonPropertyName("orderId")] public string OrderId { get; set; } = string.Empty; /// /// Quantity /// + [JsonPropertyName("quantity")] public decimal Quantity { get; set; } /// /// Quantity remaining @@ -57,26 +55,30 @@ public decimal QuantityRemaining /// /// Reduce only /// + [JsonPropertyName("reduceOnly")] public bool ReduceOnly { get; set; } /// /// Order side /// [JsonConverter(typeof(EnumConverter))] - [JsonProperty("side")] + [JsonPropertyName("side")] public OrderSide Side { get; set; } /// /// Symbol /// + [JsonPropertyName("symbol")] public string Symbol { get; set; } = string.Empty; /// /// Timestamp /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("timestamp")] public DateTime Timestamp { get; set; } /// /// Order type /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("type")] public FuturesOrderType Type { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrderBook.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrderBook.cs index 1b18978..a8fb66b 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrderBook.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrderBook.cs @@ -1,13 +1,10 @@ using CryptoExchange.Net.Converters; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesOrderBookResult : KrakenFuturesResult { - [JsonProperty("orderBook")] + [JsonPropertyName("orderBook")] public override KrakenFuturesOrderBook Data { get; set; } = null!; } @@ -19,10 +16,12 @@ public record KrakenFuturesOrderBook /// /// List of asks /// + [JsonPropertyName("asks")] public IEnumerable Asks { get; set; } = Array.Empty(); /// /// List of bids /// + [JsonPropertyName("bids")] public IEnumerable Bids { get; set; } = Array.Empty(); } @@ -30,7 +29,7 @@ public record KrakenFuturesOrderBook /// Order book entry /// [JsonConverter(typeof(ArrayConverter))] - public record KrakenFuturesOrderBookEntry + public record KrakenFuturesOrderBookEntry : ISymbolOrderBookEntry { /// /// Price diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrderId.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrderId.cs index c9bb988..51845ff 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrderId.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrderId.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { /// /// Order id info @@ -10,12 +8,12 @@ public record KrakenFuturesOrderId /// /// Client order id /// - [JsonProperty("cliOrdId")] + [JsonPropertyName("cliOrdId")] public string? ClientOrderId { get; set; } /// /// Order id /// - [JsonProperty("order_id")] + [JsonPropertyName("order_id")] public string OrderId { get; set; } = string.Empty; } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrderResult.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrderResult.cs index ed31a2b..8b3308c 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrderResult.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrderResult.cs @@ -1,26 +1,22 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesOrderCancelResult : KrakenFuturesResult { - [JsonProperty("cancelStatus")] + [JsonPropertyName("cancelStatus")] public override KrakenFuturesOrderResult Data { get; set; } = null!; } internal record KrakenFuturesOrderPlaceResult : KrakenFuturesResult { - [JsonProperty("sendStatus")] + [JsonPropertyName("sendStatus")] public override KrakenFuturesOrderResult Data { get; set; } = null!; } internal record KrakenFuturesOrderEditResult : KrakenFuturesResult { - [JsonProperty("editStatus")] + [JsonPropertyName("editStatus")] public override KrakenFuturesOrderResult Data { get; set; } = null!; } @@ -32,10 +28,10 @@ public record KrakenFuturesOrderResult /// /// Order id /// - [JsonProperty("order_id")] + [JsonPropertyName("order_id")] public string OrderId { get; set; } = string.Empty; - [JsonProperty("orderId")] + [JsonPropertyName("orderId")] internal string OrderIdInternal { get => OrderId; @@ -46,15 +42,18 @@ internal string OrderIdInternal /// Receive timestamp /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("receivedTime")] public DateTime ReceivedTime { get; set; } /// /// Order status /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("status")] public KrakenFuturesOrderActionStatus Status { get; set; } /// /// Order events /// + [JsonPropertyName("orderEvents")] public IEnumerable OrderEvents { get; set; } = Array.Empty(); } @@ -66,51 +65,58 @@ public record KrakenFuturesOrderEvent /// /// Event type /// + [JsonPropertyName("type")] public string Type { get; set; } = string.Empty; /// /// Reduced quantity /// + [JsonPropertyName("reducedQuantity")] public decimal? ReducedQuantity { get; set; } /// /// Uid /// + [JsonPropertyName("uid")] public string? Uid { get; set; } /// /// Execution id /// + [JsonPropertyName("executionId")] public string? ExecutionId { get; set; } /// /// Quantity /// - [JsonProperty("amount")] + [JsonPropertyName("amount")] public decimal? Quantity { get; set; } /// /// Quantity /// - [JsonProperty("price")] + [JsonPropertyName("price")] public decimal? Price { get; set; } /// /// Order info /// + [JsonPropertyName("order")] public KrakenFuturesOrder? Order { get; set; } /// /// New order info for edit event /// + [JsonPropertyName("new")] public KrakenFuturesOrder? New { get; set; } /// /// Old order info for edit event /// + [JsonPropertyName("old")] public KrakenFuturesOrder? Old { get; set; } /// /// Order before execution /// - [JsonProperty("orderPriorExecution")] + [JsonPropertyName("orderPriorExecution")] public KrakenFuturesOrder? OrderBeforeExecution { get; set; } /// /// Order before edit /// - [JsonProperty("orderPriorEdit")] + [JsonPropertyName("orderPriorEdit")] public KrakenFuturesOrder? OrderBeforeEdit { get; set; } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrderStatus.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrderStatus.cs index 535f0cc..dd22eba 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrderStatus.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesOrderStatus.cs @@ -1,14 +1,10 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesOrderStatusResult : KrakenFuturesResult> { - [JsonProperty("orders")] + [JsonPropertyName("orders")] public override IEnumerable Data { get; set; } = Array.Empty(); } @@ -20,19 +16,23 @@ public record KrakenFuturesOrderStatus /// /// Order error /// + [JsonPropertyName("error")] public string? Error { get; set; } /// /// Order details /// + [JsonPropertyName("order")] public KrakenFuturesCachedOrder Order { get; set; } = null!; /// /// Order status /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("status")] public KrakenFuturesOrderActiveStatus Status { get; set; } /// /// Update reason /// + [JsonPropertyName("updateReason")] public string? UpdateReason { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesPlatfromNotification.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesPlatfromNotification.cs index 81f0179..a777429 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesPlatfromNotification.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesPlatfromNotification.cs @@ -1,12 +1,8 @@ -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesPlatfromNotificationInternalResult : KrakenFuturesResult { + [JsonPropertyName("notifications")] public IEnumerable Notifications { get; set; } = Array.Empty(); } @@ -18,10 +14,12 @@ public record KrakenFuturesPlatfromNotificationResult /// /// Notifications /// + [JsonPropertyName("notifications")] public IEnumerable Notifications { get; set; } = Array.Empty(); /// /// Server time /// + [JsonPropertyName("serverTime")] public DateTime ServerTime { get; set; } } @@ -34,18 +32,22 @@ public record KrakenFuturesPlatfromNotification /// The time that notification is taking effect. /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("effectiveTime")] public DateTime? EffectiveTime { get; set; } /// /// The notification note. A short description about the specific notification. /// + [JsonPropertyName("note")] public string? Note { get; set; } /// /// The notification priorities: low / medium / high. If priority == "high" then it implies downtime will occur at EffectiveTime when Type == "maintenance". /// + [JsonPropertyName("priority")] public string Priority { get; set; } = string.Empty; /// /// If type == "maintenance" then it implies downtime will occur at EffectiveTime if Priority == "high". /// + [JsonPropertyName("type")] public string Type { get; set; } = string.Empty; } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesPnlCurrency.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesPnlCurrency.cs index 996dce1..20156b9 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesPnlCurrency.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesPnlCurrency.cs @@ -1,12 +1,8 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesPnlCurrencyResult : KrakenFuturesResult> { - [JsonProperty("preferences")] + [JsonPropertyName("preferences")] public override IEnumerable Data { get; set; } = Array.Empty(); } @@ -18,10 +14,12 @@ public record KrakenFuturesPnlCurrency /// /// Profit and loss currency /// + [JsonPropertyName("pnlCurrency")] public string PnlCurrency { get; set; } = string.Empty; /// /// Symbol /// + [JsonPropertyName("symbol")] public string Symbol { get; set; } = string.Empty; } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesPosition.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesPosition.cs index 769b287..742047d 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesPosition.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesPosition.cs @@ -1,14 +1,10 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesPositionResult : KrakenFuturesResult> { - [JsonProperty("openPositions")] + [JsonPropertyName("openPositions")] public override IEnumerable Data { get; set; } = Array.Empty(); } @@ -21,41 +17,43 @@ public record KrakenFuturesPosition /// Position enter time /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("fillTime")] public DateTime FillTime { get; set; } /// /// Max leverage selected for isolated position /// + [JsonPropertyName("maxFixedLeverage")] public decimal? MaxFixedLeverage { get; set; } /// /// Selected pnl currency for the position (default: USD) /// + [JsonPropertyName("pnlCurrency")] public string? ProfitAndLossCurrency { get; set; } /// /// The average price at which the position was entered into. /// + [JsonPropertyName("price")] public decimal Price { get; set; } /// /// The direction of the position. /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("side")] public PositionSide Side { get; set; } /// /// The size of the position. /// - [JsonProperty("size")] + [JsonPropertyName("size")] public decimal Quantity { get; set; } /// /// The symbol /// + [JsonPropertyName("symbol")] public string Symbol { get; set; } = string.Empty; /// - /// The profit and loss currency - /// - [JsonProperty("pnlCurrency")] - public string? PnlCurrency { get; set; } - /// /// Unrealised funding on the position. /// + [JsonPropertyName("unrealizedFunding")] public decimal? UnrealizedFunding { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesResult.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesResult.cs index b3f0330..0baa870 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesResult.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesResult.cs @@ -1,28 +1,30 @@ -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesResult { + [JsonPropertyName("errors")] public IEnumerable? Errors { get; set; } + [JsonPropertyName("error")] public string? Error { get; set; } public bool Success => string.Equals(Result, "success", StringComparison.Ordinal); + [JsonPropertyName("result")] public string? Result { get; set; } [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("serverTime")] public DateTime ServerTime { get; set; } } internal abstract record KrakenFuturesResult : KrakenFuturesResult { + [JsonPropertyName("data")] public abstract T Data { get; set; } } internal record KrakenFuturesError { + [JsonPropertyName("code")] public int Code { get; set; } + [JsonPropertyName("message")] public string Message { get; set; } = string.Empty; } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesSelfTradeResult.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesSelfTradeResult.cs index cabbf56..8f3c32b 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesSelfTradeResult.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesSelfTradeResult.cs @@ -1,12 +1,10 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesSelfTradeResult : KrakenFuturesResult { - [JsonProperty("strategy")] + [JsonPropertyName("strategy")] [JsonConverter(typeof(EnumConverter))] public override SelfTradeStrategy Data { get; set; } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesSymbol.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesSymbol.cs index d58c9b0..59f238b 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesSymbol.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesSymbol.cs @@ -1,14 +1,10 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesSymbolResult : KrakenFuturesResult> { - [JsonProperty("instruments")] + [JsonPropertyName("instruments")] public override IEnumerable Data { get; set; } = new List(); } @@ -20,85 +16,105 @@ public record KrakenFuturesSymbol /// /// Category /// + [JsonPropertyName("category")] public string? Category { get; set; } /// /// The contract size of the Futures /// + [JsonPropertyName("contractSize")] public decimal? ContractSize { get; set; } /// /// Trade precision for the contract (e.g. trade precision of 2 means trades are precise to the hundredth decimal place 0.01). /// + [JsonPropertyName("contractValueTradePrecision")] public decimal? ContractValueTradePrecision { get; set; } /// /// Unique identifier of fee schedule associated with the symbol /// + [JsonPropertyName("feeScheduleUid")] public string? FeeScheduleUid { get; set; } /// /// Funding rate coefficient /// + [JsonPropertyName("fundingRateCoefficient")] public decimal? FundingRateCoefficient { get; set; } /// /// Amount of contract used to calculated the mid price for the mark price /// + [JsonPropertyName("impactMidSize")] public decimal? ImpactMidSize { get; set; } /// /// Single-collateral contracts only: Contract's ISIN code /// + [JsonPropertyName("isin")] public string? Isin { get; set; } /// /// Last trade time /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("lastTradingTime")] public DateTime? LastTradingTime { get; set; } /// /// A list containing the margin schedules /// + [JsonPropertyName("marginLevels")] public IEnumerable? MarginLevels { get; set; } /// /// Maximum number of contracts that one can hold in a position /// + [JsonPropertyName("maxPositionSize")] public decimal? MaxPositionSize { get; set; } /// /// Perpetuals only: the absolute value of the maximum permissible funding rate /// + [JsonPropertyName("maxRelativeFundingRate")] public decimal? MaxRelativeFundingRate { get; set; } /// /// When the contract was first available for trading /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("openingDate")] public DateTime? OpeningDate { get; set; } /// /// True if the symbol is in post-only mode, false otherwise. /// + [JsonPropertyName("postOnly")] public bool? PostOnly { get; set; } /// /// Margin levels for retail clients (investor category no longer eligible for trading). /// + [JsonPropertyName("retailMarginLevels")] public IEnumerable? RetailMarginLevels { get; set; } /// /// Symbols /// + [JsonPropertyName("symbol")] public string Symbol { get; set; } = string.Empty; /// /// Tag for the contract (currently does not return a value). /// + [JsonPropertyName("tags")] public IEnumerable Tags { get; set; } = Array.Empty(); /// /// Tick size of the contract being traded. /// + [JsonPropertyName("tickSize")] public decimal? TickSize { get; set; } /// /// True if the symbol can be traded, False otherwise. /// + [JsonPropertyName("tradeable")] public bool Tradeable { get; set; } /// /// Type of the symbol /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("type")] public SymbolType Type { get; set; } /// /// The underlying of the Futures /// + [JsonPropertyName("underlying")] public string? Underlying { get; set; } } @@ -110,18 +126,22 @@ public record KrakenFutureMarginLevel /// /// The lower limit of the number of contracts to which this margin level applies /// + [JsonPropertyName("contracts")] public int? Contracts { get; set; } /// /// The initial margin requirement for this level /// + [JsonPropertyName("initialMargin")] public decimal InitialMargin { get; set; } /// /// The maintenance margin requirement for this level /// + [JsonPropertyName("maintenanceMargin")] public decimal MaintenanceMargin { get; set; } /// /// The lower limit of the number of non-contract units (i.e. quote currency units for linear futures) to which this margin level applies /// + [JsonPropertyName("numNonContractUnits")] public decimal NumNonContractUnits { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesSymbolStatus.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesSymbolStatus.cs index e0fc308..9995ac3 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesSymbolStatus.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesSymbolStatus.cs @@ -1,11 +1,8 @@ -using Newtonsoft.Json; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesSymbolStatusResult : KrakenFuturesResult> { - [JsonProperty("instrumentStatus")] + [JsonPropertyName("instrumentStatus")] public override IEnumerable Data { get; set; } = new List(); } @@ -17,22 +14,27 @@ public record KrakenFuturesSymbolStatus /// /// Extreme volatility initial margin multiplier /// + [JsonPropertyName("extremeVolatilityInitialMarginMultiplier")] public int ExtremeVolatilityInitialMarginMultiplier { get; set; } /// /// Is experiencing dislocation /// + [JsonPropertyName("isExperiencingDislocation")] public bool IsExperiencingDislocation { get; set; } /// /// Is experiencing exterme volatility /// + [JsonPropertyName("isExperiencingExtremeVolatility")] public bool IsExperiencingExtremeVolatility { get; set; } /// /// Price dislocation direction /// + [JsonPropertyName("priceDislocationDirection")] public string? PriceDislocationDirection { get; set; } /// /// Tradeable /// + [JsonPropertyName("tradeable")] public string Tradeable { get; set; } = string.Empty; } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesTicker.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesTicker.cs index f3f0e0d..7007356 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesTicker.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesTicker.cs @@ -1,16 +1,17 @@ -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { - internal record KrakenFuturesTickerResult : KrakenFuturesResult> + internal record KrakenFuturesTickersResult : KrakenFuturesResult> { - [JsonProperty("tickers")] + [JsonPropertyName("tickers")] public override IEnumerable Data { get; set; } = Array.Empty(); } + internal record KrakenFuturesTickerResult : KrakenFuturesResult + { + [JsonPropertyName("ticker")] + public override KrakenFuturesTicker Data { get; set; } = null!; + } + /// /// Ticker info /// @@ -19,93 +20,103 @@ public record KrakenFuturesTicker /// /// The price of the current best ask /// - [JsonProperty("ask")] + [JsonPropertyName("ask")] public decimal? BestAskPrice { get; set; } /// /// The size of the current best ask /// - [JsonProperty("askSize")] + [JsonPropertyName("askSize")] public decimal? BestAskQuantity { get; set; } /// /// The price of the current best bid /// - [JsonProperty("bid")] + [JsonPropertyName("bid")] public decimal? BestBidPrice { get; set; } /// /// The size of the current best bid /// - [JsonProperty("bidSize")] + [JsonPropertyName("bidSize")] public decimal? BestBidQuantity { get; set; } /// /// The current absolute funding rate. /// + [JsonPropertyName("fundingRate")] public decimal? FundingRate { get; set; } /// /// The estimated next absolute funding rate. /// + [JsonPropertyName("fundingRatePrediction")] public decimal? FundingRatePrediction { get; set; } /// /// Index price /// + [JsonPropertyName("indexPrice")] public decimal? IndexPrice { get; set; } /// /// For futures: The price of the last fill. For indices: The last calculated value /// - [JsonProperty("last")] + [JsonPropertyName("last")] public decimal? LastPrice { get; set; } /// /// The size of the last fill /// - [JsonProperty("lastSize")] + [JsonPropertyName("lastSize")] public decimal? LastQuantity { get; set; } /// /// The date and time at which last price was observed. /// [JsonConverter(typeof(DateTimeConverter))] - [JsonProperty("lastTime")] + [JsonPropertyName("lastTime")] public DateTime? LastTradeTime { get; set; } /// /// The price to which Kraken Futures currently marks the Futures for margining purposes /// + [JsonPropertyName("markPrice")] public decimal MarkPrice { get; set; } /// /// The price of the fill observed 24 hours ago /// - [JsonProperty("open24h")] + [JsonPropertyName("open24h")] public decimal? OpenPrice24h { get; set; } /// /// The current open interest of the symbol /// + [JsonPropertyName("openInterest")] public decimal OpenInterest { get; set; } /// /// The currency pair of the symbol /// + [JsonPropertyName("pair")] public string Pair { get; set; } = string.Empty; /// /// Post only /// + [JsonPropertyName("postOnly")] public bool PostOnly { get; set; } /// /// True if the market is suspended, False otherwise. /// + [JsonPropertyName("suspended")] public bool Suspended { get; set; } /// /// The symbol of the Futures. /// + [JsonPropertyName("symbol")] public string Symbol { get; set; } = string.Empty; /// /// Currently can be 'perpetual', 'month' or 'quarter'. Other tags may be added without notice /// + [JsonPropertyName("tag")] public string? Tag { get; set; } /// /// The sum of the sizes of all fills observed in the last 24 hours /// - [JsonProperty("vol24h")] + [JsonPropertyName("vol24h")] public decimal Volume24h { get; set; } /// /// The sum of the size * price of all fills observed in the last 24 hours /// - [JsonProperty("volumeQuote")] + [JsonPropertyName("volumeQuote")] public decimal Volume24hQuote { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesTrade.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesTrade.cs index a9f948c..2477166 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesTrade.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesTrade.cs @@ -1,14 +1,10 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesTradeResult : KrakenFuturesResult> { - [JsonProperty("history")] + [JsonPropertyName("history")] public override IEnumerable Data { get; set; } = new List(); } @@ -20,107 +16,112 @@ public record KrakenFuturesTrade /// /// Execution venue /// - [JsonProperty("execution_venue")] + [JsonPropertyName("execution_venue")] public string? ExecutionVenue { get; set; } /// /// Symbol identification type /// - [JsonProperty("instrument_identification_type")] + [JsonPropertyName("instrument_identification_type")] public string? SymbolIdentificationType { get; set; } /// /// Isin /// + [JsonPropertyName("isin")] public string? IsIn { get; set; } /// /// Notional amount /// - [JsonProperty("notional_amount")] + [JsonPropertyName("notional_amount")] public decimal? NotionalAmount { get; set; } /// /// Notional currency /// - [JsonProperty("notional_currency")] + [JsonPropertyName("notional_currency")] public string? NotionalCurrency { get; set; } /// /// For futures: The price of a fill. For indices: The calculated value /// + [JsonPropertyName("price")] public decimal Price { get; set; } /// /// Price currency /// - [JsonProperty("price_currency")] + [JsonPropertyName("price_currency")] public string? PriceCurrency { get; set; } /// /// Price notification /// - [JsonProperty("price_notation")] + [JsonPropertyName("price_notation")] public string? PriceNotation { get; set; } /// /// Publication time /// - [JsonProperty("publication_time")] + [JsonPropertyName("publication_time")] [JsonConverter(typeof(DateTimeConverter))] public DateTime? PublicationTime { get; set; } /// /// Publication venue /// - [JsonProperty("publication_venue")] + [JsonPropertyName("publication_venue")] public string? PublicationVenue { get; set; } /// /// The classification of the taker side in the matched trade: "buy" if the taker is a buyer, "sell" if the taker is a seller. /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("side")] public OrderSide Side { get; set; } /// /// The size of a fill /// - [JsonProperty("size")] + [JsonPropertyName("size")] public decimal? Quantity { get; set; } /// /// The date and time of a trade or an index computation. For futures: The date and time of a trade.Data is not aggregated For indices: The date and time of an index computation.For real-time indices, data is aggregated to the last computation of each full hour.For reference rates, data is not aggregated /// - [JsonProperty("time")] + [JsonPropertyName("time")] [JsonConverter(typeof(DateTimeConverter))] public DateTime? Timestamp { get; set; } /// /// To be cleared /// - [JsonProperty("to_be_cleared")] + [JsonPropertyName("to_be_cleared")] public bool? ToBeCleared { get; set; } /// /// A continuous index starting at 1 for the first fill in a Futures contract maturity /// - [JsonProperty("trade_id")] + [JsonPropertyName("trade_id")] public int? TradeId { get; set; } /// /// Transaction id code /// - [JsonProperty("transaction_identification_code")] + [JsonPropertyName("transaction_identification_code")] public string? TransactionIdentificationCode { get; set; } /// /// Type /// + [JsonPropertyName("type")] public string? Type { get; set; } /// /// Uid /// + [JsonPropertyName("uid")] public string? Uid { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesUserExecutionEvents.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesUserExecutionEvents.cs index e2aee64..1167d7b 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesUserExecutionEvents.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesUserExecutionEvents.cs @@ -1,8 +1,4 @@ -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using Kraken.Net.Enums; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models.Futures { @@ -14,24 +10,28 @@ public record KrakenFuturesUserExecutionEvents /// /// Account id /// + [JsonPropertyName("accountUid")] public string AccountUid { get; set; } = string.Empty; /// /// Continuation token for pagination /// + [JsonPropertyName("continuationToken")] public string ContinuationToken { get; set; } = string.Empty; /// /// Total number of results /// - [JsonProperty("len")] + [JsonPropertyName("len")] public long Total { get; set; } /// /// Server time /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("serverTime")] public DateTime ServerTime { get; set; } /// /// Elements /// + [JsonPropertyName("elements")] public IEnumerable Elements { get; set; } = Array.Empty(); } @@ -43,15 +43,18 @@ public record KrakenFuturesExecutionElement /// /// Uid /// + [JsonPropertyName("uid")] public string Uid { get; set; } = string.Empty; /// /// Event timestamp /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("timestamp")] public DateTime Timestamp { get; set; } /// /// Event info /// + [JsonPropertyName("event")] public KrakenFuturesExecutionEventWrapper Event { get; set; } = null!; } @@ -63,6 +66,7 @@ public record KrakenFuturesExecutionEventWrapper /// /// Execution info /// + [JsonPropertyName("execution")] public KrakenFuturesExecutionEvent Execution { get; set; } = null!; } @@ -74,11 +78,13 @@ public record KrakenFuturesExecutionEvent /// /// Execution info /// + [JsonPropertyName("execution")] public KrakenFuturesExecution Execution { get; set; } = null!; /// /// Taker reduced quantity /// + [JsonPropertyName("takerReducedQuantity")] public decimal? TakerReducedQuantity { get; set; } } @@ -90,51 +96,63 @@ public record KrakenFuturesExecution /// /// Limit filled /// + [JsonPropertyName("limitFilled")] public bool? LimitFilled { get; set; } /// /// Maker order info /// + [JsonPropertyName("makerOrder")] public KrakenFuturesExecutionOrder? MakerOrder { get; set; } /// /// Maker order data /// + [JsonPropertyName("makerOrderData")] public KrakenFuturesOrderData? MakerOrderData { get; set; } /// /// Mark price /// + [JsonPropertyName("markPrice")] public decimal? MarkPrice { get; set; } /// /// Old taker order /// + [JsonPropertyName("oldTakerOrder")] public KrakenFuturesExecutionOrder? OldTakerOrder { get; set; } /// /// Price /// + [JsonPropertyName("price")] public decimal? Price { get; set; } /// /// Quantity /// + [JsonPropertyName("quantity")] public decimal? Quantity { get; set; } /// /// Taker order /// + [JsonPropertyName("takerOrder")] public KrakenFuturesExecutionOrder? TakerOrder { get; set; } /// /// Taker order data /// + [JsonPropertyName("takerOrderData")] public KrakenFuturesOrderData? TakerOrderData { get; set; } /// /// Timestamp /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("timestamp")] public DateTime? Timestamp { get; set; } /// /// Uid /// + [JsonPropertyName("uid")] public string Uid { get; set; } = string.Empty; /// /// Usd value /// + [JsonPropertyName("usdValue")] public decimal? UsdValue { get; set; } } @@ -146,11 +164,13 @@ public record KrakenFuturesOrderData /// /// Fee /// + [JsonPropertyName("fee")] public decimal Fee { get; set; } /// /// Position size /// + [JsonPropertyName("positionSize")] public decimal PositionSize { get; set; } } @@ -162,63 +182,66 @@ public record KrakenFuturesExecutionOrder /// /// Client order id /// - [JsonProperty("clientId")] + [JsonPropertyName("clientId")] public string? ClientOrderId { get; set; } /// /// Account id /// - [JsonProperty("accountUid")] + [JsonPropertyName("accountUid")] public string AccountUid { get; set; } = string.Empty; /// /// Tradeable /// - [JsonProperty("tradeable")] + [JsonPropertyName("tradeable")] public string Tradeable { get; set; } = string.Empty; /// /// Quantity filled /// - [JsonProperty("filled")] + [JsonPropertyName("filled")] public decimal QuantityFilled { get; set; } /// /// Last update time /// [JsonConverter(typeof(DateTimeConverter))] - [JsonProperty("lastUpdateTimestamp")] + [JsonPropertyName("lastUpdateTimestamp")] public DateTime? LastUpdateTime { get; set; } /// /// Price /// - [JsonProperty("limitPrice")] + [JsonPropertyName("limitPrice")] public decimal? Price { get; set; } /// /// Order id /// - [JsonProperty("uid")] + [JsonPropertyName("uid")] public string OrderId { get; set; } = string.Empty; /// /// Quantity /// + [JsonPropertyName("quantity")] public decimal Quantity { get; set; } /// /// Reduce only /// + [JsonPropertyName("reduceOnly")] public bool ReduceOnly { get; set; } /// /// Order side /// [JsonConverter(typeof(EnumConverter))] - [JsonProperty("direction")] + [JsonPropertyName("direction")] public OrderSide Side { get; set; } /// /// Timestamp /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("timestamp")] public DateTime Timestamp { get; set; } /// /// Order type /// [JsonConverter(typeof(EnumConverter))] - [JsonProperty("orderType")] + [JsonPropertyName("orderType")] public FuturesOrderType Type { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenFuturesUserTrade.cs b/Kraken.Net/Objects/Models/Futures/KrakenFuturesUserTrade.cs index 69f1046..a3f674b 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenFuturesUserTrade.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenFuturesUserTrade.cs @@ -1,14 +1,10 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models.Futures { internal record KrakenFuturesUserTradeResult : KrakenFuturesResult> { - [JsonProperty("fills")] + [JsonPropertyName("fills")] public override IEnumerable Data { get; set; } = new List(); } @@ -20,46 +16,50 @@ public record KrakenFuturesUserTrade /// /// Client order id /// - [JsonProperty("cliOrdId")] + [JsonPropertyName("cliOrdId")] public string? ClientOrderId { get; set; } /// /// Time of the trade /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("fillTime")] public DateTime FillTime { get; set; } /// /// Type of trade /// - [JsonProperty("fillType")] + [JsonPropertyName("fillType")] [JsonConverter(typeof(EnumConverter))] public TradeType Type { get; set; } /// /// Trade id /// - [JsonProperty("fill_id")] + [JsonPropertyName("fill_id")] public string Id { get; set; } = string.Empty; /// /// Order id /// - [JsonProperty("order_id")] + [JsonPropertyName("order_id")] public string OrderId { get; set; } = string.Empty; /// /// Price /// + [JsonPropertyName("price")] public decimal Price { get; set; } /// /// Side /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("side")] public OrderSide Side { get; set; } /// /// Quantity /// - [JsonProperty("size")] + [JsonPropertyName("size")] public decimal Quantity { get; set; } /// /// Symbol /// + [JsonPropertyName("symbol")] public string Symbol { get; set; } = string.Empty; } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenMarginAccountBalances.cs b/Kraken.Net/Objects/Models/Futures/KrakenMarginAccountBalances.cs index 4cd7563..ef6984c 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenMarginAccountBalances.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenMarginAccountBalances.cs @@ -1,32 +1,38 @@ -using Newtonsoft.Json; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { /// /// Margin account balances /// public record KrakenMarginAccountBalances : KrakenBalances { + /// + /// Symbol + /// + public string Symbol { get; set; } = string.Empty; /// /// Balances /// + [JsonPropertyName("balances")] public Dictionary Balances { get; set; } = new Dictionary(); /// /// Currency /// + [JsonPropertyName("currency")] public string Currency { get; set; } = string.Empty; /// /// Auxiliary account info /// + [JsonPropertyName("auxiliary")] public KrakenAuxiliaryAccountInfo? Auxiliary { get; set; } /// /// Account's margin requirements. /// + [JsonPropertyName("marginRequirements")] public KrakenMarginRequirements? MarginRequirements { get; set; } /// /// Account's margin trigger estimates. /// + [JsonPropertyName("triggerEstimates")] public KrakenMarginRequirements? TriggerEstimates { get; set; } } @@ -38,22 +44,22 @@ public record KrakenMarginRequirements /// /// The initial margin requirement of the account. /// - [JsonProperty("im")] + [JsonPropertyName("im")] public decimal? InitialMargin { get; set; } /// /// The liquidation threshold of the account. /// - [JsonProperty("lt")] + [JsonPropertyName("lt")] public decimal? LiquidationThreshold { get; set; } /// /// The maintenance margin requirement of the account. /// - [JsonProperty("mm")] + [JsonPropertyName("mm")] public decimal? MaintenanceMargin { get; set; } /// /// The termination threshold of the account /// - [JsonProperty("tt")] + [JsonPropertyName("tt")] public decimal? TerminationThreshold { get; set; } } @@ -66,26 +72,27 @@ public record KrakenAuxiliaryAccountInfo /// /// The available funds of the account, in currency. /// - [JsonProperty("af")] + [JsonPropertyName("af")] public decimal? AvailableFunds { get; set; } /// /// Funding /// + [JsonPropertyName("funding")] public decimal? Funding { get; set; } /// /// The PnL of current open positions of the account, in currency. /// - [JsonProperty("pnl")] + [JsonPropertyName("pnl")] public decimal? ProfitAndLoss { get; set; } /// /// The portfolio value of the account, in currency. /// - [JsonProperty("pv")] + [JsonPropertyName("pv")] public decimal? PortfolioValue { get; set; } /// /// Usd /// - [JsonProperty("usd")] + [JsonPropertyName("usd")] public decimal? Usd { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenMultiCollateralMarginBalances.cs b/Kraken.Net/Objects/Models/Futures/KrakenMultiCollateralMarginBalances.cs index 072e3a9..b66964e 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenMultiCollateralMarginBalances.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenMultiCollateralMarginBalances.cs @@ -1,7 +1,4 @@ -using Newtonsoft.Json; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Futures +namespace Kraken.Net.Objects.Models.Futures { /// /// Multi-collateral margin account balances @@ -11,55 +8,67 @@ public record KrakenMultiCollateralMarginBalances : KrakenBalances /// /// Total initial margin held for open positions (USD). /// + [JsonPropertyName("initialMargin")] public decimal InitialMargin { get; set; } /// /// Total initial margin held for open positions and open orders (USD). /// + [JsonPropertyName("initialMarginWithOrders")] public decimal InitialMarginWithOrders { get; set; } /// /// Total maintenance margin held for open positions (USD). /// + [JsonPropertyName("mnaintenanceMargin")] public decimal MaintenanceMargin { get; set; } /// /// USD value of all collateral in multi-collateral wallet. /// + [JsonPropertyName("balanceValue")] public decimal BalanceValue { get; set; } /// /// Balance value plus unrealised PnL in USD. /// + [JsonPropertyName("portfolioValue")] public decimal PortfolioValue { get; set; } /// /// USD value of balances in account usable for margin (Balance Value * Haircut). /// + [JsonPropertyName("collateralValue")] public decimal CollateralValue { get; set; } /// /// Unrealised PnL in USD. /// - [JsonProperty("pnl")] + [JsonPropertyName("pnl")] public decimal ProfitAndLoss { get; set; } /// /// Unrealised funding from funding rate (USD). /// + [JsonPropertyName("unrealizedFunding")] public decimal UnrealizedFunding { get; set; } /// /// Total USD value of unrealised funding and unrealised PnL. /// + [JsonPropertyName("totalUnrealized")] public decimal TotalUnrealized { get; set; } /// /// Unrealised pnl and unrealised funding that is usable as margin [(Unrealised Profit/Loss + Unrealised Funding Rate) * Haircut - Conversion Fee]. /// + [JsonPropertyName("totalUnrealizedAsMargin")] public decimal TotalUnrealizedAsMargin { get; set; } /// /// Margin Equity - Total Initial Margin. /// + [JsonPropertyName("availableMargin")] public decimal AvailableMargin { get; set; } /// /// [Balance Value in USD * (1-Haircut)] + (Total Unrealised Profit/Loss as Margin in USD) /// + [JsonPropertyName("marginEquity")] public decimal MarginEquity { get; set; } /// /// Flex currencies /// + [JsonPropertyName("currencies")] public Dictionary Currencies { get; set; } = new Dictionary(); } @@ -71,18 +80,22 @@ public record KrakenFlexCurrencySummary /// /// Margin (in base currency) available for trading. /// + [JsonPropertyName("available")] public decimal Available { get; set; } /// /// USD value of the asset usable for margin (Asset Value * Haircut). /// + [JsonPropertyName("collateral")] public decimal Collateral { get; set; } /// /// Quantity of asset. /// + [JsonPropertyName("quantity")] public decimal Quantity { get; set; } /// /// USD value of asset. /// + [JsonPropertyName("value")] public decimal Value { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Futures/KrakenTriggerOptions.cs b/Kraken.Net/Objects/Models/Futures/KrakenTriggerOptions.cs index 03cd513..e6627ae 100644 --- a/Kraken.Net/Objects/Models/Futures/KrakenTriggerOptions.cs +++ b/Kraken.Net/Objects/Models/Futures/KrakenTriggerOptions.cs @@ -1,6 +1,4 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models.Futures { @@ -12,16 +10,19 @@ public record KrakenTriggerOptions /// /// Trigger price /// + [JsonPropertyName("triggerPrice")] public decimal TriggerPrice { get; set; } /// /// Trigger side /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("triggerSide")] public TriggerSide TriggerSide { get; set; } /// /// Trigger signal /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("triggerSignal")] public TriggerSignal TriggerSignal { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenAllocationsCursorPage.cs b/Kraken.Net/Objects/Models/KrakenAllocationsCursorPage.cs index a898a24..6af112f 100644 --- a/Kraken.Net/Objects/Models/KrakenAllocationsCursorPage.cs +++ b/Kraken.Net/Objects/Models/KrakenAllocationsCursorPage.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Allocations page @@ -12,17 +8,17 @@ public record KrakenAllocationsCursorPage : KrakenCursorPage /// /// Converted asset /// - [JsonProperty("converted_asset")] + [JsonPropertyName("converted_asset")] public string ConvertedAsset { get; set; } = string.Empty; /// /// Total allocated /// - [JsonProperty("total_allocated")] + [JsonPropertyName("total_allocated")] public decimal TotalAllocated { get; set; } /// /// Total rewarded /// - [JsonProperty("total_rewarded")] + [JsonPropertyName("total_rewarded")] public decimal TotalRewarded { get; set; } } @@ -34,29 +30,29 @@ public record KrakenAllocation /// /// Strategy id /// - [JsonProperty("strategy_id")] + [JsonPropertyName("strategy_id")] public string StrategyId { get; set; } = string.Empty; /// /// Native asset /// - [JsonProperty("native_asset")] + [JsonPropertyName("native_asset")] public string Asset { get; set; } = string.Empty; /// /// Allocated amounts /// - [JsonProperty("amount_allocated")] + [JsonPropertyName("amount_allocated")] public KrakenAllocatedAmount AmountAllocated { get; set; } = null!; /// /// Total rewarded /// - [JsonProperty("total_rewarded")] + [JsonPropertyName("total_rewarded")] public KrakenAllocationRewarded TotalRewarded { get; set; } = null!; /// /// Information about the current payout period, absent if when there is no current payout period. /// - [JsonProperty("payout")] + [JsonPropertyName("payout")] public KrakenAllocationPayout Payout { get; set; } = null!; } @@ -68,17 +64,17 @@ public record KrakenAllocatedAmount /// /// Bonding allocations /// - [JsonProperty("bonding")] + [JsonPropertyName("bonding")] public KrakenBondingAwarded Bonding { get; set; } = null!; /// /// Unbonding allocations /// - [JsonProperty("unbonding")] + [JsonPropertyName("unbonding")] public KrakenBondingAwarded Unbonding { get; set; } = null!; /// /// Total allocations /// - [JsonProperty("total")] + [JsonPropertyName("total")] public KrakenAllocationRewarded Total { get; set; } = null!; } @@ -90,12 +86,12 @@ public record KrakenBondingAwarded : KrakenAllocationRewarded /// /// Allocation count /// - [JsonProperty("allocation_count")] + [JsonPropertyName("allocation_count")] public int AllocationCount { get; set; } /// /// Allocations /// - [JsonProperty("allocations")] + [JsonPropertyName("allocations")] public IEnumerable Allocations { get; set; } = Array.Empty(); } @@ -107,12 +103,12 @@ public record KrakenBondingAllocation : KrakenAllocationRewarded /// /// Create time /// - [JsonProperty("created_at")] + [JsonPropertyName("created_at")] public DateTime CreateTime { get; set; } /// /// Expire time /// - [JsonProperty("expires")] + [JsonPropertyName("expires")] public DateTime ExpireTime { get; set; } } @@ -124,12 +120,12 @@ public record KrakenAllocationRewarded /// /// Rewarded in native /// - [JsonProperty("native")] + [JsonPropertyName("native")] public decimal Native { get; set; } /// /// Rewarded in converted /// - [JsonProperty("converted")] + [JsonPropertyName("converted")] public decimal Converted { get; set; } } @@ -141,25 +137,25 @@ public record KrakenAllocationPayout /// /// Reward accumulated in the payout period until now /// - [JsonProperty("accumulated_reward")] + [JsonPropertyName("accumulated_reward")] public KrakenAllocationRewarded AccumulatedReward { get; set; } = default!; /// /// Estimated reward from now until the payout /// - [JsonProperty("estimated_reward")] + [JsonPropertyName("estimated_reward")] public KrakenAllocationRewarded EstimatedReward { get; set; } = default!; /// /// Tentative date of the next reward payout. /// - [JsonProperty("period_end")] + [JsonPropertyName("period_end")] public DateTime PeriodEnd { get; set; } /// /// When the current payout period started. Either the date of the last payout or when it was enabled. /// - [JsonProperty("period_start")] + [JsonPropertyName("period_start")] public DateTime PeriodStart { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenAssetInfo.cs b/Kraken.Net/Objects/Models/KrakenAssetInfo.cs index 521e479..8c1e36d 100644 --- a/Kraken.Net/Objects/Models/KrakenAssetInfo.cs +++ b/Kraken.Net/Objects/Models/KrakenAssetInfo.cs @@ -1,6 +1,4 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models { @@ -12,31 +10,33 @@ public record KrakenAssetInfo /// /// Alternative name /// - [JsonProperty("altname")] + [JsonPropertyName("altname")] public string AlternateName { get; set; } = string.Empty; /// /// Class of the asset /// - [JsonProperty("aclass")] + [JsonPropertyName("aclass")] public string AssetClass { get; set; } = string.Empty; /// /// Decimal precision of the asset /// + [JsonPropertyName("decimals")] public int Decimals { get; set; } /// /// Decimals to display /// - [JsonProperty("display_decimals")] + [JsonPropertyName("display_decimals")] public int DisplayDecimals { get; set; } /// /// Collateral value /// - [JsonProperty("collateral_value")] + [JsonPropertyName("collateral_value")] public decimal? CollateralValue { get; set; } /// /// Status /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("status")] public AssetStatus Status { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenBalance.cs b/Kraken.Net/Objects/Models/KrakenBalance.cs deleted file mode 100644 index 341c4ba..0000000 --- a/Kraken.Net/Objects/Models/KrakenBalance.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Kraken.Net.Objects.Models -{ - /// - /// Balance info - /// - public record KrakenBalance - { - /// - /// Asset - /// - public string Asset { get; set; } = string.Empty; - /// - /// Balance - /// - public decimal Balance { get; set; } - } -} diff --git a/Kraken.Net/Objects/Models/KrakenBalanceAvailable.cs b/Kraken.Net/Objects/Models/KrakenBalanceAvailable.cs index 9e59403..b5da03d 100644 --- a/Kraken.Net/Objects/Models/KrakenBalanceAvailable.cs +++ b/Kraken.Net/Objects/Models/KrakenBalanceAvailable.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Balance info @@ -10,17 +8,18 @@ public record KrakenBalanceAvailable /// /// Asset /// + [JsonPropertyName("asset")] public string Asset { get; set; } = string.Empty; /// /// Balance /// - [JsonProperty("balance")] + [JsonPropertyName("balance")] public decimal Total { get; set; } /// /// The quantity currently locked into a trade /// - [JsonProperty("hold_trade")] + [JsonPropertyName("hold_trade")] public decimal Locked { get; set; } /// diff --git a/Kraken.Net/Objects/Models/Socket/KrakenStreamCancelAfterResult.cs b/Kraken.Net/Objects/Models/KrakenCancelAfterResult.cs similarity index 55% rename from Kraken.Net/Objects/Models/Socket/KrakenStreamCancelAfterResult.cs rename to Kraken.Net/Objects/Models/KrakenCancelAfterResult.cs index 7a66b21..d37801f 100644 --- a/Kraken.Net/Objects/Models/Socket/KrakenStreamCancelAfterResult.cs +++ b/Kraken.Net/Objects/Models/KrakenCancelAfterResult.cs @@ -1,20 +1,19 @@ -using Kraken.Net.Objects.Sockets; -using System; - -namespace Kraken.Net.Objects.Models.Socket +namespace Kraken.Net.Objects.Models { /// /// Cancel after result /// - public record KrakenStreamCancelAfterResult : KrakenQueryEvent + public record KrakenCancelAfterResult { /// /// Current time /// + [JsonPropertyName("currentTime")] public DateTime CurrentTime { get; set; } /// /// Trigger time /// - public DateTime TriggerTime { get; set; } + [JsonPropertyName("triggerTime")] + public DateTime? TriggerTime { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenCancelResult.cs b/Kraken.Net/Objects/Models/KrakenCancelResult.cs index 038fadf..0dcdfd9 100644 --- a/Kraken.Net/Objects/Models/KrakenCancelResult.cs +++ b/Kraken.Net/Objects/Models/KrakenCancelResult.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Result of a cancel request @@ -11,10 +8,12 @@ public record KrakenCancelResult /// /// Amount of canceled orders /// + [JsonPropertyName("count")] public int Count { get; set; } /// /// Pending cancelation orders /// + [JsonPropertyName("pending")] public IEnumerable Pending { get; set; } = Array.Empty(); } } diff --git a/Kraken.Net/Objects/Models/KrakenCursorPage.cs b/Kraken.Net/Objects/Models/KrakenCursorPage.cs index 5c5d062..5077ad2 100644 --- a/Kraken.Net/Objects/Models/KrakenCursorPage.cs +++ b/Kraken.Net/Objects/Models/KrakenCursorPage.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Data page with a cusor for pagination @@ -12,17 +8,17 @@ public record KrakenCursorPage /// /// Cursor for the next page /// - [JsonProperty("next_cursor")] + [JsonPropertyName("next_cursor")] public string? NextCursor { get; set; } /// /// Page data /// - [JsonProperty("items")] + [JsonPropertyName("items")] public IEnumerable Items { get; set; } = Array.Empty(); - [JsonProperty("withdrawals")] + [JsonPropertyName("withdrawals")] internal IEnumerable Withdrawals { set => Items = value; } - [JsonProperty("deposits")] + [JsonPropertyName("deposits")] internal IEnumerable Deposits { set => Items = value; } } } diff --git a/Kraken.Net/Objects/Models/KrakenDepositAddress.cs b/Kraken.Net/Objects/Models/KrakenDepositAddress.cs index d2b7a0a..8fc5e14 100644 --- a/Kraken.Net/Objects/Models/KrakenDepositAddress.cs +++ b/Kraken.Net/Objects/Models/KrakenDepositAddress.cs @@ -1,8 +1,4 @@ -using System; -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Deposit address @@ -12,26 +8,27 @@ public record KrakenDepositAddress /// /// The actual address /// + [JsonPropertyName("address")] public string Address { get; set; } = string.Empty; /// /// The expire time of the address /// - [JsonProperty("expiretm"), JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("expiretm"), JsonConverter(typeof(DateTimeConverter))] public DateTime ExpireTime { get; set; } /// /// If the address has been used before /// - [JsonProperty("new")] + [JsonPropertyName("new")] public bool IsNew { get; set; } /// /// Tag /// - [JsonProperty("tag")] + [JsonPropertyName("tag")] public string? Tag { get; set; } /// /// Memo /// - [JsonProperty("memo")] + [JsonPropertyName("memo")] public string? Memo { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenDepositMethod.cs b/Kraken.Net/Objects/Models/KrakenDepositMethod.cs index 5aa2b42..7aa3f21 100644 --- a/Kraken.Net/Objects/Models/KrakenDepositMethod.cs +++ b/Kraken.Net/Objects/Models/KrakenDepositMethod.cs @@ -1,5 +1,4 @@ -using Newtonsoft.Json; - + namespace Kraken.Net.Objects.Models { /// @@ -10,29 +9,32 @@ public record KrakenDepositMethod /// /// Name of the method /// + [JsonPropertyName("method")] public string Method { get; set; } = string.Empty; /// /// Deposit limit (max) of the method /// + [JsonPropertyName("limit"), JsonConverter(typeof(NumberStringConverter))] public string Limit { get; set; } = string.Empty; /// /// The deposit fee for the method /// + [JsonPropertyName("fee")] public decimal Fee { get; set; } /// /// The fee for setting up an address /// - [JsonProperty("address-setup-fee")] + [JsonPropertyName("address-setup-fee")] public decimal? AddressSetupFee { get; set; } /// /// Generate address /// - [JsonProperty("gen-address")] + [JsonPropertyName("gen-address")] public bool? GenerateAddress { get; set; } /// /// Minimum deposit amount /// - [JsonProperty("minimum")] + [JsonPropertyName("minimum")] public decimal? MinimumDepositAmount { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenEarnStatus.cs b/Kraken.Net/Objects/Models/KrakenEarnStatus.cs index d8a7bca..0028e63 100644 --- a/Kraken.Net/Objects/Models/KrakenEarnStatus.cs +++ b/Kraken.Net/Objects/Models/KrakenEarnStatus.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Earn status @@ -12,7 +8,7 @@ public record KrakenEarnStatus /// /// Is pending /// - [JsonProperty("pending")] + [JsonPropertyName("pending")] public bool Pending { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenEarnStrategy.cs b/Kraken.Net/Objects/Models/KrakenEarnStrategy.cs index e997c6d..fb8ece3 100644 --- a/Kraken.Net/Objects/Models/KrakenEarnStrategy.cs +++ b/Kraken.Net/Objects/Models/KrakenEarnStrategy.cs @@ -1,9 +1,4 @@ using Kraken.Net.Enums; -using Newtonsoft.Json.Linq; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Text; namespace Kraken.Net.Objects.Models { @@ -15,67 +10,67 @@ public record KrakenEarnStrategy /// /// Id /// - [JsonProperty("id")] + [JsonPropertyName("id")] public string Id { get; set; } = string.Empty; /// /// Asset /// - [JsonProperty("asset")] + [JsonPropertyName("asset")] public string Asset { get; set; } = string.Empty; /// /// Minimum amount (in USD) for an allocation or deallocation. Absence means no minimum. /// - [JsonProperty("user_min_allocation")] + [JsonPropertyName("user_min_allocation")] public decimal MinAllocation { get; set; } /// /// Fee applied when allocating to this strategy /// - [JsonProperty("allocation_fee")] + [JsonPropertyName("allocation_fee")] public decimal AllocationFee { get; set; } /// /// Fee applied when deallocating from this strategy /// - [JsonProperty("deallocation_fee")] + [JsonPropertyName("deallocation_fee")] public decimal DeallocationFee { get; set; } /// /// The maximum amount of funds that any given user may allocate to an account. Absence of value means there is no limit. Zero means that all new allocations will return an error (though auto-compound is unaffected). /// - [JsonProperty("user_cap")] + [JsonPropertyName("user_cap")] public decimal MaxAllocation { get; set; } /// /// Is allocation available for this strategy /// - [JsonProperty("can_allocate")] + [JsonPropertyName("can_allocate")] public bool CanAllocate { get; set; } /// /// Is deallocation available for this strategy /// - [JsonProperty("can_deallocate")] + [JsonPropertyName("can_deallocate")] public bool CanDeallocate { get; set; } /// /// Reason for restrictions /// - [JsonProperty("allocation_restriction_info")] + [JsonPropertyName("allocation_restriction_info")] public IEnumerable AllocationRestrictionInfo { get; set; } = Array.Empty(); /// /// Yield source /// - [JsonProperty("yield_source")] + [JsonPropertyName("yield_source")] public YieldSourceType YieldSource { get; set; } = null!; /// /// Auto compound /// - [JsonProperty("auto_compound")] + [JsonPropertyName("auto_compound")] public AutoCompoundType AutoCompound { get; set; } = null!; /// /// Apr estimation /// - [JsonProperty("apr_estimate")] + [JsonPropertyName("apr_estimate")] public AprEstimate AprEstimate { get; set; } = null!; /// /// Lock type info /// - [JsonProperty("lock_type")] + [JsonPropertyName("lock_type")] public LockTypeInfo LockType { get; set; } = null!; } @@ -87,47 +82,47 @@ public record LockTypeInfo /// /// Duration of the bonding period, in seconds /// - [JsonProperty("bonding_period")] + [JsonPropertyName("bonding_period")] public int? BondingPeriod { get; set; } /// /// Is the bonding period length variable (true) or static (false) /// - [JsonProperty("bonding_period_variable")] + [JsonPropertyName("bonding_period_variable")] public bool? IsBondingPeriodVariable { get; set; } /// /// Whether rewards are earned during the bonding period (payouts occur after bonding is complete) /// - [JsonProperty("bonding_rewards")] + [JsonPropertyName("bonding_rewards")] public bool? RewardsDuringBonding { get; set; } /// /// In order to remove funds, if this value is greater than 0, funds will first have to enter an exit queue and will have to wait for the exit queue period to end. Once ended, her funds will then follow and respect the unbonding_period. If the value of the exit queue period is 0, then no waiting will have to occur and the exit queue will be skipped. Rewards are always paid out for the exit queue /// - [JsonProperty("exit_queue_period")] + [JsonPropertyName("exit_queue_period")] public int? ExitQueuePeriod { get; set; } /// /// At what intervals are rewards distributed and credited to the user’s ledger, in seconds /// - [JsonProperty("payout_frequency")] + [JsonPropertyName("payout_frequency")] public int? PayoutFrequency { get; set; } /// /// Duration of the unbonding period in seconds. In order to remove funds, you must wait for the unbonding period to pass after requesting removal before funds become available in her spot wallet /// - [JsonProperty("unbonding_period")] + [JsonPropertyName("unbonding_period")] public int? UnbondingPeriod { get; set; } /// /// Lock type /// - [JsonProperty("type")] + [JsonPropertyName("type")] public LockType Type { get; set; } /// /// Is the unbonding period length variable (true) or static (false) /// - [JsonProperty("unbonding_period_variable")] + [JsonPropertyName("unbonding_period_variable")] public bool? IsUnbondingPeriodVariable { get; set; } /// /// Whether rewards are earned during the unbonding period (payouts occur after bonding is complete) /// - [JsonProperty("unbonding_rewards")] + [JsonPropertyName("unbonding_rewards")] public bool? RewardsDuringUnbonding { get; set; } } @@ -139,7 +134,7 @@ public record YieldSourceType /// /// Yield source type /// - [JsonProperty("type")] + [JsonPropertyName("type")] public YieldSource Type { get; set; } } @@ -151,12 +146,12 @@ public record AutoCompoundType /// /// Whether it is the default (if Type is Optional) /// - [JsonProperty("default")] + [JsonPropertyName("default")] public bool? Default { get; set; } /// /// Auto Compound type /// - [JsonProperty("type")] + [JsonPropertyName("type")] public AutoCompound Type { get; set; } } @@ -168,12 +163,12 @@ public record AprEstimate /// /// High estimate /// - [JsonProperty("high")] + [JsonPropertyName("high")] public decimal High { get; set; } /// /// Low estimate /// - [JsonProperty("low")] + [JsonPropertyName("low")] public decimal Low { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenEditOrder.cs b/Kraken.Net/Objects/Models/KrakenEditOrder.cs index 33f9d3e..592ea85 100644 --- a/Kraken.Net/Objects/Models/KrakenEditOrder.cs +++ b/Kraken.Net/Objects/Models/KrakenEditOrder.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Edited order info @@ -10,12 +8,12 @@ public record KrakenEditOrder /// /// Order ids /// - [JsonProperty("txid")] + [JsonPropertyName("txid")] public string OrderId { get; set; } = string.Empty; /// /// Descriptions /// - [JsonProperty("descr")] + [JsonPropertyName("descr")] public KrakenPlacedOrderDescription Descriptions { get; set; } = default!; } } diff --git a/Kraken.Net/Objects/Models/KrakenFeeEntry.cs b/Kraken.Net/Objects/Models/KrakenFeeEntry.cs index 56c6419..ebedd31 100644 --- a/Kraken.Net/Objects/Models/KrakenFeeEntry.cs +++ b/Kraken.Net/Objects/Models/KrakenFeeEntry.cs @@ -1,5 +1,4 @@ using CryptoExchange.Net.Converters; -using Newtonsoft.Json; namespace Kraken.Net.Objects.Models { diff --git a/Kraken.Net/Objects/Models/KrakenKline.cs b/Kraken.Net/Objects/Models/KrakenKline.cs index 7fe5646..e6b9e26 100644 --- a/Kraken.Net/Objects/Models/KrakenKline.cs +++ b/Kraken.Net/Objects/Models/KrakenKline.cs @@ -1,6 +1,4 @@ -using System; -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; +using CryptoExchange.Net.Converters; namespace Kraken.Net.Objects.Models { @@ -19,25 +17,25 @@ public record KrakenKline /// The open price for this kline /// [ArrayProperty(1)] - [JsonProperty("open")] + [JsonPropertyName("open")] public decimal OpenPrice { get; set; } /// /// The highest price during this kline /// [ArrayProperty(2)] - [JsonProperty("high")] + [JsonPropertyName("high")] public decimal HighPrice { get; set; } /// /// The lowest price during this kline /// [ArrayProperty(3)] - [JsonProperty("low")] + [JsonPropertyName("low")] public decimal LowPrice { get; set; } /// /// The close price of this kline (or price of last trade if kline isn't closed yet) /// [ArrayProperty(4)] - [JsonProperty("close")] + [JsonPropertyName("close")] public decimal ClosePrice { get; set; } /// /// The volume weighted average price diff --git a/Kraken.Net/Objects/Models/KrakenLedgerEntry.cs b/Kraken.Net/Objects/Models/KrakenLedgerEntry.cs index e161bf0..b5ee044 100644 --- a/Kraken.Net/Objects/Models/KrakenLedgerEntry.cs +++ b/Kraken.Net/Objects/Models/KrakenLedgerEntry.cs @@ -1,8 +1,4 @@ -using System; -using CryptoExchange.Net.Converters; -using Kraken.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models { @@ -14,49 +10,53 @@ public record KrakenLedgerEntry /// /// The id /// + [JsonPropertyName("id")] public string Id { get; set; } = string.Empty; /// /// Reference id /// - [JsonProperty("refid")] + [JsonPropertyName("refid")] public string ReferenceId { get; set; } = string.Empty; /// /// Timestamp /// - [JsonProperty("time"), JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("time"), JsonConverter(typeof(DateTimeConverter))] public DateTime Timestamp { get; set; } /// /// The type of entry /// - [JsonConverter(typeof(LedgerEntryTypeConverter))] + [JsonPropertyName("type")] public LedgerEntryType Type { get; set; } /// /// Sub type /// + [JsonPropertyName("subtype")] public string? SubType { get; set; } /// /// Class of the asset /// - [JsonProperty("aclass")] + [JsonPropertyName("aclass")] public string AssetClass { get; set; } = string.Empty; /// /// Name of the asset /// + [JsonPropertyName("asset")] public string Asset { get; set; } = string.Empty; /// /// The quantity of the entry /// - [JsonProperty("amount")] + [JsonPropertyName("amount")] public decimal Quantity { get; set; } /// /// Fee paid /// + [JsonPropertyName("fee")] public decimal Fee { get; set; } /// /// Resulting balance /// - [JsonProperty("balance")] + [JsonPropertyName("balance")] public decimal BalanceAfter { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenMovementStatus.cs b/Kraken.Net/Objects/Models/KrakenMovementStatus.cs index a6ac08c..508fbdc 100644 --- a/Kraken.Net/Objects/Models/KrakenMovementStatus.cs +++ b/Kraken.Net/Objects/Models/KrakenMovementStatus.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Deposit status info @@ -13,63 +8,68 @@ public record KrakenMovementStatus /// /// The name of the deposit method /// + [JsonPropertyName("method")] public string Method { get; set; } = string.Empty; /// /// The record of the asset /// - [JsonProperty("aclass")] + [JsonPropertyName("aclass")] public string AssetClass { get; set; } = string.Empty; /// /// The asset name /// + [JsonPropertyName("asset")] public string Asset { get; set; } = string.Empty; /// /// Reference id /// - [JsonProperty("refid")] + [JsonPropertyName("refid")] public string ReferenceId { get; set; } = string.Empty; /// /// Transaction id /// - [JsonProperty("txid")] + [JsonPropertyName("txid")] public string TransactionId { get; set; } = string.Empty; /// /// Info about the transaction /// - [JsonProperty("info")] + [JsonPropertyName("info")] public string TransactionInfo { get; set; } = string.Empty; /// /// The quantity involved in the deposit /// - [JsonProperty("amount")] + [JsonPropertyName("amount")] public decimal Quantity { get; set; } /// /// The fee paid for the deposit /// + [JsonPropertyName("fee")] public decimal Fee { get; set; } /// /// The timestamp /// [JsonConverter(typeof(DateTimeConverter))] - [JsonProperty("time")] + [JsonPropertyName("time")] public DateTime Timestamp { get; set; } /// /// Status of the transaction /// + [JsonPropertyName("status")] public string Status { get; set; } = string.Empty; /// /// Additional status info /// - [JsonProperty("status-prop")] + [JsonPropertyName("status-prop")] public string? AdditionalStatus { get; set; } = string.Empty; /// /// Withdrawal key name, as set up on your account /// - [JsonProperty("key")] + [JsonPropertyName("key")] public string? Key { get; set; } /// /// Originators /// + [JsonPropertyName("originators")] public IEnumerable? Originators { get; set; } = Array.Empty(); } } diff --git a/Kraken.Net/Objects/Models/KrakenOrder.cs b/Kraken.Net/Objects/Models/KrakenOrder.cs index cc535ee..4adcec4 100644 --- a/Kraken.Net/Objects/Models/KrakenOrder.cs +++ b/Kraken.Net/Objects/Models/KrakenOrder.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using CryptoExchange.Net.Converters; -using Kraken.Net.Clients.SpotApi; -using Kraken.Net.Converters; +using Kraken.Net.Clients.SpotApi; using Kraken.Net.Enums; -using Newtonsoft.Json; namespace Kraken.Net.Objects.Models { @@ -17,104 +12,116 @@ public record KrakenOrder /// The id of the order /// [JsonIgnore] + [JsonPropertyName("id")] public string Id { get; set; } = string.Empty; /// /// Reference id /// - [JsonProperty("refid")] + [JsonPropertyName("refid")] public string ReferenceId { get; set; } = string.Empty; /// /// Client reference id /// - [JsonProperty("userref")] - public string ClientOrderId { get; set; } = string.Empty; + [JsonPropertyName("userref")] + public uint UserReference { get; set; } + /// + /// Client reference id + /// + [JsonPropertyName("cl_ord_id")] + public string? ClientOrderId { get; set; } /// /// Status of the order /// - [JsonConverter(typeof(OrderStatusConverter))] + [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("status")] public OrderStatus Status { get; set; } /// /// Open timestamp /// - [JsonProperty("opentm"), JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("opentm"), JsonConverter(typeof(DateTimeConverter))] public DateTime CreateTime { get; set; } /// /// Start timestamp /// - [JsonProperty("starttm"), JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("starttm"), JsonConverter(typeof(DateTimeConverter))] public DateTime? StartTime { get; set; } /// /// Expire timestamp /// - [JsonProperty("expiretm"), JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("expiretm"), JsonConverter(typeof(DateTimeConverter))] public DateTime? ExpireTime { get; set; } /// /// Close timestamp /// - [JsonProperty("closetm"), JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("closetm"), JsonConverter(typeof(DateTimeConverter))] public DateTime? CloseTime { get; set; } /// /// Order details /// - [JsonProperty("descr")] + [JsonPropertyName("descr")] public KrakenOrderInfo OrderDetails { get; set; } = default!; /// /// Quantity of the order /// - [JsonProperty("vol")] + [JsonPropertyName("vol")] public decimal Quantity { get; set; } /// /// Filled quantity /// - [JsonProperty("vol_exec")] + [JsonPropertyName("vol_exec")] public decimal QuantityFilled { get; set; } /// /// Cost of the order /// - [JsonProperty("cost")] + [JsonPropertyName("cost")] public decimal QuoteQuantityFilled { get; set; } /// /// Fee /// + [JsonPropertyName("fee")] public decimal Fee { get; set; } /// /// Average price of the order /// - [JsonProperty("price")] + [JsonPropertyName("price")] public decimal AveragePrice { get; set; } - [JsonProperty("avg_price")] + [JsonPropertyName("avg_price")] private decimal AveragePrice2 { get => AveragePrice; set => AveragePrice = value; } /// /// Stop price /// + [JsonPropertyName("stopPrice")] public decimal StopPrice { get; set; } /// /// Limit price /// - [JsonProperty("limitprice")] + [JsonPropertyName("limitprice")] public decimal Price { get; set; } /// /// Miscellaneous info /// + [JsonPropertyName("misc")] public string Misc { get; set; } = string.Empty; /// /// Order flags /// + [JsonPropertyName("oflags")] public string Oflags { get; set; } = string.Empty; /// /// Reason of failure /// + [JsonPropertyName("reason")] public string Reason { get; set; } = string.Empty; /// /// Indicates if the order is funded on margin. /// - [JsonProperty("margin")] + [JsonPropertyName("margin")] public bool? Margin { get; set; } /// /// Trade ids /// - [JsonProperty("trades")] + [JsonPropertyName("trades")] public IEnumerable TradeIds { get; set; } = Array.Empty(); } @@ -126,38 +133,42 @@ public record KrakenOrderInfo /// /// The symbol of the order /// - [JsonProperty("pair")] + [JsonPropertyName("pair")] public string Symbol { get; set; } = string.Empty; /// /// Side of the order /// - [JsonProperty("type"), JsonConverter(typeof(OrderSideConverter))] + [JsonPropertyName("type"), JsonConverter(typeof(EnumConverter))] public OrderSide Side { get; set; } /// /// Type of the order /// - [JsonProperty("ordertype"), JsonConverter(typeof(OrderTypeConverter))] + [JsonPropertyName("ordertype"), JsonConverter(typeof(EnumConverter))] public OrderType Type { get; set; } /// /// Price of the order /// + [JsonPropertyName("price")] public decimal Price { get; set; } /// /// Secondary price of the order ( for details) /// - [JsonProperty("price2")] + [JsonPropertyName("price2")] public decimal SecondaryPrice { get; set; } /// /// Amount of leverage /// + [JsonPropertyName("leverage")] public string Leverage { get; set; } = string.Empty; /// /// Order description /// + [JsonPropertyName("order")] public string Order { get; set; } = string.Empty; /// /// Conditional close order description /// + [JsonPropertyName("close")] public string Close { get; set; } = string.Empty; } @@ -169,6 +180,7 @@ public record KrakenStreamOrder: KrakenOrder /// /// The update sequence number /// + [JsonPropertyName("sequenceNumber")] public int SequenceNumber { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenOrderBook.cs b/Kraken.Net/Objects/Models/KrakenOrderBook.cs index dabf9b8..28271f9 100644 --- a/Kraken.Net/Objects/Models/KrakenOrderBook.cs +++ b/Kraken.Net/Objects/Models/KrakenOrderBook.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using CryptoExchange.Net.Converters; -using CryptoExchange.Net.Interfaces; -using Newtonsoft.Json; +using CryptoExchange.Net.Converters; namespace Kraken.Net.Objects.Models { @@ -14,11 +10,13 @@ public record KrakenOrderBook /// /// Asks in the book /// - public IEnumerable Asks { get; set; } = Array.Empty(); + [JsonPropertyName("asks")] + public IEnumerable Asks { get; set; } = Array.Empty(); /// /// Bids in the book /// - public IEnumerable Bids { get; set; } = Array.Empty(); + [JsonPropertyName("bids")] + public IEnumerable Bids { get; set; } = Array.Empty(); } /// @@ -63,22 +61,24 @@ public record KrakenStreamOrderBook /// /// Asks /// - [JsonProperty("as")] + [JsonPropertyName("as")] public IEnumerable Asks { get; set; } = Array.Empty(); /// /// Bids /// - [JsonProperty("bs")] + [JsonPropertyName("bs")] public IEnumerable Bids { get; set; } = Array.Empty(); /// /// Checksum /// + [JsonPropertyName("checksum")] public uint? Checksum { get; set; } /// /// Is this a snapshot? /// + [JsonPropertyName("snapshot")] internal bool Snapshot { get; set; } } diff --git a/Kraken.Net/Objects/Models/KrakenOrderRequest.cs b/Kraken.Net/Objects/Models/KrakenOrderRequest.cs index 8e86725..b441e3f 100644 --- a/Kraken.Net/Objects/Models/KrakenOrderRequest.cs +++ b/Kraken.Net/Objects/Models/KrakenOrderRequest.cs @@ -1,9 +1,4 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models { @@ -12,80 +7,85 @@ namespace Kraken.Net.Objects.Models /// public record KrakenOrderRequest { + /// + /// User reference + /// + [JsonPropertyName("userref"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public uint? UserReference { get; set; } /// /// Client order id /// - [JsonProperty("userref", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("cl_ord_id"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? ClientOrderId { get; set; } /// /// Order type /// - [JsonProperty("ordertype"), JsonConverter(typeof(OrderTypeConverter))] + [JsonPropertyName("ordertype")] public OrderType OrderType { get; set; } /// /// Order side /// - [JsonProperty("type"), JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("type"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OrderSide Side { get; set; } /// /// Quantity /// - [JsonProperty("volume"), JsonConverter(typeof(DecimalStringWriterConverter))] + [JsonPropertyName("volume"), JsonConverter(typeof(DecimalStringWriterConverter))] public decimal Quantity { get; set; } /// /// Iceberg quantity /// - [JsonProperty("displayvol", DefaultValueHandling = DefaultValueHandling.Ignore), JsonConverter(typeof(DecimalStringWriterConverter))] + [JsonPropertyName("displayvol"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault), JsonConverter(typeof(DecimalStringWriterConverter))] public decimal? IcebergQuanty { get; set; } /// /// Price /// - [JsonProperty("price", DefaultValueHandling = DefaultValueHandling.Ignore), JsonConverter(typeof(DecimalStringWriterConverter))] + [JsonPropertyName("price"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault), JsonConverter(typeof(DecimalStringWriterConverter))] public decimal? Price { get; set; } /// /// Secondary price /// - [JsonProperty("price2", DefaultValueHandling = DefaultValueHandling.Ignore), JsonConverter(typeof(DecimalStringWriterConverter))] + [JsonPropertyName("price2"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault), JsonConverter(typeof(DecimalStringWriterConverter))] public decimal? SecondaryPrice { get; set; } /// /// Trigger /// - [JsonProperty("trigger", DefaultValueHandling = DefaultValueHandling.Ignore), JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("trigger"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault), JsonConverter(typeof(EnumConverter))] public Trigger? Trigger { get; set; } /// /// Leverage /// - [JsonProperty("leverage", DefaultValueHandling = DefaultValueHandling.Ignore), JsonConverter(typeof(DecimalStringWriterConverter))] + [JsonPropertyName("leverage"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault), JsonConverter(typeof(DecimalStringWriterConverter))] public decimal? Leverage { get; set; } /// /// Reduce only /// - [JsonProperty("reduce_only", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("reduce_only"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool? ReduceOnly { get; set; } /// /// Self trade prevention type /// - [JsonProperty("stptype", DefaultValueHandling = DefaultValueHandling.Ignore), JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("stptype"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault), JsonConverter(typeof(EnumConverter))] public SelfTradePreventionType? SelfTradePreventionType { get; set; } /// /// Order flags /// - [JsonProperty("oflags", DefaultValueHandling = DefaultValueHandling.Ignore, ItemConverterType = typeof(OrderFlagsConverter))] + [JsonPropertyName("oflags"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IEnumerable? Flags { get; set; } /// /// Time in force /// - [JsonProperty("timeinforce", DefaultValueHandling = DefaultValueHandling.Ignore), JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("timeinforce"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault), JsonConverter(typeof(EnumConverter))] public TimeInForce? TimeInForce { get; set; } /// /// Start time /// - [JsonProperty("starttm", DefaultValueHandling = DefaultValueHandling.Ignore), JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("starttm"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault), JsonConverter(typeof(DateTimeConverter))] public DateTime? StartTime { get; set; } /// /// Expire time /// - [JsonProperty("expiretm", DefaultValueHandling = DefaultValueHandling.Ignore), JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("expiretm"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault), JsonConverter(typeof(DateTimeConverter))] public DateTime? ExpireTime { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenPageData.cs b/Kraken.Net/Objects/Models/KrakenPageData.cs index 9ac9a57..aa6c1e4 100644 --- a/Kraken.Net/Objects/Models/KrakenPageData.cs +++ b/Kraken.Net/Objects/Models/KrakenPageData.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Base page data @@ -10,6 +8,7 @@ public record KrakenPageData /// /// Total number of records /// + [JsonPropertyName("count")] public int Count { get; set; } } @@ -21,6 +20,7 @@ public record OpenOrdersPage : KrakenPageData /// /// Open orders /// + [JsonPropertyName("open")] public Dictionary Open { get; set; } = new Dictionary(); } @@ -32,6 +32,7 @@ public record KrakenClosedOrdersPage: KrakenPageData /// /// Closed orders /// + [JsonPropertyName("closed")] public Dictionary Closed { get; set; } = new Dictionary(); } @@ -43,6 +44,7 @@ public record KrakenUserTradesPage : KrakenPageData /// /// Trades /// + [JsonPropertyName("trades")] public Dictionary Trades { get; set; } = new Dictionary(); } @@ -54,6 +56,7 @@ public record KrakenLedgerPage : KrakenPageData /// /// Ledger entries /// + [JsonPropertyName("ledger")] public Dictionary Ledger { get; set; } = new Dictionary(); } } diff --git a/Kraken.Net/Objects/Models/KrakenPlacedBatchOrder.cs b/Kraken.Net/Objects/Models/KrakenPlacedBatchOrder.cs index a57951a..42403e1 100644 --- a/Kraken.Net/Objects/Models/KrakenPlacedBatchOrder.cs +++ b/Kraken.Net/Objects/Models/KrakenPlacedBatchOrder.cs @@ -1,9 +1,4 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Batch order result @@ -13,7 +8,7 @@ public record KrakenBatchOrderResult /// /// Orders /// - [JsonProperty("orders")] + [JsonPropertyName("orders")] public IEnumerable Orders { get; set; } = Array.Empty(); } @@ -25,17 +20,17 @@ public record KrakenPlacedBatchOrder /// /// Order id /// - [JsonProperty("txid")] + [JsonPropertyName("txid")] public string OrderId { get; set; } = null!; /// /// Description /// - [JsonProperty("descr")] + [JsonPropertyName("descr")] public KrakenPlacedOrderDescription Description { get; set; } = null!; /// /// Close order description /// - [JsonProperty("close")] + [JsonPropertyName("close")] public string? CloseOrderInfo { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenPlacedOrder.cs b/Kraken.Net/Objects/Models/KrakenPlacedOrder.cs index bbdd9a9..9360338 100644 --- a/Kraken.Net/Objects/Models/KrakenPlacedOrder.cs +++ b/Kraken.Net/Objects/Models/KrakenPlacedOrder.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Placed order info @@ -12,12 +8,12 @@ public record KrakenPlacedOrder /// /// Order ids /// - [JsonProperty("txid")] + [JsonPropertyName("txid")] public IEnumerable OrderIds { get; set; } = Array.Empty(); /// /// Descriptions /// - [JsonProperty("descr")] + [JsonPropertyName("descr")] public KrakenPlacedOrderDescription Descriptions { get; set; } = default!; } @@ -29,12 +25,12 @@ public record KrakenPlacedOrderDescription /// /// Order description /// - [JsonProperty("order")] + [JsonPropertyName("order")] public string OrderDescription { get; set; } = string.Empty; /// /// Close order description /// - [JsonProperty("close")] + [JsonPropertyName("close")] public string CloseOrderDescription { get; set; } = string.Empty; } } diff --git a/Kraken.Net/Objects/Models/KrakenPosition.cs b/Kraken.Net/Objects/Models/KrakenPosition.cs index 374feec..7d76548 100644 --- a/Kraken.Net/Objects/Models/KrakenPosition.cs +++ b/Kraken.Net/Objects/Models/KrakenPosition.cs @@ -1,8 +1,4 @@ -using System; -using CryptoExchange.Net.Converters; -using Kraken.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models { @@ -14,84 +10,92 @@ public record KrakenPosition /// /// The position id /// + [JsonPropertyName("id")] public string Id { get; set; } = string.Empty; /// /// Order id /// - [JsonProperty("ordertxid")] + [JsonPropertyName("ordertxid")] public string OrderId { get; set; } = string.Empty; /// /// Status /// - [JsonProperty("posstatus")] + [JsonPropertyName("posstatus")] public string Status { get; set; } = string.Empty; /// /// Symbol /// - [JsonProperty("pair")] + [JsonPropertyName("pair")] public string Symbol { get; set; } = string.Empty; /// /// Timestamp /// - [JsonProperty("time"), JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("time"), JsonConverter(typeof(DateTimeConverter))] public DateTime Timestamp { get; set; } /// /// Rollover time /// - [JsonProperty("rollovertm"), JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("rollovertm"), JsonConverter(typeof(DateTimeConverter))] public DateTime? RollOverTime { get; set; } /// /// Side /// - [JsonProperty("type"), JsonConverter(typeof(OrderSideConverter))] + [JsonPropertyName("type"), JsonConverter(typeof(EnumConverter))] public OrderSide Side { get; set; } /// /// Type /// - [JsonProperty("ordertype"), JsonConverter(typeof(OrderTypeConverter))] + [JsonPropertyName("ordertype"), JsonConverter(typeof(EnumConverter))] public OrderType Type { get; set; } /// /// Cost /// + [JsonPropertyName("cost")] public decimal Cost { get; set; } /// /// Fee /// + [JsonPropertyName("fee")] public decimal Fee { get; set; } /// /// Quantity /// - [JsonProperty("vol")] + [JsonPropertyName("vol")] public decimal Quantity { get; set; } /// /// Closed quantity /// - [JsonProperty("vol_closed")] + [JsonPropertyName("vol_closed")] public decimal QuantityClosed { get; set; } /// /// Margin /// + [JsonPropertyName("margin")] public decimal Margin { get; set; } /// /// Value /// + [JsonPropertyName("value")] public decimal? Value { get; set; } /// /// Net profit/loss /// - [JsonProperty("net")] + [JsonPropertyName("net")] public decimal? ProfitLoss { get; set; } /// /// Misc info /// + [JsonPropertyName("misc")] public string Misc { get; set; } = string.Empty; /// /// Flags /// + [JsonPropertyName("oflags")] public string OFlags { get; set; } = string.Empty; /// /// Terms /// + [JsonPropertyName("terms")] public string Terms { get; set; } = string.Empty; } } diff --git a/Kraken.Net/Objects/Models/KrakenReferenceId.cs b/Kraken.Net/Objects/Models/KrakenReferenceId.cs index 40effef..ece2aaf 100644 --- a/Kraken.Net/Objects/Models/KrakenReferenceId.cs +++ b/Kraken.Net/Objects/Models/KrakenReferenceId.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Id @@ -10,7 +8,7 @@ public record KrakenReferenceId /// /// The id /// - [JsonProperty("refid")] + [JsonPropertyName("refid")] public string ReferenceId { get; set; } = string.Empty; } } diff --git a/Kraken.Net/Objects/Models/KrakenServerTime.cs b/Kraken.Net/Objects/Models/KrakenServerTime.cs index b95ff5b..5e996da 100644 --- a/Kraken.Net/Objects/Models/KrakenServerTime.cs +++ b/Kraken.Net/Objects/Models/KrakenServerTime.cs @@ -1,14 +1,11 @@ -using System; -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { internal record KrakenServerTime { [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("unixTime")] public DateTime UnixTime { get; set; } - [JsonProperty("rfc1123")] + [JsonPropertyName("rfc1123")] public string RfcTime { get; set; } = string.Empty; } } diff --git a/Kraken.Net/Objects/Models/KrakenSpread.cs b/Kraken.Net/Objects/Models/KrakenSpread.cs index c39cc98..7e8893c 100644 --- a/Kraken.Net/Objects/Models/KrakenSpread.cs +++ b/Kraken.Net/Objects/Models/KrakenSpread.cs @@ -1,6 +1,4 @@ -using System; -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; +using CryptoExchange.Net.Converters; namespace Kraken.Net.Objects.Models { diff --git a/Kraken.Net/Objects/Models/KrakenStakeResponse.cs b/Kraken.Net/Objects/Models/KrakenStakeResponse.cs deleted file mode 100644 index 79a30a3..0000000 --- a/Kraken.Net/Objects/Models/KrakenStakeResponse.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Kraken.Net.Objects.Models -{ - using Newtonsoft.Json; - - /// - /// Kraken's response to a staking request. - /// - public record KrakenStakeResponse - { - /// - /// Reference id which can be tracked back to a ledger entry corresponding to the - /// staked asset (e.g. DOT.S). - /// - [JsonProperty("refid")] - public string ReferenceId { get; set; } = null!; - } -} diff --git a/Kraken.Net/Objects/Models/KrakenStakingAsset.cs b/Kraken.Net/Objects/Models/KrakenStakingAsset.cs deleted file mode 100644 index 54033ff..0000000 --- a/Kraken.Net/Objects/Models/KrakenStakingAsset.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace Kraken.Net.Objects.Models -{ - using Newtonsoft.Json; - - /// - /// Represents an asset that can be staked by the user. - /// - public record KrakenStakingAsset - { - /// - /// Unique ID of the staking option (used in Stake/Unstake operations). - /// - [JsonProperty("method")] - public string Method { get; set; } = null!; - - /// - /// Asset code/name e.g. DOT. - /// - [JsonProperty("asset")] - public string Asset { get; set; } = null!; - - /// - /// Staking asset code/name e.g. DOT.S - /// - [JsonProperty("staking_asset")] - public string StakingAsset { get; set; } = null!; - - /// - /// Describes the rewards earned while staking. - /// - [JsonProperty("rewards")] - public KrakenStakingRewardInfo? Rewards { get; set; } - - /// - /// Whether the staking operation is on-chain or not. - /// - [JsonProperty("on_chain")] - public bool OnChain { get; set; } - - /// - /// Whether the user will be able to stake this asset. - /// - [JsonProperty("can_stake")] - public bool CanStake { get; set; } - - /// - /// Whether the user will be able to unstake this asset. - /// - [JsonProperty("can_unstake")] - public bool CanUnstake { get; set; } - - /// - /// Minimium amounts for staking/unstaking. - /// - [JsonProperty("minimum_amount")] - public KrakenStakingMinimumInfo? Minimums { get; set; } - } -} \ No newline at end of file diff --git a/Kraken.Net/Objects/Models/KrakenStakingMinimumInfo.cs b/Kraken.Net/Objects/Models/KrakenStakingMinimumInfo.cs deleted file mode 100644 index 09d5ad4..0000000 --- a/Kraken.Net/Objects/Models/KrakenStakingMinimumInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Kraken.Net.Objects.Models -{ - using Newtonsoft.Json; - - /// - /// Minimum amounts for staking/unstaking. - /// - public record KrakenStakingMinimumInfo - { - /// - /// The minimum amount of value that can be staked. - /// - [JsonProperty("staking")] - public decimal Staking { get; set; } - - /// - /// The minimum amount of value that can be unstaked. - /// - [JsonProperty("unstaking")] - public decimal Unstaking { get; set; } - } -} diff --git a/Kraken.Net/Objects/Models/KrakenStakingRewardInfo.cs b/Kraken.Net/Objects/Models/KrakenStakingRewardInfo.cs deleted file mode 100644 index 5756b19..0000000 --- a/Kraken.Net/Objects/Models/KrakenStakingRewardInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Kraken.Net.Objects.Models -{ - using Kraken.Net.Converters; - using Kraken.Net.Enums; - - using Newtonsoft.Json; - - /// - /// Describes the rewards earned while staking. - /// - public record KrakenStakingRewardInfo - { - /// - /// Reward earned while staking. - /// - [JsonProperty("reward")] - public string? Reward { get; set; } - - /// - /// The type of the reward e.g. "percentage". - /// - - [JsonProperty("type"), JsonConverter(typeof(StakingRewardTypeConverter))] - public StakingRewardType Type { get; set; } - } -} diff --git a/Kraken.Net/Objects/Models/KrakenStakingTransaction.cs b/Kraken.Net/Objects/Models/KrakenStakingTransaction.cs deleted file mode 100644 index 015ccd3..0000000 --- a/Kraken.Net/Objects/Models/KrakenStakingTransaction.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Kraken.Net.Objects.Models -{ - using System; - - using CryptoExchange.Net.Converters; - - using Kraken.Net.Converters; - using Kraken.Net.Enums; - - using Newtonsoft.Json; - - /// - /// Staking Transaction Info - /// - public record KrakenStakingTransaction - { - /// - /// Staking method as described by . - /// - public string Method { get; set; } = null!; - - /// - /// Asset code/name e.g. DOT - /// - [JsonProperty("asset")] - public string Asset { get; set; } = null!; - - /// - /// The reference ID of the transaction. - /// - [JsonProperty("refid")] - public string ReferenceId { get; set; } = null!; - - /// - /// The transaction amount. - /// - [JsonProperty("amount")] - public decimal Quantity { get; set; } - - /// - /// Transaction fee. - /// - [JsonProperty("fee")] - public decimal? Fee { get; set; } - - /// - /// Unix timestamp when the transaction was initiated. - /// - [JsonProperty("time"), JsonConverter(typeof(DateTimeConverter))] - public DateTime Timestamp { get; set; } - - /// - /// Transaction status. - /// - [JsonProperty("status"), JsonConverter(typeof(StakingTransactionStatusConverter))] - public StakingTransactionStatus Status { get; set; } - - /// - /// Transaction type. - /// - [JsonProperty("type"), JsonConverter(typeof(StakingTypeConverter))] - public StakingType Type { get; set; } - - /// - /// Unix timestamp from the start of bond period (applicable only to bonding transactions). - /// - [JsonProperty("bond_start"), JsonConverter(typeof(DateTimeConverter))] - public DateTime? BondStart { get; set; } - - /// - /// Unix timestamp from the start of bond period (applicable only to bonding transactions). - /// - [JsonProperty("bond_end"), JsonConverter(typeof(DateTimeConverter))] - public DateTime? BondEnd { get; set; } - } -} diff --git a/Kraken.Net/Objects/Models/KrakenSymbol.cs b/Kraken.Net/Objects/Models/KrakenSymbol.cs index 669410d..ebafb87 100644 --- a/Kraken.Net/Objects/Models/KrakenSymbol.cs +++ b/Kraken.Net/Objects/Models/KrakenSymbol.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using Kraken.Net.Enums; -using Newtonsoft.Json; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models { @@ -13,122 +10,124 @@ public record KrakenSymbol /// /// Alternative name /// - [JsonProperty("altname")] + [JsonPropertyName("altname")] public string AlternateName { get; set; } = string.Empty; /// /// Name to use for the socket client subscriptions /// - [JsonProperty("wsname")] + [JsonPropertyName("wsname")] public string WebsocketName { get; set; } = string.Empty; /// /// Class of the base asset /// - [JsonProperty("aclass_base")] + [JsonPropertyName("aclass_base")] public string BaseAssetClass { get; set; } = string.Empty; /// /// Name of the base asset /// - [JsonProperty("base")] + [JsonPropertyName("base")] public string BaseAsset { get; set; } = string.Empty; /// /// Class of the quote asset /// - [JsonProperty("aclass_quote")] + [JsonPropertyName("aclass_quote")] public string QuoteAssetClass { get; set; } = string.Empty; /// /// Name of the quote asset /// - [JsonProperty("quote")] + [JsonPropertyName("quote")] public string QuoteAsset { get; set; } = string.Empty; /// /// Lot size /// - [JsonProperty("lot")] + [JsonPropertyName("lot")] public string VolumeLotSize { get; set; } = string.Empty; /// /// Decimals of the symbol /// - [JsonProperty("pair_decimals")] + [JsonPropertyName("pair_decimals")] public int PriceDecimals { get; set; } /// /// Lot decimals /// - [JsonProperty("lot_decimals")] + [JsonPropertyName("lot_decimals")] public int LotDecimals { get; set; } /// /// Cost decimals /// - [JsonProperty("cost_decimals")] + [JsonPropertyName("cost_decimals")] public int CostDecimals { get; set; } /// /// Lot multiplier /// - [JsonProperty("lot_multiplier")] + [JsonPropertyName("lot_multiplier")] public decimal LotMultiplier { get; set; } /// /// Buy leverage amounts /// - [JsonProperty("leverage_buy")] + [JsonPropertyName("leverage_buy")] public IEnumerable LeverageBuy { get; set; } = Array.Empty(); /// /// Sell leverage amounts /// - [JsonProperty("leverage_sell")] + [JsonPropertyName("leverage_sell")] public IEnumerable LeverageSell { get; set; } = Array.Empty(); /// /// Fee structure /// + [JsonPropertyName("fees")] public IEnumerable Fees { get; set; } = Array.Empty(); /// /// Maker fee structure /// - [JsonProperty("fees_maker")] + [JsonPropertyName("fees_maker")] public IEnumerable FeesMaker { get; set; } = Array.Empty(); /// /// The asset the fee is deducted from /// - [JsonProperty("fee_volume_currency")] + [JsonPropertyName("fee_volume_currency")] public string FeeAsset { get; set; } = string.Empty; /// /// Margin call level /// - [JsonProperty("margin_call")] + [JsonPropertyName("margin_call")] public int MarginCall { get; set; } /// /// Stop-out/liquidation margin level /// - [JsonProperty("margin_stop")] + [JsonPropertyName("margin_stop")] public int MarginStop { get; set; } /// /// The minimum order volume for pair /// /// - [JsonProperty("ordermin")] + [JsonPropertyName("ordermin")] public decimal OrderMin { get; set; } /// /// The minimum value of an order /// - [JsonProperty("costmin")] + [JsonPropertyName("costmin")] public decimal? MinValue { get; set; } /// /// Tick size /// - [JsonProperty("tick_size")] + [JsonPropertyName("tick_size")] public decimal? TickSize { get; set; } /// /// Status /// + [JsonPropertyName("status")] public SymbolStatus Status { get; set; } /// /// Long position limit /// - [JsonProperty("long_position_limit")] + [JsonPropertyName("long_position_limit")] public long LongPositionLimit { get; set; } /// /// Short position limit /// - [JsonProperty("short_position_limit")] + [JsonPropertyName("short_position_limit")] public long ShortPositionLimit { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenSystemStatus.cs b/Kraken.Net/Objects/Models/KrakenSystemStatus.cs index 792dc27..c289114 100644 --- a/Kraken.Net/Objects/Models/KrakenSystemStatus.cs +++ b/Kraken.Net/Objects/Models/KrakenSystemStatus.cs @@ -1,7 +1,4 @@ -using System; -using Kraken.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models { @@ -13,11 +10,13 @@ public record KrakenSystemStatus /// /// Platform status /// - [JsonConverter(typeof(SystemStatusConverter))] + [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("status")] public SystemStatus Status { get; set; } /// /// Timestamp /// + [JsonPropertyName("timestamp")] public DateTime Timestamp { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenTick.cs b/Kraken.Net/Objects/Models/KrakenTick.cs index 4812191..583c8c3 100644 --- a/Kraken.Net/Objects/Models/KrakenTick.cs +++ b/Kraken.Net/Objects/Models/KrakenTick.cs @@ -1,5 +1,4 @@ using CryptoExchange.Net.Converters; -using Newtonsoft.Json; namespace Kraken.Net.Objects.Models { @@ -11,42 +10,42 @@ public record KrakenTick /// /// High price info /// - [JsonProperty("h")] + [JsonPropertyName("h")] public KrakenTickInfo High { get; set; } = default!; /// /// Low price info /// - [JsonProperty("l")] + [JsonPropertyName("l")] public KrakenTickInfo Low { get; set; } = default!; /// /// Last trade info /// - [JsonProperty("c")] + [JsonPropertyName("c")] public KrakenLastTrade LastTrade { get; set; } = default!; /// /// Best ask info /// - [JsonProperty("a")] + [JsonPropertyName("a")] public KrakenBestEntry BestAsks { get; set; } = default!; /// /// Best bid info /// - [JsonProperty("b")] + [JsonPropertyName("b")] public KrakenBestEntry BestBids { get; set; } = default!; /// /// Trade count info /// - [JsonProperty("t")] + [JsonPropertyName("t")] public KrakenTickInfo Trades { get; set; } = default!; /// /// Volume weighted average price info /// - [JsonProperty("p")] + [JsonPropertyName("p")] public KrakenTickInfo VolumeWeightedAveragePrice { get; set; } = default!; /// /// Volume info /// - [JsonProperty("v")] + [JsonPropertyName("v")] public KrakenTickInfo Volume { get; set; } = default!; } @@ -58,11 +57,12 @@ public record KrakenRestTick: KrakenTick /// /// Symbol /// + [JsonPropertyName("symbol")] public string Symbol { get; set; } = string.Empty; /// /// Open price /// - [JsonProperty("o")] + [JsonPropertyName("o")] public decimal OpenPrice { get; set; } } @@ -74,7 +74,7 @@ public record KrakenStreamTick : KrakenTick /// /// Open price info /// - [JsonProperty("o")] + [JsonPropertyName("o")] public KrakenTickInfo Open { get; set; } = default!; } diff --git a/Kraken.Net/Objects/Models/KrakenTrade.cs b/Kraken.Net/Objects/Models/KrakenTrade.cs index bd0ef23..36725ae 100644 --- a/Kraken.Net/Objects/Models/KrakenTrade.cs +++ b/Kraken.Net/Objects/Models/KrakenTrade.cs @@ -1,8 +1,5 @@ -using System; -using CryptoExchange.Net.Converters; -using Kraken.Net.Converters; +using CryptoExchange.Net.Converters; using Kraken.Net.Enums; -using Newtonsoft.Json; namespace Kraken.Net.Objects.Models { @@ -30,12 +27,12 @@ public record KrakenTrade /// /// Side /// - [ArrayProperty(3), JsonConverter(typeof(OrderSideConverter))] + [ArrayProperty(3), JsonConverter(typeof(EnumConverter))] public OrderSide Side { get; set; } /// /// Order type /// - [ArrayProperty(4), JsonConverter(typeof(OrderTypeMinimalConverter))] + [ArrayProperty(4), JsonConverter(typeof(EnumConverter))] public OrderTypeMinimal Type { get; set; } /// diff --git a/Kraken.Net/Objects/Models/KrakenTradeBalance.cs b/Kraken.Net/Objects/Models/KrakenTradeBalance.cs index 7eca9bc..29f36ad 100644 --- a/Kraken.Net/Objects/Models/KrakenTradeBalance.cs +++ b/Kraken.Net/Objects/Models/KrakenTradeBalance.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Trade balance info @@ -10,47 +8,47 @@ public record KrakenTradeBalance /// /// Combined balance /// - [JsonProperty("eb")] + [JsonPropertyName("eb")] public decimal CombinedBalance { get; set; } /// /// Trade balance /// - [JsonProperty("tb")] + [JsonPropertyName("tb")] public decimal TradeBalance { get; set; } /// /// Margin open positions /// - [JsonProperty("m")] + [JsonPropertyName("m")] public decimal MarginOpenPositions { get; set; } /// /// Unrealized net profit in open positions /// - [JsonProperty("n")] + [JsonPropertyName("n")] public decimal OpenPositionsUnrealizedNetProfit { get; set; } /// /// Cost basis for open positions /// - [JsonProperty("c")] + [JsonPropertyName("c")] public decimal OpenPositionsCostBasis { get; set; } /// /// Open positions valuation /// - [JsonProperty("v")] + [JsonPropertyName("v")] public decimal OpenPositionsValuation { get; set; } /// /// Equity /// - [JsonProperty("e")] + [JsonPropertyName("e")] public decimal Equity { get; set; } /// /// Free margin /// - [JsonProperty("mf")] + [JsonPropertyName("mf")] public decimal FreeMargin { get; set; } /// /// Margin level /// - [JsonProperty("ml")] + [JsonPropertyName("ml")] public decimal MarginLevel { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenTradeVolume.cs b/Kraken.Net/Objects/Models/KrakenTradeVolume.cs index eacfbd4..e109e53 100644 --- a/Kraken.Net/Objects/Models/KrakenTradeVolume.cs +++ b/Kraken.Net/Objects/Models/KrakenTradeVolume.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Trade volume info @@ -11,21 +8,23 @@ public record KrakenTradeVolume /// /// Asset /// - [JsonProperty("currency")] + [JsonPropertyName("currency")] public string Asset { get; set; } = string.Empty; /// /// Volume /// + [JsonPropertyName("volume")] public decimal Volume { get; set; } /// /// Fees structure /// + [JsonPropertyName("fees")] public Dictionary Fees { get; set; } = new Dictionary(); /// /// Maker fees structure /// - [JsonProperty("fees_maker")] + [JsonPropertyName("fees_maker")] public Dictionary MakerFees { get; set; } = new Dictionary(); } @@ -37,28 +36,32 @@ public record KrakenFeeStruct /// /// Fee /// + [JsonPropertyName("fee")] public decimal Fee { get; set; } /// /// Minimal fee /// - [JsonProperty("minfee")] + [JsonPropertyName("minfee")] public decimal MinimalFee { get; set; } /// /// Maximal fee /// - [JsonProperty("maxfee")] + [JsonPropertyName("maxfee")] public decimal MaximumFee { get; set; } /// /// Next fee /// + [JsonPropertyName("nextFee")] public decimal? NextFee { get; set; } /// /// Next volume /// + [JsonPropertyName("nextVolume")] public decimal? NextVolume { get; set; } /// /// Tier volume /// + [JsonPropertyName("tierVolume")] public decimal TierVolume { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenUnstakeResponse.cs b/Kraken.Net/Objects/Models/KrakenUnstakeResponse.cs deleted file mode 100644 index 30d2dec..0000000 --- a/Kraken.Net/Objects/Models/KrakenUnstakeResponse.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Kraken.Net.Objects.Models -{ - using Newtonsoft.Json; - - /// - /// Kraken's response to an unstaking request. - /// - public record KrakenUnstakeResponse - { - /// - /// Reference id which can be tracked back to a ledger entry corresponding to the - /// unstaked asset (e.g. DOT.S). - /// - [JsonProperty("refid")] - public string ReferenceId { get; set; } = null!; - } -} diff --git a/Kraken.Net/Objects/Models/KrakenUserTrade.cs b/Kraken.Net/Objects/Models/KrakenUserTrade.cs index e89ca9a..fe6b83b 100644 --- a/Kraken.Net/Objects/Models/KrakenUserTrade.cs +++ b/Kraken.Net/Objects/Models/KrakenUserTrade.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using CryptoExchange.Net.Converters; -using Kraken.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models { @@ -15,13 +10,13 @@ public record KrakenUserTrade /// /// Order id /// - [JsonProperty("ordertxid")] + [JsonPropertyName("ordertxid")] public string OrderId { get; set; } = string.Empty; /// /// Pos id /// - [JsonProperty("postxid")] + [JsonPropertyName("postxid")] public string PosId { get; set; } = string.Empty; /// @@ -33,94 +28,99 @@ public record KrakenUserTrade /// /// Symbol /// - [JsonProperty("pair")] + [JsonPropertyName("pair")] public string Symbol { get; set; } = string.Empty; /// /// Timestamp of trade /// - [JsonProperty("time"), JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("time"), JsonConverter(typeof(DateTimeConverter))] public DateTime Timestamp { get; set; } /// /// Side /// - [JsonProperty("type"), JsonConverter(typeof(OrderSideConverter))] + [JsonPropertyName("type"), JsonConverter(typeof(EnumConverter))] public OrderSide Side { get; set; } /// /// Order type /// - [JsonProperty("ordertype"), JsonConverter(typeof(OrderTypeConverter))] + [JsonPropertyName("ordertype"), JsonConverter(typeof(EnumConverter))] public OrderType Type { get; set; } /// /// Price of the trade /// + [JsonPropertyName("price")] public decimal Price { get; set; } /// /// Cost of the trade /// - [JsonProperty("cost")] + [JsonPropertyName("cost")] public decimal QuoteQuantity { get; set; } /// /// Fee paid for trade /// + [JsonPropertyName("fee")] public decimal Fee { get; set; } /// /// Quantity of the trade /// - [JsonProperty("vol")] + [JsonPropertyName("vol")] public decimal Quantity { get; set; } /// /// Margin /// + [JsonPropertyName("margin")] public decimal Margin { get; set; } /// /// Misc info /// + [JsonPropertyName("misc")] public string Misc { get; set; } = string.Empty; /// /// Position status /// - [JsonProperty("posstatus")] + [JsonPropertyName("posstatus")] public string PositionStatus { get; set; } = string.Empty; /// /// Closed average price /// - [JsonProperty("cprice")] + [JsonPropertyName("cprice")] public decimal? ClosedAveragePrice { get; set; } /// /// Closed cost /// - [JsonProperty("ccost")] + [JsonPropertyName("ccost")] public decimal? ClosedCost { get; set; } /// /// Closed fee /// - [JsonProperty("cfee")] + [JsonPropertyName("cfee")] public decimal? ClosedFee { get; set; } /// /// Closed quantity /// - [JsonProperty("cvol")] + [JsonPropertyName("cvol")] public decimal? ClosedQuantity { get; set; } /// /// Closed margin /// - [JsonProperty("cmargin")] + [JsonPropertyName("cmargin")] public decimal? ClosedMargin { get; set; } /// /// Closed net profit/loss /// - [JsonProperty("net")] + [JsonPropertyName("net")] public decimal? ClosedProfitLoss { get; set; } /// /// True if trade was executed with user as maker /// - [JsonProperty("maker")] + [JsonPropertyName("maker")] public bool Maker { get; set; } /// /// Trade ids /// + [JsonPropertyName("trades")] public IEnumerable Trades { get; set; } = Array.Empty(); } @@ -132,6 +132,7 @@ public record KrakenStreamUserTrade: KrakenUserTrade /// /// The update sequence number /// + [JsonPropertyName("sequenceNumber")] public int SequenceNumber { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenWithdraw.cs b/Kraken.Net/Objects/Models/KrakenWithdraw.cs index 8ca7937..9385953 100644 --- a/Kraken.Net/Objects/Models/KrakenWithdraw.cs +++ b/Kraken.Net/Objects/Models/KrakenWithdraw.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Order info @@ -10,7 +8,7 @@ public record KrakenWithdraw /// /// Reference id /// - [JsonProperty("refid")] + [JsonPropertyName("refid")] public string ReferenceId { get; set; } = string.Empty; } } \ No newline at end of file diff --git a/Kraken.Net/Objects/Models/KrakenWithdrawAddress.cs b/Kraken.Net/Objects/Models/KrakenWithdrawAddress.cs index 7b0db30..85ada77 100644 --- a/Kraken.Net/Objects/Models/KrakenWithdrawAddress.cs +++ b/Kraken.Net/Objects/Models/KrakenWithdrawAddress.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Info about a withdraw address @@ -10,22 +8,27 @@ public record KrakenWithdrawAddress /// /// The actual address /// + [JsonPropertyName("address")] public string Address { get; set; } = string.Empty; /// /// Name of the asset /// + [JsonPropertyName("asset")] public string Asset { get; set; } = string.Empty; /// /// Name of the method /// + [JsonPropertyName("method")] public string Method { get; set; } = string.Empty; /// /// Key /// + [JsonPropertyName("key")] public string Key { get; set; } = string.Empty; /// /// Verified indicator /// + [JsonPropertyName("verified")] public bool Verified { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenWithdrawInfo.cs b/Kraken.Net/Objects/Models/KrakenWithdrawInfo.cs index 62e8101..dbb0e7e 100644 --- a/Kraken.Net/Objects/Models/KrakenWithdrawInfo.cs +++ b/Kraken.Net/Objects/Models/KrakenWithdrawInfo.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Withdraw info @@ -10,19 +8,22 @@ public record KrakenWithdrawInfo /// /// Method that will be used /// + [JsonPropertyName("method")] public string Method { get; set; } = string.Empty; /// /// Limit to what can be withdrawn right now /// + [JsonPropertyName("limit")] public decimal Limit { get; set; } /// /// Quantity that will be send, after fees /// - [JsonProperty("amount")] + [JsonPropertyName("amount")] public decimal Quantity { get; set; } /// /// Fee that will be paid /// + [JsonPropertyName("fee")] public decimal Fee { get; set; } } } diff --git a/Kraken.Net/Objects/Models/KrakenWithdrawMethod.cs b/Kraken.Net/Objects/Models/KrakenWithdrawMethod.cs index a4a20d8..fad2eb9 100644 --- a/Kraken.Net/Objects/Models/KrakenWithdrawMethod.cs +++ b/Kraken.Net/Objects/Models/KrakenWithdrawMethod.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Models +namespace Kraken.Net.Objects.Models { /// /// Info about a withdraw method @@ -10,18 +8,22 @@ public record KrakenWithdrawMethod /// /// Name of the asset /// + [JsonPropertyName("asset")] public string Asset { get; set; } = string.Empty; /// /// Name of the method /// + [JsonPropertyName("method")] public string Method { get; set; } = string.Empty; /// /// Name of the Network /// + [JsonPropertyName("network")] public string Network { get; set; } = string.Empty; /// /// Minimum amount /// + [JsonPropertyName("minimum")] public decimal Minimum { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Socket/Futures/KrakenBalanceUpdate.cs b/Kraken.Net/Objects/Models/Socket/Futures/KrakenBalanceUpdate.cs new file mode 100644 index 0000000..498a304 --- /dev/null +++ b/Kraken.Net/Objects/Models/Socket/Futures/KrakenBalanceUpdate.cs @@ -0,0 +1,73 @@ +using Kraken.Net.Enums; + +namespace Kraken.Net.Objects.Models.Socket.Futures +{ + /// + /// Balance update + /// + public record KrakenBalanceUpdate + { + /// + /// Ledger id + /// + [JsonPropertyName("ledger_id")] + public string LedgerId { get; set; } = string.Empty; + /// + /// Reference id + /// + [JsonPropertyName("ref_id")] + public string ReferenceId { get; set; } = string.Empty; + /// + /// Timestamp + /// + [JsonPropertyName("timestamp")] + public DateTime Timestamp { get; set; } + /// + /// Type + /// + [JsonPropertyName("type")] + public BalanceUpdateType BalanceUpdateType { get; set; } + /// + /// Asset + /// + [JsonPropertyName("asset")] + public string Asset { get; set; } = string.Empty; + /// + /// Asset class + /// + [JsonPropertyName("asset_class")] + public string AssetClass { get; set; } = string.Empty; + /// + /// Category + /// + [JsonPropertyName("category")] + public BalanceUpdateCategory Category { get; set; } + /// + /// Wallet type + /// + [JsonPropertyName("wallet_type")] + public WalletType WalletType { get; set; } + /// + /// Wallet id + /// + [JsonPropertyName("wallet_id")] + public string WalletId { get; set; } = string.Empty; + /// + /// Quantity + /// + [JsonPropertyName("amount")] + public decimal Quantity { get; set; } + /// + /// Fee + /// + [JsonPropertyName("fee")] + public decimal Fee { get; set; } + /// + /// Balance + /// + [JsonPropertyName("balance")] + public decimal Balance { get; set; } + } + + +} diff --git a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesAccountLogsUpdate.cs b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesAccountLogsUpdate.cs index 38b3382..957a33d 100644 --- a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesAccountLogsUpdate.cs +++ b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesAccountLogsUpdate.cs @@ -1,7 +1,4 @@ using Kraken.Net.Objects.Models.Futures; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; namespace Kraken.Net.Objects.Models.Socket.Futures { @@ -13,6 +10,7 @@ public record KrakenFuturesAccountLogsSnapshotUpdate : KrakenFuturesSocketMessag /// /// Account logs /// + [JsonPropertyName("logs")] public IEnumerable Logs { get; set; } = Array.Empty(); } @@ -24,7 +22,7 @@ public record KrakenFuturesAccountLogsUpdate : KrakenFuturesSocketMessage /// /// New entry /// - [JsonProperty("new_entry")] + [JsonPropertyName("new_entry")] public KrakenAccountLog NewEntry { get; set; } = null!; } } diff --git a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesBalances.cs b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesBalances.cs index da94c0e..f27a1de 100644 --- a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesBalances.cs +++ b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesBalances.cs @@ -1,8 +1,4 @@ -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - + namespace Kraken.Net.Objects.Models.Socket.Futures { /// @@ -13,29 +9,33 @@ public record KrakenFuturesBalancesUpdate : KrakenFuturesSocketMessage /// /// Account id /// + [JsonPropertyName("account")] public string Account { get; set; } = string.Empty; /// /// Sequence /// - [JsonProperty("seq")] + [JsonPropertyName("seq")] public long Sequence { get; set; } /// /// Timestamp /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("timestamp")] public DateTime Timestamp { get; set; } /// /// Holdings /// + [JsonPropertyName("holding")] public Dictionary? Holdings { get; set; } /// /// Futures balances /// + [JsonPropertyName("futures")] public Dictionary? Futures { get; set; } /// /// Flex futures /// - [JsonProperty("flex_futures")] + [JsonPropertyName("flex_futures")] public KrakenFlexFutures? FlexFutures { get; set; } } @@ -47,74 +47,77 @@ public record KrakenFlexFutures /// /// A map from collateral wallet names to collateral wallet structure /// + [JsonPropertyName("currencies")] public Dictionary? Currencies { get; set; } /// /// The current margin information for isolated position(s) /// + [JsonPropertyName("isolated")] public Dictionary? Isolated { get; set; } /// /// The current margin information for cross position(s) /// + [JsonPropertyName("cross")] public KrakenFlexCrossBalance? Cross { get; set; } /// /// The current USD balance of the account /// - [JsonProperty("balance_value")] + [JsonPropertyName("balance_value")] public decimal BalanceValue { get; set; } /// /// The current collateral value with unrealized margin from any open positions /// - [JsonProperty("portfolio_value")] + [JsonPropertyName("portfolio_value")] public decimal PortfolioValue { get; set; } /// /// The current USD balance with haircuts /// - [JsonProperty("collateral_value")] + [JsonPropertyName("collateral_value")] public decimal CollateralValue { get; set; } /// /// The total initial margin for open positions and open orders /// - [JsonProperty("initial_margin")] + [JsonPropertyName("initial_margin")] public decimal InitialMargin { get; set; } /// /// The total initial margin for open positions /// - [JsonProperty("initial_margin_without_orders")] + [JsonPropertyName("initial_margin_without_orders")] public decimal InitialMarginWithoutOrders { get; set; } /// /// The total maintenance margin for open positions /// - [JsonProperty("maintenance_margin")] + [JsonPropertyName("maintenance_margin")] public decimal MaintenanceMargin { get; set; } /// /// The total profit and loss for open positions /// - [JsonProperty("pnl")] + [JsonPropertyName("pnl")] public decimal ProfitAndLoss { get; set; } /// /// The total unrealized funding for open positions /// - [JsonProperty("unrealized_funding")] + [JsonPropertyName("unrealized_funding")] public decimal UnrealizedFunding { get; set; } /// /// The total unrealized funding and pnl /// - [JsonProperty("total_unrealized")] + [JsonPropertyName("total_unrealized")] public decimal TotalUnrealized { get; set; } /// /// The total unrealized in USD /// - [JsonProperty("total_unrealized_as_margin")] + [JsonPropertyName("total_unrealized_as_margin")] public decimal TotalUnrealizedAsMargin { get; set; } /// /// The current collateral value and unrealized margin /// - [JsonProperty("margin_equity")] + [JsonPropertyName("margin_equity")] public decimal MarginEquity { get; set; } /// /// The margin equity minus initial margin /// - [JsonProperty("available_margin")] + [JsonPropertyName("available_margin")] public decimal AvailableMargin { get; set; } } @@ -126,67 +129,67 @@ public record KrakenFlexCrossBalance /// /// The total initial margin for open positions and open orders /// - [JsonProperty("initial_margin")] + [JsonPropertyName("initial_margin")] public decimal InitialMargin { get; set; } /// /// The total initial margin for open positions /// - [JsonProperty("initial_margin_without_orders")] + [JsonPropertyName("initial_margin_without_orders")] public decimal InitialMarginWithoutOrders { get; set; } /// /// The total maintenance margin for open positions /// - [JsonProperty("maintenance_margin")] + [JsonPropertyName("maintenance_margin")] public decimal MaintenanceMargin { get; set; } /// /// The total profit and loss for open positions /// - [JsonProperty("pnl")] + [JsonPropertyName("pnl")] public decimal ProfitAndLoss { get; set; } /// /// The total unrealized funding for open positions /// - [JsonProperty("unrealized_funding")] + [JsonPropertyName("unrealized_funding")] public decimal UnrealizedFunding { get; set; } /// /// The total unrealized funding and pnl /// - [JsonProperty("total_unrealized")] + [JsonPropertyName("total_unrealized")] public decimal TotalUnrealized { get; set; } /// /// The total unrealized in USD /// - [JsonProperty("total_unrealized_as_margin")] + [JsonPropertyName("total_unrealized_as_margin")] public decimal TotalUnrealizedAsMargin { get; set; } /// /// The current USD balance of the account /// - [JsonProperty("balance_value")] + [JsonPropertyName("balance_value")] public decimal BalanceValue { get; set; } /// /// The current collateral value with unrealized margin from any open positions /// - [JsonProperty("portfolio_value")] + [JsonPropertyName("portfolio_value")] public decimal PortfolioValue { get; set; } /// /// The current USD balance with haircuts /// - [JsonProperty("collateral_value")] + [JsonPropertyName("collateral_value")] public decimal CollateralValue { get; set; } /// /// The current collateral value and unrealized margin /// - [JsonProperty("margin_equity")] + [JsonPropertyName("margin_equity")] public decimal MarginEquity { get; set; } /// /// The margin equity minus initial margin /// - [JsonProperty("available_margin")] + [JsonPropertyName("available_margin")] public decimal AvailableMargin { get; set; } /// /// Ratio of position size to margin equity /// - [JsonProperty("effective_leverage")] + [JsonPropertyName("effective_leverage")] public decimal EffectiveLeverage { get; set; } } @@ -198,37 +201,37 @@ public record KrakenFlexIsolatedBalance /// /// The total initial margin for open positions and open orders /// - [JsonProperty("initial_margin")] + [JsonPropertyName("initial_margin")] public decimal InitialMargin { get; set; } /// /// The total initial margin for open positions /// - [JsonProperty("initial_margin_without_orders")] + [JsonPropertyName("initial_margin_without_orders")] public decimal InitialMarginWithoutOrders { get; set; } /// /// The total maintenance margin for open positions /// - [JsonProperty("maintenance_margin")] + [JsonPropertyName("maintenance_margin")] public decimal MaintenanceMargin { get; set; } /// /// The total profit and loss for open positions /// - [JsonProperty("pnl")] + [JsonPropertyName("pnl")] public decimal ProfitAndLoss { get; set; } /// /// The total unrealized funding for open positions /// - [JsonProperty("unrealized_funding")] + [JsonPropertyName("unrealized_funding")] public decimal UnrealizedFunding { get; set; } /// /// The total unrealized funding and pnl /// - [JsonProperty("total_unrealized")] + [JsonPropertyName("total_unrealized")] public decimal TotalUnrealized { get; set; } /// /// The total unrealized in USD /// - [JsonProperty("total_unrealized_as_margin")] + [JsonPropertyName("total_unrealized_as_margin")] public decimal TotalUnrealizedAsMargin { get; set; } } @@ -240,28 +243,32 @@ public record KrakenFlexFuturesCurrency /// /// The currency quantity /// + [JsonPropertyName("quantity")] public decimal Quantity { get; set; } /// /// The USD value of the currency balance /// + [JsonPropertyName("value")] public decimal Value { get; set; } /// /// The current USD balance with haircuts /// - [JsonProperty("collateral_value")] + [JsonPropertyName("collateral_value")] public decimal CollateralValue { get; set; } /// /// The total available margin valued in the wallet currency /// + [JsonPropertyName("available")] public decimal Available { get; set; } /// /// The rate of reduction in the value of a collateral asset that may be used as margin /// + [JsonPropertyName("haircut")] public decimal Haircut { get; set; } /// /// The conversion spread is used to calculate conversion fee for multi-collateral wallets /// - [JsonProperty("conversion_spread")] + [JsonPropertyName("conversion_spread")] public decimal ConversionSpread { get; set; } } @@ -273,47 +280,52 @@ public record KrakenFutureBalance /// /// The name of the account /// + [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; /// /// The wallet currency pair /// + [JsonPropertyName("pair")] public string Pair { get; set; } = string.Empty; /// /// The wallet settlement unit /// + [JsonPropertyName("unit")] public string Unit { get; set; } = string.Empty; /// /// The current balance with haircuts and any unrealized margin from open positions in settlement units /// - [JsonProperty("portfolio_value")] + [JsonPropertyName("portfolio_value")] public decimal PortfolioValue { get; set; } /// /// The current balance in settlement units /// + [JsonPropertyName("balance")] public decimal Balance { get; set; } /// /// The maintenance margin for open positions /// - [JsonProperty("maintenance_margin")] + [JsonPropertyName("maintenance_margin")] public decimal MaintenanceMargin { get; set; } /// /// The initial margin for open positions and open orders /// - [JsonProperty("initial_margin")] + [JsonPropertyName("initial_margin")] public decimal InitialMargin { get; set; } /// /// The current portfolio value minus initial margin /// + [JsonPropertyName("available")] public decimal Available { get; set; } /// /// The total unrealized funding for open positions /// - [JsonProperty("unrealized_funding")] + [JsonPropertyName("unrealized_funding")] public decimal UnrealizedFunding { get; set; } /// /// The total profit and loss for open positions /// - [JsonProperty("pnl")] + [JsonPropertyName("pnl")] public decimal ProfitAndLoss { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesHeartbeatUpdate.cs b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesHeartbeatUpdate.cs index 12bcba7..7ad6ab7 100644 --- a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesHeartbeatUpdate.cs +++ b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesHeartbeatUpdate.cs @@ -1,7 +1,4 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Objects.Sockets; -using Newtonsoft.Json; -using System; +using Kraken.Net.Objects.Sockets; namespace Kraken.Net.Objects.Models.Socket.Futures { @@ -13,7 +10,7 @@ public record KrakenFuturesHeartbeatUpdate : KrakenFuturesEvent /// /// Timestamp /// - [JsonProperty("time")] + [JsonPropertyName("time")] [JsonConverter(typeof(DateTimeConverter))] public DateTime Timestamp { get; set; } } diff --git a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesMiniTickerUpdate.cs b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesMiniTickerUpdate.cs index 28181a5..bdb1da9 100644 --- a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesMiniTickerUpdate.cs +++ b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesMiniTickerUpdate.cs @@ -1,7 +1,4 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Objects.Sockets; -using Newtonsoft.Json; -using System; +using Kraken.Net.Objects.Sockets; namespace Kraken.Net.Objects.Models.Socket.Futures { @@ -13,43 +10,48 @@ public record KrakenFuturesMiniTickerUpdate : KrakenFuturesEvent /// /// The best current bid price /// - [JsonProperty("bid")] + [JsonPropertyName("bid")] public decimal BestBidPrice { get; set; } /// /// The best current ask price /// - [JsonProperty("ask")] + [JsonPropertyName("ask")] public decimal BestAskPrice { get; set; } /// /// The sum of the sizes of all fills observed in the last 24 hours /// - [JsonProperty("volume")] + [JsonPropertyName("volume")] public decimal Volume { get; set; } /// /// The premium associated with the symbol /// + [JsonPropertyName("premium")] public decimal Premium { get; set; } /// /// The 24h change in price /// - [JsonProperty("change")] + [JsonPropertyName("change")] public decimal ChangePercentage24h { get; set; } /// /// Currently can be perpetual, month or quarter. Other tags may be added without notice. /// + [JsonPropertyName("tag")] public string Tag { get; set; } = string.Empty; /// /// The currency pair of the symbol /// + [JsonPropertyName("pair")] public string Pair { get; set; } = string.Empty; /// /// The market price of the symbol /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("maturityTime")] public DateTime? MaturityTime { get; set; } /// /// The same as volume except that, for multi-collateral futures, it is converted to the non-base currency /// + [JsonPropertyName("volumeQuote")] public decimal VolumeQuote { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesNotifcation.cs b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesNotifcation.cs index 5c33a4e..425e986 100644 --- a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesNotifcation.cs +++ b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesNotifcation.cs @@ -1,6 +1,4 @@ using Kraken.Net.Objects.Models.Futures; -using System; -using System.Collections.Generic; namespace Kraken.Net.Objects.Models.Socket.Futures { @@ -12,6 +10,7 @@ public record KrakenFuturesNotificationUpdate : KrakenFuturesSocketMessage /// /// Notifications /// + [JsonPropertyName("notifications")] public IEnumerable Notifications { get; set; } = Array.Empty(); } @@ -23,6 +22,7 @@ public record KrakenFuturesNotifcation : KrakenFuturesPlatfromNotification /// /// Notification id /// + [JsonPropertyName("id")] public int Id { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesOpenPosition.cs b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesOpenPosition.cs index 458a020..3ed34ac 100644 --- a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesOpenPosition.cs +++ b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesOpenPosition.cs @@ -1,8 +1,4 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Socket.Futures +namespace Kraken.Net.Objects.Models.Socket.Futures { /// /// Open positions update @@ -12,11 +8,13 @@ public record KrakenFuturesOpenPositionUpdate : KrakenFuturesSocketMessage /// /// Account /// + [JsonPropertyName("account")] public string Account { get; set; } = string.Empty; /// /// Open positions /// + [JsonPropertyName("positions")] public IEnumerable Positions { get; set; } = Array.Empty(); } @@ -28,46 +26,47 @@ public record KrakenFuturesOpenPosition /// /// The symbol /// - [JsonProperty("instrument")] + [JsonPropertyName("instrument")] public string Symbol { get; set; } = string.Empty; /// /// The size of the position. /// + [JsonPropertyName("balance")] public decimal Balance { get; set; } /// /// The average entry price of the symbol. /// - [JsonProperty("entry_price")] + [JsonPropertyName("entry_price")] public decimal EntryPrice { get; set; } /// /// The market price of the position symbol. /// - [JsonProperty("mark_price")] + [JsonPropertyName("mark_price")] public decimal MarkPrice { get; set; } /// /// The index price of the position symbol. /// - [JsonProperty("index_price")] + [JsonPropertyName("index_price")] public decimal IndexPrice { get; set; } /// /// The profit and loss of the position. /// - [JsonProperty("pnl")] + [JsonPropertyName("pnl")] public decimal ProfitAndLoss { get; set; } /// /// The mark price of the contract at which the position will be liquidated. /// - [JsonProperty("liquidation_threshold")] + [JsonPropertyName("liquidation_threshold")] public decimal LiquidationThreshold { get; set; } /// /// The percentage gain or loss relative to the initial margin used in the position. Formula: PnL/IM /// - [JsonProperty("return_on_equity")] + [JsonPropertyName("return_on_equity")] public decimal ReturnOnEquity { get; set; } /// /// How leveraged the net position is in a given margin account. Formula: Position Value at Market / Portfolio Value. /// - [JsonProperty("effective_leverage")] + [JsonPropertyName("effective_leverage")] public decimal EffectiveLeverage { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesSnapshotBook.cs b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesSnapshotBook.cs index a1d791c..01f1e6d 100644 --- a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesSnapshotBook.cs +++ b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesSnapshotBook.cs @@ -1,9 +1,4 @@ -using CryptoExchange.Net.Converters; -using CryptoExchange.Net.Interfaces; -using Kraken.Net.Enums; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models.Socket.Futures { @@ -16,20 +11,23 @@ public record KrakenFuturesBookSnapshotUpdate : KrakenFuturesUpdateMessage /// Timestamp /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("timestamp")] public DateTime Timestamp { get; set; } /// /// Sequence number /// - [JsonProperty("seq")] + [JsonPropertyName("seq")] public long Sequence { get; set; } /// /// List of asks /// + [JsonPropertyName("asks")] public IEnumerable Asks { get; set; } = Array.Empty(); /// /// List of bids /// + [JsonPropertyName("bids")] public IEnumerable Bids { get; set; } = Array.Empty(); } @@ -41,11 +39,12 @@ public record KrakenFuturesOrderBookEntry : ISymbolOrderBookEntry /// /// Quantity /// - [JsonProperty("qty")] + [JsonPropertyName("qty")] public decimal Quantity { get; set; } /// /// Price /// + [JsonPropertyName("price")] public decimal Price { get; set; } } @@ -58,25 +57,28 @@ public record KrakenFuturesBookUpdate : KrakenFuturesUpdateMessage, ISymbolOrder /// Timestamp /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("timestamp")] public DateTime Timestamp { get; set; } /// /// Sequence number /// - [JsonProperty("seq")] + [JsonPropertyName("seq")] public long Sequence { get; set; } /// /// Side /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("side")] public OrderSide Side { get; set; } /// /// Quantity /// - [JsonProperty("qty")] + [JsonPropertyName("qty")] public decimal Quantity { get; set; } /// /// Price /// + [JsonPropertyName("price")] public decimal Price { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesSnapshotOpenOrders.cs b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesSnapshotOpenOrders.cs index a4ceaf1..f79dc08 100644 --- a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesSnapshotOpenOrders.cs +++ b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesSnapshotOpenOrders.cs @@ -1,8 +1,4 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models.Socket.Futures { @@ -14,10 +10,12 @@ public record KrakenFuturesOpenOrdersSnapshotUpdate : KrakenFuturesUpdateMessage /// /// Account id /// + [JsonPropertyName("account")] public string Account { get; set; } = string.Empty; /// /// Current open orders /// + [JsonPropertyName("orders")] public IEnumerable Orders { get; set; } = Array.Empty(); } @@ -29,20 +27,22 @@ public record KrakenFuturesOpenOrdersUpdate : KrakenFuturesUpdateMessage /// /// Is cancel /// - [JsonProperty("is_cancel")] + [JsonPropertyName("is_cancel")] public bool IsCancel { get; set; } /// /// Reason /// + [JsonPropertyName("reason")] public string Reason { get; set; } = string.Empty; /// /// Reason /// - [JsonProperty("order_id")] + [JsonPropertyName("order_id")] public string OrderId { get; set; } = string.Empty; /// /// Order info /// + [JsonPropertyName("order")] public KrakenFuturesSocketOpenOrder? Order { get; set; } = null!; } @@ -54,75 +54,76 @@ public record KrakenFuturesSocketOpenOrder /// /// Symbol /// - [JsonProperty("instrument")] + [JsonPropertyName("instrument")] public string Symbol { get; set; } = string.Empty; /// /// Timestamp /// - [JsonProperty("time")] + [JsonPropertyName("time")] [JsonConverter(typeof(DateTimeConverter))] public DateTime Timestamp { get; set; } /// /// /// - [JsonProperty("last_update_time")] + [JsonPropertyName("last_update_time")] [JsonConverter(typeof(DateTimeConverter))] public DateTime LastUpdateTime { get; set; } /// /// Quantitiy /// - [JsonProperty("qty")] + [JsonPropertyName("qty")] public decimal Quantity { get; set; } /// /// Filled quantity /// - [JsonProperty("filled")] + [JsonPropertyName("filled")] public decimal QuantityFilled { get; set; } /// /// Limit price /// - [JsonProperty("limit_price")] + [JsonPropertyName("limit_price")] public decimal Price { get; set; } /// /// Stop price /// - [JsonProperty("stop_price")] + [JsonPropertyName("stop_price")] public decimal? StopPrice { get; set; } /// /// Order type /// - [JsonProperty("type")] - public string Type { get; set; } = string.Empty; + [JsonPropertyName("type")] + public FuturesOrderType Type { get; set; } /// /// Order id /// - [JsonProperty("order_id")] + [JsonPropertyName("order_id")] public string OrderId { get; set; } = string.Empty; /// /// Client order id /// - [JsonProperty("cli_ord_id")] + [JsonPropertyName("cli_ord_id")] public string? ClientOrderId { get; set; } /// /// Side /// - [JsonProperty("direction")] + [JsonPropertyName("direction")] [JsonConverter(typeof(EnumConverter))] public OrderSide Side { get; set; } /// /// Reduce only /// - [JsonProperty("reduce_only")] + [JsonPropertyName("reduce_only")] public bool ReduceOnly { get; set; } /// /// Trigger signal /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("triggerSignal")] public TriggerSignal? TriggerSignal { get; set; } /// /// Trailing stop options /// - [JsonProperty("trailing_stop_options")] + [JsonPropertyName("trailing_stop_options")] public KrakenFuturesTrailingStopOptions? TrailingStopOptions { get; set; } } @@ -134,12 +135,13 @@ public record KrakenFuturesTrailingStopOptions /// /// Max deviation /// - [JsonProperty("max_deviation")] + [JsonPropertyName("max_deviation")] public decimal? MaxDeviation { get; set; } /// /// Unit /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("unit")] public TrailingStopDeviationUnit Unit { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesSocketMessage.cs b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesSocketMessage.cs index 3e3c31c..fca5fb5 100644 --- a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesSocketMessage.cs +++ b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesSocketMessage.cs @@ -1,7 +1,4 @@ -using Newtonsoft.Json; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Models.Socket.Futures +namespace Kraken.Net.Objects.Models.Socket.Futures { /// /// Socket update @@ -11,33 +8,33 @@ public record KrakenFuturesSocketMessage /// /// The event type /// - [JsonProperty("event")] + [JsonPropertyName("event")] public string Event { get; set; } = string.Empty; /// /// The feed /// - [JsonProperty("feed")] + [JsonPropertyName("feed")] public string Feed { get; set; } = string.Empty; /// /// Error if any /// - [JsonProperty("error")] + [JsonPropertyName("error")] public string? Error { get; set; } } internal record KrakenFuturesSubscribeMessage : KrakenFuturesSocketMessage { - [JsonProperty("product_ids")] + [JsonPropertyName("product_ids")] public List? Symbols { get; set; } } internal record KrakenFuturesSubscribeAuthMessage : KrakenFuturesSubscribeMessage { - [JsonProperty("api_key")] + [JsonPropertyName("api_key")] public string ApiKey { get; set; } = string.Empty; - [JsonProperty("original_challenge")] + [JsonPropertyName("original_challenge")] public string OriginalChallenge { get; set; } = string.Empty; - [JsonProperty("signed_challenge")] + [JsonPropertyName("signed_challenge")] public string SignedChallenge { get; set; } = string.Empty; } @@ -49,7 +46,7 @@ public record KrakenFuturesUpdateMessage : KrakenFuturesSocketMessage /// /// The symbol /// - [JsonProperty("product_id")] + [JsonPropertyName("product_id")] public string Symbol { get; set; } = string.Empty; } } diff --git a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesTickerUpdate.cs b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesTickerUpdate.cs index b86a44c..f8a8784 100644 --- a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesTickerUpdate.cs +++ b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesTickerUpdate.cs @@ -1,7 +1,4 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Objects.Sockets; -using Newtonsoft.Json; -using System; +using Kraken.Net.Objects.Sockets; namespace Kraken.Net.Objects.Models.Socket.Futures { @@ -13,120 +10,130 @@ public record KrakenFuturesTickerUpdate: KrakenFuturesEvent /// /// Timestamp /// - [JsonProperty("time")] + [JsonPropertyName("time")] [JsonConverter(typeof(DateTimeConverter))] public DateTime Timestamp { get; set; } /// /// Funding rate /// - [JsonProperty("funding_rate")] + [JsonPropertyName("funding_rate")] public decimal? FundingRate { get; set; } /// /// The estimated next funding rate /// - [JsonProperty("funding_rate_prediction")] + [JsonPropertyName("funding_rate_prediction")] public decimal? FundingRatePrediction { get; set; } /// /// The absolute funding rate relative to the spot price at the time of funding rate calculation /// - [JsonProperty("relative_funding_rate")] + [JsonPropertyName("relative_funding_rate")] public decimal? RelativeFundingRate { get; set; } /// /// The estimated next absolute funding rate relative to the current spot price /// - [JsonProperty("relative_funding_rate_prediction")] + [JsonPropertyName("relative_funding_rate_prediction")] public decimal? RelativeFundingRatePrediction { get; set; } /// /// Next funding rate in miliseconds /// - [JsonProperty("next_funding_rate_time")] + [JsonPropertyName("next_funding_rate_time")] [JsonConverter(typeof(DateTimeConverter))] public DateTime NextFundingRateTime { get; set; } /// /// The best current bid price /// - [JsonProperty("bid")] + [JsonPropertyName("bid")] public decimal BestBidPrice { get; set; } /// /// The quantity of the current best bid /// - [JsonProperty("bid_size")] + [JsonPropertyName("bid_size")] public decimal BestBidQuantity { get; set; } /// /// The best current ask price /// - [JsonProperty("ask")] + [JsonPropertyName("ask")] public decimal BestAskPrice { get; set; } /// /// The quantity of the current best ask /// - [JsonProperty("ask_size")] + [JsonPropertyName("ask_size")] public decimal BestAskQuantity { get; set; } /// /// The sum of the sizes of all fills observed in the last 24 hours /// - [JsonProperty("volume")] + [JsonPropertyName("volume")] public decimal Volume { get; set; } /// /// Days until maturity /// - [JsonProperty("dtm")] + [JsonPropertyName("dtm")] public int? DaysUntilMaturity { get; set; } /// /// Leverage /// + [JsonPropertyName("leverage")] public string Leverage { get; set; } = string.Empty; /// /// The real time index of the symbol /// + [JsonPropertyName("index")] public decimal Index { get; set; } /// /// The premium associated with the symbol /// + [JsonPropertyName("permium")] public decimal Premium { get; set; } /// /// The premium associated with the symbol /// - [JsonProperty("last")] + [JsonPropertyName("last")] public decimal LastPrice { get; set; } /// /// The 24h change in price /// - [JsonProperty("change")] + [JsonPropertyName("change")] public decimal ChangePercentage24h { get; set; } /// /// True if the market is suspended, false otherwise /// + [JsonPropertyName("suspended")] public bool Suspended { get; set; } /// /// Currently can be perpetual, month or quarter. Other tags may be added without notice. /// + [JsonPropertyName("tag")] public string Tag { get; set; } = string.Empty; /// /// The currency pair of the symbol /// + [JsonPropertyName("pair")] public string Pair { get; set; } = string.Empty; /// /// The current open interest of the symbol /// + [JsonPropertyName("openInterest")] public decimal OpenInterest { get; set; } /// /// The market price of the symbol /// + [JsonPropertyName("markPrice")] public decimal MarkPrice { get; set; } /// /// The maturity time /// [JsonConverter(typeof(DateTimeConverter))] + [JsonPropertyName("maturityTime")] public DateTime? MaturityTime { get; set; } /// /// True if the market is in post-only, false otherwise /// - [JsonProperty("post_only")] + [JsonPropertyName("post_only")] public bool PostOnly { get; set; } /// /// The same as volume except that, for multi-collateral futures, it is converted to the non-base currency /// + [JsonPropertyName("volumeQuote")] public decimal VolumeQuote { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesTradesUpdate.cs b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesTradesUpdate.cs index 8f425cb..29ec0c6 100644 --- a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesTradesUpdate.cs +++ b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesTradesUpdate.cs @@ -1,9 +1,5 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; +using Kraken.Net.Enums; using Kraken.Net.Objects.Sockets; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; namespace Kraken.Net.Objects.Models.Socket.Futures { @@ -15,6 +11,7 @@ public record KrakenFuturesTradesSnapshotUpdate : KrakenFuturesEvent /// /// Trades /// + [JsonPropertyName("trades")] public IEnumerable Trades { get; set; } = Array.Empty(); } @@ -26,35 +23,39 @@ public record KrakenFuturesTradeUpdate : KrakenFuturesUpdateMessage /// /// Uid /// + [JsonPropertyName("uid")] public string Uid { get; set; } = string.Empty; /// /// Order side /// [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("side")] public OrderSide Side { get; set; } /// /// Trade type /// + [JsonPropertyName("type")] public string Type { get; set; } = string.Empty; /// /// Sequence number /// - [JsonProperty("seq")] + [JsonPropertyName("seq")] public long Sequence { get; set; } /// /// Timestamp /// - [JsonProperty("time")] + [JsonPropertyName("time")] [JsonConverter(typeof(DateTimeConverter))] public DateTime Timestamp { get; set; } /// /// Quantity /// - [JsonProperty("qty")] + [JsonPropertyName("qty")] public decimal Quantity { get; set; } /// /// Trade price /// + [JsonPropertyName("price")] public decimal Price { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesUserTradesUpdate.cs b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesUserTradesUpdate.cs index a6ff9a0..0599736 100644 --- a/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesUserTradesUpdate.cs +++ b/Kraken.Net/Objects/Models/Socket/Futures/KrakenFuturesUserTradesUpdate.cs @@ -1,8 +1,4 @@ -using CryptoExchange.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models.Socket.Futures { @@ -14,11 +10,12 @@ public record KrakenFuturesUserTradesUpdate : KrakenFuturesSocketMessage /// /// Account /// + [JsonPropertyName("account")] public string Account { get; set; } = string.Empty; /// /// Trades /// - [JsonProperty("fills")] + [JsonPropertyName("fills")] public IEnumerable Trades { get; set; } = Array.Empty(); } @@ -30,67 +27,69 @@ public record KrakenFuturesUserTrade /// /// Symbol /// - [JsonProperty("instrument")] + [JsonPropertyName("instrument")] public string Symbol { get; set; } = string.Empty; /// /// Timestamp /// - [JsonProperty("time")] + [JsonPropertyName("time")] [JsonConverter(typeof(DateTimeConverter))] public DateTime Timestamp { get; set; } /// /// Price /// + [JsonPropertyName("price")] public decimal Price { get; set; } /// /// Quantity /// - [JsonProperty("qty")] + [JsonPropertyName("qty")] public decimal Quantity { get; set; } /// /// Sequence number /// - [JsonProperty("seq")] + [JsonPropertyName("seq")] public long Sequence { get; set; } /// /// Is buy /// + [JsonPropertyName("buy")] public bool Buy { get; set; } /// /// Order id /// - [JsonProperty("order_id")] + [JsonPropertyName("order_id")] public string OrderId { get; set; } = string.Empty; /// /// Trade id /// - [JsonProperty("fill_id")] + [JsonPropertyName("fill_id")] public string TradeId { get; set; } = string.Empty; /// /// Trade type /// [JsonConverter(typeof(EnumConverter))] - [JsonProperty("fill_type")] + [JsonPropertyName("fill_type")] public TradeType TradeType { get; set; } /// /// Fee paid on trade /// - [JsonProperty("fee_paid")] + [JsonPropertyName("fee_paid")] public decimal FeePaid { get; set; } /// /// Fee currency /// - [JsonProperty("fee_currency")] + [JsonPropertyName("fee_currency")] public string FeeCurrency { get; set; } = string.Empty; /// /// Order type of the taker /// - [JsonProperty("taker_order_type")] + [JsonPropertyName("taker_order_type")] public string TakerOrderType { get; set; } = string.Empty; /// /// Order type /// - [JsonProperty("order_type")] + [JsonPropertyName("order_type")] public string OrderType { get; set; } = string.Empty; } } diff --git a/Kraken.Net/Objects/Models/Socket/Futures/KrakenInstrumentUpdate.cs b/Kraken.Net/Objects/Models/Socket/Futures/KrakenInstrumentUpdate.cs new file mode 100644 index 0000000..1fd320f --- /dev/null +++ b/Kraken.Net/Objects/Models/Socket/Futures/KrakenInstrumentUpdate.cs @@ -0,0 +1,152 @@ +using Kraken.Net.Enums; + +namespace Kraken.Net.Objects.Models.Socket.Futures +{ + /// + /// Symbol and asset updates + /// + public record KrakenInstrumentUpdate + { + /// + /// Assets + /// + [JsonPropertyName("assets")] + public IEnumerable Assets { get; set; } = Array.Empty(); + /// + /// Symbols + /// + [JsonPropertyName("pairs")] + public IEnumerable Symbols { get; set; } = Array.Empty(); + } + + /// + /// Asset info + /// + public record KrakenSymbolUpdateAsset + { + /// + /// Asset + /// + [JsonPropertyName("id")] + public string Asset { get; set; } = string.Empty; + /// + /// Status + /// + [JsonPropertyName("status")] + public AssetStatus AssetStatus { get; set; } + /// + /// Precision + /// + [JsonPropertyName("precision")] + public decimal Precision { get; set; } + /// + /// Recommended display precision + /// + [JsonPropertyName("precision_display")] + public decimal PrecisionDisplay { get; set; } + /// + /// Borrowable + /// + [JsonPropertyName("borrowable")] + public bool Borrowable { get; set; } + /// + /// Collateral value + /// + [JsonPropertyName("collateral_value")] + public decimal CollateralValue { get; set; } + /// + /// Margin rate + /// + [JsonPropertyName("margin_rate")] + public decimal MarginRate { get; set; } + } + + /// + /// Symbol info + /// + public record KrakenSymbolUpdateSymbol + { + /// + /// Symbol + /// + [JsonPropertyName("symbol")] + public string Symbol { get; set; } = string.Empty; + /// + /// Base asset + /// + [JsonPropertyName("base")] + public string BaseAsset { get; set; } = string.Empty; + /// + /// Quote asset + /// + [JsonPropertyName("quote")] + public string QuoteAsset { get; set; } = string.Empty; + /// + /// Status + /// + [JsonPropertyName("status")] + public SymbolStatus SymbolStatus { get; set; } + /// + /// Quantity precision + /// + [JsonPropertyName("qty_precision")] + public decimal QuantityPrecision { get; set; } + /// + /// Quantity increment step + /// + [JsonPropertyName("qty_increment")] + public decimal QuantityStep { get; set; } + /// + /// Price precision + /// + [JsonPropertyName("price_precision")] + public decimal PricePrecision { get; set; } + /// + /// Cost precision + /// + [JsonPropertyName("cost_precision")] + public decimal CostPrecision { get; set; } + /// + /// Marginable + /// + [JsonPropertyName("marginable")] + public bool Marginable { get; set; } + /// + /// Has index + /// + [JsonPropertyName("has_index")] + public bool HasIndex { get; set; } + /// + /// Minimal notional value of an order + /// + [JsonPropertyName("cost_min")] + public decimal MinNotionalValue { get; set; } + /// + /// Initial margin requirement + /// + [JsonPropertyName("margin_initial")] + public decimal MarginInitial { get; set; } + /// + /// Position limit long + /// + [JsonPropertyName("position_limit_long")] + public decimal PositionLimitLong { get; set; } + /// + /// Position limit short + /// + [JsonPropertyName("position_limit_short")] + public decimal PositionLimitShort { get; set; } + /// + /// Price increment step + /// + [JsonPropertyName("price_increment")] + public decimal PriceStep { get; set; } + /// + /// Min order quantity + /// + [JsonPropertyName("qty_min")] + public decimal MinOrderQuantity { get; set; } + } + + +} diff --git a/Kraken.Net/Objects/Models/Socket/KrakenBalanceSnapshot.cs b/Kraken.Net/Objects/Models/Socket/KrakenBalanceSnapshot.cs new file mode 100644 index 0000000..a1a706a --- /dev/null +++ b/Kraken.Net/Objects/Models/Socket/KrakenBalanceSnapshot.cs @@ -0,0 +1,55 @@ +using Kraken.Net.Enums; + +namespace Kraken.Net.Objects.Models.Socket +{ + /// + /// Snapshot data + /// + public record KrakenBalanceSnapshot + { + /// + /// Asset + /// + [JsonPropertyName("asset")] + public string Asset { get; set; } = string.Empty; + /// + /// Asset class + /// + [JsonPropertyName("asset_class")] + public string AssetClass { get; set; } = string.Empty; + /// + /// Balance + /// + [JsonPropertyName("balance")] + public decimal Balance { get; set; } + /// + /// Wallets + /// + [JsonPropertyName("wallets")] + public IEnumerable Wallets { get; set; } = Array.Empty(); + } + + /// + /// Wallet info + /// + public record KrakenBalanceSnapshotWallet + { + /// + /// Type + /// + [JsonPropertyName("type")] + public WalletType WalletType { get; set; } + /// + /// Id + /// + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + /// + /// Balance + /// + [JsonPropertyName("balance")] + public decimal Balance { get; set; } + } + + +} diff --git a/Kraken.Net/Objects/Models/Socket/KrakenBookUpdate.cs b/Kraken.Net/Objects/Models/Socket/KrakenBookUpdate.cs new file mode 100644 index 0000000..b6b2f63 --- /dev/null +++ b/Kraken.Net/Objects/Models/Socket/KrakenBookUpdate.cs @@ -0,0 +1,46 @@ +namespace Kraken.Net.Objects.Models.Socket +{ + /// + /// Order book update + /// + public record KrakenBookUpdate + { + /// + /// The symbol + /// + [JsonPropertyName("symbol")] + public string Symbol { get; set; } = string.Empty; + /// + /// Asks in the book + /// + [JsonPropertyName("asks")] + public IEnumerable Asks { get; set; } = Array.Empty(); + /// + /// Bids in the book + /// + [JsonPropertyName("bids")] + public IEnumerable Bids { get; set; } = Array.Empty(); + /// + /// Checksum + /// + [JsonPropertyName("checksum")] + public long Checksum { get; set; } + } + + /// + /// Order book entry + /// + public record KrakenBookUpdateEntry : ISymbolOrderBookEntry + { + /// + /// The price + /// + [JsonPropertyName("price")] + public decimal Price { get; set; } + /// + /// The quantity + /// + [JsonPropertyName("qty")] + public decimal Quantity { get; set; } + } +} diff --git a/Kraken.Net/Objects/Models/Socket/KrakenIndividualBookUpdate.cs b/Kraken.Net/Objects/Models/Socket/KrakenIndividualBookUpdate.cs new file mode 100644 index 0000000..10c8370 --- /dev/null +++ b/Kraken.Net/Objects/Models/Socket/KrakenIndividualBookUpdate.cs @@ -0,0 +1,58 @@ +using Kraken.Net.Enums; + +namespace Kraken.Net.Objects.Models.Socket +{ + /// + /// Book update + /// + public record KrakenIndividualBookUpdate + { + /// + /// The symbol + /// + [JsonPropertyName("symbol")] + public string Symbol { get; set; } = string.Empty; + /// + /// Asks in the book + /// + [JsonPropertyName("asks")] + public IEnumerable Asks { get; set; } = Array.Empty(); + /// + /// Bids in the book + /// + [JsonPropertyName("bids")] + public IEnumerable Bids { get; set; } = Array.Empty(); + } + + /// + /// Book order entry + /// + public record KrakenIndividualBookUpdateEntry : ISymbolOrderBookEntry + { + /// + /// The order id + /// + [JsonPropertyName("order_id")] + public string OrderId { get; set; } = string.Empty; + /// + /// Price + /// + [JsonPropertyName("limit_price")] + public decimal Price { get; set; } + /// + /// Quantity + /// + [JsonPropertyName("order_qty")] + public decimal Quantity { get; set; } + /// + /// Timestamp + /// + [JsonPropertyName("timestamp")] + public DateTime Timestamp { get; set; } + /// + /// Event + /// + [JsonPropertyName("event")] + public OrderBookChange Event { get; set; } + } +} diff --git a/Kraken.Net/Objects/Models/Socket/KrakenKlineUpdate.cs b/Kraken.Net/Objects/Models/Socket/KrakenKlineUpdate.cs new file mode 100644 index 0000000..afaaa33 --- /dev/null +++ b/Kraken.Net/Objects/Models/Socket/KrakenKlineUpdate.cs @@ -0,0 +1,61 @@ +using Kraken.Net.Enums; + +namespace Kraken.Net.Objects.Models.Socket +{ + /// + /// Kline/candlestick info + /// + public record KrakenKlineUpdate + { + /// + /// Symbol + /// + [JsonPropertyName("symbol")] + public string Symbol { get; set; } = string.Empty; + /// + /// Open price + /// + [JsonPropertyName("open")] + public decimal OpenPrice { get; set; } + /// + /// High price + /// + [JsonPropertyName("high")] + public decimal HighPrice { get; set; } + /// + /// Low price + /// + [JsonPropertyName("low")] + public decimal LowPrice { get; set; } + /// + /// Close price + /// + [JsonPropertyName("close")] + public decimal ClosePrice { get; set; } + /// + /// Number of trades + /// + [JsonPropertyName("trades")] + public decimal Trades { get; set; } + /// + /// Volume + /// + [JsonPropertyName("volume")] + public decimal Volume { get; set; } + /// + /// Volume weighted average price + /// + [JsonPropertyName("vwap")] + public decimal Vwap { get; set; } + /// + /// Open timestamp + /// + [JsonPropertyName("interval_begin")] + public DateTime OpenTime { get; set; } + /// + /// Interval + /// + [JsonPropertyName("interval")] + public KlineInterval KlineInterval { get; set; } + } +} diff --git a/Kraken.Net/Objects/Models/Socket/KrakenOrderResult.cs b/Kraken.Net/Objects/Models/Socket/KrakenOrderResult.cs new file mode 100644 index 0000000..b3e80d8 --- /dev/null +++ b/Kraken.Net/Objects/Models/Socket/KrakenOrderResult.cs @@ -0,0 +1,34 @@ +namespace Kraken.Net.Objects.Models.Socket +{ + /// + /// Order result + /// + public record KrakenOrderResult + { + /// + /// Order id + /// + [JsonPropertyName("order_id")] + public string OrderId { get; set; } = string.Empty; + /// + /// Order id + /// + [JsonPropertyName("cl_ord_id")] + public string? ClientOrderId { get; set; } + /// + /// User reference id + /// + [JsonPropertyName("order_userref")] + public long? UserReference { get; set; } + /// + /// Error + /// + [JsonPropertyName("error")] + public string? Error { get; set; } + /// + /// Status + /// + [JsonPropertyName("status")] + public string? Status { get; set; } + } +} diff --git a/Kraken.Net/Objects/Models/Socket/KrakenOrderUpdate.cs b/Kraken.Net/Objects/Models/Socket/KrakenOrderUpdate.cs new file mode 100644 index 0000000..6d44c2e --- /dev/null +++ b/Kraken.Net/Objects/Models/Socket/KrakenOrderUpdate.cs @@ -0,0 +1,219 @@ +using Kraken.Net.Enums; + +namespace Kraken.Net.Objects.Models.Socket +{ + /// + /// Kraken order update + /// + public record KrakenOrderUpdate + { + /// + /// Order id + /// + [JsonPropertyName("order_id")] + public string OrderId { get; set; } = string.Empty; + /// + /// Symbol + /// + [JsonPropertyName("symbol")] + public string? Symbol { get; set; } + /// + /// Client order id + /// + [JsonPropertyName("cl_ord_id")] + public string? ClientOrderId { get; set; } + /// + /// Order quantity in quote asset + /// + [JsonPropertyName("cash_order_qty")] + public decimal? QuoteOrderQuantity { get; set; } + /// + /// Order quantity + /// + [JsonPropertyName("order_qty")] + public decimal? OrderQuantity { get; set; } + /// + /// Filled quantity value + /// + [JsonPropertyName("cum_cost")] + public decimal? ValueFilled { get; set; } + /// + /// Filled quantity + /// + [JsonPropertyName("cum_qty")] + public decimal? QuantityFilled { get; set; } + /// + /// Display quantity for iceberg orders + /// + [JsonPropertyName("display_qty")] + public decimal? IcebergQuantity { get; set; } + /// + /// Time in force + /// + [JsonPropertyName("time_in_force")] + public TimeInForce? TimeInForce { get; set; } + /// + /// Order event + /// + [JsonPropertyName("exec_type")] + public OrderEventType OrderEventType { get; set; } + /// + /// Side + /// + [JsonPropertyName("side")] + public OrderSide? OrderSide { get; set; } + /// + /// Order type + /// + [JsonPropertyName("order_type")] + public OrderType? OrderType { get; set; } + /// + /// Order user reference + /// + [JsonPropertyName("order_userref")] + public decimal OrderUserReference { get; set; } + /// + /// Limit price + /// + [JsonPropertyName("limit_price")] + public decimal? LimitPrice { get; set; } + /// + /// Stop price + /// + [JsonPropertyName("stop_price")] + public decimal? StopPrice { get; set; } + /// + /// Order status + /// + [JsonPropertyName("order_status")] + public OrderStatusUpdate OrderStatus { get; set; } + /// + /// Fee paid expressed in USD + /// + [JsonPropertyName("fee_usd_equiv")] + public decimal? FeeUsdEquiv { get; set; } + /// + /// Fee asset preference + /// + [JsonPropertyName("fee_ccy_pref")] + public OrderFlags? FeeAssetPreference { get; set; } + /// + /// Scheduled start time of the order + /// + [JsonPropertyName("effective_time")] + public DateTime? EffectiveTime { get; set; } + /// + /// Scheduled expiration time of the order + /// + [JsonPropertyName("expire_time")] + public DateTime? ExpireTime { get; set; } + /// + /// Timestamp + /// + [JsonPropertyName("timestamp")] + public DateTime Timestamp { get; set; } + /// + /// Average order trade price + /// + [JsonPropertyName("avg_price")] + public decimal? AveragePrice { get; set; } + /// + /// Fees paid + /// + [JsonPropertyName("fees")] + public IEnumerable? Fees { get; set; } + /// + /// Whether the order has been amended + /// + [JsonPropertyName("amended")] + public bool? Amended { get; set; } + /// + /// Indicates if the order has been liquidated by the engine + /// + [JsonPropertyName("liquidated")] + public bool? Liquidated { get; set; } + /// + /// Indicates if the order can be funded on margin + /// + [JsonPropertyName("margin")] + public bool? Margin { get; set; } + /// + /// Indicates if an execution is on margin, i.e. if the trade increased or reduced size of margin borrowing. On trade events only + /// + [JsonPropertyName("margin_borrow")] + public bool? MarginBorrow { get; set; } + /// + /// Indicates if the order has market price protection + /// + [JsonPropertyName("no_mpp")] + public bool? NoMarketPriceProtection { get; set; } + /// + /// Post only flag + /// + [JsonPropertyName("post_only")] + public bool? PostOnly { get; set; } + /// + /// Reduce only flag + /// + [JsonPropertyName("reduce_only")] + public bool? ReduceOnly { get; set; } + /// + /// Indicates status of the position on a margin order + /// + [JsonPropertyName("position_status")] + public string? PositionStatus { get; set; } + /// + /// The reason associated with an event, if applicable + /// + [JsonPropertyName("reason")] + public string? Reason { get; set; } + + /// + /// Id of the execution this update is for + /// + [JsonPropertyName("exec_id")] + public string? ExecutionId { get; set; } + /// + /// Id of the trade this update is for + /// + [JsonPropertyName("trade_id")] + public long? LastTradeId { get; set; } + /// + /// Quantity of the trade this update is for + /// + [JsonPropertyName("last_qty")] + public decimal? LastTradeQuantity { get; set; } + /// + /// Price of the trade this update is for + /// + [JsonPropertyName("last_price")] + public decimal? LastTradePrice { get; set; } + /// + /// Value of the trade this update is for + /// + [JsonPropertyName("cost")] + public decimal? LastTradeValue { get; set; } + /// + /// Trade role of the trade this update is for, maker or taker + /// + [JsonPropertyName("liquidity_ind")] + public TradeType? LastTradeRole { get; set; } + } + + /// + /// Fee info + /// + public record KrakenOrderUpdateFee + { + /// + /// Fee asset + /// + [JsonPropertyName("asset")] + public string Asset { get; set; } = string.Empty; + /// + /// Quantity + /// + [JsonPropertyName("qty")] + public decimal Quantity { get; set; } + } +} diff --git a/Kraken.Net/Objects/Models/Socket/KrakenSocketAmendOrderResult.cs b/Kraken.Net/Objects/Models/Socket/KrakenSocketAmendOrderResult.cs new file mode 100644 index 0000000..f9691d9 --- /dev/null +++ b/Kraken.Net/Objects/Models/Socket/KrakenSocketAmendOrderResult.cs @@ -0,0 +1,24 @@ +namespace Kraken.Net.Objects.Models.Socket +{ + /// + /// Amend order result + /// + public record KrakenSocketAmendOrderResult + { + /// + /// Amend id + /// + [JsonPropertyName("amend_id")] + public string AmendId { get; set; } = string.Empty; + /// + /// Order id + /// + [JsonPropertyName("order_id")] + public string OrderId { get; set; } = string.Empty; + /// + /// Client order id + /// + [JsonPropertyName("cl_ord_id")] + public string? ClientOrderId { get; set; } + } +} diff --git a/Kraken.Net/Objects/Models/Socket/KrakenSocketOrderRequest.cs b/Kraken.Net/Objects/Models/Socket/KrakenSocketOrderRequest.cs new file mode 100644 index 0000000..64452a0 --- /dev/null +++ b/Kraken.Net/Objects/Models/Socket/KrakenSocketOrderRequest.cs @@ -0,0 +1,115 @@ +using Kraken.Net.Enums; +using Kraken.Net.Objects.Internal; + +namespace Kraken.Net.Objects.Models.Socket +{ + /// + /// Order request + /// + public record KrakenSocketOrderRequest + { + /// + /// Order type + /// + [JsonPropertyName("order_type")] + [JsonConverter(typeof(EnumConverter))] + public OrderType OrderType { get; set; } + /// + /// Order side + /// + [JsonPropertyName("side")] + [JsonConverter(typeof(EnumConverter))] + public OrderSide Side { get; set; } + /// + /// Quote quantity + /// + [JsonPropertyName("cash_order_qty"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? QuoteQuantity { get; set; } + /// + /// Conditional order + /// + [JsonPropertyName("conditional"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public KrakenSocketPlaceOrderRequestV2Condition? Conditional { get; set; } + /// + /// Iceberg quantity + /// + [JsonPropertyName("display_qty"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? IcebergQuantity { get; set; } + /// + /// Start time + /// + [JsonPropertyName("effective_time"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public DateTime? StartTime { get; set; } + /// + /// Expire time + /// + [JsonPropertyName("expire_time"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public DateTime? ExpireTime { get; set; } + /// + /// Fee preference setting + /// + [JsonPropertyName("fee_preference"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public FeePreference? FeePreference { get; set; } + /// + /// Limit price + /// + [JsonPropertyName("limit_price"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? Price { get; set; } + /// + /// Limit price type + /// + [JsonPropertyName("limit_price_type"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public PriceType? LimitPriceType { get; set; } + /// + /// Funds the order on margin using the maximum leverage for the pair (maximum is leverage of 5). + /// + [JsonPropertyName("margin"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? Margin { get; set; } + /// + /// Disable market price protection + /// + [JsonPropertyName("no_mpp"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? NoMarketPriceProtection { get; set; } + /// + /// Client order id + /// + [JsonPropertyName("cl_ord_id"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? ClientOrderId { get; set; } + /// + /// User reference + /// + [JsonPropertyName("order_userref"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public uint? UserReference { get; set; } + /// + /// Order quantity + /// + [JsonPropertyName("order_qty"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? Quantity { get; set; } + /// + /// Post only flag + /// + [JsonPropertyName("post_only"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? PostOnly { get; set; } + /// + /// Reduce only flag + /// + [JsonPropertyName("reduce_only"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? ReduceOnly { get; set; } + /// + /// Self trade prevention type + /// + [JsonPropertyName("stp_type"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public SelfTradePreventionType? SelfTradePreventionType { get; set; } + /// + /// Time in force + /// + [JsonPropertyName("time_in_force"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [JsonConverter(typeof(EnumConverter))] + public TimeInForce? TimeInForce { get; set; } + /// + /// Trigger info + /// + [JsonPropertyName("triggers"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public KrakenSocketPlaceOrderRequestV2Trigger? Trigger { get; set; } + } +} diff --git a/Kraken.Net/Objects/Models/Socket/KrakenSocketReplaceOrderResult.cs b/Kraken.Net/Objects/Models/Socket/KrakenSocketReplaceOrderResult.cs new file mode 100644 index 0000000..10b464a --- /dev/null +++ b/Kraken.Net/Objects/Models/Socket/KrakenSocketReplaceOrderResult.cs @@ -0,0 +1,19 @@ +namespace Kraken.Net.Objects.Models.Socket +{ + /// + /// Replace order result + /// + public record KrakenSocketReplaceOrderResult + { + /// + /// Order id + /// + [JsonPropertyName("order_id")] + public string OrderId { get; set; } = string.Empty; + /// + /// The original order id + /// + [JsonPropertyName("original_order_id")] + public string OriginalOrderId { get; set; } = string.Empty; + } +} diff --git a/Kraken.Net/Objects/Models/Socket/KrakenStreamCancelAllResult.cs b/Kraken.Net/Objects/Models/Socket/KrakenStreamCancelAllResult.cs index 9754680..99eaa8f 100644 --- a/Kraken.Net/Objects/Models/Socket/KrakenStreamCancelAllResult.cs +++ b/Kraken.Net/Objects/Models/Socket/KrakenStreamCancelAllResult.cs @@ -1,15 +1,14 @@ -using Kraken.Net.Objects.Sockets; - -namespace Kraken.Net.Objects.Models.Socket +namespace Kraken.Net.Objects.Models.Socket { /// /// Cancel all result /// - public record KrakenStreamCancelAllResult : KrakenQueryEvent + public record KrakenStreamCancelAllResult { /// /// Number of orders canceled /// + [JsonPropertyName("count")] public int Count { get; set; } } } diff --git a/Kraken.Net/Objects/Models/Socket/KrakenStreamPlacedOrder.cs b/Kraken.Net/Objects/Models/Socket/KrakenStreamPlacedOrder.cs deleted file mode 100644 index d115b75..0000000 --- a/Kraken.Net/Objects/Models/Socket/KrakenStreamPlacedOrder.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Kraken.Net.Objects.Sockets; -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Models.Socket -{ - /// - /// Placed order result - /// - public record KrakenStreamPlacedOrder: KrakenQueryEvent - { - /// - /// Order description - /// - [JsonProperty("descr")] - public string Description { get; set; } = string.Empty; - /// - /// Placed order id - /// - [JsonProperty("txId")] - public string OrderId { get; set; } = string.Empty; - } -} diff --git a/Kraken.Net/Objects/Models/Socket/KrakenStreamSystemStatus.cs b/Kraken.Net/Objects/Models/Socket/KrakenStreamSystemStatus.cs index ada7cca..c3731d5 100644 --- a/Kraken.Net/Objects/Models/Socket/KrakenStreamSystemStatus.cs +++ b/Kraken.Net/Objects/Models/Socket/KrakenStreamSystemStatus.cs @@ -1,6 +1,4 @@ -using Kraken.Net.Converters; -using Kraken.Net.Enums; -using Newtonsoft.Json; +using Kraken.Net.Enums; namespace Kraken.Net.Objects.Models.Socket { @@ -12,19 +10,23 @@ public record KrakenStreamSystemStatus /// /// Connection id /// - public string ConnectionId { get; set; } = string.Empty; - /// - /// Name of the event - /// - public string Event { get; set; } = string.Empty; + [JsonPropertyName("connection_id")] + public long ConnectionId { get; set; } /// /// Status /// - [JsonConverter(typeof(SystemStatusConverter))] + [JsonConverter(typeof(EnumConverter))] + [JsonPropertyName("system")] public SystemStatus Status { get; set; } /// /// Version /// + [JsonPropertyName("version")] public string Version { get; set; } = string.Empty; + /// + /// API Version + /// + [JsonPropertyName("api_version")] + public string ApiVersion { get; set; } = string.Empty; } } diff --git a/Kraken.Net/Objects/Models/Socket/KrakenTickerUpdate.cs b/Kraken.Net/Objects/Models/Socket/KrakenTickerUpdate.cs new file mode 100644 index 0000000..db25e21 --- /dev/null +++ b/Kraken.Net/Objects/Models/Socket/KrakenTickerUpdate.cs @@ -0,0 +1,71 @@ +namespace Kraken.Net.Objects.Models.Socket +{ + /// + /// Ticker info + /// + public record KrakenTickerUpdate + { + /// + /// Symbol + /// + [JsonPropertyName("symbol")] + public string Symbol { get; set; } = string.Empty; + /// + /// Price of best bid + /// + [JsonPropertyName("bid")] + public decimal BestBidPrice { get; set; } + /// + /// Quantity of the best bid + /// + [JsonPropertyName("bid_qty")] + public decimal BestBidQuantity { get; set; } + /// + /// Price of best ask + /// + [JsonPropertyName("ask")] + public decimal BestAskPrice { get; set; } + /// + /// Quantity of the best ask + /// + [JsonPropertyName("ask_qty")] + public decimal BestAskQuantity { get; set; } + /// + /// Last trade price + /// + [JsonPropertyName("last")] + public decimal LastPrice { get; set; } + /// + /// Volume + /// + [JsonPropertyName("volume")] + public decimal Volume { get; set; } + /// + /// Volume weighted average price of last 24 hours + /// + [JsonPropertyName("vwap")] + public decimal Vwap { get; set; } + /// + /// Low price + /// + [JsonPropertyName("low")] + public decimal LowPrice { get; set; } + /// + /// High price + /// + [JsonPropertyName("high")] + public decimal HighPrice { get; set; } + /// + /// Price change in the last 24 hours + /// + [JsonPropertyName("change")] + public decimal PriceChange { get; set; } + /// + /// Price change percentage in last 24 hours + /// + [JsonPropertyName("change_pct")] + public decimal PriceChangePercentage { get; set; } + } + + +} diff --git a/Kraken.Net/Objects/Models/Socket/KrakenTradeUpdate.cs b/Kraken.Net/Objects/Models/Socket/KrakenTradeUpdate.cs new file mode 100644 index 0000000..d75bcf4 --- /dev/null +++ b/Kraken.Net/Objects/Models/Socket/KrakenTradeUpdate.cs @@ -0,0 +1,48 @@ +using Kraken.Net.Enums; + +namespace Kraken.Net.Objects.Models.Socket +{ + /// + /// Trade info + /// + public record KrakenTradeUpdate + { + /// + /// Symbol + /// + [JsonPropertyName("symbol")] + public string Symbol { get; set; } = string.Empty; + /// + /// Side + /// + [JsonPropertyName("side")] + public OrderSide Side { get; set; } + /// + /// Price + /// + [JsonPropertyName("price")] + public decimal Price { get; set; } + /// + /// Quantity + /// + [JsonPropertyName("qty")] + public decimal Quantity { get; set; } + /// + /// Order type + /// + [JsonPropertyName("ord_type")] + public OrderType OrderType { get; set; } + /// + /// Trade id + /// + [JsonPropertyName("trade_id")] + public long TradeId { get; set; } + /// + /// Timestamp + /// + [JsonPropertyName("timestamp")] + public DateTime Timestamp { get; set; } + } + + +} diff --git a/Kraken.Net/Objects/Models/Socket/KrakenWebSocketToken.cs b/Kraken.Net/Objects/Models/Socket/KrakenWebSocketToken.cs index 388bbe0..c0b66c8 100644 --- a/Kraken.Net/Objects/Models/Socket/KrakenWebSocketToken.cs +++ b/Kraken.Net/Objects/Models/Socket/KrakenWebSocketToken.cs @@ -8,10 +8,12 @@ public record KrakenWebSocketToken /// /// Token to use for connecting to private websockets /// + [JsonPropertyName("token")] public string Token { get; set; } = string.Empty; /// /// Expires after x seconds /// + [JsonPropertyName("expires")] public int Expires { get; set; } } } diff --git a/Kraken.Net/Objects/Options/KrakenOrderBookOptions.cs b/Kraken.Net/Objects/Options/KrakenOrderBookOptions.cs index fa49e7a..7cc4841 100644 --- a/Kraken.Net/Objects/Options/KrakenOrderBookOptions.cs +++ b/Kraken.Net/Objects/Options/KrakenOrderBookOptions.cs @@ -1,5 +1,4 @@ using CryptoExchange.Net.Objects.Options; -using System; namespace Kraken.Net.Objects.Options { diff --git a/Kraken.Net/Objects/Options/KrakenRestOptions.cs b/Kraken.Net/Objects/Options/KrakenRestOptions.cs index 6f167b7..808059d 100644 --- a/Kraken.Net/Objects/Options/KrakenRestOptions.cs +++ b/Kraken.Net/Objects/Options/KrakenRestOptions.cs @@ -1,8 +1,4 @@ -using CryptoExchange.Net.Interfaces; -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Options; -using System; -using System.Collections.Generic; +using CryptoExchange.Net.Objects.Options; namespace Kraken.Net.Objects.Options { diff --git a/Kraken.Net/Objects/Options/KrakenSocketOptions.cs b/Kraken.Net/Objects/Options/KrakenSocketOptions.cs index a4a2eff..094688d 100644 --- a/Kraken.Net/Objects/Options/KrakenSocketOptions.cs +++ b/Kraken.Net/Objects/Options/KrakenSocketOptions.cs @@ -1,5 +1,4 @@ -using CryptoExchange.Net.Interfaces; -using CryptoExchange.Net.Objects.Options; +using CryptoExchange.Net.Objects.Options; namespace Kraken.Net.Objects.Options { diff --git a/Kraken.Net/Objects/Sockets/KrakenAuthSocketUpdate.cs b/Kraken.Net/Objects/Sockets/KrakenAuthSocketUpdate.cs deleted file mode 100644 index 4f1757e..0000000 --- a/Kraken.Net/Objects/Sockets/KrakenAuthSocketUpdate.cs +++ /dev/null @@ -1,25 +0,0 @@ -using CryptoExchange.Net.Attributes; -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Sockets -{ - [JsonConverter(typeof(ArrayConverter))] - internal class KrakenAuthSocketUpdate - { - [ArrayProperty(0)] - [JsonConversion] - public T Data { get; set; } = default!; - [ArrayProperty(1)] - public string ChannelName { get; set; } = null!; - [ArrayProperty(2)] - [JsonConversion] - public KrakenAuthSequence Sequence { get; set; } = null!; - } - - internal class KrakenAuthSequence - { - [JsonProperty("sequence")] - public int Sequence { get; set; } - } -} diff --git a/Kraken.Net/Objects/Sockets/KrakenChallengeRequest.cs b/Kraken.Net/Objects/Sockets/KrakenChallengeRequest.cs index 712bd9d..1b00fc2 100644 --- a/Kraken.Net/Objects/Sockets/KrakenChallengeRequest.cs +++ b/Kraken.Net/Objects/Sockets/KrakenChallengeRequest.cs @@ -1,16 +1,18 @@ -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Sockets +namespace Kraken.Net.Objects.Sockets { - internal record KrakenChallengeRequest : KrakenEvent + internal record KrakenChallengeRequest { - [JsonProperty("api_key")] + [JsonPropertyName("event")] + public string Event { get; set; } = string.Empty; + [JsonPropertyName("api_key")] public string ApiKey { get; set; } = string.Empty; } internal record KrakenChallengeResponse : KrakenEvent { - [JsonProperty("message")] + [JsonPropertyName("event")] + public string? Event { get; set; } + [JsonPropertyName("message")] public string Message { get; set; } = string.Empty; } } diff --git a/Kraken.Net/Objects/Sockets/KrakenEvent.cs b/Kraken.Net/Objects/Sockets/KrakenEvent.cs index 0acf06f..2b0abed 100644 --- a/Kraken.Net/Objects/Sockets/KrakenEvent.cs +++ b/Kraken.Net/Objects/Sockets/KrakenEvent.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Sockets +namespace Kraken.Net.Objects.Sockets { /// /// Kraken message event @@ -8,9 +6,9 @@ namespace Kraken.Net.Objects.Sockets public record KrakenEvent { /// - /// The message event + /// The channel /// - [JsonProperty("event")] - public string Event { get; set; } = null!; + [JsonPropertyName("channel")] + public string Channel { get; set; } = null!; } } diff --git a/Kraken.Net/Objects/Sockets/KrakenFuturesEvent.cs b/Kraken.Net/Objects/Sockets/KrakenFuturesEvent.cs index 623059f..a4c967b 100644 --- a/Kraken.Net/Objects/Sockets/KrakenFuturesEvent.cs +++ b/Kraken.Net/Objects/Sockets/KrakenFuturesEvent.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Sockets +namespace Kraken.Net.Objects.Sockets { /// /// Socket event @@ -10,18 +8,18 @@ public record KrakenFuturesEvent /// /// The event type /// - [JsonProperty("event")] + [JsonPropertyName("event")] public string Event { get; set; } = string.Empty; /// /// The feed /// - [JsonProperty("feed")] + [JsonPropertyName("feed")] public string Feed { get; set; } = string.Empty; /// /// The symbols /// - [JsonProperty("product_id")] + [JsonPropertyName("product_id")] public string? Symbol { get; set; } } } diff --git a/Kraken.Net/Objects/Sockets/KrakenFuturesRequest.cs b/Kraken.Net/Objects/Sockets/KrakenFuturesRequest.cs index 24068e4..f64227c 100644 --- a/Kraken.Net/Objects/Sockets/KrakenFuturesRequest.cs +++ b/Kraken.Net/Objects/Sockets/KrakenFuturesRequest.cs @@ -1,35 +1,32 @@ -using Newtonsoft.Json; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Sockets +namespace Kraken.Net.Objects.Sockets { internal class KrakenFuturesRequest { /// /// The event type /// - [JsonProperty("event")] + [JsonPropertyName("event")] public string Event { get; set; } = string.Empty; /// /// The feed /// - [JsonProperty("feed")] + [JsonPropertyName("feed")] public string Feed { get; set; } = string.Empty; /// /// The symbols /// - [JsonProperty("product_ids")] + [JsonPropertyName("product_ids")] public List? Symbols { get; set; } } internal class KrakenFuturesAuthRequest : KrakenFuturesRequest { - [JsonProperty("api_key")] + [JsonPropertyName("api_key")] public string ApiKey { get; set; } = string.Empty; - [JsonProperty("original_challenge")] + [JsonPropertyName("original_challenge")] public string OriginalChallenge { get; set; } = string.Empty; - [JsonProperty("signed_challenge")] + [JsonPropertyName("signed_challenge")] public string SignedChallenge { get; set; } = string.Empty; } } diff --git a/Kraken.Net/Objects/Sockets/KrakenFuturesResponse.cs b/Kraken.Net/Objects/Sockets/KrakenFuturesResponse.cs index 9c9a9fb..b47d0e7 100644 --- a/Kraken.Net/Objects/Sockets/KrakenFuturesResponse.cs +++ b/Kraken.Net/Objects/Sockets/KrakenFuturesResponse.cs @@ -1,30 +1,27 @@ -using Newtonsoft.Json; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Sockets +namespace Kraken.Net.Objects.Sockets { internal class KrakenFuturesResponse { /// /// The event type /// - [JsonProperty("event")] + [JsonPropertyName("event")] public string Event { get; set; } = string.Empty; /// /// The feed /// - [JsonProperty("feed")] + [JsonPropertyName("feed")] public string Feed { get; set; } = string.Empty; /// /// Message /// - [JsonProperty("message")] + [JsonPropertyName("message")] public string? Message { get; set; } /// /// The symbols /// - [JsonProperty("product_ids")] + [JsonPropertyName("product_ids")] public List? Symbols { get; set; } } } diff --git a/Kraken.Net/Objects/Sockets/KrakenQueryEvent.cs b/Kraken.Net/Objects/Sockets/KrakenQueryEvent.cs deleted file mode 100644 index 1334166..0000000 --- a/Kraken.Net/Objects/Sockets/KrakenQueryEvent.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Kraken.Net.Objects.Sockets -{ - /// - /// Kraken response to a query - /// - public record KrakenQueryEvent: KrakenEvent - { - /// - /// Response status - /// - public string Status { get; set; } = string.Empty; - /// - /// Optional error message - /// - public string? ErrorMessage { get; set; } - } -} diff --git a/Kraken.Net/Objects/Sockets/KrakenSocketUpdate.cs b/Kraken.Net/Objects/Sockets/KrakenSocketUpdate.cs deleted file mode 100644 index 819f1be..0000000 --- a/Kraken.Net/Objects/Sockets/KrakenSocketUpdate.cs +++ /dev/null @@ -1,20 +0,0 @@ -using CryptoExchange.Net.Attributes; -using CryptoExchange.Net.Converters; -using Newtonsoft.Json; - -namespace Kraken.Net.Objects.Sockets -{ - [JsonConverter(typeof(ArrayConverter))] - internal class KrakenSocketUpdate - { - [ArrayProperty(0)] - public long ChannelId { get; set; } - [ArrayProperty(1)] - [JsonConversion] - public T Data { get; set; } = default!; - [ArrayProperty(2)] - public string ChannelName { get; set; } = string.Empty; - [ArrayProperty(3)] - public string Symbol { get; set; } = string.Empty; - } -} diff --git a/Kraken.Net/Objects/Sockets/KrakenSubscriptionEvent.cs b/Kraken.Net/Objects/Sockets/KrakenSubscriptionEvent.cs deleted file mode 100644 index 0e9459b..0000000 --- a/Kraken.Net/Objects/Sockets/KrakenSubscriptionEvent.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Newtonsoft.Json; -using System.Collections.Generic; - -namespace Kraken.Net.Objects.Sockets -{ - internal record KrakenSubscriptionEvent : KrakenQueryEvent - { - [JsonProperty("channelID")] - public long ChannelId { get; set; } - [JsonProperty("channelName")] - public string ChannelName { get; set; } = string.Empty; - [JsonProperty("pair")] - public string Symbol { get; set; } = string.Empty; - [JsonProperty("reqId")] - public int RequestId { get; set; } - [JsonProperty("subscription")] - public Dictionary Subscription { get; set; } = new Dictionary(); - } -} diff --git a/Kraken.Net/Objects/Sockets/Queries/KrakenFuturesAuthQuery.cs b/Kraken.Net/Objects/Sockets/Queries/KrakenFuturesAuthQuery.cs index 2bece38..c0f1af7 100644 --- a/Kraken.Net/Objects/Sockets/Queries/KrakenFuturesAuthQuery.cs +++ b/Kraken.Net/Objects/Sockets/Queries/KrakenFuturesAuthQuery.cs @@ -1,8 +1,5 @@ -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Sockets; -using System.Collections.Generic; -using System.Threading.Tasks; namespace Kraken.Net.Objects.Sockets.Queries { @@ -12,12 +9,14 @@ internal class KrakenFuturesAuthQuery : Query public KrakenFuturesAuthQuery(string apiKey) : base(new KrakenChallengeRequest { ApiKey = apiKey, Event = "challenge" }, false) { - ListenerIdentifiers = new HashSet() { "challenge" }; + ListenerIdentifiers = new HashSet() { "challenge", "alert" }; } public override CallResult HandleMessage(SocketConnection connection, DataEvent message) { - // TODO test error? + if (message.Data.Event == "alert") + return new CallResult(default, message.OriginalData, new ServerError(message.Data.Message)); + var authProvider = (KrakenFuturesAuthenticationProvider)connection.ApiClient.AuthenticationProvider!; var sign = authProvider.AuthenticateWebsocketChallenge(message.Data.Message); connection.Properties["OriginalChallenge"] = message.Data.Message; diff --git a/Kraken.Net/Objects/Sockets/Queries/KrakenFuturesQuery.cs b/Kraken.Net/Objects/Sockets/Queries/KrakenFuturesQuery.cs index 3a84744..24e0d29 100644 --- a/Kraken.Net/Objects/Sockets/Queries/KrakenFuturesQuery.cs +++ b/Kraken.Net/Objects/Sockets/Queries/KrakenFuturesQuery.cs @@ -1,9 +1,5 @@ -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Sockets; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace Kraken.Net.Objects.Sockets.Queries { @@ -36,7 +32,13 @@ public KrakenFuturesQuery(KrakenFuturesRequest request, bool authenticated) : ba public override CallResult HandleMessage(SocketConnection connection, DataEvent message) { if (string.Equals(message.Data.Event, "alert", StringComparison.Ordinal)) + { + if (message.Data.Message == "Already subscribed to feed, re-requesting") + // Duplicate subscriptions are not an error + return message.ToCallResult(); + return new CallResult(new ServerError(message.Data.Message!)); + } else return new CallResult(message.Data!); } diff --git a/Kraken.Net/Objects/Sockets/Queries/KrakenSpotQuery.cs b/Kraken.Net/Objects/Sockets/Queries/KrakenSpotQuery.cs deleted file mode 100644 index 25e2d47..0000000 --- a/Kraken.Net/Objects/Sockets/Queries/KrakenSpotQuery.cs +++ /dev/null @@ -1,27 +0,0 @@ -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; -using CryptoExchange.Net.Sockets; -using Kraken.Net.Objects.Internal; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Kraken.Net.Objects.Sockets.Queries -{ - internal class KrakenSpotQuery : Query where T : KrakenQueryEvent - { - public override HashSet ListenerIdentifiers { get; set; } - - public KrakenSpotQuery(KrakenSocketRequest request, bool authenticated) : base(request, authenticated) - { - ListenerIdentifiers = new HashSet() { request.RequestId.ToString() }; - } - - public override CallResult HandleMessage(SocketConnection connection, DataEvent message) - { - if (message.Data.Status != "error") - return new CallResult(message.Data!); - else - return new CallResult(new ServerError(message.Data.ErrorMessage!)); - } - } -} diff --git a/Kraken.Net/Objects/Sockets/Queries/KrakenSpotQueryV2.cs b/Kraken.Net/Objects/Sockets/Queries/KrakenSpotQueryV2.cs new file mode 100644 index 0000000..688b1cc --- /dev/null +++ b/Kraken.Net/Objects/Sockets/Queries/KrakenSpotQueryV2.cs @@ -0,0 +1,31 @@ +using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Sockets; +using Kraken.Net.Objects.Internal; +using Kraken.Net.Objects.Models.Socket; + +namespace Kraken.Net.Objects.Sockets.Queries +{ + internal class KrakenSpotQueryV2 : Query> + { + public override HashSet ListenerIdentifiers { get; set; } + + public KrakenSpotQueryV2(KrakenSocketRequestV2 request, bool authenticated) : base(request, authenticated) + { + ListenerIdentifiers = new HashSet() { request.RequestId.ToString() }; + } + + public override CallResult> HandleMessage(SocketConnection connection, DataEvent> message) + { + if (message.Data.Success) + return message.ToCallResult(); + else if (message.Data is KrakenSocketResponseV2> response // We'll want to return the actual response data, so return as no error and handle it in the method itself + || message.Data.Error == "Already subscribed") // Duplicate subscription shouldn't be treated as an error + { + + return message.ToCallResult(); + } + else + return new CallResult>(new ServerError(message.Data.Error!)); + } + } +} diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesAccountLogSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesAccountLogSubscription.cs index 930e141..4642e65 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesAccountLogSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesAccountLogSubscription.cs @@ -1,13 +1,8 @@ using CryptoExchange.Net.Converters.MessageParsing; -using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Sockets; using Kraken.Net.Objects.Models.Socket.Futures; using Kraken.Net.Objects.Sockets.Queries; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; namespace Kraken.Net.Objects.Sockets.Subscriptions.Futures { diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesBalanceSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesBalanceSubscription.cs index 470ae21..3603f98 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesBalanceSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesBalanceSubscription.cs @@ -1,12 +1,7 @@ -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Sockets; using Kraken.Net.Objects.Models.Socket.Futures; using Kraken.Net.Objects.Sockets.Queries; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; namespace Kraken.Net.Objects.Sockets.Subscriptions.Futures { diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesInfoSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesInfoSubscription.cs index efa5b33..109c5fe 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesInfoSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesInfoSubscription.cs @@ -1,11 +1,6 @@ -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Sockets; using Kraken.Net.Objects.Internal; -using Kraken.Net.Objects.Models.Socket; -using Microsoft.Extensions.Logging; -using System.Collections.Generic; -using System.Threading.Tasks; namespace Kraken.Net.Objects.Sockets.Subscriptions.Spot { diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesNotificationSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesNotificationSubscription.cs index 756284e..31b8ef3 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesNotificationSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesNotificationSubscription.cs @@ -1,12 +1,7 @@ -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Sockets; using Kraken.Net.Objects.Models.Socket.Futures; using Kraken.Net.Objects.Sockets.Queries; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; namespace Kraken.Net.Objects.Sockets.Subscriptions.Futures { diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOpenPositionsSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOpenPositionsSubscription.cs index 2a7145a..70cc048 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOpenPositionsSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOpenPositionsSubscription.cs @@ -1,12 +1,7 @@ -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Sockets; using Kraken.Net.Objects.Models.Socket.Futures; using Kraken.Net.Objects.Sockets.Queries; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; namespace Kraken.Net.Objects.Sockets.Subscriptions.Futures { diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOrderbookSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOrderbookSubscription.cs index 774b3de..c0c90a8 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOrderbookSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOrderbookSubscription.cs @@ -1,14 +1,8 @@ using CryptoExchange.Net.Converters.MessageParsing; -using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Sockets; using Kraken.Net.Objects.Models.Socket.Futures; using Kraken.Net.Objects.Sockets.Queries; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace Kraken.Net.Objects.Sockets.Subscriptions.Futures { diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOrdersSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOrdersSubscription.cs index 96b4d11..5a41e64 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOrdersSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOrdersSubscription.cs @@ -1,13 +1,8 @@ using CryptoExchange.Net.Converters.MessageParsing; -using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Sockets; using Kraken.Net.Objects.Models.Socket.Futures; using Kraken.Net.Objects.Sockets.Queries; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; namespace Kraken.Net.Objects.Sockets.Subscriptions.Futures { diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesSubscription.cs index 1cefe79..874d3e2 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesSubscription.cs @@ -1,12 +1,6 @@ -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Sockets; using Kraken.Net.Objects.Sockets.Queries; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace Kraken.Net.Objects.Sockets.Subscriptions.Spot { diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesTradesSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesTradesSubscription.cs index 59e7275..03763b5 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesTradesSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesTradesSubscription.cs @@ -1,14 +1,8 @@ using CryptoExchange.Net.Converters.MessageParsing; -using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Sockets; using Kraken.Net.Objects.Models.Socket.Futures; using Kraken.Net.Objects.Sockets.Queries; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace Kraken.Net.Objects.Sockets.Subscriptions.Futures { diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesUserTradeSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesUserTradeSubscription.cs index 13ecc9f..0a993c2 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesUserTradeSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesUserTradeSubscription.cs @@ -1,12 +1,7 @@ -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Sockets; using Kraken.Net.Objects.Models.Socket.Futures; using Kraken.Net.Objects.Sockets.Queries; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; namespace Kraken.Net.Objects.Sockets.Subscriptions.Futures { diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenAuthSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenAuthSubscription.cs deleted file mode 100644 index 6f32bf6..0000000 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenAuthSubscription.cs +++ /dev/null @@ -1,78 +0,0 @@ -using CryptoExchange.Net; -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; -using CryptoExchange.Net.Sockets; -using Kraken.Net.Objects.Internal; -using Kraken.Net.Objects.Sockets.Queries; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Kraken.Net.Objects.Sockets.Subscriptions.Spot -{ - internal abstract class KrakenAuthSubscription : Subscription - { - protected string _token; - - protected KrakenAuthSubscription(string token, ILogger logger, bool authenticated) : base(logger, authenticated) - { - _token = token; - } - - internal void UpdateToken(string token) - { - _token = token; - } - } - - internal class KrakenAuthSubscription : KrakenAuthSubscription - { - private string _topic; - private readonly Action>> _handler; - - public override HashSet ListenerIdentifiers { get; set; } - - public KrakenAuthSubscription(ILogger logger, string topic, string token, Action>> handler) : base(token, logger, false) - { - _topic = topic; - _handler = handler; - - ListenerIdentifiers = new HashSet { topic }; - } - public override Type? GetMessageType(IMessageAccessor message) => typeof(KrakenAuthSocketUpdate); - - public override Query? GetSubQuery(SocketConnection connection) - { - return new KrakenSpotQuery( - new KrakenSubscribeRequest(_topic, _token, null, null, null, ExchangeHelpers.NextId()) - { - Event = "subscribe", - }, - Authenticated); - } - - public override Query? GetUnsubQuery() - { - return new KrakenSpotQuery( - new KrakenSubscribeRequest(_topic, _token, null, null, null, ExchangeHelpers.NextId()) - { - Event = "unsubscribe" - }, - Authenticated); - } - - public override void HandleSubQueryResponse(KrakenSubscriptionEvent message) - { - ListenerIdentifiers = new HashSet { message.ChannelName }; - } - - public override CallResult DoHandleMessage(SocketConnection connection, DataEvent message) - { - var data = (KrakenAuthSocketUpdate)message.Data!; - _handler.Invoke(message.As(data, data.ChannelName, null, data.Sequence.Sequence == 1 ? SocketUpdateType.Snapshot : SocketUpdateType.Update)); - return new CallResult(null); - } - - } -} diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenBalanceSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenBalanceSubscription.cs new file mode 100644 index 0000000..2234858 --- /dev/null +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenBalanceSubscription.cs @@ -0,0 +1,83 @@ +using CryptoExchange.Net.Converters.MessageParsing; +using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Sockets; +using Kraken.Net.Objects.Internal; +using Kraken.Net.Objects.Models.Socket; +using Kraken.Net.Objects.Models.Socket.Futures; +using Kraken.Net.Objects.Sockets.Queries; + +namespace Kraken.Net.Objects.Sockets.Subscriptions.Spot +{ + internal class KrakenBalanceSubscription : Subscription, KrakenSocketResponseV2> + { + private static readonly MessagePath _typePath = MessagePath.Get().Property("type"); + + private readonly Action>>? _snapshotHandler; + private readonly Action>> _updateHandler; + + private bool? _snapshot; + private string _token; + + public override HashSet ListenerIdentifiers { get; set; } + + public KrakenBalanceSubscription(ILogger logger, bool? snapshot, string token, Action>>? snapshotHandler, Action>> updateHandler) : base(logger, false) + { + _snapshot = snapshot; + _token = token; + + _snapshotHandler = snapshotHandler; + _updateHandler = updateHandler; + + ListenerIdentifiers = new HashSet { "balances" }; + } + + public override Type? GetMessageType(IMessageAccessor message) + { + if (message.GetValue(_typePath) == "snapshot") + return typeof(KrakenSocketUpdateV2>); + + return typeof(KrakenSocketUpdateV2>); + } + + public override Query? GetSubQuery(SocketConnection connection) + { + return new KrakenSpotQueryV2( + new KrakenSocketRequestV2() + { + Method = "subscribe", + RequestId = ExchangeHelpers.NextId(), + Parameters = new KrakenSocketSubRequest + { + Channel = "balances", + Snapshot = _snapshot, + Token = _token + } + }, false); + } + + public override Query? GetUnsubQuery() + { + return new KrakenSpotQueryV2( + new KrakenSocketRequestV2() + { + Method = "unsubscribe", + RequestId = ExchangeHelpers.NextId(), + Parameters = new KrakenSocketSubRequest + { + Channel = "balances", + Snapshot = _snapshot, + Token = _token + } + }, false); + } + + public override CallResult DoHandleMessage(SocketConnection connection, DataEvent message) + { + if (message.Data is KrakenSocketUpdateV2> snapshot) + _snapshotHandler?.Invoke(message.As(snapshot.Data, "balances", null, SocketUpdateType.Snapshot)); + else if (message.Data is KrakenSocketUpdateV2> update) + _updateHandler?.Invoke(message.As(update.Data, "balances", null, SocketUpdateType.Update)); + return new CallResult(null); + } + } +} diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenBookSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenBookSubscription.cs deleted file mode 100644 index bfad994..0000000 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenBookSubscription.cs +++ /dev/null @@ -1,82 +0,0 @@ -using CryptoExchange.Net; -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; -using CryptoExchange.Net.Sockets; -using Kraken.Net.Converters; -using Kraken.Net.Objects.Internal; -using Kraken.Net.Objects.Models; -using Kraken.Net.Objects.Sockets.Queries; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Kraken.Net.Objects.Sockets.Subscriptions.Spot -{ - internal class KrakenBookSubscription : Subscription - { - private string _topic; - private int? _depth; - private IEnumerable? _symbols; - private readonly Action> _handler; - - public override HashSet ListenerIdentifiers { get; set; } - - public KrakenBookSubscription(ILogger logger, IEnumerable? symbols, int? depth, Action> handler) : base(logger, false) - { - _topic = "book"; - _symbols = symbols; - _handler = handler; - _depth = depth; - - ListenerIdentifiers = symbols?.Any() == true ? new HashSet(symbols.Select(s => _topic.ToLowerInvariant() + "-" + s.ToLowerInvariant())) : new HashSet { _topic }; - } - public override Type? GetMessageType(IMessageAccessor message) => typeof(KrakenSocketUpdate); - - public override Query? GetSubQuery(SocketConnection connection) - { - return new KrakenSpotQuery( - new KrakenSubscribeRequest(_topic, null, null, null, _depth, ExchangeHelpers.NextId(), _symbols?.ToArray()) - { - Event = "subscribe", - }, - Authenticated) - { - RequiredResponses = _symbols.Count() - }; - } - - public override Query? GetUnsubQuery() - { - return new KrakenSpotQuery( - new KrakenSubscribeRequest(_topic, null, null, null, _depth, ExchangeHelpers.NextId(), _symbols?.ToArray()) - { - Event = "unsubscribe" - }, - Authenticated) - { - RequiredResponses = _symbols.Count() - }; - } - - public override CallResult Deserialize(IMessageAccessor message, Type type) - { - return new CallResult(StreamOrderBookConverter.Convert((JArray)message.Underlying!)!); - } - - public override void HandleSubQueryResponse(KrakenSubscriptionEvent message) - { - ListenerIdentifiers = _symbols?.Any() == true ? new HashSet(_symbols.Select(s => message.ChannelName + "-" + s.ToLowerInvariant())) : new HashSet { message.ChannelName }; - } - - public override CallResult DoHandleMessage(SocketConnection connection, DataEvent message) - { - var data = (KrakenSocketUpdate)message.Data!; - _handler.Invoke(message.As(data.Data, data.ChannelName, data.Symbol, data.Data.Snapshot ? SocketUpdateType.Snapshot : SocketUpdateType.Update)); - return new CallResult(null); - } - - } -} diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenOrderSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenOrderSubscription.cs new file mode 100644 index 0000000..da348a6 --- /dev/null +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenOrderSubscription.cs @@ -0,0 +1,77 @@ +using CryptoExchange.Net.Converters.MessageParsing; +using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Sockets; +using Kraken.Net.Objects.Internal; +using Kraken.Net.Objects.Models.Socket; +using Kraken.Net.Objects.Sockets.Queries; + +namespace Kraken.Net.Objects.Sockets.Subscriptions.Spot +{ + internal class KrakenOrderSubscription : Subscription, KrakenSocketResponseV2> + { + private static readonly MessagePath _typePath = MessagePath.Get().Property("type"); + + private readonly Action>> _updateHandler; + + private bool? _snapshotOrder; + private bool? _snapshotTrades; + private string _token; + + public override HashSet ListenerIdentifiers { get; set; } + + public KrakenOrderSubscription(ILogger logger, bool? snapshotOrder, bool? snapshotTrades, string token, Action>> updateHandler) : base(logger, false) + { + _snapshotOrder = snapshotOrder; + _snapshotTrades = snapshotTrades; + _token = token; + + _updateHandler = updateHandler; + + ListenerIdentifiers = new HashSet { "executions" }; + } + + public override Type? GetMessageType(IMessageAccessor message) + { + return typeof(KrakenSocketUpdateV2>); + } + + public override Query? GetSubQuery(SocketConnection connection) + { + return new KrakenSpotQueryV2( + new KrakenSocketRequestV2() + { + Method = "subscribe", + RequestId = ExchangeHelpers.NextId(), + Parameters = new KrakenSocketSubRequest + { + Channel = "executions", + SnapshotOrders = _snapshotOrder, + SnapshotTrades = _snapshotTrades, + Token = _token + } + }, false); + } + + public override Query? GetUnsubQuery() + { + return new KrakenSpotQueryV2( + new KrakenSocketRequestV2() + { + Method = "unsubscribe", + RequestId = ExchangeHelpers.NextId(), + Parameters = new KrakenSocketSubRequest + { + Channel = "executions", + Token = _token + } + }, false); + } + + public override CallResult DoHandleMessage(SocketConnection connection, DataEvent message) + { + var data = (KrakenSocketUpdateV2>)message.Data; + _updateHandler.Invoke(message.As(data.Data, "executions", null, data.Type == "snapshot" ? SocketUpdateType.Snapshot : SocketUpdateType.Update)); + return new CallResult(null); + } + } +} diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSubscription.cs deleted file mode 100644 index 4c292dd..0000000 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSubscription.cs +++ /dev/null @@ -1,101 +0,0 @@ -using CryptoExchange.Net; -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; -using CryptoExchange.Net.Sockets; -using Kraken.Net.Objects.Internal; -using Kraken.Net.Objects.Sockets.Queries; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Kraken.Net.Objects.Sockets.Subscriptions.Spot -{ - internal class KrakenSubscription : Subscription - { - private string _topic; - private int? _interval; - private bool? _snapshot; - private IEnumerable? _symbols; - private readonly Action> _handler; - - public override HashSet ListenerIdentifiers { get; set; } - - private static readonly Dictionary _replaceMap = new Dictionary - { - { "btc", "xbt" }, - { "doge", "xdg" }, - }; - - public KrakenSubscription(ILogger logger, string topic, IEnumerable? symbols, int? interval, bool? snapshot, Action> handler) : base(logger, false) - { - _topic = topic; - _symbols = symbols; - _handler = handler; - _snapshot = snapshot; - _interval = interval; - - ListenerIdentifiers = symbols?.Any() == true ? new HashSet(symbols.Select(s => topic.ToLowerInvariant() + "-" + GetSymbolTopic(s))) : new HashSet { topic }; - } - - private string GetSymbolTopic(string symbol) - { - var split = symbol.Split('/'); - if (split.Length < 2) - return symbol; - - var baseAsset = split[0].ToLowerInvariant(); - var quoteAsset = split[1].ToLowerInvariant(); - - if (_replaceMap.TryGetValue(baseAsset, out var replacementBase)) - baseAsset = replacementBase; - - if (_replaceMap.TryGetValue(quoteAsset, out var replacementQuote)) - quoteAsset = replacementQuote; - - return $"{baseAsset}/{quoteAsset}"; - } - - public override Type? GetMessageType(IMessageAccessor message) => typeof(KrakenSocketUpdate); - - public override Query? GetSubQuery(SocketConnection connection) - { - return new KrakenSpotQuery( - new KrakenSubscribeRequest(_topic, null, _interval, _snapshot, null, ExchangeHelpers.NextId(), _symbols?.ToArray()) - { - Event = "subscribe", - }, - Authenticated) - { - RequiredResponses = _symbols.Count() - }; - } - - public override Query? GetUnsubQuery() - { - return new KrakenSpotQuery( - new KrakenSubscribeRequest(_topic, null, _interval, _snapshot, null, ExchangeHelpers.NextId(), _symbols?.ToArray()) - { - Event = "unsubscribe" - }, - Authenticated) - { - RequiredResponses = _symbols.Count() - }; - } - - public override void HandleSubQueryResponse(KrakenSubscriptionEvent message) - { - ListenerIdentifiers = _symbols?.Any() == true ? new HashSet(_symbols.Select(s => message.ChannelName + "-" + GetSymbolTopic(s))) : new HashSet { message.ChannelName }; - } - - public override CallResult DoHandleMessage(SocketConnection connection, DataEvent message) - { - var data = (KrakenSocketUpdate)message.Data!; - _handler.Invoke(message.As(data.Data, data.ChannelName, data.Symbol, SocketUpdateType.Update)); - return new CallResult(null); - } - - } -} diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSubscriptionV2.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSubscriptionV2.cs new file mode 100644 index 0000000..5b6f288 --- /dev/null +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSubscriptionV2.cs @@ -0,0 +1,91 @@ +using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Sockets; +using Kraken.Net.Objects.Internal; +using Kraken.Net.Objects.Sockets.Queries; + +namespace Kraken.Net.Objects.Sockets.Subscriptions.Spot +{ + internal abstract class KrakenSubscription : Subscription, KrakenSocketResponseV2> + { + public string? Token { get; set; } + public bool TokenRequired { get; set; } + + protected KrakenSubscription(ILogger logger, bool auth) : base(logger, false) + { + TokenRequired = auth; + } + } + + internal class KrakenSubscriptionV2 : KrakenSubscription + { + private string _topic; + private int? _interval; + private bool? _snapshot; + private IEnumerable? _symbols; + private readonly Action> _handler; + + public override HashSet ListenerIdentifiers { get; set; } + + public KrakenSubscriptionV2(ILogger logger, string topic, IEnumerable? symbols, int? interval, bool? snapshot, string? token, Action> handler) : base(logger, token != null) + { + _topic = topic; + _symbols = symbols; + _handler = handler; + _snapshot = snapshot; + _interval = interval; + Token = token; + + ListenerIdentifiers = symbols?.Any() == true ? new HashSet(symbols.Select(s => topic + "-" + s)) : new HashSet { topic }; + } + + public override Type? GetMessageType(IMessageAccessor message) => typeof(KrakenSocketUpdateV2); + + public override Query? GetSubQuery(SocketConnection connection) + { + return new KrakenSpotQueryV2( + new KrakenSocketRequestV2() + { + Method = "subscribe", + RequestId = ExchangeHelpers.NextId(), + Parameters = new KrakenSocketSubRequest + { + Channel = _topic, + Symbol = _symbols?.ToArray(), + Interval = _interval, + Snapshot = _snapshot, + Token = Token + } + }, Authenticated) + { + RequiredResponses = _symbols?.Count() ?? 1 + }; + } + + public override Query? GetUnsubQuery() + { + return new KrakenSpotQueryV2( + new KrakenSocketRequestV2() + { + Method = "unsubscribe", + RequestId = ExchangeHelpers.NextId(), + Parameters = new KrakenSocketSubRequest + { + Channel = _topic, + Symbol = _symbols?.ToArray(), + Interval = _interval + } + }, Authenticated) + { + RequiredResponses = _symbols?.Count() ?? 1 + }; + } + + public override CallResult DoHandleMessage(SocketConnection connection, DataEvent message) + { + var data = (KrakenSocketUpdateV2)message.Data!; + _handler.Invoke(message.As(data.Data, data.Channel, null, data.Type == "snapshot" ? SocketUpdateType.Snapshot : SocketUpdateType.Update)); + return new CallResult(null); + } + + } +} diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSystemStatusSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSystemStatusSubscription.cs index 5798b08..a9de313 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSystemStatusSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSystemStatusSubscription.cs @@ -1,11 +1,7 @@ -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Sockets; +using Kraken.Net.Objects.Internal; using Kraken.Net.Objects.Models.Socket; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; namespace Kraken.Net.Objects.Sockets.Subscriptions.Spot { @@ -13,7 +9,7 @@ internal class KrakenSystemStatusSubscription : Subscription { private readonly Action> _handler; - public override HashSet ListenerIdentifiers { get; set; } = new HashSet() { "systemstatus" }; + public override HashSet ListenerIdentifiers { get; set; } = new HashSet() { "status" }; public KrakenSystemStatusSubscription(ILogger logger, Action> handler) : base(logger, false) { @@ -26,11 +22,11 @@ public KrakenSystemStatusSubscription(ILogger logger, Action message) { - var data = (KrakenStreamSystemStatus)message.Data!; - _handler.Invoke(message.As(data, data.Event, null, SocketUpdateType.Update)); + var data = (KrakenSocketUpdateV2>)message.Data!; + _handler.Invoke(message.As(data.Data.First(), data.Channel, null, SocketUpdateType.Update)); return new CallResult(null); } - public override Type? GetMessageType(IMessageAccessor message) => typeof(KrakenStreamSystemStatus); + public override Type? GetMessageType(IMessageAccessor message) => typeof(KrakenSocketUpdateV2>); } } diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/SystemStatusSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/SystemStatusSubscription.cs index 721c26e..c9b9a19 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/SystemStatusSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/SystemStatusSubscription.cs @@ -1,16 +1,12 @@ -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Sockets; using Kraken.Net.Objects.Models.Socket; -using Microsoft.Extensions.Logging; -using System.Collections.Generic; -using System.Threading.Tasks; namespace Kraken.Net.Objects.Sockets.Subscriptions.Spot { internal class SystemStatusSubscription : SystemSubscription { - public override HashSet ListenerIdentifiers { get; set; } = new HashSet { "systemStatus" }; + public override HashSet ListenerIdentifiers { get; set; } = new HashSet { "status" }; public SystemStatusSubscription(ILogger logger) : base(logger, false) { diff --git a/Kraken.Net/SymbolOrderBooks/KrakenFuturesSymbolOrderBook.cs b/Kraken.Net/SymbolOrderBooks/KrakenFuturesSymbolOrderBook.cs index 41bab5e..b76e2ee 100644 --- a/Kraken.Net/SymbolOrderBooks/KrakenFuturesSymbolOrderBook.cs +++ b/Kraken.Net/SymbolOrderBooks/KrakenFuturesSymbolOrderBook.cs @@ -1,15 +1,9 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using CryptoExchange.Net.Interfaces; -using CryptoExchange.Net.Objects; -using CryptoExchange.Net.Objects.Sockets; +using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.OrderBook; using Kraken.Net.Clients; using Kraken.Net.Interfaces.Clients; using Kraken.Net.Objects.Models.Socket.Futures; using Kraken.Net.Objects.Options; -using Microsoft.Extensions.Logging; namespace Kraken.Net.SymbolOrderBooks { diff --git a/Kraken.Net/SymbolOrderBooks/KrakenOrderBookFactory.cs b/Kraken.Net/SymbolOrderBooks/KrakenOrderBookFactory.cs index 79d7eba..92d5db5 100644 --- a/Kraken.Net/SymbolOrderBooks/KrakenOrderBookFactory.cs +++ b/Kraken.Net/SymbolOrderBooks/KrakenOrderBookFactory.cs @@ -1,11 +1,8 @@ -using CryptoExchange.Net.Interfaces; -using CryptoExchange.Net.OrderBook; +using CryptoExchange.Net.OrderBook; using Kraken.Net.Interfaces; using Kraken.Net.Interfaces.Clients; using Kraken.Net.Objects.Options; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System; namespace Kraken.Net.SymbolOrderBooks { diff --git a/Kraken.Net/SymbolOrderBooks/KrakenSpotSymbolOrderBook.cs b/Kraken.Net/SymbolOrderBooks/KrakenSpotSymbolOrderBook.cs index 0b3b3dc..dcf71eb 100644 --- a/Kraken.Net/SymbolOrderBooks/KrakenSpotSymbolOrderBook.cs +++ b/Kraken.Net/SymbolOrderBooks/KrakenSpotSymbolOrderBook.cs @@ -1,18 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using CryptoExchange.Net.Objects; +using System.Text; using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.OrderBook; using Force.Crc32; using Kraken.Net.Clients; using Kraken.Net.Interfaces.Clients; using Kraken.Net.Objects.Models; +using Kraken.Net.Objects.Models.Socket; using Kraken.Net.Objects.Options; -using Microsoft.Extensions.Logging; namespace Kraken.Net.SymbolOrderBooks { @@ -23,7 +17,6 @@ public class KrakenSpotSymbolOrderBook : SymbolOrderBook { private readonly IKrakenSocketClient _socketClient; private readonly bool _clientOwner; - private bool _initialSnapshotDone; private readonly TimeSpan _initialDataTimeout; /// @@ -66,7 +59,7 @@ public KrakenSpotSymbolOrderBook(string symbol, /// protected override async Task> DoStartAsync(CancellationToken ct) { - var result = await _socketClient.SpotApi.SubscribeToOrderBookUpdatesAsync(Symbol, Levels!.Value, ProcessUpdate).ConfigureAwait(false); + var result = await _socketClient.SpotApi.SubscribeToAggregatedOrderBookUpdatesAsync(Symbol, Levels!.Value, ProcessUpdate, true).ConfigureAwait(false); if (!result) return result; @@ -82,24 +75,17 @@ protected override async Task> DoStartAsync(Cance return setResult ? result : new CallResult(setResult.Error!); } - /// - protected override void DoReset() - { - _initialSnapshotDone = false; - } - - private void ProcessUpdate(DataEvent data) + private void ProcessUpdate(DataEvent data) { - if (!_initialSnapshotDone) + if (data.UpdateType == SocketUpdateType.Snapshot) { - var maxNumber = Math.Max(data.Data.Bids.Max(b => b.Sequence), data.Data.Asks.Max(b => b.Sequence)); - SetInitialOrderBook(maxNumber, data.Data.Bids, data.Data.Asks); - _initialSnapshotDone = true; + SetInitialOrderBook(DateTime.UtcNow.Ticks, data.Data.Bids, data.Data.Asks); } else { - UpdateOrderBook(data.Data.Bids, data.Data.Asks); - AddChecksum((int)data.Data.Checksum!); + UpdateOrderBook(DateTime.UtcNow.Ticks, data.Data.Bids, data.Data.Asks); + if (data.Data.Checksum <= int.MaxValue) + AddChecksum((int)data.Data.Checksum!); } } @@ -109,15 +95,15 @@ protected override bool DoChecksum(int checksum) var checksumValues = new List(); for (var i = 0; i < 10; i++) { - var ask = (KrakenStreamOrderBookEntry)_asks.ElementAt(i).Value; - checksumValues.Add(ToChecksumString(ask.RawPrice)); - checksumValues.Add(ToChecksumString(ask.RawQuantity)); + var ask = (KrakenBookUpdateEntry)_asks.ElementAt(i).Value; + checksumValues.Add(ToChecksumString(ask.Price)); + checksumValues.Add(ToChecksumString(ask.Quantity)); } for (var i = 0; i < 10; i++) { - var bid = (KrakenStreamOrderBookEntry)_bids.ElementAt(i).Value; - checksumValues.Add(ToChecksumString(bid.RawPrice)); - checksumValues.Add(ToChecksumString(bid.RawQuantity)); + var bid = (KrakenBookUpdateEntry)_bids.ElementAt(i).Value; + checksumValues.Add(ToChecksumString(bid.Price)); + checksumValues.Add(ToChecksumString(bid.Quantity)); } var checksumString = string.Join("", checksumValues); @@ -132,9 +118,9 @@ protected override bool DoChecksum(int checksum) return true; } - private static string ToChecksumString(string value) + private static string ToChecksumString(decimal value) { - return value.Replace(".", "").TrimStart('0'); + return value.ToString(CultureInfo.InvariantCulture).Replace(".", "").TrimStart('0'); } /// diff --git a/Kraken.Net/Usings.cs b/Kraken.Net/Usings.cs index d7109a0..94bf01c 100644 --- a/Kraken.Net/Usings.cs +++ b/Kraken.Net/Usings.cs @@ -1,17 +1,15 @@ global using CryptoExchange.Net; global using CryptoExchange.Net.Authentication; -global using CryptoExchange.Net.Converters.JsonNet; +global using CryptoExchange.Net.Converters.SystemTextJson; global using CryptoExchange.Net.Interfaces; global using CryptoExchange.Net.Objects; global using Microsoft.Extensions.Logging; -global using Newtonsoft.Json; -global using Newtonsoft.Json.Linq; -global using Newtonsoft.Json.Serialization; +global using System.Text.Json; +global using System.Text.Json.Serialization; global using System; global using System.Collections.Generic; global using System.Globalization; global using System.Linq; global using System.Net.Http; -global using System.Security; global using System.Threading; global using System.Threading.Tasks; \ No newline at end of file diff --git a/README.md b/README.md index 69d01c3..8691bf3 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ var lastPrice = tickerResult.Data.First().Value.LastTrade.Price; var socketClient = new KrakenSocketClient(); var tickerSubscriptionResult = socketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH/USD", (update) => { - var lastPrice = update.Data.LastTrade.Price; + var lastPrice = update.Data.LastPrice; }); ``` @@ -92,42 +92,58 @@ A Discord server is available [here](https://discord.gg/MSpeEtSY8t). Feel free t ## Supported functionality -### Spot Api +### Spot REST |API|Supported|Location| |--|--:|--| -|Spot Market Data|✓|`restClient.SpotApi.ExchangeData`| -|Nft Market Data|X|| +|Market Data|✓|`restClient.SpotApi.ExchangeData`| |Account Data|✓|`restClient.SpotApi.Account` / `restClient.SpotApi.Trading`| -|Spot Trading|✓|`restClient.SpotApi.Trading`| -|NFT Trading|X|| +|Trading|✓|`restClient.SpotApi.Trading`| |Funding|✓|`restClient.SpotApi.Account`| |Subaccounts|X|| |Earn|✓|`restClient.SpotApi.Earn`| -|Websocket Public Messages|✓|`socketClient.SpotApi`| -|Websocket Private Messages|✓|`socketClient.SpotApi`| +|NFT Market Data|X|| +|NFT Trading|X|| + +### Spot Api Websocket V2 +|API|Supported|Location| +|--|--:|--| +|User Trading|✓|`socketClient.SpotApi`| +|User Data|✓|`socketClient.SpotApi`| +|Market Data|✓|`socketClient.SpotApi`| +|Admin|✓|`socketClient.SpotApi`| -### Futures Api +### Spot FIX |API|Supported|Location| |--|--:|--| -|Account Information|✓|`restClient.FuturesApi.Account`| +|*|X|| + +### Futures REST +|API|Supported|Location| +|--|--:|--| +|Trading Market Data|✓|`restClient.FuturesApi.ExchangeData`| +|Trading Instrument Details|✓|`restClient.FuturesApi.ExchangeData`| +|Trading Fee Schedules|✓|`restClient.FuturesApi.ExchangeData`| +|Trading Account Info|✓|`restClient.FuturesApi.Account` / `restClient.FuturesApi.Trading`| +|Trading Order Management|✓|`restClient.FuturesApi.Trading`| +|Subaccounts|X|| +|Transfers|✓|`restClient.FuturesApi.Account`| |Assignment Program|X|| -|Fee Schedules|✓|`restClient.FuturesApi.ExchangeData`| +|Multi-Collateral|✓|`restClient.FuturesApi.Account` / `restClient.FuturesApi.Trading`| |General|✓|`restClient.FuturesApi.ExchangeData`| |Historical data|✓|`restClient.FuturesApi.Trading`| |Historical Funding Rates|✓|`restClient.FuturesApi.ExchangeData`| -|Instrument Details|✓|`restClient.FuturesApi.ExchangeData`| -|Market Data|✓|`restClient.FuturesApi.ExchangeData`| -|Multi-Collateral|✓|`restClient.FuturesApi.Account` / `restClient.FuturesApi.Trading`| -|Order Management|✓|`restClient.FuturesApi.Trading`| -|Subaccounts|X|| -|Trading settings|X|| -|Transfers|✓|`restClient.FuturesApi.Account`| -|Account History|✓|`restClient.FuturesApi.Account` / `restClient.FuturesApi.Trading`| -|Market History|✓|`restClient.FuturesApi.ExchangeData`| -|Analytics|X|| -|Candles|✓|`restClient.FuturesApi.ExchangeData`| -|Websocket Public Feeds|✓|`socketClient.FuturesApi`| -|Websocket Private Feeds|✓|`socketClient.FuturesApi`| +|History Account History|✓|`restClient.FuturesApi.Account` / `restClient.FuturesApi.Trading`| +|History Market History|✓|`restClient.FuturesApi.ExchangeData`| +|Charts Candles|✓|`restClient.FuturesApi.ExchangeData`| +|Charts Analytics|X|| + +### Futures Websocket +|API|Supported|Location| +|--|--:|--| +|Trading Market Data|✓|`restClient.FuturesApi.ExchangeData`| +|User Data|✓|`socketClient.FuturesApi`| +|Market Data|✓|`socketClient.FuturesApi`| +|Admin|✓|`socketClient.FuturesApi`| ## Support the project Any support is greatly appreciated.