1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2025-06-10 17:36:19 +00:00
This commit is contained in:
JKorf 2020-06-16 16:19:00 +02:00
commit 01da87a481
9 changed files with 66 additions and 98 deletions

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Logging; using CryptoExchange.Net.Logging;
@ -38,14 +39,14 @@ namespace CryptoExchange.Net.UnitTests
{ {
} }
public override Dictionary<string, string> AddAuthenticationToHeaders(string uri, HttpMethod method, Dictionary<string, object> parameters, bool signed) public override Dictionary<string, string> AddAuthenticationToHeaders(string uri, HttpMethod method, Dictionary<string, object> parameters, bool signed, PostParameters postParameters, ArrayParametersSerialization arraySerialization)
{ {
return base.AddAuthenticationToHeaders(uri, method, parameters, signed); return base.AddAuthenticationToHeaders(uri, method, parameters, signed, postParameters, arraySerialization);
} }
public override Dictionary<string, object> AddAuthenticationToParameters(string uri, HttpMethod method, Dictionary<string, object> parameters, bool signed) public override Dictionary<string, object> AddAuthenticationToParameters(string uri, HttpMethod method, Dictionary<string, object> parameters, bool signed, PostParameters postParameters, ArrayParametersSerialization arraySerialization)
{ {
return base.AddAuthenticationToParameters(uri, method, parameters, signed); return base.AddAuthenticationToParameters(uri, method, parameters, signed, postParameters, arraySerialization);
} }
public override string Sign(string toSign) public override string Sign(string toSign)

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using CryptoExchange.Net.Objects;
using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
namespace CryptoExchange.Net.Authentication namespace CryptoExchange.Net.Authentication
@ -29,8 +30,11 @@ namespace CryptoExchange.Net.Authentication
/// <param name="method"></param> /// <param name="method"></param>
/// <param name="parameters"></param> /// <param name="parameters"></param>
/// <param name="signed"></param> /// <param name="signed"></param>
/// <param name="postParameterPosition"></param>
/// <param name="arraySerialization"></param>
/// <returns></returns> /// <returns></returns>
public virtual Dictionary<string, object> AddAuthenticationToParameters(string uri, HttpMethod method, Dictionary<string, object> parameters, bool signed) public virtual Dictionary<string, object> AddAuthenticationToParameters(string uri, HttpMethod method, Dictionary<string, object> parameters, bool signed,
PostParameters postParameterPosition, ArrayParametersSerialization arraySerialization)
{ {
return parameters; return parameters;
} }
@ -42,8 +46,11 @@ namespace CryptoExchange.Net.Authentication
/// <param name="method"></param> /// <param name="method"></param>
/// <param name="parameters"></param> /// <param name="parameters"></param>
/// <param name="signed"></param> /// <param name="signed"></param>
/// <param name="postParameterPosition"></param>
/// <param name="arraySerialization"></param>
/// <returns></returns> /// <returns></returns>
public virtual Dictionary<string, string> AddAuthenticationToHeaders(string uri, HttpMethod method, Dictionary<string, object> parameters, bool signed) public virtual Dictionary<string, string> AddAuthenticationToHeaders(string uri, HttpMethod method, Dictionary<string, object> parameters, bool signed,
PostParameters postParameterPosition, ArrayParametersSerialization arraySerialization)
{ {
return new Dictionary<string, string>(); return new Dictionary<string, string>();
} }

View File

@ -6,12 +6,12 @@
<PackageId>CryptoExchange.Net</PackageId> <PackageId>CryptoExchange.Net</PackageId>
<Authors>JKorf</Authors> <Authors>JKorf</Authors>
<Description>A base package for implementing cryptocurrency exchange API's</Description> <Description>A base package for implementing cryptocurrency exchange API's</Description>
<PackageVersion>3.0.8</PackageVersion> <PackageVersion>3.0.9</PackageVersion>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance> <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageProjectUrl>https://github.com/JKorf/CryptoExchange.Net</PackageProjectUrl> <PackageProjectUrl>https://github.com/JKorf/CryptoExchange.Net</PackageProjectUrl>
<NeutralLanguage>en</NeutralLanguage> <NeutralLanguage>en</NeutralLanguage>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageReleaseNotes>3.0.8 - Added empty body content setting, added TryParseError virtual method</PackageReleaseNotes> <PackageReleaseNotes>3.0.9 - Added arraySerialization and postParameterPosition to AuthenticationProvider interface, fixed array serialization in request body</PackageReleaseNotes>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>

View File

@ -97,7 +97,7 @@
</summary> </summary>
<param name="credentials"></param> <param name="credentials"></param>
</member> </member>
<member name="M:CryptoExchange.Net.Authentication.AuthenticationProvider.AddAuthenticationToParameters(System.String,System.Net.Http.HttpMethod,System.Collections.Generic.Dictionary{System.String,System.Object},System.Boolean)"> <member name="M:CryptoExchange.Net.Authentication.AuthenticationProvider.AddAuthenticationToParameters(System.String,System.Net.Http.HttpMethod,System.Collections.Generic.Dictionary{System.String,System.Object},System.Boolean,CryptoExchange.Net.Objects.PostParameters,CryptoExchange.Net.Objects.ArrayParametersSerialization)">
<summary> <summary>
Add authentication to the parameter list Add authentication to the parameter list
</summary> </summary>
@ -105,9 +105,11 @@
<param name="method"></param> <param name="method"></param>
<param name="parameters"></param> <param name="parameters"></param>
<param name="signed"></param> <param name="signed"></param>
<param name="postParameterPosition"></param>
<param name="arraySerialization"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:CryptoExchange.Net.Authentication.AuthenticationProvider.AddAuthenticationToHeaders(System.String,System.Net.Http.HttpMethod,System.Collections.Generic.Dictionary{System.String,System.Object},System.Boolean)"> <member name="M:CryptoExchange.Net.Authentication.AuthenticationProvider.AddAuthenticationToHeaders(System.String,System.Net.Http.HttpMethod,System.Collections.Generic.Dictionary{System.String,System.Object},System.Boolean,CryptoExchange.Net.Objects.PostParameters,CryptoExchange.Net.Objects.ArrayParametersSerialization)">
<summary> <summary>
Add authentication to the header dictionary Add authentication to the header dictionary
</summary> </summary>
@ -115,6 +117,8 @@
<param name="method"></param> <param name="method"></param>
<param name="parameters"></param> <param name="parameters"></param>
<param name="signed"></param> <param name="signed"></param>
<param name="postParameterPosition"></param>
<param name="arraySerialization"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:CryptoExchange.Net.Authentication.AuthenticationProvider.Sign(System.String)"> <member name="M:CryptoExchange.Net.Authentication.AuthenticationProvider.Sign(System.String)">
@ -1671,41 +1675,6 @@
<member name="M:CryptoExchange.Net.Objects.SocketClientOptions.ToString"> <member name="M:CryptoExchange.Net.Objects.SocketClientOptions.ToString">
<inheritdoc /> <inheritdoc />
</member> </member>
<member name="T:CryptoExchange.Net.OrderBook.ProcessBufferEntry">
<summary>
Buffer entry for order book
</summary>
</member>
<member name="P:CryptoExchange.Net.OrderBook.ProcessBufferEntry.Asks">
<summary>
List of asks
</summary>
</member>
<member name="P:CryptoExchange.Net.OrderBook.ProcessBufferEntry.Bids">
<summary>
List of bids
</summary>
</member>
<member name="T:CryptoExchange.Net.OrderBook.ProcessBufferSingleSequenceEntry">
<summary>
Buffer entry with a single update id per update
</summary>
</member>
<member name="P:CryptoExchange.Net.OrderBook.ProcessBufferSingleSequenceEntry.UpdateId">
<summary>
First update id
</summary>
</member>
<member name="P:CryptoExchange.Net.OrderBook.ProcessBufferSingleSequenceEntry.Asks">
<summary>
List of asks
</summary>
</member>
<member name="P:CryptoExchange.Net.OrderBook.ProcessBufferSingleSequenceEntry.Bids">
<summary>
List of bids
</summary>
</member>
<member name="T:CryptoExchange.Net.OrderBook.ProcessBufferRangeSequenceEntry"> <member name="T:CryptoExchange.Net.OrderBook.ProcessBufferRangeSequenceEntry">
<summary> <summary>
Buffer entry with a first and last update id Buffer entry with a first and last update id
@ -2191,7 +2160,7 @@
</summary> </summary>
<returns>The roundtrip time of the ping request</returns> <returns>The roundtrip time of the ping request</returns>
</member> </member>
<member name="M:CryptoExchange.Net.RestClient.SendRequest``1(System.Uri,System.Net.Http.HttpMethod,System.Threading.CancellationToken,System.Collections.Generic.Dictionary{System.String,System.Object},System.Boolean,System.Boolean)"> <member name="M:CryptoExchange.Net.RestClient.SendRequest``1(System.Uri,System.Net.Http.HttpMethod,System.Threading.CancellationToken,System.Collections.Generic.Dictionary{System.String,System.Object},System.Boolean,System.Boolean,System.Nullable{CryptoExchange.Net.Objects.PostParameters},System.Nullable{CryptoExchange.Net.Objects.ArrayParametersSerialization})">
<summary> <summary>
Execute a request Execute a request
</summary> </summary>
@ -2201,7 +2170,9 @@
<param name="cancellationToken">Cancellation token</param> <param name="cancellationToken">Cancellation token</param>
<param name="parameters">The parameters of the request</param> <param name="parameters">The parameters of the request</param>
<param name="signed">Whether or not the request should be authenticated</param> <param name="signed">Whether or not the request should be authenticated</param>
<param name="checkResult">Whether or not the resulting object should be checked for missing properties in the mapping (only outputs if log verbosity is Debug)</param> <param name="checkResult">Whether or not the resulting object should be checked for missing properties in the mapping (only outputs if log verbosity is Debug)</param>
<param name="postPosition">Where the post parameters should be placed</param>
<param name="arraySerialization">How array paramters should be serialized</param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:CryptoExchange.Net.RestClient.GetResponse``1(CryptoExchange.Net.Interfaces.IRequest,System.Threading.CancellationToken)"> <member name="M:CryptoExchange.Net.RestClient.GetResponse``1(CryptoExchange.Net.Interfaces.IRequest,System.Threading.CancellationToken)">
@ -2220,7 +2191,7 @@
<param name="data">Received data</param> <param name="data">Received data</param>
<returns>Null if not an error, Error otherwise</returns> <returns>Null if not an error, Error otherwise</returns>
</member> </member>
<member name="M:CryptoExchange.Net.RestClient.ConstructRequest(System.Uri,System.Net.Http.HttpMethod,System.Collections.Generic.Dictionary{System.String,System.Object},System.Boolean)"> <member name="M:CryptoExchange.Net.RestClient.ConstructRequest(System.Uri,System.Net.Http.HttpMethod,System.Collections.Generic.Dictionary{System.String,System.Object},System.Boolean,CryptoExchange.Net.Objects.PostParameters,CryptoExchange.Net.Objects.ArrayParametersSerialization)">
<summary> <summary>
Creates a request object Creates a request object
</summary> </summary>
@ -2228,6 +2199,8 @@
<param name="method">The method of the request</param> <param name="method">The method of the request</param>
<param name="parameters">The parameters of the request</param> <param name="parameters">The parameters of the request</param>
<param name="signed">Whether or not the request should be authenticated</param> <param name="signed">Whether or not the request should be authenticated</param>
<param name="postPosition">Where the post parameters should be placed</param>
<param name="arraySerialization">How array paramters should be serialized</param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:CryptoExchange.Net.RestClient.WriteParamBody(CryptoExchange.Net.Interfaces.IRequest,System.Collections.Generic.Dictionary{System.String,System.Object},System.String)"> <member name="M:CryptoExchange.Net.RestClient.WriteParamBody(CryptoExchange.Net.Interfaces.IRequest,System.Collections.Generic.Dictionary{System.String,System.Object},System.String)">

View File

@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
using System.Security; using System.Security;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web;
using CryptoExchange.Net.Logging; using CryptoExchange.Net.Logging;
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -79,16 +80,16 @@ namespace CryptoExchange.Net
foreach (var arrayEntry in arraysParameters) foreach (var arrayEntry in arraysParameters)
{ {
if(serializationType == ArrayParametersSerialization.Array) if(serializationType == ArrayParametersSerialization.Array)
uriString += $"{string.Join("&", ((object[])(urlEncodeValues ? WebUtility.UrlEncode(arrayEntry.Value.ToString()) : arrayEntry.Value)).Select(v => $"{arrayEntry.Key}[]={v}"))}&"; uriString += $"{string.Join("&", ((object[])(urlEncodeValues ? Uri.EscapeDataString(arrayEntry.Value.ToString()) : arrayEntry.Value)).Select(v => $"{arrayEntry.Key}[]={v}"))}&";
else else
{ {
var array = (Array)arrayEntry.Value; var array = (Array)arrayEntry.Value;
uriString += string.Join("&", array.OfType<object>().Select(a => $"{arrayEntry.Key}={WebUtility.UrlEncode(a.ToString())}")); uriString += string.Join("&", array.OfType<object>().Select(a => $"{arrayEntry.Key}={Uri.EscapeDataString(a.ToString())}"));
uriString += "&"; uriString += "&";
} }
} }
uriString += $"{string.Join("&", parameters.Where(p => !p.Value.GetType().IsArray).Select(s => $"{s.Key}={(urlEncodeValues ? WebUtility.UrlEncode(s.Value.ToString()) : s.Value)}"))}"; uriString += $"{string.Join("&", parameters.Where(p => !p.Value.GetType().IsArray).Select(s => $"{s.Key}={(urlEncodeValues ? Uri.EscapeDataString(s.Value.ToString()) : s.Value)}"))}";
uriString = uriString.TrimEnd('&'); uriString = uriString.TrimEnd('&');
return uriString; return uriString;
} }

View File

@ -3,40 +3,6 @@ using System.Collections.Generic;
namespace CryptoExchange.Net.OrderBook namespace CryptoExchange.Net.OrderBook
{ {
/// <summary>
/// Buffer entry for order book
/// </summary>
public class ProcessBufferEntry
{
/// <summary>
/// List of asks
/// </summary>
public IEnumerable<ISymbolOrderSequencedBookEntry> Asks { get; set; } = new List<ISymbolOrderSequencedBookEntry>();
/// <summary>
/// List of bids
/// </summary>
public IEnumerable<ISymbolOrderSequencedBookEntry> Bids { get; set; } = new List<ISymbolOrderSequencedBookEntry>();
}
/// <summary>
/// Buffer entry with a single update id per update
/// </summary>
public class ProcessBufferSingleSequenceEntry
{
/// <summary>
/// First update id
/// </summary>
public long UpdateId { get; set; }
/// <summary>
/// List of asks
/// </summary>
public IEnumerable<ISymbolOrderBookEntry> Asks { get; set; } = new List<ISymbolOrderBookEntry>();
/// <summary>
/// List of bids
/// </summary>
public IEnumerable<ISymbolOrderBookEntry> Bids { get; set; } = new List<ISymbolOrderBookEntry>();
}
/// <summary> /// <summary>
/// Buffer entry with a first and last update id /// Buffer entry with a first and last update id
/// </summary> /// </summary>

View File

@ -22,7 +22,6 @@ namespace CryptoExchange.Net.OrderBook
/// The process buffer, used while syncing /// The process buffer, used while syncing
/// </summary> /// </summary>
protected readonly List<ProcessBufferRangeSequenceEntry> processBuffer; protected readonly List<ProcessBufferRangeSequenceEntry> processBuffer;
private readonly object bookLock = new object();
/// <summary> /// <summary>
/// The ask list /// The ask list
/// </summary> /// </summary>
@ -30,8 +29,10 @@ namespace CryptoExchange.Net.OrderBook
/// <summary> /// <summary>
/// The bid list /// The bid list
/// </summary> /// </summary>
protected SortedList<decimal, ISymbolOrderBookEntry> bids; protected SortedList<decimal, ISymbolOrderBookEntry> bids;
private readonly object bookLock = new object();
private OrderBookStatus status; private OrderBookStatus status;
private UpdateSubscription? subscription; private UpdateSubscription? subscription;
private readonly bool sequencesAreConsecutive; private readonly bool sequencesAreConsecutive;
@ -224,6 +225,7 @@ namespace CryptoExchange.Net.OrderBook
/// <returns></returns> /// <returns></returns>
public async Task<CallResult<bool>> StartAsync() public async Task<CallResult<bool>> StartAsync()
{ {
log.Write(LogVerbosity.Debug, $"{Id} order book {Symbol} starting");
Status = OrderBookStatus.Connecting; Status = OrderBookStatus.Connecting;
_processTask = Task.Run(ProcessQueue); _processTask = Task.Run(ProcessQueue);
@ -279,6 +281,7 @@ namespace CryptoExchange.Net.OrderBook
/// <returns></returns> /// <returns></returns>
public async Task StopAsync() public async Task StopAsync()
{ {
log.Write(LogVerbosity.Debug, $"{Id} order book {Symbol} stopping");
Status = OrderBookStatus.Disconnected; Status = OrderBookStatus.Disconnected;
_queueEvent.Set(); _queueEvent.Set();
_processTask.Wait(); _processTask.Wait();

View File

@ -164,11 +164,13 @@ namespace CryptoExchange.Net
/// <param name="cancellationToken">Cancellation token</param> /// <param name="cancellationToken">Cancellation token</param>
/// <param name="parameters">The parameters of the request</param> /// <param name="parameters">The parameters of the request</param>
/// <param name="signed">Whether or not the request should be authenticated</param> /// <param name="signed">Whether or not the request should be authenticated</param>
/// <param name="checkResult">Whether or not the resulting object should be checked for missing properties in the mapping (only outputs if log verbosity is Debug)</param> /// <param name="checkResult">Whether or not the resulting object should be checked for missing properties in the mapping (only outputs if log verbosity is Debug)</param>
/// <param name="postPosition">Where the post parameters should be placed</param>
/// <param name="arraySerialization">How array paramters should be serialized</param>
/// <returns></returns> /// <returns></returns>
[return: NotNull] [return: NotNull]
protected virtual async Task<WebCallResult<T>> SendRequest<T>(Uri uri, HttpMethod method, CancellationToken cancellationToken, protected virtual async Task<WebCallResult<T>> SendRequest<T>(Uri uri, HttpMethod method, CancellationToken cancellationToken,
Dictionary<string, object>? parameters = null, bool signed = false, bool checkResult = true) where T : class Dictionary<string, object>? parameters = null, bool signed = false, bool checkResult = true, PostParameters? postPosition = null, ArrayParametersSerialization? arraySerialization = null) where T : class
{ {
log.Write(LogVerbosity.Debug, "Creating request for " + uri); log.Write(LogVerbosity.Debug, "Creating request for " + uri);
if (signed && authProvider == null) if (signed && authProvider == null)
@ -177,7 +179,7 @@ namespace CryptoExchange.Net
return new WebCallResult<T>(null, null, null, new NoApiCredentialsError()); return new WebCallResult<T>(null, null, null, new NoApiCredentialsError());
} }
var request = ConstructRequest(uri, method, parameters, signed); var request = ConstructRequest(uri, method, parameters, signed, postPosition ?? postParametersPosition, arraySerialization ?? this.arraySerialization);
foreach (var limiter in RateLimiters) foreach (var limiter in RateLimiters)
{ {
var limitResult = limiter.LimitRequest(this, uri.AbsolutePath, RateLimitBehaviour); var limitResult = limiter.LimitRequest(this, uri.AbsolutePath, RateLimitBehaviour);
@ -294,17 +296,19 @@ namespace CryptoExchange.Net
/// <param name="method">The method of the request</param> /// <param name="method">The method of the request</param>
/// <param name="parameters">The parameters of the request</param> /// <param name="parameters">The parameters of the request</param>
/// <param name="signed">Whether or not the request should be authenticated</param> /// <param name="signed">Whether or not the request should be authenticated</param>
/// <param name="postPosition">Where the post parameters should be placed</param>
/// <param name="arraySerialization">How array paramters should be serialized</param>
/// <returns></returns> /// <returns></returns>
protected virtual IRequest ConstructRequest(Uri uri, HttpMethod method, Dictionary<string, object>? parameters, bool signed) protected virtual IRequest ConstructRequest(Uri uri, HttpMethod method, Dictionary<string, object>? parameters, bool signed, PostParameters postPosition, ArrayParametersSerialization arraySerialization)
{ {
if (parameters == null) if (parameters == null)
parameters = new Dictionary<string, object>(); parameters = new Dictionary<string, object>();
var uriString = uri.ToString(); var uriString = uri.ToString();
if(authProvider != null) if(authProvider != null)
parameters = authProvider.AddAuthenticationToParameters(uriString, method, parameters, signed); parameters = authProvider.AddAuthenticationToParameters(uriString, method, parameters, signed, postPosition, arraySerialization);
if((method == HttpMethod.Get || method == HttpMethod.Delete || postParametersPosition == PostParameters.InUri) && parameters?.Any() == true) if((method == HttpMethod.Get || method == HttpMethod.Delete || postPosition == PostParameters.InUri) && parameters?.Any() == true)
uriString += "?" + parameters.CreateParamString(true, arraySerialization); uriString += "?" + parameters.CreateParamString(true, arraySerialization);
var contentType = requestBodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader; var contentType = requestBodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader;
@ -313,12 +317,12 @@ namespace CryptoExchange.Net
var headers = new Dictionary<string, string>(); var headers = new Dictionary<string, string>();
if (authProvider != null) if (authProvider != null)
headers = authProvider.AddAuthenticationToHeaders(uriString, method, parameters!, signed); headers = authProvider.AddAuthenticationToHeaders(uriString, method, parameters!, signed, postPosition, arraySerialization);
foreach (var header in headers) foreach (var header in headers)
request.AddHeader(header.Key, header.Value); request.AddHeader(header.Key, header.Value);
if ((method == HttpMethod.Post || method == HttpMethod.Put) && postParametersPosition != PostParameters.InUri) if ((method == HttpMethod.Post || method == HttpMethod.Put) && postPosition != PostParameters.InUri)
{ {
if(parameters?.Any() == true) if(parameters?.Any() == true)
WriteParamBody(request, parameters, contentType); WriteParamBody(request, parameters, contentType);
@ -346,7 +350,16 @@ namespace CryptoExchange.Net
{ {
var formData = HttpUtility.ParseQueryString(string.Empty); var formData = HttpUtility.ParseQueryString(string.Empty);
foreach (var kvp in parameters.OrderBy(p => p.Key)) foreach (var kvp in parameters.OrderBy(p => p.Key))
formData.Add(kvp.Key, kvp.Value.ToString()); {
if (kvp.Value.GetType().IsArray)
{
var array = (Array)kvp.Value;
foreach(var value in array)
formData.Add(kvp.Key, value.ToString());
}
else
formData.Add(kvp.Key, kvp.Value.ToString());
}
var stringData = formData.ToString(); var stringData = formData.ToString();
request.SetContent(stringData, contentType); request.SetContent(stringData, contentType);
} }

View File

@ -194,6 +194,10 @@ The order book will automatically reconnect when the connection is lost and resy
To stop synchronizing an order book use the `Stop` method. To stop synchronizing an order book use the `Stop` method.
## Release notes ## Release notes
* Version 3.0.9 - 07 Jun 2020
* Added arraySerialization and postParameterPosition to AuthenticationProvider interface
* Fixed array serialization in request body
* Version 3.0.8 - 02 Jun 2020 * Version 3.0.8 - 02 Jun 2020
* Added requestBodyEmptyContent setting for rest client * Added requestBodyEmptyContent setting for rest client
* Added TryParseError for rest implementations to check for error with success status code * Added TryParseError for rest implementations to check for error with success status code