mirror of
https://github.com/JKorf/CryptoExchange.Net
synced 2025-06-12 02:16:23 +00:00
Added code docs, added ContinueOnQueryResponse
This commit is contained in:
parent
11016bc213
commit
780da53475
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Attributes
|
namespace CryptoExchange.Net.Attributes
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Marks property as optional
|
||||||
|
/// </summary>
|
||||||
public class JsonOptionalPropertyAttribute : Attribute
|
public class JsonOptionalPropertyAttribute : Attribute
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,9 @@ using Newtonsoft.Json.Linq;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Authentication
|
namespace CryptoExchange.Net.Authentication
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Api credentials info
|
||||||
|
/// </summary>
|
||||||
public class ApiCredentials: IDisposable
|
public class ApiCredentials: IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -85,14 +88,14 @@ namespace CryptoExchange.Net.Authentication
|
|||||||
inputStream.Seek(0, SeekOrigin.Begin);
|
inputStream.Seek(0, SeekOrigin.Begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected string TryGetValue(JToken data, string key)
|
private string TryGetValue(JToken data, string key)
|
||||||
{
|
{
|
||||||
if (data[key] == null)
|
if (data[key] == null)
|
||||||
return null;
|
return null;
|
||||||
return (string) data[key];
|
return (string) data[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SecureString CreateSecureString(string source)
|
private SecureString CreateSecureString(string source)
|
||||||
{
|
{
|
||||||
var secureString = new SecureString();
|
var secureString = new SecureString();
|
||||||
foreach (var c in source)
|
foreach (var c in source)
|
||||||
@ -101,6 +104,9 @@ namespace CryptoExchange.Net.Authentication
|
|||||||
return secureString;
|
return secureString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose
|
||||||
|
/// </summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Key?.Dispose();
|
Key?.Dispose();
|
||||||
|
@ -2,35 +2,76 @@
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Authentication
|
namespace CryptoExchange.Net.Authentication
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for authentication providers
|
||||||
|
/// </summary>
|
||||||
public abstract class AuthenticationProvider
|
public abstract class AuthenticationProvider
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The provided credentials
|
||||||
|
/// </summary>
|
||||||
public ApiCredentials Credentials { get; }
|
public ApiCredentials Credentials { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="credentials"></param>
|
||||||
protected AuthenticationProvider(ApiCredentials credentials)
|
protected AuthenticationProvider(ApiCredentials credentials)
|
||||||
{
|
{
|
||||||
Credentials = credentials;
|
Credentials = credentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add authentication to the parameter list
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uri"></param>
|
||||||
|
/// <param name="method"></param>
|
||||||
|
/// <param name="parameters"></param>
|
||||||
|
/// <param name="signed"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public virtual Dictionary<string, object> AddAuthenticationToParameters(string uri, string method, Dictionary<string, object> parameters, bool signed)
|
public virtual Dictionary<string, object> AddAuthenticationToParameters(string uri, string method, Dictionary<string, object> parameters, bool signed)
|
||||||
{
|
{
|
||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add authentication to the header dictionary
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uri"></param>
|
||||||
|
/// <param name="method"></param>
|
||||||
|
/// <param name="parameters"></param>
|
||||||
|
/// <param name="signed"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public virtual Dictionary<string, string> AddAuthenticationToHeaders(string uri, string method, Dictionary<string, object> parameters, bool signed)
|
public virtual Dictionary<string, string> AddAuthenticationToHeaders(string uri, string method, Dictionary<string, object> parameters, bool signed)
|
||||||
{
|
{
|
||||||
return new Dictionary<string, string>();
|
return new Dictionary<string, string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sign a string
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="toSign"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public virtual string Sign(string toSign)
|
public virtual string Sign(string toSign)
|
||||||
{
|
{
|
||||||
return toSign;
|
return toSign;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sign a byte array
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="toSign"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public virtual byte[] Sign(byte[] toSign)
|
public virtual byte[] Sign(byte[] toSign)
|
||||||
{
|
{
|
||||||
return toSign;
|
return toSign;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert byte array to hex
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buff"></param>
|
||||||
|
/// <returns></returns>
|
||||||
protected string ByteToString(byte[] buff)
|
protected string ByteToString(byte[] buff)
|
||||||
{
|
{
|
||||||
var result = "";
|
var result = "";
|
||||||
|
@ -3,6 +3,9 @@ using System.Security;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Authentication
|
namespace CryptoExchange.Net.Authentication
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Private key info
|
||||||
|
/// </summary>
|
||||||
public class PrivateKey : IDisposable
|
public class PrivateKey : IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -87,6 +90,9 @@ namespace CryptoExchange.Net.Authentication
|
|||||||
IsEncrypted = false;
|
IsEncrypted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose
|
||||||
|
/// </summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Key?.Dispose();
|
Key?.Dispose();
|
||||||
|
@ -12,14 +12,35 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net
|
namespace CryptoExchange.Net
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The base for all clients
|
||||||
|
/// </summary>
|
||||||
public abstract class BaseClient: IDisposable
|
public abstract class BaseClient: IDisposable
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The address of the client
|
||||||
|
/// </summary>
|
||||||
public string BaseAddress { get; private set; }
|
public string BaseAddress { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The log object
|
||||||
|
/// </summary>
|
||||||
protected internal Log log;
|
protected internal Log log;
|
||||||
|
/// <summary>
|
||||||
|
/// The api proxy
|
||||||
|
/// </summary>
|
||||||
protected ApiProxy apiProxy;
|
protected ApiProxy apiProxy;
|
||||||
|
/// <summary>
|
||||||
|
/// The auth provider
|
||||||
|
/// </summary>
|
||||||
protected internal AuthenticationProvider authProvider;
|
protected internal AuthenticationProvider authProvider;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The last used id
|
||||||
|
/// </summary>
|
||||||
protected static int lastId;
|
protected static int lastId;
|
||||||
|
/// <summary>
|
||||||
|
/// Lock for id generating
|
||||||
|
/// </summary>
|
||||||
protected static object idLock = new object();
|
protected static object idLock = new object();
|
||||||
|
|
||||||
private static readonly JsonSerializer defaultSerializer = JsonSerializer.Create(new JsonSerializerSettings
|
private static readonly JsonSerializer defaultSerializer = JsonSerializer.Create(new JsonSerializerSettings
|
||||||
@ -28,8 +49,16 @@ namespace CryptoExchange.Net
|
|||||||
Culture = CultureInfo.InvariantCulture
|
Culture = CultureInfo.InvariantCulture
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Last is used
|
||||||
|
/// </summary>
|
||||||
public static int LastId => lastId;
|
public static int LastId => lastId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
/// <param name="authenticationProvider"></param>
|
||||||
protected BaseClient(ClientOptions options, AuthenticationProvider authenticationProvider)
|
protected BaseClient(ClientOptions options, AuthenticationProvider authenticationProvider)
|
||||||
{
|
{
|
||||||
log = new Log();
|
log = new Log();
|
||||||
@ -40,7 +69,7 @@ namespace CryptoExchange.Net
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configure the client using the provided options
|
/// Configure the client using the provided options
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="clientOptionsns">Options</param>
|
/// <param name="clientOptions">Options</param>
|
||||||
protected void Configure(ClientOptions clientOptions)
|
protected void Configure(ClientOptions clientOptions)
|
||||||
{
|
{
|
||||||
log.UpdateWriters(clientOptions.LogWriters);
|
log.UpdateWriters(clientOptions.LogWriters);
|
||||||
@ -306,6 +335,9 @@ namespace CryptoExchange.Net
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose
|
||||||
|
/// </summary>
|
||||||
public virtual void Dispose()
|
public virtual void Dispose()
|
||||||
{
|
{
|
||||||
authProvider?.Credentials?.Dispose();
|
authProvider?.Credentials?.Dispose();
|
||||||
|
@ -8,13 +8,18 @@ using Newtonsoft.Json.Linq;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Converters
|
namespace CryptoExchange.Net.Converters
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converter for arrays to properties
|
||||||
|
/// </summary>
|
||||||
public class ArrayConverter : JsonConverter
|
public class ArrayConverter : JsonConverter
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public override bool CanConvert(Type objectType)
|
public override bool CanConvert(Type objectType)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
if (objectType == typeof(JToken))
|
if (objectType == typeof(JToken))
|
||||||
@ -95,6 +100,7 @@ namespace CryptoExchange.Net.Converters
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
writer.WriteStartArray();
|
writer.WriteStartArray();
|
||||||
@ -143,10 +149,20 @@ namespace CryptoExchange.Net.Converters
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mark property as an index in the array
|
||||||
|
/// </summary>
|
||||||
public class ArrayPropertyAttribute: Attribute
|
public class ArrayPropertyAttribute: Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The index in the array
|
||||||
|
/// </summary>
|
||||||
public int Index { get; }
|
public int Index { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"></param>
|
||||||
public ArrayPropertyAttribute(int index)
|
public ArrayPropertyAttribute(int index)
|
||||||
{
|
{
|
||||||
Index = index;
|
Index = index;
|
||||||
|
@ -6,16 +6,28 @@ using Newtonsoft.Json;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Converters
|
namespace CryptoExchange.Net.Converters
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for enum converters
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of enum to convert</typeparam>
|
||||||
public abstract class BaseConverter<T>: JsonConverter
|
public abstract class BaseConverter<T>: JsonConverter
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The enum->string mapping
|
||||||
|
/// </summary>
|
||||||
protected abstract List<KeyValuePair<T, string>> Mapping { get; }
|
protected abstract List<KeyValuePair<T, string>> Mapping { get; }
|
||||||
private readonly bool quotes;
|
private readonly bool quotes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="useQuotes"></param>
|
||||||
protected BaseConverter(bool useQuotes)
|
protected BaseConverter(bool useQuotes)
|
||||||
{
|
{
|
||||||
quotes = useQuotes;
|
quotes = useQuotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
var stringValue = GetValue((T) value);
|
var stringValue = GetValue((T) value);
|
||||||
@ -25,6 +37,7 @@ namespace CryptoExchange.Net.Converters
|
|||||||
writer.WriteRawValue(stringValue);
|
writer.WriteRawValue(stringValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
if (reader.Value == null)
|
if (reader.Value == null)
|
||||||
@ -39,11 +52,17 @@ namespace CryptoExchange.Net.Converters
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert a string value
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public T ReadString(string data)
|
public T ReadString(string data)
|
||||||
{
|
{
|
||||||
return Mapping.FirstOrDefault(v => v.Value == data).Key;
|
return Mapping.FirstOrDefault(v => v.Value == data).Key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override bool CanConvert(Type objectType)
|
public override bool CanConvert(Type objectType)
|
||||||
{
|
{
|
||||||
// Check if it is type, or nullable of type
|
// Check if it is type, or nullable of type
|
||||||
|
@ -3,13 +3,18 @@ using Newtonsoft.Json;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Converters
|
namespace CryptoExchange.Net.Converters
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// converter for milliseconds to datetime
|
||||||
|
/// </summary>
|
||||||
public class TimestampConverter : JsonConverter
|
public class TimestampConverter : JsonConverter
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public override bool CanConvert(Type objectType)
|
public override bool CanConvert(Type objectType)
|
||||||
{
|
{
|
||||||
return objectType == typeof(DateTime);
|
return objectType == typeof(DateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
if (reader.Value == null)
|
if (reader.Value == null)
|
||||||
@ -19,6 +24,7 @@ namespace CryptoExchange.Net.Converters
|
|||||||
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(t);
|
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
writer.WriteValue((long)Math.Round(((DateTime)value - new DateTime(1970, 1, 1)).TotalMilliseconds));
|
writer.WriteValue((long)Math.Round(((DateTime)value - new DateTime(1970, 1, 1)).TotalMilliseconds));
|
||||||
|
@ -3,13 +3,18 @@ using Newtonsoft.Json;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Converters
|
namespace CryptoExchange.Net.Converters
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converter for nanoseconds to datetime
|
||||||
|
/// </summary>
|
||||||
public class TimestampNanoSecondsConverter : JsonConverter
|
public class TimestampNanoSecondsConverter : JsonConverter
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public override bool CanConvert(Type objectType)
|
public override bool CanConvert(Type objectType)
|
||||||
{
|
{
|
||||||
return objectType == typeof(DateTime);
|
return objectType == typeof(DateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
if (reader.Value == null)
|
if (reader.Value == null)
|
||||||
@ -20,6 +25,7 @@ namespace CryptoExchange.Net.Converters
|
|||||||
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddTicks((long)Math.Round(nanoSeconds * ticksPerNanosecond));
|
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddTicks((long)Math.Round(nanoSeconds * ticksPerNanosecond));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
var ticksPerNanosecond = (TimeSpan.TicksPerMillisecond / 1000m / 1000);
|
var ticksPerNanosecond = (TimeSpan.TicksPerMillisecond / 1000m / 1000);
|
||||||
|
@ -4,13 +4,18 @@ using Newtonsoft.Json;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Converters
|
namespace CryptoExchange.Net.Converters
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converter for seconds to datetime
|
||||||
|
/// </summary>
|
||||||
public class TimestampSecondsConverter : JsonConverter
|
public class TimestampSecondsConverter : JsonConverter
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public override bool CanConvert(Type objectType)
|
public override bool CanConvert(Type objectType)
|
||||||
{
|
{
|
||||||
return objectType == typeof(DateTime);
|
return objectType == typeof(DateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
if (reader.Value is double d)
|
if (reader.Value is double d)
|
||||||
@ -20,6 +25,7 @@ namespace CryptoExchange.Net.Converters
|
|||||||
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(t);
|
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
writer.WriteValue((long)Math.Round(((DateTime)value - new DateTime(1970, 1, 1)).TotalSeconds));
|
writer.WriteValue((long)Math.Round(((DateTime)value - new DateTime(1970, 1, 1)).TotalSeconds));
|
||||||
|
@ -3,13 +3,18 @@ using Newtonsoft.Json;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Converters
|
namespace CryptoExchange.Net.Converters
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converter for utc datetime
|
||||||
|
/// </summary>
|
||||||
public class UTCDateTimeConverter: JsonConverter
|
public class UTCDateTimeConverter: JsonConverter
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
writer.WriteValue(JsonConvert.SerializeObject(value));
|
writer.WriteValue(JsonConvert.SerializeObject(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
if (reader.Value == null)
|
if (reader.Value == null)
|
||||||
@ -24,6 +29,7 @@ namespace CryptoExchange.Net.Converters
|
|||||||
return DateTime.SpecifyKind(value, DateTimeKind.Utc);
|
return DateTime.SpecifyKind(value, DateTimeKind.Utc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override bool CanConvert(Type objectType)
|
public override bool CanConvert(Type objectType)
|
||||||
{
|
{
|
||||||
return objectType == typeof(DateTime) || objectType == typeof(DateTime?);
|
return objectType == typeof(DateTime) || objectType == typeof(DateTime?);
|
||||||
|
@ -1,27 +1,23 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageId>CryptoExchange.Net</PackageId>
|
<PackageId>CryptoExchange.Net</PackageId>
|
||||||
<Authors>JKorf</Authors>
|
<Authors>JKorf</Authors>
|
||||||
<PackageVersion>2.1.5</PackageVersion>
|
<PackageVersion>2.1.6</PackageVersion>
|
||||||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||||
<PackageProjectUrl>https://github.com/JKorf/CryptoExchange.Net</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/JKorf/CryptoExchange.Net</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/JKorf/CryptoExchange.Net/blob/master/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/JKorf/CryptoExchange.Net/blob/master/LICENSE</PackageLicenseUrl>
|
||||||
<NeutralLanguage>en</NeutralLanguage>
|
<NeutralLanguage>en</NeutralLanguage>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
<PackageReleaseNotes>2.1.6 - Fix for missing subscription events if they are also a request response, added code docs</PackageReleaseNotes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DocumentationFile>CryptoExchange.Net.xml</DocumentationFile>
|
<DocumentationFile>CryptoExchange.Net.xml</DocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||||
<PackageReference Include="WebSocket4Net" Version="0.15.2" />
|
<PackageReference Include="WebSocket4Net" Version="0.15.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
</Project>
|
|
File diff suppressed because it is too large
Load Diff
@ -13,24 +13,51 @@ using Newtonsoft.Json.Linq;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net
|
namespace CryptoExchange.Net
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Helper methods
|
||||||
|
/// </summary>
|
||||||
public static class ExtensionMethods
|
public static class ExtensionMethods
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Add a parameter
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameters"></param>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
public static void AddParameter(this Dictionary<string, object> parameters, string key, string value)
|
public static void AddParameter(this Dictionary<string, object> parameters, string key, string value)
|
||||||
{
|
{
|
||||||
parameters.Add(key, value);
|
parameters.Add(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a parameter
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameters"></param>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
public static void AddParameter(this Dictionary<string, object> parameters, string key, object value)
|
public static void AddParameter(this Dictionary<string, object> parameters, string key, object value)
|
||||||
{
|
{
|
||||||
parameters.Add(key, value);
|
parameters.Add(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add an optional parameter. Not added if value is null
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameters"></param>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
public static void AddOptionalParameter(this Dictionary<string, object> parameters, string key, object value)
|
public static void AddOptionalParameter(this Dictionary<string, object> parameters, string key, object value)
|
||||||
{
|
{
|
||||||
if(value != null)
|
if(value != null)
|
||||||
parameters.Add(key, value);
|
parameters.Add(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add an optional parameter. Not added if value is null
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameters"></param>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
public static void AddOptionalParameter(this Dictionary<string, string> parameters, string key, string value)
|
public static void AddOptionalParameter(this Dictionary<string, string> parameters, string key, string value)
|
||||||
{
|
{
|
||||||
if (value != null)
|
if (value != null)
|
||||||
@ -90,6 +117,11 @@ namespace CryptoExchange.Net
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Header collection to inenumerable
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="headers"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static IEnumerable<Tuple<string, string>> ToIEnumerable(this WebHeaderCollection headers)
|
public static IEnumerable<Tuple<string, string>> ToIEnumerable(this WebHeaderCollection headers)
|
||||||
{
|
{
|
||||||
if (headers == null)
|
if (headers == null)
|
||||||
@ -102,6 +134,13 @@ namespace CryptoExchange.Net
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wait one async
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handle"></param>
|
||||||
|
/// <param name="millisecondsTimeout"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static async Task<bool> WaitOneAsync(this WaitHandle handle, int millisecondsTimeout, CancellationToken cancellationToken)
|
public static async Task<bool> WaitOneAsync(this WaitHandle handle, int millisecondsTimeout, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
RegisteredWaitHandle registeredHandle = null;
|
RegisteredWaitHandle registeredHandle = null;
|
||||||
@ -126,12 +165,24 @@ namespace CryptoExchange.Net
|
|||||||
tokenRegistration.Dispose();
|
tokenRegistration.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wait one async
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handle"></param>
|
||||||
|
/// <param name="timeout"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static Task<bool> WaitOneAsync(this WaitHandle handle, TimeSpan timeout)
|
public static Task<bool> WaitOneAsync(this WaitHandle handle, TimeSpan timeout)
|
||||||
{
|
{
|
||||||
return handle.WaitOneAsync((int)timeout.TotalMilliseconds, CancellationToken.None);
|
return handle.WaitOneAsync((int)timeout.TotalMilliseconds, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// String to JToken
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stringData"></param>
|
||||||
|
/// <param name="log"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static JToken ToJToken(this string stringData, Log log = null)
|
public static JToken ToJToken(this string stringData, Log log = null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(stringData))
|
if (string.IsNullOrEmpty(stringData))
|
||||||
|
@ -2,8 +2,18 @@
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Interfaces
|
namespace CryptoExchange.Net.Interfaces
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Rate limiter interface
|
||||||
|
/// </summary>
|
||||||
public interface IRateLimiter
|
public interface IRateLimiter
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Limit the request if needed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="client"></param>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
/// <param name="limitBehaviour"></param>
|
||||||
|
/// <returns></returns>
|
||||||
CallResult<double> LimitRequest(RestClient client, string url, RateLimitingBehaviour limitBehaviour);
|
CallResult<double> LimitRequest(RestClient client, string url, RateLimitingBehaviour limitBehaviour);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,20 +5,62 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Interfaces
|
namespace CryptoExchange.Net.Interfaces
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Request interface
|
||||||
|
/// </summary>
|
||||||
public interface IRequest
|
public interface IRequest
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The uri of the request
|
||||||
|
/// </summary>
|
||||||
Uri Uri { get; }
|
Uri Uri { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// The headers of the request
|
||||||
|
/// </summary>
|
||||||
WebHeaderCollection Headers { get; set; }
|
WebHeaderCollection Headers { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The method of the request
|
||||||
|
/// </summary>
|
||||||
string Method { get; set; }
|
string Method { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The timeout of the request
|
||||||
|
/// </summary>
|
||||||
TimeSpan Timeout { get; set; }
|
TimeSpan Timeout { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set a proxy
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host"></param>
|
||||||
|
/// <param name="port"></param>
|
||||||
|
/// <param name="login"></param>
|
||||||
|
/// <param name="password"></param>
|
||||||
void SetProxy(string host, int port, string login, string password);
|
void SetProxy(string host, int port, string login, string password);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Content type
|
||||||
|
/// </summary>
|
||||||
string ContentType { get; set; }
|
string ContentType { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// String content
|
||||||
|
/// </summary>
|
||||||
string Content { get; set; }
|
string Content { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Accept
|
||||||
|
/// </summary>
|
||||||
string Accept { get; set; }
|
string Accept { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Content length
|
||||||
|
/// </summary>
|
||||||
long ContentLength { get; set; }
|
long ContentLength { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the request stream
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
Task<Stream> GetRequestStream();
|
Task<Stream> GetRequestStream();
|
||||||
|
/// <summary>
|
||||||
|
/// Get the response object
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
Task<IResponse> GetResponse();
|
Task<IResponse> GetResponse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
namespace CryptoExchange.Net.Interfaces
|
namespace CryptoExchange.Net.Interfaces
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Request factory interface
|
||||||
|
/// </summary>
|
||||||
public interface IRequestFactory
|
public interface IRequestFactory
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create a request for an uri
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uri"></param>
|
||||||
|
/// <returns></returns>
|
||||||
IRequest Create(string uri);
|
IRequest Create(string uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,28 @@ using System.Net;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Interfaces
|
namespace CryptoExchange.Net.Interfaces
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Response object interface
|
||||||
|
/// </summary>
|
||||||
public interface IResponse
|
public interface IResponse
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The response status code
|
||||||
|
/// </summary>
|
||||||
HttpStatusCode StatusCode { get; }
|
HttpStatusCode StatusCode { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get the response stream
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
Stream GetResponseStream();
|
Stream GetResponseStream();
|
||||||
|
/// <summary>
|
||||||
|
/// Get the response headers
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
IEnumerable<Tuple<string, string>> GetResponseHeaders();
|
IEnumerable<Tuple<string, string>> GetResponseHeaders();
|
||||||
|
/// <summary>
|
||||||
|
/// Close the response
|
||||||
|
/// </summary>
|
||||||
void Close();
|
void Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,30 +5,104 @@ using WebSocket4Net;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Interfaces
|
namespace CryptoExchange.Net.Interfaces
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for websocket interaction
|
||||||
|
/// </summary>
|
||||||
public interface IWebsocket: IDisposable
|
public interface IWebsocket: IDisposable
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Websocket closed
|
||||||
|
/// </summary>
|
||||||
event Action OnClose;
|
event Action OnClose;
|
||||||
|
/// <summary>
|
||||||
|
/// Websocket message received
|
||||||
|
/// </summary>
|
||||||
event Action<string> OnMessage;
|
event Action<string> OnMessage;
|
||||||
|
/// <summary>
|
||||||
|
/// Websocket error
|
||||||
|
/// </summary>
|
||||||
event Action<Exception> OnError;
|
event Action<Exception> OnError;
|
||||||
|
/// <summary>
|
||||||
|
/// Websocket opened
|
||||||
|
/// </summary>
|
||||||
event Action OnOpen;
|
event Action OnOpen;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Id
|
||||||
|
/// </summary>
|
||||||
int Id { get; }
|
int Id { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// Origin
|
||||||
|
/// </summary>
|
||||||
string Origin { get; set; }
|
string Origin { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Reconnecting
|
||||||
|
/// </summary>
|
||||||
bool Reconnecting { get; set; }
|
bool Reconnecting { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for byte data
|
||||||
|
/// </summary>
|
||||||
Func<byte[], string> DataInterpreterBytes { get; set; }
|
Func<byte[], string> DataInterpreterBytes { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for string data
|
||||||
|
/// </summary>
|
||||||
Func<string, string> DataInterpreterString { get; set; }
|
Func<string, string> DataInterpreterString { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Socket url
|
||||||
|
/// </summary>
|
||||||
string Url { get; }
|
string Url { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// State
|
||||||
|
/// </summary>
|
||||||
WebSocketState SocketState { get; }
|
WebSocketState SocketState { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// Is closed
|
||||||
|
/// </summary>
|
||||||
bool IsClosed { get; }
|
bool IsClosed { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// Is open
|
||||||
|
/// </summary>
|
||||||
bool IsOpen { get; }
|
bool IsOpen { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// Should ping connecting
|
||||||
|
/// </summary>
|
||||||
bool PingConnection { get; set; }
|
bool PingConnection { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Interval of pinging
|
||||||
|
/// </summary>
|
||||||
TimeSpan PingInterval { get; set; }
|
TimeSpan PingInterval { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Supported ssl protocols
|
||||||
|
/// </summary>
|
||||||
SslProtocols SSLProtocols { get; set; }
|
SslProtocols SSLProtocols { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Timeout
|
||||||
|
/// </summary>
|
||||||
TimeSpan Timeout { get; set; }
|
TimeSpan Timeout { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Connect the socket
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
Task<bool> Connect();
|
Task<bool> Connect();
|
||||||
|
/// <summary>
|
||||||
|
/// Send data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
void Send(string data);
|
void Send(string data);
|
||||||
|
/// <summary>
|
||||||
|
/// Reset socket
|
||||||
|
/// </summary>
|
||||||
void Reset();
|
void Reset();
|
||||||
|
/// <summary>
|
||||||
|
/// Close the connecting
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
Task Close();
|
Task Close();
|
||||||
|
/// <summary>
|
||||||
|
/// Set proxy
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host"></param>
|
||||||
|
/// <param name="port"></param>
|
||||||
void SetProxy(string host, int port);
|
void SetProxy(string host, int port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,26 @@ using CryptoExchange.Net.Logging;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Interfaces
|
namespace CryptoExchange.Net.Interfaces
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Websocket factory interface
|
||||||
|
/// </summary>
|
||||||
public interface IWebsocketFactory
|
public interface IWebsocketFactory
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create a websocket for an url
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="log"></param>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
/// <returns></returns>
|
||||||
IWebsocket CreateWebsocket(Log log, string url);
|
IWebsocket CreateWebsocket(Log log, string url);
|
||||||
|
/// <summary>
|
||||||
|
/// Create a websocket for an url
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="log"></param>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
/// <param name="cookies"></param>
|
||||||
|
/// <param name="headers"></param>
|
||||||
|
/// <returns></returns>
|
||||||
IWebsocket CreateWebsocket(Log log, string url, IDictionary<string, string> cookies, IDictionary<string, string> headers);
|
IWebsocket CreateWebsocket(Log log, string url, IDictionary<string, string> cookies, IDictionary<string, string> headers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,15 @@ using System.Text;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Logging
|
namespace CryptoExchange.Net.Logging
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Default log writer, writes to debug
|
||||||
|
/// </summary>
|
||||||
public class DebugTextWriter: TextWriter
|
public class DebugTextWriter: TextWriter
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public override Encoding Encoding => Encoding.ASCII;
|
public override Encoding Encoding => Encoding.ASCII;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override void WriteLine(string value)
|
public override void WriteLine(string value)
|
||||||
{
|
{
|
||||||
Debug.WriteLine(value);
|
Debug.WriteLine(value);
|
||||||
|
@ -6,22 +6,39 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Logging
|
namespace CryptoExchange.Net.Logging
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Log implementation
|
||||||
|
/// </summary>
|
||||||
public class Log
|
public class Log
|
||||||
{
|
{
|
||||||
private List<TextWriter> writers;
|
private List<TextWriter> writers;
|
||||||
|
/// <summary>
|
||||||
|
/// The verbosity of the logging
|
||||||
|
/// </summary>
|
||||||
public LogVerbosity Level { get; set; } = LogVerbosity.Info;
|
public LogVerbosity Level { get; set; } = LogVerbosity.Info;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
public Log()
|
public Log()
|
||||||
{
|
{
|
||||||
writers = new List<TextWriter>();
|
writers = new List<TextWriter>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the writers
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="textWriters"></param>
|
||||||
public void UpdateWriters(List<TextWriter> textWriters)
|
public void UpdateWriters(List<TextWriter> textWriters)
|
||||||
{
|
{
|
||||||
writers = textWriters;
|
writers = textWriters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write a log entry
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logType"></param>
|
||||||
|
/// <param name="message"></param>
|
||||||
public void Write(LogVerbosity logType, string message)
|
public void Write(LogVerbosity logType, string message)
|
||||||
{
|
{
|
||||||
if ((int)logType < (int)Level)
|
if ((int)logType < (int)Level)
|
||||||
@ -42,12 +59,30 @@ namespace CryptoExchange.Net.Logging
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The log verbosity
|
||||||
|
/// </summary>
|
||||||
public enum LogVerbosity
|
public enum LogVerbosity
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Debug logging
|
||||||
|
/// </summary>
|
||||||
Debug,
|
Debug,
|
||||||
|
/// <summary>
|
||||||
|
/// Info logging
|
||||||
|
/// </summary>
|
||||||
Info,
|
Info,
|
||||||
|
/// <summary>
|
||||||
|
/// Warning logging
|
||||||
|
/// </summary>
|
||||||
Warning,
|
Warning,
|
||||||
|
/// <summary>
|
||||||
|
/// Error logging
|
||||||
|
/// </summary>
|
||||||
Error,
|
Error,
|
||||||
|
/// <summary>
|
||||||
|
/// None, used for disabling logging
|
||||||
|
/// </summary>
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,9 @@ using System.Text;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Logging
|
namespace CryptoExchange.Net.Logging
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// File writer
|
||||||
|
/// </summary>
|
||||||
public class ThreadSafeFileWriter: TextWriter
|
public class ThreadSafeFileWriter: TextWriter
|
||||||
{
|
{
|
||||||
private static readonly object openedFilesLock = new object();
|
private static readonly object openedFilesLock = new object();
|
||||||
@ -12,8 +15,13 @@ namespace CryptoExchange.Net.Logging
|
|||||||
private StreamWriter logWriter;
|
private StreamWriter logWriter;
|
||||||
private readonly object writeLock;
|
private readonly object writeLock;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override Encoding Encoding => Encoding.ASCII;
|
public override Encoding Encoding => Encoding.ASCII;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path"></param>
|
||||||
public ThreadSafeFileWriter(string path)
|
public ThreadSafeFileWriter(string path)
|
||||||
{
|
{
|
||||||
logWriter = new StreamWriter(File.Open(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite)) {AutoFlush = true};
|
logWriter = new StreamWriter(File.Open(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite)) {AutoFlush = true};
|
||||||
@ -28,12 +36,17 @@ namespace CryptoExchange.Net.Logging
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override void WriteLine(string logMessage)
|
public override void WriteLine(string logMessage)
|
||||||
{
|
{
|
||||||
lock(writeLock)
|
lock(writeLock)
|
||||||
logWriter.WriteLine(logMessage);
|
logWriter.WriteLine(logMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing"></param>
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
lock (writeLock)
|
lock (writeLock)
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Objects
|
namespace CryptoExchange.Net.Objects
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Proxy info
|
||||||
|
/// </summary>
|
||||||
public class ApiProxy
|
public class ApiProxy
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -3,8 +3,17 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Objects
|
namespace CryptoExchange.Net.Objects
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Comparer for byte order
|
||||||
|
/// </summary>
|
||||||
public class ByteOrderComparer : IComparer<byte[]>
|
public class ByteOrderComparer : IComparer<byte[]>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Compare function
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x"></param>
|
||||||
|
/// <param name="y"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public int Compare(byte[] x, byte[] y)
|
public int Compare(byte[] x, byte[] y)
|
||||||
{
|
{
|
||||||
// Shortcuts: If both are null, they are the same.
|
// Shortcuts: If both are null, they are the same.
|
||||||
|
@ -4,6 +4,10 @@ using System.Net;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Objects
|
namespace CryptoExchange.Net.Objects
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The result of an operation
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
public class CallResult<T>
|
public class CallResult<T>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -19,6 +23,11 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Success => Error == null;
|
public bool Success => Error == null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="error"></param>
|
||||||
public CallResult(T data, Error error)
|
public CallResult(T data, Error error)
|
||||||
{
|
{
|
||||||
Data = data;
|
Data = data;
|
||||||
@ -26,6 +35,10 @@ namespace CryptoExchange.Net.Objects
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The result of a request
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
public class WebCallResult<T>: CallResult<T>
|
public class WebCallResult<T>: CallResult<T>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -33,18 +46,41 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public HttpStatusCode? ResponseStatusCode { get; set; }
|
public HttpStatusCode? ResponseStatusCode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The response headers
|
||||||
|
/// </summary>
|
||||||
public IEnumerable<Tuple<string, string>> ResponseHeaders { get; set; }
|
public IEnumerable<Tuple<string, string>> ResponseHeaders { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code"></param>
|
||||||
|
/// <param name="responseHeaders"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="error"></param>
|
||||||
public WebCallResult(HttpStatusCode? code, IEnumerable<Tuple<string, string>> responseHeaders, T data, Error error): base(data, error)
|
public WebCallResult(HttpStatusCode? code, IEnumerable<Tuple<string, string>> responseHeaders, T data, Error error): base(data, error)
|
||||||
{
|
{
|
||||||
ResponseHeaders = responseHeaders;
|
ResponseHeaders = responseHeaders;
|
||||||
ResponseStatusCode = code;
|
ResponseStatusCode = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an error result
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="error"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static WebCallResult<T> CreateErrorResult(Error error)
|
public static WebCallResult<T> CreateErrorResult(Error error)
|
||||||
{
|
{
|
||||||
return new WebCallResult<T>(null, null, default(T), error);
|
return new WebCallResult<T>(null, null, default(T), error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an error result
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code"></param>
|
||||||
|
/// <param name="responseHeaders"></param>
|
||||||
|
/// <param name="error"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static WebCallResult<T> CreateErrorResult(HttpStatusCode? code, IEnumerable<Tuple<string, string>> responseHeaders, Error error)
|
public static WebCallResult<T> CreateErrorResult(HttpStatusCode? code, IEnumerable<Tuple<string, string>> responseHeaders, Error error)
|
||||||
{
|
{
|
||||||
return new WebCallResult<T>(code, responseHeaders, default(T), error);
|
return new WebCallResult<T>(code, responseHeaders, default(T), error);
|
||||||
|
@ -1,13 +1,34 @@
|
|||||||
namespace CryptoExchange.Net.Objects
|
namespace CryptoExchange.Net.Objects
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Constants
|
||||||
|
/// </summary>
|
||||||
public class Constants
|
public class Constants
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// GET Http method
|
||||||
|
/// </summary>
|
||||||
public const string GetMethod = "GET";
|
public const string GetMethod = "GET";
|
||||||
|
/// <summary>
|
||||||
|
/// POST Http method
|
||||||
|
/// </summary>
|
||||||
public const string PostMethod = "POST";
|
public const string PostMethod = "POST";
|
||||||
|
/// <summary>
|
||||||
|
/// DELETE Http method
|
||||||
|
/// </summary>
|
||||||
public const string DeleteMethod = "DELETE";
|
public const string DeleteMethod = "DELETE";
|
||||||
|
/// <summary>
|
||||||
|
/// PUT Http method
|
||||||
|
/// </summary>
|
||||||
public const string PutMethod = "PUT";
|
public const string PutMethod = "PUT";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Json content type header
|
||||||
|
/// </summary>
|
||||||
public const string JsonContentHeader = "application/json";
|
public const string JsonContentHeader = "application/json";
|
||||||
|
/// <summary>
|
||||||
|
/// Form content type header
|
||||||
|
/// </summary>
|
||||||
public const string FormContentHeader = "application/x-www-form-urlencoded";
|
public const string FormContentHeader = "application/x-www-form-urlencoded";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,85 @@
|
|||||||
namespace CryptoExchange.Net.Objects
|
namespace CryptoExchange.Net.Objects
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// What to do when a request would exceed the rate limit
|
||||||
|
/// </summary>
|
||||||
public enum RateLimitingBehaviour
|
public enum RateLimitingBehaviour
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Fail the request
|
||||||
|
/// </summary>
|
||||||
Fail,
|
Fail,
|
||||||
|
/// <summary>
|
||||||
|
/// Wait till the request can be send
|
||||||
|
/// </summary>
|
||||||
Wait
|
Wait
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Where the post parameters should be added
|
||||||
|
/// </summary>
|
||||||
public enum PostParameters
|
public enum PostParameters
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Post parameters in body
|
||||||
|
/// </summary>
|
||||||
InBody,
|
InBody,
|
||||||
|
/// <summary>
|
||||||
|
/// Post parameters in url
|
||||||
|
/// </summary>
|
||||||
InUri
|
InUri
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The format of the request body
|
||||||
|
/// </summary>
|
||||||
public enum RequestBodyFormat
|
public enum RequestBodyFormat
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Form data
|
||||||
|
/// </summary>
|
||||||
FormData,
|
FormData,
|
||||||
|
/// <summary>
|
||||||
|
/// Json
|
||||||
|
/// </summary>
|
||||||
Json
|
Json
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Status of the order book
|
||||||
|
/// </summary>
|
||||||
public enum OrderBookStatus
|
public enum OrderBookStatus
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Not connected
|
||||||
|
/// </summary>
|
||||||
Disconnected,
|
Disconnected,
|
||||||
|
/// <summary>
|
||||||
|
/// Connecting
|
||||||
|
/// </summary>
|
||||||
Connecting,
|
Connecting,
|
||||||
|
/// <summary>
|
||||||
|
/// Syncing data
|
||||||
|
/// </summary>
|
||||||
Syncing,
|
Syncing,
|
||||||
|
/// <summary>
|
||||||
|
/// Data synced, order book is up to date
|
||||||
|
/// </summary>
|
||||||
Synced,
|
Synced,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Order book entry type
|
||||||
|
/// </summary>
|
||||||
public enum OrderBookEntryType
|
public enum OrderBookEntryType
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ask
|
||||||
|
/// </summary>
|
||||||
Ask,
|
Ask,
|
||||||
|
/// <summary>
|
||||||
|
/// Bid
|
||||||
|
/// </summary>
|
||||||
Bid
|
Bid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
namespace CryptoExchange.Net.Objects
|
namespace CryptoExchange.Net.Objects
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for errors
|
||||||
|
/// </summary>
|
||||||
public abstract class Error
|
public abstract class Error
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -11,59 +14,127 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code"></param>
|
||||||
|
/// <param name="message"></param>
|
||||||
protected Error(int code, string message)
|
protected Error(int code, string message)
|
||||||
{
|
{
|
||||||
Code = code;
|
Code = code;
|
||||||
Message = message;
|
Message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// String representation
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"{Code}: {Message}";
|
return $"{Code}: {Message}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cant reach server error
|
||||||
|
/// </summary>
|
||||||
public class CantConnectError : Error
|
public class CantConnectError : Error
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
public CantConnectError() : base(1, "Can't connect to the server") { }
|
public CantConnectError() : base(1, "Can't connect to the server") { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// No api credentials provided while trying to access private endpoint
|
||||||
|
/// </summary>
|
||||||
public class NoApiCredentialsError : Error
|
public class NoApiCredentialsError : Error
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
public NoApiCredentialsError() : base(2, "No credentials provided for private endpoint") { }
|
public NoApiCredentialsError() : base(2, "No credentials provided for private endpoint") { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Error returned by the server
|
||||||
|
/// </summary>
|
||||||
public class ServerError: Error
|
public class ServerError: Error
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
public ServerError(string message) : base(3, "Server error: " + message) { }
|
public ServerError(string message) : base(3, "Server error: " + message) { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code"></param>
|
||||||
|
/// <param name="message"></param>
|
||||||
public ServerError(int code, string message) : base(code, message)
|
public ServerError(int code, string message) : base(code, message)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Web error returned by the server
|
||||||
|
/// </summary>
|
||||||
public class WebError : Error
|
public class WebError : Error
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
public WebError(string message) : base(4, "Web error: " + message) { }
|
public WebError(string message) : base(4, "Web error: " + message) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Error while deserializing data
|
||||||
|
/// </summary>
|
||||||
public class DeserializeError : Error
|
public class DeserializeError : Error
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
public DeserializeError(string message) : base(5, "Error deserializing data: " + message) { }
|
public DeserializeError(string message) : base(5, "Error deserializing data: " + message) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unknown error
|
||||||
|
/// </summary>
|
||||||
public class UnknownError : Error
|
public class UnknownError : Error
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
public UnknownError(string message) : base(6, "Unknown error occured " + message) { }
|
public UnknownError(string message) : base(6, "Unknown error occured " + message) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An invalid parameter has been provided
|
||||||
|
/// </summary>
|
||||||
public class ArgumentError : Error
|
public class ArgumentError : Error
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
public ArgumentError(string message) : base(7, "Invalid parameter: " + message) { }
|
public ArgumentError(string message) : base(7, "Invalid parameter: " + message) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rate limit exceeded
|
||||||
|
/// </summary>
|
||||||
public class RateLimitError: Error
|
public class RateLimitError: Error
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
public RateLimitError(string message) : base(8, "Rate limit exceeded: " + message) { }
|
public RateLimitError(string message) : base(8, "Rate limit exceeded: " + message) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,6 +91,11 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(30);
|
public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(30);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a copy of the options
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
public T Copy<T>() where T:RestClientOptions, new()
|
public T Copy<T>() where T:RestClientOptions, new()
|
||||||
{
|
{
|
||||||
var copy = new T
|
var copy = new T
|
||||||
@ -141,6 +146,11 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int? SocketSubscriptionsCombineTarget { get; set; }
|
public int? SocketSubscriptionsCombineTarget { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a copy of the options
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
public T Copy<T>() where T : SocketClientOptions, new()
|
public T Copy<T>() where T : SocketClientOptions, new()
|
||||||
{
|
{
|
||||||
var copy = new T
|
var copy = new T
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
namespace CryptoExchange.Net.OrderBook
|
namespace CryptoExchange.Net.OrderBook
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for order book entries
|
||||||
|
/// </summary>
|
||||||
public interface ISymbolOrderBookEntry
|
public interface ISymbolOrderBookEntry
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,10 +1,24 @@
|
|||||||
namespace CryptoExchange.Net.OrderBook
|
namespace CryptoExchange.Net.OrderBook
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Order book entry
|
||||||
|
/// </summary>
|
||||||
public class OrderBookEntry : ISymbolOrderBookEntry
|
public class OrderBookEntry : ISymbolOrderBookEntry
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Quantity of the entry
|
||||||
|
/// </summary>
|
||||||
public decimal Quantity { get; set; }
|
public decimal Quantity { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Price of the entry
|
||||||
|
/// </summary>
|
||||||
public decimal Price { get; set; }
|
public decimal Price { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="price"></param>
|
||||||
|
/// <param name="quantity"></param>
|
||||||
public OrderBookEntry(decimal price, decimal quantity)
|
public OrderBookEntry(decimal price, decimal quantity)
|
||||||
{
|
{
|
||||||
Quantity = quantity;
|
Quantity = quantity;
|
||||||
|
@ -2,12 +2,27 @@
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.OrderBook
|
namespace CryptoExchange.Net.OrderBook
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Buffer entry for order book
|
||||||
|
/// </summary>
|
||||||
public class ProcessBufferEntry
|
public class ProcessBufferEntry
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The first sequence number of the entries
|
||||||
|
/// </summary>
|
||||||
public long FirstSequence { get; set; }
|
public long FirstSequence { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The last sequence number of the entries
|
||||||
|
/// </summary>
|
||||||
public long LastSequence { get; set; }
|
public long LastSequence { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// List of entries
|
||||||
|
/// </summary>
|
||||||
public List<ProcessEntry> Entries { get; set; }
|
public List<ProcessEntry> Entries { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
public ProcessBufferEntry()
|
public ProcessBufferEntry()
|
||||||
{
|
{
|
||||||
Entries = new List<ProcessEntry>();
|
Entries = new List<ProcessEntry>();
|
||||||
|
@ -2,11 +2,25 @@
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.OrderBook
|
namespace CryptoExchange.Net.OrderBook
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Process entry for order book
|
||||||
|
/// </summary>
|
||||||
public class ProcessEntry
|
public class ProcessEntry
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The entry
|
||||||
|
/// </summary>
|
||||||
public ISymbolOrderBookEntry Entry { get; set; }
|
public ISymbolOrderBookEntry Entry { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The type
|
||||||
|
/// </summary>
|
||||||
public OrderBookEntryType Type { get; set; }
|
public OrderBookEntryType Type { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <param name="entry"></param>
|
||||||
public ProcessEntry(OrderBookEntryType type, ISymbolOrderBookEntry entry)
|
public ProcessEntry(OrderBookEntryType type, ISymbolOrderBookEntry entry)
|
||||||
{
|
{
|
||||||
Type = type;
|
Type = type;
|
||||||
|
@ -15,14 +15,26 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class SymbolOrderBook: IDisposable
|
public abstract class SymbolOrderBook: IDisposable
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The process buffer, used while syncing
|
||||||
|
/// </summary>
|
||||||
protected readonly List<ProcessBufferEntry> processBuffer;
|
protected readonly List<ProcessBufferEntry> processBuffer;
|
||||||
private readonly object bookLock = new object();
|
private readonly object bookLock = new object();
|
||||||
|
/// <summary>
|
||||||
|
/// The ask list
|
||||||
|
/// </summary>
|
||||||
protected SortedList<decimal, OrderBookEntry> asks;
|
protected SortedList<decimal, OrderBookEntry> asks;
|
||||||
|
/// <summary>
|
||||||
|
/// The bid list
|
||||||
|
/// </summary>
|
||||||
protected SortedList<decimal, OrderBookEntry> bids;
|
protected SortedList<decimal, OrderBookEntry> bids;
|
||||||
private OrderBookStatus status;
|
private OrderBookStatus status;
|
||||||
private UpdateSubscription subscription;
|
private UpdateSubscription subscription;
|
||||||
private readonly bool sequencesAreConsecutive;
|
private readonly bool sequencesAreConsecutive;
|
||||||
private readonly string id;
|
private readonly string id;
|
||||||
|
/// <summary>
|
||||||
|
/// The log
|
||||||
|
/// </summary>
|
||||||
protected Log log;
|
protected Log log;
|
||||||
|
|
||||||
private bool bookSet;
|
private bool bookSet;
|
||||||
@ -116,6 +128,11 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="symbol"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
protected SymbolOrderBook(string symbol, OrderBookOptions options)
|
protected SymbolOrderBook(string symbol, OrderBookOptions options)
|
||||||
{
|
{
|
||||||
id = options.OrderBookName;
|
id = options.OrderBookName;
|
||||||
@ -198,12 +215,29 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
await subscription.Close().ConfigureAwait(false);
|
await subscription.Close().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start the order book
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
protected abstract Task<CallResult<UpdateSubscription>> DoStart();
|
protected abstract Task<CallResult<UpdateSubscription>> DoStart();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reset the order book
|
||||||
|
/// </summary>
|
||||||
protected virtual void DoReset() { }
|
protected virtual void DoReset() { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resync the order book
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
protected abstract Task<CallResult<bool>> DoResync();
|
protected abstract Task<CallResult<bool>> DoResync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the initial data for the order book
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="orderBookSequenceNumber">The last update sequence number</param>
|
||||||
|
/// <param name="askList">List of asks</param>
|
||||||
|
/// <param name="bidList">List of bids</param>
|
||||||
protected void SetInitialOrderBook(long orderBookSequenceNumber, IEnumerable<ISymbolOrderBookEntry> askList, IEnumerable<ISymbolOrderBookEntry> bidList)
|
protected void SetInitialOrderBook(long orderBookSequenceNumber, IEnumerable<ISymbolOrderBookEntry> askList, IEnumerable<ISymbolOrderBookEntry> bidList)
|
||||||
{
|
{
|
||||||
lock (bookLock)
|
lock (bookLock)
|
||||||
@ -229,6 +263,12 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update the order book with entries
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="firstSequenceNumber">First sequence number</param>
|
||||||
|
/// <param name="lastSequenceNumber">Last sequence number</param>
|
||||||
|
/// <param name="entries">List of entries</param>
|
||||||
protected void UpdateOrderBook(long firstSequenceNumber, long lastSequenceNumber, List<ProcessEntry> entries)
|
protected void UpdateOrderBook(long firstSequenceNumber, long lastSequenceNumber, List<ProcessEntry> entries)
|
||||||
{
|
{
|
||||||
lock (bookLock)
|
lock (bookLock)
|
||||||
@ -264,6 +304,9 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check and empty the process buffer; see what entries to update the book with
|
||||||
|
/// </summary>
|
||||||
protected void CheckProcessBuffer()
|
protected void CheckProcessBuffer()
|
||||||
{
|
{
|
||||||
foreach (var bufferEntry in processBuffer.OrderBy(b => b.FirstSequence).ToList())
|
foreach (var bufferEntry in processBuffer.OrderBy(b => b.FirstSequence).ToList())
|
||||||
@ -284,6 +327,11 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update order book with an entry
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Type of entry</param>
|
||||||
|
/// <param name="entry">The entry</param>
|
||||||
protected virtual void ProcessUpdate(OrderBookEntryType type, ISymbolOrderBookEntry entry)
|
protected virtual void ProcessUpdate(OrderBookEntryType type, ISymbolOrderBookEntry entry)
|
||||||
{
|
{
|
||||||
var listToChange = type == OrderBookEntryType.Ask ? asks : bids;
|
var listToChange = type == OrderBookEntryType.Ask ? asks : bids;
|
||||||
@ -311,13 +359,24 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose the order book
|
||||||
|
/// </summary>
|
||||||
public abstract void Dispose();
|
public abstract void Dispose();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// String representation of the top 3 entries
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return ToString(3);
|
return ToString(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// String representation of the top x entries
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public string ToString(int numberOfEntries)
|
public string ToString(int numberOfEntries)
|
||||||
{
|
{
|
||||||
var result = "";
|
var result = "";
|
||||||
|
@ -4,17 +4,33 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.RateLimiter
|
namespace CryptoExchange.Net.RateLimiter
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Rate limiting object
|
||||||
|
/// </summary>
|
||||||
public class RateLimitObject
|
public class RateLimitObject
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Lock
|
||||||
|
/// </summary>
|
||||||
public object LockObject { get; }
|
public object LockObject { get; }
|
||||||
private List<DateTime> Times { get; }
|
private List<DateTime> Times { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
public RateLimitObject()
|
public RateLimitObject()
|
||||||
{
|
{
|
||||||
LockObject = new object();
|
LockObject = new object();
|
||||||
Times = new List<DateTime>();
|
Times = new List<DateTime>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get time to wait for a specific time
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="time"></param>
|
||||||
|
/// <param name="limit"></param>
|
||||||
|
/// <param name="perTimePeriod"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public int GetWaitTime(DateTime time, int limit, TimeSpan perTimePeriod)
|
public int GetWaitTime(DateTime time, int limit, TimeSpan perTimePeriod)
|
||||||
{
|
{
|
||||||
Times.RemoveAll(d => d < time - perTimePeriod);
|
Times.RemoveAll(d => d < time - perTimePeriod);
|
||||||
@ -23,6 +39,10 @@ namespace CryptoExchange.Net.RateLimiter
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add an executed request time
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="time"></param>
|
||||||
public void Add(DateTime time)
|
public void Add(DateTime time)
|
||||||
{
|
{
|
||||||
Times.Add(time);
|
Times.Add(time);
|
||||||
|
@ -29,7 +29,7 @@ namespace CryptoExchange.Net.RateLimiter
|
|||||||
this.perTimePeriod = perTimePeriod;
|
this.perTimePeriod = perTimePeriod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public CallResult<double> LimitRequest(RestClient client, string url, RateLimitingBehaviour limitBehaviour)
|
public CallResult<double> LimitRequest(RestClient client, string url, RateLimitingBehaviour limitBehaviour)
|
||||||
{
|
{
|
||||||
if(client.authProvider?.Credentials == null)
|
if(client.authProvider?.Credentials == null)
|
||||||
|
@ -29,6 +29,7 @@ namespace CryptoExchange.Net.RateLimiter
|
|||||||
this.perTimePeriod = perTimePeriod;
|
this.perTimePeriod = perTimePeriod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public CallResult<double> LimitRequest(RestClient client, string url, RateLimitingBehaviour limitingBehaviour)
|
public CallResult<double> LimitRequest(RestClient client, string url, RateLimitingBehaviour limitingBehaviour)
|
||||||
{
|
{
|
||||||
int waitTime;
|
int waitTime;
|
||||||
|
@ -30,6 +30,7 @@ namespace CryptoExchange.Net.RateLimiter
|
|||||||
this.perTimePeriod = perTimePeriod;
|
this.perTimePeriod = perTimePeriod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public CallResult<double> LimitRequest(RestClient client, string url, RateLimitingBehaviour limitBehaviour)
|
public CallResult<double> LimitRequest(RestClient client, string url, RateLimitingBehaviour limitBehaviour)
|
||||||
{
|
{
|
||||||
var sw = Stopwatch.StartNew();
|
var sw = Stopwatch.StartNew();
|
||||||
|
@ -6,65 +6,84 @@ using CryptoExchange.Net.Interfaces;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Requests
|
namespace CryptoExchange.Net.Requests
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Request object
|
||||||
|
/// </summary>
|
||||||
public class Request : IRequest
|
public class Request : IRequest
|
||||||
{
|
{
|
||||||
private readonly WebRequest request;
|
private readonly WebRequest request;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create request object for webrequest
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
public Request(WebRequest request)
|
public Request(WebRequest request)
|
||||||
{
|
{
|
||||||
this.request = request;
|
this.request = request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public WebHeaderCollection Headers
|
public WebHeaderCollection Headers
|
||||||
{
|
{
|
||||||
get => request.Headers;
|
get => request.Headers;
|
||||||
set => request.Headers = value;
|
set => request.Headers = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string ContentType
|
public string ContentType
|
||||||
{
|
{
|
||||||
get => request.ContentType;
|
get => request.ContentType;
|
||||||
set => request.ContentType = value;
|
set => request.ContentType = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string Content { get; set; }
|
public string Content { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string Accept
|
public string Accept
|
||||||
{
|
{
|
||||||
get => ((HttpWebRequest)request).Accept;
|
get => ((HttpWebRequest)request).Accept;
|
||||||
set => ((HttpWebRequest)request).Accept = value;
|
set => ((HttpWebRequest)request).Accept = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public long ContentLength
|
public long ContentLength
|
||||||
{
|
{
|
||||||
get => ((HttpWebRequest)request).ContentLength;
|
get => ((HttpWebRequest)request).ContentLength;
|
||||||
set => ((HttpWebRequest)request).ContentLength = value;
|
set => ((HttpWebRequest)request).ContentLength = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string Method
|
public string Method
|
||||||
{
|
{
|
||||||
get => request.Method;
|
get => request.Method;
|
||||||
set => request.Method = value;
|
set => request.Method = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public TimeSpan Timeout
|
public TimeSpan Timeout
|
||||||
{
|
{
|
||||||
get => TimeSpan.FromMilliseconds(request.Timeout);
|
get => TimeSpan.FromMilliseconds(request.Timeout);
|
||||||
set => request.Timeout = (int)Math.Round(value.TotalMilliseconds);
|
set => request.Timeout = (int)Math.Round(value.TotalMilliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public Uri Uri => request.RequestUri;
|
public Uri Uri => request.RequestUri;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public void SetProxy(string host, int port, string login, string password)
|
public void SetProxy(string host, int port, string login, string password)
|
||||||
{
|
{
|
||||||
request.Proxy = new WebProxy(host, port);
|
request.Proxy = new WebProxy(host, port);
|
||||||
if(!string.IsNullOrEmpty(login) && !string.IsNullOrEmpty(password)) request.Proxy.Credentials = new NetworkCredential(login, password);
|
if(!string.IsNullOrEmpty(login) && !string.IsNullOrEmpty(password)) request.Proxy.Credentials = new NetworkCredential(login, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public async Task<Stream> GetRequestStream()
|
public async Task<Stream> GetRequestStream()
|
||||||
{
|
{
|
||||||
return await request.GetRequestStreamAsync().ConfigureAwait(false);
|
return await request.GetRequestStreamAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public async Task<IResponse> GetResponse()
|
public async Task<IResponse> GetResponse()
|
||||||
{
|
{
|
||||||
return new Response((HttpWebResponse)await request.GetResponseAsync().ConfigureAwait(false));
|
return new Response((HttpWebResponse)await request.GetResponseAsync().ConfigureAwait(false));
|
||||||
|
@ -3,8 +3,12 @@ using CryptoExchange.Net.Interfaces;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Requests
|
namespace CryptoExchange.Net.Requests
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// WebRequest factory
|
||||||
|
/// </summary>
|
||||||
public class RequestFactory : IRequestFactory
|
public class RequestFactory : IRequestFactory
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public IRequest Create(string uri)
|
public IRequest Create(string uri)
|
||||||
{
|
{
|
||||||
return new Request(WebRequest.Create(uri));
|
return new Request(WebRequest.Create(uri));
|
||||||
|
@ -1,33 +1,43 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using CryptoExchange.Net.Interfaces;
|
using CryptoExchange.Net.Interfaces;
|
||||||
|
|
||||||
namespace CryptoExchange.Net.Requests
|
namespace CryptoExchange.Net.Requests
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// HttpWebResponse response object
|
||||||
|
/// </summary>
|
||||||
public class Response : IResponse
|
public class Response : IResponse
|
||||||
{
|
{
|
||||||
private readonly HttpWebResponse response;
|
private readonly HttpWebResponse response;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public HttpStatusCode StatusCode => response.StatusCode;
|
public HttpStatusCode StatusCode => response.StatusCode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create response for http web response
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="response"></param>
|
||||||
public Response(HttpWebResponse response)
|
public Response(HttpWebResponse response)
|
||||||
{
|
{
|
||||||
this.response = response;
|
this.response = response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public Stream GetResponseStream()
|
public Stream GetResponseStream()
|
||||||
{
|
{
|
||||||
return response.GetResponseStream();
|
return response.GetResponseStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public IEnumerable<Tuple<string, string>> GetResponseHeaders()
|
public IEnumerable<Tuple<string, string>> GetResponseHeaders()
|
||||||
{
|
{
|
||||||
return response.Headers.ToIEnumerable();
|
return response.Headers.ToIEnumerable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public void Close()
|
public void Close()
|
||||||
{
|
{
|
||||||
response.Close();
|
response.Close();
|
||||||
|
@ -19,6 +19,9 @@ using Newtonsoft.Json.Linq;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net
|
namespace CryptoExchange.Net
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base rest client
|
||||||
|
/// </summary>
|
||||||
public abstract class RestClient: BaseClient, IRestClient
|
public abstract class RestClient: BaseClient, IRestClient
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -27,14 +30,37 @@ namespace CryptoExchange.Net
|
|||||||
public IRequestFactory RequestFactory { get; set; } = new RequestFactory();
|
public IRequestFactory RequestFactory { get; set; } = new RequestFactory();
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Where to place post parameters
|
||||||
|
/// </summary>
|
||||||
protected PostParameters postParametersPosition = PostParameters.InBody;
|
protected PostParameters postParametersPosition = PostParameters.InBody;
|
||||||
|
/// <summary>
|
||||||
|
/// Request body content type
|
||||||
|
/// </summary>
|
||||||
protected RequestBodyFormat requestBodyFormat = RequestBodyFormat.Json;
|
protected RequestBodyFormat requestBodyFormat = RequestBodyFormat.Json;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Timeout for requests
|
||||||
|
/// </summary>
|
||||||
protected TimeSpan RequestTimeout { get; private set; }
|
protected TimeSpan RequestTimeout { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Rate limiting behaviour
|
||||||
|
/// </summary>
|
||||||
public RateLimitingBehaviour RateLimitBehaviour { get; private set; }
|
public RateLimitingBehaviour RateLimitBehaviour { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// List of ratelimitters
|
||||||
|
/// </summary>
|
||||||
public IEnumerable<IRateLimiter> RateLimiters { get; private set; }
|
public IEnumerable<IRateLimiter> RateLimiters { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Total requests made
|
||||||
|
/// </summary>
|
||||||
public int TotalRequestsMade { get; private set; }
|
public int TotalRequestsMade { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="exchangeOptions"></param>
|
||||||
|
/// <param name="authenticationProvider"></param>
|
||||||
protected RestClient(RestClientOptions exchangeOptions, AuthenticationProvider authenticationProvider): base(exchangeOptions, authenticationProvider)
|
protected RestClient(RestClientOptions exchangeOptions, AuthenticationProvider authenticationProvider): base(exchangeOptions, authenticationProvider)
|
||||||
{
|
{
|
||||||
Configure(exchangeOptions);
|
Configure(exchangeOptions);
|
||||||
|
@ -13,6 +13,9 @@ using Newtonsoft.Json.Linq;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net
|
namespace CryptoExchange.Net
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base for socket client implementations
|
||||||
|
/// </summary>
|
||||||
public abstract class SocketClient: BaseClient, ISocketClient
|
public abstract class SocketClient: BaseClient, ISocketClient
|
||||||
{
|
{
|
||||||
#region fields
|
#region fields
|
||||||
@ -25,6 +28,8 @@ namespace CryptoExchange.Net
|
|||||||
/// List of socket connections currently connecting/connected
|
/// List of socket connections currently connecting/connected
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected internal ConcurrentDictionary<int, SocketConnection> sockets = new ConcurrentDictionary<int, SocketConnection>();
|
protected internal ConcurrentDictionary<int, SocketConnection> sockets = new ConcurrentDictionary<int, SocketConnection>();
|
||||||
|
/// <summary>
|
||||||
|
/// </summary>
|
||||||
protected internal readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
|
protected internal readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
|
||||||
|
|
||||||
/// <inheritdoc cref="SocketClientOptions.ReconnectInterval"/>
|
/// <inheritdoc cref="SocketClientOptions.ReconnectInterval"/>
|
||||||
@ -42,14 +47,43 @@ namespace CryptoExchange.Net
|
|||||||
/// <inheritdoc cref="SocketClientOptions.SocketSubscriptionsCombineTarget"/>
|
/// <inheritdoc cref="SocketClientOptions.SocketSubscriptionsCombineTarget"/>
|
||||||
public int SocketCombineTarget { get; protected set; }
|
public int SocketCombineTarget { get; protected set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for byte data
|
||||||
|
/// </summary>
|
||||||
protected Func<byte[], string> dataInterpreterBytes;
|
protected Func<byte[], string> dataInterpreterBytes;
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for string data
|
||||||
|
/// </summary>
|
||||||
protected Func<string, string> dataInterpreterString;
|
protected Func<string, string> dataInterpreterString;
|
||||||
|
/// <summary>
|
||||||
|
/// Generic handlers
|
||||||
|
/// </summary>
|
||||||
protected Dictionary<string, Action<SocketConnection, JToken>> genericHandlers = new Dictionary<string, Action<SocketConnection, JToken>>();
|
protected Dictionary<string, Action<SocketConnection, JToken>> genericHandlers = new Dictionary<string, Action<SocketConnection, JToken>>();
|
||||||
|
/// <summary>
|
||||||
|
/// Periodic task
|
||||||
|
/// </summary>
|
||||||
protected Task periodicTask;
|
protected Task periodicTask;
|
||||||
|
/// <summary>
|
||||||
|
/// Periodic task event
|
||||||
|
/// </summary>
|
||||||
protected AutoResetEvent periodicEvent;
|
protected AutoResetEvent periodicEvent;
|
||||||
|
/// <summary>
|
||||||
|
/// Is disposing
|
||||||
|
/// </summary>
|
||||||
protected bool disposing;
|
protected bool disposing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true; data which is a response to a query will also be distributed to subscriptions
|
||||||
|
/// If false; data which is a response to a query won't get forwarded to subscriptions as well
|
||||||
|
/// </summary>
|
||||||
|
protected internal bool ContinueOnQueryResponse { get; protected set; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a socket client
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="exchangeOptions">Client options</param>
|
||||||
|
/// <param name="authenticationProvider">Authentication provider</param>
|
||||||
protected SocketClient(SocketClientOptions exchangeOptions, AuthenticationProvider authenticationProvider): base(exchangeOptions, authenticationProvider)
|
protected SocketClient(SocketClientOptions exchangeOptions, AuthenticationProvider authenticationProvider): base(exchangeOptions, authenticationProvider)
|
||||||
{
|
{
|
||||||
Configure(exchangeOptions);
|
Configure(exchangeOptions);
|
||||||
@ -174,6 +208,13 @@ namespace CryptoExchange.Net
|
|||||||
return new CallResult<bool>(callResult?.Success ?? false, callResult == null ? new ServerError("No response on subscription request received"): callResult.Error);
|
return new CallResult<bool>(callResult?.Success ?? false, callResult == null ? new ServerError("No response on subscription request received"): callResult.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Query for data
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Exepected result type</typeparam>
|
||||||
|
/// <param name="request">The request to send</param>
|
||||||
|
/// <param name="authenticated">Whether the socket should be authenticated</param>
|
||||||
|
/// <returns></returns>
|
||||||
protected virtual Task<CallResult<T>> Query<T>(object request, bool authenticated)
|
protected virtual Task<CallResult<T>> Query<T>(object request, bool authenticated)
|
||||||
{
|
{
|
||||||
return Query<T>(BaseAddress, request, authenticated);
|
return Query<T>(BaseAddress, request, authenticated);
|
||||||
@ -183,6 +224,7 @@ namespace CryptoExchange.Net
|
|||||||
/// Query for data
|
/// Query for data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The expected result type</typeparam>
|
/// <typeparam name="T">The expected result type</typeparam>
|
||||||
|
/// <param name="url">The url for the request</param>
|
||||||
/// <param name="request">The request to send</param>
|
/// <param name="request">The request to send</param>
|
||||||
/// <param name="authenticated">Whether the socket should be authenticated</param>
|
/// <param name="authenticated">Whether the socket should be authenticated</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
@ -293,7 +335,7 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="s">The socket connection</param>
|
/// <param name="s">The socket connection</param>
|
||||||
/// <param name="subscription"></param>
|
/// <param name="subscription"></param>
|
||||||
/// <param name="request">The request that a response is awaited for</param>
|
/// <param name="request">The request that a response is awaited for</param>
|
||||||
/// <param name="data">The message</param>
|
/// <param name="message">The message</param>
|
||||||
/// <param name="callResult">The interpretation (null if message wasn't a response to the request)</param>
|
/// <param name="callResult">The interpretation (null if message wasn't a response to the request)</param>
|
||||||
/// <returns>True if the message was a response to the subscription request</returns>
|
/// <returns>True if the message was a response to the subscription request</returns>
|
||||||
protected internal abstract bool HandleSubscriptionResponse(SocketConnection s, SocketSubscription subscription, object request, JToken message, out CallResult<object> callResult);
|
protected internal abstract bool HandleSubscriptionResponse(SocketConnection s, SocketSubscription subscription, object request, JToken message, out CallResult<object> callResult);
|
||||||
|
@ -13,7 +13,10 @@ using WebSocket4Net;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Sockets
|
namespace CryptoExchange.Net.Sockets
|
||||||
{
|
{
|
||||||
public class BaseSocket: IWebsocket
|
/// <summary>
|
||||||
|
/// Socket implementation
|
||||||
|
/// </summary>
|
||||||
|
internal class BaseSocket: IWebsocket
|
||||||
{
|
{
|
||||||
internal static int lastStreamId;
|
internal static int lastStreamId;
|
||||||
private static readonly object streamIdLock = new object();
|
private static readonly object streamIdLock = new object();
|
||||||
|
@ -11,25 +11,58 @@ using Newtonsoft.Json.Linq;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Sockets
|
namespace CryptoExchange.Net.Sockets
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Socket connecting
|
||||||
|
/// </summary>
|
||||||
public class SocketConnection
|
public class SocketConnection
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Connection lost event
|
||||||
|
/// </summary>
|
||||||
public event Action ConnectionLost;
|
public event Action ConnectionLost;
|
||||||
|
/// <summary>
|
||||||
|
/// Connecting restored event
|
||||||
|
/// </summary>
|
||||||
public event Action<TimeSpan> ConnectionRestored;
|
public event Action<TimeSpan> ConnectionRestored;
|
||||||
|
/// <summary>
|
||||||
|
/// Connecting closed event
|
||||||
|
/// </summary>
|
||||||
public event Action Closed;
|
public event Action Closed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of handlers
|
||||||
|
/// </summary>
|
||||||
public int HandlerCount
|
public int HandlerCount
|
||||||
{
|
{
|
||||||
get { lock (handlersLock)
|
get { lock (handlersLock)
|
||||||
return handlers.Count(h => h.UserSubscription); }
|
return handlers.Count(h => h.UserSubscription); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If connection is authenticated
|
||||||
|
/// </summary>
|
||||||
public bool Authenticated { get; set; }
|
public bool Authenticated { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// If connection is made
|
||||||
|
/// </summary>
|
||||||
public bool Connected { get; private set; }
|
public bool Connected { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The socket
|
||||||
|
/// </summary>
|
||||||
public IWebsocket Socket { get; set; }
|
public IWebsocket Socket { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// If should reconnect upon closing
|
||||||
|
/// </summary>
|
||||||
public bool ShouldReconnect { get; set; }
|
public bool ShouldReconnect { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Time of disconnecting
|
||||||
|
/// </summary>
|
||||||
public DateTime? DisconnectTime { get; set; }
|
public DateTime? DisconnectTime { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// If activity is paused
|
||||||
|
/// </summary>
|
||||||
public bool PausedActivity { get; set; }
|
public bool PausedActivity { get; set; }
|
||||||
|
|
||||||
internal readonly List<SocketSubscription> handlers;
|
internal readonly List<SocketSubscription> handlers;
|
||||||
@ -41,6 +74,11 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
|
|
||||||
private readonly List<PendingRequest> pendingRequests;
|
private readonly List<PendingRequest> pendingRequests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// New socket connection
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="client">The socket client</param>
|
||||||
|
/// <param name="socket">The socket</param>
|
||||||
public SocketConnection(SocketClient client, IWebsocket socket)
|
public SocketConnection(SocketClient client, IWebsocket socket)
|
||||||
{
|
{
|
||||||
log = client.log;
|
log = client.log;
|
||||||
@ -72,6 +110,13 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a handler
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">The request object</param>
|
||||||
|
/// <param name="userSubscription">If it is a user subscription or a generic handler</param>
|
||||||
|
/// <param name="dataHandler">The data handler</param>
|
||||||
|
/// <returns></returns>
|
||||||
public SocketSubscription AddHandler(object request, bool userSubscription, Action<SocketConnection, JToken> dataHandler)
|
public SocketSubscription AddHandler(object request, bool userSubscription, Action<SocketConnection, JToken> dataHandler)
|
||||||
{
|
{
|
||||||
var handler = new SocketSubscription(null, request, userSubscription, dataHandler);
|
var handler = new SocketSubscription(null, request, userSubscription, dataHandler);
|
||||||
@ -80,6 +125,14 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a handler
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identifier">The identifier of the handler</param>
|
||||||
|
/// <param name="userSubscription">If it is a user subscription or a generic handler</param>
|
||||||
|
/// <param name="dataHandler">The data handler</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <returns></returns>
|
||||||
public SocketSubscription AddHandler(string identifier, bool userSubscription, Action<SocketConnection, JToken> dataHandler)
|
public SocketSubscription AddHandler(string identifier, bool userSubscription, Action<SocketConnection, JToken> dataHandler)
|
||||||
{
|
{
|
||||||
var handler = new SocketSubscription(identifier, null, userSubscription, dataHandler);
|
var handler = new SocketSubscription(identifier, null, userSubscription, dataHandler);
|
||||||
@ -88,7 +141,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ProcessMessage(string data)
|
private void ProcessMessage(string data)
|
||||||
{
|
{
|
||||||
log.Write(LogVerbosity.Debug, $"Socket {Socket.Id} received data: " + data);
|
log.Write(LogVerbosity.Debug, $"Socket {Socket.Id} received data: " + data);
|
||||||
var tokenData = data.ToJToken(log);
|
var tokenData = data.ToJToken(log);
|
||||||
@ -100,7 +153,9 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
if (pendingRequest.Check(tokenData))
|
if (pendingRequest.Check(tokenData))
|
||||||
{
|
{
|
||||||
pendingRequests.Remove(pendingRequest);
|
pendingRequests.Remove(pendingRequest);
|
||||||
return;
|
if (!socketClient.ContinueOnQueryResponse)
|
||||||
|
return;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,6 +211,14 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Send data
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The data type</typeparam>
|
||||||
|
/// <param name="obj">The object to send</param>
|
||||||
|
/// <param name="timeout">The timeout for response</param>
|
||||||
|
/// <param name="handler">The response handler</param>
|
||||||
|
/// <returns></returns>
|
||||||
public virtual Task SendAndWait<T>(T obj, TimeSpan timeout, Func<JToken, bool> handler)
|
public virtual Task SendAndWait<T>(T obj, TimeSpan timeout, Func<JToken, bool> handler)
|
||||||
{
|
{
|
||||||
var pending = new PendingRequest(handler, timeout);
|
var pending = new PendingRequest(handler, timeout);
|
||||||
@ -230,7 +293,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
if (lostTriggered)
|
if (lostTriggered)
|
||||||
{
|
{
|
||||||
lostTriggered = false;
|
lostTriggered = false;
|
||||||
Task.Run(() => ConnectionRestored?.Invoke(DisconnectTime.HasValue ? DateTime.UtcNow - DisconnectTime.Value : TimeSpan.FromSeconds(0)));
|
InvokeConnectionRestored();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -248,7 +311,12 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ProcessReconnect()
|
private async void InvokeConnectionRestored()
|
||||||
|
{
|
||||||
|
await Task.Run(() => ConnectionRestored?.Invoke(DisconnectTime.HasValue ? DateTime.UtcNow - DisconnectTime.Value : TimeSpan.FromSeconds(0))).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> ProcessReconnect()
|
||||||
{
|
{
|
||||||
if (Authenticated)
|
if (Authenticated)
|
||||||
{
|
{
|
||||||
@ -279,6 +347,10 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Close the connection
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public async Task Close()
|
public async Task Close()
|
||||||
{
|
{
|
||||||
Connected = false;
|
Connected = false;
|
||||||
@ -290,6 +362,11 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
Socket.Dispose();
|
Socket.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Close the subscriptions
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="subscription">Subscription to close</param>
|
||||||
|
/// <returns></returns>
|
||||||
public async Task Close(SocketSubscription subscription)
|
public async Task Close(SocketSubscription subscription)
|
||||||
{
|
{
|
||||||
if (subscription.Confirmed)
|
if (subscription.Confirmed)
|
||||||
@ -308,7 +385,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PendingRequest
|
internal class PendingRequest
|
||||||
{
|
{
|
||||||
public Func<JToken, bool> Handler { get; }
|
public Func<JToken, bool> Handler { get; }
|
||||||
public JToken Result { get; private set; }
|
public JToken Result { get; private set; }
|
||||||
|
@ -3,8 +3,14 @@ using Newtonsoft.Json.Linq;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Sockets
|
namespace CryptoExchange.Net.Sockets
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Socket subscription
|
||||||
|
/// </summary>
|
||||||
public class SocketSubscription
|
public class SocketSubscription
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Exception event
|
||||||
|
/// </summary>
|
||||||
public event Action<Exception> Exception;
|
public event Action<Exception> Exception;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -12,13 +18,32 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Action<SocketConnection, JToken> MessageHandler { get; set; }
|
public Action<SocketConnection, JToken> MessageHandler { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request object
|
||||||
|
/// </summary>
|
||||||
public object Request { get; set; }
|
public object Request { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Subscription identifier
|
||||||
|
/// </summary>
|
||||||
public string Identifier { get; set; }
|
public string Identifier { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Is user subscription or generic
|
||||||
|
/// </summary>
|
||||||
public bool UserSubscription { get; set; }
|
public bool UserSubscription { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If the subscription has been confirmed
|
||||||
|
/// </summary>
|
||||||
public bool Confirmed { get; set; }
|
public bool Confirmed { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identifier"></param>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="userSubscription"></param>
|
||||||
|
/// <param name="dataHandler"></param>
|
||||||
public SocketSubscription(string identifier, object request, bool userSubscription, Action<SocketConnection, JToken> dataHandler)
|
public SocketSubscription(string identifier, object request, bool userSubscription, Action<SocketConnection, JToken> dataHandler)
|
||||||
{
|
{
|
||||||
UserSubscription = userSubscription;
|
UserSubscription = userSubscription;
|
||||||
@ -27,6 +52,10 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
Request = request;
|
Request = request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoke the exception event
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e"></param>
|
||||||
public void InvokeExceptionHandler(Exception e)
|
public void InvokeExceptionHandler(Exception e)
|
||||||
{
|
{
|
||||||
Exception?.Invoke(e);
|
Exception?.Invoke(e);
|
||||||
|
@ -3,6 +3,9 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Sockets
|
namespace CryptoExchange.Net.Sockets
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Subscription
|
||||||
|
/// </summary>
|
||||||
public class UpdateSubscription
|
public class UpdateSubscription
|
||||||
{
|
{
|
||||||
private readonly SocketConnection connection;
|
private readonly SocketConnection connection;
|
||||||
@ -40,6 +43,11 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int Id => connection.Socket.Id;
|
public int Id => connection.Socket.Id;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connection"></param>
|
||||||
|
/// <param name="subscription"></param>
|
||||||
public UpdateSubscription(SocketConnection connection, SocketSubscription subscription)
|
public UpdateSubscription(SocketConnection connection, SocketSubscription subscription)
|
||||||
{
|
{
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
|
@ -4,13 +4,18 @@ using CryptoExchange.Net.Logging;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Sockets
|
namespace CryptoExchange.Net.Sockets
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Factory implementation
|
||||||
|
/// </summary>
|
||||||
public class WebsocketFactory : IWebsocketFactory
|
public class WebsocketFactory : IWebsocketFactory
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public IWebsocket CreateWebsocket(Log log, string url)
|
public IWebsocket CreateWebsocket(Log log, string url)
|
||||||
{
|
{
|
||||||
return new BaseSocket(log, url);
|
return new BaseSocket(log, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public IWebsocket CreateWebsocket(Log log, string url, IDictionary<string, string> cookies, IDictionary<string, string> headers)
|
public IWebsocket CreateWebsocket(Log log, string url, IDictionary<string, string> cookies, IDictionary<string, string> headers)
|
||||||
{
|
{
|
||||||
return new BaseSocket(log, url, cookies, headers);
|
return new BaseSocket(log, url, cookies, headers);
|
||||||
|
@ -190,6 +190,9 @@ The order book will automatically reconnect when the connection is lost and resy
|
|||||||
To stop synchronizing an order book use the `Stop` method.
|
To stop synchronizing an order book use the `Stop` method.
|
||||||
|
|
||||||
## Release notes
|
## Release notes
|
||||||
|
* Version 2.1.6 - 06 Aug 2019
|
||||||
|
* Fix for missing subscription events if they are also a request response, added code docs
|
||||||
|
|
||||||
* Version 2.1.5 - 09 jul 2019
|
* Version 2.1.5 - 09 jul 2019
|
||||||
* Updated SymbolOrderBook
|
* Updated SymbolOrderBook
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user