Skip to content

Commit

Permalink
Trackers (#241)
Browse files Browse the repository at this point in the history
Updated examples
Updated CryptoExchange.Net to v8.1.0
Moved FormatSymbol to BybitExchange class
Added support Side setting on SharedTrade model
Added BybitTrackerFactory
Added overload to Create method on BybitOrderBookFactory support SharedSymbol parameter
  • Loading branch information
JKorf authored Oct 28, 2024
1 parent de06c1e commit 5b99008
Show file tree
Hide file tree
Showing 21 changed files with 455 additions and 52 deletions.
2 changes: 1 addition & 1 deletion ByBit.Net/Bybit.Net.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="CryptoExchange.Net" Version="8.1.0" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="CryptoExchange.Net" Version="8.0.3" />
</ItemGroup>
</Project>
35 changes: 34 additions & 1 deletion ByBit.Net/BybitExchange.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
namespace Bybit.Net
using CryptoExchange.Net;
using CryptoExchange.Net.SharedApis;
using System;

namespace Bybit.Net
{
/// <summary>
/// Bybit exchange information and configuration
Expand All @@ -22,5 +26,34 @@ public static class BybitExchange
"https://bybit-exchange.github.io/docs/v3/intro",
"https://bybit-exchange.github.io/docs/v5/intro"
};

/// <summary>
/// Format a base and quote asset to a Bybit recognized symbol
/// </summary>
/// <param name="baseAsset">Base asset</param>
/// <param name="quoteAsset">Quote asset</param>
/// <param name="tradingMode">Trading mode</param>
/// <param name="deliverTime">Delivery time for delivery futures</param>
/// <returns></returns>
public static string FormatSymbol(string baseAsset, string quoteAsset, TradingMode tradingMode, DateTime? deliverTime = null)
{
if (tradingMode == TradingMode.Spot)
return baseAsset.ToUpperInvariant() + quoteAsset.ToUpperInvariant();

if (tradingMode.IsLinear())
{
if (tradingMode.IsPerpetual())
{
if (quoteAsset == "USDC")
return baseAsset + "PERP";

return baseAsset.ToUpperInvariant() + quoteAsset.ToUpperInvariant();
}

return baseAsset.ToUpperInvariant() + "-" + deliverTime!.Value.ToString("ddMMMyy").ToUpperInvariant();
}

return baseAsset.ToUpperInvariant() + quoteAsset.ToUpperInvariant() + (deliverTime == null ? string.Empty : (ExchangeHelpers.GetDeliveryMonthSymbol(deliverTime.Value) + deliverTime.Value.ToString("yy")));
}
}
}
105 changes: 105 additions & 0 deletions ByBit.Net/BybitTrackerFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using Bybit.Net.Clients;
using Bybit.Net.Interfaces;
using Bybit.Net.Interfaces.Clients;
using CryptoExchange.Net;
using CryptoExchange.Net.SharedApis;
using CryptoExchange.Net.Trackers.Klines;
using CryptoExchange.Net.Trackers.Trades;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;

namespace Bybit.Net
{
/// <inheritdoc />
public class BybitTrackerFactory : IBybitTrackerFactory
{
private readonly IServiceProvider? _serviceProvider;

/// <summary>
/// ctor
/// </summary>
public BybitTrackerFactory()
{
}

/// <summary>
/// ctor
/// </summary>
/// <param name="serviceProvider">Service provider for resolving logging and clients</param>
public BybitTrackerFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}

/// <inheritdoc />
public IKlineTracker CreateKlineTracker(SharedSymbol symbol, SharedKlineInterval interval, int? limit = null, TimeSpan? period = null)
{
var restClient = _serviceProvider?.GetRequiredService<IBybitRestClient>() ?? new BybitRestClient();
var socketClient = _serviceProvider?.GetRequiredService<IBybitSocketClient>() ?? new BybitSocketClient();

IKlineRestClient sharedRestClient;
IKlineSocketClient sharedSocketClient;
if (symbol.TradingMode == TradingMode.Spot)
{
sharedRestClient = restClient.V5Api.SharedClient;
sharedSocketClient = socketClient.V5SpotApi.SharedClient;
}
else if (symbol.TradingMode.IsLinear())
{
sharedRestClient = restClient.V5Api.SharedClient;
sharedSocketClient = socketClient.V5LinearApi.SharedClient;
}
else
{
sharedRestClient = restClient.V5Api.SharedClient;
sharedSocketClient = socketClient.V5InverseApi.SharedClient;
}

return new KlineTracker(
_serviceProvider?.GetRequiredService<ILoggerFactory>().CreateLogger(restClient.Exchange),
sharedRestClient,
sharedSocketClient,
symbol,
interval,
limit,
period
);
}

/// <inheritdoc />
public ITradeTracker CreateTradeTracker(SharedSymbol symbol, int? limit = null, TimeSpan? period = null)
{
var restClient = _serviceProvider?.GetRequiredService<IBybitRestClient>() ?? new BybitRestClient();
var socketClient = _serviceProvider?.GetRequiredService<IBybitSocketClient>() ?? new BybitSocketClient();

IRecentTradeRestClient? sharedRestClient = null;
ITradeSocketClient sharedSocketClient;
if (symbol.TradingMode == TradingMode.Spot)
{
sharedRestClient = restClient.V5Api.SharedClient;
sharedSocketClient = socketClient.V5SpotApi.SharedClient;
}
else if (symbol.TradingMode.IsLinear())
{
sharedRestClient = restClient.V5Api.SharedClient;
sharedSocketClient = socketClient.V5LinearApi.SharedClient;
}
else
{
sharedRestClient = restClient.V5Api.SharedClient;
sharedSocketClient = socketClient.V5InverseApi.SharedClient;
}

return new TradeTracker(
_serviceProvider?.GetRequiredService<ILoggerFactory>().CreateLogger(socketClient.Exchange),
sharedRestClient,
null,
sharedSocketClient,
symbol,
limit,
period
);
}
}
}
19 changes: 1 addition & 18 deletions ByBit.Net/Clients/V5/BybitRestClientApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,24 +80,7 @@ protected override AuthenticationProvider CreateAuthenticationProvider(ApiCreden

/// <inheritdoc />
public override string FormatSymbol(string baseAsset, string quoteAsset, TradingMode tradingMode, DateTime? deliverTime = null)
{
if (tradingMode == TradingMode.Spot)
return baseAsset.ToUpperInvariant() + quoteAsset.ToUpperInvariant();

if (tradingMode.IsLinear()) {
if (tradingMode.IsPerpetual())
{
if (quoteAsset == "USDC")
return baseAsset + "PERP";

return baseAsset.ToUpperInvariant() + quoteAsset.ToUpperInvariant();
}

return baseAsset.ToUpperInvariant() + "-" + deliverTime!.Value.ToString("ddMMMyy").ToUpperInvariant();
}

return baseAsset.ToUpperInvariant() + quoteAsset.ToUpperInvariant() + (deliverTime == null ? string.Empty : (ExchangeHelpers.GetDeliveryMonthSymbol(deliverTime.Value) + deliverTime.Value.ToString("yy")));
}
=> BybitExchange.FormatSymbol(baseAsset, quoteAsset, tradingMode, deliverTime);

/// <summary>
/// Get url for an endpoint
Expand Down
5 changes: 4 additions & 1 deletion ByBit.Net/Clients/V5/BybitRestClientApiShared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,10 @@ async Task<ExchangeWebResult<IEnumerable<SharedTrade>>> IRecentTradeRestClient.G
if (!result)
return result.AsExchangeResult<IEnumerable<SharedTrade>>(Exchange, null, default);

return result.AsExchangeResult<IEnumerable<SharedTrade>>(Exchange, request.Symbol.TradingMode, result.Data.List.Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)).ToArray());
return result.AsExchangeResult<IEnumerable<SharedTrade>>(Exchange, request.Symbol.TradingMode, result.Data.List.Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)
{
Side = x.Side == OrderSide.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell
}).ToArray());
}

#endregion
Expand Down
17 changes: 1 addition & 16 deletions ByBit.Net/Clients/V5/BybitSocketClientBaseApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,9 @@ internal BybitSocketClientBaseApi(ILogger log, BybitSocketOptions options, strin
KeepAliveInterval = TimeSpan.Zero;
}

/// <inheritdoc />

/// <inheritdoc />
public override string FormatSymbol(string baseAsset, string quoteAsset, TradingMode tradingMode, DateTime? deliverTime = null)
{
if (tradingMode == TradingMode.Spot)
return baseAsset.ToUpperInvariant() + quoteAsset.ToUpperInvariant();

if (tradingMode.IsLinear())
{
if (tradingMode.IsPerpetual())
return baseAsset.ToUpperInvariant() + quoteAsset.ToUpperInvariant();

return baseAsset.ToUpperInvariant() + "-" + deliverTime!.Value.ToString("ddMMMyy").ToUpperInvariant();
}

return baseAsset.ToUpperInvariant() + quoteAsset.ToUpperInvariant() + (deliverTime == null ? string.Empty : (ExchangeHelpers.GetDeliveryMonthSymbol(deliverTime.Value) + deliverTime.Value.ToString("yy")));
}
=> BybitExchange.FormatSymbol(baseAsset, quoteAsset, tradingMode, deliverTime);

/// <inheritdoc />
public virtual Task<CallResult<UpdateSubscription>> SubscribeToOrderbookUpdatesAsync(string symbol, int depth, Action<DataEvent<BybitOrderbook>> updateHandler, CancellationToken ct = default)
Expand Down
8 changes: 6 additions & 2 deletions ByBit.Net/Clients/V5/BybitSocketClientInverseApiShared.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Bybit.Net.Interfaces.Clients.V5;
using Bybit.Net.Enums;
using Bybit.Net.Interfaces.Clients.V5;
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.SharedApis;
Expand Down Expand Up @@ -60,7 +61,10 @@ async Task<ExchangeResult<UpdateSubscription>> ITradeSocketClient.SubscribeToTra
return new ExchangeResult<UpdateSubscription>(Exchange, validationError);

var symbol = request.Symbol.GetSymbol(FormatSymbol);
var result = await SubscribeToTradeUpdatesAsync(symbol, update => handler(update.AsExchangeEvent(Exchange, update.Data.Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)))), ct).ConfigureAwait(false);
var result = await SubscribeToTradeUpdatesAsync(symbol, update => handler(update.AsExchangeEvent(Exchange, update.Data.Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)
{
Side = x.Side == OrderSide.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell
}))), ct).ConfigureAwait(false);

return new ExchangeResult<UpdateSubscription>(Exchange, result);
}
Expand Down
8 changes: 6 additions & 2 deletions ByBit.Net/Clients/V5/BybitSocketClientLinearApiShared.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Bybit.Net.Interfaces.Clients.V5;
using Bybit.Net.Enums;
using Bybit.Net.Interfaces.Clients.V5;
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.SharedApis;
Expand Down Expand Up @@ -59,7 +60,10 @@ async Task<ExchangeResult<UpdateSubscription>> ITradeSocketClient.SubscribeToTra
return new ExchangeResult<UpdateSubscription>(Exchange, validationError);

var symbol = request.Symbol.GetSymbol(FormatSymbol);
var result = await SubscribeToTradeUpdatesAsync(symbol, update => handler(update.AsExchangeEvent(Exchange, update.Data.Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)))), ct).ConfigureAwait(false);
var result = await SubscribeToTradeUpdatesAsync(symbol, update => handler(update.AsExchangeEvent(Exchange, update.Data.Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)
{
Side = x.Side == OrderSide.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell
}))), ct).ConfigureAwait(false);

return new ExchangeResult<UpdateSubscription>(Exchange, result);
}
Expand Down
8 changes: 6 additions & 2 deletions ByBit.Net/Clients/V5/BybitSocketClientSpotApiShared.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Bybit.Net.Interfaces.Clients.V5;
using Bybit.Net.Enums;
using Bybit.Net.Interfaces.Clients.V5;
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.SharedApis;
Expand Down Expand Up @@ -46,7 +47,10 @@ async Task<ExchangeResult<UpdateSubscription>> ITradeSocketClient.SubscribeToTra
return new ExchangeResult<UpdateSubscription>(Exchange, validationError);

var symbol = request.Symbol.GetSymbol(FormatSymbol);
var result = await SubscribeToTradeUpdatesAsync(symbol, update => handler(update.AsExchangeEvent(Exchange, update.Data.Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)))), ct).ConfigureAwait(false);
var result = await SubscribeToTradeUpdatesAsync(symbol, update => handler(update.AsExchangeEvent(Exchange, update.Data.Select(x => new SharedTrade(x.Quantity, x.Price, x.Timestamp)
{
Side = x.Side == OrderSide.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell
}))), ct).ConfigureAwait(false);

return new ExchangeResult<UpdateSubscription>(Exchange, result);
}
Expand Down
4 changes: 3 additions & 1 deletion ByBit.Net/ExtensionMethods/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Bybit.Net.Clients;
using Bybit.Net;
using Bybit.Net.Clients;
using Bybit.Net.Interfaces;
using Bybit.Net.Interfaces.Clients;
using Bybit.Net.Objects.Options;
Expand Down Expand Up @@ -61,6 +62,7 @@ public static IServiceCollection AddBybit(
services.AddTransient<ICryptoRestClient, CryptoRestClient>();
services.AddTransient<ICryptoSocketClient, CryptoSocketClient>();
services.AddTransient<IBybitOrderBookFactory, BybitOrderBookFactory>();
services.AddTransient<IBybitTrackerFactory, BybitTrackerFactory>();
services.AddTransient(x => x.GetRequiredService<IBybitRestClient>().V5Api.CommonSpotClient);

services.RegisterSharedRestInterfaces(x => x.GetRequiredService<IBybitRestClient>().V5Api.SharedClient);
Expand Down
9 changes: 9 additions & 0 deletions ByBit.Net/Interfaces/IBybitOrderBookFactory.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Bybit.Net.Enums;
using Bybit.Net.Objects.Options;
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.SharedApis;
using System;

namespace Bybit.Net.Interfaces
Expand All @@ -25,6 +26,14 @@ public interface IBybitOrderBookFactory
/// </summary>
public IOrderBookFactory<BybitOrderBookOptions> LinearInverse { get; }

/// <summary>
/// Create a SymbolOrderBook for the symbol
/// </summary>
/// <param name="symbol">The symbol</param>
/// <param name="options">Book options</param>
/// <returns></returns>
ISymbolOrderBook Create(SharedSymbol symbol, Action<BybitOrderBookOptions>? options = null);

/// <summary>
/// Create a SymbolOrderBook specifying the category
/// </summary>
Expand Down
34 changes: 34 additions & 0 deletions ByBit.Net/Interfaces/IBybitTrackerFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using CryptoExchange.Net.SharedApis;
using CryptoExchange.Net.Trackers.Klines;
using CryptoExchange.Net.Trackers.Trades;
using System;
using System.Collections.Generic;
using System.Text;

namespace Bybit.Net.Interfaces
{
/// <summary>
/// Tracker factory
/// </summary>
public interface IBybitTrackerFactory
{
/// <summary>
/// Create a new kline tracker
/// </summary>
/// <param name="symbol">The symbol</param>
/// <param name="interval">Kline interval</param>
/// <param name="limit">The max amount of klines to retain</param>
/// <param name="period">The max period the data should be retained</param>
/// <returns></returns>
IKlineTracker CreateKlineTracker(SharedSymbol symbol, SharedKlineInterval interval, int? limit = null, TimeSpan? period = null);

/// <summary>
/// Create a new trade tracker for a symbol
/// </summary>
/// <param name="symbol">The symbol</param>
/// <param name="limit">The max amount of klines to retain</param>
/// <param name="period">The max period the data should be retained</param>
/// <returns></returns>
ITradeTracker CreateTradeTracker(SharedSymbol symbol, int? limit = null, TimeSpan? period = null);
}
}
23 changes: 20 additions & 3 deletions ByBit.Net/SymbolOrderBooks/BybitOrderBookFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Bybit.Net.Objects.Options;
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.OrderBook;
using CryptoExchange.Net.SharedApis;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
Expand Down Expand Up @@ -32,9 +33,25 @@ public BybitOrderBookFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;

Spot = new OrderBookFactory<BybitOrderBookOptions>((symbol, options) => CreateSpot(symbol, options), (baseAsset, quoteAsset, options) => CreateSpot(baseAsset + quoteAsset, options));
Options = new OrderBookFactory<BybitOrderBookOptions>((symbol, options) => CreateOption(symbol, options), (baseAsset, quoteAsset, options) => CreateOption(baseAsset + quoteAsset, options));
LinearInverse = new OrderBookFactory<BybitOrderBookOptions>((symbol, options) => CreateLinearInverse(symbol, options), (baseAsset, quoteAsset, options) => CreateLinearInverse(baseAsset + quoteAsset, options));
Spot = new OrderBookFactory<BybitOrderBookOptions>(
CreateSpot,
(sharedSymbol, options) => CreateSpot(BybitExchange.FormatSymbol(sharedSymbol.BaseAsset, sharedSymbol.QuoteAsset, sharedSymbol.TradingMode, sharedSymbol.DeliverTime), options));
Options = new OrderBookFactory<BybitOrderBookOptions>(
CreateOption,
(sharedSymbol, options) => CreateOption(BybitExchange.FormatSymbol(sharedSymbol.BaseAsset, sharedSymbol.QuoteAsset, sharedSymbol.TradingMode, sharedSymbol.DeliverTime), options));
LinearInverse = new OrderBookFactory<BybitOrderBookOptions>(
CreateLinearInverse,
(sharedSymbol, options) => CreateLinearInverse(BybitExchange.FormatSymbol(sharedSymbol.BaseAsset, sharedSymbol.QuoteAsset, sharedSymbol.TradingMode, sharedSymbol.DeliverTime), options));
}

/// <inheritdoc />
public ISymbolOrderBook Create(SharedSymbol symbol, Action<BybitOrderBookOptions>? options = null)
{
var symbolName = BybitExchange.FormatSymbol(symbol.BaseAsset, symbol.QuoteAsset, symbol.TradingMode, symbol.DeliverTime);
if (symbol.TradingMode == TradingMode.Spot)
return CreateSpot(symbolName, options);

return CreateLinearInverse(symbolName, options);
}

/// <inheritdoc />
Expand Down
Loading

0 comments on commit 5b99008

Please sign in to comment.