diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..4a8d999
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,8 @@
+language: csharp
+mono: none
+solution: Kraken.Net.sln
+dotnet: 2.0.0
+dist: xenial
+script:
+ - dotnet build Kraken.Net/Kraken.Net.csproj --framework "netstandard2.0"
+ - dotnet test Kraken.Net.UnitTests/Kraken.Net.UnitTests.csproj
\ No newline at end of file
diff --git a/Kraken.Net.UnitTests/Kraken.Net.UnitTests.csproj b/Kraken.Net.UnitTests/Kraken.Net.UnitTests.csproj
new file mode 100644
index 0000000..87973b4
--- /dev/null
+++ b/Kraken.Net.UnitTests/Kraken.Net.UnitTests.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ netcoreapp2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Kraken.Net.UnitTests/KrakenClientTests.cs b/Kraken.Net.UnitTests/KrakenClientTests.cs
new file mode 100644
index 0000000..882871f
--- /dev/null
+++ b/Kraken.Net.UnitTests/KrakenClientTests.cs
@@ -0,0 +1,83 @@
+using Newtonsoft.Json;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using CryptoExchange.Net.Authentication;
+using CryptoExchange.Net.Logging;
+using CryptoExchange.Net.Objects;
+using Kraken.Net.Objects;
+using Kraken.Net.UnitTests.TestImplementations;
+
+namespace Kraken.Net.UnitTests
+{
+ [TestFixture]
+ public class KrakenClientTests
+ {
+ [TestCase()]
+ public void TestConversions()
+ {
+ var ignoreMethods = new string[]
+ {
+ "GetMarkets",
+ "GetOrderBook",
+ };
+ var defaultParameterValues = new Dictionary
+ {
+ { "assets", new [] { "XBTUSD" } },
+ {"clientOrderId", null }
+ };
+
+ var methods = typeof(KrakenClient).GetMethods(BindingFlags.Public | BindingFlags.Instance);
+ var callResultMethods = methods.Where(m => m.ReturnType.IsGenericType && m.ReturnType.GetGenericTypeDefinition() == typeof(WebCallResult<>));
+ foreach (var method in callResultMethods)
+ {
+ if (ignoreMethods.Contains(method.Name))
+ continue;
+
+ var expectedType = method.ReturnType.GetGenericArguments()[0];
+ var expected = typeof(TestHelpers).GetMethod("CreateObjectWithTestParameters").MakeGenericMethod(expectedType).Invoke(null, null);
+ var parameters = TestHelpers.CreateParametersForMethod(method, defaultParameterValues);
+ var client = TestHelpers.CreateResponseClient(SerializeExpected(expected), new KrakenClientOptions(){ ApiCredentials = new ApiCredentials("Test", "Test"), LogVerbosity = LogVerbosity.Debug});
+
+ // act
+ var result = method.Invoke(client, parameters);
+ var callResult = result.GetType().GetProperty("Success").GetValue(result);
+ var data = result.GetType().GetProperty("Data").GetValue(result);
+
+ // assert
+ Assert.AreEqual(true, callResult);
+ Assert.IsTrue(TestHelpers.AreEqual(expected, data), method.Name);
+ }
+ }
+
+
+ [TestCase()]
+ public void TestErrorResult_Should_ResultInFailedCall()
+ {
+ // arrange
+ var client = TestHelpers.CreateAuthResponseClient($"{{\"error\": [\"first error\", \"another error\"], \"result\": null}}");
+
+ // act
+ var result = client.GetMarkets();
+
+ // assert
+ Assert.IsFalse(result.Success);
+ Assert.IsTrue(result.Error.Message.Contains("first error"));
+ Assert.IsTrue(result.Error.Message.Contains("another error"));
+ }
+
+ public string SerializeExpected(T data)
+ {
+ var result = new KrakenResult()
+ {
+ Result = data,
+ Error = new string[] {}
+ };
+
+ return JsonConvert.SerializeObject(result);
+ }
+ }
+}
diff --git a/Kraken.Net.UnitTests/KrakenSocketClientTests.cs b/Kraken.Net.UnitTests/KrakenSocketClientTests.cs
new file mode 100644
index 0000000..680efe9
--- /dev/null
+++ b/Kraken.Net.UnitTests/KrakenSocketClientTests.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using CryptoExchange.Net;
+using CryptoExchange.Net.Objects;
+using Kraken.Net.UnitTests.TestImplementations;
+using Kucoin.Net.UnitTests.TestImplementations;
+using Newtonsoft.Json;
+using NUnit.Framework;
+
+namespace Kraken.Net.UnitTests
+{
+ [TestFixture]
+ public class KrakenSocketClientTests
+ {
+ [Test]
+ public void Subscribe_Should_SucceedIfAckResponse()
+ {
+ // arrange
+ var socket = new TestSocket();
+ socket.CanConnect = true;
+ var client = TestHelpers.CreateSocketClient(socket);
+
+ // act
+ var subTask = client.SubscribeToTickerUpdatesAsync("test", test => { });
+ socket.InvokeMessage($"{{\"channelID\": 1, \"status\": \"subscribed\", \"reqid\":\"{BaseClient.LastId}\"}}");
+ var subResult = subTask.Result;
+
+ // assert
+ Assert.IsTrue(subResult.Success);
+ }
+
+ [Test]
+ public void Subscribe_Should_FailIfNotAckResponse()
+ {
+ // arrange
+ var socket = new TestSocket();
+ socket.CanConnect = true;
+ var client = TestHelpers.CreateSocketClient(socket);
+
+ // act
+ var subTask = client.SubscribeToTickerUpdatesAsync("test", test => { });
+ socket.InvokeMessage($"{{\"channelID\": 1, \"status\": \"error\", \"errormessage\": \"Failed to sub\", \"reqid\":\"{BaseClient.LastId}\"}}");
+ var subResult = subTask.Result;
+
+ // assert
+ Assert.IsFalse(subResult.Success);
+ Assert.IsTrue(subResult.Error.Message.Contains("Failed to sub"));
+ }
+ }
+}
diff --git a/Kraken.Net.UnitTests/TestImplementations/TestHelpers.cs b/Kraken.Net.UnitTests/TestImplementations/TestHelpers.cs
new file mode 100644
index 0000000..da8c271
--- /dev/null
+++ b/Kraken.Net.UnitTests/TestImplementations/TestHelpers.cs
@@ -0,0 +1,316 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Net;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using CryptoExchange.Net;
+using CryptoExchange.Net.Authentication;
+using CryptoExchange.Net.Interfaces;
+using CryptoExchange.Net.Logging;
+using Kraken.Net.Interfaces;
+using Moq;
+using Newtonsoft.Json;
+
+namespace Kraken.Net.UnitTests.TestImplementations
+{
+ public class TestHelpers
+ {
+ [ExcludeFromCodeCoverage]
+ public static bool AreEqual(object self, object to, params string[] ignore)
+ {
+ if (self == null && to == null)
+ return true;
+
+ if ((self != null && to == null) || (self == null && to != null))
+ return false;
+
+ var type = self.GetType();
+ if (type.IsArray)
+ {
+ var list = (Array) self;
+ var listOther = (Array)to;
+ for (int i = 0; i < list.Length; i++)
+ {
+ if(!AreEqual(list.GetValue(i), listOther.GetValue(i)))
+ return false;
+ }
+
+ return true;
+ }
+
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
+ {
+ var dict = (IDictionary) self;
+ var other = (IDictionary) to;
+ var items1 = new List();
+ var items2 = new List();
+ foreach (DictionaryEntry item in dict)
+ items1.Add(item);
+ foreach (DictionaryEntry item in other)
+ items2.Add(item);
+
+ for (int i = 0; i < items1.Count; i++)
+ {
+ if (!AreEqual(items1[i].Key, items2[i].Key))
+ return false;
+ if (!AreEqual(items1[i].Value, items2[i].Value))
+ return false;
+ }
+
+ return true;
+ }
+
+
+ if (type.IsValueType || type == typeof(string))
+ return Equals(self, to);
+
+ var ignoreList = new List(ignore);
+ foreach (var pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ {
+ if (ignoreList.Contains(pi.Name))
+ continue;
+
+ var selfValue = type.GetProperty(pi.Name).GetValue(self, null);
+ var toValue = type.GetProperty(pi.Name).GetValue(to, null);
+
+ if (pi.PropertyType.IsValueType || pi.PropertyType == typeof(string))
+ {
+ if (!Equals(selfValue, toValue))
+ return false;
+ continue;
+ }
+
+ if (pi.PropertyType.IsClass)
+ {
+ if (!AreEqual(selfValue, toValue, ignore))
+ return false;
+ continue;
+ }
+
+ if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)))
+ return false;
+ }
+
+ return true;
+ }
+
+ public static KrakenSocketClient CreateSocketClient(IWebsocket socket, KrakenSocketClientOptions options = null)
+ {
+ KrakenSocketClient client;
+ client = options != null ? new KrakenSocketClient(options) : new KrakenSocketClient(new KrakenSocketClientOptions() { LogVerbosity = LogVerbosity.Debug, ApiCredentials = new ApiCredentials("Test", "Test") });
+ client.SocketFactory = Mock.Of();
+ Mock.Get(client.SocketFactory).Setup(f => f.CreateWebsocket(It.IsAny(), It.IsAny())).Returns(socket);
+ return client;
+ }
+
+ public static KrakenSocketClient CreateAuthenticatedSocketClient(IWebsocket socket, KrakenSocketClientOptions options = null)
+ {
+ KrakenSocketClient client;
+ client = options != null ? new KrakenSocketClient(options) : new KrakenSocketClient(new KrakenSocketClientOptions(){ LogVerbosity = LogVerbosity.Debug, ApiCredentials = new ApiCredentials("Test", "Test")});
+ client.SocketFactory = Mock.Of();
+ Mock.Get(client.SocketFactory).Setup(f => f.CreateWebsocket(It.IsAny(), It.IsAny())).Returns(socket);
+ return client;
+ }
+
+ public static IKrakenClient CreateClient(KrakenClientOptions options = null)
+ {
+ IKrakenClient client;
+ client = options != null ? new KrakenClient(options) : new KrakenClient(new KrakenClientOptions(){LogVerbosity = LogVerbosity.Debug});
+ client.RequestFactory = Mock.Of();
+ return client;
+ }
+
+ public static IKrakenClient CreateAuthResponseClient(string response)
+ {
+ var client = (KrakenClient)CreateClient(new KrakenClientOptions(){ ApiCredentials = new ApiCredentials("Test", "Test")});
+ SetResponse(client, response);
+ return client;
+ }
+
+
+ public static IKrakenClient CreateResponseClient(string response, KrakenClientOptions options = null)
+ {
+ var client = (KrakenClient)CreateClient(options);
+ SetResponse(client, response);
+ return client;
+ }
+
+ public static IKrakenClient CreateResponseClient(T response, KrakenClientOptions options = null)
+ {
+ var client = (KrakenClient)CreateClient(options);
+ SetResponse(client, JsonConvert.SerializeObject(response));
+ return client;
+ }
+
+ public static void SetResponse(RestClient client, string responseData)
+ {
+ var expectedBytes = Encoding.UTF8.GetBytes(responseData);
+ var responseStream = new MemoryStream();
+ responseStream.Write(expectedBytes, 0, expectedBytes.Length);
+ responseStream.Seek(0, SeekOrigin.Begin);
+
+ var response = new Mock();
+ response.Setup(c => c.GetResponseStream()).Returns(responseStream);
+
+ var request = new Mock();
+ request.Setup(c => c.Headers).Returns(new WebHeaderCollection());
+ request.Setup(c => c.Uri).Returns(new Uri("http://www.test.com"));
+ request.Setup(c => c.GetResponse()).Returns(Task.FromResult(response.Object));
+ request.Setup(c => c.GetRequestStream()).Returns(Task.FromResult((Stream)new MemoryStream()));
+
+ var factory = Mock.Get(client.RequestFactory);
+ factory.Setup(c => c.Create(It.IsAny()))
+ .Returns(request.Object);
+ }
+
+ public static void SetErrorWithResponse(IKrakenClient client, string responseData, HttpStatusCode code)
+ {
+ var expectedBytes = Encoding.UTF8.GetBytes(responseData);
+ var responseStream = new MemoryStream();
+ responseStream.Write(expectedBytes, 0, expectedBytes.Length);
+ responseStream.Seek(0, SeekOrigin.Begin);
+
+ var r = new Mock();
+ r.Setup(x => x.GetResponseStream()).Returns(responseStream);
+ var we = new WebException("", null, WebExceptionStatus.Success, r.Object);
+
+ var request = new Mock();
+ request.Setup(c => c.Headers).Returns(new WebHeaderCollection());
+ request.Setup(c => c.GetResponse()).Throws(we);
+
+ var factory = Mock.Get(client.RequestFactory);
+ factory.Setup(c => c.Create(It.IsAny()))
+ .Returns(request.Object);
+ }
+
+
+ public static T CreateObjectWithTestParameters() where T: class
+ {
+ var type = typeof(T);
+ if (type.IsArray)
+ {
+ var elementType = type.GetElementType();
+ var result = Array.CreateInstance(elementType, 2);
+ result.SetValue(GetTestValue(elementType, 0), 0);
+ result.SetValue(GetTestValue(elementType, 1), 1);
+ return (T)Convert.ChangeType(result, type);
+ }
+ else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
+ {
+ var result = (IDictionary)Activator.CreateInstance(type);
+ result.Add(GetTestValue(type.GetGenericArguments()[0], 0), GetTestValue(type.GetGenericArguments()[1], 0));
+ result.Add(GetTestValue(type.GetGenericArguments()[0], 1), GetTestValue(type.GetGenericArguments()[1], 1));
+ return (T)Convert.ChangeType(result, type);
+ }
+ else
+ {
+ var obj = Activator.CreateInstance();
+ return FillWithTestParameters(obj);
+ }
+ }
+
+ public static T FillWithTestParameters(T obj) where T : class
+ {
+
+ var properties = obj.GetType().GetProperties();
+ int i = 1;
+ foreach (var property in properties)
+ {
+ var value = GetTestValue(property.PropertyType, i);
+ Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
+ object safeValue = (value == null) ? null : Convert.ChangeType(value, t);
+ property.SetValue(obj, safeValue, null);
+ i++;
+ }
+
+ return obj;
+ }
+
+ public static object[] CreateParametersForMethod(MethodInfo method, Dictionary defaultValues)
+ {
+ var param = method.GetParameters();
+ var result = new object[param.Length];
+ for(int i = 0; i < param.Length; i++)
+ {
+ if (defaultValues.ContainsKey(param[i].Name))
+ result[i] = defaultValues[param[i].Name];
+ else
+ result[i] = GetTestValue(param[i].ParameterType, i);
+ }
+
+ return result;
+ }
+
+ public static object GetTestValue(Type type, int i)
+ {
+ if (type == typeof(bool))
+ return true;
+
+ if (type == typeof(bool?))
+ return (bool?) true;
+
+ if (type == typeof(decimal))
+ return i / 10m;
+
+ if (type == typeof(decimal?))
+ return (decimal?) (i / 10m);
+
+ if (type == typeof(int) || type == typeof(long))
+ return i;
+
+ if (type == typeof(int?))
+ return (int?) i;
+
+ if (type == typeof(long?))
+ return (long?) i;
+
+ if (type == typeof(DateTime))
+ return new DateTime(2019, 1, Math.Max(i, 1));
+
+ if(type == typeof(DateTime?))
+ return (DateTime?) new DateTime(2019, 1, Math.Max(i, 1));
+
+ if (type == typeof(string))
+ return "string" + i;
+
+ if (type.IsEnum)
+ {
+ return Activator.CreateInstance(type);
+ }
+
+ if (type.IsArray)
+ {
+ var elementType = type.GetElementType();
+ var result = Array.CreateInstance(elementType, 2);
+ result.SetValue(GetTestValue(elementType, 0), 0);
+ result.SetValue(GetTestValue(elementType, 1), 1);
+ return result;
+ }
+
+ if (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(List<>)))
+ {
+ var result = (IList)Activator.CreateInstance(type);
+ result.Add(GetTestValue(type.GetGenericArguments()[0], 0));
+ result.Add(GetTestValue(type.GetGenericArguments()[0], 1));
+ return result;
+ }
+
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
+ {
+ var result = (IDictionary)Activator.CreateInstance(type);
+ result.Add(GetTestValue(type.GetGenericArguments()[0], 0), GetTestValue(type.GetGenericArguments()[1], 0));
+ result.Add(GetTestValue(type.GetGenericArguments()[0], 1), GetTestValue(type.GetGenericArguments()[1], 1));
+ return Convert.ChangeType(result, type);
+ }
+
+ if (type.IsClass)
+ return FillWithTestParameters(Activator.CreateInstance(type));
+
+ return null;
+ }
+ }
+}
diff --git a/Kraken.Net.UnitTests/TestImplementations/TestSocket.cs b/Kraken.Net.UnitTests/TestImplementations/TestSocket.cs
new file mode 100644
index 0000000..4412652
--- /dev/null
+++ b/Kraken.Net.UnitTests/TestImplementations/TestSocket.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Security.Authentication;
+using System.Text;
+using System.Threading.Tasks;
+using CryptoExchange.Net.Interfaces;
+using Newtonsoft.Json;
+using WebSocket4Net;
+
+namespace Kucoin.Net.UnitTests.TestImplementations
+{
+ public class TestSocket: IWebsocket
+ {
+ public bool CanConnect { get; set; }
+ public bool Connected { get; set; }
+
+ public event Action OnClose;
+ public event Action OnMessage;
+ public event Action OnError;
+ public event Action OnOpen;
+
+ public int Id { get; }
+ public bool ShouldReconnect { get; set; }
+ public Func DataInterpreterString { get; set; }
+ public Func DataInterpreterBytes { get; set; }
+ public DateTime? DisconnectTime { get; set; }
+ public string Url { get; }
+ public WebSocketState SocketState { get; }
+ public bool IsClosed => !Connected;
+ public bool IsOpen => Connected;
+ public bool PingConnection { get; set; }
+ public TimeSpan PingInterval { get; set; }
+ public SslProtocols SSLProtocols { get; set; }
+ public TimeSpan Timeout { get; set; }
+ public string Origin { get; set; }
+ public bool Reconnecting { get; set; }
+
+ public Task Connect()
+ {
+ Connected = CanConnect;
+ return Task.FromResult(CanConnect);
+ }
+
+ public void Send(string data)
+ {
+ if(!Connected)
+ throw new Exception("Socket not connected");
+ }
+
+ public void Reset()
+ {
+
+ }
+
+ public Task Close()
+ {
+ Connected = false;
+ return Task.FromResult(0);
+ }
+
+ public void SetProxy(string host, int port)
+ {
+ throw new NotImplementedException();
+ }
+ public void Dispose()
+ {
+ }
+
+ public void InvokeClose()
+ {
+ Connected = false;
+ OnClose?.Invoke();
+ }
+
+ public void InvokeOpen()
+ {
+ OnOpen?.Invoke();
+ }
+
+ public void InvokeMessage(string data)
+ {
+ OnMessage?.Invoke(data);
+ }
+
+ public void InvokeMessage(T data)
+ {
+ OnMessage?.Invoke(JsonConvert.SerializeObject(data));
+ }
+ }
+}
diff --git a/Kraken.Net.sln b/Kraken.Net.sln
new file mode 100644
index 0000000..b16b39a
--- /dev/null
+++ b/Kraken.Net.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.28822.285
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kraken.Net", "Kraken.Net\Kraken.Net.csproj", "{FDB8D284-1B3D-4258-BF89-E2DBCADF3DE8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kraken.Net.UnitTests", "Kraken.Net.UnitTests\Kraken.Net.UnitTests.csproj", "{8BB6B9AF-27E3-4C6B-8B85-1CDA0697497C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {FDB8D284-1B3D-4258-BF89-E2DBCADF3DE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FDB8D284-1B3D-4258-BF89-E2DBCADF3DE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FDB8D284-1B3D-4258-BF89-E2DBCADF3DE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FDB8D284-1B3D-4258-BF89-E2DBCADF3DE8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8BB6B9AF-27E3-4C6B-8B85-1CDA0697497C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8BB6B9AF-27E3-4C6B-8B85-1CDA0697497C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8BB6B9AF-27E3-4C6B-8B85-1CDA0697497C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8BB6B9AF-27E3-4C6B-8B85-1CDA0697497C}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {D55A3C45-A9F3-47E3-B382-8748EA6061BD}
+ EndGlobalSection
+EndGlobal
diff --git a/Kraken.Net/AssemblyInfo.cs b/Kraken.Net/AssemblyInfo.cs
new file mode 100644
index 0000000..b9e00a6
--- /dev/null
+++ b/Kraken.Net/AssemblyInfo.cs
@@ -0,0 +1 @@
+[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Kraken.Net.UnitTests")]
\ No newline at end of file
diff --git a/Kraken.Net/Converters/ExtendedDictionaryConverter.cs b/Kraken.Net/Converters/ExtendedDictionaryConverter.cs
new file mode 100644
index 0000000..99007eb
--- /dev/null
+++ b/Kraken.Net/Converters/ExtendedDictionaryConverter.cs
@@ -0,0 +1,83 @@
+using System;
+using CryptoExchange.Net.Converters;
+using Kraken.Net.Objects;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Kraken.Net.Converters
+{
+ internal class ExtendedDictionaryConverter: JsonConverter
+ {
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ var data = (KrakenDictionaryResult) value;
+ writer.WriteStartObject();
+ writer.WritePropertyName("data");
+ writer.WriteRawValue(JsonConvert.SerializeObject(data.Data));
+ writer.WritePropertyName("last");
+ writer.WriteValue(JsonConvert.SerializeObject(data.Last, new TimestampSecondsConverter()));
+ writer.WriteEndObject();
+ }
+
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ var obj = JObject.Load(reader);
+ var inner = obj.First;
+ var data = inner.First.ToObject();
+ var result = (KrakenDictionaryResult)Activator.CreateInstance(objectType);
+ result.Data = data;
+ var timestamp = (long)obj["last"];
+ if(timestamp > 1000000000000000000)
+ result.Last = obj["last"].ToObject(new JsonSerializer() {Converters = {new TimestampNanoSecondsConverter()}});
+ else
+ result.Last = obj["last"].ToObject(new JsonSerializer() {Converters = {new TimestampSecondsConverter()}});
+ return Convert.ChangeType(result, objectType);
+ }
+
+ public override bool CanConvert(Type objectType)
+ {
+ return true;
+ }
+ }
+
+ ///
+ /// Dictionary result
+ ///
+ /// Type of the data
+ public class KrakenDictionaryResult
+ {
+ ///
+ /// The data
+ ///
+ public T Data { get; set; }
+ ///
+ /// The timestamp of the data
+ ///
+ [JsonConverter(typeof(TimestampSecondsConverter))]
+ public DateTime Last { get; set; }
+ }
+
+ ///
+ /// Kline result
+ ///
+ [JsonConverter(typeof(ExtendedDictionaryConverter))]
+ public class KrakenKlinesResult : KrakenDictionaryResult
+ {
+ }
+
+ ///
+ /// Trade result
+ ///
+ [JsonConverter(typeof(ExtendedDictionaryConverter))]
+ public class KrakenTradesResult : KrakenDictionaryResult
+ {
+ }
+
+ ///
+ /// Spread result
+ ///
+ [JsonConverter(typeof(ExtendedDictionaryConverter))]
+ public class KrakenSpreadsResult : KrakenDictionaryResult
+ {
+ }
+}
diff --git a/Kraken.Net/Converters/KlineIntervalConverter.cs b/Kraken.Net/Converters/KlineIntervalConverter.cs
new file mode 100644
index 0000000..9152d13
--- /dev/null
+++ b/Kraken.Net/Converters/KlineIntervalConverter.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using CryptoExchange.Net.Converters;
+using Kraken.Net.Objects;
+
+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/LedgerEntryTypeConverter.cs b/Kraken.Net/Converters/LedgerEntryTypeConverter.cs
new file mode 100644
index 0000000..f3e556b
--- /dev/null
+++ b/Kraken.Net/Converters/LedgerEntryTypeConverter.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using CryptoExchange.Net.Converters;
+using Kraken.Net.Objects;
+
+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.Trade, "trade"),
+ new KeyValuePair(LedgerEntryType.Deposit, "deposit"),
+ new KeyValuePair(LedgerEntryType.Withdrawal, "withdrawal"),
+ new KeyValuePair(LedgerEntryType.Margin, "margin"),
+ };
+ }
+}
diff --git a/Kraken.Net/Converters/OrderSideConverter.cs b/Kraken.Net/Converters/OrderSideConverter.cs
new file mode 100644
index 0000000..c02d795
--- /dev/null
+++ b/Kraken.Net/Converters/OrderSideConverter.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using CryptoExchange.Net.Converters;
+using Kraken.Net.Objects;
+
+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
new file mode 100644
index 0000000..8fd7d83
--- /dev/null
+++ b/Kraken.Net/Converters/OrderStatusConverter.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using CryptoExchange.Net.Converters;
+using Kraken.Net.Objects;
+
+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
new file mode 100644
index 0000000..a9ea000
--- /dev/null
+++ b/Kraken.Net/Converters/OrderTypeConverter.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using CryptoExchange.Net.Converters;
+using Kraken.Net.Objects;
+
+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.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
new file mode 100644
index 0000000..da50ede
--- /dev/null
+++ b/Kraken.Net/Converters/OrderTypeMinimalConverter.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+using CryptoExchange.Net.Converters;
+using Kraken.Net.Objects;
+
+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/StreamOrderBookConverter.cs b/Kraken.Net/Converters/StreamOrderBookConverter.cs
new file mode 100644
index 0000000..48e16ac
--- /dev/null
+++ b/Kraken.Net/Converters/StreamOrderBookConverter.cs
@@ -0,0 +1,50 @@
+using Kraken.Net.Objects;
+using Kraken.Net.Objects.Socket;
+using Newtonsoft.Json.Linq;
+
+namespace Kraken.Net.Converters
+{
+ internal static class StreamOrderBookConverter
+ {
+ public static KrakenSocketEvent Convert(JArray arr)
+ {
+ var result = new KrakenSocketEvent();
+ result.ChannelId = (int)arr[0];
+
+ var orderBook = new KrakenStreamOrderBook();
+ if (arr.Count == 4)
+ {
+ var innerObject = arr[1];
+ if (innerObject["as"] != null)
+ {
+ // snapshot
+ orderBook.Asks = innerObject["as"].ToObject();
+ orderBook.Bids = innerObject["bs"].ToObject();
+ }
+ else if (innerObject["a"] != null)
+ {
+ // Only asks
+ orderBook.Asks = innerObject["a"].ToObject();
+ }
+ else
+ {
+ // Only bids
+ orderBook.Bids = innerObject["b"].ToObject();
+ }
+
+ result.Topic = (string)arr[2];
+ result.Market = (string)arr[3];
+ }
+ else
+ {
+ orderBook.Asks = arr[1]["a"].ToObject();
+ orderBook.Bids = arr[2]["b"].ToObject();
+ result.Topic = (string)arr[3];
+ result.Market = (string)arr[4];
+ }
+
+ result.Data = orderBook;
+ return result;
+ }
+ }
+}
diff --git a/Kraken.Net/Interfaces/IKrakenClient.cs b/Kraken.Net/Interfaces/IKrakenClient.cs
new file mode 100644
index 0000000..5c9048d
--- /dev/null
+++ b/Kraken.Net/Interfaces/IKrakenClient.cs
@@ -0,0 +1,457 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using CryptoExchange.Net.Interfaces;
+using CryptoExchange.Net.Objects;
+using CryptoExchange.Net.RateLimiter;
+using Kraken.Net.Converters;
+using Kraken.Net.Objects;
+
+namespace Kraken.Net.Interfaces
+{
+ ///
+ /// Interface for the Kraken client
+ ///
+ public interface IKrakenClient: IRestClient
+ {
+ ///
+ /// Get the server time
+ ///
+ /// Server time
+ CallResult GetServerTime();
+
+ ///
+ /// Get the server time
+ ///
+ /// Server time
+ Task> GetServerTimeAsync();
+
+ ///
+ /// Get a list of assets and info about them
+ ///
+ /// Filter list for specific assets
+ /// Dictionary of asset info
+ WebCallResult> GetAssets(params string[] assets);
+
+ ///
+ /// Get a list of assets and info about them
+ ///
+ /// Filter list for specific assets
+ /// Dictionary of asset info
+ Task>> GetAssetsAsync(params string[] assets);
+
+ ///
+ /// Get a list of markets and info about them
+ ///
+ /// Filter list for specific markets
+ /// Dictionary of market info
+ WebCallResult> GetMarkets(params string[] markets);
+
+ ///
+ /// Get a list of markets and info about them
+ ///
+ /// Filter list for specific markets
+ /// Dictionary of market info
+ Task>> GetMarketsAsync(params string[] markets);
+
+ ///
+ /// Get tickers for markets
+ ///
+ /// Markets to get tickers for
+ /// Dictionary with ticker info
+ WebCallResult> GetTickers(params string[] markets);
+
+ ///
+ /// Get tickers for markets
+ ///
+ /// Markets to get tickers for
+ /// Dictionary with ticker info
+ Task>> GetTickersAsync(params string[] markets);
+
+ ///
+ /// Gets kline data for a market
+ ///
+ /// The market to get data for
+ /// The interval of the klines
+ /// Return klines since a secific time
+ /// Kline data
+ WebCallResult GetKlines(string market, KlineInterval interval, DateTime? since = null);
+
+ ///
+ /// Gets kline data for a market
+ ///
+ /// The market to get data for
+ /// The interval of the klines
+ /// Return klines since a secific time
+ /// Kline data
+ Task> GetKlinesAsync(string market, KlineInterval interval, DateTime? since = null);
+
+ ///
+ /// Get the order book for a market
+ ///
+ /// Market to get the book for
+ /// Limit to book to the best x bids/asks
+ /// Order book for the market
+ WebCallResult GetOrderBook(string market, int? limit = null);
+
+ ///
+ /// Get the order book for a market
+ ///
+ /// Market to get the book for
+ /// Limit to book to the best x bids/asks
+ /// Order book for the market
+ Task> GetOrderBookAsync(string market, int? limit = null);
+
+ ///
+ /// Get a list of recent trades for a market
+ ///
+ /// Market to get trades for
+ /// Return trades since a specific time
+ /// Recent trades
+ WebCallResult GetRecentTrades(string market, DateTime? since = null);
+
+ ///
+ /// Get a list of recent trades for a market
+ ///
+ /// Market to get trades for
+ /// Return trades since a specific time
+ /// Recent trades
+ Task> GetRecentTradesAsync(string market, DateTime? since = null);
+
+ ///
+ /// Get spread data for a market
+ ///
+ /// Market to get spread data for
+ /// Return spread data since a specific time
+ /// Spread data
+ WebCallResult GetRecentSpread(string market, DateTime? since = null);
+
+ ///
+ /// Get spread data for a market
+ ///
+ /// Market to get spread data for
+ /// Return spread data since a specific time
+ /// Spread data
+ Task> GetRecentSpreadAsync(string market, DateTime? since = null);
+
+ ///
+ /// Get balances
+ ///
+ /// Dictionary with balances for assets
+ WebCallResult> GetAccountBalance();
+
+ ///
+ /// Get balances
+ ///
+ /// Dictionary with balances for assets
+ Task>> GetAccountBalanceAsync();
+
+ ///
+ /// Get trade balance
+ ///
+ /// Base asset to get trade balance for
+ /// Trade balance data
+ WebCallResult GetTradeBalance(string baseAsset = null);
+
+ ///
+ /// Get trade balance
+ ///
+ /// Base asset to get trade balance for
+ /// Trade balance data
+ Task> GetTradeBalanceAsync(string baseAsset = null);
+
+ ///
+ /// Get a list of open orders
+ ///
+ /// Filter by client order id
+ /// List of open orders
+ WebCallResult GetOpenOrders(string clientOrderId = null);
+
+ ///
+ /// Get a list of open orders
+ ///
+ /// Filter by client order id
+ /// List of open orders
+ Task> GetOpenOrdersAsync(string clientOrderId = null);
+
+ ///
+ /// Get a list of closed orders
+ ///
+ /// Filter by client order id
+ /// Return data after this time
+ /// Return data before this time
+ /// Offset the results by
+ /// Closed orders page
+ WebCallResult GetClosedOrders(string clientOrderId = null, DateTime? startTime = null, DateTime? endTime = null, int? resultOffset = null);
+
+ ///
+ /// Get a list of closed orders
+ ///
+ /// Filter by client order id
+ /// Return data after this time
+ /// Return data before this time
+ /// Offset the results by
+ /// Closed orders page
+ Task> GetClosedOrdersAsync(string clientOrderId = null, DateTime? startTime = null, DateTime? endTime = null, int? resultOffset = null);
+
+ ///
+ /// Get info on specific orders
+ ///
+ /// Get orders by clientOrderId
+ /// Get orders by their order ids
+ /// Dictionary with order info
+ WebCallResult> GetOrders(string clientOrderId = null, params string[] orderIds);
+
+ ///
+ /// Get info on specific orders
+ ///
+ /// Get orders by clientOrderId
+ /// Get orders by their order ids
+ /// Dictionary with order info
+ Task>> GetOrdersAsync(string clientOrderId = null, params string[] orderIds);
+
+ ///
+ /// Get trade history
+ ///
+ /// Return data after this time
+ /// Return data before this time
+ /// Offset the results by
+ /// Trade history page
+ WebCallResult GetTradeHistory(DateTime? startTime = null, DateTime? endTime = null, int? resultOffset = null);
+
+ ///
+ /// Get trade history
+ ///
+ /// Return data after this time
+ /// Return data before this time
+ /// Offset the results by
+ /// Trade history page
+ Task> GetTradeHistoryAsync(DateTime? startTime = null, DateTime? endTime = null, int? resultOffset = null);
+
+ ///
+ /// Get info on specific trades
+ ///
+ /// The trades to get info on
+ /// Dictionary with trade info
+ WebCallResult> GetTrades(params string[] tradeIds);
+
+ ///
+ /// Get info on specific trades
+ ///
+ /// The trades to get info on
+ /// Dictionary with trade info
+ Task>> GetTradesAsync(params string[] tradeIds);
+
+ ///
+ /// Get a list of open positions
+ ///
+ /// Filter by transaction ids
+ /// Dictionary with position info
+ WebCallResult> GetOpenPositions(params string[] transactionIds);
+
+ ///
+ /// Get a list of open positions
+ ///
+ /// Filter by transaction ids
+ /// Dictionary with position info
+ Task>> GetOpenPositionsAsync(params string[] transactionIds);
+
+ ///
+ /// Get ledger entries info
+ ///
+ /// Filter list by asset names
+ /// Filter list by entry types
+ /// Return data after this time
+ /// Return data before this time
+ /// Offset the results by
+ /// Ledger entries page
+ WebCallResult GetLedgerInfo(string[] assets = null, LedgerEntryType[] entryTypes = null, DateTime? startTime = null, DateTime? endTime = null, int? resultOffset = null);
+
+ ///
+ /// Get ledger entries info
+ ///
+ /// Filter list by asset names
+ /// Filter list by entry types
+ /// Return data after this time
+ /// Return data before this time
+ /// Offset the results by
+ /// Ledger entries page
+ Task> GetLedgerInfoAsync(string[] assets = null, LedgerEntryType[] entryTypes = null, DateTime? startTime = null, DateTime? endTime = null, int? resultOffset = null);
+
+ ///
+ /// Get info on specific ledger entries
+ ///
+ /// The ids to get info for
+ /// Dictionary with ledger entry info
+ WebCallResult> GetLedgersEntry(params string[] ledgerIds);
+
+ ///
+ /// Get info on specific ledger entries
+ ///
+ /// The ids to get info for
+ /// Dictionary with ledger entry info
+ Task>> GetLedgersEntryAsync(params string[] ledgerIds);
+
+ ///
+ /// Get trade volume
+ ///
+ /// Markets to get data for
+ /// Trade fee info
+ WebCallResult GetTradeVolume(params string[] markets);
+
+ ///
+ /// Get trade volume
+ ///
+ /// Markets to get data for
+ /// Trade fee info
+ Task> GetTradeVolumeAsync(params string[] markets);
+
+ ///
+ /// Get deposit methods
+ ///
+ /// Asset to get methods for
+ /// Array with methods for deposit
+ WebCallResult GetDepositMethods(string asset);
+
+ ///
+ /// Get deposit methods
+ ///
+ /// Asset to get methods for
+ /// Array with methods for deposit
+ Task> GetDepositMethodsAsync(string asset);
+
+ ///
+ /// Get deposit addresses for an asset
+ ///
+ /// The asset to get the deposit address for
+ /// The method of deposit
+ /// Whether to generate a new address
+ ///
+ WebCallResult GetDepositAddresses(string asset, string depositMethod, bool generateNew = false);
+
+ ///
+ /// Get deposit addresses for an asset
+ ///
+ /// The asset to get the deposit address for
+ /// The method of deposit
+ /// Whether to generate a new address
+ ///
+ Task> GetDepositAddressesAsync(string asset, string depositMethod, bool generateNew = false);
+
+ ///
+ /// Get status deposits for an asset
+ ///
+ /// Asset to get deposit info for
+ /// The deposit method
+ /// Deposit status list
+ WebCallResult GetDepositStatus(string asset, string depositMethod);
+
+ ///
+ /// Get status deposits for an asset
+ ///
+ /// Asset to get deposit info for
+ /// The deposit method
+ /// Deposit status list
+ Task> GetDepositStatusAsync(string asset, string depositMethod);
+
+ ///
+ /// Place a new order
+ ///
+ /// The market the order is on
+ /// 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
+ /// Scheduled start time
+ /// Expiration time
+ /// Only validate inputs, don't actually place the order
+ /// Placed order info
+ WebCallResult PlaceOrder(
+ string market,
+ OrderSide side,
+ OrderType type,
+ decimal quantity,
+ uint? clientOrderId = null,
+ decimal? price = null,
+ decimal? secondaryPrice = null,
+ decimal? leverage = null,
+ DateTime? startTime = null,
+ DateTime? expireTime = null,
+ bool? validateOnly = null);
+
+ ///
+ /// Place a new order
+ ///
+ /// The market the order is on
+ /// 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
+ /// Scheduled start time
+ /// Expiration time
+ /// Only validate inputs, don't actually place the order
+ /// Placed order info
+ Task> PlaceOrderAsync(
+ string market,
+ OrderSide side,
+ OrderType type,
+ decimal quantity,
+ uint? clientOrderId = null,
+ decimal? price = null,
+ decimal? secondaryPrice = null,
+ decimal? leverage = null,
+ DateTime? startTime = null,
+ DateTime? expireTime = null,
+ bool? validateOnly = null);
+
+ ///
+ /// Cancel an order
+ ///
+ /// The id of the order to cancel
+ /// Cancel result
+ WebCallResult CancelOrder(string orderId);
+
+ ///
+ /// Cancel an order
+ ///
+ /// The id of the order to cancel
+ /// Cancel result
+ Task> CancelOrderAsync(string orderId);
+ }
+}
\ No newline at end of file
diff --git a/Kraken.Net/Interfaces/IKrakenSocketClient.cs b/Kraken.Net/Interfaces/IKrakenSocketClient.cs
new file mode 100644
index 0000000..965d757
--- /dev/null
+++ b/Kraken.Net/Interfaces/IKrakenSocketClient.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using CryptoExchange.Net.Interfaces;
+using CryptoExchange.Net.Objects;
+using CryptoExchange.Net.Sockets;
+using Kraken.Net.Objects;
+using Kraken.Net.Objects.Socket;
+
+namespace Kraken.Net.Interfaces
+{
+ ///
+ /// Interface for the Kraken socket client
+ ///
+ public interface IKrakenSocketClient: ISocketClient
+ {
+ ///
+ /// Subscribe to ticker updates
+ ///
+ /// Market to subscribe to
+ /// Data handler
+ /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+ CallResult SubscribeToTickerUpdates(string market, Action> handler);
+
+ ///
+ /// Subscribe to ticker updates
+ ///
+ /// Market to subscribe to
+ /// Data handler
+ /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+ Task> SubscribeToTickerUpdatesAsync(string market, Action> handler);
+
+ ///
+ /// Subscribe to kline updates
+ ///
+ /// Market to subscribe to
+ /// Kline interval
+ /// Data handler
+ /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+ CallResult SubscribeToKlineUpdates(string market, KlineInterval interval, Action> handler);
+
+ ///
+ /// Subscribe to kline updates
+ ///
+ /// Market to subscribe to
+ /// Kline interval
+ /// Data handler
+ /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+ Task> SubscribeToKlineUpdatesAsync(string market, KlineInterval interval, Action> handler);
+
+ ///
+ /// Subscribe to trade updates
+ ///
+ /// Market to subscribe to
+ /// Data handler
+ /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+ CallResult SubscribeToTradeUpdates(string market, Action>> handler);
+
+ ///
+ /// Subscribe to trade updates
+ ///
+ /// Market to subscribe to
+ /// Data handler
+ /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+ Task> SubscribeToTradeUpdatesAsync(string market, Action>> handler);
+
+ ///
+ /// Subscribe to spread updates
+ ///
+ /// Market to subscribe to
+ /// Data handler
+ /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+ CallResult SubscribeToSpreadUpdates(string market, Action> handler);
+
+ ///
+ /// Subscribe to spread updates
+ ///
+ /// Market to subscribe to
+ /// Data handler
+ /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+ Task> SubscribeToSpreadUpdatesAsync(string market, Action> handler);
+
+ ///
+ /// Subscribe to depth updates
+ ///
+ /// Market to subscribe to
+ /// Depth of the initial order book snapshot
+ /// Data handler
+ /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+ CallResult SubscribeToDepthUpdates(string market, int depth, Action> handler);
+
+ ///
+ /// Subscribe to depth updates
+ ///
+ /// Market to subscribe to
+ /// Depth of the initial order book snapshot
+ /// Data handler
+ /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+ Task> SubscribeToDepthUpdatesAsync(string market, int depth, Action> handler);
+ }
+}
\ No newline at end of file
diff --git a/Kraken.Net/Kraken.Net.csproj b/Kraken.Net/Kraken.Net.csproj
new file mode 100644
index 0000000..88ce060
--- /dev/null
+++ b/Kraken.Net/Kraken.Net.csproj
@@ -0,0 +1,25 @@
+
+
+ netstandard2.0
+
+
+ Kraken.Net
+ JKorf
+ 0.0.1
+ Kraken.Net is a .Net wrapper for the Kraken API. It includes all features the API provides, REST API and Websocket, using clear and readable objects including but not limited to Reading market info, Placing and managing orders and Reading balances and funds
+ false
+ Kraken Kraken.Net C# .Net CryptoCurrency Exchange API wrapper
+ https://github.com/JKorf/Kraken.Net
+ https://github.com/JKorf/Kraken.Net/blob/master/LICENSE
+ https://raw.githubusercontent.com/JKorf/Kraken.Net/master/Resources/icon.png
+ en
+ true
+ 0.0.1 - Initial release
+
+
+ Kraken.Net.xml
+
+
+
+
+
\ No newline at end of file
diff --git a/Kraken.Net/Kraken.Net.xml b/Kraken.Net/Kraken.Net.xml
new file mode 100644
index 0000000..99c7637
--- /dev/null
+++ b/Kraken.Net/Kraken.Net.xml
@@ -0,0 +1,2574 @@
+
+
+
+ Kraken.Net
+
+
+
+
+ Dictionary result
+
+ Type of the data
+
+
+
+ The data
+
+
+
+
+ The timestamp of the data
+
+
+
+
+ Kline result
+
+
+
+
+ Trade result
+
+
+
+
+ Spread result
+
+
+
+
+ Interface for the Kraken client
+
+
+
+
+ Get the server time
+
+ Server time
+
+
+
+ Get the server time
+
+ Server time
+
+
+
+ Get a list of assets and info about them
+
+ Filter list for specific assets
+ Dictionary of asset info
+
+
+
+ Get a list of assets and info about them
+
+ Filter list for specific assets
+ Dictionary of asset info
+
+
+
+ Get a list of markets and info about them
+
+ Filter list for specific markets
+ Dictionary of market info
+
+
+
+ Get a list of markets and info about them
+
+ Filter list for specific markets
+ Dictionary of market info
+
+
+
+ Get tickers for markets
+
+ Markets to get tickers for
+ Dictionary with ticker info
+
+
+
+ Get tickers for markets
+
+ Markets to get tickers for
+ Dictionary with ticker info
+
+
+
+ Gets kline data for a market
+
+ The market to get data for
+ The interval of the klines
+ Return klines since a secific time
+ Kline data
+
+
+
+ Gets kline data for a market
+
+ The market to get data for
+ The interval of the klines
+ Return klines since a secific time
+ Kline data
+
+
+
+ Get the order book for a market
+
+ Market to get the book for
+ Limit to book to the best x bids/asks
+ Order book for the market
+
+
+
+ Get the order book for a market
+
+ Market to get the book for
+ Limit to book to the best x bids/asks
+ Order book for the market
+
+
+
+ Get a list of recent trades for a market
+
+ Market to get trades for
+ Return trades since a specific time
+ Recent trades
+
+
+
+ Get a list of recent trades for a market
+
+ Market to get trades for
+ Return trades since a specific time
+ Recent trades
+
+
+
+ Get spread data for a market
+
+ Market to get spread data for
+ Return spread data since a specific time
+ Spread data
+
+
+
+ Get spread data for a market
+
+ Market to get spread data for
+ Return spread data since a specific time
+ Spread data
+
+
+
+ Get balances
+
+ Dictionary with balances for assets
+
+
+
+ Get balances
+
+ Dictionary with balances for assets
+
+
+
+ Get trade balance
+
+ Base asset to get trade balance for
+ Trade balance data
+
+
+
+ Get trade balance
+
+ Base asset to get trade balance for
+ Trade balance data
+
+
+
+ Get a list of open orders
+
+ Filter by client order id
+ List of open orders
+
+
+
+ Get a list of open orders
+
+ Filter by client order id
+ List of open orders
+
+
+
+ Get a list of closed orders
+
+ Filter by client order id
+ Return data after this time
+ Return data before this time
+ Offset the results by
+ Closed orders page
+
+
+
+ Get a list of closed orders
+
+ Filter by client order id
+ Return data after this time
+ Return data before this time
+ Offset the results by
+ Closed orders page
+
+
+
+ Get info on specific orders
+
+ Get orders by clientOrderId
+ Get orders by their order ids
+ Dictionary with order info
+
+
+
+ Get info on specific orders
+
+ Get orders by clientOrderId
+ Get orders by their order ids
+ Dictionary with order info
+
+
+
+ Get trade history
+
+ Return data after this time
+ Return data before this time
+ Offset the results by
+ Trade history page
+
+
+
+ Get trade history
+
+ Return data after this time
+ Return data before this time
+ Offset the results by
+ Trade history page
+
+
+
+ Get info on specific trades
+
+ The trades to get info on
+ Dictionary with trade info
+
+
+
+ Get info on specific trades
+
+ The trades to get info on
+ Dictionary with trade info
+
+
+
+ Get a list of open positions
+
+ Filter by transaction ids
+ Dictionary with position info
+
+
+
+ Get a list of open positions
+
+ Filter by transaction ids
+ Dictionary with position info
+
+
+
+ Get ledger entries info
+
+ Filter list by asset names
+ Filter list by entry types
+ Return data after this time
+ Return data before this time
+ Offset the results by
+ Ledger entries page
+
+
+
+ Get ledger entries info
+
+ Filter list by asset names
+ Filter list by entry types
+ Return data after this time
+ Return data before this time
+ Offset the results by
+ Ledger entries page
+
+
+
+ Get info on specific ledger entries
+
+ The ids to get info for
+ Dictionary with ledger entry info
+
+
+
+ Get info on specific ledger entries
+
+ The ids to get info for
+ Dictionary with ledger entry info
+
+
+
+ Get trade volume
+
+ Markets to get data for
+ Trade fee info
+
+
+
+ Get trade volume
+
+ Markets to get data for
+ Trade fee info
+
+
+
+ Get deposit methods
+
+ Asset to get methods for
+ Array with methods for deposit
+
+
+
+ Get deposit methods
+
+ Asset to get methods for
+ Array with methods for deposit
+
+
+
+ Get deposit addresses for an asset
+
+ The asset to get the deposit address for
+ The method of deposit
+ Whether to generate a new address
+
+
+
+
+ Get deposit addresses for an asset
+
+ The asset to get the deposit address for
+ The method of deposit
+ Whether to generate a new address
+
+
+
+
+ Get status deposits for an asset
+
+ Asset to get deposit info for
+ The deposit method
+ Deposit status list
+
+
+
+ Get status deposits for an asset
+
+ Asset to get deposit info for
+ The deposit method
+ Deposit status list
+
+
+
+ Place a new order
+
+ The market the order is on
+ 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
+ Scheduled start time
+ Expiration time
+ Only validate inputs, don't actually place the order
+ Placed order info
+
+
+
+ Place a new order
+
+ The market the order is on
+ 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
+ Scheduled start time
+ Expiration time
+ Only validate inputs, don't actually place the order
+ Placed order info
+
+
+
+ Cancel an order
+
+ The id of the order to cancel
+ Cancel result
+
+
+
+ Cancel an order
+
+ The id of the order to cancel
+ Cancel result
+
+
+
+ Interface for the Kraken socket client
+
+
+
+
+ Subscribe to ticker updates
+
+ Market to subscribe to
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to ticker updates
+
+ Market to subscribe to
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to kline updates
+
+ Market to subscribe to
+ Kline interval
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to kline updates
+
+ Market to subscribe to
+ Kline interval
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to trade updates
+
+ Market to subscribe to
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to trade updates
+
+ Market to subscribe to
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to spread updates
+
+ Market to subscribe to
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to spread updates
+
+ Market to subscribe to
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to depth updates
+
+ Market to subscribe to
+ Depth of the initial order book snapshot
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to depth updates
+
+ Market to subscribe to
+ Depth of the initial order book snapshot
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Client for the Kraken Rest API
+
+
+
+
+ Create a new instance of KrakenClient using the default options
+
+
+
+
+ Create a new instance of KrakenClient using provided options
+
+ The options to use for this client
+
+
+
+ Get the server time
+
+ Server time
+
+
+
+ Get the server time
+
+ Server time
+
+
+
+ Get a list of assets and info about them
+
+ Filter list for specific assets
+ Dictionary of asset info
+
+
+
+ Get a list of assets and info about them
+
+ Filter list for specific assets
+ Dictionary of asset info
+
+
+
+ Get a list of markets and info about them
+
+ Filter list for specific markets
+ Dictionary of market info
+
+
+
+ Get a list of markets and info about them
+
+ Filter list for specific markets
+ Dictionary of market info
+
+
+
+ Get tickers for markets
+
+ Markets to get tickers for
+ Dictionary with ticker info
+
+
+
+ Get tickers for markets
+
+ Markets to get tickers for
+ Dictionary with ticker info
+
+
+
+ Gets kline data for a market
+
+ The market to get data for
+ The interval of the klines
+ Return klines since a secific time
+ Kline data
+
+
+
+ Gets kline data for a market
+
+ The market to get data for
+ The interval of the klines
+ Return klines since a secific time
+ Kline data
+
+
+
+ Get the order book for a market
+
+ Market to get the book for
+ Limit to book to the best x bids/asks
+ Order book for the market
+
+
+
+ Get the order book for a market
+
+ Market to get the book for
+ Limit to book to the best x bids/asks
+ Order book for the market
+
+
+
+ Get a list of recent trades for a market
+
+ Market to get trades for
+ Return trades since a specific time
+ Recent trades
+
+
+
+ Get a list of recent trades for a market
+
+ Market to get trades for
+ Return trades since a specific time
+ Recent trades
+
+
+
+ Get spread data for a market
+
+ Market to get spread data for
+ Return spread data since a specific time
+ Spread data
+
+
+
+ Get spread data for a market
+
+ Market to get spread data for
+ Return spread data since a specific time
+ Spread data
+
+
+
+ Get balances
+
+ Dictionary with balances for assets
+
+
+
+ Get balances
+
+ Dictionary with balances for assets
+
+
+
+ Get trade balance
+
+ Base asset to get trade balance for
+ Trade balance data
+
+
+
+ Get trade balance
+
+ Base asset to get trade balance for
+ Trade balance data
+
+
+
+ Get a list of open orders
+
+ Filter by client order id
+ List of open orders
+
+
+
+ Get a list of open orders
+
+ Filter by client order id
+ List of open orders
+
+
+
+ Get a list of closed orders
+
+ Filter by client order id
+ Return data after this time
+ Return data before this time
+ Offset the results by
+ Closed orders page
+
+
+
+ Get a list of closed orders
+
+ Filter by client order id
+ Return data after this time
+ Return data before this time
+ Offset the results by
+ Closed orders page
+
+
+
+ Get info on specific orders
+
+ Get orders by clientOrderId
+ Get orders by their order ids
+ Dictionary with order info
+
+
+
+ Get info on specific orders
+
+ Get orders by clientOrderId
+ Get orders by their order ids
+ Dictionary with order info
+
+
+
+ Get trade history
+
+ Return data after this time
+ Return data before this time
+ Offset the results by
+ Trade history page
+
+
+
+ Get trade history
+
+ Return data after this time
+ Return data before this time
+ Offset the results by
+ Trade history page
+
+
+
+ Get info on specific trades
+
+ The trades to get info on
+ Dictionary with trade info
+
+
+
+ Get info on specific trades
+
+ The trades to get info on
+ Dictionary with trade info
+
+
+
+ Get a list of open positions
+
+ Filter by transaction ids
+ Dictionary with position info
+
+
+
+ Get a list of open positions
+
+ Filter by transaction ids
+ Dictionary with position info
+
+
+
+ Get ledger entries info
+
+ Filter list by asset names
+ Filter list by entry types
+ Return data after this time
+ Return data before this time
+ Offset the results by
+ Ledger entries page
+
+
+
+ Get ledger entries info
+
+ Filter list by asset names
+ Filter list by entry types
+ Return data after this time
+ Return data before this time
+ Offset the results by
+ Ledger entries page
+
+
+
+ Get info on specific ledger entries
+
+ The ids to get info for
+ Dictionary with ledger entry info
+
+
+
+ Get info on specific ledger entries
+
+ The ids to get info for
+ Dictionary with ledger entry info
+
+
+
+ Get trade volume
+
+ Markets to get data for
+ Trade fee info
+
+
+
+ Get trade volume
+
+ Markets to get data for
+ Trade fee info
+
+
+
+ Get deposit methods
+
+ Asset to get methods for
+ Array with methods for deposit
+
+
+
+ Get deposit methods
+
+ Asset to get methods for
+ Array with methods for deposit
+
+
+
+ Get deposit addresses for an asset
+
+ The asset to get the deposit address for
+ The method of deposit
+ Whether to generate a new address
+
+
+
+
+ Get deposit addresses for an asset
+
+ The asset to get the deposit address for
+ The method of deposit
+ Whether to generate a new address
+
+
+
+
+ Get status deposits for an asset
+
+ Asset to get deposit info for
+ The deposit method
+ Deposit status list
+
+
+
+ Get status deposits for an asset
+
+ Asset to get deposit info for
+ The deposit method
+ Deposit status list
+
+
+
+ Place a new order
+
+ The market the order is on
+ 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
+ Scheduled start time
+ Expiration time
+ Only validate inputs, don't actually place the order
+ Placed order info
+
+
+
+ Place a new order
+
+ The market the order is on
+ 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
+ Scheduled start time
+ Expiration time
+ Only validate inputs, don't actually place the order
+ Placed order info
+
+
+
+ Cancel an order
+
+ The id of the order to cancel
+ Cancel result
+
+
+
+ Cancel an order
+
+ The id of the order to cancel
+ Cancel result
+
+
+
+
+
+
+ Options for the Kraken client
+
+
+
+
+ ctor
+
+
+
+
+ Options for the Kraken socket client
+
+
+
+
+ ctor
+
+
+
+
+ Options for the Kraken symbol order book
+
+
+
+
+ The client to use for the socket connection. When using the same client for multiple order books the connection can be shared.
+
+
+
+
+
+ The client to use for the socket connection. When using the same client for multiple order books the connection can be shared.
+
+
+
+ Client for the Kraken websocket API
+
+
+
+
+ Create a new instance of KrakenSocketClient using the default options
+
+
+
+
+ Create a new instance of KrakenSocketClient using provided options
+
+ The options to use for this client
+
+
+
+ Subscribe to ticker updates
+
+ Market to subscribe to
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to ticker updates
+
+ Market to subscribe to
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to kline updates
+
+ Market to subscribe to
+ Kline interval
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to kline updates
+
+ Market to subscribe to
+ Kline interval
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to trade updates
+
+ Market to subscribe to
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to trade updates
+
+ Market to subscribe to
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to spread updates
+
+ Market to subscribe to
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to spread updates
+
+ Market to subscribe to
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to depth updates
+
+ Market to subscribe to
+ Depth of the initial order book snapshot
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+ Subscribe to depth updates
+
+ Market to subscribe to
+ Depth of the initial order book snapshot
+ Data handler
+ A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Live order book implementation
+
+
+
+
+ Create a new order book instance
+
+ The symbol the order book is for
+ The initial limit of entries in the order book
+ Options for the order book
+
+
+
+
+
+
+
+
+
+
+
+
+ Dispose
+
+
+
+
+ The time interval of kline data
+
+
+
+
+ 1m
+
+
+
+
+ 5m
+
+
+
+
+ 15m
+
+
+
+
+ 30m
+
+
+
+
+ 1h
+
+
+
+
+ 4h
+
+
+
+
+ 1d
+
+
+
+
+ 1w
+
+
+
+
+ 15d
+
+
+
+
+ Side of an order
+
+
+
+
+ Buy
+
+
+
+
+ Sell
+
+
+
+
+ Order type, limited to market or limit
+
+
+
+
+ Limit order
+
+
+
+
+ Market order
+
+
+
+
+ Order type
+
+
+
+
+ Limit order
+
+
+
+
+ Market order
+
+
+
+
+ Stop loss order
+
+
+
+
+ Take profit order
+
+
+
+
+ Stop loss profit order
+
+
+
+
+ Stop loss profit limit order
+
+
+
+
+ Stop loss limit order
+
+
+
+
+ Take profit limit order
+
+
+
+
+ Trailing stop order
+
+
+
+
+ Trailing stop limit order
+
+
+
+
+ Stop loss and limit order
+
+
+
+
+ Settle position
+
+
+
+
+ Status of an order
+
+
+
+
+ Pending
+
+
+
+
+ Active, not (fully) filled
+
+
+
+
+ Fully filled
+
+
+
+
+ Canceled
+
+
+
+
+ Expired
+
+
+
+
+ The type of a ledger entry
+
+
+
+
+ Deposit
+
+
+
+
+ Withdrawal
+
+
+
+
+ Trade change
+
+
+
+
+ Margin
+
+
+
+
+ Info on an asset
+
+
+
+
+ Alternative name
+
+
+
+
+ Class of the asset
+
+
+
+
+ Decimal precision of the asset
+
+
+
+
+ Decimals to display
+
+
+
+
+ Result of a cancel request
+
+
+
+
+ Amount of canceled orders
+
+
+
+
+ Pending cancellation orders
+
+
+
+
+ Deposit address
+
+
+
+
+ The actual address
+
+
+
+
+ The expire time of the address
+
+
+
+
+ If the address has been used before
+
+
+
+
+ Info about a deposit method
+
+
+
+
+ Name of the method
+
+
+
+
+ Deposit limit (max) of the method
+
+
+
+
+ The deposit fee for the method
+
+
+
+
+ The fee for setting up an address
+
+
+
+
+ Generate address
+
+
+
+
+ Deposit status info
+
+
+
+
+ The name of the deposit method
+
+
+
+
+ The class of the asset
+
+
+
+
+ The asset name
+
+
+
+
+ Reference id
+
+
+
+
+ Transaction id
+
+
+
+
+ Info about the transaction
+
+
+
+
+ The amount involved in the deposit
+
+
+
+
+ The fee paid for the deposit
+
+
+
+
+ The timestamp
+
+
+
+
+ Status of the transaction
+
+
+
+
+ Fee level details
+
+
+
+
+ The minimal volume for this level
+
+
+
+
+ The fee percentage for this level
+
+
+
+
+ Kline data
+
+
+
+
+ Timestamp of the kline
+
+
+
+
+ The open price for this kline
+
+
+
+
+ The highest price during this kline
+
+
+
+
+ The lowest price during this kline
+
+
+
+
+ The close price of this kline (or price of last trade if kline isn't closed yet)
+
+
+
+
+ The volume weighted average price
+
+
+
+
+ Volume during this kline
+
+
+
+
+ The number of trades during this kline
+
+
+
+
+ Kline data from stream
+
+
+
+
+ Timestamp of the kline
+
+
+
+
+ The end time for the kline
+
+
+
+
+ The open price for this kline
+
+
+
+
+ The highest price during this kline
+
+
+
+
+ The lowest price during this kline
+
+
+
+
+ The close price of this kline (or price of last trade if kline isn't closed yet)
+
+
+
+
+ The volume weighted average price
+
+
+
+
+ Volume during this kline
+
+
+
+
+ The number of trades during this kline
+
+
+
+
+ Ledger entry info
+
+
+
+
+ Reference id
+
+
+
+
+ Timestamp
+
+
+
+
+ The type of entry
+
+
+
+
+ Class of the asset
+
+
+
+
+ Name of the asset
+
+
+
+
+ The quantity of the entry
+
+
+
+
+ Fee paid
+
+
+
+
+ Resulting balance
+
+
+
+
+ Market 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
+
+
+
+
+ Let size
+
+
+
+
+ Decimals of the market
+
+
+
+
+ Lot decimals
+
+
+
+
+ Lot multiplier
+
+
+
+
+ Buy leverage amounts
+
+
+
+
+ Sell leverage amounts
+
+
+
+
+ Fee structure
+
+
+
+
+ Maker fee structure
+
+
+
+
+ The currency the fee is deducted from
+
+
+
+
+ Margin call level
+
+
+
+
+ Stop-out/liquidation margin level
+
+
+
+
+ Order info
+
+
+
+
+ Reference id
+
+
+
+
+ Client reference id
+
+
+
+
+ Status of the order
+
+
+
+
+ Open timestamp
+
+
+
+
+ Start timestamp
+
+
+
+
+ Expire timestamp
+
+
+
+
+ Close timestamp
+
+
+
+
+ Order details
+
+
+
+
+ Quantity of the order
+
+
+
+
+ Filled quantity
+
+
+
+
+ Cost of the order
+
+
+
+
+ Fee
+
+
+
+
+ Average price of the order
+
+
+
+
+ Stop price
+
+
+
+
+ Limit price
+
+
+
+
+ Miscellaneous info
+
+
+
+
+ Order flags
+
+
+
+
+ Reason of failure
+
+
+
+
+ Trade ids
+
+
+
+
+ Order details
+
+
+
+
+ The market of the order
+
+
+
+
+ Side of the order
+
+
+
+
+ Type of the order
+
+
+
+
+ Price of the order
+
+
+
+
+ Secondary price of the order ( for details)
+
+
+
+
+ Amount of leverage
+
+
+
+
+ Order description
+
+
+
+
+ Conditional close order description
+
+
+
+
+ Order book
+
+
+
+
+ Asks in the book
+
+
+
+
+ Bids in the book
+
+
+
+
+ Order book entry
+
+
+
+
+ Price of the entry
+
+
+
+
+ Quantity of the entry
+
+
+
+
+ Timestamp of change
+
+
+
+
+ Stream order book
+
+
+
+
+ Asks
+
+
+
+
+ Bids
+
+
+
+
+ ctor
+
+
+
+
+ Stream order book entry
+
+
+
+
+ Price of the entry
+
+
+
+
+ Quantity of the entry
+
+
+
+
+ Timestamp of the entry
+
+
+
+
+ Type of update
+
+
+
+
+ Base page data
+
+
+
+
+ Total number of records
+
+
+
+
+ Open orders page
+
+
+
+
+ Open orders
+
+
+
+
+ Closed orders page
+
+
+
+
+ Closed orders
+
+
+
+
+ User trades page
+
+
+
+
+ Trades
+
+
+
+
+ Ledger page
+
+
+
+
+ Ledger entries
+
+
+
+
+ Placed order info
+
+
+
+
+ Order ids
+
+
+
+
+ Descriptions
+
+
+
+
+ Order descriptions
+
+
+
+
+ Order description
+
+
+
+
+ Close order description
+
+
+
+
+ Position info
+
+
+
+
+ Order id
+
+
+
+
+ Market
+
+
+
+
+ Timestamp
+
+
+
+
+ Side
+
+
+
+
+ Type
+
+
+
+
+ Cost
+
+
+
+
+ Fee
+
+
+
+
+ Quantity
+
+
+
+
+ Closed quantity
+
+
+
+
+ Margin
+
+
+
+
+ Value
+
+
+
+
+ Net profit/loss
+
+
+
+
+ Misc info
+
+
+
+
+ Flags
+
+
+
+
+ Spread info
+
+
+
+
+ Timestamp of the data
+
+
+
+
+ Best bid price
+
+
+
+
+ Best ask price
+
+
+
+
+ Stream spread data
+
+
+
+
+ Best bid price
+
+
+
+
+ Best ask price
+
+
+
+
+ Timestamp of the data
+
+
+
+
+ Best bid volume
+
+
+
+
+ Best ask volume
+
+
+
+
+ Tick info
+
+
+
+
+ High price info
+
+
+
+
+ Low price info
+
+
+
+
+ Last trade info
+
+
+
+
+ Best ask info
+
+
+
+
+ Best bid info
+
+
+
+
+ Trade count info
+
+
+
+
+ Volume weighted average price info
+
+
+
+
+ Volume info
+
+
+
+
+ Tick info
+
+
+
+
+ Open price
+
+
+
+
+ Tick info
+
+
+
+
+ Open price info
+
+
+
+
+ Tick detail info
+
+
+
+
+ Value for today
+
+
+
+
+ Rolling 24h window value
+
+
+
+
+ Last trade details
+
+
+
+
+ Price of last trade
+
+
+
+
+ Quantity of last trade
+
+
+
+
+ Best entry info
+
+
+
+
+ Price of best entry
+
+
+
+
+ Lot quantity
+
+
+
+
+ Quantity
+
+
+
+
+ Trade info
+
+
+
+
+ Price of the trade
+
+
+
+
+ Quantity of the trade
+
+
+
+
+ Timestamp of trade
+
+
+
+
+ Side
+
+
+
+
+ Order type
+
+
+
+
+ Misc info
+
+
+
+
+ Trade balance info
+
+
+
+
+ Combined balance
+
+
+
+
+ Trade balance
+
+
+
+
+ Margin open positions
+
+
+
+
+ Unrealized net profit in open positions
+
+
+
+
+ Cost basis for open positions
+
+
+
+
+ Open positions valuation
+
+
+
+
+ Equity
+
+
+
+
+ Free margin
+
+
+
+
+ Margin level
+
+
+
+
+ Trade volume info
+
+
+
+
+ Currency
+
+
+
+
+ Volume
+
+
+
+
+ Fees structure
+
+
+
+
+ Maker fees structure
+
+
+
+
+ Fee level info
+
+
+
+
+ Fee
+
+
+
+
+ Minimal fee
+
+
+
+
+ Maximal fee
+
+
+
+
+ Next fee
+
+
+
+
+ Next volume
+
+
+
+
+ Tier volume
+
+
+
+
+ User trade info
+
+
+
+
+ Order id
+
+
+
+
+ Market
+
+
+
+
+ Timestamp of trade
+
+
+
+
+ Side
+
+
+
+
+ Order type
+
+
+
+
+ Price of the trade
+
+
+
+
+ Cost of the trade
+
+
+
+
+ Fee paid for trade
+
+
+
+
+ Quantity of the trade
+
+
+
+
+ Margin
+
+
+
+
+ Misc info
+
+
+
+
+ Position status
+
+
+
+
+ Closed average price
+
+
+
+
+ Closed cost
+
+
+
+
+ Closed fee
+
+
+
+
+ Closed quantity
+
+
+
+
+ Closed margin
+
+
+
+
+ Closed net profit/loss
+
+
+
+
+ Trade ids
+
+
+
+
+ Event received from the socket
+
+
+
+
+
+ Id of the channel
+
+
+
+
+ The data
+
+
+
+
+ The topic of the data
+
+
+
+
+ The market the data is for
+
+
+
+
diff --git a/Kraken.Net/KrakenAuthenticationProvider.cs b/Kraken.Net/KrakenAuthenticationProvider.cs
new file mode 100644
index 0000000..0e0de43
--- /dev/null
+++ b/Kraken.Net/KrakenAuthenticationProvider.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using CryptoExchange.Net;
+using CryptoExchange.Net.Authentication;
+
+namespace Kraken.Net
+{
+ internal class KrakenAuthenticationProvider: AuthenticationProvider
+ {
+ private static readonly object nonceLock = new object();
+ private static long lastNonce;
+ internal static string Nonce
+ {
+ get
+ {
+ lock (nonceLock)
+ {
+ var nonce = DateTime.UtcNow.Ticks;
+ if (nonce == lastNonce)
+ nonce += 1;
+
+ lastNonce = nonce;
+ return lastNonce.ToString(CultureInfo.InvariantCulture);
+ }
+ }
+ }
+
+ private HMACSHA512 encryptor;
+
+ public KrakenAuthenticationProvider(ApiCredentials credentials) : base(credentials)
+ {
+ encryptor = new HMACSHA512(Convert.FromBase64String(credentials.Secret.GetString()));
+ }
+
+ public override Dictionary AddAuthenticationToParameters(string uri, string method, Dictionary parameters, bool signed)
+ {
+ if (!signed)
+ return parameters;
+
+ parameters.Add("nonce", Nonce);
+ return parameters;
+ }
+
+ public override Dictionary AddAuthenticationToHeaders(string uri, string method, Dictionary parameters, bool signed)
+ {
+ if(!signed)
+ return new Dictionary();
+
+ var nonce = parameters.Single(n => n.Key == "nonce").Value;
+ var paramList = parameters.OrderBy(o => o.Key != "nonce");
+ var pars = string.Join("&", paramList.Select(p => $"{p.Key}={p.Value}"));
+
+ var result = new Dictionary();
+ result.Add("API-Key", Credentials.Key.GetString());
+ var np = nonce + pars;
+ byte[] nonceParamsBytes;
+ using (SHA256 sha = SHA256Managed.Create())
+ nonceParamsBytes = sha.ComputeHash(Encoding.UTF8.GetBytes(np));
+ var pathBytes = Encoding.UTF8.GetBytes(uri.Split(new[] { ".com" }, StringSplitOptions.None)[1]);
+ var allBytes = pathBytes.Concat(nonceParamsBytes).ToArray();
+ var sign = encryptor.ComputeHash(allBytes);
+
+ result.Add("API-Sign", Convert.ToBase64String(sign));
+ return result;
+ }
+ }
+}
diff --git a/Kraken.Net/KrakenClient.cs b/Kraken.Net/KrakenClient.cs
new file mode 100644
index 0000000..2299f7a
--- /dev/null
+++ b/Kraken.Net/KrakenClient.cs
@@ -0,0 +1,681 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using CryptoExchange.Net;
+using CryptoExchange.Net.Converters;
+using CryptoExchange.Net.Interfaces;
+using CryptoExchange.Net.Objects;
+using Kraken.Net.Converters;
+using Kraken.Net.Interfaces;
+using Kraken.Net.Objects;
+using Newtonsoft.Json;
+
+namespace Kraken.Net
+{
+ ///
+ /// Client for the Kraken Rest API
+ ///
+ public class KrakenClient: RestClient, IKrakenClient
+ {
+ #region fields
+ private static KrakenClientOptions defaultOptions = new KrakenClientOptions();
+ private static KrakenClientOptions DefaultOptions => defaultOptions.Copy();
+ #endregion
+
+ #region ctor
+ ///
+ /// Create a new instance of KrakenClient using the default options
+ ///
+ public KrakenClient() : this(DefaultOptions)
+ {
+ }
+
+ ///
+ /// Create a new instance of KrakenClient using provided options
+ ///
+ /// The options to use for this client
+ public KrakenClient(KrakenClientOptions options) : base(options, options.ApiCredentials == null ? null : new KrakenAuthenticationProvider(options.ApiCredentials))
+ {
+ postParametersPosition = PostParameters.InBody;
+ requestBodyFormat = RequestBodyFormat.FormData;
+ Configure(options);
+ }
+ #endregion
+
+ #region methods
+
+ ///
+ /// Get the server time
+ ///
+ /// Server time
+ public CallResult GetServerTime() => GetServerTimeAsync().Result;
+ ///
+ /// Get the server time
+ ///
+ /// Server time
+ public async Task> GetServerTimeAsync()
+ {
+ var result = await Execute(GetUri("/0/public/Time")).ConfigureAwait(false);
+ if (!result.Success)
+ return WebCallResult.CreateErrorResult(result.Error);
+ return new CallResult(result.Data.UnixTime, null);
+ }
+
+ ///
+ /// Get a list of assets and info about them
+ ///
+ /// Filter list for specific assets
+ /// Dictionary of asset info
+ public WebCallResult> GetAssets(params string[] assets) => GetAssetsAsync(assets).Result;
+ ///
+ /// Get a list of assets and info about them
+ ///
+ /// Filter list for specific assets
+ /// Dictionary of asset info
+ public async Task>> GetAssetsAsync(params string[] assets)
+ {
+ var parameters = new Dictionary();
+ if(assets.Any())
+ parameters.AddOptionalParameter("asset", string.Join(",", assets));
+
+ return await Execute>(GetUri("/0/public/Assets"), parameters:parameters).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get a list of markets and info about them
+ ///
+ /// Filter list for specific markets
+ /// Dictionary of market info
+ public WebCallResult> GetMarkets(params string[] markets) => GetMarketsAsync(markets).Result;
+ ///
+ /// Get a list of markets and info about them
+ ///
+ /// Filter list for specific markets
+ /// Dictionary of market info
+ public async Task>> GetMarketsAsync(params string[] markets)
+ {
+ var parameters = new Dictionary();
+ if (markets.Any())
+ parameters.AddOptionalParameter("pair", string.Join(",", markets));
+
+ return await Execute>(GetUri("/0/public/AssetPairs"), parameters: parameters).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get tickers for markets
+ ///
+ /// Markets to get tickers for
+ /// Dictionary with ticker info
+ public WebCallResult> GetTickers(params string[] markets) => GetTickersAsync(markets).Result;
+ ///
+ /// Get tickers for markets
+ ///
+ /// Markets to get tickers for
+ /// Dictionary with ticker info
+ public async Task>> GetTickersAsync(params string[] markets)
+ {
+ if (!markets.Any())
+ return WebCallResult>.CreateErrorResult(new ArgumentError("Specify markets to get tickers for"));
+
+ var parameters = new Dictionary();
+ parameters.AddParameter("pair", string.Join(",", markets));
+
+ return await Execute>(GetUri("/0/public/Ticker"), parameters: parameters).ConfigureAwait(false);
+ }
+
+ ///
+ /// Gets kline data for a market
+ ///
+ /// The market to get data for
+ /// The interval of the klines
+ /// Return klines since a secific time
+ /// Kline data
+ public WebCallResult GetKlines(string market, KlineInterval interval, DateTime? since = null) => GetKlinesAsync(market, interval, since).Result;
+ ///
+ /// Gets kline data for a market
+ ///
+ /// The market to get data for
+ /// The interval of the klines
+ /// Return klines since a secific time
+ /// Kline data
+ public async Task> GetKlinesAsync(string market, KlineInterval interval, DateTime? since = null)
+ {
+ var parameters = new Dictionary()
+ {
+ {"pair", market},
+ {"interval", JsonConvert.SerializeObject(interval, new KlineIntervalConverter(false))}
+ };
+ parameters.AddOptionalParameter("since", since.HasValue ? JsonConvert.SerializeObject(since, new TimestampSecondsConverter()): null);
+ return await Execute(GetUri("/0/public/OHLC"), parameters: parameters).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get the order book for a market
+ ///
+ /// Market to get the book for
+ /// Limit to book to the best x bids/asks
+ /// Order book for the market
+ public WebCallResult GetOrderBook(string market, int? limit = null) => GetOrderBookAsync(market, limit).Result;
+ ///
+ /// Get the order book for a market
+ ///
+ /// Market to get the book for
+ /// Limit to book to the best x bids/asks
+ /// Order book for the market
+ public async Task> GetOrderBookAsync(string market, int? limit = null)
+ {
+ var parameters = new Dictionary()
+ {
+ {"pair", market},
+ };
+ parameters.AddOptionalParameter("count", limit);
+ var result = await Execute>(GetUri("/0/public/Depth"), parameters: parameters).ConfigureAwait(false);
+ if(!result.Success)
+ return new WebCallResult(result.ResponseStatusCode, result.ResponseHeaders, null, result.Error);
+ return new WebCallResult(result.ResponseStatusCode, result.ResponseHeaders, result.Data.First().Value, result.Error);
+ }
+
+ ///
+ /// Get a list of recent trades for a market
+ ///
+ /// Market to get trades for
+ /// Return trades since a specific time
+ /// Recent trades
+ public WebCallResult GetRecentTrades(string market, DateTime? since = null) => GetRecentTradesAsync(market, since).Result;
+ ///
+ /// Get a list of recent trades for a market
+ ///
+ /// Market to get trades for
+ /// Return trades since a specific time
+ /// Recent trades
+ public async Task> GetRecentTradesAsync(string market, DateTime? since = null)
+ {
+ var parameters = new Dictionary()
+ {
+ {"pair", market},
+ };
+ parameters.AddOptionalParameter("since", since.HasValue ? JsonConvert.SerializeObject(since, new TimestampSecondsConverter()) : null);
+ return await Execute(GetUri("/0/public/Trades"), parameters: parameters).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get spread data for a market
+ ///
+ /// Market to get spread data for
+ /// Return spread data since a specific time
+ /// Spread data
+ public WebCallResult GetRecentSpread(string market, DateTime? since = null) => GetRecentSpreadAsync(market, since).Result;
+ ///
+ /// Get spread data for a market
+ ///
+ /// Market to get spread data for
+ /// Return spread data since a specific time
+ /// Spread data
+ public async Task> GetRecentSpreadAsync(string market, DateTime? since = null)
+ {
+ var parameters = new Dictionary()
+ {
+ {"pair", market},
+ };
+ parameters.AddOptionalParameter("since", since.HasValue ? JsonConvert.SerializeObject(since, new TimestampSecondsConverter()) : null);
+ return await Execute(GetUri("/0/public/Spread"), parameters: parameters).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get balances
+ ///
+ /// Dictionary with balances for assets
+ public WebCallResult> GetAccountBalance() => GetAccountBalanceAsync().Result;
+ ///
+ /// Get balances
+ ///
+ /// Dictionary with balances for assets
+ public async Task>> GetAccountBalanceAsync()
+ {
+ return await Execute>(GetUri("/0/private/Balance"), Constants.PostMethod, null, true).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get trade balance
+ ///
+ /// Base asset to get trade balance for
+ /// Trade balance data
+ public WebCallResult GetTradeBalance(string baseAsset = null) => GetTradeBalanceAsync(baseAsset).Result;
+ ///
+ /// Get trade balance
+ ///
+ /// Base asset to get trade balance for
+ /// Trade balance data
+ public async Task> GetTradeBalanceAsync(string baseAsset = null)
+ {
+ var parameters = new Dictionary();
+ parameters.AddOptionalParameter("aclass", "currency");
+ parameters.AddOptionalParameter("asset", baseAsset);
+ return await Execute(GetUri("/0/private/TradeBalance"), Constants.PostMethod, null, true).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get a list of open orders
+ ///
+ /// Filter by client order id
+ /// List of open orders
+ public WebCallResult GetOpenOrders(string clientOrderId = null) => GetOpenOrdersAsync(clientOrderId).Result;
+ ///
+ /// Get a list of open orders
+ ///
+ /// Filter by client order id
+ /// List of open orders
+ public async Task> GetOpenOrdersAsync(string clientOrderId = null)
+ {
+ var parameters = new Dictionary();
+ parameters.AddOptionalParameter("trades", true);
+ parameters.AddOptionalParameter("userref", clientOrderId);
+ return await Execute(GetUri("/0/private/OpenOrders"), Constants.PostMethod, parameters, true).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get a list of closed orders
+ ///
+ /// Filter by client order id
+ /// Return data after this time
+ /// Return data before this time
+ /// Offset the results by
+ /// Closed orders page
+ public WebCallResult GetClosedOrders(string clientOrderId = null, DateTime? startTime = null, DateTime? endTime = null, int? resultOffset = null) => GetClosedOrdersAsync(clientOrderId, startTime, endTime, resultOffset).Result;
+ ///
+ /// Get a list of closed orders
+ ///
+ /// Filter by client order id
+ /// Return data after this time
+ /// Return data before this time
+ /// Offset the results by
+ /// Closed orders page
+ public async Task> GetClosedOrdersAsync(string clientOrderId = null, DateTime? startTime = null, DateTime? endTime = null, int? resultOffset = null)
+ {
+ var parameters = new Dictionary();
+ parameters.AddOptionalParameter("trades", true);
+ parameters.AddOptionalParameter("userref", clientOrderId);
+ parameters.AddOptionalParameter("start", startTime.HasValue ? JsonConvert.SerializeObject(startTime.Value, new TimestampSecondsConverter()) : null);
+ parameters.AddOptionalParameter("end", endTime.HasValue ? JsonConvert.SerializeObject(endTime.Value, new TimestampSecondsConverter()) : null);
+ parameters.AddOptionalParameter("ofs", resultOffset);
+ return await Execute(GetUri("/0/private/ClosedOrders"), Constants.PostMethod, parameters, true).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get info on specific orders
+ ///
+ /// Get orders by clientOrderId
+ /// Get orders by their order ids
+ /// Dictionary with order info
+ public WebCallResult> GetOrders(string clientOrderId = null, params string[] orderIds) => GetOrdersAsync(clientOrderId, orderIds).Result;
+ ///
+ /// Get info on specific orders
+ ///
+ /// Get orders by clientOrderId
+ /// Get orders by their order ids
+ /// Dictionary with order info
+ public async Task>> GetOrdersAsync(string clientOrderId = null, params string[] orderIds)
+ {
+ if((string.IsNullOrEmpty(clientOrderId) && !orderIds.Any()) || (!string.IsNullOrEmpty(clientOrderId) && orderIds.Any()))
+ return WebCallResult>.CreateErrorResult(new ArgumentError("Provide either clientOrderId or orderIds"));
+
+ var parameters = new Dictionary();
+ parameters.AddOptionalParameter("trades", true);
+ parameters.AddOptionalParameter("userref", clientOrderId);
+ parameters.AddOptionalParameter("txid", orderIds.Any() ? string.Join(",", orderIds): null);
+ return await Execute>(GetUri("/0/private/QueryOrders"), Constants.PostMethod, parameters, true).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get trade history
+ ///
+ /// Return data after this time
+ /// Return data before this time
+ /// Offset the results by
+ /// Trade history page
+ public WebCallResult GetTradeHistory(DateTime? startTime = null, DateTime? endTime = null, int? resultOffset = null) => GetTradeHistoryAsync(startTime, endTime, resultOffset).Result;
+ ///
+ /// Get trade history
+ ///
+ /// Return data after this time
+ /// Return data before this time
+ /// Offset the results by
+ /// Trade history page
+ public async Task> GetTradeHistoryAsync(DateTime? startTime = null, DateTime? endTime = null, int? resultOffset = null)
+ {
+ var parameters = new Dictionary();
+ parameters.AddOptionalParameter("trades", true);
+ parameters.AddOptionalParameter("start", startTime.HasValue ? JsonConvert.SerializeObject(startTime.Value, new TimestampSecondsConverter()) : null);
+ parameters.AddOptionalParameter("end", endTime.HasValue ? JsonConvert.SerializeObject(endTime.Value, new TimestampSecondsConverter()) : null);
+ parameters.AddOptionalParameter("ofs", resultOffset);
+ return await Execute(GetUri("/0/private/TradesHistory"), Constants.PostMethod, parameters, true).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get info on specific trades
+ ///
+ /// The trades to get info on
+ /// Dictionary with trade info
+ public WebCallResult> GetTrades(params string[] tradeIds) => GetTradesAsync(tradeIds).Result;
+ ///
+ /// Get info on specific trades
+ ///
+ /// The trades to get info on
+ /// Dictionary with trade info
+ public async Task>> GetTradesAsync(params string[] tradeIds)
+ {
+ var parameters = new Dictionary();
+ parameters.AddOptionalParameter("trades", true);
+ parameters.AddOptionalParameter("txid", tradeIds.Any() ? string.Join(",", tradeIds) : null);
+ return await Execute>(GetUri("/0/private/QueryTrades"), Constants.PostMethod, parameters, true).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get a list of open positions
+ ///
+ /// Filter by transaction ids
+ /// Dictionary with position info
+ public WebCallResult> GetOpenPositions(params string[] transactionIds) => GetOpenPositionsAsync(transactionIds).Result;
+ ///
+ /// Get a list of open positions
+ ///
+ /// Filter by transaction ids
+ /// Dictionary with position info
+ public async Task>> GetOpenPositionsAsync(params string[] transactionIds)
+ {
+ var parameters = new Dictionary();
+ parameters.AddOptionalParameter("docalcs", true);
+ parameters.AddOptionalParameter("txid", transactionIds.Any() ? string.Join(",", transactionIds) : null);
+ return await Execute>(GetUri("/0/private/OpenPositions"), Constants.PostMethod, parameters, true).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get ledger entries info
+ ///
+ /// Filter list by asset names
+ /// Filter list by entry types
+ /// Return data after this time
+ /// Return data before this time
+ /// Offset the results by
+ /// Ledger entries page
+ public WebCallResult GetLedgerInfo(string[] assets = null, LedgerEntryType[] entryTypes = null, DateTime? startTime = null, DateTime? endTime = null, int? resultOffset = null) => GetLedgerInfoAsync(assets, entryTypes, startTime, endTime, resultOffset).Result;
+ ///
+ /// Get ledger entries info
+ ///
+ /// Filter list by asset names
+ /// Filter list by entry types
+ /// Return data after this time
+ /// Return data before this time
+ /// Offset the results by
+ /// Ledger entries page
+ public async Task> GetLedgerInfoAsync(string[] assets = null, LedgerEntryType[] entryTypes = null, DateTime? startTime = null, DateTime? endTime = null, int? resultOffset = null)
+ {
+ var parameters = new Dictionary();
+ parameters.AddOptionalParameter("asset", assets != null ? string.Join(",", assets) : null);
+ parameters.AddOptionalParameter("type", entryTypes != null ? string.Join(",", entryTypes.Select(e => JsonConvert.SerializeObject(e, new LedgerEntryTypeConverter()))) : null);
+ parameters.AddOptionalParameter("start", startTime.HasValue ? JsonConvert.SerializeObject(startTime.Value, new TimestampSecondsConverter()) : null);
+ parameters.AddOptionalParameter("end", endTime.HasValue ? JsonConvert.SerializeObject(endTime.Value, new TimestampSecondsConverter()) : null);
+ parameters.AddOptionalParameter("ofs", resultOffset);
+ return await Execute(GetUri("/0/private/Ledgers"), Constants.PostMethod, parameters, true).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get info on specific ledger entries
+ ///
+ /// The ids to get info for
+ /// Dictionary with ledger entry info
+ public WebCallResult> GetLedgersEntry(params string[] ledgerIds) => GetLedgersEntryAsync(ledgerIds).Result;
+ ///
+ /// Get info on specific ledger entries
+ ///
+ /// The ids to get info for
+ /// Dictionary with ledger entry info
+ public async Task>> GetLedgersEntryAsync(params string[] ledgerIds)
+ {
+ var parameters = new Dictionary