diff --git a/CryptoExchange.Net/Attributes/JsonOptionalPropertyAttribute.cs b/CryptoExchange.Net/Attributes/JsonOptionalPropertyAttribute.cs index 9c33a6a..c50ff7b 100644 --- a/CryptoExchange.Net/Attributes/JsonOptionalPropertyAttribute.cs +++ b/CryptoExchange.Net/Attributes/JsonOptionalPropertyAttribute.cs @@ -2,6 +2,9 @@ namespace CryptoExchange.Net.Attributes { + /// + /// Marks property as optional + /// public class JsonOptionalPropertyAttribute : Attribute { } diff --git a/CryptoExchange.Net/Authentication/ApiCredentials.cs b/CryptoExchange.Net/Authentication/ApiCredentials.cs index f2b71d8..0cadf6f 100644 --- a/CryptoExchange.Net/Authentication/ApiCredentials.cs +++ b/CryptoExchange.Net/Authentication/ApiCredentials.cs @@ -6,6 +6,9 @@ using Newtonsoft.Json.Linq; namespace CryptoExchange.Net.Authentication { + /// + /// Api credentials info + /// public class ApiCredentials: IDisposable { /// @@ -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; } + /// + /// Dispose + /// public void Dispose() { Key?.Dispose(); diff --git a/CryptoExchange.Net/Authentication/AuthenticationProvider.cs b/CryptoExchange.Net/Authentication/AuthenticationProvider.cs index deb4e9b..d2a41a0 100644 --- a/CryptoExchange.Net/Authentication/AuthenticationProvider.cs +++ b/CryptoExchange.Net/Authentication/AuthenticationProvider.cs @@ -2,35 +2,76 @@ namespace CryptoExchange.Net.Authentication { + /// + /// Base class for authentication providers + /// public abstract class AuthenticationProvider { + /// + /// The provided credentials + /// public ApiCredentials Credentials { get; } + /// + /// ctor + /// + /// protected AuthenticationProvider(ApiCredentials credentials) { Credentials = credentials; } + /// + /// Add authentication to the parameter list + /// + /// + /// + /// + /// + /// public virtual Dictionary AddAuthenticationToParameters(string uri, string method, Dictionary parameters, bool signed) { return parameters; } + /// + /// Add authentication to the header dictionary + /// + /// + /// + /// + /// + /// public virtual Dictionary AddAuthenticationToHeaders(string uri, string method, Dictionary parameters, bool signed) { return new Dictionary(); } + /// + /// Sign a string + /// + /// + /// public virtual string Sign(string toSign) { return toSign; } + /// + /// Sign a byte array + /// + /// + /// public virtual byte[] Sign(byte[] toSign) { return toSign; } + /// + /// Convert byte array to hex + /// + /// + /// protected string ByteToString(byte[] buff) { var result = ""; diff --git a/CryptoExchange.Net/Authentication/PrivateKey.cs b/CryptoExchange.Net/Authentication/PrivateKey.cs index 62c20b1..47c4755 100644 --- a/CryptoExchange.Net/Authentication/PrivateKey.cs +++ b/CryptoExchange.Net/Authentication/PrivateKey.cs @@ -3,6 +3,9 @@ using System.Security; namespace CryptoExchange.Net.Authentication { + /// + /// Private key info + /// public class PrivateKey : IDisposable { /// @@ -87,6 +90,9 @@ namespace CryptoExchange.Net.Authentication IsEncrypted = false; } + /// + /// Dispose + /// public void Dispose() { Key?.Dispose(); diff --git a/CryptoExchange.Net/BaseClient.cs b/CryptoExchange.Net/BaseClient.cs index 7adf435..6d48158 100644 --- a/CryptoExchange.Net/BaseClient.cs +++ b/CryptoExchange.Net/BaseClient.cs @@ -12,14 +12,35 @@ using System.Reflection; namespace CryptoExchange.Net { + /// + /// The base for all clients + /// public abstract class BaseClient: IDisposable { + /// + /// The address of the client + /// public string BaseAddress { get; private set; } + /// + /// The log object + /// protected internal Log log; + /// + /// The api proxy + /// protected ApiProxy apiProxy; + /// + /// The auth provider + /// protected internal AuthenticationProvider authProvider; + /// + /// The last used id + /// protected static int lastId; + /// + /// Lock for id generating + /// 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 }); + /// + /// Last is used + /// public static int LastId => lastId; + /// + /// ctor + /// + /// + /// protected BaseClient(ClientOptions options, AuthenticationProvider authenticationProvider) { log = new Log(); @@ -40,7 +69,7 @@ namespace CryptoExchange.Net /// /// Configure the client using the provided options /// - /// Options + /// Options protected void Configure(ClientOptions clientOptions) { log.UpdateWriters(clientOptions.LogWriters); @@ -306,6 +335,9 @@ namespace CryptoExchange.Net return path; } + /// + /// Dispose + /// public virtual void Dispose() { authProvider?.Credentials?.Dispose(); diff --git a/CryptoExchange.Net/Converters/ArrayConverter.cs b/CryptoExchange.Net/Converters/ArrayConverter.cs index dd91b57..62cebc6 100644 --- a/CryptoExchange.Net/Converters/ArrayConverter.cs +++ b/CryptoExchange.Net/Converters/ArrayConverter.cs @@ -8,13 +8,18 @@ using Newtonsoft.Json.Linq; namespace CryptoExchange.Net.Converters { + /// + /// Converter for arrays to properties + /// public class ArrayConverter : JsonConverter { + /// public override bool CanConvert(Type objectType) { return true; } - + + /// 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; } + /// public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { writer.WriteStartArray(); @@ -143,10 +149,20 @@ namespace CryptoExchange.Net.Converters } } + /// + /// Mark property as an index in the array + /// public class ArrayPropertyAttribute: Attribute { + /// + /// The index in the array + /// public int Index { get; } + /// + /// ctor + /// + /// public ArrayPropertyAttribute(int index) { Index = index; diff --git a/CryptoExchange.Net/Converters/BaseConverter.cs b/CryptoExchange.Net/Converters/BaseConverter.cs index 9e47948..47ce489 100644 --- a/CryptoExchange.Net/Converters/BaseConverter.cs +++ b/CryptoExchange.Net/Converters/BaseConverter.cs @@ -6,16 +6,28 @@ using Newtonsoft.Json; namespace CryptoExchange.Net.Converters { + /// + /// Base class for enum converters + /// + /// Type of enum to convert public abstract class BaseConverter: JsonConverter { + /// + /// The enum->string mapping + /// protected abstract List> Mapping { get; } private readonly bool quotes; + /// + /// ctor + /// + /// protected BaseConverter(bool useQuotes) { quotes = useQuotes; } + /// 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); } + /// 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; } + /// + /// Convert a string value + /// + /// + /// public T ReadString(string data) { return Mapping.FirstOrDefault(v => v.Value == data).Key; } + /// public override bool CanConvert(Type objectType) { // Check if it is type, or nullable of type diff --git a/CryptoExchange.Net/Converters/TimestampConverter.cs b/CryptoExchange.Net/Converters/TimestampConverter.cs index aefed15..6d08a0d 100644 --- a/CryptoExchange.Net/Converters/TimestampConverter.cs +++ b/CryptoExchange.Net/Converters/TimestampConverter.cs @@ -3,13 +3,18 @@ using Newtonsoft.Json; namespace CryptoExchange.Net.Converters { + /// + /// converter for milliseconds to datetime + /// public class TimestampConverter : JsonConverter { + /// public override bool CanConvert(Type objectType) { return objectType == typeof(DateTime); } + /// 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); } + /// public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { writer.WriteValue((long)Math.Round(((DateTime)value - new DateTime(1970, 1, 1)).TotalMilliseconds)); diff --git a/CryptoExchange.Net/Converters/TimestampNanoSecondsConverter.cs b/CryptoExchange.Net/Converters/TimestampNanoSecondsConverter.cs index a617131..17a6803 100644 --- a/CryptoExchange.Net/Converters/TimestampNanoSecondsConverter.cs +++ b/CryptoExchange.Net/Converters/TimestampNanoSecondsConverter.cs @@ -3,13 +3,18 @@ using Newtonsoft.Json; namespace CryptoExchange.Net.Converters { + /// + /// Converter for nanoseconds to datetime + /// public class TimestampNanoSecondsConverter : JsonConverter { + /// public override bool CanConvert(Type objectType) { return objectType == typeof(DateTime); } + /// 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)); } + /// public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var ticksPerNanosecond = (TimeSpan.TicksPerMillisecond / 1000m / 1000); diff --git a/CryptoExchange.Net/Converters/TimestampSecondsConverter.cs b/CryptoExchange.Net/Converters/TimestampSecondsConverter.cs index 21f660c..c1dff7e 100644 --- a/CryptoExchange.Net/Converters/TimestampSecondsConverter.cs +++ b/CryptoExchange.Net/Converters/TimestampSecondsConverter.cs @@ -4,13 +4,18 @@ using Newtonsoft.Json; namespace CryptoExchange.Net.Converters { + /// + /// Converter for seconds to datetime + /// public class TimestampSecondsConverter : JsonConverter { + /// public override bool CanConvert(Type objectType) { return objectType == typeof(DateTime); } + /// 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); } + /// public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { writer.WriteValue((long)Math.Round(((DateTime)value - new DateTime(1970, 1, 1)).TotalSeconds)); diff --git a/CryptoExchange.Net/Converters/UTCDateTimeConverter.cs b/CryptoExchange.Net/Converters/UTCDateTimeConverter.cs index 270f588..5210ccf 100644 --- a/CryptoExchange.Net/Converters/UTCDateTimeConverter.cs +++ b/CryptoExchange.Net/Converters/UTCDateTimeConverter.cs @@ -3,13 +3,18 @@ using Newtonsoft.Json; namespace CryptoExchange.Net.Converters { + /// + /// Converter for utc datetime + /// public class UTCDateTimeConverter: JsonConverter { + /// public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { writer.WriteValue(JsonConvert.SerializeObject(value)); } + /// 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); } + /// public override bool CanConvert(Type objectType) { return objectType == typeof(DateTime) || objectType == typeof(DateTime?); diff --git a/CryptoExchange.Net/CryptoExchange.Net.csproj b/CryptoExchange.Net/CryptoExchange.Net.csproj index 2709772..ecc7a86 100644 --- a/CryptoExchange.Net/CryptoExchange.Net.csproj +++ b/CryptoExchange.Net/CryptoExchange.Net.csproj @@ -1,27 +1,23 @@ - netstandard2.0 - CryptoExchange.Net - JKorf - 2.1.5 - false + JKorf + 2.1.6 + false https://github.com/JKorf/CryptoExchange.Net - https://github.com/JKorf/CryptoExchange.Net/blob/master/LICENSE - en - true + https://github.com/JKorf/CryptoExchange.Net/blob/master/LICENSE + en + true + 2.1.6 - Fix for missing subscription events if they are also a request response, added code docs - CryptoExchange.Net.xml - - - + \ No newline at end of file diff --git a/CryptoExchange.Net/CryptoExchange.Net.xml b/CryptoExchange.Net/CryptoExchange.Net.xml index c9b64ab..df25990 100644 --- a/CryptoExchange.Net/CryptoExchange.Net.xml +++ b/CryptoExchange.Net/CryptoExchange.Net.xml @@ -4,6 +4,16 @@ CryptoExchange.Net + + + Marks property as optional + + + + + Api credentials info + + The api key to authenticate requests @@ -47,6 +57,73 @@ A key to identify the credentials for the API. For example, when set to `binanceKey` the json data should contain a value for the property `binanceKey`. Defaults to 'apiKey'. A key to identify the credentials for the API. For example, when set to `binanceSecret` the json data should contain a value for the property `binanceSecret`. Defaults to 'apiSecret'. + + + Dispose + + + + + Base class for authentication providers + + + + + The provided credentials + + + + + ctor + + + + + + Add authentication to the parameter list + + + + + + + + + + Add authentication to the header dictionary + + + + + + + + + + Sign a string + + + + + + + Sign a byte array + + + + + + + Convert byte array to hex + + + + + + + Private key info + + The private key @@ -88,11 +165,63 @@ The private key used for signing + + + Dispose + + + + + The base for all clients + + + + + The address of the client + + + + + The log object + + + + + The api proxy + + + + + The auth provider + + + + + The last used id + + + + + Lock for id generating + + + + + Last is used + + + + + ctor + + + + Configure the client using the provided options - Options + Options @@ -141,6 +270,167 @@ The values to fill + + + Dispose + + + + + Converter for arrays to properties + + + + + + + + + + + + + + Mark property as an index in the array + + + + + The index in the array + + + + + ctor + + + + + + Base class for enum converters + + Type of enum to convert + + + + The enum->string mapping + + + + + ctor + + + + + + + + + + + + Convert a string value + + + + + + + + + + converter for milliseconds to datetime + + + + + + + + + + + + + + Converter for nanoseconds to datetime + + + + + + + + + + + + + + Converter for seconds to datetime + + + + + + + + + + + + + + Converter for utc datetime + + + + + + + + + + + + + + Helper methods + + + + + Add a parameter + + + + + + + + Add a parameter + + + + + + + + Add an optional parameter. Not added if value is null + + + + + + + + Add an optional parameter. Not added if value is null + + + + + Create a query string of the specified parameters @@ -156,6 +446,157 @@ The source secure string + + + Header collection to inenumerable + + + + + + + Wait one async + + + + + + + + + Wait one async + + + + + + + + String to JToken + + + + + + + + Rate limiter interface + + + + + Limit the request if needed + + + + + + + + + Request interface + + + + + The uri of the request + + + + + The headers of the request + + + + + The method of the request + + + + + The timeout of the request + + + + + Set a proxy + + + + + + + + + Content type + + + + + String content + + + + + Accept + + + + + Content length + + + + + Get the request stream + + + + + + Get the response object + + + + + + Request factory interface + + + + + Create a request for an uri + + + + + + + Response object interface + + + + + The response status code + + + + + Get the response stream + + + + + + Get the response headers + + + + + + Close the response + + Base class for rest API implementations @@ -247,6 +688,246 @@ + + + Interface for websocket interaction + + + + + Websocket closed + + + + + Websocket message received + + + + + Websocket error + + + + + Websocket opened + + + + + Id + + + + + Origin + + + + + Reconnecting + + + + + Handler for byte data + + + + + Handler for string data + + + + + Socket url + + + + + State + + + + + Is closed + + + + + Is open + + + + + Should ping connecting + + + + + Interval of pinging + + + + + Supported ssl protocols + + + + + Timeout + + + + + Connect the socket + + + + + + Send data + + + + + + Reset socket + + + + + Close the connecting + + + + + + Set proxy + + + + + + + Websocket factory interface + + + + + Create a websocket for an url + + + + + + + + Create a websocket for an url + + + + + + + + + + Default log writer, writes to debug + + + + + + + + + + + Log implementation + + + + + The verbosity of the logging + + + + + ctor + + + + + Set the writers + + + + + + Write a log entry + + + + + + + The log verbosity + + + + + Debug logging + + + + + Info logging + + + + + Warning logging + + + + + Error logging + + + + + None, used for disabling logging + + + + + File writer + + + + + + + + ctor + + + + + + + + + Dispose + + + + + + Proxy info + + The host address of the proxy @@ -284,6 +965,25 @@ The proxy login The proxy password + + + Comparer for byte order + + + + + Compare function + + + + + + + + The result of an operation + + + The data returned by the call @@ -299,11 +999,179 @@ Whether the call was successful + + + ctor + + + + + + + The result of a request + + + The status code of the response. Note that a OK status does not always indicate success, check the Success parameter for this. + + + The response headers + + + + + ctor + + + + + + + + + Create an error result + + + + + + + Create an error result + + + + + + + + + Constants + + + + + GET Http method + + + + + POST Http method + + + + + DELETE Http method + + + + + PUT Http method + + + + + Json content type header + + + + + Form content type header + + + + + What to do when a request would exceed the rate limit + + + + + Fail the request + + + + + Wait till the request can be send + + + + + Where the post parameters should be added + + + + + Post parameters in body + + + + + Post parameters in url + + + + + The format of the request body + + + + + Form data + + + + + Json + + + + + Status of the order book + + + + + Not connected + + + + + Connecting + + + + + Syncing data + + + + + Data synced, order book is up to date + + + + + Order book entry type + + + + + Ask + + + + + Bid + + + + + Base class for errors + + The error code @@ -314,6 +1182,112 @@ The message for the error that occured + + + ctor + + + + + + + String representation + + + + + + Cant reach server error + + + + + ctor + + + + + No api credentials provided while trying to access private endpoint + + + + + ctor + + + + + Error returned by the server + + + + + ctor + + + + + + ctor + + + + + + + Web error returned by the server + + + + + ctor + + + + + + Error while deserializing data + + + + + ctor + + + + + + Unknown error + + + + + ctor + + + + + + An invalid parameter has been provided + + + + + ctor + + + + + + Rate limit exceeded + + + + + ctor + + + Base options @@ -390,6 +1364,13 @@ The time the server has to respond to a request before timing out + + + Create a copy of the options + + + + Base for socket client options @@ -421,6 +1402,18 @@ Setting this to a higher number increases subscription speed, but having more subscriptions on a single connection will also increase the amount of traffic on that single connection. + + + Create a copy of the options + + + + + + + Interface for order book entries + + The quantity of the entry @@ -431,11 +1424,100 @@ The price of the entry + + + Order book entry + + + + + Quantity of the entry + + + + + Price of the entry + + + + + ctor + + + + + + + Buffer entry for order book + + + + + The first sequence number of the entries + + + + + The last sequence number of the entries + + + + + List of entries + + + + + ctor + + + + + Process entry for order book + + + + + The entry + + + + + The type + + + + + ctor + + + + Base for order book implementations + + + The process buffer, used while syncing + + + + + The ask list + + + + + The bid list + + + + + The log + + The status of the order book. Order book is up to date when the status is `Synced` @@ -486,6 +1568,13 @@ The best ask currently in the order book + + + ctor + + + + Start connecting and synchronizing the order book @@ -510,6 +1599,68 @@ + + + Start the order book + + + + + + Reset the order book + + + + + Resync the order book + + + + + + Set the initial data for the order book + + The last update sequence number + List of asks + List of bids + + + + Update the order book with entries + + First sequence number + Last sequence number + List of entries + + + + Check and empty the process buffer; see what entries to update the book with + + + + + Update order book with an entry + + Type of entry + The entry + + + + Dispose the order book + + + + + String representation of the top 3 entries + + + + + + String representation of the top x entries + + + Limits the amount of requests per time period to a certain limit, counts the request per API key. @@ -522,6 +1673,9 @@ The amount to limit to The time period over which the limit counts + + + Limits the amount of requests per time period to a certain limit, counts the request per endpoint. @@ -534,6 +1688,9 @@ The amount to limit to The time period over which the limit counts + + + Limits the amount of requests per time period to a certain limit, counts the total amount of requests. @@ -546,11 +1703,161 @@ The amount to limit to The time period over which the limit counts + + + + + + Rate limiting object + + + + + Lock + + + + + ctor + + + + + Get time to wait for a specific time + + + + + + + + + Add an executed request time + + + + + + Request object + + + + + Create request object for webrequest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WebRequest factory + + + + + + + + HttpWebResponse response object + + + + + + + + Create response for http web response + + + + + + + + + + + + + + + Base rest client + + The factory for creating requests. Used for unit testing + + + Where to place post parameters + + + + + Request body content type + + + + + Timeout for requests + + + + + Rate limiting behaviour + + + + + List of ratelimitters + + + + + Total requests made + + + + + ctor + + + + Configure the client using the provided options @@ -637,6 +1944,11 @@ The string the request returned + + + Base for socket client implementations + + The factory for creating sockets. Used for unit testing @@ -647,6 +1959,10 @@ List of socket connections currently connecting/connected + + + + @@ -667,6 +1983,49 @@ + + + Handler for byte data + + + + + Handler for string data + + + + + Generic handlers + + + + + Periodic task + + + + + Periodic task event + + + + + Is disposing + + + + + 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 + + + + + Create a socket client + + Client options + Authentication provider + Configure the client using the provided options @@ -712,11 +2071,21 @@ The subscription the request is for + + + Query for data + + Exepected result type + The request to send + Whether the socket should be authenticated + + Query for data The expected result type + The url for the request The request to send Whether the socket should be authenticated @@ -756,7 +2125,7 @@ The socket connection The request that a response is awaited for - The message + The message The interpretation (null if message wasn't a response to the request) True if the message was a response to the subscription request @@ -864,6 +2233,102 @@ Dispose the client + + + Socket implementation + + + + + Socket connecting + + + + + Connection lost event + + + + + Connecting restored event + + + + + Connecting closed event + + + + + The amount of handlers + + + + + If connection is authenticated + + + + + If connection is made + + + + + The socket + + + + + If should reconnect upon closing + + + + + Time of disconnecting + + + + + If activity is paused + + + + + New socket connection + + The socket client + The socket + + + + Add a handler + + The request object + If it is a user subscription or a generic handler + The data handler + + + + + Add a handler + + The identifier of the handler + If it is a user subscription or a generic handler + The data handler + + + + + + Send data + + The data type + The object to send + The timeout for response + The response handler + + Send data to the websocket @@ -883,11 +2348,74 @@ Handler for a socket closing. Reconnects the socket if needed, or removes it from the active socket list if not + + + Close the connection + + + + + + Close the subscriptions + + Subscription to close + + + + + Socket subscription + + + + + Exception event + + Message handlers for this subscription. Should return true if the message is handled and should not be distributed to the other handlers + + + Request object + + + + + Subscription identifier + + + + + Is user subscription or generic + + + + + If the subscription has been confirmed + + + + + ctor + + + + + + + + + Invoke the exception event + + + + + + Subscription + + Event when the connection is lost. The socket will automatically reconnect when possible. @@ -908,6 +2436,13 @@ The id of the socket + + + ctor + + + + Close the subscription @@ -920,5 +2455,16 @@ + + + Factory implementation + + + + + + + + diff --git a/CryptoExchange.Net/ExtensionMethods.cs b/CryptoExchange.Net/ExtensionMethods.cs index b682107..f2485a8 100644 --- a/CryptoExchange.Net/ExtensionMethods.cs +++ b/CryptoExchange.Net/ExtensionMethods.cs @@ -13,24 +13,51 @@ using Newtonsoft.Json.Linq; namespace CryptoExchange.Net { + /// + /// Helper methods + /// public static class ExtensionMethods { + /// + /// Add a parameter + /// + /// + /// + /// public static void AddParameter(this Dictionary parameters, string key, string value) { parameters.Add(key, value); } + /// + /// Add a parameter + /// + /// + /// + /// public static void AddParameter(this Dictionary parameters, string key, object value) { parameters.Add(key, value); } + /// + /// Add an optional parameter. Not added if value is null + /// + /// + /// + /// public static void AddOptionalParameter(this Dictionary parameters, string key, object value) { if(value != null) parameters.Add(key, value); } + /// + /// Add an optional parameter. Not added if value is null + /// + /// + /// + /// public static void AddOptionalParameter(this Dictionary parameters, string key, string value) { if (value != null) @@ -90,6 +117,11 @@ namespace CryptoExchange.Net } } + /// + /// Header collection to inenumerable + /// + /// + /// public static IEnumerable> ToIEnumerable(this WebHeaderCollection headers) { if (headers == null) @@ -102,6 +134,13 @@ namespace CryptoExchange.Net ); } + /// + /// Wait one async + /// + /// + /// + /// + /// public static async Task WaitOneAsync(this WaitHandle handle, int millisecondsTimeout, CancellationToken cancellationToken) { RegisteredWaitHandle registeredHandle = null; @@ -126,12 +165,24 @@ namespace CryptoExchange.Net tokenRegistration.Dispose(); } } - + + /// + /// Wait one async + /// + /// + /// + /// public static Task WaitOneAsync(this WaitHandle handle, TimeSpan timeout) { return handle.WaitOneAsync((int)timeout.TotalMilliseconds, CancellationToken.None); } + /// + /// String to JToken + /// + /// + /// + /// public static JToken ToJToken(this string stringData, Log log = null) { if (string.IsNullOrEmpty(stringData)) diff --git a/CryptoExchange.Net/Interfaces/IRateLimiter.cs b/CryptoExchange.Net/Interfaces/IRateLimiter.cs index 5220f46..735f3ec 100644 --- a/CryptoExchange.Net/Interfaces/IRateLimiter.cs +++ b/CryptoExchange.Net/Interfaces/IRateLimiter.cs @@ -2,8 +2,18 @@ namespace CryptoExchange.Net.Interfaces { + /// + /// Rate limiter interface + /// public interface IRateLimiter { + /// + /// Limit the request if needed + /// + /// + /// + /// + /// CallResult LimitRequest(RestClient client, string url, RateLimitingBehaviour limitBehaviour); } } diff --git a/CryptoExchange.Net/Interfaces/IRequest.cs b/CryptoExchange.Net/Interfaces/IRequest.cs index 457a42d..6c42ca5 100644 --- a/CryptoExchange.Net/Interfaces/IRequest.cs +++ b/CryptoExchange.Net/Interfaces/IRequest.cs @@ -5,20 +5,62 @@ using System.Threading.Tasks; namespace CryptoExchange.Net.Interfaces { + /// + /// Request interface + /// public interface IRequest { + /// + /// The uri of the request + /// Uri Uri { get; } + /// + /// The headers of the request + /// WebHeaderCollection Headers { get; set; } + /// + /// The method of the request + /// string Method { get; set; } + /// + /// The timeout of the request + /// TimeSpan Timeout { get; set; } + /// + /// Set a proxy + /// + /// + /// + /// + /// void SetProxy(string host, int port, string login, string password); + /// + /// Content type + /// string ContentType { get; set; } + /// + /// String content + /// string Content { get; set; } + /// + /// Accept + /// string Accept { get; set; } + /// + /// Content length + /// long ContentLength { get; set; } + /// + /// Get the request stream + /// + /// Task GetRequestStream(); + /// + /// Get the response object + /// + /// Task GetResponse(); } } diff --git a/CryptoExchange.Net/Interfaces/IRequestFactory.cs b/CryptoExchange.Net/Interfaces/IRequestFactory.cs index 5acdb4b..193db1d 100644 --- a/CryptoExchange.Net/Interfaces/IRequestFactory.cs +++ b/CryptoExchange.Net/Interfaces/IRequestFactory.cs @@ -1,7 +1,15 @@ namespace CryptoExchange.Net.Interfaces { + /// + /// Request factory interface + /// public interface IRequestFactory { + /// + /// Create a request for an uri + /// + /// + /// IRequest Create(string uri); } } diff --git a/CryptoExchange.Net/Interfaces/IResponse.cs b/CryptoExchange.Net/Interfaces/IResponse.cs index 58430ed..cc32604 100644 --- a/CryptoExchange.Net/Interfaces/IResponse.cs +++ b/CryptoExchange.Net/Interfaces/IResponse.cs @@ -5,11 +5,28 @@ using System.Net; namespace CryptoExchange.Net.Interfaces { + /// + /// Response object interface + /// public interface IResponse { + /// + /// The response status code + /// HttpStatusCode StatusCode { get; } + /// + /// Get the response stream + /// + /// Stream GetResponseStream(); + /// + /// Get the response headers + /// + /// IEnumerable> GetResponseHeaders(); + /// + /// Close the response + /// void Close(); } } diff --git a/CryptoExchange.Net/Interfaces/IWebsocket.cs b/CryptoExchange.Net/Interfaces/IWebsocket.cs index ec8db64..6a97da1 100644 --- a/CryptoExchange.Net/Interfaces/IWebsocket.cs +++ b/CryptoExchange.Net/Interfaces/IWebsocket.cs @@ -5,30 +5,104 @@ using WebSocket4Net; namespace CryptoExchange.Net.Interfaces { + /// + /// Interface for websocket interaction + /// public interface IWebsocket: IDisposable { + /// + /// Websocket closed + /// event Action OnClose; + /// + /// Websocket message received + /// event Action OnMessage; + /// + /// Websocket error + /// event Action OnError; + /// + /// Websocket opened + /// event Action OnOpen; + /// + /// Id + /// int Id { get; } + /// + /// Origin + /// string Origin { get; set; } + /// + /// Reconnecting + /// bool Reconnecting { get; set; } + /// + /// Handler for byte data + /// Func DataInterpreterBytes { get; set; } + /// + /// Handler for string data + /// Func DataInterpreterString { get; set; } + /// + /// Socket url + /// string Url { get; } + /// + /// State + /// WebSocketState SocketState { get; } + /// + /// Is closed + /// bool IsClosed { get; } + /// + /// Is open + /// bool IsOpen { get; } + /// + /// Should ping connecting + /// bool PingConnection { get; set; } + /// + /// Interval of pinging + /// TimeSpan PingInterval { get; set; } + /// + /// Supported ssl protocols + /// SslProtocols SSLProtocols { get; set; } + /// + /// Timeout + /// TimeSpan Timeout { get; set; } + /// + /// Connect the socket + /// + /// Task Connect(); + /// + /// Send data + /// + /// void Send(string data); + /// + /// Reset socket + /// void Reset(); + /// + /// Close the connecting + /// + /// Task Close(); + /// + /// Set proxy + /// + /// + /// void SetProxy(string host, int port); } } diff --git a/CryptoExchange.Net/Interfaces/IWebsocketFactory.cs b/CryptoExchange.Net/Interfaces/IWebsocketFactory.cs index e3a45c7..1b0d74f 100644 --- a/CryptoExchange.Net/Interfaces/IWebsocketFactory.cs +++ b/CryptoExchange.Net/Interfaces/IWebsocketFactory.cs @@ -3,9 +3,26 @@ using CryptoExchange.Net.Logging; namespace CryptoExchange.Net.Interfaces { + /// + /// Websocket factory interface + /// public interface IWebsocketFactory { + /// + /// Create a websocket for an url + /// + /// + /// + /// IWebsocket CreateWebsocket(Log log, string url); + /// + /// Create a websocket for an url + /// + /// + /// + /// + /// + /// IWebsocket CreateWebsocket(Log log, string url, IDictionary cookies, IDictionary headers); } } diff --git a/CryptoExchange.Net/Logging/DebugTextWriter.cs b/CryptoExchange.Net/Logging/DebugTextWriter.cs index 9cea25b..3e0bc14 100644 --- a/CryptoExchange.Net/Logging/DebugTextWriter.cs +++ b/CryptoExchange.Net/Logging/DebugTextWriter.cs @@ -4,10 +4,15 @@ using System.Text; namespace CryptoExchange.Net.Logging { + /// + /// Default log writer, writes to debug + /// public class DebugTextWriter: TextWriter { + /// public override Encoding Encoding => Encoding.ASCII; + /// public override void WriteLine(string value) { Debug.WriteLine(value); diff --git a/CryptoExchange.Net/Logging/Log.cs b/CryptoExchange.Net/Logging/Log.cs index e090c30..9338280 100644 --- a/CryptoExchange.Net/Logging/Log.cs +++ b/CryptoExchange.Net/Logging/Log.cs @@ -6,22 +6,39 @@ using System.Linq; namespace CryptoExchange.Net.Logging { + /// + /// Log implementation + /// public class Log { private List writers; - + /// + /// The verbosity of the logging + /// public LogVerbosity Level { get; set; } = LogVerbosity.Info; + /// + /// ctor + /// public Log() { writers = new List(); } + /// + /// Set the writers + /// + /// public void UpdateWriters(List textWriters) { writers = textWriters; } + /// + /// Write a log entry + /// + /// + /// public void Write(LogVerbosity logType, string message) { if ((int)logType < (int)Level) @@ -42,12 +59,30 @@ namespace CryptoExchange.Net.Logging } } + /// + /// The log verbosity + /// public enum LogVerbosity { + /// + /// Debug logging + /// Debug, + /// + /// Info logging + /// Info, + /// + /// Warning logging + /// Warning, + /// + /// Error logging + /// Error, + /// + /// None, used for disabling logging + /// None } } diff --git a/CryptoExchange.Net/Logging/ThreadSafeFileWriter.cs b/CryptoExchange.Net/Logging/ThreadSafeFileWriter.cs index f5c03d3..5caac53 100644 --- a/CryptoExchange.Net/Logging/ThreadSafeFileWriter.cs +++ b/CryptoExchange.Net/Logging/ThreadSafeFileWriter.cs @@ -4,6 +4,9 @@ using System.Text; namespace CryptoExchange.Net.Logging { + /// + /// File writer + /// 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; + /// public override Encoding Encoding => Encoding.ASCII; + /// + /// ctor + /// + /// 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 } } + /// public override void WriteLine(string logMessage) { lock(writeLock) logWriter.WriteLine(logMessage); } + /// + /// Dispose + /// + /// protected override void Dispose(bool disposing) { lock (writeLock) diff --git a/CryptoExchange.Net/Objects/ApiProxy.cs b/CryptoExchange.Net/Objects/ApiProxy.cs index 970f5b6..b4db095 100644 --- a/CryptoExchange.Net/Objects/ApiProxy.cs +++ b/CryptoExchange.Net/Objects/ApiProxy.cs @@ -2,6 +2,9 @@ namespace CryptoExchange.Net.Objects { + /// + /// Proxy info + /// public class ApiProxy { /// diff --git a/CryptoExchange.Net/Objects/ByteOrderComparer.cs b/CryptoExchange.Net/Objects/ByteOrderComparer.cs index 25f026d..29be971 100644 --- a/CryptoExchange.Net/Objects/ByteOrderComparer.cs +++ b/CryptoExchange.Net/Objects/ByteOrderComparer.cs @@ -3,8 +3,17 @@ using System.Collections.Generic; namespace CryptoExchange.Net.Objects { + /// + /// Comparer for byte order + /// public class ByteOrderComparer : IComparer { + /// + /// Compare function + /// + /// + /// + /// public int Compare(byte[] x, byte[] y) { // Shortcuts: If both are null, they are the same. diff --git a/CryptoExchange.Net/Objects/CallResult.cs b/CryptoExchange.Net/Objects/CallResult.cs index fbfb5cb..d8c2f59 100644 --- a/CryptoExchange.Net/Objects/CallResult.cs +++ b/CryptoExchange.Net/Objects/CallResult.cs @@ -4,6 +4,10 @@ using System.Net; namespace CryptoExchange.Net.Objects { + /// + /// The result of an operation + /// + /// public class CallResult { /// @@ -19,6 +23,11 @@ namespace CryptoExchange.Net.Objects /// public bool Success => Error == null; + /// + /// ctor + /// + /// + /// public CallResult(T data, Error error) { Data = data; @@ -26,6 +35,10 @@ namespace CryptoExchange.Net.Objects } } + /// + /// The result of a request + /// + /// public class WebCallResult: CallResult { /// @@ -33,18 +46,41 @@ namespace CryptoExchange.Net.Objects /// public HttpStatusCode? ResponseStatusCode { get; set; } + /// + /// The response headers + /// public IEnumerable> ResponseHeaders { get; set; } + /// + /// ctor + /// + /// + /// + /// + /// public WebCallResult(HttpStatusCode? code, IEnumerable> responseHeaders, T data, Error error): base(data, error) { ResponseHeaders = responseHeaders; ResponseStatusCode = code; } + /// + /// Create an error result + /// + /// + /// public static WebCallResult CreateErrorResult(Error error) { return new WebCallResult(null, null, default(T), error); } + + /// + /// Create an error result + /// + /// + /// + /// + /// public static WebCallResult CreateErrorResult(HttpStatusCode? code, IEnumerable> responseHeaders, Error error) { return new WebCallResult(code, responseHeaders, default(T), error); diff --git a/CryptoExchange.Net/Objects/Constants.cs b/CryptoExchange.Net/Objects/Constants.cs index 95e3a7f..55112fa 100644 --- a/CryptoExchange.Net/Objects/Constants.cs +++ b/CryptoExchange.Net/Objects/Constants.cs @@ -1,13 +1,34 @@ namespace CryptoExchange.Net.Objects { + /// + /// Constants + /// public class Constants { + /// + /// GET Http method + /// public const string GetMethod = "GET"; + /// + /// POST Http method + /// public const string PostMethod = "POST"; + /// + /// DELETE Http method + /// public const string DeleteMethod = "DELETE"; + /// + /// PUT Http method + /// public const string PutMethod = "PUT"; + /// + /// Json content type header + /// public const string JsonContentHeader = "application/json"; + /// + /// Form content type header + /// public const string FormContentHeader = "application/x-www-form-urlencoded"; } } diff --git a/CryptoExchange.Net/Objects/Enums.cs b/CryptoExchange.Net/Objects/Enums.cs index 53cff06..fc6b630 100644 --- a/CryptoExchange.Net/Objects/Enums.cs +++ b/CryptoExchange.Net/Objects/Enums.cs @@ -1,34 +1,85 @@ namespace CryptoExchange.Net.Objects { + /// + /// What to do when a request would exceed the rate limit + /// public enum RateLimitingBehaviour { + /// + /// Fail the request + /// Fail, + /// + /// Wait till the request can be send + /// Wait } + /// + /// Where the post parameters should be added + /// public enum PostParameters { + /// + /// Post parameters in body + /// InBody, + /// + /// Post parameters in url + /// InUri } + /// + /// The format of the request body + /// public enum RequestBodyFormat { + /// + /// Form data + /// FormData, + /// + /// Json + /// Json } + /// + /// Status of the order book + /// public enum OrderBookStatus { + /// + /// Not connected + /// Disconnected, + /// + /// Connecting + /// Connecting, + /// + /// Syncing data + /// Syncing, + /// + /// Data synced, order book is up to date + /// Synced, } + /// + /// Order book entry type + /// public enum OrderBookEntryType { + /// + /// Ask + /// Ask, + /// + /// Bid + /// Bid } } diff --git a/CryptoExchange.Net/Objects/Error.cs b/CryptoExchange.Net/Objects/Error.cs index f692762..29240b1 100644 --- a/CryptoExchange.Net/Objects/Error.cs +++ b/CryptoExchange.Net/Objects/Error.cs @@ -1,5 +1,8 @@ namespace CryptoExchange.Net.Objects { + /// + /// Base class for errors + /// public abstract class Error { /// @@ -11,59 +14,127 @@ /// public string Message { get; set; } + /// + /// ctor + /// + /// + /// protected Error(int code, string message) { Code = code; Message = message; } + /// + /// String representation + /// + /// public override string ToString() { return $"{Code}: {Message}"; } } + /// + /// Cant reach server error + /// public class CantConnectError : Error { + /// + /// ctor + /// public CantConnectError() : base(1, "Can't connect to the server") { } } + /// + /// No api credentials provided while trying to access private endpoint + /// public class NoApiCredentialsError : Error { + /// + /// ctor + /// public NoApiCredentialsError() : base(2, "No credentials provided for private endpoint") { } } + /// + /// Error returned by the server + /// public class ServerError: Error { + /// + /// ctor + /// + /// public ServerError(string message) : base(3, "Server error: " + message) { } + /// + /// ctor + /// + /// + /// public ServerError(int code, string message) : base(code, message) { } } + /// + /// Web error returned by the server + /// public class WebError : Error { + /// + /// ctor + /// + /// public WebError(string message) : base(4, "Web error: " + message) { } } + /// + /// Error while deserializing data + /// public class DeserializeError : Error { + /// + /// ctor + /// + /// public DeserializeError(string message) : base(5, "Error deserializing data: " + message) { } } + /// + /// Unknown error + /// public class UnknownError : Error { + /// + /// ctor + /// + /// public UnknownError(string message) : base(6, "Unknown error occured " + message) { } } + /// + /// An invalid parameter has been provided + /// public class ArgumentError : Error { + /// + /// ctor + /// + /// public ArgumentError(string message) : base(7, "Invalid parameter: " + message) { } } + /// + /// Rate limit exceeded + /// public class RateLimitError: Error { + /// + /// ctor + /// + /// public RateLimitError(string message) : base(8, "Rate limit exceeded: " + message) { } } } diff --git a/CryptoExchange.Net/Objects/Options.cs b/CryptoExchange.Net/Objects/Options.cs index aae859d..525f1c8 100644 --- a/CryptoExchange.Net/Objects/Options.cs +++ b/CryptoExchange.Net/Objects/Options.cs @@ -91,6 +91,11 @@ namespace CryptoExchange.Net.Objects /// public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(30); + /// + /// Create a copy of the options + /// + /// + /// public T Copy() where T:RestClientOptions, new() { var copy = new T @@ -141,6 +146,11 @@ namespace CryptoExchange.Net.Objects /// public int? SocketSubscriptionsCombineTarget { get; set; } + /// + /// Create a copy of the options + /// + /// + /// public T Copy() where T : SocketClientOptions, new() { var copy = new T diff --git a/CryptoExchange.Net/OrderBook/ISymbolOrderBookEntry.cs b/CryptoExchange.Net/OrderBook/ISymbolOrderBookEntry.cs index 9354ad1..b5cb8ce 100644 --- a/CryptoExchange.Net/OrderBook/ISymbolOrderBookEntry.cs +++ b/CryptoExchange.Net/OrderBook/ISymbolOrderBookEntry.cs @@ -1,5 +1,8 @@ namespace CryptoExchange.Net.OrderBook { + /// + /// Interface for order book entries + /// public interface ISymbolOrderBookEntry { /// diff --git a/CryptoExchange.Net/OrderBook/OrderBookEntry.cs b/CryptoExchange.Net/OrderBook/OrderBookEntry.cs index 606dccd..3faccf2 100644 --- a/CryptoExchange.Net/OrderBook/OrderBookEntry.cs +++ b/CryptoExchange.Net/OrderBook/OrderBookEntry.cs @@ -1,10 +1,24 @@ namespace CryptoExchange.Net.OrderBook { + /// + /// Order book entry + /// public class OrderBookEntry : ISymbolOrderBookEntry { + /// + /// Quantity of the entry + /// public decimal Quantity { get; set; } + /// + /// Price of the entry + /// public decimal Price { get; set; } + /// + /// ctor + /// + /// + /// public OrderBookEntry(decimal price, decimal quantity) { Quantity = quantity; diff --git a/CryptoExchange.Net/OrderBook/ProcessBufferEntry.cs b/CryptoExchange.Net/OrderBook/ProcessBufferEntry.cs index e6bf3aa..8a48794 100644 --- a/CryptoExchange.Net/OrderBook/ProcessBufferEntry.cs +++ b/CryptoExchange.Net/OrderBook/ProcessBufferEntry.cs @@ -2,12 +2,27 @@ namespace CryptoExchange.Net.OrderBook { + /// + /// Buffer entry for order book + /// public class ProcessBufferEntry { + /// + /// The first sequence number of the entries + /// public long FirstSequence { get; set; } + /// + /// The last sequence number of the entries + /// public long LastSequence { get; set; } + /// + /// List of entries + /// public List Entries { get; set; } + /// + /// ctor + /// public ProcessBufferEntry() { Entries = new List(); diff --git a/CryptoExchange.Net/OrderBook/ProcessEntry.cs b/CryptoExchange.Net/OrderBook/ProcessEntry.cs index 4ddb93f..a59bc5f 100644 --- a/CryptoExchange.Net/OrderBook/ProcessEntry.cs +++ b/CryptoExchange.Net/OrderBook/ProcessEntry.cs @@ -2,11 +2,25 @@ namespace CryptoExchange.Net.OrderBook { + /// + /// Process entry for order book + /// public class ProcessEntry { + /// + /// The entry + /// public ISymbolOrderBookEntry Entry { get; set; } + /// + /// The type + /// public OrderBookEntryType Type { get; set; } + /// + /// ctor + /// + /// + /// public ProcessEntry(OrderBookEntryType type, ISymbolOrderBookEntry entry) { Type = type; diff --git a/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs b/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs index 9418129..e6c2e29 100644 --- a/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs +++ b/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs @@ -15,14 +15,26 @@ namespace CryptoExchange.Net.OrderBook /// public abstract class SymbolOrderBook: IDisposable { + /// + /// The process buffer, used while syncing + /// protected readonly List processBuffer; private readonly object bookLock = new object(); + /// + /// The ask list + /// protected SortedList asks; + /// + /// The bid list + /// protected SortedList bids; private OrderBookStatus status; private UpdateSubscription subscription; private readonly bool sequencesAreConsecutive; private readonly string id; + /// + /// The log + /// protected Log log; private bool bookSet; @@ -116,6 +128,11 @@ namespace CryptoExchange.Net.OrderBook } } + /// + /// ctor + /// + /// + /// protected SymbolOrderBook(string symbol, OrderBookOptions options) { id = options.OrderBookName; @@ -198,12 +215,29 @@ namespace CryptoExchange.Net.OrderBook await subscription.Close().ConfigureAwait(false); } + /// + /// Start the order book + /// + /// protected abstract Task> DoStart(); + /// + /// Reset the order book + /// protected virtual void DoReset() { } + /// + /// Resync the order book + /// + /// protected abstract Task> DoResync(); + /// + /// Set the initial data for the order book + /// + /// The last update sequence number + /// List of asks + /// List of bids protected void SetInitialOrderBook(long orderBookSequenceNumber, IEnumerable askList, IEnumerable bidList) { lock (bookLock) @@ -229,6 +263,12 @@ namespace CryptoExchange.Net.OrderBook } } + /// + /// Update the order book with entries + /// + /// First sequence number + /// Last sequence number + /// List of entries protected void UpdateOrderBook(long firstSequenceNumber, long lastSequenceNumber, List entries) { lock (bookLock) @@ -264,6 +304,9 @@ namespace CryptoExchange.Net.OrderBook } } + /// + /// Check and empty the process buffer; see what entries to update the book with + /// protected void CheckProcessBuffer() { foreach (var bufferEntry in processBuffer.OrderBy(b => b.FirstSequence).ToList()) @@ -284,6 +327,11 @@ namespace CryptoExchange.Net.OrderBook } } + /// + /// Update order book with an entry + /// + /// Type of entry + /// The entry protected virtual void ProcessUpdate(OrderBookEntryType type, ISymbolOrderBookEntry entry) { var listToChange = type == OrderBookEntryType.Ask ? asks : bids; @@ -311,13 +359,24 @@ namespace CryptoExchange.Net.OrderBook } } + /// + /// Dispose the order book + /// public abstract void Dispose(); + /// + /// String representation of the top 3 entries + /// + /// public override string ToString() { return ToString(3); } + /// + /// String representation of the top x entries + /// + /// public string ToString(int numberOfEntries) { var result = ""; diff --git a/CryptoExchange.Net/RateLimiter/RateLimitObject.cs b/CryptoExchange.Net/RateLimiter/RateLimitObject.cs index 1618252..34ed08c 100644 --- a/CryptoExchange.Net/RateLimiter/RateLimitObject.cs +++ b/CryptoExchange.Net/RateLimiter/RateLimitObject.cs @@ -4,17 +4,33 @@ using System.Linq; namespace CryptoExchange.Net.RateLimiter { + /// + /// Rate limiting object + /// public class RateLimitObject { + /// + /// Lock + /// public object LockObject { get; } private List Times { get; } + /// + /// ctor + /// public RateLimitObject() { LockObject = new object(); Times = new List(); } + /// + /// Get time to wait for a specific time + /// + /// + /// + /// + /// 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; } + /// + /// Add an executed request time + /// + /// public void Add(DateTime time) { Times.Add(time); diff --git a/CryptoExchange.Net/RateLimiter/RateLimiterAPIKey.cs b/CryptoExchange.Net/RateLimiter/RateLimiterAPIKey.cs index b686345..ce8d9e0 100644 --- a/CryptoExchange.Net/RateLimiter/RateLimiterAPIKey.cs +++ b/CryptoExchange.Net/RateLimiter/RateLimiterAPIKey.cs @@ -29,7 +29,7 @@ namespace CryptoExchange.Net.RateLimiter this.perTimePeriod = perTimePeriod; } - + /// public CallResult LimitRequest(RestClient client, string url, RateLimitingBehaviour limitBehaviour) { if(client.authProvider?.Credentials == null) diff --git a/CryptoExchange.Net/RateLimiter/RateLimiterPerEndpoint.cs b/CryptoExchange.Net/RateLimiter/RateLimiterPerEndpoint.cs index e21ea69..fb62900 100644 --- a/CryptoExchange.Net/RateLimiter/RateLimiterPerEndpoint.cs +++ b/CryptoExchange.Net/RateLimiter/RateLimiterPerEndpoint.cs @@ -29,6 +29,7 @@ namespace CryptoExchange.Net.RateLimiter this.perTimePeriod = perTimePeriod; } + /// public CallResult LimitRequest(RestClient client, string url, RateLimitingBehaviour limitingBehaviour) { int waitTime; diff --git a/CryptoExchange.Net/RateLimiter/RateLimiterTotal.cs b/CryptoExchange.Net/RateLimiter/RateLimiterTotal.cs index f28beb8..85919e6 100644 --- a/CryptoExchange.Net/RateLimiter/RateLimiterTotal.cs +++ b/CryptoExchange.Net/RateLimiter/RateLimiterTotal.cs @@ -30,6 +30,7 @@ namespace CryptoExchange.Net.RateLimiter this.perTimePeriod = perTimePeriod; } + /// public CallResult LimitRequest(RestClient client, string url, RateLimitingBehaviour limitBehaviour) { var sw = Stopwatch.StartNew(); diff --git a/CryptoExchange.Net/Requests/Request.cs b/CryptoExchange.Net/Requests/Request.cs index 8b64c4f..5566699 100644 --- a/CryptoExchange.Net/Requests/Request.cs +++ b/CryptoExchange.Net/Requests/Request.cs @@ -6,65 +6,84 @@ using CryptoExchange.Net.Interfaces; namespace CryptoExchange.Net.Requests { + /// + /// Request object + /// public class Request : IRequest { private readonly WebRequest request; + /// + /// Create request object for webrequest + /// + /// public Request(WebRequest request) { this.request = request; } + /// public WebHeaderCollection Headers { get => request.Headers; set => request.Headers = value; } + + /// public string ContentType { get => request.ContentType; set => request.ContentType = value; } + /// public string Content { get; set; } + /// public string Accept { get => ((HttpWebRequest)request).Accept; set => ((HttpWebRequest)request).Accept = value; } + /// public long ContentLength { get => ((HttpWebRequest)request).ContentLength; set => ((HttpWebRequest)request).ContentLength = value; } + /// public string Method { get => request.Method; set => request.Method = value; } + /// public TimeSpan Timeout { get => TimeSpan.FromMilliseconds(request.Timeout); set => request.Timeout = (int)Math.Round(value.TotalMilliseconds); } + /// public Uri Uri => request.RequestUri; + /// 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); } + /// public async Task GetRequestStream() { return await request.GetRequestStreamAsync().ConfigureAwait(false); } + /// public async Task GetResponse() { return new Response((HttpWebResponse)await request.GetResponseAsync().ConfigureAwait(false)); diff --git a/CryptoExchange.Net/Requests/RequestFactory.cs b/CryptoExchange.Net/Requests/RequestFactory.cs index 4469293..add3f81 100644 --- a/CryptoExchange.Net/Requests/RequestFactory.cs +++ b/CryptoExchange.Net/Requests/RequestFactory.cs @@ -3,8 +3,12 @@ using CryptoExchange.Net.Interfaces; namespace CryptoExchange.Net.Requests { + /// + /// WebRequest factory + /// public class RequestFactory : IRequestFactory { + /// public IRequest Create(string uri) { return new Request(WebRequest.Create(uri)); diff --git a/CryptoExchange.Net/Requests/Response.cs b/CryptoExchange.Net/Requests/Response.cs index 515414f..a5d88a1 100644 --- a/CryptoExchange.Net/Requests/Response.cs +++ b/CryptoExchange.Net/Requests/Response.cs @@ -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 { + /// + /// HttpWebResponse response object + /// public class Response : IResponse { private readonly HttpWebResponse response; + /// public HttpStatusCode StatusCode => response.StatusCode; + /// + /// Create response for http web response + /// + /// public Response(HttpWebResponse response) { this.response = response; } + /// public Stream GetResponseStream() { return response.GetResponseStream(); } + /// public IEnumerable> GetResponseHeaders() { return response.Headers.ToIEnumerable(); } + /// public void Close() { response.Close(); diff --git a/CryptoExchange.Net/RestClient.cs b/CryptoExchange.Net/RestClient.cs index 85511c7..844607f 100644 --- a/CryptoExchange.Net/RestClient.cs +++ b/CryptoExchange.Net/RestClient.cs @@ -19,6 +19,9 @@ using Newtonsoft.Json.Linq; namespace CryptoExchange.Net { + /// + /// Base rest client + /// public abstract class RestClient: BaseClient, IRestClient { /// @@ -27,14 +30,37 @@ namespace CryptoExchange.Net public IRequestFactory RequestFactory { get; set; } = new RequestFactory(); + /// + /// Where to place post parameters + /// protected PostParameters postParametersPosition = PostParameters.InBody; + /// + /// Request body content type + /// protected RequestBodyFormat requestBodyFormat = RequestBodyFormat.Json; + /// + /// Timeout for requests + /// protected TimeSpan RequestTimeout { get; private set; } + /// + /// Rate limiting behaviour + /// public RateLimitingBehaviour RateLimitBehaviour { get; private set; } + /// + /// List of ratelimitters + /// public IEnumerable RateLimiters { get; private set; } + /// + /// Total requests made + /// public int TotalRequestsMade { get; private set; } + /// + /// ctor + /// + /// + /// protected RestClient(RestClientOptions exchangeOptions, AuthenticationProvider authenticationProvider): base(exchangeOptions, authenticationProvider) { Configure(exchangeOptions); diff --git a/CryptoExchange.Net/SocketClient.cs b/CryptoExchange.Net/SocketClient.cs index c06ffd9..cc53894 100644 --- a/CryptoExchange.Net/SocketClient.cs +++ b/CryptoExchange.Net/SocketClient.cs @@ -13,6 +13,9 @@ using Newtonsoft.Json.Linq; namespace CryptoExchange.Net { + /// + /// Base for socket client implementations + /// public abstract class SocketClient: BaseClient, ISocketClient { #region fields @@ -25,6 +28,8 @@ namespace CryptoExchange.Net /// List of socket connections currently connecting/connected /// protected internal ConcurrentDictionary sockets = new ConcurrentDictionary(); + /// + /// protected internal readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1); /// @@ -42,14 +47,43 @@ namespace CryptoExchange.Net /// public int SocketCombineTarget { get; protected set; } + /// + /// Handler for byte data + /// protected Func dataInterpreterBytes; + /// + /// Handler for string data + /// protected Func dataInterpreterString; + /// + /// Generic handlers + /// protected Dictionary> genericHandlers = new Dictionary>(); + /// + /// Periodic task + /// protected Task periodicTask; + /// + /// Periodic task event + /// protected AutoResetEvent periodicEvent; + /// + /// Is disposing + /// protected bool disposing; + + /// + /// 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 + /// + protected internal bool ContinueOnQueryResponse { get; protected set; } #endregion + /// + /// Create a socket client + /// + /// Client options + /// Authentication provider protected SocketClient(SocketClientOptions exchangeOptions, AuthenticationProvider authenticationProvider): base(exchangeOptions, authenticationProvider) { Configure(exchangeOptions); @@ -174,6 +208,13 @@ namespace CryptoExchange.Net return new CallResult(callResult?.Success ?? false, callResult == null ? new ServerError("No response on subscription request received"): callResult.Error); } + /// + /// Query for data + /// + /// Exepected result type + /// The request to send + /// Whether the socket should be authenticated + /// protected virtual Task> Query(object request, bool authenticated) { return Query(BaseAddress, request, authenticated); @@ -183,6 +224,7 @@ namespace CryptoExchange.Net /// Query for data /// /// The expected result type + /// The url for the request /// The request to send /// Whether the socket should be authenticated /// @@ -293,7 +335,7 @@ namespace CryptoExchange.Net /// The socket connection /// /// The request that a response is awaited for - /// The message + /// The message /// The interpretation (null if message wasn't a response to the request) /// True if the message was a response to the subscription request protected internal abstract bool HandleSubscriptionResponse(SocketConnection s, SocketSubscription subscription, object request, JToken message, out CallResult callResult); diff --git a/CryptoExchange.Net/Sockets/BaseSocket.cs b/CryptoExchange.Net/Sockets/BaseSocket.cs index 912885e..e1b719f 100644 --- a/CryptoExchange.Net/Sockets/BaseSocket.cs +++ b/CryptoExchange.Net/Sockets/BaseSocket.cs @@ -13,7 +13,10 @@ using WebSocket4Net; namespace CryptoExchange.Net.Sockets { - public class BaseSocket: IWebsocket + /// + /// Socket implementation + /// + internal class BaseSocket: IWebsocket { internal static int lastStreamId; private static readonly object streamIdLock = new object(); diff --git a/CryptoExchange.Net/Sockets/SocketConnection.cs b/CryptoExchange.Net/Sockets/SocketConnection.cs index 152003f..d827517 100644 --- a/CryptoExchange.Net/Sockets/SocketConnection.cs +++ b/CryptoExchange.Net/Sockets/SocketConnection.cs @@ -11,25 +11,58 @@ using Newtonsoft.Json.Linq; namespace CryptoExchange.Net.Sockets { + /// + /// Socket connecting + /// public class SocketConnection { + /// + /// Connection lost event + /// public event Action ConnectionLost; + /// + /// Connecting restored event + /// public event Action ConnectionRestored; + /// + /// Connecting closed event + /// public event Action Closed; + /// + /// The amount of handlers + /// public int HandlerCount { get { lock (handlersLock) return handlers.Count(h => h.UserSubscription); } } + /// + /// If connection is authenticated + /// public bool Authenticated { get; set; } + /// + /// If connection is made + /// public bool Connected { get; private set; } - + /// + /// The socket + /// public IWebsocket Socket { get; set; } + /// + /// If should reconnect upon closing + /// public bool ShouldReconnect { get; set; } + + /// + /// Time of disconnecting + /// public DateTime? DisconnectTime { get; set; } + /// + /// If activity is paused + /// public bool PausedActivity { get; set; } internal readonly List handlers; @@ -41,6 +74,11 @@ namespace CryptoExchange.Net.Sockets private readonly List pendingRequests; + /// + /// New socket connection + /// + /// The socket client + /// The socket public SocketConnection(SocketClient client, IWebsocket socket) { log = client.log; @@ -72,6 +110,13 @@ namespace CryptoExchange.Net.Sockets }; } + /// + /// Add a handler + /// + /// The request object + /// If it is a user subscription or a generic handler + /// The data handler + /// public SocketSubscription AddHandler(object request, bool userSubscription, Action dataHandler) { var handler = new SocketSubscription(null, request, userSubscription, dataHandler); @@ -80,6 +125,14 @@ namespace CryptoExchange.Net.Sockets return handler; } + /// + /// Add a handler + /// + /// The identifier of the handler + /// If it is a user subscription or a generic handler + /// The data handler + /// + /// public SocketSubscription AddHandler(string identifier, bool userSubscription, Action 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 } } + /// + /// Send data + /// + /// The data type + /// The object to send + /// The timeout for response + /// The response handler + /// public virtual Task SendAndWait(T obj, TimeSpan timeout, Func 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 ProcessReconnect() + private async void InvokeConnectionRestored() + { + await Task.Run(() => ConnectionRestored?.Invoke(DisconnectTime.HasValue ? DateTime.UtcNow - DisconnectTime.Value : TimeSpan.FromSeconds(0))).ConfigureAwait(false); + } + + private async Task ProcessReconnect() { if (Authenticated) { @@ -279,6 +347,10 @@ namespace CryptoExchange.Net.Sockets return true; } + /// + /// Close the connection + /// + /// public async Task Close() { Connected = false; @@ -290,6 +362,11 @@ namespace CryptoExchange.Net.Sockets Socket.Dispose(); } + /// + /// Close the subscriptions + /// + /// Subscription to close + /// 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 Handler { get; } public JToken Result { get; private set; } diff --git a/CryptoExchange.Net/Sockets/SocketSubscription.cs b/CryptoExchange.Net/Sockets/SocketSubscription.cs index 552ec5e..0de0546 100644 --- a/CryptoExchange.Net/Sockets/SocketSubscription.cs +++ b/CryptoExchange.Net/Sockets/SocketSubscription.cs @@ -3,8 +3,14 @@ using Newtonsoft.Json.Linq; namespace CryptoExchange.Net.Sockets { + /// + /// Socket subscription + /// public class SocketSubscription { + /// + /// Exception event + /// public event Action Exception; /// @@ -12,13 +18,32 @@ namespace CryptoExchange.Net.Sockets /// public Action MessageHandler { get; set; } + /// + /// Request object + /// public object Request { get; set; } + /// + /// Subscription identifier + /// public string Identifier { get; set; } + /// + /// Is user subscription or generic + /// public bool UserSubscription { get; set; } + /// + /// If the subscription has been confirmed + /// public bool Confirmed { get; set; } + /// + /// ctor + /// + /// + /// + /// + /// public SocketSubscription(string identifier, object request, bool userSubscription, Action dataHandler) { UserSubscription = userSubscription; @@ -27,6 +52,10 @@ namespace CryptoExchange.Net.Sockets Request = request; } + /// + /// Invoke the exception event + /// + /// public void InvokeExceptionHandler(Exception e) { Exception?.Invoke(e); diff --git a/CryptoExchange.Net/Sockets/UpdateSubscription.cs b/CryptoExchange.Net/Sockets/UpdateSubscription.cs index 176d406..681b0c5 100644 --- a/CryptoExchange.Net/Sockets/UpdateSubscription.cs +++ b/CryptoExchange.Net/Sockets/UpdateSubscription.cs @@ -3,6 +3,9 @@ using System.Threading.Tasks; namespace CryptoExchange.Net.Sockets { + /// + /// Subscription + /// public class UpdateSubscription { private readonly SocketConnection connection; @@ -40,6 +43,11 @@ namespace CryptoExchange.Net.Sockets /// public int Id => connection.Socket.Id; + /// + /// ctor + /// + /// + /// public UpdateSubscription(SocketConnection connection, SocketSubscription subscription) { this.connection = connection; diff --git a/CryptoExchange.Net/Sockets/WebsocketFactory.cs b/CryptoExchange.Net/Sockets/WebsocketFactory.cs index 02b0a36..bfe17df 100644 --- a/CryptoExchange.Net/Sockets/WebsocketFactory.cs +++ b/CryptoExchange.Net/Sockets/WebsocketFactory.cs @@ -4,13 +4,18 @@ using CryptoExchange.Net.Logging; namespace CryptoExchange.Net.Sockets { + /// + /// Factory implementation + /// public class WebsocketFactory : IWebsocketFactory { + /// public IWebsocket CreateWebsocket(Log log, string url) { return new BaseSocket(log, url); } + /// public IWebsocket CreateWebsocket(Log log, string url, IDictionary cookies, IDictionary headers) { return new BaseSocket(log, url, cookies, headers); diff --git a/README.md b/README.md index c01b4b3..c7243c5 100644 --- a/README.md +++ b/README.md @@ -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