/// <summary>Helper for POSTs</summary> private async Task <JToken> PostData(string method, string command, CancellationToken?cancel, Params parameters = null) { // If called from the UI thread, disable the SynchronisationContext // to prevent deadlocks when waiting for Async results. using (Task_.NoSyncContext()) { // Poloniex requires the 'nonce' values to be strictly increasing. // That means all POSTs must be serialised to avoid a race condition // when POSTing two messages in quick succession. var cancel_token = CancellationTokenSource.CreateLinkedTokenSource(Shutdown, cancel ?? CancellationToken.None).Token; using (RequestThrottle.Lock(cancel_token)) // Limit requests to the required rate { await RequestThrottle.Wait(cancel_token); // Add the command parameter parameters = parameters ?? new Params(); parameters["command"] = command; parameters["nonce"] = Misc.Nonce; // Create the content to POST var post_data_string = Http_.UrlEncode(parameters).TrimStart('?'); var content = new StringContent(post_data_string, Encoding.UTF8, "application/x-www-form-urlencoded"); var msg_hash = Hasher.ComputeHash(Encoding.UTF8.GetBytes(post_data_string)); var signature = Misc.ToStringHex(msg_hash); content.Headers.Add("Sign", signature); var url = $"{Client.BaseAddress}{method}{Http_.UrlEncode(parameters)}"; // Submit the request // Result Codes: // - 422 Un-processable Entity: // Status code is directly reported by Poloniex server. It means the server understands the content type of the request entity, // and the syntax of the request entity is correct, but was unable to process the contained instructions. var response = await Client.PostAsync(url, content, cancel_token); if (!response.IsSuccessStatusCode) { throw new HttpException((int)response.StatusCode, response.ReasonPhrase); } // Interpret the reply var reply = await response.Content.ReadAsStringAsync(); return(JToken.Parse(reply)); } } }
/// <summary>Helper for GETs</summary> private async Task <JToken> GetData(string method, string command, CancellationToken?cancel, Params parameters = null) { // If called from the UI thread, disable the SynchronisationContext // to prevent deadlocks when waiting for Async results. using (Task_.NoSyncContext()) { var cancel_token = CancellationTokenSource.CreateLinkedTokenSource(Shutdown, cancel ?? CancellationToken.None).Token; using (RequestThrottle.Lock(cancel_token)) // Limit requests to the required rate { await RequestThrottle.Wait(cancel_token); // Add the API key for non-public methods parameters = parameters ?? new Params(); if (method != Method.Public) { parameters["apikey"] = Key; parameters["nonce"] = Misc.Nonce; } // Create the URL for the command + parameters var url = $"{UrlRestAddress}api/v1.1/{method}/{command}{Http_.UrlEncode(parameters)}"; // Construct the GET request var req = new HttpRequestMessage(HttpMethod.Get, url); if (method != Method.Public) { var hash = Hasher.ComputeHash(Encoding.UTF8.GetBytes(url)); req.Headers.Add("apisign", Misc.ToStringHex(hash)); } // Submit the request var response = await Client.SendAsync(req, cancel_token); if (!response.IsSuccessStatusCode) { throw new HttpException((int)response.StatusCode, response.ReasonPhrase); } // Interpret the reply var reply = await response.Content.ReadAsStringAsync(); return(JToken.Parse(reply)); } } }
/// <summary>Helper for GETs</summary> private async Task <JToken> GetData(string method, string command, CancellationToken?cancel, Params parameters = null) { // If called from the UI thread, disable the SynchronisationContext // to prevent deadlocks when waiting for Async results. using (Task_.NoSyncContext()) { // Poloniex requires the 'nonce' values to be strictly increasing. // That means all POSTs must be serialised to avoid a race condition // when POSTing two messages in quick succession. var cancel_token = CancellationTokenSource.CreateLinkedTokenSource(Shutdown, cancel ?? CancellationToken.None).Token; using (RequestThrottle.Lock(cancel_token)) // Limit requests to the required rate { await RequestThrottle.Wait(cancel_token); // Add the command to the parameters parameters = parameters ?? new Params(); parameters["command"] = command; // Create the URL for the command + parameters var url = $"{UrlRestAddress}{method}{Http_.UrlEncode(parameters)}"; // Submit the request var response = await Client.GetAsync(url, cancel_token); if (!response.IsSuccessStatusCode) { throw new HttpException((int)response.StatusCode, response.ReasonPhrase); } // Interpret the reply var reply = await response.Content.ReadAsStringAsync(); return(JToken.Parse(reply)); } } }
/// <summary>Helper for GETs</summary> private async Task <JToken> GetData(HttpMethod method, ESecurityType security, string command, CancellationToken?cancel, Params parameters = null, bool timestamp = false, bool log_trace = false) { // If called from the UI thread, disable the SynchronisationContext // to prevent deadlocks when waiting for Async results. using (Task_.NoSyncContext()) { // Poloniex requires the 'nonce' values to be strictly increasing. // That means all POSTs must be serialised to avoid a race condition // when POSTing two messages in quick succession. var cancel_token = CancellationTokenSource.CreateLinkedTokenSource(Shutdown, cancel ?? CancellationToken.None).Token; using (RequestThrottle.Lock(cancel_token)) // Limit requests to the required rate { await RequestThrottle.Wait(cancel_token); // Add fields to the request based on 'security' parameters = parameters ?? new Params(); if (timestamp) { // Needs to be added after waiting on the throttle parameters["timestamp"] = RequestTimestamp; } if (security == ESecurityType.TRADE || security == ESecurityType.USER_DATA) { var query_string = Http_.UrlEncode(parameters).TrimStart('?'); var hash = Hasher.ComputeHash(Encoding.UTF8.GetBytes(query_string)); parameters["signature"] = Misc.ToStringHex(hash); } // Create the request var url = $"{UrlRestAddress}{command}{Http_.UrlEncode(parameters)}"; var req = new HttpRequestMessage(method, url); if (security == ESecurityType.TRADE || security == ESecurityType.USER_DATA || security == ESecurityType.USER_STREAM || security == ESecurityType.MARKET_DATA) { req.Headers.Add("X-MBX-APIKEY", Key); } if (log_trace) { Log.Write(ELogLevel.Debug, req.ToString()); } // Submit the request var sw = new Stopwatch().StartNow(); var response = await Client.SendAsync(req, cancel_token); var reply = await response.Content.ReadAsStringAsync(); Log.Write(ELogLevel.Debug, $"Req time: {sw.Elapsed.ToPrettyString(min_unit: TimeSpan_.ETimeUnits.Milliseconds)} - {url}"); if (log_trace) { Log.Write(ELogLevel.Debug, reply.ToString()); } // Check the API usage weight if (response.Headers.TryGetValues("X-MBX-USED-WEIGHT", out var weights)) { RequestThrottle.UsedWeight = long.Parse(weights.First()); if (RequestThrottle.UsedWeight > 0.5 * RequestThrottle.WeightLimit) { Debug.Assert(false); } } // Interpret the reply if (!response.IsSuccessStatusCode) { // Check for an error var jobj = reply != null?JObject.Parse(reply) : null; if (jobj != null && jobj["code"]?.Value <int>() is int code && jobj["msg"]?.Value <string>() is string msg) { throw new BinanceException((EErrorCode)code, msg); } else { throw new HttpException((int)response.StatusCode, response.ReasonPhrase); } } return(JToken.Parse(reply)); } }