diff --git a/Bitmex.Net.ClientExample/Program.cs b/Bitmex.Net.ClientExample/Program.cs index 34308c4..65331bd 100644 --- a/Bitmex.Net.ClientExample/Program.cs +++ b/Bitmex.Net.ClientExample/Program.cs @@ -10,18 +10,9 @@ using Bitmex.Net.Client.Objects; using Bitmex.Net.Client.HistoricalData; -using System.Collections.Generic; -using CryptoExchange.Net.Logging; -using Microsoft.Extensions.DependencyInjection; -using System.Net.Http; -using Polly.Extensions.Http; -using Polly; -using System.Collections.Concurrent; using Microsoft.Extensions.Logging; using CryptoExchange.Net.Sockets; using Newtonsoft.Json; -using CryptoExchange.Net.Authentication; -using System.Threading; using CryptoExchange.Net.CommonObjects; using Bitmex.Net.Client.Objects.Requests; @@ -37,7 +28,7 @@ static void onData(DataEvent> data) static async Task Main(string[] args) { // using CryptoExchange.Net.OrderBook.SymbolOrderBook, it subscribes to socket orderbook under the hood - var socketBook = new BitmexSymbolOrderBook("XBTUSD", new BitmexSocketOrderBookOptions("bitmex"){LogLevel = LogLevel.Trace}); + var socketBook = new BitmexSymbolOrderBook("XBTUSD", new BitmexSocketOrderBookOptions()); socketBook.OnBestOffersChanged += S_OnBestOffersChanged; await socketBook.StartAsync(); Console.ReadLine(); @@ -49,7 +40,7 @@ static async Task Main(string[] args) var configuration = builder.Build(); - var client = new BitmexClient(new BitmexClientOptions(configuration["prod:key"], configuration["prod:secret"])).MarginClient; + var client = new BitmexClient(new BitmexRestOptions(configuration["prod:key"], configuration["prod:secret"])).MarginClient; // get last 100 public trades for XBTUSD var pubTrades = await client.GetTradesAsync(new BitmexRequestWithFilter(){Symbol = "XBTUSD"}); // get last 100 your wallet changes @@ -61,9 +52,9 @@ static async Task Main(string[] args) // get wallet with all currencies var wallet = await client.GetUserWalletAllCurrenciesAsync(); // place order via CryptoExchange.Net.Interfaces.CommonClients.IFuturesClient interface implementation - var ordId = client.PlaceOrderAsync("XBTUSD", CommonOrderSide.Buy, CommonOrderType.Limit, 100, 10000); + var ordId = await client.PlaceOrderAsync("XBTUSD", CommonOrderSide.Buy, CommonOrderType.Limit, 100, 10000); // place order via IBitmexCommonTradeClient interface implementation - var ord = client.PlaceOrderAsync(new PlaceOrderRequest("XBTUSD") + var ord = await client.PlaceOrderAsync(new PlaceOrderRequest("XBTUSD") { BitmexOrderType = BitmexOrderType.Limit, Side = BitmexOrderSide.Buy, @@ -75,10 +66,7 @@ static async Task Main(string[] args) Console.ReadLine(); // subscribe to testnet.bitmex.com's Order, 1h klines using appconfig.json credentials - using (var socket = new BitmexSocketClient(new BitmexSocketClientOptions(configuration["testnet:key"], configuration["testnet:secret"], isTestnet: true) - { - LogLevel = LogLevel.Trace, - })) + using (var socket = new BitmexSocketClient(new BitmexSocketClientOptions(configuration["testnet:key"], configuration["testnet:secret"], isTestnet: true))) { // each subscription method has corresponding BitmexSocketClient event triggered on updates // e.g. Order => OnUserOrdersUpdate, TradeBin1h => OnOneHourTradeBinUpdate, Trade => OnTradeUpdate and so on @@ -86,7 +74,7 @@ static async Task Main(string[] args) socket.OnOneHourTradeBinUpdate += (data) => { //add action that should be do on new candle comes var whichCandle = data.Action == BitmexAction.Partial ? "snapshot" : "new"; foreach(var candle in data.Data) - Console.WriteLine($"{whichCandle} candle come: open {candle.Open}, high {candle.High}, low {candle.Low}, close: {candle.Close}"); + Console.WriteLine($"{whichCandle} candle come: open {candle.Open}, high {candle.High}, low {candle.Low}, close: {candle.Close}"); }; var listOfSubscriptionsResults = await socket.SubscribeAsync(new BitmexSubscribeRequest() .AddSubscription(BitmexSubscribtions.Order, "XBTUSD") // subscribe to orders updates @@ -122,7 +110,7 @@ private static void S_OnTradeUpdate(BitmexSocketEvent obj) private static void S_OnBestOffersChanged((CryptoExchange.Net.Interfaces.ISymbolOrderBookEntry BestBid, CryptoExchange.Net.Interfaces.ISymbolOrderBookEntry BestAsk) obj) { - Console.WriteLine($"S_OnBestOffersChanged:{obj.BestAsk.Price}:{obj.BestBid.Price}"); + Console.WriteLine($"S_OnBestOffersChanged: best ask is {obj.BestAsk.Price} {obj.BestAsk.Quantity} ; best bid is{obj.BestBid.Price} {obj.BestBid.Quantity} "); } diff --git a/Bitmex.Net/Bitmex.Net.Client.csproj b/Bitmex.Net/Bitmex.Net.Client.csproj index 2178916..a98943f 100644 --- a/Bitmex.Net/Bitmex.Net.Client.csproj +++ b/Bitmex.Net/Bitmex.Net.Client.csproj @@ -21,9 +21,8 @@ - + - diff --git a/Bitmex.Net/BitmexAuthenticationProvider.cs b/Bitmex.Net/BitmexAuthenticationProvider.cs index 03fda7f..012cbd0 100644 --- a/Bitmex.Net/BitmexAuthenticationProvider.cs +++ b/Bitmex.Net/BitmexAuthenticationProvider.cs @@ -16,7 +16,7 @@ public class BitmexAuthenticationProvider : AuthenticationProvider { private readonly int LifetimeSeconds; - private static readonly object nonceLock = new object(); + private static readonly object nonceLock = new(); private long lastNonce; public long ApiExpires { @@ -64,7 +64,7 @@ public override void AuthenticateRequest(RestApiClient apiClient, } var dataToSign = CreateAuthPayload(method, uri.PathAndQuery, apiexpires, additionalData); var signedData = Sign(dataToSign); - headers.Add("api-key", Credentials.Key.GetString()); + headers.Add("api-key", _credentials.Key.GetString()); headers.Add("api-expires", apiexpires.ToString(CultureInfo.InvariantCulture)); headers.Add("api-signature", signedData); } diff --git a/Bitmex.Net/BitmexBaseClient.cs b/Bitmex.Net/BitmexBaseClient.cs index 633dc45..c500d33 100644 --- a/Bitmex.Net/BitmexBaseClient.cs +++ b/Bitmex.Net/BitmexBaseClient.cs @@ -1,33 +1,27 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Bitmex.Net.Client; using Bitmex.Net.Client.Helpers.Extensions; -using Bitmex.Net.Client.Objects; using Bitmex.Net.Client.Objects.Requests; +using Bitmex.Net.Objects.Errors; using CryptoExchange.Net; using CryptoExchange.Net.Authentication; -using CryptoExchange.Net.Logging; using CryptoExchange.Net.Objects; -using Newtonsoft.Json.Linq; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; namespace Bitmex.Net { public abstract class BitmexBaseClient : RestApiClient { - protected readonly BitmexClient baseClient; - protected readonly Log log; - protected BitmexBaseClient(string name, BitmexClientOptions options, CryptoExchange.Net.Logging.Log log, BitmexClient client) : base(log,options, options.CommonApiOptions) + protected BitmexBaseClient(ILogger logger, HttpClient? httpClient, BitmexRestOptions options) + : base(logger, httpClient, options.BaseAddress, options, options.CommonApiOptions) { - ExchangeName = name; - baseClient = client; - this.log = log; } - public string ExchangeName { get; } protected Uri GetUrl(string endpoint) { @@ -82,12 +76,13 @@ protected override AuthenticationProvider CreateAuthenticationProvider(ApiCreden { return new BitmexAuthenticationProvider(credentials); } - protected override Error ParseErrorResponse(JToken error) + + protected override Error ParseErrorResponse(int httpStatusCode, IEnumerable>> responseHeaders, string data) { - if (error["error"] != null) + var response = JsonConvert.DeserializeObject(data); + if (response.Error != null) { - var message = error["error"]?.ToString();// $"{(string)error["error"]["name"]}: {(string)error["error"]["message"]}"; - return new BitmexError(42, message, error); + return new ServerError(httpStatusCode, response.Error?.Message, response); } return null; } diff --git a/Bitmex.Net/BitmexBaseTradeClient.cs b/Bitmex.Net/BitmexBaseTradeClient.cs index d1b8d05..5a0c134 100644 --- a/Bitmex.Net/BitmexBaseTradeClient.cs +++ b/Bitmex.Net/BitmexBaseTradeClient.cs @@ -12,8 +12,8 @@ using CryptoExchange.Net; using CryptoExchange.Net.CommonObjects; using CryptoExchange.Net.Interfaces.CommonClients; -using CryptoExchange.Net.Logging; using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Objects.Options; using Microsoft.Extensions.Logging; namespace Bitmex.Net @@ -65,14 +65,18 @@ public abstract class BitmexBaseTradeClient : BitmexBaseClient, IBitmexCommonTra private const string UserEventEndpoint = "userEvent"; private const string WalletAssetsEndpoint = "wallet/assets"; + #endregion - protected BitmexBaseTradeClient(string name, BitmexClientOptions options, Log log, BitmexClient client) : base(name, options, log, client) + protected BitmexBaseTradeClient(ILogger logger, HttpClient httpClient, BitmexRestOptions options) + : base(logger, httpClient, options) { } public event Action OnOrderPlaced; public event Action OnOrderCanceled; + public virtual string ExchangeName => "Kuna"; + #region Execution : Raw Order and Balance Data /// @@ -198,7 +202,7 @@ public async Task>> CancelOrderAsync(CancelOrder { if (!string.IsNullOrEmpty(o.Error)) { - log.Write(LogLevel.Error, $"Order {o.Id} cancelling error: {o.Error}"); + _logger.Log(LogLevel.Error, $"Order {o.Id} cancelling error: {o.Error}"); } else { @@ -235,7 +239,7 @@ Use PlaceOrderAsync() instead of PlaceOrdersBulkAsync(), please.", false)] public async Task>> PlaceOrdersBulkAsync(List placeOrderRequests, CancellationToken ct = default) { placeOrderRequests.ValidateNotNull(nameof(placeOrderRequests)); - List>> results = new List>>(); + List>> results = new(); await Task.Run(() => { foreach (var req in placeOrderRequests) @@ -253,7 +257,7 @@ Use UpdateOrderAsync() instead of UpdateOrdersBulkAsync(), please.", false)] public async Task>> UpdateOrdersBulkAsync(List ordersToUpdate, CancellationToken ct = default) { ordersToUpdate.ValidateNotNull(nameof(ordersToUpdate)); - List>> results = new List>>(); + List>> results = new(); await Task.Run(() => { foreach (var req in ordersToUpdate) @@ -263,7 +267,7 @@ await Task.Run(() => }); foreach (var faulted in results.Where(t => t.Exception != null)) { - log.Write(LogLevel.Error, faulted.Exception.Message); + _logger.Log(LogLevel.Error, faulted.Exception.Message); } return results.FirstOrDefault().Result.As>(results.Select(x => x.Result.Data).ToList()); } @@ -272,8 +276,10 @@ await Task.Run(() => /// public async Task> CancellAllAfterAsync(TimeSpan timeOut, CancellationToken ct = default) { - var parameters = new Dictionary(); - parameters.Add("timeOut", (long)timeOut.TotalMilliseconds); + var parameters = new Dictionary + { + { "timeOut", (long)timeOut.TotalMilliseconds } + }; return await SendRequestAsync(OrderCancelAllAfterEndpoint, HttpMethod.Post, ct, parameters); } diff --git a/Bitmex.Net/BitmexClient.cs b/Bitmex.Net/BitmexClient.cs index cd8a641..7756ae3 100644 --- a/Bitmex.Net/BitmexClient.cs +++ b/Bitmex.Net/BitmexClient.cs @@ -10,6 +10,7 @@ using CryptoExchange.Net; using CryptoExchange.Net.Interfaces.CommonClients; using CryptoExchange.Net.Objects; +using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; namespace Bitmex.Net @@ -18,18 +19,19 @@ public class BitmexClient: BaseRestClient, IBitmexClient { private const string ExchangeName = "Bitmex"; - public BitmexClient() : this(BitmexClientOptions.Default) + public BitmexClient() : this(BitmexRestOptions.Default) { } - public BitmexClient(BitmexClientOptions options) : this(ExchangeName, options) + // public BitmexClient(BitmexClientOptions options) : this(ExchangeName, options) + // { + // } + public BitmexClient(BitmexRestOptions options, ILoggerFactory loggerFactory = null, HttpClient httpClient = null ) : base(loggerFactory, ExchangeName) { - } - public BitmexClient(string name, BitmexClientOptions options) : base(name, options) - { - var opt = options ?? BitmexClientOptions.Default; - SpotClient = AddApiClient(new BitmexSpotClient(name, opt, log, this)); - MarginClient = AddApiClient(new BitmexMarginClient(name, opt, log, this)); - NonTradeFeatureClient = AddApiClient(new BitmexNonTradeFeatureClient(name, opt, log, this)); + var opt = options ?? BitmexRestOptions.Default; + Initialize(opt); + SpotClient = AddApiClient(new BitmexSpotClient(_logger, httpClient, opt)); + MarginClient = AddApiClient(new BitmexMarginClient(_logger, httpClient, opt)); + NonTradeFeatureClient = AddApiClient(new BitmexNonTradeFeatureClient(_logger, httpClient, opt)); } public IBitmexSpotClient SpotClient { get; } diff --git a/Bitmex.Net/BitmexClientOptions.cs b/Bitmex.Net/BitmexClientOptions.cs index 02a9a1a..3ace2b4 100644 --- a/Bitmex.Net/BitmexClientOptions.cs +++ b/Bitmex.Net/BitmexClientOptions.cs @@ -1,76 +1,76 @@ using CryptoExchange.Net.Authentication; -using CryptoExchange.Net.Logging; using CryptoExchange.Net.Objects; -using Microsoft.Extensions.Logging; +using CryptoExchange.Net.Objects.Options; using System; -using System.Collections.Generic; -using System.Net.Http; namespace Bitmex.Net.Client { - public class BitmexClientOptions : ClientOptions + public class BitmexRestOptions : RestExchangeOptions { private const string ProductionEndpoint = "https://www.bitmex.com/api/v1"; private const string TestNetEndpoint = "https://testnet.bitmex.com/api/v1"; - public BitmexClientOptions(HttpClient client, string key, string secret, bool isTest = false, bool outputOriginalData = false) : this(new ApiCredentials(key, secret), isTest, outputOriginalData) + readonly bool isTestnet = false; + public BitmexRestOptions(string key, string secret, bool isTest = false, bool outputOriginalData = false) : this(new ApiCredentials(key, secret), isTest, outputOriginalData) { - CommonApiOptions.HttpClient = client; } - public BitmexClientOptions(HttpClient client) : this(false) + public BitmexRestOptions() : this(null) { - CommonApiOptions.HttpClient = client; } - public BitmexClientOptions(string key, string secret, bool isTest = false, bool outputOriginalData = false) : this(new ApiCredentials(key, secret), isTest, outputOriginalData) + public BitmexRestOptions(bool isTest) : this(null, isTest) { } - - public BitmexClientOptions(bool isTest = false, bool outputOriginalData = false) : base() + + private BitmexRestOptions(bool isTest, bool outputOriginalData) : base() { - CommonApiOptions = new(isTest ? TestNetEndpoint : ProductionEndpoint) { OutputOriginalData = outputOriginalData }; - LogLevel = Microsoft.Extensions.Logging.LogLevel.Debug; + isTestnet = isTest; + CommonApiOptions = new() { OutputOriginalData = outputOriginalData }; } - public BitmexClientOptions(ApiCredentials apiCredentials, bool isTest, bool outputOriginalData = false) : this(isTest, outputOriginalData) + public BitmexRestOptions(ApiCredentials apiCredentials, bool isTest = false, bool outputOriginalData = false, RateLimitingBehaviour rateLimitingBehaviour = RateLimitingBehaviour.Wait) + : this(isTest, outputOriginalData) { ApiCredentials = apiCredentials; - CommonApiOptions.RateLimiters.Add( - new RateLimiter().AddTotalRateLimit( - ApiCredentials is null ? 30 : 120, - TimeSpan.FromMinutes(1)) - ); - CommonApiOptions.RateLimiters.Add( - new RateLimiter().AddEndpointLimit( - BitmexMarginClient.GetEndPointsWithAdditionalRateLimiter(CommonApiOptions.BaseAddress), - 10, - TimeSpan.FromSeconds(1)) - ); - CommonApiOptions.RateLimitingBehaviour = RateLimitingBehaviour.Wait; - } - - // for cloning this instance only - private BitmexClientOptions(BitmexClientOptions baseOn) : base(baseOn) - { - CommonApiOptions = baseOn.CommonApiOptions; + UpdateRateLimiters(); + CommonApiOptions.RateLimitingBehaviour = rateLimitingBehaviour; } - /// + /// /// Default options /// - public static BitmexClientOptions Default { get; set; } = new BitmexClientOptions() + public static BitmexRestOptions Default { get; set; } = new BitmexRestOptions() { }; - internal RestApiClientOptions CommonApiOptions { get; set; } - + internal RestApiOptions CommonApiOptions { get; set; } + internal string BaseAddress => isTestnet ? TestNetEndpoint : ProductionEndpoint; public void SetApiCredentials(ApiCredentials credentials) { ApiCredentials = credentials; + UpdateRateLimiters(); + } + public BitmexRestOptions Copy() + { + var newOne = Copy(); + newOne.CommonApiOptions = CommonApiOptions; + return newOne; } - public BitmexClientOptions Copy() + + private void UpdateRateLimiters() { - return new BitmexClientOptions(this); + CommonApiOptions.RateLimiters.Clear(); + CommonApiOptions.RateLimiters.Add( + new RateLimiter().AddTotalRateLimit( + ApiCredentials is null ? 30 : 120, + TimeSpan.FromMinutes(1)) + ); + CommonApiOptions.RateLimiters.Add( + new RateLimiter().AddEndpointLimit( + BitmexMarginClient.GetEndPointsWithAdditionalRateLimiter(BaseAddress), + 10, + TimeSpan.FromSeconds(1)) + ); } } } diff --git a/Bitmex.Net/BitmexMarginClient.cs b/Bitmex.Net/BitmexMarginClient.cs index 643b82a..e8c8690 100644 --- a/Bitmex.Net/BitmexMarginClient.cs +++ b/Bitmex.Net/BitmexMarginClient.cs @@ -1,17 +1,11 @@ -using Bitmex.Net.Client.Helpers.Extensions; -using Bitmex.Net.Client.Interfaces; +using Bitmex.Net.Client.Interfaces; using Bitmex.Net.Client.Objects; using Bitmex.Net.Client.Objects.Requests; using CryptoExchange.Net; -using CryptoExchange.Net.Authentication; using CryptoExchange.Net.CommonObjects; using CryptoExchange.Net.Interfaces.CommonClients; -using CryptoExchange.Net.Logging; using CryptoExchange.Net.Objects; using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; @@ -22,8 +16,8 @@ namespace Bitmex.Net.Client { public class BitmexMarginClient : BitmexBaseTradeClient, IBitmexMarginClient { - private static BitmexClientOptions defaultOptions = new BitmexClientOptions(); - private static BitmexClientOptions DefaultOptions => defaultOptions.Copy(); + private static readonly BitmexRestOptions defaultOptions = new(); + private static BitmexRestOptions DefaultOptions => defaultOptions.Copy(); #region Endpoints private const string LiquidationEndpoint = "liquidation"; @@ -36,7 +30,8 @@ public class BitmexMarginClient : BitmexBaseTradeClient, IBitmexMarginClient private const string OrderClosePositionEndpoint = "order/closePosition"; #endregion - internal BitmexMarginClient(string name, BitmexClientOptions options, Log log, BitmexClient client) : base(name, options, log, client) + internal BitmexMarginClient(ILogger logger, HttpClient httpClient, BitmexRestOptions opt) + : base(logger, httpClient, opt) { } diff --git a/Bitmex.Net/BitmexNonTradeFeatureClient.cs b/Bitmex.Net/BitmexNonTradeFeatureClient.cs index 0940542..1a1e764 100644 --- a/Bitmex.Net/BitmexNonTradeFeatureClient.cs +++ b/Bitmex.Net/BitmexNonTradeFeatureClient.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Generic; -using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -9,10 +7,8 @@ using Bitmex.Net.Client.Objects; using Bitmex.Net.Client.Objects.Requests; using CryptoExchange.Net; -using CryptoExchange.Net.Authentication; -using CryptoExchange.Net.CommonObjects; -using CryptoExchange.Net.Logging; using CryptoExchange.Net.Objects; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; namespace Bitmex.Net @@ -33,7 +29,8 @@ public class BitmexNonTradeFeatureClient : BitmexBaseClient, IBitmexNonTradeFeat private const string SchemaWebsokcetHelpEndpoint = "schema/websocketHelp"; #endregion - internal BitmexNonTradeFeatureClient(string name, BitmexClientOptions options, Log log, BitmexClient client) : base(name, options, log, client) + internal BitmexNonTradeFeatureClient(ILogger logger, HttpClient httpClient, BitmexRestOptions opt) + : base(logger, httpClient, opt) { } diff --git a/Bitmex.Net/BitmexSocketClient.cs b/Bitmex.Net/BitmexSocketClient.cs index 73151ce..ba3a247 100644 --- a/Bitmex.Net/BitmexSocketClient.cs +++ b/Bitmex.Net/BitmexSocketClient.cs @@ -1,22 +1,16 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Bitmex.Net.Client.Interfaces; using Bitmex.Net.Client.Objects; using Bitmex.Net.Client.Objects.Socket; -using Bitmex.Net.Client.Objects.Socket.Repsonses; using Bitmex.Net.Client.Objects.Socket.Requests; using CryptoExchange.Net; using CryptoExchange.Net.Objects; using CryptoExchange.Net.Sockets; using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace Bitmex.Net.Client { @@ -27,10 +21,11 @@ public BitmexSocketClient() : this(BitmexSocketClientOptions.Default.Copy()) { } - public BitmexSocketClient(BitmexSocketClientOptions options) : base("Bitmex", options) + public BitmexSocketClient(BitmexSocketClientOptions options, ILoggerFactory logger = null) : base(logger, "Bitmex") { - MainSocketStreams = AddApiClient(new BitmexSocketStream(log, this, options)); - NonTradeSocketStreams = AddApiClient(new BitmexSocketStream(log, this, options.CopyWithNonTradeSocketEndpoint())); + Initialize(options.CommonStreamsOptions); + MainSocketStreams = AddApiClient(new BitmexSocketStream(_logger, options)); + NonTradeSocketStreams = AddApiClient(new BitmexSocketStream(_logger, options.Copy())); } public BitmexSocketStream MainSocketStreams { get; set; } public BitmexSocketStream NonTradeSocketStreams { get; set; } diff --git a/Bitmex.Net/BitmexSocketClientOptions.cs b/Bitmex.Net/BitmexSocketClientOptions.cs index 0959fcf..480408b 100644 --- a/Bitmex.Net/BitmexSocketClientOptions.cs +++ b/Bitmex.Net/BitmexSocketClientOptions.cs @@ -1,19 +1,15 @@ using CryptoExchange.Net; -using CryptoExchange.Net.Objects; -using System; -using System.Collections.Generic; -using System.Text; +using CryptoExchange.Net.Objects.Options; namespace Bitmex.Net.Client -{ - public class BitmexSocketClientOptions : ClientOptions +{ + public class BitmexSocketClientOptions : SocketApiOptions { private const string SocketEndpoint = "wss://ws.bitmex.com/realtime"; private const string TestNetSocketEndpoint = "wss://ws.testnet.bitmex.com/realtime"; public bool SendPingManually = false; - - public BitmexSocketClientOptions() : this(false, true) + public BitmexSocketClientOptions() : this(false) { } @@ -23,8 +19,7 @@ public BitmexSocketClientOptions() : this(false, true) /// /// /// - /// now is obsolete and not used anymore, left for compatibility - public BitmexSocketClientOptions(string key, string secret, bool isTestnet = false, bool loadInstrumentIndexes = true) : this(isTestnet, loadInstrumentIndexes) + public BitmexSocketClientOptions(string key, string secret, bool isTestnet = false) : this(isTestnet) { key.ValidateNotNull(nameof(key)); secret.ValidateNotNull(nameof(secret)); @@ -35,42 +30,32 @@ public BitmexSocketClientOptions(string key, string secret, bool isTestnet = fal /// /// /// - /// now is obsolete and not used anymore, left for compatibility - public BitmexSocketClientOptions(bool isTestnet, bool loadInstrumentIndexes=true, SocketApiClientOptions commonStreamsOptions = null) : base() + public BitmexSocketClientOptions(bool isTestnet, SocketExchangeOptions commonStreamsOptions = null) : base() { - CommonStreamsOptions = commonStreamsOptions ?? new SocketApiClientOptions(isTestnet ? TestNetSocketEndpoint : SocketEndpoint); + CommonStreamsOptions = commonStreamsOptions ?? new(); IsTestnet = isTestnet; } - private BitmexSocketClientOptions(BitmexSocketClientOptions baseOn) : base(baseOn) - { - IsTestnet = baseOn.IsTestnet; - SendPingManually = baseOn.SendPingManually; - CommonStreamsOptions = baseOn.CommonStreamsOptions; - } - /// /// Default options /// - public static BitmexSocketClientOptions Default { get; set; } = new BitmexSocketClientOptions() - { - }; - public SocketApiClientOptions CommonStreamsOptions { get; private set; } + public static BitmexSocketClientOptions Default { get; set; } = new BitmexSocketClientOptions(); + public SocketExchangeOptions CommonStreamsOptions { get; private set; } public bool IsTestnet { get; private set; } + internal virtual string BaseAddress => IsTestnet ? TestNetSocketEndpoint : SocketEndpoint; - - public BitmexSocketClientOptions Copy() + public BitmexSocketClientOptions Copy() => Copy(); + public new BitmexSocketClientOptions Copy() { - return new BitmexSocketClientOptions(this); + var newOpt = base.Copy(); + newOpt.IsTestnet = IsTestnet; + newOpt.SendPingManually = SendPingManually; + newOpt.CommonStreamsOptions = CommonStreamsOptions; + return newOpt; } - - internal BitmexSocketClientOptions CopyWithNonTradeSocketEndpoint() - { - return new BitmexSocketClientOptions(this) - { - CommonStreamsOptions = new(this.CommonStreamsOptions, new($"{this.CommonStreamsOptions.BaseAddress}Platform")) - }; - } - + } + internal class BitmexNonTradeSocketClientOptions :BitmexSocketClientOptions + { + internal override string BaseAddress => $"{base.BaseAddress}Platform"; } } diff --git a/Bitmex.Net/BitmexSocketOrderBookOptions.cs b/Bitmex.Net/BitmexSocketOrderBookOptions.cs index 8e34022..c85f08c 100644 --- a/Bitmex.Net/BitmexSocketOrderBookOptions.cs +++ b/Bitmex.Net/BitmexSocketOrderBookOptions.cs @@ -1,7 +1,4 @@ -using CryptoExchange.Net.Objects; -using System; -using System.Collections.Generic; -using System.Text; +using CryptoExchange.Net.Objects.Options; namespace Bitmex.Net.Client { @@ -15,10 +12,6 @@ public class BitmexSocketOrderBookOptions : OrderBookOptions /// This value is used for price calculation by orderbook entry id. Set it carefully /// public readonly decimal? TickSize; - /// - /// This value is used for price calculation by orderbook entry id. Set it carefully. - /// - public readonly int? InstrumentIndex; /// /// @@ -34,12 +27,10 @@ public class BitmexSocketOrderBookOptions : OrderBookOptions /// these instruments, you must use their original tick as part of your calculations. If not, this can be ignored, /// and you can use `instrument.tickSize` directly. For example, XBTUSD has 88 index and tick size returned by api =0.5, but to calculate price at orderbookL2 update you should use 0.01. this value is hardcoded /// - /// Used for price calculation. Bitmex docs - public BitmexSocketOrderBookOptions(string name, bool isTest = false, int? instrumentIndex=null, decimal? tickSize = null) : base() + public BitmexSocketOrderBookOptions(bool isTest = false, decimal? tickSize = null) : base() { IsTestnet = isTest; TickSize = tickSize; - InstrumentIndex = instrumentIndex; } } } diff --git a/Bitmex.Net/BitmexSocketStream.cs b/Bitmex.Net/BitmexSocketStream.cs index ca26428..46480f7 100644 --- a/Bitmex.Net/BitmexSocketStream.cs +++ b/Bitmex.Net/BitmexSocketStream.cs @@ -1,13 +1,9 @@ -using Bitmex.Net.Client.Helpers.Extensions; -using Bitmex.Net.Client.Interfaces; -using Bitmex.Net.Client.Objects; +using Bitmex.Net.Client.Objects; using Bitmex.Net.Client.Objects.Socket; using Bitmex.Net.Client.Objects.Socket.Repsonses; using Bitmex.Net.Client.Objects.Socket.Requests; using CryptoExchange.Net; using CryptoExchange.Net.Authentication; -using CryptoExchange.Net.Interfaces; -using CryptoExchange.Net.Logging; using CryptoExchange.Net.Objects; using CryptoExchange.Net.Sockets; using Microsoft.Extensions.Logging; @@ -27,21 +23,15 @@ namespace Bitmex.Net.Client { public class BitmexSocketStream : SocketApiClient { - private static readonly Dictionary instrumentsIndexesAndTicks = new Dictionary(); private static readonly SemaphoreSlim instumentGetWaiter = new(1,1); private static bool areInstrumentsLoaded; private readonly ConcurrentDictionary _sendedSubscriptions = new ConcurrentDictionary(); private readonly List _subscriptions = new List(); - protected Log log; protected BitmexSocketClient socketClient; - private readonly bool isTestnet; private object _locker = new object(); - public BitmexSocketStream(Log log, BitmexSocketClient bitmexSocketClient, BitmexSocketClientOptions options) : base(log, options, options.CommonStreamsOptions) + internal BitmexSocketStream(ILogger logger, BitmexSocketClientOptions options) : base(logger, options.BaseAddress, options.CommonStreamsOptions, options) { - isTestnet = options.IsTestnet; - this.log = log; - this.socketClient = bitmexSocketClient; } #region events @@ -79,34 +69,8 @@ public BitmexSocketStream(Log log, BitmexSocketClient bitmexSocketClient, Bitmex #endregion - // protected override IWebsocket CreateSocket(string address) - // { - // Dictionary emptyCoockies = new Dictionary(); - // Dictionary headers = new Dictionary(); - // //if (authProvider != null) - // //{ - // // headers = this.authProvider.AddAuthenticationToHeaders("bitmex.com/realtime", HttpMethod.Get, null, true, PostParameters.InUri, ArrayParametersSerialization.MultipleValues); - // //} - // headers.Add("Accept-Encoding", "gzip, deflate, br"); - // headers.Add("Cache-Control", "no-cache"); - // headers.Add("Connection", "Upgrade"); - // headers.Add("Host", $"{(isTestnet ? "testnet" : "www")}.bitmex.com"); - // headers.Add("Origin", $"https://{(isTestnet ? "testnet" : "www")}.bitmex.com"); - // headers.Add("Sec-WebSocket-Extensions", ""); - // headers.Add("Sec-WebSocket-Version", "13"); - // headers.Add("Upgrade", "websocket"); - // headers.Add("User-Agent", "https://github.com/ridicoulous/Bitmex.Net/"); - - // var s = SocketFactory.CreateWebsocket(this.log, address, emptyCoockies, headers); - // s.Origin = $"https://{(isTestnet ? "testnet" : "www")}.bitmex.com"; - // s.OnClose += S_OnClose; - // s.OnError += S_OnError; - // s.OnOpen += S_OnOpen; - // return s; - // } - - - + internal ILogger Logger => _logger; + public override async Task UnsubscribeAllAsync() { foreach (var c in this._subscriptions) @@ -124,7 +88,7 @@ public async Task> SubscribeToOrderBookUpdatesAsy return await SubscribeAsync(new BitmexSubscribeRequest().AddSubscription(orderbookType, symbol), onData, ct); } - ResponseTableToDataTypeMapping Map = new ResponseTableToDataTypeMapping(); + ResponseTableToDataTypeMapping Map = new(); public async Task> SubscribeAsync(BitmexSubscribeRequest bitmexSubscribeRequest, CancellationToken ct = default) { @@ -154,7 +118,7 @@ public async Task> SubscribeAsync(BitmexSubscribe } else { - log.Write(LogLevel.Warning, $"Unknown table [{table}] update catched at data {data}"); + _logger.Log(LogLevel.Warning, $"Unknown table [{table}] update catched at data {data}"); return; } } @@ -168,7 +132,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnAnnouncementUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.Chat: @@ -177,7 +141,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnChatMessageUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.Connected: @@ -186,7 +150,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnChatConnectionUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.Funding: @@ -195,7 +159,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnFundingUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.Instrument: @@ -204,7 +168,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnInstrimentUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.Insurance: @@ -213,7 +177,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnInsuranceUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.Liquidation: @@ -222,7 +186,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnLiquidationUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.OrderBookL2_25: @@ -233,7 +197,7 @@ public async Task> SubscribeAsync(BitmexSubscribe OnOrderBookL2_25Update?.Invoke(result.Data); } else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.OrderBookL2: @@ -244,7 +208,7 @@ public async Task> SubscribeAsync(BitmexSubscribe OnorderBookL2Update?.Invoke(result.Data); } else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.OrderBook10: @@ -253,7 +217,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnOrderBook10Update?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.PublicNotifications: @@ -262,7 +226,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnGlobalNotificationUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.Quote: @@ -271,7 +235,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnQuotesUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.QuoteBin1m: @@ -280,7 +244,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnOneMinuteQuoteBinUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.QuoteBin5m: @@ -289,7 +253,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnFiveMinuteQuoteBinUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.QuoteBin1h: @@ -298,7 +262,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnOneHourQuoteBinUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.QuoteBin1d: @@ -307,7 +271,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnDailyQuoteBinUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.Settlement: @@ -316,7 +280,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnSettlementUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.Trade: @@ -325,7 +289,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnTradeUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.TradeBin1m: @@ -334,7 +298,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnOneMinuteTradeBinUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.TradeBin5m: @@ -343,7 +307,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnFiveMinuteTradeBinUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.TradeBin1h: @@ -352,7 +316,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnOneHourTradeBinUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.TradeBin1d: @@ -361,7 +325,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnDailyTradeBinUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.Affiliate: @@ -370,7 +334,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnUserAffiliatesUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.Execution: @@ -379,7 +343,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnUserExecutionsUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.Order: @@ -388,7 +352,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnUserOrdersUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.Margin: @@ -397,7 +361,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnUserMarginUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.Position: @@ -406,7 +370,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnUserPositionsUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.Transact: @@ -415,7 +379,7 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnUserTransactionsUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } case BitmexSubscribtions.Wallet: @@ -424,12 +388,12 @@ public async Task> SubscribeAsync(BitmexSubscribe if (result.Success) OnUserWalletUpdate?.Invoke(result.Data); else - log.Write(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); + _logger.Log(LogLevel.Warning, "Couldn't deserialize data received from stream: " + result.Error); break; } default: { - log.Write(LogLevel.Warning, $"Catched inknown table update: {data}"); + _logger.Log(LogLevel.Warning, $"Catched inknown table update: {data}"); break; } } @@ -444,11 +408,11 @@ private async Task> SubscribeAsync(BitmexSubsc private void OnUnsubscribe(BitmexSubscriptionResponse response) { - log.Write(LogLevel.Debug, $"Unsub: {JsonConvert.SerializeObject(response)}"); + _logger.Log(LogLevel.Debug, $"Unsub: {JsonConvert.SerializeObject(response)}"); if (!String.IsNullOrEmpty(response.Unsubscribe)) { // _sendedSubscriptions.TryRemove(response.Unsubscribe, out _); - log.Write(LogLevel.Warning, $"{response.Unsubscribe} topic from {JsonConvert.SerializeObject(response.Request)} subscription was unsubscribed"); + _logger.Log(LogLevel.Warning, $"{response.Unsubscribe} topic from {JsonConvert.SerializeObject(response.Request)} subscription was unsubscribed"); } } @@ -468,13 +432,13 @@ protected override async Task> AuthenticateSocketAsync(SocketCo bool isSuccess = false; ServerError serverError = null; var authRequest = new BitmexSubscribeRequest() { Op = BitmexWebSocketOperation.AuthKeyExpires }; - var authParams = ((BitmexAuthenticationProvider)socketConnection.ApiClient.AuthenticationProvider); + var authParams = (BitmexAuthenticationProvider)socketConnection.ApiClient.AuthenticationProvider; // request = {"op": "authKeyExpires", "args": [API_KEY, expires, signature]} var expires = authParams.ApiExpires; - authRequest.Args.Add(authParams.Credentials.Key.GetString()); + authRequest.Args.Add(ApiOptions.ApiCredentials.Key.GetString()); authRequest.Args.Add(expires); authRequest.Args.Add(authParams.Sign(authParams.CreateAuthPayload(HttpMethod.Get, "/realtime", expires))); - await socketConnection.SendAndWaitAsync(authRequest, TimeSpan.FromSeconds(1), null, token => + await socketConnection.SendAndWaitAsync(authRequest, TimeSpan.FromSeconds(1), null, 1, token => { if (String.IsNullOrEmpty(token.ToString())) { @@ -519,7 +483,7 @@ protected override bool HandleSubscriptionResponse(SocketConnection socketConnec } if (message["info"] != null && ((string)message["info"]).StartsWith("Welcome")) { - log.Write(LogLevel.Debug, "skipping welcome message by request"); + _logger.Log(LogLevel.Debug, "skipping welcome message by request"); return false; } if (message.Type == JTokenType.String && (string)message == "pong") @@ -547,12 +511,12 @@ protected override bool MessageMatchesHandler(SocketConnection socketConnection, } if (message["info"] != null && ((string)message["info"]).StartsWith("Welcome")) { - log.Write(LogLevel.Trace, "skipping welcome message by request"); + _logger.Log(LogLevel.Trace, "skipping welcome message by request"); return false; } if (message["error"]?.ToString()?.StartsWith("Not subscribed") == true) { - log.Write(LogLevel.Debug, "Seems we sent an unsubscribe request, but have already unsubscribed"); + _logger.Log(LogLevel.Debug, "Seems we sent an unsubscribe request, but have already unsubscribed"); return false; } return true; @@ -567,7 +531,7 @@ protected override bool MessageMatchesHandler(SocketConnection socketConnection, } if (message["info"] != null && ((string)message["info"]).StartsWith("Welcome")) { - log.Write(LogLevel.Debug, "skipping welcome message by id"); + _logger.Log(LogLevel.Debug, "skipping welcome message by id"); return false; } return true; @@ -577,16 +541,16 @@ protected async override Task UnsubscribeAsync(SocketConnection connection { if (subscription.Request is BitmexSubscribeRequest subReq) { - var result = await QueryAndWaitAsync(connection, subReq.CreateUnsubscribeRequest()); + var result = await QueryAndWaitAsync(connection, subReq.CreateUnsubscribeRequest(), 1); if (result && result.Data.Success && result.Data.Request.Args.Contains(result.Data.Unsubscribe)) { _sendedSubscriptions.TryRemove(result.Data.Unsubscribe, out _); - log.Write(LogLevel.Trace, $"{JsonConvert.SerializeObject(subscription.Request)} subscription was unsubscribed"); + _logger.Log(LogLevel.Trace, $"{JsonConvert.SerializeObject(subscription.Request)} subscription was unsubscribed"); return true; } else { - log.Write(LogLevel.Debug, $"{JsonConvert.SerializeObject(subscription.Request)} subscription was not unsubscribed: {result.Error?.Message}"); + _logger.Log(LogLevel.Debug, $"{JsonConvert.SerializeObject(subscription.Request)} subscription was not unsubscribed: {result.Error?.Message}"); } } return false; @@ -625,7 +589,7 @@ private void CheckDoubleSendingRequest(BitmexSubscribeRequest request) } if (toRemove.Any()) { - log.Write(LogLevel.Warning, $"Not sending another subscribe request for topics: {String.Join(',', toRemove)}, cause it was already sended"); + _logger.Log(LogLevel.Warning, $"Not sending another subscribe request for topics: {String.Join(',', toRemove)}, cause it was already sended"); request.Args.RemoveAll(c => toRemove.Contains(c)); } } @@ -641,7 +605,7 @@ internal async Task> SubscribeInternal CheckDoubleSendingRequest(request); if (!request.Args.Any()) { - log.Write(LogLevel.Warning, $"Not sending empty request {JsonConvert.SerializeObject(request)}"); + _logger.Log(LogLevel.Warning, $"Not sending empty request {JsonConvert.SerializeObject(request)}"); return new CallResult(new ServerError("Not sending empty request ", request)); } request.Args.ValidateNotNull(nameof(request)); @@ -650,7 +614,7 @@ internal async Task> SubscribeInternal ( url, request, - url + NextId(), + null, authenticate, onData, ct).ConfigureAwait(false); diff --git a/Bitmex.Net/BitmexSpotClient.cs b/Bitmex.Net/BitmexSpotClient.cs index bc59583..ab664cd 100644 --- a/Bitmex.Net/BitmexSpotClient.cs +++ b/Bitmex.Net/BitmexSpotClient.cs @@ -1,19 +1,8 @@ -using Bitmex.Net.Client.Helpers.Extensions; using Bitmex.Net.Client.Interfaces; -using Bitmex.Net.Client.Objects; -using Bitmex.Net.Client.Objects.Requests; -using CryptoExchange.Net; -using CryptoExchange.Net.Authentication; using CryptoExchange.Net.CommonObjects; using CryptoExchange.Net.Interfaces.CommonClients; -using CryptoExchange.Net.Logging; using CryptoExchange.Net.Objects; using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -22,7 +11,8 @@ namespace Bitmex.Net.Client { public class BitmexSpotClient : BitmexBaseTradeClient, IBitmexSpotClient { - internal BitmexSpotClient(string name, BitmexClientOptions options, Log log, BitmexClient client) : base(name, options, log, client) + internal BitmexSpotClient(ILogger logger, HttpClient httpClient, BitmexRestOptions opt) + : base(logger, httpClient, opt) { } diff --git a/Bitmex.Net/BitmexSymbolOrderBook.cs b/Bitmex.Net/BitmexSymbolOrderBook.cs index a94597c..dd23d04 100644 --- a/Bitmex.Net/BitmexSymbolOrderBook.cs +++ b/Bitmex.Net/BitmexSymbolOrderBook.cs @@ -14,7 +14,7 @@ namespace Bitmex.Net.Client { public class BitmexSymbolOrderBook : SymbolOrderBook { - private static BitmexSocketOrderBookOptions defaultOrderBookOptions = new BitmexSocketOrderBookOptions("BitmexOrderBook"); + private static BitmexSocketOrderBookOptions defaultOrderBookOptions = new BitmexSocketOrderBookOptions(); private readonly BitmexSocketStream _bitmexSocketStream; private bool usedNewSocketClient; private readonly decimal InstrumentTickSize; @@ -45,22 +45,26 @@ protected long NextId() return lastId; } } - public BitmexSymbolOrderBook(string symbol, bool isTest = false) : this(symbol, defaultOrderBookOptions) + + public BitmexSymbolOrderBook(string symbol, ILogger logger, bool isTest = false) : base(logger, $"Bitmex-{symbol}", symbol) { + isTestnet = isTest; + } /// - /// AttentioN! For price calculation at order book update you should use level id and and instrument index - /// example, XBTUSD has 88 index and tick size returned by api =0.5, but to calculate price at orderbookL2 update you should use 0.01. this value is hardcoded + /// /// /// /// /// - public BitmexSymbolOrderBook(string symbol, BitmexSocketOrderBookOptions options, BitmexSocketClient bitmexSocketClient = null) : base("Bitmex", symbol, options) + public BitmexSymbolOrderBook(string symbol, BitmexSocketOrderBookOptions options, BitmexSocketClient bitmexSocketClient = null) + : base(bitmexSocketClient?.MainSocketStreams?.Logger, $"Bitmex-{symbol}", symbol) { + Initialize(options); isTestnet = options.IsTestnet; usedNewSocketClient = bitmexSocketClient is null; - var mainClient = bitmexSocketClient ?? new BitmexSocketClient(new BitmexSocketClientOptions(options.IsTestnet){LogLevel = options.LogLevel}); - _bitmexSocketStream = (BitmexSocketStream)mainClient.MainSocketStreams; + var mainClient = bitmexSocketClient ?? new BitmexSocketClient(new BitmexSocketClientOptions(options.IsTestnet)); + _bitmexSocketStream = mainClient.MainSocketStreams; } protected override void Dispose(bool disposing) @@ -141,10 +145,10 @@ private void Update(List entries) } else { - log.Write(LogLevel.Error, $"Orderbook was not updated cause not initiated"); - using (var client = new BitmexClient(new BitmexClientOptions(isTestnet))) + _logger.Log(LogLevel.Error, $"Orderbook was not updated cause not initiated"); + using (var client = new BitmexClient(new BitmexRestOptions(isTestnet))) { - log.Write(LogLevel.Debug, $"Setting orderdbook through api"); + _logger.Log(LogLevel.Debug, $"Setting orderdbook through api"); var ob = client.MarginClient.GetOrderBookAsync(Symbol, 0).GetAwaiter().GetResult(); if (ob) @@ -157,7 +161,7 @@ private void Update(List entries) } catch (Exception ex) { - log.Write(LogLevel.Error, $"Orderbook was not updated {ex.ToString()}"); + _logger.Log(LogLevel.Error, $"Orderbook was not updated {ex.ToString()}"); } } } diff --git a/Bitmex.Net/Objects/BitmexError.cs b/Bitmex.Net/Objects/BitmexError.cs deleted file mode 100644 index 60e0128..0000000 --- a/Bitmex.Net/Objects/BitmexError.cs +++ /dev/null @@ -1,24 +0,0 @@ -using CryptoExchange.Net.Objects; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Bitmex.Net.Client.Objects -{ - public class BitmexError : Error - { - public BitmexError(int code, string message, object data) : base(code, message, data) - { - } - } - //public class Error - //{ - // public string message { get; set; } - // public string name { get; set; } - //} - - //public class RootObject - //{ - // public Error error { get; set; } - //} -} diff --git a/Bitmex.Net/Objects/Errors/BitmexErrorResponse.cs b/Bitmex.Net/Objects/Errors/BitmexErrorResponse.cs new file mode 100644 index 0000000..02db432 --- /dev/null +++ b/Bitmex.Net/Objects/Errors/BitmexErrorResponse.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Bitmex.Net.Objects.Errors +{ + internal class BitmexErrorResponse + { + [JsonProperty("error")] + public BitmexError Error { get; set; } + } + + public class BitmexError + { + [JsonProperty("message")] + public string Message { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + } +} \ No newline at end of file