Exemple #1
0
        public async Task RateGate()
        {
            const int timesPerPeriod = 1;
            const int ms             = 100;
            const int loops          = 5;
            double    msMax          = (double)ms * 1.5;
            double    msMin          = (double)ms * (1.0 / 1.5);
            RateGate  gate           = new RateGate(timesPerPeriod, TimeSpan.FromMilliseconds(ms));

            if (!(await gate.WaitToProceedAsync(0)))
            {
                throw new APIException("Rate gate should have allowed immediate access to first attempt");
            }
            for (int i = 0; i < loops; i++)
            {
                Stopwatch timer = Stopwatch.StartNew();
                await gate.WaitToProceedAsync();

                timer.Stop();

                if (i > 0)
                {
                    // check for too much elapsed time with a little fudge
                    Assert.IsTrue(timer.Elapsed.TotalMilliseconds <= msMax, "Rate gate took too long to wait in between calls: " + timer.Elapsed.TotalMilliseconds + "ms");
                    Assert.IsTrue(timer.Elapsed.TotalMilliseconds >= msMin, "Rate gate took too little to wait in between calls: " + timer.Elapsed.TotalMilliseconds + "ms");
                }
            }
        }
Exemple #2
0
        protected async Task <ExchangeResponse> GetResponse(ExchangeRequestBase request)
        {
            var relativeUrlForURL = request.RequestUrl;
            var baseURI           = IsSandbox ? API_SANDBOX_ENDPOINT_URL : API_ENDPOINT_URL;
            var absoluteUri       = new Uri(baseURI, relativeUrlForURL);
            var uriBuilder        = new UriBuilder(absoluteUri);

            uriBuilder.Port = -1;

            // add query parameters
            var requestCasted = request as ExchangePageableRequestBase;
            var nvc           = HttpUtility.ParseQueryString(string.Empty);

            if (request is ExchangePageableRequestBase)
            {
                if (requestCasted.afterCursor != null)
                {
                    nvc["after"] = requestCasted.afterCursor;
                }
                if (requestCasted.RecordCount != null)
                {
                    nvc["limit"] = requestCasted.RecordCount.Value.ToString();
                }
                //if (requestCasted.ZeroBasedCursor < 0)
                //{
                //	nvc["before"] = (Math.Abs(requestCasted.ZeroBasedCursor-1)).ToString(); // change to 1 based
                //	if (requestCasted.RecordCount != null)
                //		nvc["limit"] = requestCasted.RecordCount.Value.ToString();
                //}
                //else if (requestCasted.ZeroBasedCursor > 0)
                //{
                //	nvc["after"] = (requestCasted.ZeroBasedCursor+1).ToString(); // change to 1 based
                //	if (requestCasted.RecordCount != null)
                //		nvc["limit"] = requestCasted.RecordCount.Value.ToString();
                //}
                // else it's zero so no need to put any query parameter
            }
            if (request is GetPersonalOrdersRequest)
            {
                var requestCasted2 = request as GetPersonalOrdersRequest;
                if (requestCasted2.Status != null)
                {
                    foreach (var status in requestCasted2.Status)
                    {
                        nvc.Add("status", status);
                    }
                }
            }
            if (request is CancelAllPersonalOrdersRequest)
            {
                var requestCasted3 = request as CancelAllPersonalOrdersRequest;
                if (requestCasted3.product_id != null)
                {
                    nvc["product_id"] = requestCasted3.product_id;
                }
            }
            if (nvc.Keys.Count > 0)
            {
                uriBuilder.Query = nvc.ToString();
            }

            var body   = request.RequestBody;
            var method = request.Method;
            var url    = uriBuilder.ToString();
            var relativeUrlForSignature = baseURI.MakeRelativeUri(uriBuilder.Uri).ToString();
            await rateGatePolling.WaitToProceedAsync();             // rate limit prior to TimeStamp being generated

            using (var httpClient = new HttpClient())
            {
                if (_authContainer != null)
                {                 // authenticated get, required for querying account specific data, but optional for public data
                                  // Caution: Use the relative URL, *NOT* the absolute one.
                    var signature = _authContainer.ComputeSignature("/" + relativeUrlForSignature, method, body);
                    httpClient.DefaultRequestHeaders.Add("CB-ACCESS-KEY", signature.ApiKey);
                    httpClient.DefaultRequestHeaders.Add("CB-ACCESS-SIGN", signature.Signature);
                    httpClient.DefaultRequestHeaders.Add("CB-ACCESS-TIMESTAMP", signature.TimeStamp);
                    httpClient.DefaultRequestHeaders.Add("CB-ACCESS-PASSPHRASE", signature.Passphrase);
                }

                httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(ContentType));
                httpClient.DefaultRequestHeaders.Add("User-Agent", "vslee fork of sefbkn.github.io");

                HttpResponseMessage response;
                switch (method)
                {
                case "GET":
                    response = await httpClient.GetAsync(url);

                    break;

                case "POST":
                    var requestBody = new StringContent(body, Encoding.UTF8, "application/json");
                    response = await httpClient.PostAsync(url, requestBody);

                    break;

                case "DELETE":
                    response = await httpClient.DeleteAsync(url);

                    break;

                case "PUT":
                    throw new NotImplementedException("PUT");

                default:
                    throw new NotImplementedException("The supplied HTTP method is not supported: " + method ?? "(null)");
                }

                var contentBody = await response.Content.ReadAsStringAsync();

                var headers    = response.Headers.AsEnumerable();
                var statusCode = response.StatusCode;
                var isSuccess  = response.IsSuccessStatusCode;

                var genericExchangeResponse = new ExchangeResponse(statusCode, isSuccess, headers, contentBody);
                return(genericExchangeResponse);
            }
        }
Exemple #3
0
        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;
                }
            }
        }
Exemple #4
0
        /// <summary>
        /// Don't await this - or it won't return until the subscription ends
        /// Authenticated feed messages will not increment the sequence number. It is currently not possible to detect if an authenticated feed message was dropped.
        /// </summary>
        /// <param name="onMessageReceived"></param>
        public async Task SubscribeAsync()
        {
            if (String.IsNullOrWhiteSpace(ProductString))
            {
                throw new ArgumentNullException("product");
            }

            string requestString;
            var    uri = ExchangeClientBase.IsSandbox ? WSS_SANDBOX_ENDPOINT_URL : WSS_ENDPOINT_URL;

            if (_authContainer == null)
            {             // unauthenticated feed
                requestString = String.Format(@"{{""type"": ""subscribe"",""product_id"": ""{0}""}}", ProductString);
            }
            else
            {             // authenticated feed
                var signBlock = _authContainer.ComputeSignature(relativeUrl: "/users/self", method: "GET", body: "");
                requestString = String.Format(
                    @"{{""type"": ""subscribe"",""product_id"": ""{0}"",""signature"": ""{1}"",""key"": ""{2}"",""passphrase"": ""{3}"",""timestamp"": ""{4}""}}",
                    ProductString, signBlock.Signature, signBlock.ApiKey, signBlock.Passphrase, signBlock.TimeStamp);
                uri = new Uri(uri, "/users/self");
            }
            var requestBytes      = UTF8Encoding.UTF8.GetBytes(requestString);
            var subscribeRequest  = new ArraySegment <byte>(requestBytes);
            var cancellationToken = cancellationTokenSource.Token;

            while (!cancellationToken.IsCancellationRequested)
            {
                try
                {
                    using (var webSocketClient = new ClientWebSocket())
                    {
                        await webSocketClient.ConnectAsync(uri, cancellationToken);

                        if (webSocketClient.State == WebSocketState.Open)
                        {
                            await rateGateRealtime.WaitToProceedAsync();                             // don't subscribe at too high of a rate

                            await webSocketClient.SendAsync(subscribeRequest, WebSocketMessageType.Text, true, cancellationToken);

                            while (webSocketClient.State == WebSocketState.Open)
                            {
                                string jsonResponse = "<not assigned>";
                                try
                                {
                                    var receiveBuffer          = new ArraySegment <byte>(new byte[1024 * 1024 * 5]);                           // 5MB buffer
                                    var webSocketReceiveResult = await webSocketClient.ReceiveAsync(receiveBuffer, cancellationToken);

                                    if (webSocketReceiveResult.Count == 0)
                                    {
                                        continue;
                                    }

                                    jsonResponse = Encoding.UTF8.GetString(receiveBuffer.Array, 0, webSocketReceiveResult.Count);
                                    var jToken = JToken.Parse(jsonResponse);

                                    var typeToken = jToken["type"];
                                    if (typeToken == null)
                                    {
                                        OnRealtimeError(new RealtimeError("null typeToken: + " + jsonResponse));
                                        continue;                                         // go to next msg
                                    }

                                    var type = typeToken.Value <string>();
                                    switch (type)
                                    {
                                    case "received":
                                        EventHandler <RealtimeReceived> receivedHandler = RealtimeReceived;
                                        receivedHandler?.Invoke(this, new RealtimeReceived(jToken));
                                        break;

                                    case "open":
                                        EventHandler <RealtimeOpen> openHandler = RealtimeOpen;
                                        openHandler?.Invoke(this, new RealtimeOpen(jToken));
                                        break;

                                    case "done":
                                        EventHandler <RealtimeDone> doneHandler = RealtimeDone;
                                        doneHandler?.Invoke(this, new RealtimeDone(jToken));
                                        break;

                                    case "match":
                                        EventHandler <RealtimeMatch> matchHandler = RealtimeMatch;
                                        matchHandler?.Invoke(this, new RealtimeMatch(jToken));
                                        break;

                                    case "change":
                                        EventHandler <RealtimeChange> changeHandler = RealtimeChange;
                                        changeHandler?.Invoke(this, new RealtimeChange(jToken));
                                        break;

                                    case "heartbeat":
                                        // + should implement this
                                        break;

                                    case "error":
                                        OnRealtimeError(new RealtimeError(jToken));
                                        break;

                                    default:
                                        break;
                                    }
                                }
                                catch (Newtonsoft.Json.JsonReaderException e)
                                {                                                                             // Newtonsoft.Json.JsonReaderException occurred Message = Unexpected end of content while loading JObject.Path 'time'
                                    OnRealtimeError(new RealtimeError(e.Message + ", Msg: " + jsonResponse)); // probably malformed message, so just go to the next msg
                                }
                            }
                        }
                    }
                }
                catch (System.Net.WebSockets.WebSocketException e)
                {                                                  // System.Net.WebSockets.WebSocketException: 'The remote party closed the WebSocket connection without completing the close handshake.'
                    OnRealtimeError(new RealtimeError(e.Message)); // probably just disconnected, so loop back and reconnect again
                }
                catch (System.OperationCanceledException e)
                {                 // System.OperationCanceledException: 'The operation was canceled.'
                    OnRealtimeError(new RealtimeError("Cancellation successful: " + e.Message));
                    break;        // exit loop
                }
            }
        }
        public async Task <Message> OnSendingAsync(Message envelope, CancellationToken cancellationToken)
        {
            await _rateGate.WaitToProceedAsync(cancellationToken);

            return(envelope);
        }