/// <summary> /// Constructor for brokerage /// </summary> /// <param name="apiKey">api key</param> /// <param name="apiSecret">api secret</param> /// <param name="algorithm">the algorithm instance is required to retrieve account type</param> /// <param name="aggregator">the aggregator for consolidating ticks</param> /// <param name="job">The live job packet</param> public BinanceBrokerage(string apiKey, string apiSecret, IAlgorithm algorithm, IDataAggregator aggregator, LiveNodePacket job) : base(WebSocketBaseUrl, new WebSocketClientWrapper(), null, apiKey, apiSecret, "Binance") { _job = job; _algorithm = algorithm; _aggregator = aggregator; _messageHandler = new BrokerageConcurrentMessageHandler <WebSocketMessage>(OnMessageImpl); var subscriptionManager = new EventBasedDataQueueHandlerSubscriptionManager(); subscriptionManager.SubscribeImpl += (s, t) => { Subscribe(s); return(true); }; subscriptionManager.UnsubscribeImpl += (s, t) => Unsubscribe(s); SubscriptionManager = subscriptionManager; _apiClient = new BinanceRestApiClient( _symbolMapper, algorithm?.Portfolio, apiKey, apiSecret); _apiClient.OrderSubmit += (s, e) => OnOrderSubmit(e); _apiClient.OrderStatusChanged += (s, e) => OnOrderEvent(e); _apiClient.Message += (s, e) => OnMessage(e); // User data streams will close after 60 minutes. It's recommended to send a ping about every 30 minutes. // Source: https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#pingkeep-alive-a-listenkey _keepAliveTimer = new Timer { // 30 minutes Interval = 30 * 60 * 1000 }; _keepAliveTimer.Elapsed += (s, e) => _apiClient.SessionKeepAlive(); WebSocket.Open += (s, e) => { _keepAliveTimer.Start(); }; WebSocket.Closed += (s, e) => { _keepAliveTimer.Stop(); }; // A single connection to stream.binance.com is only valid for 24 hours; expect to be disconnected at the 24 hour mark // Source: https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md#general-wss-information _reconnectTimer = new Timer { // 23.5 hours Interval = 23.5 * 60 * 60 * 1000 }; _reconnectTimer.Elapsed += (s, e) => { Log.Trace("Daily websocket restart: disconnect"); Disconnect(); Log.Trace("Daily websocket restart: connect"); Connect(); }; }
public void MessagesHandledCorrectly() { const int expectedCount = 10000; var numbers = new List <string>(); void Action(string number) => numbers.Add(number); var handler = new BrokerageConcurrentMessageHandler <string>(Action); var task = Task.Factory.StartNew(() => { var counter = 0; for (var i = 0; i < expectedCount; i++) { handler.HandleNewMessage($"{Interlocked.Increment(ref counter)}"); if (i % 50 == 0) { Thread.Sleep(1); } } }); for (var i = 0; i < expectedCount;) { handler.WithLockedStream(() => { // place order i++; }); if (i % 50 == 0) { Thread.Sleep(1); } } if (!task.Wait(TimeSpan.FromSeconds(5))) { Assert.Fail("BrokerageConcurrentMessageHandlerTests.MessagesHandledCorrectly(): timeout waiting for task to finish"); } // all processed Assert.AreEqual(expectedCount, numbers.Count); for (var i = 0; i < numbers.Count; i++) { // all in order Assert.AreEqual($"{i + 1}", numbers[i]); } }
/// <summary> /// Initialize the instance of this class /// </summary> /// <param name="wssUrl">The web socket base url</param> /// <param name="restApiUrl">The rest api url</param> /// <param name="apiKey">api key</param> /// <param name="apiSecret">api secret</param> /// <param name="algorithm">the algorithm instance is required to retrieve account type</param> /// <param name="aggregator">the aggregator for consolidating ticks</param> /// <param name="job">The live job packet</param> private void Initialize(string wssUrl, string restApiUrl, string apiKey, string apiSecret, IAlgorithm algorithm, IDataAggregator aggregator, LiveNodePacket job) { if (IsInitialized) { return; } base.Initialize(wssUrl, new WebSocketClientWrapper(), null, apiKey, apiSecret); _job = job; _algorithm = algorithm; _aggregator = aggregator; _webSocketBaseUrl = wssUrl; _messageHandler = new BrokerageConcurrentMessageHandler <WebSocketMessage>(OnUserMessage); var maximumWebSocketConnections = Config.GetInt("binance-maximum-websocket-connections"); var symbolWeights = maximumWebSocketConnections > 0 ? FetchSymbolWeights() : null; var subscriptionManager = new BrokerageMultiWebSocketSubscriptionManager( wssUrl, MaximumSymbolsPerConnection, maximumWebSocketConnections, symbolWeights, () => new BinanceWebSocketWrapper(null), Subscribe, Unsubscribe, OnDataMessage, new TimeSpan(23, 45, 0)); SubscriptionManager = subscriptionManager; // can be null, if BinanceBrokerage is used as DataQueueHandler only if (_algorithm != null) { // Binance rest api endpoint is different for sport and margin trading // we need to delay initialization of rest api client until Algorithm is initialized // and user brokerage choise is actually applied _apiClientLazy = new Lazy <BinanceBaseRestApiClient>(() => { BinanceBaseRestApiClient apiClient = _algorithm.BrokerageModel.AccountType == AccountType.Cash ? new BinanceSpotRestApiClient(_symbolMapper, algorithm?.Portfolio, apiKey, apiSecret, restApiUrl) : new BinanceCrossMarginRestApiClient(_symbolMapper, algorithm?.Portfolio, apiKey, apiSecret, restApiUrl); apiClient.OrderSubmit += (s, e) => OnOrderSubmit(e); apiClient.OrderStatusChanged += (s, e) => OnOrderEvent(e); apiClient.Message += (s, e) => OnMessage(e); // once we know the api endpoint we can subscribe to user data stream apiClient.CreateListenKey(); _keepAliveTimer.Elapsed += (s, e) => apiClient.SessionKeepAlive(); Connect(apiClient.SessionId); return(apiClient); }); } // User data streams will close after 60 minutes. It's recommended to send a ping about every 30 minutes. // Source: https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#pingkeep-alive-a-listenkey _keepAliveTimer = new Timer { // 30 minutes Interval = 30 * 60 * 1000 }; WebSocket.Open += (s, e) => { _keepAliveTimer.Start(); }; WebSocket.Closed += (s, e) => { ApiClient.StopSession(); _keepAliveTimer.Stop(); }; // A single connection to stream.binance.com is only valid for 24 hours; expect to be disconnected at the 24 hour mark // Source: https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md#general-wss-information _reconnectTimer = new Timer { // 23.5 hours Interval = 23.5 * 60 * 60 * 1000 }; _reconnectTimer.Elapsed += (s, e) => { Log.Trace("Daily websocket restart: disconnect"); Disconnect(); Log.Trace("Daily websocket restart: connect"); Connect(); }; }
/// <summary> /// Initialize the instance of this class /// </summary> /// <param name="wssUrl">The web socket base url</param> /// <param name="restApiUrl">The rest api url</param> /// <param name="apiKey">api key</param> /// <param name="apiSecret">api secret</param> /// <param name="algorithm">the algorithm instance is required to retrieve account type</param> /// <param name="aggregator">the aggregator for consolidating ticks</param> /// <param name="job">The live job packet</param> private void Initialize(string wssUrl, string restApiUrl, string apiKey, string apiSecret, IAlgorithm algorithm, IDataAggregator aggregator, LiveNodePacket job) { if (IsInitialized) { return; } base.Initialize(wssUrl, new WebSocketClientWrapper(), null, apiKey, apiSecret); _job = job; _algorithm = algorithm; _aggregator = aggregator; _webSocketBaseUrl = wssUrl; _messageHandler = new BrokerageConcurrentMessageHandler <WebSocketMessage>(OnUserMessage); var maximumWebSocketConnections = Config.GetInt("binance-maximum-websocket-connections"); var symbolWeights = maximumWebSocketConnections > 0 ? FetchSymbolWeights() : null; var subscriptionManager = new BrokerageMultiWebSocketSubscriptionManager( wssUrl, MaximumSymbolsPerConnection, maximumWebSocketConnections, symbolWeights, () => new BinanceWebSocketWrapper(null), Subscribe, Unsubscribe, OnDataMessage, new TimeSpan(23, 45, 0)); SubscriptionManager = subscriptionManager; _apiClient = new BinanceRestApiClient(_symbolMapper, algorithm?.Portfolio, apiKey, apiSecret, restApiUrl); _apiClient.OrderSubmit += (s, e) => OnOrderSubmit(e); _apiClient.OrderStatusChanged += (s, e) => OnOrderEvent(e); _apiClient.Message += (s, e) => OnMessage(e); // User data streams will close after 60 minutes. It's recommended to send a ping about every 30 minutes. // Source: https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#pingkeep-alive-a-listenkey _keepAliveTimer = new Timer { // 30 minutes Interval = 30 * 60 * 1000 }; _keepAliveTimer.Elapsed += (s, e) => _apiClient.SessionKeepAlive(); WebSocket.Open += (s, e) => { _keepAliveTimer.Start(); }; WebSocket.Closed += (s, e) => { _keepAliveTimer.Stop(); }; // A single connection to stream.binance.com is only valid for 24 hours; expect to be disconnected at the 24 hour mark // Source: https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md#general-wss-information _reconnectTimer = new Timer { // 23.5 hours Interval = 23.5 * 60 * 60 * 1000 }; _reconnectTimer.Elapsed += (s, e) => { Log.Trace("Daily websocket restart: disconnect"); Disconnect(); Log.Trace("Daily websocket restart: connect"); Connect(); }; }