private async void OnMatch(object sender, RealtimeMatch match) { if (await checkSequenceNumber(match.Sequence)) { System.Windows.Application.Current.Dispatcher.Invoke( System.Windows.Threading.DispatcherPriority.Background, new Action(() => { // change to UI thread var list = GetOrderList(side: match.Side); list.Item2.TryGetValue(match.Price.Value, out var linkedList); if (linkedList == null) { return; // + handle when order is not in book (say program missed some orders being added) } var order = linkedList.Where(o => o.Id == match.MakerOrderId).SingleOrDefault(); if (order != null) { order.Size -= match.Size; if (order.Size == 0) { RemoveOrder(match.MakerOrderId, side: match.Side); } if (order.Size < 0) { RemoveOrder(match.MakerOrderId, side: match.Side); } } })); } }
private void OnMatch(object sender, RealtimeMatch match) { var side = match.Side; var list = GetOrderList(side: side); decimal newPrice; lock (list.Item1) { list.Item2.TryGetValue(match.Price.Value, out var linkedList); if (linkedList == null) { return; // handle when order is not in book (say program missed some orders being added) } var order = linkedList.First.Value; // first order in queue gets matched order.Size -= match.Size; newPrice = order.Size; } if (newPrice == 0) { RemoveOrder(match.MakerOrderId, side: side); // keep outside of lock to avoid deadlock } }
private void OnMatch(object sender, RealtimeMatch e) { this.Price = e.Price.Value; PriceChanged?.Invoke(this, e.Price.Value); }
private static async void Subscribe(string product, Action <RealtimeMessage> onMessageReceived) { if (String.IsNullOrWhiteSpace(product)) { throw new ArgumentNullException("product"); } if (onMessageReceived == null) { throw new ArgumentNullException("onMessageReceived", "Message received callback must not be null."); } var uri = new Uri("wss://ws-feed.exchange.coinbase.com"); var webSocketClient = new ClientWebSocket(); var cancellationToken = new CancellationToken(); var requestString = String.Format(@"{{""type"": ""subscribe"",""product_id"": ""{0}""}}", product); var requestBytes = UTF8Encoding.UTF8.GetBytes(requestString); await webSocketClient.ConnectAsync(uri, cancellationToken); if (webSocketClient.State == WebSocketState.Open) { var subscribeRequest = new ArraySegment <byte>(requestBytes); var sendCancellationToken = new CancellationToken(); await webSocketClient.SendAsync(subscribeRequest, WebSocketMessageType.Text, true, sendCancellationToken); while (webSocketClient.State == WebSocketState.Open) { var receiveCancellationToken = new CancellationToken(); var receiveBuffer = new ArraySegment <byte>(new byte[1024 * 1024 * 5]); // 5MB buffer var webSocketReceiveResult = await webSocketClient.ReceiveAsync(receiveBuffer, receiveCancellationToken); if (webSocketReceiveResult.Count == 0) { continue; } var jsonResponse = Encoding.UTF8.GetString(receiveBuffer.Array, 0, webSocketReceiveResult.Count); var jToken = JToken.Parse(jsonResponse); var typeToken = jToken["type"]; if (typeToken == null) { continue; } var type = typeToken.Value <string>(); RealtimeMessage realtimeMessage = null; switch (type) { case "received": realtimeMessage = new RealtimeReceived(jToken); break; case "open": realtimeMessage = new RealtimeOpen(jToken); break; case "done": realtimeMessage = new RealtimeDone(jToken); break; case "match": realtimeMessage = new RealtimeMatch(jToken); break; case "change": realtimeMessage = new RealtimeChange(jToken); break; default: break; } if (realtimeMessage == null) { continue; } onMessageReceived(realtimeMessage); } } }
private static async void Subscribe(string product, Action <RealtimeMessage> onMessageReceived) { if (String.IsNullOrWhiteSpace(product)) { throw new ArgumentNullException("product"); } if (onMessageReceived == null) { throw new ArgumentNullException("onMessageReceived", "Message received callback must not be null."); } JArray aj = new JArray(); var uri = new Uri("wss://ws-feed.gdax.com"); var webSocketClient = new ClientWebSocket(); var cancellationToken = new CancellationToken(); //jStr.Append() var requestString = string.Format(""); //String.Format(@"{{""type"": ""subscribe"",""product_id"": ""{0}""}}", product); //JObject jObj = new JObject( // new JProperty( // "type", "subscribe"), // new JProperty( // "product_ids", new JArray( // "BTC-USD")), // new JProperty( // "channels", new JArray( // "level2", "heartbeat", new JObject( // new JProperty( // "name", "ticker"), new JProperty( // "product_ids", new JArray( // "BTC-USD")))))); //JObject jObj = new JObject( // new JProperty( // "type", "subscribe"), // new JProperty( // "product_ids", new JArray( // "BTC-USD")), // new JProperty( // "channels", new JArray( // "heartbeat", new JObject( // new JProperty( // "name", "ticker"), new JProperty( // "product_ids", new JArray( // "BTC-USD")))))); //JObject jObj = new JObject( // new JProperty( // "type", "subscribe"), // new JProperty( // "product_ids", new JArray( // "BTC-USD")), // new JProperty( // "channels", new JArray( // "matches", "heartbeat", new JObject( // new JProperty( // "name", "ticker"), new JProperty( // "product_ids", new JArray( // "BTC-USD")))))); JObject jObj = new JObject( new JProperty( "type", "subscribe"), new JProperty( "product_ids", new JArray( "BTC-USD", product)), new JProperty( "channels", new JArray( "matches"))); //Console.WriteLine(jObj.ToString()); var requestBytes = UTF8Encoding.UTF8.GetBytes(jObj.ToString()); await webSocketClient.ConnectAsync(uri, cancellationToken); if (webSocketClient.State == WebSocketState.Open) { var subscribeRequest = new ArraySegment <byte>(requestBytes); var sendCancellationToken = new CancellationToken(); await webSocketClient.SendAsync(subscribeRequest, WebSocketMessageType.Text, true, sendCancellationToken); while (webSocketClient.State == WebSocketState.Open) { var receiveCancellationToken = new CancellationToken(); var receiveBuffer = new ArraySegment <byte>(new byte[1024 * 1024 * 1]); // 5MB buffer 1024 * 1024 * 5 var webSocketReceiveResult = await webSocketClient.ReceiveAsync(receiveBuffer, receiveCancellationToken); if (webSocketReceiveResult.Count == 0) { continue; } var jsonResponse = Encoding.UTF8.GetString(receiveBuffer.Array, 0, webSocketReceiveResult.Count); //var jToken = JToken.Parse(jsonResponse); var jToken = JObject.Parse(jsonResponse); var typeToken = jToken["type"]; if (typeToken == null) { continue; } var type = typeToken.Value <string>(); RealtimeMessage realtimeMessage = null; //Console.WriteLine("MSG TYPE: {0}", type); switch (type) { case "received": realtimeMessage = new RealtimeReceived(jToken); break; case "open": realtimeMessage = new RealtimeOpen(jToken); break; case "done": realtimeMessage = new RealtimeDone(jToken); break; case "match": realtimeMessage = new RealtimeMatch(jToken); break; case "change": realtimeMessage = new RealtimeChange(jToken); break; default: break; } if (realtimeMessage == null) { continue; } onMessageReceived(realtimeMessage); } } }
public async Task SubscribeAsync(bool reConnectOnDisconnect) { var uri = ExchangeClientBase.IsSandbox ? WSS_SANDBOX_ENDPOINT_URL : WSS_ENDPOINT_URL; if (_authContainer != null) // authenticated feed { uri = new Uri(uri, "/users/self/verify"); } cancellationTokenSource = new CancellationTokenSource(); while (!cancellationTokenSource.IsCancellationRequested) { string disconnectReason = ""; try { webSocketClient = new ClientWebSocket(); await webSocketClient.ConnectAsync(uri, cancellationTokenSource.Token); if (webSocketClient.State == System.Net.WebSockets.WebSocketState.Open && !cancellationTokenSource.IsCancellationRequested) { await rateGateRealtime.WaitToProceedAsync(); // don't subscribe at too high of a rate await sendSubscriptionMsgAsync(Products : Products, gdax_Channel : gdax_Channel); // key is product name, value is whether connection was just opened if (webSocketClient.State == System.Net.WebSockets.WebSocketState.Open && !cancellationTokenSource.IsCancellationRequested) { // checking again bc maybe the server disconnected after the subscribe msg was sent // + move to processing subscriptions section below later foreach (var product in Products) { ConnectionOpened?.Invoke(product, gdax_Channel); } } while (webSocketClient.State == System.Net.WebSockets.WebSocketState.Open && !cancellationTokenSource.IsCancellationRequested) { using (var timeoutCTS = new CancellationTokenSource(6500)) // heartbeat every 1000 ms, so give it 5 hearbeat chances using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutCTS.Token, cancellationTokenSource.Token)) using (var stream = new MemoryStream(1024)) { var receiveBuffer = new ArraySegment <byte>(new byte[1024 * 8]); bool timedOut = false; WebSocketReceiveResult webSocketReceiveResult; do { try { webSocketReceiveResult = await webSocketClient.ReceiveAsync(receiveBuffer, linkedTokenSource.Token); } catch (OperationCanceledException) { timedOut = true; disconnectReason = " - stream timed out"; break; } await stream.WriteAsync(receiveBuffer.Array, receiveBuffer.Offset, webSocketReceiveResult.Count, cancellationTokenSource.Token); } while (!webSocketReceiveResult.EndOfMessage && !cancellationTokenSource.IsCancellationRequested); if (!timedOut && !cancellationTokenSource.IsCancellationRequested) { var message = stream.ToArray().Where(b => b != 0).ToArray(); var messageString = Encoding.ASCII.GetString(message, 0, message.Length); if (!String.IsNullOrEmpty(messageString)) { try { var jToken = JToken.Parse(messageString); var typeToken = jToken["type"]; if (typeToken == null) { RealtimeDataError?.Invoke(this, new RealtimeError("null typeToken: + " + Encoding.ASCII.GetString(message, 0, message.Length))); return; // go to next msg } var type = typeToken.Value <string>(); switch (type) { case "subscriptions": // + process initial subscription confirmation // + also for unsubscribe confirmation break; case "received": var rr = new RealtimeReceived(jToken); if (rr.Message != null) { RealtimeDataError?.Invoke(this, rr); } RealtimeReceived?.Invoke(this, rr); break; case "open": var ro = new RealtimeOpen(jToken); if (ro.Message != null) { RealtimeDataError?.Invoke(this, ro); } RealtimeOpen?.Invoke(this, ro); break; case "done": var rd = new RealtimeDone(jToken); if (rd.Message != null) { RealtimeDataError?.Invoke(this, rd); } RealtimeDone?.Invoke(this, rd); break; case "match": var rm = new RealtimeMatch(jToken); if (rm.Message != null) { RealtimeDataError?.Invoke(this, rm); } RealtimeMatch?.Invoke(this, rm); break; case "last_match": var rlm = new RealtimeMatch(jToken); if (rlm.Message != null) { RealtimeDataError?.Invoke(this, rlm); } RealtimeLastMatch?.Invoke(this, rlm); break; case "change": var rc = new RealtimeChange(jToken); if (rc.Message != null) { RealtimeDataError?.Invoke(this, rc); } RealtimeChange?.Invoke(this, rc); break; case "heartbeat": // + should implement this (checking LastTraderId) var hb = new Heartbeat(jToken); Heartbeat?.Invoke(this, hb); break; case "error": RealtimeDataError?.Invoke(this, new RealtimeError(jToken)); break; default: RealtimeDataError?.Invoke(this, new RealtimeError("Unexpected type: " + jToken)); break; } } catch (JsonReaderException e) { RealtimeDataError?.Invoke(this, new RealtimeError( "JsonReaderException: " + e.Message + ":" + messageString)); } } else { RealtimeDataError?.Invoke(this, new RealtimeError("empty message received. Connection state: " + webSocketClient.State + ", linkedToken: " + linkedTokenSource.Token.IsCancellationRequested)); } } } } } } catch (Exception e) { if (e.Message == "The remote party closed the WebSocket connection without completing the close handshake.") // System.Net.WebSockets.WebSocketException { disconnectReason = " - remote closed the WebSocket w/o completing the close handshake"; } else if (e.Message == "Unable to connect to the remote server") // System.Net.WebSockets.WebSocketException { disconnectReason = " - unable to connect to server"; // shorten it a bit await Task.Delay(10000); // if unable to connect, then wait 10 seconds before trying to connect again } else { RealtimeStreamError?.Invoke(this, new RealtimeError("other exception caught: " + e.GetType() + " : " + e.Message)); } } if (!reConnectOnDisconnect) { UnSubscribe(); } foreach (var product in Products) { RealtimeStreamError?.Invoke(this, new RealtimeError("disconnected" + disconnectReason)); ConnectionClosed?.Invoke(product, gdax_Channel); } if (!reConnectOnDisconnect) { break; } } }
private static async void Subscribe(string product, Action<RealtimeMessage> onMessageReceived) { if (String.IsNullOrWhiteSpace(product)) throw new ArgumentNullException("product"); if (onMessageReceived == null) throw new ArgumentNullException("onMessageReceived", "Message received callback must not be null."); var uri = new Uri("wss://ws-feed.exchange.coinbase.com"); var webSocketClient = new ClientWebSocket(); var cancellationToken = new CancellationToken(); var requestString = String.Format(@"{{""type"": ""subscribe"",""product_id"": ""{0}""}}", product); var requestBytes = UTF8Encoding.UTF8.GetBytes(requestString); await webSocketClient.ConnectAsync(uri, cancellationToken); if (webSocketClient.State == WebSocketState.Open) { var subscribeRequest = new ArraySegment<byte>(requestBytes); var sendCancellationToken = new CancellationToken(); await webSocketClient.SendAsync(subscribeRequest, WebSocketMessageType.Text, true, sendCancellationToken); while (webSocketClient.State == WebSocketState.Open) { var receiveCancellationToken = new CancellationToken(); var receiveBuffer = new ArraySegment<byte>(new byte[1024 * 1024 * 5]); // 5MB buffer var webSocketReceiveResult = await webSocketClient.ReceiveAsync(receiveBuffer, receiveCancellationToken); if (webSocketReceiveResult.Count == 0) continue; var jsonResponse = Encoding.UTF8.GetString(receiveBuffer.Array, 0, webSocketReceiveResult.Count); var jToken = JToken.Parse(jsonResponse); var typeToken = jToken["type"]; if (typeToken == null) continue; var type = typeToken.Value<string>(); RealtimeMessage realtimeMessage = null; switch (type) { case "received": realtimeMessage = new RealtimeReceived(jToken); break; case "open": realtimeMessage = new RealtimeOpen(jToken); break; case "done": realtimeMessage = new RealtimeDone(jToken); break; case "match": realtimeMessage = new RealtimeMatch(jToken); break; case "change": realtimeMessage = new RealtimeChange(jToken); break; default: break; } if (realtimeMessage == null) continue; onMessageReceived(realtimeMessage); } } }
private async void OnLastMatch(object sender, RealtimeMatch lastMatch) { await checkSequenceNumber(lastMatch.Sequence); }