What is the correct way to manage those instances/services? In my case, IExchangeClient
should be disposed at some point and more accurately what's inside it: BinanceClient
, BinanceSocketClient
and Subject<IObservable<Unit>>
(these 3 are third-party library instances). There are many ways where it can be disposed. I tried make it more like ASP.NET Core's way and dispose it in Main()
. Maybe there is a cool NuGet package which handles that better, just like ASP.NET Core? I feel like subproject 2's design is completely messed up.
BacktestOptions, ExchangeOptions and TradeOptions are appsettings configs, in case you ask.
Backtesting subproject:
public class Backtest
{
private readonly ITradingStrategy _tradingStrategy;
private readonly IDataProvider _dataProvider;
private readonly BacktestOptions _backtestOptions;
public Backtest(ITradingStrategy tradingStrategy, IDataProvider dataProvider, BacktestOptions backtestOptions)
{
_tradingStrategy = tradingStrategy;
_dataProvider = dataProvider;
_backtestOptions = backtestOptions;
}
public async Task RunAsync()
{
...
}
}
public interface IDataProvider
{
Task<List<OHLCV>> DownloadCandlesAsync(string pair, Timeframe timeframe, DateTime startDate, DateTime endDate, int startupCandleCount);
}
public class DataProvider : IDataProvider
{
private readonly IExchangeClient _exchangeClient;
public DataProvider(IExchangeClient exchangeClient)
{
_exchangeClient = exchangeClient;
}
public async Task<List<OHLCV>> DownloadCandlesAsync(string pair, Timeframe timeframe, DateTime startDate, DateTime endDate, int startupCandleCount)
{
...
}
}
public interface IExchangeClient : IDisposable
{
...
}
public class BinanceSpotClient : IExchangeClient
{
private readonly IBinanceClient _client;
private readonly IBinanceSocketClient _socketClient;
public BinanceSpotClient(ExchangeOptions exchangeOptions)
{
_client = new BinanceClient(new BinanceClientOptions()
{
ApiCredentials = new ApiCredentials(exchangeOptions.ApiKey, exchangeOptions.SecretKey),
AutoTimestamp = true,
AutoTimestampRecalculationInterval = TimeSpan.FromMinutes(30),
TradeRulesBehaviour = TradeRulesBehaviour.AutoComply,
#if DEBUG
LogVerbosity = LogVerbosity.Debug
#endif
});
_socketClient = new BinanceSocketClient(new BinanceSocketClientOptions()
{
ApiCredentials = new ApiCredentials(exchangeOptions.ApiKey, exchangeOptions.SecretKey),
AutoReconnect = true,
ReconnectInterval = TimeSpan.FromSeconds(15),
#if DEBUG
LogVerbosity = LogVerbosity.Debug
#endif
});
}
private readonly Subject<IObservable<Unit>> _subject = new Subject<IObservable<Unit>>();
...
private bool _disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
if (_client.NotNull())
_client.Dispose();
if (_socketClient.NotNull())
{
_socketClient.UnsubscribeAll();
_socketClient.Dispose();
}
_subject.OnNext(Observable.Never<Unit>());
}
_disposed = true;
}
}
class Program
{
static async Task Main(string[] args)
{
using IExchangeClient exchangeClient = new BinanceSpotClient(configuration.ExchangeOptions);
ITradingStrategy tradingStrategy = StrategyUtils.GetStrategyInstance(configuration.BacktestOptions.StrategyName);
IDataProvider dataProvider = new DataProvider(exchangeClient);
var backtest = new Backtest(tradingStrategy, dataProvider, configuration.BacktestOptions);
await backtest.RunAsync().ConfigureAwait(false);
Console.ReadLine();
}
}
Live trading subproject 2:
In this case, I have a factory method implementation, which makes the disposing harder. I'm disposing IExchangeClient
in LiveTradeManager
and I feel like that's terribly wrong. Maybe I should dispose IExchangeClient elsewhere and leave only the x4 web socket stream subscriptions disposed there?
public static class TradeManagerFactory
{
public static ITradeManager Build(Manager managers, ExchangeOptions exchangeOptions, TradeOptions tradeOptions)
{
IExchangeClient exchangeClient = new BinanceSpotClient(exchangeOptions);
ITradingStrategy tradingStrategy = StrategyUtils.GetStrategyInstance(tradeOptions.StrategyName);
ITradeManager tradeManager = null;
switch (managers)
{
case Manager.LiveTradeManager:
tradeManager = new LiveTradeManager(exchangeClient, tradingStrategy, exchangeOptions, tradeOptions);
break;
}
return tradeManager;
}
}
public interface ITradeManager : IDisposable
{
Task RunAsync();
}
public class LiveTradeManager : ITradeManager
{
private static readonly ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType.Name);
private readonly IExchangeClient _exchangeClient;
private readonly ITradingStrategy _tradingStrategy;
private readonly ExchangeOptions _exchangeOptions;
private readonly TradeOptions _tradeOptions;
private readonly Wallets _wallets;
private readonly Trades _trades;
private readonly List<OHLCV> _candles;
public LiveTradeManager(IExchangeClient exchangeClient, ITradingStrategy tradingStrategy, ExchangeOptions exchangeOptions, TradeOptions tradeOptions)
{
_exchangeClient = exchangeClient;
_tradingStrategy = tradingStrategy;
_exchangeOptions = exchangeOptions;
_tradeOptions = tradeOptions;
_wallets = new Wallets(exchangeClient);
_trades = new Trades();
_candles = new List<OHLCV>();
}
private CallResult<UpdateSubscription> _tickerSubscription;
private CallResult<UpdateSubscription> _candlestickSubscription;
private CallResult<UpdateSubscription> _orderSubscription;
private IDisposable _throttlerSubscription;
public async Task RunAsync()
{
...
}
private bool _disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
if (_exchangeClient.NotNull())
_exchangeClient.Dispose();
_exchangeClient.Unsubscribe(_tickerSubscription.Data);
_exchangeClient.Unsubscribe(_candlestickSubscription.Data);
_exchangeClient.Unsubscribe(_orderSubscription.Data);
if (_throttlerSubscription.NotNull())
_throttlerSubscription.Dispose();
}
_disposed = true;
}
}
class Program
{
static async Task Main(string[] args)
{
var tradeManager = TradeManagerFactory.Build(Manager.LiveTradeManager, configuration.ExchangeOptions, configuration.TradeOptions);
await tradeManager.RunAsync().ConfigureAwait(false);
Console.ReadLine();
}
}
Aucun commentaire:
Enregistrer un commentaire