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