using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Logging; using Microsoft.Extensions.Logging; namespace CryptoExchange.Net.Objects { /// /// Base options, applicable to everything /// public class BaseOptions { /// /// The minimum log level to output /// public LogLevel LogLevel { get; set; } = LogLevel.Information; /// /// The log writers /// public List LogWriters { get; set; } = new List { new DebugLogger() }; /// /// If true, the CallResult and DataEvent objects will also include the originally received json data in the OriginalData property /// public bool OutputOriginalData { get; set; } = false; /// /// ctor /// public BaseOptions(): this(null) { } /// /// ctor /// /// Copy options from these options to the new options public BaseOptions(BaseOptions? baseOptions) { if (baseOptions == null) return; LogLevel = baseOptions.LogLevel; LogWriters = baseOptions.LogWriters.ToList(); OutputOriginalData = baseOptions.OutputOriginalData; } /// public override string ToString() { return $"LogLevel: {LogLevel}, Writers: {LogWriters.Count}, OutputOriginalData: {OutputOriginalData}"; } } /// /// Client options, for both the socket and rest clients /// public class BaseClientOptions : BaseOptions { /// /// Proxy to use when connecting /// public ApiProxy? Proxy { get; set; } /// /// Api credentials to be used for signing requests to private endpoints. These credentials will be used for each API in the client, unless overriden in the API options /// public ApiCredentials? ApiCredentials { get; set; } /// /// ctor /// public BaseClientOptions() : this(null) { } /// /// ctor /// /// Copy options from these options to the new options public BaseClientOptions(BaseClientOptions? baseOptions) : base(baseOptions) { if (baseOptions == null) return; Proxy = baseOptions.Proxy; ApiCredentials = baseOptions.ApiCredentials?.Copy(); } /// public override string ToString() { return $"{base.ToString()}, Proxy: {(Proxy == null ? "-" : Proxy.Host)}, Base.ApiCredentials: {(ApiCredentials == null ? "-" : "set")}"; } } /// /// Rest client options /// public class BaseRestClientOptions : BaseClientOptions { /// /// The time the server has to respond to a request before timing out /// public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(30); /// /// Http client to use. If a HttpClient is provided in this property the RequestTimeout and Proxy options provided in these options will be ignored in requests and should be set on the provided HttpClient instance /// public HttpClient? HttpClient { get; set; } /// /// ctor /// public BaseRestClientOptions(): this(null) { } /// /// ctor /// /// Copy options from these options to the new options public BaseRestClientOptions(BaseRestClientOptions? baseOptions): base(baseOptions) { if (baseOptions == null) return; HttpClient = baseOptions.HttpClient; RequestTimeout = baseOptions.RequestTimeout; } /// public override string ToString() { return $"{base.ToString()}, RequestTimeout: {RequestTimeout:c}, HttpClient: {(HttpClient == null ? "-" : "set")}"; } } /// /// Socket client options /// public class BaseSocketClientOptions : BaseClientOptions { /// /// Whether or not the socket should automatically reconnect when losing connection /// public bool AutoReconnect { get; set; } = true; /// /// Time to wait between reconnect attempts /// public TimeSpan ReconnectInterval { get; set; } = TimeSpan.FromSeconds(5); /// /// The maximum number of times to try to reconnect, default null will retry indefinitely /// public int? MaxReconnectTries { get; set; } /// /// The maximum number of times to try to resubscribe after reconnecting /// public int? MaxResubscribeTries { get; set; } = 5; /// /// Max number of concurrent resubscription tasks per socket after reconnecting a socket /// public int MaxConcurrentResubscriptionsPerSocket { get; set; } = 5; /// /// The max time to wait for a response after sending a request on the socket before giving a timeout /// public TimeSpan SocketResponseTimeout { get; set; } = TimeSpan.FromSeconds(10); /// /// The max time of not receiving any data after which the connection is assumed to be dropped. This can only be used for socket connections where a steady flow of data is expected, /// for example when the server sends intermittent ping requests /// public TimeSpan SocketNoDataTimeout { get; set; } /// /// The amount of subscriptions that should be made on a single socket connection. Not all API's support multiple subscriptions on a single socket. /// Setting this to a higher number increases subscription speed because not every subscription needs to connect to the server, but having more subscriptions on a /// single connection will also increase the amount of traffic on that single connection, potentially leading to issues. /// public int? SocketSubscriptionsCombineTarget { get; set; } /// /// ctor /// public BaseSocketClientOptions(): this(null) { } /// /// ctor /// /// Copy options from these options to the new options public BaseSocketClientOptions(BaseSocketClientOptions? baseOptions): base(baseOptions) { if (baseOptions == null) return; AutoReconnect = baseOptions.AutoReconnect; ReconnectInterval = baseOptions.ReconnectInterval; MaxReconnectTries = baseOptions.MaxReconnectTries; MaxResubscribeTries = baseOptions.MaxResubscribeTries; MaxConcurrentResubscriptionsPerSocket = baseOptions.MaxConcurrentResubscriptionsPerSocket; SocketResponseTimeout = baseOptions.SocketResponseTimeout; SocketNoDataTimeout = baseOptions.SocketNoDataTimeout; SocketSubscriptionsCombineTarget = baseOptions.SocketSubscriptionsCombineTarget; } /// public override string ToString() { return $"{base.ToString()}, AutoReconnect: {AutoReconnect}, ReconnectInterval: {ReconnectInterval}, MaxReconnectTries: {MaxReconnectTries}, MaxResubscribeTries: {MaxResubscribeTries}, MaxConcurrentResubscriptionsPerSocket: {MaxConcurrentResubscriptionsPerSocket}, SocketResponseTimeout: {SocketResponseTimeout:c}, SocketNoDataTimeout: {SocketNoDataTimeout}, SocketSubscriptionsCombineTarget: {SocketSubscriptionsCombineTarget}"; } } /// /// API client options /// public class ApiClientOptions { /// /// The base address of the API /// public string BaseAddress { get; set; } /// /// The api credentials used for signing requests to this API. Overrides API credentials provided in the client options /// public ApiCredentials? ApiCredentials { get; set; } /// /// ctor /// #pragma warning disable 8618 // Will always get filled by the implementation public ApiClientOptions() { } #pragma warning restore 8618 /// /// ctor /// /// Base address for the API public ApiClientOptions(string baseAddress) { BaseAddress = baseAddress; } /// /// ctor /// /// Copy values for the provided options #pragma warning disable 8618 // Will always get filled by the provided options public ApiClientOptions(ApiClientOptions baseOptions, ApiClientOptions? newValues) { BaseAddress = newValues?.BaseAddress ?? baseOptions.BaseAddress; ApiCredentials = newValues?.ApiCredentials?.Copy() ?? baseOptions.ApiCredentials?.Copy(); } #pragma warning restore 8618 /// public override string ToString() { return $"Credentials: {(ApiCredentials == null ? "-" : "Set")}, BaseAddress: {BaseAddress}"; } } /// /// Rest API client options /// public class RestApiClientOptions: ApiClientOptions { /// /// List of rate limiters to use /// public List RateLimiters { get; set; } = new List(); /// /// What to do when a call would exceed the rate limit /// public RateLimitingBehaviour RateLimitingBehaviour { get; set; } = RateLimitingBehaviour.Wait; /// /// Whether or not to automatically sync the local time with the server time /// public bool AutoTimestamp { get; set; } /// /// How often the timestamp adjustment between client and server is recalculated. If you need a very small TimeSpan here you're probably better of syncing your server time more often /// public TimeSpan TimestampRecalculationInterval { get; set; } = TimeSpan.FromHours(1); /// /// ctor /// public RestApiClientOptions() { } /// /// ctor /// /// Base address for the API public RestApiClientOptions(string baseAddress): base(baseAddress) { } /// /// ctor /// /// Copy values for the provided options public RestApiClientOptions(RestApiClientOptions baseOn, RestApiClientOptions? newValues): base(baseOn, newValues) { RateLimitingBehaviour = newValues?.RateLimitingBehaviour ?? baseOn.RateLimitingBehaviour; AutoTimestamp = newValues?.AutoTimestamp ?? baseOn.AutoTimestamp; TimestampRecalculationInterval = newValues?.TimestampRecalculationInterval ?? baseOn.TimestampRecalculationInterval; RateLimiters = newValues?.RateLimiters.ToList() ?? baseOn?.RateLimiters.ToList() ?? new List(); } /// public override string ToString() { return $"{base.ToString()}, RateLimiters: {RateLimiters?.Count}, RateLimitBehaviour: {RateLimitingBehaviour}, AutoTimestamp: {AutoTimestamp}, TimestampRecalculationInterval: {TimestampRecalculationInterval}"; } } /// /// Base for order book options /// public class OrderBookOptions : BaseOptions { /// /// Whether or not checksum validation is enabled. Default is true, disabling will ignore checksum messages. /// public bool ChecksumValidationEnabled { get; set; } = true; } }