context: using System;
  using System.Collections.Generic;
  using System.Globalization;
  using System.IO;
  using System.Linq;
  using System.Net;
  using System.Security.Cryptography;
  using System.Text;
  using System.Threading;
  using Newtonsoft.Json;
  using Newtonsoft.Json.Linq;
  using plugins;
  using RestEase;
  
  namespace CryptoFacilities.Api.V1
  {
      public class CryptoFacilitiesApi
      {
          public static List<HistoryLine> Test(Action<string> consoleWrite = null)
          {
              // Create an implementation of that interface
              // We'll pass in the base URL for the API
              var api = RestClient.For<ICryptoFacilitiesApi>("https://www.cryptofacilities.com/derivatives/api/v3/");
  
              var response = api.GetHistoryAsync("F-XBT:USD-Mar18").Result;
              var s = response.GetContent();
              var r = s.Items.OrderBy(x => x.UtcDate).ToList();
              foreach (var historyLine in r)
              {
                  consoleWrite?.Invoke($"Date: {historyLine.UtcDate}");
              }
              return r;
          }
  
          private const string endpoint = "https://www.cryptofacilities.com/api/v3/history";
          private const string _endpoint = "https://www.cryptofacilities.com/derivatives";
  
          private readonly string apiKey;
          private readonly string apiSecret;
          private readonly int rateLimit;
  
          /// <summary>
          /// Contruct the API interface object.
          ///
          /// This class is thread safe.
          /// </summary>
          /// <param name="key">API Key</param>
          /// <param name="secret">API Secret Key</param>
          /// <param name="rateLimit">Rate limit in milliseconds</param>
          public CryptoFacilitiesApi(string key, string secret, int rateLimit = 500)
          {
              this.apiKey = key;
              this.apiSecret = secret;
              this.rateLimit = rateLimit;
          }
  
          /// <summary>
          /// Execute a query to the CryptoFacilities API.
          /// </summary>
          /// <param name="path">API Path (e.g. /api/ticker)</param>
          /// <param name="param">Parameters and their values</param>
          /// <param name="auth">Set to true when authentication is to be used</param>
          /// <returns></returns>
          public string Query(string path, Dictionary<string, string> param = null, bool auth = false)
          {
              RateLimit();
  
              string postData = BuildPostData(param);
  
              string url = endpoint + path;
              if (postData != "")
                  url += "?" + postData;
  
              HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
              webRequest.Method = "POST";
  
              if (auth)
                  AddHeaders(webRequest, path, postData);
  
              if (postData != "")
              {
                  using (var writer = new StreamWriter(webRequest.GetRequestStreamAsync().Result))
                  {
                      writer.Write(postData);
                  }
              }
  
              using (WebResponse webResponse = webRequest.GetResponseAsync().Result)
              using (Stream str = webResponse.GetResponseStream())
              using (StreamReader sr = new StreamReader(str))
              {
                  return sr.ReadToEnd();
              }
          }
  
          /// <summary>
          /// This method returns all contracts currently listed, together with their specifications.
          /// </summary>
          /// <returns></returns>
          public List<Contract> GetContracts()
          {
              string res = Query("/api/contracts");
              var response = JsonConvert.DeserializeObject<GetContractsResponse>(res);
              if (response.Result != "success")
                  throw new Exception(response.Error);
              return response.Contracts;
          }
  
          /// <summary>
          /// This method returns the current best bid and ask prices for a contract (i.e. Level 1 market depth).
          /// </summary>
          /// <param name="tradeable">The name of the contract (e.g. F-XBT:USD-Mar15)</param>
          /// <param name="unit">The currency of denomination of the contract (always USD)</param>
          /// <param name="ask">Current best ask price</param>
          /// <param name="bid">Current best bid price</param>
          public void GetTicker(string tradeable, string unit, out decimal ask, out decimal bid)
          {
              var param = new Dictionary<string, string>();
              param["tradeable"] = tradeable;
              param["unit"] = unit;
  
              string res = Query("/api/ticker", param);
              var response = JsonConvert.DeserializeObject<GetTickerResponse>(res);
              if (response.Result != "success")
                  throw new Exception(response.Error);
  
              ask = response.Ask;
              bid = response.Bid;
          }
  
          /// <summary>
          /// This method returns the current best bid and best ask prices for a contract (i.e. Level 2 market depth), together with their cumulative volumes.
          /// </summary>
          /// <param name="tradeable">The name of the contract (e.g. F-XBT:USD-Mar15)</param>
          /// <param name="unit">The currency of denomination of the contract (always USD)</param>
          /// <param name="cumulatedBids">Array or the form [[bid, cumQty],[bid, cumQty],...,[bid, cumQty]]</param>
          /// <param name="cumulatedAsks">Array or the form [[ask, cumQty],[ask, cumQty],...,[ask, cumQty]]</param>
          public void GetCumulativeBidAsk(
              string tradeable,
              string unit,
              out decimal[][] cumulatedBids,
              out decimal[][] cumulatedAsks)
          {
              var param = new Dictionary<string, string>();
              param["tradeable"] = tradeable;
              param["unit"] = unit;
  
              string res = Query("/api/cumulativebidask", param);
              var response = JsonConvert.DeserializeObject<GetCumulativeBidAskResponse>(res);
              if (response.Result != "success")
                  throw new Exception(response.Error);
  
              var cBids = JsonConvert.DeserializeObject<decimal[][]>(response.CumulatedBids);
              var cAsks = JsonConvert.DeserializeObject<decimal[][]>(response.CumulatedAsks);
  
              cumulatedBids = cBids.OrderByDescending(x => x[0]).ToArray();
              cumulatedAsks = cAsks;
          }
  
          /// <summary>
          /// This method returns the current price of the CF-BPI.
          /// </summary>
          /// <returns></returns>
          public decimal GetCFBPI()
          {
              string res = Query("/api/cfbpi");
              var response = (JObject)JsonConvert.DeserializeObject(res);
              if (response["result"].ToString() != "success")
                  throw new Exception(response["error"].ToString());
  
              return Convert.ToDecimal(response["cf-bpi"], CultureInfo.InvariantCulture);
          }
  
          /// <summary>
          /// This method returns an estimate of the current annualized volatility of the CF-BPI.
          /// It is calculated as the standard deviation of log returns of the last 60 observed
          /// minutely prices of the CF-BPI, scaled by sqrt( 60 * 24 * 365 ). It is updated every
          /// 60 seconds and is provided purely for informational purposes but you might find it
          /// useful for monitoring the risk of your portfolio.
          /// </summary>
          /// <returns></returns>
          public decimal GetVolatility()
          {
              string res = Query("/api/volatility");
              var response = (JObject)JsonConvert.DeserializeObject(res);
              if (response["result"].ToString() != "success")
                  throw new Exception(response["error"].ToString());
  
              return Convert.ToDecimal(response["volatility"], CultureInfo.InvariantCulture);
          }
  
          /// <summary>
          /// This method returns your bitcoin account balance, together with the balances of
          /// all contract positions you might have.
          /// </summary>
          /// <returns>Dictionary with (Tradeable, Balance) entries</returns>
          public Dictionary<string, decimal> GetBalance()
          {
              string res = Query("/api/balance", auth: true);
              var response = (JObject)JsonConvert.DeserializeObject(res);
              if (response["result"].ToString() != "success")
                  throw new Exception(response["error"].ToString());
  
              var result = new Dictionary<string, decimal>();
  
              foreach (var row in response)
              {
  
gt:                 if (row.Key == "result")