Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upt to CryptoExchange.Net v6.1.4 #45

Merged
merged 1 commit into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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