Skip to content

Commit

Permalink
upt to CryptoExchange.Net v6.1.4
Browse files Browse the repository at this point in the history
  • Loading branch information
alokym86 committed Sep 27, 2023
1 parent 5927576 commit aa0788b
Show file tree
Hide file tree
Showing 17 changed files with 210 additions and 300 deletions.
26 changes: 7 additions & 19 deletions Bitmex.Net.ClientExample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -37,7 +28,7 @@ static void onData(DataEvent<BitmexSocketEvent<BitmexOrderBookEntry>> 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();
Expand All @@ -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
Expand All @@ -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,
Expand All @@ -75,18 +66,15 @@ 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
socket.OnUserOrdersUpdate += Socket_OnUserOrdersUpdate; //add action that should be do on orders updates
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
Expand Down Expand Up @@ -122,7 +110,7 @@ private static void S_OnTradeUpdate(BitmexSocketEvent<BitmexTrade> 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} ");
}


Expand Down
3 changes: 1 addition & 2 deletions Bitmex.Net/Bitmex.Net.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CryptoExchange.Net" Version="5.4.3" />
<PackageReference Include="CryptoExchange.Net" Version="6.1.4" />
<PackageReference Include="CsvHelper" Version="27.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>


Expand Down
4 changes: 2 additions & 2 deletions Bitmex.Net/BitmexAuthenticationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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);
}
Expand Down
25 changes: 10 additions & 15 deletions Bitmex.Net/BitmexBaseClient.cs
Original file line number Diff line number Diff line change
@@ -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)
{
Expand Down Expand Up @@ -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<KeyValuePair<string, IEnumerable<string>>> responseHeaders, string data)
{
if (error["error"] != null)
var response = JsonConvert.DeserializeObject<BitmexErrorResponse>(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;
}
Expand Down
22 changes: 14 additions & 8 deletions Bitmex.Net/BitmexBaseTradeClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<OrderId> OnOrderPlaced;
public event Action<OrderId> OnOrderCanceled;

public virtual string ExchangeName => "Kuna";

#region Execution : Raw Order and Balance Data

///<inheritdoc/>
Expand Down Expand Up @@ -198,7 +202,7 @@ public async Task<WebCallResult<List<BitmexOrder>>> 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
{
Expand Down Expand Up @@ -235,7 +239,7 @@ Use PlaceOrderAsync() instead of PlaceOrdersBulkAsync(), please.", false)]
public async Task<WebCallResult<List<BitmexOrder>>> PlaceOrdersBulkAsync(List<PlaceOrderRequest> placeOrderRequests, CancellationToken ct = default)
{
placeOrderRequests.ValidateNotNull(nameof(placeOrderRequests));
List<Task<WebCallResult<BitmexOrder>>> results = new List<Task<WebCallResult<BitmexOrder>>>();
List<Task<WebCallResult<BitmexOrder>>> results = new();
await Task.Run(() =>
{
foreach (var req in placeOrderRequests)
Expand All @@ -253,7 +257,7 @@ Use UpdateOrderAsync() instead of UpdateOrdersBulkAsync(), please.", false)]
public async Task<WebCallResult<List<BitmexOrder>>> UpdateOrdersBulkAsync(List<UpdateOrderRequest> ordersToUpdate, CancellationToken ct = default)
{
ordersToUpdate.ValidateNotNull(nameof(ordersToUpdate));
List<Task<WebCallResult<BitmexOrder>>> results = new List<Task<WebCallResult<BitmexOrder>>>();
List<Task<WebCallResult<BitmexOrder>>> results = new();
await Task.Run(() =>
{
foreach (var req in ordersToUpdate)
Expand All @@ -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<List<BitmexOrder>>(results.Select(x => x.Result.Data).ToList());
}
Expand All @@ -272,8 +276,10 @@ await Task.Run(() =>
///<inheritdoc/>
public async Task<WebCallResult<object>> CancellAllAfterAsync(TimeSpan timeOut, CancellationToken ct = default)
{
var parameters = new Dictionary<string, object>();
parameters.Add("timeOut", (long)timeOut.TotalMilliseconds);
var parameters = new Dictionary<string, object>
{
{ "timeOut", (long)timeOut.TotalMilliseconds }
};
return await SendRequestAsync<object>(OrderCancelAllAfterEndpoint, HttpMethod.Post, ct, parameters);
}

Expand Down
20 changes: 11 additions & 9 deletions Bitmex.Net/BitmexClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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; }
Expand Down
78 changes: 39 additions & 39 deletions Bitmex.Net/BitmexClientOptions.cs
Original file line number Diff line number Diff line change
@@ -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;
}

/// <summary>
/// <summary>
/// Default options
/// </summary>
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<BitmexRestOptions>();
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))
);
}
}
}
Loading

0 comments on commit aa0788b

Please sign in to comment.